Skip to content
code0.HDL

Bool, UInt, and the operators

Tell a one-bit Bool from a multi-bit UInt, apply the bitwise, bit-field, Cat and reduction operators to an 8-bit value by hand, run the operator example, see what shifts, arithmetic and comparisons build and how wide their results are, then build a parity frame.

In this chapter

  • Read an expression as a graph of operator nodes and say what a val adds to it
  • Apply the bitwise, logical, comparison, bit-field, Cat, Fill and reduction operators to an 8-bit value by hand, with the width of each result
  • State the hardware and result width of each arithmetic, shift and comparison operator, and run them to see wrapping, underflow, arithmetic shift and signed comparison
  • Write === for a comparator and parenthesize a comparison mixed with &, ^ or |, or a shift mixed with a comparison
  • Assemble a frame from a reduction and Cat with the parity bit in the right position
Concepts in this chapter
  • Bool, UInt, bit operators
Prerequisite concepts

1. Bool is one bit; UInt is a row of bits

Every port and wire in Chisel carries a value of a Chisel type. The two types this chapter needs are Bool(), a single bit whose literals are true.B and false.B, and UInt(n.W), an unsigned number n bits wide whose literals are written 5.U or "hc5".U. A Bool is also a 1-bit UInt, so it can drive a UInt sink. Both types describe hardware signals whose values are modeled during simulation; the Scala Boolean and Int that steer elaboration are different things, as the first chapter showed.

TypeWidthLiteralsTypical use
Bool()1true.B, false.Ba condition, a flag, a single wire
UInt(8.W)8197.U, "hc5".U, "b1100_0101".Udata, counts, addresses

Widths, the signed type SInt, and the width every literal gets are the subject of Widths, SInt, and arithmetic. This chapter needs only the fact that an 8-bit UInt is eight wires, numbered 7 down to 0.

2. Expressions build circuits

A combinational circuit computes its outputs from its current inputs alone, with no memory of earlier cycles. In Chisel you describe it with expressions and connections, and elaboration turns each operator into a node of the circuit graph. (a & b) | (~c & d) is a tree: input wires at the leaves, operators inside, and the final | at the root. During elaboration, each operator call adds one node to the circuit graph and returns that node's output.

val t = io.a & io.b        // one AND node
io.x := t | io.c           // t feeds an OR node
io.y := t ^ io.d           // t also feeds an XOR node
CodeWhat it builds
io.a & io.bOne AND node
val t = ...No hardware; a Scala name for the AND node's output
t | io.c and t ^ io.dTwo more nodes that share t as an input

A val lets one node feed several later expressions. That sharing, called fan-out, turns a tree into a general directed acyclic graph. During simulation the whole graph is recomputed whenever an input changes.

3. Operators

Every operator below is a piece of hardware with a fixed result width. The width comes from the operand widths alone, during elaboration: not from the values, and not from the port the result is later connected to. Each subsection gives the hardware, the width, and what to write when the obvious spelling does something else. Section 3.5 runs most of them at once.

3.1 Bitwise and logical operators

A bitwise operator builds one gate per bit position: x & y on two 8-bit values is eight AND gates side by side, and bit 3 of the result depends only on bit 3 of each operand. A logical operator treats each operand as one truth value.

Syntax

~x       x & y     x | y     x ^ y      // bitwise
!b       b && c    b || c               // logical
val enable = io.valid && !io.stall   // Bool
val masked = io.data & "h0f".U       // keeps the low 4 bits of an 8-bit value
val flipped = ~io.data               // inverts all 8 bits
OperatorHardwareOperandsResult width
~xOne inverter per bitUInt, SInt, Boolw(x); on a Bool, a Bool
x & y, x | y, x ^ yOne gate per bit positionTwo UInt, two SInt, or Boolmax(w(x), w(y))
b && c, b || cOne AND or OR gateBool only1
!xAn inverter on a Bool; on a UInt, a test for zeroBool, UInt1

