Skip to content

Commit

Permalink
Testing access to internal signals
Browse files Browse the repository at this point in the history
  • Loading branch information
schoeberl committed Nov 30, 2023
1 parent 61d0086 commit edb53c0
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 8 deletions.
84 changes: 76 additions & 8 deletions chisel-book.tex
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,11 @@ \section*{Foreword for the Sixth Edition}

\todo{VHDL and SV}

Chisel formal (if finished).

We extended the testing chapter with a description how to access internal signals. \todo{That is too
little to be mentioned here. More is needed.}




Expand Down Expand Up @@ -387,8 +392,8 @@ \section*{Acknowledgements}
learning and using a bleeding-edge hardware description language.
Many of your questions have helped to shape this book.

It was a pleasure to use Chisel in the last three years of teaching a digital electronics
course at the Technical University of Denmark.
It was a pleasure to use Chisel in teaching a digital electronics
course at the Technical University of Denmark since 2020.
I know it is a challenge to pickup Chisel and Java in parallel in the second
semester. Thank you to all students from this course, who had on open mind to
pickup a modern programming language for hardware description.
Expand Down Expand Up @@ -5958,6 +5963,8 @@ \section{Testing in Chisel}
\longlist{code/test_counter.txt}{Testing the counter device.}{lst:test:cnt}
\subsection{Use Functions}
As you can see, the test covers only a few cases, but is already very long to read.
All those pokes and expects are cumbersome. As a first step, we shall introduce
functions to represent a read and a write request. Those functions abstract away the manual
Expand Down Expand Up @@ -6019,6 +6026,65 @@ \section{Testing in Chisel}
The following subsections present advanced testing techniques that you likely do
not need yet. You can skip ahead to the exercise and return later if you find the need.
\subsection{Accessing Internal Signals}
When testing a circuit, the test code usually has access to the ports of the DTU only.
This abstraction is generally good practice, as access to internal signals and state is considered
bad practice (in hardware and software testing).
However, it is sometimes beneficial to access the internal state. For example,
testing a microprocessor with small assembler test programs.
In that case, one would usually compare the state of the hardware implementation of
the processor against a software-based simulator of the same processor.
For a RISC-style processor comparing the content of the register file of the two implementations
would be enough for this kind of testing, as all data that is computed, loaded,
or stored passes the register file at some time.
Another use case is to explore and test a state machine (with or without datapath)
with access to the internal state.
\todo{Explain and explore the co-simulation in the Leros chapter.}
\longlist{code/boring_tickgen.txt}{The tick generater as DUT.}{lst:boring:tickgen}
To show the access to internal signals in action, we use a very minimal example:
a tick generator with an internal counter. Listing~\ref{lst:boring:tickgen} shows the code.
Only the really necessary signal \code{tick} is connected to an output port.
The counter is not exposed, which is good design practice.
For example, we want to access this internal counter in our test code.
We could add a port to expose the counter. We could even use a Scala \code{Boolean}
flag to conditionally have this port when we are debugging and disable it
when generating hardware.
However, mixing debugging code into the hardware description is not good practice.
To get access to internal signals, we can use the \code{BoringUtils}.
\code{BoringUtils} allows us to \emph{bore} a connection through a module hierarchy.
Behind the scenes, it adds the additional needed ports throughout the hierarchy.
It does exactly what we could do manually, but avoids cluttering the original code.
At the time of this writing, \code{BoringUtils} is still considered experimental.
Therefore, we need to include the following package when using it:
\shortlist{code/boring_import.txt}
\longlist{code/boring_tickgen_test_top.txt}{A top-level wrapper for our DUT.}{lst:boring:tickgen:top}
To support additional ports, we wrap our DUT into another top-level module for testing.
Listing~\ref{lst:boring:tickgen:top} shows that top-level module with the additional port
counter. Within the \code{TickGenTestTop}, we instantiate our original DUT and connect
the \code{tick} port. For the \code{counter} output, first, we must assign a value to
make the Chisel compiler happy. As we connect it later anyway to the inner module,
we connect it to \code{DontCare}.
The next line connects the \code{io.counter} to the count register in \code{tickGen}.
We need to wrap \code{io.counter} into a Scala \code{Seq}, as \code{boring()} supports
connecting to several signals.
\longlist{code/boring_tickgen_test.txt}{A top-level wrapper for our DUT.}{lst:boring:tickgen:test}
Finally, Listing~\ref{lst:boring:tickgen:test} shows the testing code for our DUT.
Note that we instantiate the top-level wrapper to use the additional output port.
\section{Multithreaded Testing}
Digital hardware is inherently parallel.
Expand Down Expand Up @@ -6181,7 +6247,7 @@ \section{Exercise}
be used to show the presence of bugs, but never to show their absence!''}
However, there is hope in recent development in formal verification to complement testing.
The topic of formal verification with Chisel will be covered in a future edition of this book.
\todo{remove this when the verification section is written.}
\todo{move this into the verification section when it is better written.}
\chapter{Design of a Processor}
Expand Down Expand Up @@ -7032,19 +7098,24 @@ \subsection{Components}
Listing~\ref{lst:sv:adder} shows the adder component in SV. Similar to Chisel,
a component is also called a module.~\footnote{In fact, the naming in Chisel
was inspired by Verilog.}
Listing~\ref{lst:sv:adder} shows an SV module with two 8-bit inputs and one 8-bit
The adder has two 8-bit inputs and one 8-bit
output. The main body of the module uses the SV construct \code{always\_comb}
to declare that this code block describes combinational logic.
The difference between SV and Chisel is minimal for such a simple component.
\longlist{code/sv_register.txt}{A register with reset and enable in SystemVerilog.}{lst:sv:register}
\subsection{Using a Component}
\subsection{Register}
\longlist{code/sv_ch_register.txt}{A register with reset and enable in Chisel.}{lst:sv:ch:register}
Listing~\ref{lst:sv:ch:register} show a register with reset and enable in Chisel, using
the three-parameter version of \code{RegEnable}.
\todo{always\_reg}
\longlist{code/sv_register.txt}{A register with reset and enable in SystemVerilog.}{lst:sv:register}
Listing~\ref{lst:sv:register} shows a module that contains the definition of a
register (\code{reg\_data}) with a synchronous reset and an enable input in SystemVerilog.
The SV \code{reg} keyword declares a variable. This does not necessarily mean
Expand All @@ -7058,10 +7129,7 @@ \subsection{Register}
The \code{assign} statement is a concurrent statement that continuously
assigns the value of the register to the output port \code{out}.
\longlist{code/sv_ch_register.txt}{A register with reset and enable in Chisel.}{lst:sv:ch:register}
Listing~\ref{lst:sv:ch:register} show the register with reset and enable in Chisel, using
the three-parameter version of \code{RegEnable}.
\todo{use logic in the parameters and explain that this is the new way in SV.}
Expand Down
57 changes: 57 additions & 0 deletions src/test/scala/BoringTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
//- start boring_import
import chisel3.util.experimental.BoringUtils
//- end

