Skip to content

Chisel Introductory Examples

apaj edited this page Jan 2, 2018 · 15 revisions

In this section first an important practice of maintaining the directory structure is described and, then, the introductory examples of digital circuits implementation using Chisel are presented.

Directory Structure

The Chisel tutorial is intended to be hands on experience, meaning that you are expected to go through a certain set of problems, solve them and then compare your implementation to the solution provided by the exercises' authors. Finally, of course, please, as we consider your input very valuable, please provide your thoughts on how to improve this Learning Journey using the User Experiences section.

The circuit problems are found in: src/main/scala/problems and corresponding automated tests (such an automated test framework is called a harness) in: src/test/scala/problems. Each of the exercises contains clearly marked places where its functionality (in case of circuits) and test cases (in case of harnesses) should be completed. Successful execution of the harness means a successful solution of the problem. The authors' solutions to the circuit problems are found in: src/main/scala/solutions and corresponding harnesses are found in: src/test/scala/solutions. You are welcome to take a peak if you find yourself taking to much time for a certain problem, but also, even if you do provide a correct solution, please do compare it to the provided solutions. The elegance of Chisel code will surely surprise you!

Finally, as implicitly already exposed, let us also directly note that this hierarchy in separation of circuits and tests represents the directory structure we would like to keep within this tutorial and all other, future projects.

An Actual Circuit Performance Test

The first actual circuit to run tests on and describe in Chisel is a multiplexer with two data inputs and one control input, let's call it Mux2. It is already implemented, so let's try running it. First, start sbt and keep it running (this will speed up the following executions):

$ sbt

Then, enter the following line in sbt console to run tests on circuit Mux2:

> test:run-main problems.Launcher Mux2

After initial information on what is being run and that the circuit state has been created, this run should result in successful passing of all tests, as follows:

...
test Mux2 Success: 8 tests passed in 13 cycles taking 0.082884 seconds
[info] [0.050] RAN 8 CYCLES PASSED
Tutorials passing: 1
[success] Total time: 40 s, completed Dec 23, 2017 12:59:27 PM

If you faced any other resolution of the test run, please let us know using either User Experiences section, or as a question at the Hangout group chat. Of course, any other feedback is also valuable.

Circuit Implementation

Chisel implementation of Mux2 is found in src/main/scala/problems/Mux4.scala, so please go ahead, open up that file and focus on the first circuit (the second being Mux4 that will be discussed in the next section).

The first line:

class Mux2 extends Module {
...
}

defines a Module, which represents an equivalent to the box that limits the circuit implementation from the rest of the universe, also a module in Verilog.

The next line:

val io = IO(new Bundle {
...
})

is an instantiation of a Bundle class, which is used to make collection of values with named fields (similar to struct in other languages). In this specific case, it is used to create the interface, i.e. a collection of ports (pins, if you will) of the multiplexer circuit. Those are:

val sel = Input(UInt(1.W)) # control input that selects which of the data inputs is wired to the output
val in0 = Input(UInt(1.W)) # first data input
val in1 = Input(UInt(1.W)) # second data input
val out = Output(UInt(1.W) # output

where val is a keyword of Scala, used to name variables that have values that will not change. The Input or Output designation is used to select the direction of the port and UInt(1.W) is the correct way to specify a one bit width value of the type UInt. More on types and widths later on, this is just a heads up and enough for now.

Finally, after defining the input/output interface in the form of a Bundle, the internals of the multiplexer circuit are given as:

io.out := (io.sel & io.in1) | (~io.sel & io.in0)

which is the standard way to implement combinational circuits - in this case, a multiplexer. Note how values from within the io, the interface collection, are referenced.

Test Harness

Corresponding test harness is found in src/test/scala/problems/Mux2Tests.scala, so please open this file now.

The first line:

class Mux2Tests(c: Mux2) extends PeekPokeTester(c) {
...
}

extends the base class used for creating tests of Chisel circuits. Then, wrapped in three for loops (omitted from explanations here), three functions are used to test the behavior of DUT:

poke(...) # bring 0 or 1 to each of the circuit inputs: sel, in0 and in1
step(...) # advance the timeline
expect(...) # read the circuit output and compare to the correct result

Please note how multiplexer's ports are referenced from within the harness.

Next Steps

Please proceed to the next step, i.e. examples, explained line-by-line. If you come with a strong Verilog background, please check out the Comparison to Verilog table.

Clone this wiki locally