Operands of different widths. Before a bitwise operator combines two operands, the narrower one is widened to the width of the wider one. A UInt gets zeros on top, so in io.data & "h0f".U the 4-bit literal 1111 becomes 0000_1111 and clears the high nibble of data. An SInt gets copies of its sign bit: OR-ing a 4-bit -1 (1111) into an 8-bit 0 gives 1111_1111, not 0000_1111.

! and && on a UInt. && and || exist only on Bool. With two UInt operands the Scala compiler stops with value && is not a member of chisel3.UInt. ! is looser: on a UInt it compiles and means "is zero", true only when every bit is 0. Say which test you mean instead:

val isZero  = io.count === 0.U    // what !io.count computes
val nonZero = io.count =/= 0.U    // the same as io.count.orR: some bit is 1

On a Bool, ~b and !b build the same inverter and both return a Bool. Use ! in conditions so they read as logic.

3.2 Arithmetic

Arithmetic operators build adders, subtractors, multipliers, and dividers.

Syntax

x + y    x - y    x +% y   x -% y    // wrapping: max(w(x), w(y)) bits
x +& y   x -& y                      // expanding: one bit more
x * y    x / y    x % y
OperatorHardwareResult widthWhat happens to a result that does not fit
x + y, x - yAdder, subtractormax(w(x), w(y))The carry or borrow out is dropped: the result wraps
x +% y, x -% yThe same circuitmax(w(x), w(y))Wraps. The % spells out that wrapping is intended
x +& y, x -& yAdder or subtractor with its carry outmax(w(x), w(y)) + 1Kept in the extra bit
x * yMultiplierw(x) + w(y)Cannot happen: every product fits
x / yDividerw(x) for UInt, w(x) + 1 for SIntCannot happen
x % yDivider, remainder outputmin(w(x), w(y))Cannot happen

Wrapping. 200.U(8.W) + 100.U(8.W) is 44. The true sum, 300, needs nine bits, and + keeps eight. +& keeps the ninth and gives 300. A wider port on the left of := does not bring the carry back, because the 8-bit sum already exists before the connection sees it.

Unsigned underflow. A UInt has no negative values, so 0.U(4.W) - 1.U is not -1: the subtraction wraps to 1111, which is 15. -& gives five bits, 1_1111 = 31. The extra bit is a borrow, not a sign; the result is still unsigned. A negative result needs SInt operands, the topic of the next chapter.

Signed division is one bit wider because -128 / -1 is +128, which does not fit in eight signed bits.

*, / and % are large circuits. Each is one character in the source, but a multiplier is an array of partial-product adders, and a divider whose divisor is a signal is far larger and slower than an adder. Use them where the design needs them, not as shorthand. For an unsigned x, a constant power-of-two divisor needs no divider: x / 4.U has the same value as x >> 2, which is only wiring, so write the shift. A divisor such as 3 has no shift equivalent.

The next chapter collects these widths in one table and runs + against +& on its own.

3.3 Shifts

The type of the shift amount decides what hardware a shift becomes.

Syntax

x << 3    x >> 3     // Scala Int amount, fixed during elaboration
x << n    x >> n     // UInt amount, chosen during simulation
Expression, x is 8 bits, n is UInt(3.W)HardwareResult width
x << 3Wiring: every bit moves up 3 places, zeros fill the bottomw(x) + 3 = 11
x >> 3Wiring: the low 3 bits are droppedw(x) - 3 = 5
x << nA shifter: one multiplexer per output bitw(x) + 2^w(n) - 1 = 15
x >> nA shifterw(x) = 8

A shift never drops a high bit on its own: << widens its result instead. If you need the result in the original width, select the bits you want, as in (x << 3)(7, 0). A shift right by a Scala Int removes bits; in Chisel 6.7, shifting an 8-bit value right by 8 or more leaves a result 0 bits wide.

