// concatenates 2 (b10) with 1 (b1) to give 5 (101) io.out_cat := Cat(2.U, 1.U) // assign io_out_cat = 4'h5 }
More operators
Example
Arbiter
The following circuit arbitrates data coming from a FIFO into two parallel processing units. The FIFO and processing elements (PEs) communicate with ready-valid interfaces. Construct the arbiter to send data to whichever PE is ready to receive data, prioritizing PE0 if both are ready to receive data. Remember that the arbiter should tell the FIFO that it’s ready to receive data when at least one of the PEs can receive data. Also, wait for a PE to assert that it’s ready before asserting that the data are valid. You will likely need binary operators to complete this exercise.
classArbiterextendsModule{ val io = IO(newBundle { // FIFO val fifo_valid = Input(Bool()) val fifo_ready = Output(Bool()) val fifo_data = Input(UInt(16.W))
// PE0 val pe0_valid = Output(Bool()) val pe0_ready = Input(Bool()) val pe0_data = Output(UInt(16.W))
// PE1 val pe1_valid = Output(Bool()) val pe1_ready = Input(Bool()) val pe1_data = Output(UInt(16.W)) })
This optional exercise exposes you to one of the most powerful features of Chisel, it’s parameterization capabilities. To demonstrate this, we’ll construct a parameterized adder that can either saturate the output when overflow occurs, or truncate the results (i.e. wrap around).
First, look at the Module below. The parameter we pass into it is called saturate and has type ScalaBoolean. This is not a Chisel Bool. So, we’re not creating a single hardware adder that can either saturate or truncate, but rather we’re creating a generator that produces either a saturating hardware adder or a truncating hardware adder. The decision is made at compile time.
Next, notice the inputs and outputs are all 4-bit UInts. Chisel has built-in width inferencing, and if you look at the cheatsheet, you’ll see that the bitwidth of a normal summation is equal to the maximum bitwidth of the two inputs. This means that
1
val sum = io.in_a + io.in_b
will make sum a 4-bit wire, and the value will be the truncated result for 4-bit inputs. To check if the summation should saturate, you need to place the result in a 5-bit wire. This can be done with the +& summation, as seen on the cheatsheet.
1
val sum = io.in_a +& io.in_b
Finally, note that connecting a 4-bit UInt wire to a 5-bit UInt wire will truncate the MSB by default. You can use this to easily truncate the 5-bit sum for the non-saturating adder.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classParameterizedAdder(saturate: Boolean) extendsModule{ val io = IO(newBundle { val in_a = Input(UInt(4.W)) val in_b = Input(UInt(4.W)) val out = Output(UInt(4.W)) })
val sum = io.in_a +& io.in_b if (saturate) { io.out := Mux(sum > 15.U, 15.U, sum) } else { io.out := sum } }