//- start boring_tickgen
class TickGen extends Module {
val io = IO(new Bundle {
val tick = Output(Bool())
})

val cntReg = RegInit(0.U(8.W))
cntReg := cntReg + 1.U
io.tick := cntReg === 9.U
when(io.tick) {
cntReg := 0.U
}
}
//- end

//- start boring_tickgen_test_top
class TickGenTestTop extends Module {
val io = IO(new Bundle {
val tick = Output(Bool())
val counter = Output(UInt(8.W))
})

val tickGen = Module(new TickGen)
io.tick := tickGen.io.tick
io.counter := DontCare
BoringUtils.bore(tickGen.cntReg, Seq(io.counter))
}
//- end
class BoringTest extends AnyFlatSpec with ChiselScalatestTester {
"Boring" should "work" in {
//- start boring_tickgen_test
test(new TickGenTestTop()) { dut =>
dut.io.tick.expect(false.B)
dut.io.counter.expect(0.U)

dut.clock.step()
dut.io.tick.expect(false.B)
dut.io.counter.expect(1.U)

dut.clock.step(8)
dut.io.tick.expect(true.B)
dut.io.counter.expect(9.U)

dut.clock.step()
dut.io.tick.expect(false.B)
dut.io.counter.expect(0.U)
}
//- end
}
}

0 comments on commit edb53c0

Please sign in to comment.