A shift by a UInt has to make room for the largest amount the signal can carry: 7 for a 3-bit n, so 8 + 7 = 15 bits. A 4-bit n raises that to 8 + 15 = 23 bits. Keep a shift amount as narrow as its largest real value.

>> on an SInt is an arithmetic shift: the vacated high bits are filled with copies of the sign bit, so a negative value stays negative. The bits 1100_0101 are 197 as a UInt and -59 as an SInt; shifted right by 2 they become 49 and -15. << fills with zeros on both types. In the example below, x.asSInt reads the same 8 bits of a UInt as an SInt; casts are covered in the next chapter.

x.rotateLeft(k) and x.rotateRight(k) rotate a UInt: the bits pushed out at one end come back in at the other, and the width stays w(x).

3.4 Comparisons

A comparison builds a comparator and always produces a Bool, whatever the operand widths.

Syntax

x === y   x =/= y   x < y   x <= y   x > y   x >= y
val isZero   = io.count === 0.U
val notThree = io.x =/= 3.U
val inRange  = io.temp >= 20.U && io.temp < 30.U

Equality is written === and inequality =/=, with three characters. Scala reserves == for comparing Scala objects: io.x == io.y asks whether the two port objects are the same object during elaboration, and returns a Scala Boolean that cannot drive hardware.

Signed and unsigned. The type decides how the bits are compared. 1111_1000 against 0000_1111 is 248 < 15, false, as two UInt, and -8 < 15, true, as two SInt. Chisel does not compare a UInt with an SInt: io.u < io.s stops the Scala compiler with type mismatch; found: chisel3.SInt, required: chisel3.UInt. Convert one side first, for example io.u.zext < io.s, so the code says which reading you want.

3.5 Run it: shifts, arithmetic, and comparison

ShiftArith.scala computes the operators from 3.2 to 3.4 on two 8-bit inputs x and y and a 3-bit shift amount n. During elaboration it prints the width of each result on two WIDTH lines. The testbench then pokes four input sets and prints one labelled SIMULATE line per operator group.

ShiftArith.scala
import chisel3._

class ShiftArith extends Module {
  val io = IO(new Bundle {
    val x        = Input(UInt(8.W))
    val y        = Input(UInt(8.W))
    val n        = Input(UInt(3.W))
    val shlConst = Output(UInt(11.W))
    val shrConst = Output(UInt(5.W))
    val shlDyn   = Output(UInt(15.W))
    val shrDyn   = Output(UInt(8.W))
    val sraConst = Output(SInt(6.W))
    val sraDyn   = Output(SInt(8.W))
    val sum      = Output(UInt(8.W))
    val sumWide  = Output(UInt(9.W))
    val diff     = Output(UInt(8.W))
    val diffWide = Output(UInt(9.W))
    val prod     = Output(UInt(16.W))
    val quot     = Output(UInt(8.W))
    val rem      = Output(UInt(8.W))
    val quarter  = Output(UInt(8.W))
    val ltU      = Output(Bool())
    val ltS      = Output(Bool())
  })

  // A Scala Int shift amount is fixed during elaboration: only wiring.
  val shlConst = io.x << 3
  val shrConst = io.x >> 3
  // A UInt shift amount builds a shifter; << leaves room for the largest amount.
  val shlDyn = io.x << io.n
  val shrDyn = io.x >> io.n
  // >> on an SInt copies the sign bit into the vacated high bits.
  val sraConst = io.x.asSInt >> 2
  val sraDyn   = io.x.asSInt >> io.n
  // + and - wrap at the wider operand's width; +& and -& keep one more bit.
  val sum      = io.x + io.y
  val sumWide  = io.x +& io.y
  val diff     = io.x - io.y
  val diffWide = io.x -& io.y
  // * keeps every product bit; / and % build a divider.
  val prod = io.x * io.y
  val quot = io.x / io.y
  val rem  = io.x % io.y
  val quarter = io.x / 4.U
  // The same bits compare differently as UInt and as SInt.
  val ltU = io.x < io.y
  val ltS = io.x.asSInt < io.y.asSInt

