5. SystemVerilog
Up to this point, we specified our hardware modules through elementary logic gates. Given an abstract description of a function, we had to manually lower it to logic gates. This process is cumbersome, error-prone and time-consuming, especially when aiming for an optimized circuit where different realizations have to be considered.
A Hardware Description Language (HDL) allows us to “simply” specify the logical function of a module. Computer-Aided Design (CAD) tools which understand such an HDL then perform the mapping of our abstract module description to logic gates. This process process is called synthesis. Further, we are able to test our HDL-based hardware designs through simulation. Here, we apply inputs to our hardware modules and check the correctness of the outputs.
In this class we harness SystemVerilog as our HDL. The following tools will allow us to test our designs:
Simulation: Icarus Verilog
Waveform visualization: GTKWave
Note
We rely on open source or at least free software if possible. This means that you may install and run the tools on your own machines if desired. However, for the regular sessions, use the provided installations. If you’d like to explore further and use a custom installation, we can talk in the Open Lab / Q&A.
5.1. Simple Example Module
Our first module implements the following function:
(5.1.1)
where and are one-bit inputs and the one-bit output. Table 5.1.1 is the truth table of this simple example function.
0 |
0 |
1 |
1 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
1 |
Tasks
Implement Eq. (5.1.1) in the module
simple_example_module
using the filenamesimple_example_module.sv
.Test your implementation in the testbench
simple_example_module_tb
. Use the filenamesimple_example_module_tb.sv
. Verify that your module produces the correct outputs for all inputs (see Table 5.1.1).Visualize the waveforms generated by your module.
Hint
Use the arguments -Wall -Winfloop -g2012
when invoking iverilog
.
Details on using GTKWave with Icarus Verilog are available from the Icarus Verilog Wiki.
You may increase the GTKWave’s font size, e.g., by using the arguments -A --rcvar 'fontname_signals Monospace 12' --rcvar 'fontname_waves Monospace 12'
when invoking the GTKWave-binary.
5.2. One-bit Full Adder
This chapter designs a one-bit full adder in SystemVerilog. The full adder consists of the three one-bit inputs , and and has the two one-bit outputs and :
(5.2.1)
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
1 |
1 |
0 |
1 |
1 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
Table 5.2.1 is the truth table of the full adder. In your tests and waveform visualization cover all possible inputs and output. Additionally, follow the order of the table, e.g., test/visualize the first row before the second one.
1/**
2 * 1-bit full adder.
3 *
4 * @param i_a input A.
5 * @param i_b input B.
6 * @param i_carry_in carry in.
7 * @param o_s sum.
8 * @param o_carry_out carry out.
9 **/
10module full_adder_1( input logic i_a,
11 input logic i_b,
12 input logic i_carry_in,
13 output logic o_s,
14 output logic o_carry_out );
15 // TODO: implement full adder
16endmodule
1/**
2 * Testbench of the 1-bit full adder.
3 */
4module full_adder_1_tb;
5 logic l_a, l_b, l_s;
6 logic l_carry_in, l_carry_out;
7
8 full_adder_1 dut( l_a,
9 l_b,
10 l_carry_in,
11 l_s,
12 l_carry_out );
13
14 initial begin
15 $dumpfile("dump_full_adder_1.vcd");
16 $dumpvars;
17
18 l_a = 1'b0;
19 l_b = 1'b0;
20 l_carry_in = 1'b0;
21 #10;
22 assert( l_s === 1'b0 );
23 assert( l_carry_out === 1'b0 );
24
25 l_a = 1'b1;
26 l_b = 1'b0;
27 l_carry_in = 1'b0;
28 #10;
29 assert( l_s === 1'b1 );
30 assert( l_carry_out === 1'b0 );
31
32 // TODO: add additional tests
33
34 $finish;
35 end
36endmodule
Use the template in Listing 5.2.1 for your implementation of the full adder in the module full_adder_1
using the filename full_adder_1.sv
.
Use the template in Listing 5.2.2 for your implementation of the testbench full_adder_1_tb
in the file full_adder_1_tb.sv
.
Tasks
Implement the module
full_adder_1
.Test your implementation in the testbench
full_adder_1_tb
. Your testbench should check the outputs for all possible inputs (see Table 5.2.1) throughassert()
-statements.Visualize the waveform generated by your module. Only show
i_a
,i_b
,i_carry_in
,o_s
ando_carry_out
of thefull_adder_1
-module in that order.
5.3. Four-bit Ripple-Carry Adder
This task covers basic modularity in SystemVerilog. In detail, we use simple(r) SystemVerilog modules as building blocks for more complex ones. Nothing else is done in processor design. This means, that one would combine a “simple” module, e.g., an Arithmetic and Logic Unit (ALU), with other modules such that the combined logics executes desired instructions. We’ll cover details on ALUs and processors later in the lab. For now, we’ll stick to our adders.
Specifically, instead of adding three one-bit inputs as done in Section 5.2, we’ll add two four-bit inputs and , and the carry in . We’ll use the so-called ripple-carry adder to perform this task. A ripple-carry adder chains a series of 1-bit full adders together and is one possible implementation of a carry propagate adder.
We obtain a respective formula for the ripple-carry adder by repeatedly applying the two functions and of Eq. (5.2.1) to our input:
(5.3.1)
Combined the equations (5.3.1) compute the four-bit output and the one-bit carry out from the inputs. are intermediate carries through which we harness to connect our one-bit full adders: The carry ripples through the obtained multi-bit adder.
0000 |
0000 |
0 |
0000 |
0 |
1010 |
0001 |
0 |
1011 |
0 |
1100 |
0000 |
1 |
||
0101 |
1010 |
1 |
||
0111 |
1100 |
0 |
||
1111 |
1111 |
1 |
Once again we have to to think about reasonable unit tests for our testbench. Now, we have to deal with an excessive amount of potential inputs due to the increased size of the inputs. Thus, we limit ourselves for the mandatory tests to the small subset given in Table 5.3.1.
Note
Table 5.3.1 only gives the required tests for a successful completion of this task. Feel free to try additional inputs and implement further test if you see a need for this.
1/**
2 * 4-bit ripple-carry adder.
3 *
4 * @param i_a input A.
5 * @param i_b input B.
6 * @param i_carry_in carry in.
7 * @param o_s sum.
8 * @param o_carry_out carry out.
9 **/
10module ripple_carry_adder_4( input logic [3:0] i_a,
11 input logic [3:0] i_b,
12 input logic i_carry_in,
13 output logic [3:0] o_s,
14 output logic o_carry_out );
15 // TODO: implement ripple-carry adder here
16endmodule
1/**
2 * Testbench of the 4-bit ripple-carry adder.
3 */
4module ripple_carry_adder_4_tb;
5 logic [3:0] l_a, l_b, l_s;
6 logic l_carry_in, l_carry_out;
7
8 ripple_carry_adder_4 dut( l_a,
9 l_b,
10 l_carry_in,
11 l_s,
12 l_carry_out );
13
14 initial begin
15 $dumpfile("dump_ripple_carry_adder_4.vcd");
16 $dumpvars;
17
18 l_a = 4'b0000;
19 l_b = 4'b0000;
20 l_carry_in = 1'b0;
21 #10;
22 assert( l_s === 4'b0000 );
23 assert( l_carry_out === 1'b0 );
24
25 // TODO: add additional unit tests
26
27 $finish;
28 end
29endmodule
Tasks
Draw the schematic of the four-bit ripple-carry adder as a sequence of one-bit full adders.
Derive the total number of possible inputs for the four-bit ripple-carry adder! How would this change if we were to implement a 32-bit ripple-carry adder?
Complete Table 5.3.1!
Implement the module
ripple_carry_adder_4
by completing the module in Listing 5.3.1.Test your implementation in the testbench
ripple_carry_adder_4_tb
by completing the module in Listing 5.3.2. Your testbench should check the outputs for all inputs in Table 5.3.1 throughassert()
-statements.Visualize the waveform generated by your module for Table 5.3.1’s inputs. Only show the four-bit inputs
i_a
,i_b
, the carry ini_carry_in
, the four-bit outputo_s
and the carry outo_carry_out
of theripple_carry_adder_4
module in that order.