General Cookbook
Please note that these examples make use of Chisel's scala-style printing.
- Type Conversions
- Vectors and Registers
- Bundles
- How do I deal with aliased Bundle fields?
- How do I deal with the "unable to clone" error?
- How do I create a finite state machine (FSM)?
- How do I unpack a value ("reverse concatenation") like in Verilog?
- How do I do subword assignment (assign to some bits in a UInt)?
- How do I create an optional I/O?
- How do I create I/O without a prefix?
- How do I override the implicit clock or reset within a Module?
- How do I minimize the number of bits used in an output vector?
- How do I resolve "Dynamic index ... is too wide/narrow for extractee ..."?
- Predictable Naming
- Directionality
Type Conversions
How do I create a UInt from an instance of a Bundle?
Call asUInt
on the Bundle
instance.
import chisel3._
class MyBundle extends Bundle {
val foo = UInt(4.W)
val bar = UInt(4.W)
}
class Foo extends Module {
val bundle = Wire(new MyBundle)
bundle.foo := 0xc.U
bundle.bar := 0x3.U
val uint = bundle.asUInt
printf(cf"$uint") // 195
// Test
assert(uint === 0xc3.U)
}
How do I create a Bundle from a UInt?
Use the asTypeOf
method to reinterpret the UInt
as the type of the Bundle
.
import chisel3._
class MyBundle extends Bundle {
val foo = UInt(4.W)
val bar = UInt(4.W)
}
class Foo extends Module {
val uint = 0xb4.U
val bundle = uint.asTypeOf(new MyBundle)
printf(cf"$bundle") // Bundle(foo -> 11, bar -> 4)
// Test
assert(bundle.foo === 0xb.U)
assert(bundle.bar === 0x4.U)
}
How can I tieoff a Bundle/Vec to 0?
You can use asTypeOf
as above. If you don't want to worry about the type of the thing
you are tying off, you can use chiselTypeOf
:
import chisel3._
class MyBundle extends Bundle {
val foo = UInt(4.W)
val bar = Vec(4, UInt(1.W))
}
class Foo(typ: MyBundle) extends Module {
val bundleA = IO(Output(typ))
val bundleB = IO(Output(typ))
// typ is already a Chisel Data Type, so can use it directly here, but you
// need to know that bundleA is of type typ
bundleA := 0.U.asTypeOf(typ)
// bundleB is a Hardware data IO(Output(...)) so need to call chiselTypeOf,
// but this will work no matter the type of bundleB:
bundleB := 0.U.asTypeOf(chiselTypeOf(bundleB))
}
How do I create a Vec of Bools from a UInt?
Use VecInit
given a Seq[Bool]
generated using the asBools
method.
import chisel3._
class Foo extends Module {
val uint = 0xc.U
val vec = VecInit(uint.asBools)
printf(cf"$vec") // Vec(0, 0, 1, 1)
// Test
assert(vec(0) === false.B)
assert(vec(1) === false.B)
assert(vec(2) === true.B)
assert(vec(3) === true.B)
}
How do I create a UInt from a Vec of Bool?
Use the builtin function asUInt
import chisel3._
class Foo extends Module {
val vec = VecInit(true.B, false.B, true.B, true.B)
val uint = vec.asUInt
printf(cf"$uint") // 13
// Test
// (remember leftmost Bool in Vec is low order bit)
assert(0xd.U === uint)
}
How do I connect a subset of Bundle fields?
See the DataView cookbook.
Vectors and Registers
Can I make a 2D or 3D Vector?
Yes. Using VecInit
you can make Vectors that hold Vectors of Chisel types. Methods fill
and tabulate
make these multi-dimensional Vectors.
import chisel3._
class MyBundle extends Bundle {
val foo = UInt(4.W)
val bar = UInt(4.W)
}
class Foo extends Module {
//2D Fill
val twoDVec = VecInit.fill(2, 3)(5.U)
//3D Fill
val myBundle = Wire(new MyBundle)
myBundle.foo := 0xc.U
myBundle.bar := 0x3.U
val threeDVec = VecInit.fill(1, 2, 3)(myBundle)
assert(threeDVec(0)(0)(0).foo === 0xc.U && threeDVec(0)(0)(0).bar === 0x3.U)
//2D Tabulate
val indexTiedVec = VecInit.tabulate(2, 2){ (x, y) => (x + y).U }
assert(indexTiedVec(0)(0) === 0.U)
assert(indexTiedVec(0)(1) === 1.U)
assert(indexTiedVec(1)(0) === 1.U)
assert(indexTiedVec(1)(1) === 2.U)
//3D Tabulate
val indexTiedVec3D = VecInit.tabulate(2, 3, 4){ (x, y, z) => (x + y * z).U }
assert(indexTiedVec3D(0)(0)(0) === 0.U)
assert(indexTiedVec3D(1)(1)(1) === 2.U)
assert(indexTiedVec3D(1)(1)(2) === 3.U)
assert(indexTiedVec3D(1)(1)(3) === 4.U)
assert(indexTiedVec3D(1)(2)(3) === 7.U)
}
How do I create a Vector of Registers?
Rule! Use Reg of Vec not Vec of Reg!
You create a Reg of type Vec. Because Vecs are a type (like UInt
, Bool
) rather than a value, we must bind the Vec to some concrete value.
How do I create a Reg of type Vec?
For more information, the API Documentation for Vec
provides more information.
import chisel3._
class Foo extends Module {
val regOfVec = Reg(Vec(4, UInt(32.W))) // Register of 32-bit UInts
regOfVec(0) := 123.U // Assignments to elements of the Vec
regOfVec(1) := 456.U
regOfVec(2) := 789.U
regOfVec(3) := regOfVec(0)
// Reg of Vec of 32-bit UInts initialized to zero
// Note that Seq.fill constructs 4 32-bit UInt literals with the value 0
// VecInit(...) then constructs a Wire of these literals
// The Reg is then initialized to the value of the Wire (which gives it the same type)
val initRegOfVec = RegInit(VecInit(Seq.fill(4)(0.U(32.W))))
}
How do I partially reset an Aggregate Reg?
The easiest way is to use a partially-specified Bundle Literal or Vec Literal to match the type of the Reg.
import chisel3._
import chisel3.experimental.BundleLiterals._
class MyBundle extends Bundle {
val foo = UInt(8.W)
val bar = UInt(8.W)
}
class MyModule extends Module {
// Only .foo will be reset, .bar will have no reset value
val reg = RegInit((new MyBundle).Lit(_.foo -> 123.U))
}
If your initial value is not a literal, or if you just prefer, you can use a
Wire as the initial value for the Reg. Simply connect fields to DontCare
that
you do not wish to be reset.
class MyModule2 extends Module {
val reg = RegInit({
// The wire could be constructed before the reg rather than in the RegInit scope,
// but this style has nice lexical scoping behavior, keeping the Wire private
val init = Wire(new MyBundle)
init := DontCare // No fields will be reset
init.foo := 123.U // Last connect override, .foo is reset
init
})
}
Bundles
How do I deal with aliased Bundle fields?
Following the gen
pattern when creating Bundles can result in some opaque error messages:
class AliasedBundle[T <: Data](gen: T) extends Bundle {
val foo = gen
val bar = gen
}
getVerilogString(new Top(new AliasedBundle(UInt(8.W))))
// chisel3.AliasedAggregateFieldException: AliasedBundle contains aliased fields named (foo,bar)
// at ... ()
// at repl.MdocSession$MdocApp17$Top$$anonfun$50$$anonfun$apply$37.apply(cookbook.md:298)
// at repl.MdocSession$MdocApp17$Top$$anonfun$50$$anonfun$apply$37.apply(cookbook.md:298)
// at chisel3.experimental.prefix$.apply(prefix.scala:50)
// at repl.MdocSession$MdocApp17$Top$$anonfun$50.apply(cookbook.md:298)
// at repl.MdocSession$MdocApp17$Top$$anonfun$50.apply(cookbook.md)
// at chisel3.internal.plugin.package$.autoNameRecursively(package.scala:33)
// at repl.MdocSession$MdocApp17$Top.<init>(cookbook.md:298)
// at repl.MdocSession$MdocApp17$$anonfun$55$$anonfun$apply$43.apply(cookbook.md:317)
// at repl.MdocSession$MdocApp17$$anonfun$55$$anonfun$apply$43.apply(cookbook.md:317)
// at chisel3.ObjectModuleImpl.evaluate(ModuleImpl.scala:84)
// at chisel3.ObjectModuleImpl.evaluate$(ModuleImpl.scala:52)
// at chisel3.Module$.evaluate(Module.scala:10)
// at chisel3.ObjectModuleImpl._applyImpl(ModuleImpl.scala:25)
// at chisel3.ObjectModuleImpl._applyImpl$(ModuleImpl.scala:23)
// at chisel3.Module$._applyImpl(Module.scala:10)
// at chisel3.Module$.do_apply(Module.scala:22)
// at chisel3.stage.phases.Elaborate.$anonfun$transform$2(Elaborate.scala:54)
// at chisel3.internal.Builder$.$anonfun$buildImpl$1(Builder.scala:1048)
// at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
// at chisel3.internal.Builder$.buildImpl(Builder.scala:1038)
// at chisel3.internal.Builder$.$anonfun$build$1(Builder.scala:1030)
// at logger.Logger$.$anonfun$makeScope$4(Logger.scala:148)
// at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
// at logger.Logger$.makeScope(Logger.scala:146)
// at logger.Logger$.makeScope(Logger.scala:133)
// at ... ()
// at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)
This error is saying that fields foo
and bar
of AliasedBundle
are the
exact same object in memory.
This is a problem for Chisel because we need to be able to distinguish uses of
foo
and bar
but cannot when they are referentially the same.
Note that the following example looks different but will give you exactly the same issue:
class AlsoAliasedBundle[T <: Data](val gen: T) extends Bundle {
// ^ This val makes `gen` a field, just like `foo`
val foo = gen
}
By making gen
a val
, it becomes a public field of the class
, just like foo
.
getVerilogString(new Top(new AlsoAliasedBundle(UInt(8.W))))
// chisel3.AliasedAggregateFieldException: AlsoAliasedBundle contains aliased fields named (gen,foo)
// at ... ()
// at repl.MdocSession$MdocApp17$Top$$anonfun$50$$anonfun$apply$37.apply(cookbook.md:298)
// at repl.MdocSession$MdocApp17$Top$$anonfun$50$$anonfun$apply$37.apply(cookbook.md:298)
// at chisel3.experimental.prefix$.apply(prefix.scala:50)
// at repl.MdocSession$MdocApp17$Top$$anonfun$50.apply(cookbook.md:298)
// at repl.MdocSession$MdocApp17$Top$$anonfun$50.apply(cookbook.md)
// at chisel3.internal.plugin.package$.autoNameRecursively(package.scala:33)
// at repl.MdocSession$MdocApp17$Top.<init>(cookbook.md:298)
// at repl.MdocSession$MdocApp17$$anonfun$57$$anonfun$apply$44.apply(cookbook.md:336)
// at repl.MdocSession$MdocApp17$$anonfun$57$$anonfun$apply$44.apply(cookbook.md:336)
// at chisel3.ObjectModuleImpl.evaluate(ModuleImpl.scala:84)
// at chisel3.ObjectModuleImpl.evaluate$(ModuleImpl.scala:52)
// at chisel3.Module$.evaluate(Module.scala:10)
// at chisel3.ObjectModuleImpl._applyImpl(ModuleImpl.scala:25)
// at chisel3.ObjectModuleImpl._applyImpl$(ModuleImpl.scala:23)
// at chisel3.Module$._applyImpl(Module.scala:10)
// at chisel3.Module$.do_apply(Module.scala:22)
// at chisel3.stage.phases.Elaborate.$anonfun$transform$2(Elaborate.scala:54)
// at chisel3.internal.Builder$.$anonfun$buildImpl$1(Builder.scala:1048)
// at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
// at chisel3.internal.Builder$.buildImpl(Builder.scala:1038)
// at chisel3.internal.Builder$.$anonfun$build$1(Builder.scala:1030)
// at logger.Logger$.$anonfun$makeScope$4(Logger.scala:148)
// at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
// at logger.Logger$.makeScope(Logger.scala:146)
// at logger.Logger$.makeScope(Logger.scala:133)
// at ... ()
// at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)
There are several ways to solve this issue with their own advantages and disadvantages.
1. 0-arity function parameters
Instead of passing an object as a parameter, you can pass a 0-arity function (a function with no arguments):
class UsingAFunctionBundle[T <: Data](gen: () => T) extends Bundle {
val foo = gen()
val bar = gen()
}
Note that the type of gen
is now () => T
.
Because it is now a function and not a subtype of Data
, you can safely make gen
a val
without
it becoming a hardware field of the Bundle
.
Note that this also means you must pass gen
as a function, for example:
chisel3.docs.emitSystemVerilog(new Top(new UsingAFunctionBundle(() => UInt(8.W))))
Aliased Warning
Warning: you must ensure that gen
creates fresh objects rather than capturing an already constructed value:
class MisusedFunctionArguments extends Module {
// This usage is correct
val in = IO(Input(new UsingAFunctionBundle(() => UInt(8.W))))
// This usage is incorrect
val fizz = UInt(8.W)
val out = IO(Output(new UsingAFunctionBundle(() => fizz)))
}
getVerilogString(new MisusedFunctionArguments)
// chisel3.AutoClonetypeException: The bundle plugin was unable to clone UsingAFunctionBundle that has field 'bar' aliased with base UsingAFunctionBundle.This likely happened because you tried nesting Data arguments inside of other data structures. Try wrapping the field(s) in Input(...), Output(...), or Flipped(...) if appropriate. As a last resort, you can call chisel3.reflect.DataMirror.internal.chiselTypeClone on any nested Data arguments. See the cookbook entry 'How do I deal with the "unable to clone" error?' for more details.
// at ... ()
// at repl.MdocSession$MdocApp17$$anonfun$59$MisusedFunctionArguments$1$$anonfun$62$$anonfun$apply$51$$anonfun$apply$52.apply(cookbook.md:367)
// at repl.MdocSession$MdocApp17$$anonfun$59$MisusedFunctionArguments$1$$anonfun$62$$anonfun$apply$51$$anonfun$apply$52.apply(cookbook.md:367)
// at chisel3.IO$.apply(IO.scala:34)
// at chisel3.experimental.BaseModule.IO(ModuleImpl.scala:863)
// at repl.MdocSession$MdocApp17$$anonfun$59$MisusedFunctionArguments$1$$anonfun$62$$anonfun$apply$51.apply(cookbook.md:367)
// at repl.MdocSession$MdocApp17$$anonfun$59$MisusedFunctionArguments$1$$anonfun$62$$anonfun$apply$51.apply(cookbook.md:367)
// at chisel3.experimental.prefix$.apply(prefix.scala:50)
// at repl.MdocSession$MdocApp17$$anonfun$59$MisusedFunctionArguments$1$$anonfun$62.apply(cookbook.md:367)
// at repl.MdocSession$MdocApp17$$anonfun$59$MisusedFunctionArguments$1$$anonfun$62.apply(cookbook.md)
// at chisel3.internal.plugin.package$.autoNameRecursively(package.scala:33)
// at repl.MdocSession$MdocApp17$$anonfun$59$MisusedFunctionArguments$1.<init>(cookbook.md:367)
// at repl.MdocSession$MdocApp17$$anonfun$59$$anonfun$apply$55.apply(cookbook.md:369)
// at repl.MdocSession$MdocApp17$$anonfun$59$$anonfun$apply$55.apply(cookbook.md:369)
// at chisel3.ObjectModuleImpl.evaluate(ModuleImpl.scala:84)
// at chisel3.ObjectModuleImpl.evaluate$(ModuleImpl.scala:52)
// at chisel3.Module$.evaluate(Module.scala:10)
// at chisel3.ObjectModuleImpl._applyImpl(ModuleImpl.scala:25)
// at chisel3.ObjectModuleImpl._applyImpl$(ModuleImpl.scala:23)
// at chisel3.Module$._applyImpl(Module.scala:10)
// at chisel3.Module$.do_apply(Module.scala:22)
// at chisel3.stage.phases.Elaborate.$anonfun$transform$2(Elaborate.scala:54)
// at chisel3.internal.Builder$.$anonfun$buildImpl$1(Builder.scala:1048)
// at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
// at chisel3.internal.Builder$.buildImpl(Builder.scala:1038)
// at chisel3.internal.Builder$.$anonfun$build$1(Builder.scala:1030)
// at logger.Logger$.$anonfun$makeScope$4(Logger.scala:148)
// at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
// at logger.Logger$.makeScope(Logger.scala:146)
// at logger.Logger$.makeScope(Logger.scala:133)
// at ... ()
// at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)
In the above example, value fizz
and fields foo
and bar
of out
are all the same object in memory.
2. By-name function parameters
Functionally the same as (1) but with more subtle syntax, you can use Scala by-name function parameters:
class UsingByNameParameters[T <: Data](gen: => T) extends Bundle {
val foo = gen
val bar = gen
}
With this usage, you do not include () =>
when passing the argument:
chisel3.docs.emitSystemVerilog(new Top(new UsingByNameParameters(UInt(8.W))))
Note that as this is just syntactic sugar over (1), the same warning applies.
3. Directioned Bundle fields
You can alternatively wrap the fields with Output(...)
, which creates fresh instances of the passed argument.
Chisel treats Output
as the "default direction" so if all fields are outputs, the Bundle
is functionally equivalent to a Bundle
with no directioned fields.
class DirectionedBundle[T <: Data](gen: T) extends Bundle {
val foo = Output(gen)
val bar = Output(gen)
}
This approach is admittedly a little ugly and may mislead others reading the code because it implies that this Bundle is intended to be used as an Output
.
4. Call .cloneType
directly
You can also just call .cloneType
on your gen
argument directly.
While we try to hide this implementation detail from the user, .cloneType
is the mechanism by which Chisel creates fresh instances of Data
objects:
class UsingCloneTypeBundle[T <: Data](gen: T) extends Bundle {
val foo = gen.cloneType
val bar = gen.cloneType
}
How do I deal with the "unable to clone" error?
Most Chisel objects need to be cloned in order to differentiate between the software representation of the bundle field from its "bound" hardware representation, where "binding" is the process of generating a hardware component. For Bundle fields, this cloning is supposed to happen automatically with a compiler plugin.
In some cases though, the plugin may not be able to clone the Bundle fields. The
most common case for when this happens is when the chisel3.Data
part of the
Bundle field is nested inside some other data structure and the compiler plugin
is unable to figure out how to clone the entire structure. It is best to avoid
such nested structures.
There are a few ways around this issue - you can try wrapping the problematic
fields in Input(...), Output(...), or Flipped(...) if appropriate. You can also
try manually cloning each field in the Bundle using the chiselTypeClone
method
in chisel3.reflect.DataMirror
. Here's an example with the Bundle whose fields
won't get cloned:
class CustomBundleBroken(elts: (String, Data)*) extends Record {
val elements = ListMap(elts: _*)
def apply(elt: String): Data = elements(elt)
}
class NewModule extends Module {
val out = Output(UInt(8.W))
val recordType = new CustomBundleBroken("fizz" -> UInt(16.W), "buzz" -> UInt(16.W))
val record = Wire(recordType)
val uint = record.asUInt
val record2 = uint.asTypeOf(recordType)
out := record
}
getVerilogString(new NewModule)
// chisel3.AutoClonetypeException: The bundle plugin was unable to clone CustomBundleBroken$1 that has field 'fizz' aliased with base CustomBundleBroken$1.This likely happened because you tried nesting Data arguments inside of other data structures. Try wrapping the field(s) in Input(...), Output(...), or Flipped(...) if appropriate. As a last resort, you can call chisel3.reflect.DataMirror.internal.chiselTypeClone on any nested Data arguments. See the cookbook entry 'How do I deal with the "unable to clone" error?' for more details.
// at ... ()
// at repl.MdocSession$MdocApp17$$anonfun$70$NewModule$1$$anonfun$74$$anonfun$apply$63.apply(cookbook.md:441)
// at repl.MdocSession$MdocApp17$$anonfun$70$NewModule$1$$anonfun$74$$anonfun$apply$63.apply(cookbook.md:441)
// at chisel3.experimental.prefix$.apply(prefix.scala:50)
// at repl.MdocSession$MdocApp17$$anonfun$70$NewModule$1$$anonfun$74.apply(cookbook.md:441)
// at repl.MdocSession$MdocApp17$$anonfun$70$NewModule$1$$anonfun$74.apply(cookbook.md)
// at chisel3.internal.plugin.package$.autoNameRecursively(package.scala:33)
// at repl.MdocSession$MdocApp17$$anonfun$70$NewModule$1.<init>(cookbook.md:441)
// at repl.MdocSession$MdocApp17$$anonfun$70$$anonfun$apply$67.apply(cookbook.md:446)
// at repl.MdocSession$MdocApp17$$anonfun$70$$anonfun$apply$67.apply(cookbook.md:446)
// at chisel3.ObjectModuleImpl.evaluate(ModuleImpl.scala:84)
// at chisel3.ObjectModuleImpl.evaluate$(ModuleImpl.scala:52)
// at chisel3.Module$.evaluate(Module.scala:10)
// at chisel3.ObjectModuleImpl._applyImpl(ModuleImpl.scala:25)
// at chisel3.ObjectModuleImpl._applyImpl$(ModuleImpl.scala:23)
// at chisel3.Module$._applyImpl(Module.scala:10)
// at chisel3.Module$.do_apply(Module.scala:22)
// at chisel3.stage.phases.Elaborate.$anonfun$transform$2(Elaborate.scala:54)
// at chisel3.internal.Builder$.$anonfun$buildImpl$1(Builder.scala:1048)
// at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
// at chisel3.internal.Builder$.buildImpl(Builder.scala:1038)
// at chisel3.internal.Builder$.$anonfun$build$1(Builder.scala:1030)
// at logger.Logger$.$anonfun$makeScope$4(Logger.scala:148)
// at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
// at logger.Logger$.makeScope(Logger.scala:146)
// at logger.Logger$.makeScope(Logger.scala:133)
// at ... ()
// at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)
You can use chiselTypeClone
to clone the elements as:
import chisel3.reflect.DataMirror
import chisel3.experimental.requireIsChiselType
class CustomBundleFixed(elts: (String, Data)*) extends Record {
val elements = ListMap(elts.map {
case (field, elt) =>
requireIsChiselType(elt)
field -> DataMirror.internal.chiselTypeClone(elt)
}: _*)
def apply(elt: String): Data = elements(elt)
}
How do I create a finite state machine (FSM)?
The advised way is to use ChiselEnum
to construct enumerated types representing the state of the FSM.
State transitions are then handled with switch
/is
and when
/.elsewhen
/.otherwise
.
import chisel3._
import chisel3.util.{switch, is}
object DetectTwoOnes {
object State extends ChiselEnum {
val sNone, sOne1, sTwo1s = Value
}
}
/* This FSM detects two 1's one after the other */
class DetectTwoOnes extends Module {
import DetectTwoOnes.State
import DetectTwoOnes.State._
val io = IO(new Bundle {
val in = Input(Bool())
val out = Output(Bool())
val state = Output(State())
})
val state = RegInit(sNone)
io.out := (state === sTwo1s)
io.state := state
switch (state) {
is (sNone) {
when (io.in) {
state := sOne1
}
}
is (sOne1) {
when (io.in) {
state := sTwo1s
} .otherwise {
state := sNone
}
}
is (sTwo1s) {
when (!io.in) {
state := sNone
}
}
}
}
Note: the is
statement can take multiple conditions e.g. is (sTwo1s, sOne1) { ... }
.
How do I unpack a value ("reverse concatenation") like in Verilog?
In Verilog, you can do something like the following which will unpack a the value z
:
wire [1:0] a;
wire [3:0] b;
wire [2:0] c;
wire [8:0] z = [...];
assign {a,b,c} = z;
Unpacking often corresponds to reinterpreting an unstructured data type as a structured data type. Frequently, this structured type is used prolifically in the design, and has been declared as in the following example:
import chisel3._
class MyBundle extends Bundle {
val a = UInt(2.W)
val b = UInt(4.W)
val c = UInt(3.W)
}
The easiest way to accomplish this in Chisel would be:
class Foo extends Module {
val z = Wire(UInt(9.W))
z := DontCare // This is a dummy connection
val unpacked = z.asTypeOf(new MyBundle)
printf("%d", unpacked.a)
printf("%d", unpacked.b)
printf("%d", unpacked.c)
}
If you really need to do this for a one-off case (Think thrice! It is likely you can better structure the code using bundles), then rocket-chip has a Split utility which can accomplish this.
How do I do subword assignment (assign to some bits in a UInt)?
You may try to do something like the following where you want to assign only some bits of a Chisel type.
Below, the left-hand side connection to io.out(0)
is not allowed.
import chisel3._
class Foo extends Module {
val io = IO(new Bundle {
val bit = Input(Bool())
val out = Output(UInt(10.W))
})
io.out(0) := io.bit
}
If you try to compile this, you will get an error.
getVerilogString(new Foo)
// chisel3.package$ChiselException: Cannot reassign to read-only Foo.?: OpResult[Bool]
// at ... ()
// at repl.MdocSession$MdocApp26$Foo.<init>(cookbook.md:583)
// at repl.MdocSession$MdocApp26$$anonfun$104$$anonfun$apply$90.apply(cookbook.md:591)
// at repl.MdocSession$MdocApp26$$anonfun$104$$anonfun$apply$90.apply(cookbook.md:591)
// at chisel3.ObjectModuleImpl.evaluate(ModuleImpl.scala:84)
// at chisel3.ObjectModuleImpl.evaluate$(ModuleImpl.scala:52)
// at chisel3.Module$.evaluate(Module.scala:10)
// at chisel3.ObjectModuleImpl._applyImpl(ModuleImpl.scala:25)
// at chisel3.ObjectModuleImpl._applyImpl$(ModuleImpl.scala:23)
// at chisel3.Module$._applyImpl(Module.scala:10)
// at chisel3.Module$.do_apply(Module.scala:22)
// at chisel3.stage.phases.Elaborate.$anonfun$transform$2(Elaborate.scala:54)
// at chisel3.internal.Builder$.$anonfun$buildImpl$1(Builder.scala:1048)
// at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
// at chisel3.internal.Builder$.buildImpl(Builder.scala:1038)
// at chisel3.internal.Builder$.$anonfun$build$1(Builder.scala:1030)
// at logger.Logger$.$anonfun$makeScope$4(Logger.scala:148)
// at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
// at logger.Logger$.makeScope(Logger.scala:146)
// at logger.Logger$.makeScope(Logger.scala:133)
// at ... ()
// at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)
Chisel3 does not support subword assignment.
The reason for this is that subword assignment generally hints at a better abstraction with an aggregate/structured types, i.e., a Bundle
or a Vec
.
If you must express it this way, one approach is to blast your UInt
to a Vec
of Bool
and back:
import chisel3._
class Foo extends Module {
val io = IO(new Bundle {
val in = Input(UInt(10.W))
val bit = Input(Bool())
val out = Output(UInt(10.W))
})
val bools = VecInit(io.in.asBools)
bools(0) := io.bit
io.out := bools.asUInt
}
How do I create an optional I/O?
The following example is a module which includes the optional port out2
only if the given parameter is true
.
import chisel3._
class ModuleWithOptionalIOs(flag: Boolean) extends Module {
val io = IO(new Bundle {
val in = Input(UInt(12.W))
val out = Output(UInt(12.W))
val out2 = if (flag) Some(Output(UInt(12.W))) else None
})
io.out := io.in
if (flag) {
io.out2.get := io.in
}
}
The following is an example where an entire IO
is optional:
import chisel3._
class ModuleWithOptionalIO(flag: Boolean) extends Module {
val in = if (flag) Some(IO(Input(Bool()))) else None
val out = IO(Output(Bool()))
out := in.getOrElse(false.B)
}
How do I create I/O without a prefix?
In most cases, you can simply call IO
multiple times:
import chisel3._
class MyModule extends Module {
val in = IO(Input(UInt(8.W)))
val out = IO(Output(UInt(8.W)))
out := in +% 1.U
}
// Generated by CIRCT firtool-1.87.0
module MyModule(
input clock,
reset,
input [7:0] in,
output [7:0] out
);
assign out = in + 8'h1;
endmodule
If you have a Bundle
from which you would like to create ports without the
normal val
prefix, you can use FlatIO
:
import chisel3._
class MyBundle extends Bundle {
val foo = Input(UInt(8.W))
val bar = Output(UInt(8.W))
}
class MyModule extends Module {
val io = FlatIO(new MyBundle)
io.bar := io.foo +% 1.U
}
Note that io_
is nowhere to be seen!
// Generated by CIRCT firtool-1.87.0
module MyModule(
input clock,
reset,
input [7:0] foo,
output [7:0] bar
);
assign bar = foo + 8'h1;
endmodule
How do I override the implicit clock or reset within a Module?
To change the clock or reset for a region of code, use withClock
, withReset
, or withClockAndReset
.
See Multiple Clock Domains for examples and details.
To override the clock or reset for the entire scope of the Module
, you can mixin the ImplicitClock
and ImplicitReset
traits.
For example, you could "gate" the default implicit clock as follows:
import chisel3._
class MyModule extends Module with ImplicitClock {
val gate = IO(Input(Bool()))
val in = IO(Input(UInt(8.W)))
val out = IO(Output(UInt(8.W)))
// We could just assign this to val implicitClock, but this allows us to give it a custom name
val gatedClock = (clock.asBool || gate).asClock
// The trait requires us to implement this method referring to the clock
// Note that this is a def, but the actual clock value must be assigned to a val
override protected def implicitClock = gatedClock
val r = Reg(UInt(8.W))
out := r
r := in
}
This gives the following Verilog:
// Generated by CIRCT firtool-1.87.0
module MyModule(
input clock,
reset,
gate,
input [7:0] in,
output [7:0] out
);
wire gatedClock = clock | gate;
reg [7:0] r;
always @(posedge gatedClock)
r <= in;
assign out = r;
endmodule
If you do not care about the name of the overriden clock, you can just assign it to val implicitClock
:
override protected val implicitClock = (clock.asBool || gate).asClock
ImplicitReset
works analogously to ImplicitClock
.
How do I minimize the number of bits used in an output vector?
Use inferred width and a Seq
instead of a Vec
:
Consider:
import chisel3._
// Count the number of set bits up to and including each bit position
class CountBits(width: Int) extends Module {
val bits = IO(Input(UInt(width.W)))
val countVector = IO(Output(Vec(width, UInt())))
private val countSequence = Seq.tabulate(width)(i => Wire(UInt()))
countSequence.zipWithIndex.foreach { case (port, i) =>
port := util.PopCount(bits(i, 0))
}
countVector := countSequence
}
class Top(width: Int) extends Module {
val countBits = Module(new CountBits(width))
countBits.bits :<>= DontCare
dontTouch(countBits.bits)
dontTouch(countBits.countVector)
}
Note that top modules or public modules cannot have unknown widths.
Unlike Vecs
which represent a singular Chisel type and must have the same width for every element,
Seq
is a purely Scala construct, so their elements are independent from the perspective of Chisel and can have different widths.
// Generated by CIRCT firtool-1.87.0
module CountBits(
input [3:0] bits,
output [2:0] countVector_0,
countVector_1,
countVector_2,
countVector_3
);
How do I resolve "Dynamic index ... is too wide/narrow for extractee ..."?
Chisel will warn if a dynamic index is not the correctly-sized width for indexing a Vec or UInt. "Correctly-sized" means that the width of the index should be the log2 of the size of the indexee. If the indexee is a non-power-of-2 size, use the ceiling of the log2 result.
When the index does not have enough bits to address all entries or bits in the extractee, you can .pad
the index to increase the width.
class TooNarrow extends RawModule {
val extractee = Wire(UInt(7.W))
val index = Wire(UInt(2.W))
extractee(index)
}
compile(new TooNarrow)
// [warn] cookbook.md 816:12: [W003] Dynamic index with width 2 is too small for extractee of width 7
// [warn] There were 1 warning(s) during hardware elaboration.
This can be fixed with pad
:
class TooNarrowFixed extends RawModule {
val extractee = Wire(UInt(7.W))
val index = Wire(UInt(2.W))
extractee(index.pad(3))
}
compile(new TooNarrowFixed)
Use bit extraction when the index is too wide
class TooWide extends RawModule {
val extractee = Wire(Vec(8, UInt(32.W)))
val index = Wire(UInt(4.W))
extractee(index)
}
compile(new TooWide)
// [warn] cookbook.md 842:12: [W004] Dynamic index with width 4 is too wide for Vec of size 8 (expected index width 3).
// [warn] There were 1 warning(s) during hardware elaboration.
This can be fixed with bit extraction:
class TooWideFixed extends RawModule {
val extractee = Wire(Vec(8, UInt(32.W)))
val index = Wire(UInt(4.W))
extractee(index(2, 0))
}
compile(new TooWideFixed)
Note that size 1 Vecs
and UInts
should be indexed by a zero-width UInt
:
class SizeOneVec extends RawModule {
val extractee = Wire(Vec(1, UInt(32.W)))
val index = Wire(UInt(0.W))
extractee(index)
}
compile(new SizeOneVec)
Because pad
only pads if the desired width is less than the current width of the argument,
you can use pad
in conjunction with bit extraction when the widths may be too wide or too
narrow under different circumstances
import chisel3.util.log2Ceil
class TooWideOrNarrow(extracteeSize: Int, indexWidth: Int) extends Module {
val extractee = Wire(Vec(extracteeSize, UInt(8.W)))
val index = Wire(UInt(indexWidth.W))
val correctWidth = log2Ceil(extracteeSize)
extractee(index.pad(correctWidth)(correctWidth - 1, 0))
}
compile(new TooWideOrNarrow(8, 2))
compile(new TooWideOrNarrow(8, 4))
Another option for dynamic bit selection of UInts
(but not Vec
dynamic indexing) is to do a dynamic
right shift of the extractee by the index and then just bit select a single bit:
class TooWideOrNarrowUInt(extracteeSize: Int, indexWidth: Int) extends Module {
val extractee = Wire(UInt(extracteeSize.W))
val index = Wire(UInt(indexWidth.W))
(extractee >> index)(0)
}
compile(new TooWideOrNarrowUInt(8, 2))
compile(new TooWideOrNarrowUInt(8, 4))
Predictable Naming
How do I get Chisel to name signals properly in blocks like when/withClockAndReset?
Use the compiler plugin, and check out the Naming Cookbook if that still does not do what you want.
How do I get Chisel to name the results of vector reads properly?
Currently, name information is lost when using dynamic indexing. For example:
import chisel3._
class Foo extends Module {
val io = IO(new Bundle {
val in = Input(Vec(4, Bool()))
val idx = Input(UInt(2.W))
val en = Input(Bool())
val out = Output(Bool())
})
val x = io.in(io.idx)
val y = x && io.en
io.out := y
}
The above code loses the x
name, instead using _GEN_3
(the other _GEN_*
signals are expected).
// Generated by CIRCT firtool-1.87.0
module Foo(
input clock,
reset,
io_in_0,
io_in_1,
io_in_2,
io_in_3,
input [1:0] io_idx,
input io_en,
output io_out
);
wire [3:0] _GEN = {{io_in_3}, {io_in_2}, {io_in_1}, {io_in_0}};
assign io_out = _GEN[io_idx] & io_en;
endmodule
This can be worked around by creating a wire and connecting the dynamic index to the wire:
val x = WireInit(io.in(io.idx))
Which produces:
// Generated by CIRCT firtool-1.87.0
module Foo2(
input clock,
reset,
io_in_0,
io_in_1,
io_in_2,
io_in_3,
input [1:0] io_idx,
input io_en,
output io_out
);
wire [3:0] _GEN = {{io_in_3}, {io_in_2}, {io_in_1}, {io_in_0}};
assign io_out = _GEN[io_idx] & io_en;
endmodule
How can I dynamically set/parametrize the name of a module?
You can override the desiredName
function. This works with normal Chisel modules and BlackBox
es. Example:
import chisel3._
class Coffee extends BlackBox {
val io = IO(new Bundle {
val I = Input(UInt(32.W))
val O = Output(UInt(32.W))
})
override def desiredName = "Tea"
}
class Salt extends Module {
val io = IO(new Bundle {})
val drink = Module(new Coffee)
override def desiredName = "SodiumMonochloride"
drink.io.I := 42.U
}
Elaborating the Chisel module Salt
yields our "desired names" for Salt
and Coffee
in the output Verilog:
// Generated by CIRCT firtool-1.87.0
module SodiumMonochloride(
input clock,
reset
);
Tea drink (
.I (32'h2A),
.O (/* unused */)
);
endmodule
Directionality
How do I strip directions from a bidirectional Bundle (or other Data)?
Given a bidirectional port like a Decoupled
, you will get an error if you try to connect it directly
to a register:
import chisel3._
import chisel3.util.Decoupled
class BadRegConnect extends Module {
val io = IO(new Bundle {
val enq = Decoupled(UInt(8.W))
})
val monitor = Reg(chiselTypeOf(io.enq))
monitor := io.enq
}
getVerilogString(new BadRegConnect)
// circt.stage.phases.Exceptions$FirtoolNonZeroExitCode: /home/runner/.cache/llvm-firtool/1.87.0/bin/firtool returned a non-zero exit code. Note that this version of Chisel (7.0.0-M2+141-75e8fbe0-SNAPSHOT) was published against firtool version 1.87.0.
// ------------------------------------------------------------------------------
// ExitCode:
// 1
// STDOUT:
//
// STDERR:
// cookbook.md:1013:20: error: 'firrtl.reg' op result #0 must be a passive non-'const' base type that does not contain analog, but got '!firrtl.bundle<ready flip: uint<1>, valid: uint<1>, bits: uint<8>>'
// cookbook.md:1013:20: note: see current operation: %4 = "firrtl.reg"(%arg0) {annotations = [], name = "monitor", nameKind = #firrtl<name_kind interesting_name>} : (!firrtl.clock) -> !firrtl.bundle<ready flip: uint<1>, valid: uint<1>, bits: uint<8>>
//
// ------------------------------------------------------------------------------
While there is no construct to "strip direction" in Chisel3, wrapping a type in Output(...)
(the default direction in Chisel3) will
set all of the individual elements to output direction.
This will have the desired result when used to construct a Register:
import chisel3._
import chisel3.util.Decoupled
class CoercedRegConnect extends Module {
val io = IO(new Bundle {
val enq = Flipped(Decoupled(UInt(8.W)))
})
// Make a Reg which contains all of the bundle's signals, regardless of their directionality
val monitor = Reg(Output(chiselTypeOf(io.enq)))
// Even though io.enq is bidirectional, := will drive all fields of monitor with the fields of io.enq
monitor := io.enq
}