  println(s"WIDTH x<<3=${shlConst.getWidth} x>>3=${shrConst.getWidth} x<<n=${shlDyn.getWidth} " +
    s"x>>n=${shrDyn.getWidth} s>>2=${sraConst.getWidth} s>>n=${sraDyn.getWidth}")
  println(s"WIDTH x+y=${sum.getWidth} x+&y=${sumWide.getWidth} x-y=${diff.getWidth} x-&y=${diffWide.getWidth} " +
    s"x*y=${prod.getWidth} x/y=${quot.getWidth} x%y=${rem.getWidth} x<y=${ltU.getWidth}")

  io.shlConst := shlConst
  io.shrConst := shrConst
  io.shlDyn   := shlDyn
  io.shrDyn   := shrDyn
  io.sraConst := sraConst
  io.sraDyn   := sraDyn
  io.sum      := sum
  io.sumWide  := sumWide
  io.diff     := diff
  io.diffWide := diffWide
  io.prod     := prod
  io.quot     := quot
  io.rem      := rem
  io.quarter  := quarter
  io.ltU      := ltU
  io.ltS      := ltS
}
Tb.scala
import chisel3._
import chiseltest._
import chiseltest.RawTester.test

object Main extends App {
  test(new ShiftArith) { dut =>
    def set(x: Int, y: Int, n: Int): Unit = {
      dut.io.x.poke(x.U)
      dut.io.y.poke(y.U)
      dut.io.n.poke(n.U)
    }
    def v(p: Data) = p.peek().litValue

    set(0xc5, 3, 2)
    println(s"SIMULATE shift x=197 n=2 x<<3=${v(dut.io.shlConst)} x>>3=${v(dut.io.shrConst)} " +
      s"x<<n=${v(dut.io.shlDyn)} x>>n=${v(dut.io.shrDyn)}")
    println(s"SIMULATE sint x.asSInt=-59 n=2 s>>2=${v(dut.io.sraConst)} s>>n=${v(dut.io.sraDyn)}")
    println(s"SIMULATE divide x=197 y=3 x/y=${v(dut.io.quot)} x%y=${v(dut.io.rem)} " +
      s"x/4=${v(dut.io.quarter)} x>>n=${v(dut.io.shrDyn)}")

    set(200, 100, 0)
    println(s"SIMULATE add x=200 y=100 x+y=${v(dut.io.sum)} x+&y=${v(dut.io.sumWide)} x*y=${v(dut.io.prod)}")

    set(0, 1, 0)
    println(s"SIMULATE sub x=0 y=1 x-y=${v(dut.io.diff)} x-&y=${v(dut.io.diffWide)}")

    set(0xf8, 0x0f, 0)
    println(s"SIMULATE compare x=f8 y=0f x<y=${v(dut.io.ltU)} x.asSInt<y.asSInt=${v(dut.io.ltS)}")
  }
}

Check

The sub line pokes x = 0 and y = 1 into the 8-bit UInt inputs. What will x-y show?

Your answer

What to look at in the output. Press Run and read the lines against this table.

LineLook atWhat it shows
First WIDTHx<<3=11, x<<n=15, x>>n=8A constant << grows by the amount; a UInt amount grows by 2^w(n) - 1; >> by a UInt keeps w(x)
Second WIDTHx+y=8, x+&y=9, x*y=16, x<y=1Widths depend on operand widths only
shiftx<<3=1576, x<<n=788The constant shift is fixed; the UInt amount, 2 here, is picked during simulation
sints>>2=-15 beside x>>n=49 on the shift lineThe same bits shifted right as SInt and as UInt: the sign bit is copied
dividex/y=65, x%y=2, x/4=49, x>>n=49197 = 3 × 65 + 2; division by 4 equals a shift by 2
addx+y=44, x+&y=300, x*y=20000+ wrapped, +& kept the carry, * kept all 16 bits
subx-y=255, x-&y=511UInt underflow wraps; the extra bit of -& is not a sign
comparex<y=0, x.asSInt<y.asSInt=1The same bits: 248 vs 15 unsigned, -8 vs 15 signed

The WIDTH lines come first because elaboration finishes before the simulation starts.

Try it yourself. Change n to UInt(4.W) and run again. The first WIDTH line now reports x<<n=23, because the largest amount is 15. The shlDyn port is still 15 bits wide, so the connection silently drops the top 8 bits. The SIMULATE values do not change, because 788 fits in 15 bits; a larger n would lose bits without any warning.

3.6 Operator precedence

Chisel operators are Scala methods, so they follow Scala's precedence, not Verilog's. Scala ranks an operator by its first character. From tightest to loosest:

First characterChisel operators in this group
Prefix ~, !~x, !b bind tightest
*, /, %*, /, %
+, -+, -, +&, -&, +%, -%
<, ><<, >>, <, <=, >, >=
====, =/=
&&, &&
^^
||, ||
Assignment:= binds loosest, so the whole right-hand side is evaluated first

Operators in the same group group from left to right. These cases need parentheses:

You writeScala readsWrite instead
io.x & io.y === io.yio.x & (io.y === io.y)(io.x & io.y) === io.y
a < b << 1(a < b) << 1: shift and compare share a groupa < (b << 1)
x << n + 1, n a Scala Intx << (n + 1)(x << n) + 1.U
a + b >> 1, an average(a + b) >> 1, as intended, but + already dropped the carry(a +& b) >> 1

Caution. Every wrong version above compiles and quietly computes something else. With a = 200 and b = 100, a + b >> 1 gives 22 and (a +& b) >> 1 gives 150. a < b << 1 connected to a Bool keeps bit 0 of (a < b) << 1, which is always 0. Section 4 runs the first case.

3.7 Bit fields

A bit field selects some bits of a value. Bits are numbered from the least significant bit, starting at 0.

Syntax

x(i)         // bit i, a Bool
x(hi, lo)    // bits hi down to lo, width hi - lo + 1
val data = io.data      // 8 bits
data(0)                 // the LSB
data(7)                 // the MSB
data(7, 4)              // the high nibble, 4 bits

The high index comes first. With Scala Int indices, the selection is decided during elaboration and costs no logic, only wiring.

3.8 Cat and Fill

Cat joins values into a wider one; Fill repeats a value. Both live in chisel3.util, so a file that uses them imports that package.

Syntax

import chisel3.util._
Cat(a, b, ...)   // a ends up in the most significant bits
Fill(n, x)       // x repeated n times
Cat("b11".U, "b0001".U(4.W))   // 11_0001, 6 bits
Fill(3, "b10".U)               // 10_10_10, 6 bits
ExpressionResult width
Cat(a, b)w(a) + w(b)
Fill(n, x)n * w(x)

Cat follows the reading order of the source: the first argument becomes the leftmost, most significant part, the way you read a binary number from left to right.

3.9 Reduction operators

A reduction folds every bit of a value into a single Bool.

Syntax

x.andR   // true when all bits are 1
x.orR    // true when any bit is 1
x.xorR   // true when the number of 1s is odd
val allOnes  = io.data.andR
val nonZero  = io.data.orR
val oddCount = io.data.xorR   // the even-parity bit for io.data

4. Example: the operator set

Ops.scala applies one operator per output to two 8-bit inputs, x and y. Read the three commented groups: bit fields, Cat, and reductions. The testbench pokes three input pairs and prints every output in hexadecimal, two hex digits for the 8-bit values.

Ops.scala
import chisel3._
import chisel3.util.Cat

class Ops extends Module {
  val io = IO(new Bundle {
    val x    = Input(UInt(8.W))
    val y    = Input(UInt(8.W))
    val and  = Output(UInt(8.W))
    val or   = Output(UInt(8.W))
    val xor  = Output(UInt(8.W))
    val notX = Output(UInt(8.W))
    val hiX  = Output(UInt(4.W))
    val msbX = Output(Bool())
    val swap = Output(UInt(8.W))
    val allX = Output(Bool())
    val anyX = Output(Bool())
    val parX = Output(Bool())
    val same = Output(Bool())
  })

  io.and  := io.x & io.y
  io.or   := io.x | io.y
  io.xor  := io.x ^ io.y
  io.notX := ~io.x
  // A bit field is written (high, low); bit 0 is the least significant.
  io.hiX  := io.x(7, 4)
  io.msbX := io.x(7)
  // Cat places its first argument in the most significant bits.
  io.swap := Cat(io.x(3, 0), io.x(7, 4))
  // Reductions fold every bit of x into one Bool.
  io.allX := io.x.andR
  io.anyX := io.x.orR
  io.parX := io.x.xorR
  // === builds a comparator; Scala == would compare Scala objects.
  io.same := io.x === io.y
}
Tb.scala
import chisel3._
import chiseltest._
import chiseltest.RawTester.test


object Main extends App {
  test(new Ops) { dut =>
    for ((x, y) <- Seq((0xc5, 0x0f), (0xff, 0xff), (0x00, 0x80))) {
      dut.io.x.poke(x.U)
      dut.io.y.poke(y.U)
      def v(p: Data) = p.peek().litValue
      println(f"SIMULATE x=$x%02x y=$y%02x and=${v(dut.io.and)}%02x or=${v(dut.io.or)}%02x " +
        f"xor=${v(dut.io.xor)}%02x not=${v(dut.io.notX)}%02x hi=${v(dut.io.hiX)}%x " +
        f"msb=${v(dut.io.msbX)} swap=${v(dut.io.swap)}%02x all=${v(dut.io.allX)} " +
        f"any=${v(dut.io.anyX)} par=${v(dut.io.parX)} same=${v(dut.io.same)}")
    }
  }
}

Practice this concept

Use this concept in a short Chisel problem as your next step.

Predict before you run. Work out the first row by hand for x = 0xc5 and y = 0x0f. In binary, x is 1100_0101 and y is 0000_1111. What are and, or, xor, and not? What does swap become, given that Cat puts its first argument on top? x contains four 1s: what are all, any, and par?

Check

What is x ^ y for x = 0xc5 and y = 0x0f, in hex?

Check

swap is Cat(io.x(3, 0), io.x(7, 4)). For x = 0xc5, what does it show?

Your answer

Check

x = 0xc5 has four 1 bits. What is par, that is x.xorR?

Check the result. Press Run and compare the first output row with this table.

OutputComputationValue
and1100_0101 & 0000_111105
or1100_0101 | 0000_1111cf
xor1100_0101 ^ 0000_1111ca
not~1100_01013a
hix(7, 4)c
msbx(7)1
swapCat(x(3, 0), x(7, 4)): 0101 then 11005c
all, any, parFour 1s: not all, at least one, an even count0, 1, 0
samex === y0

The other two rows test the edges. With 0xff, andR is true and xor is zero. With 0x00, orR is false and not is all ones. swap shows Cat order directly: the low nibble came first in the argument list and now occupies the high nibble.

Try it yourself.

  1. Replace === with ==. The Scala compiler stops with a type mismatch that reports found: Boolean and required: chisel3.Data. Scala compared the two port objects, got a plain Boolean, and := cannot connect that to hardware.
  2. Change the last line to io.same := io.x & io.y === io.y, meaning "every bit of y is also set in x". The first row now reports same=1, although 0x05 is not 0x0f. Precedence made it io.x & (io.y === io.y): the comparison is always true, so the result is x & 1, and connecting that to a Bool keeps bit 0 of x, which is 1 for 0xc5. Writing (io.x & io.y) === io.y restores the expected 0.

5. Change the order of Cat

In the lab, change the swap line to Cat(io.x(7, 4), io.x(3, 0)) and run again.

Check

With Cat(io.x(7, 4), io.x(3, 0)), what does swap show for x = 0xc5?

Your answer

Nothing checks the argument order of Cat; a reversed order shows up only as wrong values. Read Cat the way you read a binary number: left to right, most significant first.

6. Apply it: a frame with a parity bit

The Practice problem below asks for an 8-bit frame from 7 data bits: data unchanged in bits 6 to 0 and an even-parity bit in bit 7, chosen so that the whole frame has an even number of ones. Two operators from this chapter are enough: one reduction decides the parity bit, and Cat places it above the data.

Check

For data = 0x55, which is 101_0101, what is the parity bit the frame needs?

7. Common mistakes

7.1 == instead of ===

// Wrong: compares Scala objects and returns a Scala Boolean
io.same := io.x == io.y

// Right: builds a comparator
io.same := io.x === io.y

The compiler reports type mismatch; found: Boolean, required: chisel3.Data.

7.2 Leaving out parentheses around a comparison

// Wrong: parsed as io.x & (io.y === io.y)
io.covers := io.x & io.y === io.y

// Right
io.covers := (io.x & io.y) === io.y

Scala's === binds tighter than &, ^, and |. The wrong version compiles and returns bit 0 of x.

7.3 Reading Cat backwards

// Wrong if you wanted lo in the low bits: lo ends up on top
val word = Cat(lo, hi)

// Right: the first argument is the most significant part
val word = Cat(hi, lo)

Cat reads like a binary number written left to right. Nothing checks the order, so the mistake shows up only as wrong values.

8. Summary

TopicKey point
Bool(), UInt(n.W)One bit; n unsigned bits. A Bool is also a 1-bit UInt
ExpressionsEach operator adds a node; a val names a node so several expressions can share it
&, |, ^, ~Gates per bit position; the result is as wide as the wider operand
&&, ||, !Gates on Bool; ! on a UInt tests for zero, so write === 0.U
Arithmetic+, - wrap at max width; +&, -& keep one more bit; * is w(x) + w(y); /, % build a divider
ShiftsA Scala Int amount is wiring and << widens by it; a UInt amount builds a shifter; >> on SInt copies the sign
ComparisonsReturn a Bool; === and =/=, since Scala == compares objects; UInt and SInt are not compared directly
PrecedenceScala rules by first character: === binds tighter than &, and << shares a group with <, so use parentheses
Bit fieldsx(i), x(hi, lo); bit 0 is the LSB, high index first; wiring, not logic
Cat, FillCat puts its first argument on top; Fill(n, x) repeats x
ReductionsandR, orR, xorR fold all bits into one Bool: all, any, odd count

9. Check your understanding

  1. In Cat(x(3, 0), x(7, 4)), which argument ends up in bits 7 to 4, and why?

    Show answer

    x(3, 0), the first argument. Cat places its first argument in the most significant bits.

  2. What is the width and value of Fill(2, "b101".U)?

    Show answer

    6 bits, 101101. Fill repeats the 3-bit value twice.

  3. How does Scala parse io.x & io.y === io.y, and what does the expression compute for an 8-bit x?

    Show answer

    As io.x & (io.y === io.y), because === binds tighter than &. The comparison is always true, so the result is x & 1: bit 0 of x, zero-extended. Parentheses around io.x & io.y give the intended comparison.

  4. What does io.x == io.y return, and why can it not drive io.same?

    Show answer

    A Scala Boolean: == asks during elaboration whether the two port objects are the same object. := needs hardware on its right, so the compiler reports found: Boolean, required: chisel3.Data. === builds the comparator.

The Practice problem below builds an 8-bit frame from seven data bits plus an even-parity bit in bit 7. The starter connects the data straight through, so bit 7 is always 0 and frames with an odd number of 1s break the parity rule.

Checkpoint

Submit the Practice problem for grading

Grading runs on the server against hidden tests; the result is recorded in this browser only.

Sources

Licences and notices for the sources this site follows