Systemverilog For RTL Design Workshop: Lab Guide
Systemverilog For RTL Design Workshop: Lab Guide
Systemverilog For RTL Design Workshop: Lab Guide
Disclaimer
SYNOPSYS, INC., AND ITS LICENSORS MAKE NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
Trademarks
Synopsys and certain Synopsys product names are trademarks of Synopsys, as set forth at
https://www.synopsys.com/company/legal/trademarks-brands.html
All other product or company names may be trademarks of their respective owners.
Third-Party Links
Any links to third-party websites included in this document are for your convenience only. Synopsys does not endorse
and is not responsible for such websites and their practices, including privacy practices, availability, and content.
Synopsys, Inc.
690 E. Middlefield Road
Mountain View, CA 94043
www.synopsys.com
Learning Objectives
Lab Duration:
30 minutes
Lab Overview
To implement high Quality of Result (QoR) RTL design, one must first make sure that
the RTL code written is interpreted by the synthesis tool as intented.
In this lab, you will see that one can better achieve this goal with SystemVerilog.
labs/
lab1/ Lab2/
For each individual lab, you will work in the specified lab directory.
The general work flow for each section of this lab is illustrated as follows.
Examine
Verilog RTL code
Synthesize and
verify functionality
Create equivalent
SystemVerilog RTL code
Synthesize and
verify functionality
Compare the
differences
A common problem associated with Verilog is that synthesizer ignores the event list
whereas simulator obeys them.
For this first section, you will run simulation on a Verilog RTL code and log the result.
Then, synthesize the RTL and run the simulation at the gate level. Comparing the result
of RTL v.s. gate level simulation, you will see that they do not match.
Note that the signal C is left off the Verilog event list:
In the opened waveform window, you can clearly see when C is "1", the output D
errorenously still shows "0".
This time, in the opened waveform window, you see that when C is "1", the
output D correctly shows "1".
5. You can also see that the synthesis tool completely ignores the event list by
looking at the resulting gates.
> make dv ddc=mismatch_mapped
6. In the opened Design Vision window, double click on the design, you will see the
synthesized gates
With Verilog, this type of simulation mismatches can be very frustrating to debug. This
type of error can be completely eliminated with SystemVerilog
7. Open the existing mismatch.sv file with an editor (if not already opened):
> gvim rtl/mismatch.sv
8. Look for the ToDo comment line and enter the following SystemVerilg code:
endmodule
You should see that the gate level simulation produces the same result when using
the SystemVerilog always_comb feature.
The difference between the Verilog vs. the SystemVerilog code is the elimination
of simulation mismatch.
Unintentional Latch
In this section, you will run simulation at RTL and gate level to see that if RTL branch
code is not complete, both the simulator and synthesizer will treat the code as latch.
The branch on selA is incomplete for both the Verilog and SystemVerilog code:
`ifndef sverilog
always@(selA, A) begin
if (selA) begin
B = A;
end
end
Missing
`else else
else
always_comb begin
if (selA) begin
B = A;
end
end
`endif
When selA is 1, B is the A value otherwise B should be 0. You can see that this
is not the result. When selA is "0", the output B stayed at the captured value
(latched) when selA returned to 0. Also, B should never be x!
Scroll down, you should see that a latch was created and no warning issued:
You will need to modify the RTL code to resolve the problem.
8. Look for the ToDo’s (two of them) and add the missing branch to the source code.
10. Synthesize the modified Verilog RTL code and view the simulation results
> make syn rtl=unintentional_latch
> make gate rtl=unintentional_latch
If you set the language to sverilog, you will also see that with the else
statement added, all are working correctly.
One of the most useful data type in SystemVerilog for state machine development is the
new enum data type. To illustrate the benefit of the enum data type, let’s use a very
simple traffic light state machine as an example.
reset_n
RED
3 sec 20 sec
YELLOW GREEN
20 sec
This works, but trying to deciphor the code takes a little getting use to. The effect
is more pronounced in the waveform window.
Let’s convert the RTL state machine to use enum data type.
6. Change the following highlighted code to use the enum data type values
Isn’t this a lot more readable? The RTL source code is also a lot easier to read.
This benefit, exist only for the RTL simulation though. After synthesis, at the
gate level, they are once again just ones and zeros.
Answers / Solutions
mismatch.sv Solution:
`ifndef sverilog
`else
always_comb begin
D = (A & B) | C;
end
`endif
endmodule
unintentional_latch.sv Solution:
`ifndef sverilog
else begin
B = 0;
end
end
`else
else begin
B = 0;
end
end
`endif
endmodule
traffic_light.sv Solution:
typedef enum logic[2:0] {RED = 3'b001, GREEN = 3'b010, YELLOW = 3'b100} state_e;
module traffic_light(input logic clk, reset_n, output logic green_on, yellow_on,
red_on);
state_e state, nxt_state;
logic [5:0] count_seconds;
logic reset_count;
always_comb begin
nxt_state = state;
reset_count = 1'b0; red_on = 1'b0; green_on = 1'b0; yellow_on = 1'b0;
case(state)
RED: begin
red_on = 1'b1;
if (count_seconds >= 20) begin
nxt_state = GREEN;
reset_count = 1'b1;
end
end
GREEN: begin
green_on = 1'b1;
if (count_seconds >= 20) begin
nxt_state = YELLOW;
reset_count = 1'b1;
end
end
YELLOW: begin
yellow_on = 1'b1;
if (count_seconds >= 3) begin
nxt_state = RED;
reset_count = 1'b1;
end
end
endcase
end
always_ff @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
state <= RED;
end else begin
state <= nxt_state;
end
end
always_ff @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
count_seconds <= '0;
end else begin
if (reset_count) begin
count_seconds <= '0;
end else begin
count_seconds <= count_seconds + 1;
end
end
end
endmodule
Learning Objectives
Lab Duration:
45 minutes
Lab Overview
Of the new features in SystemVerilog, the one that makes the biggest impact to the RTL
coding style is the new interface mechanism. But, because backend tools do not support
SystemVerilog interface, synthesis tools deconstruct the interface into individual port
listings. This results in a problem that one must resolve when integrating gate level
netlist into higher level blocks (bottom up approach to synthesis) and developing
testbench for gate level simulation.
This lab will take you through how to manage the interfaces (especially with
parameters) for gate level block integration and simulation.
The general work flow for each section of this lab is illustrated as follows.
Modify
Verilog RTL to use
SystemVerilog Interface
Synthesize and
verify functionality
encryptor
din[WIDTH-1:0]
dout[WIDTH-1:0]
key[$clog2(WIDTH)-1:0] Barrel Shifter
clk
reset_n
• First, verify the fifo module implemented with Verilog style port list
• Then, replace the port list of signals of the fifo module with a
SystemVerilog interface
• Develop DC script to synthesize the fifo module and generate files needed
for integration and simulation
• Verify the fifo gate level netlist using the generated definition
• Develop DC script to synthesize the top-level encryptor using the fifo
block gate-level ddc generated with the previous synthesis run
• Verify that the synthesized top-level encryptor is functionally correct
You should see that the module is parameterized and the port list of the module is
coded without SystemVerilog interface.
2. Take a look at the fifo testbench
> less test/fifo_test_top.sv
module fifo_test_top;
parameter WIDTH = 8;
parameter BUF_SIZE = 16;
fifo_io#(WIDTH) fifo_if(clk);
`ifdef GATE
...
`else
...
fifo#(WIDTH, BUF_SIZE) dut(.*,
.rd_n (fifo_if.rd_n),
.wr_n (fifo_if.wr_n),
.full (fifo_if.full),
.empty (fifo_if.empty),
.din (fifo_if.din),
.dout (fifo_if.dout));
`endif
Notice that even though the RTL code did not implement an interface for the
fifo module, the testbench still makes use of the SystemVerilog interface.
The reality is that, in the verification world, testbenches make use of the
SystemVerilog interface to simplify the development of the testbench device
drivers even if the RTL code does not. This is one of the reasons why design
engineers must understand the SystemVerilog interface mechanism.
3. Verify that this code works correctly before playing with the SystemVerilog
interface
> make sim rtl=fifo
The clocking blocks in the interface is for simulation only. They are typically
specified by the verification engineer.
The final step in the RTL conversion is a bit of a headache. You will need to add
fifo_if in front of all modified port signals with a dot notation.
3. Locate all the modified port signals (rd_n, wr_n, din, empty, full and dout) and
change them to – fifo_if.rd_n, fifo_if.wr_n, fifo_if.din,
fifo_if.empty, fifo_if.full and fifo_if.dout.
(In gvim, it would be something like the following)
:50,$s/rd_n/fifo_if.rd_n/g
:50,$s/wr_n/fifo_if.wr_n/g
:50,$s/din/fifo_if.din/g
:50,$s/dout/fifo_if.dout/g
:50,$s/empty/fifo_if.empty/g
:50,$s/full/fifo_if.full/g
`else
fifo#(WIDTH, BUF_SIZE) dut(.*,
.rd_n (fifo_if.rd_n),
.wr_n (fifo_if.wr_n),
.full (fifo_if.full),
.empty(fifo_if.empty),
.din (fifo_if.din),
.dout (fifo_if.dout));
`endif
To:
`else
fifo#(WIDTH, BUF_SIZE) dut(.*);
`endif
You should see all expected values matched. You are now ready to synthesis the
fifo into a gate level netlist.
fifo_io
dout[WIDTH-1:0]
din[WIDTH-1:0]
full fifo
empty
wr_n
rd_n
clk
reset_n
1. Take a look at the synthesis script to make sure you see what’s being executed
> less script/fifo_run.tcl
2. Synthesize this into gate level
> make syn rtl=fifo
3. Take a look at the elaborated netlist
> less unmapped/fifo_unmapped.v
Notice after elaboration, the module name and the port list changed.
Was:
At synthesis, the parameters become part of the module name and the interface is
decomposed into individual signal sets:
This can be a huge problem. To see the issue, execute the following steps.
4. Synthesize fifo with a parameter that’s different from the default value
> make syn rtl=fifo WIDTH=16
5. Check the gate level netlist result
> less unmapped/fifo_unmapped.v
You should see that the name of the module reflects the new parameter. But, bit
width of the content of the interface is wrong!
You may attemp to get around the issue by adding the parameter to the interface
reference:
If you read the SystemVerilog LRM, it tells you to use a generic interface:
The synthesis tool now has no idea what kind of interface fifo_if is supposed
to be when the module is synthesized as the current design in a bottom up
synthesis approach.
By instantiating the interface with parameter, DC will recognize the user’s intent.
You do need to adjust the synthesis script to use this wrapper module.
The purpose of the wrapper is to get the synthesizer to recognize the interface
parameters at the elaboration phase. Once elaborated, you will only deal with the
fifo module for synthesis. The wildcard (*) is needed because the name of the
module is no longer fifo, but the expanded name.
You should see that the name of the module now reflects not only the paramerter
of the module, but the parameterized interface as well.
The bit width of the content of the interface are also correct.
One more issue. You may need to verify the operation of this gate level netlist.
The last thing you want to do is to hand code all these changes into the testbench.
Let’s adjust the synthesis script one more time to generate an instance of the gate
level module that you can copy and paste into the testbench.
You should see the instantiated fifo module with the correct module name and
mapping of module port signals.
fifo_WIDTH16_BUF_SIZE16_I_fifo_if_fifo_io__16 fifo_WIDTH16_BUF_SIZE16_I_fifo_if_fifo_io__16(
{>>{ clk }}, {>>{ reset_n }}, , {>>{ fifo_if.rd_n }},
{>>{ fifo_if.wr_n }}, {>>{ fifo_if.empty }}, {>>{ fifo_if.full }},
{>>{ fifo_if.din }}, {>>{ fifo_if.dout }} );
fifo_io#(WIDTH) fifo_if(clk);
`ifdef GATE
// Lab 2 Task 9 Step 6
//
// Add the remapped gate-level module here:
//
// ToDo:
fifo_WIDTH16_BUF_SIZE16_I_fifo_if_fifo_io__16 fifo_WIDTH16_BUF_SIZE16_I_fifo_if_fifo_io__16(
{>>{ clk }}, {>>{ reset_n }}, , {>>{ fifo_if.rd_n }},
{>>{ fifo_if.wr_n }}, {>>{ fifo_if.empty }}, {>>{ fifo_if.full }},
{>>{ fifo_if.din }}, {>>{ fifo_if.dout }} );
`else
A couple of thing to notice: the gate level instance name no longer matches the
RTL instance name. If naming consistency is important, you will need to
manually change the instance to match the RTL instance name (dut). And, this
testbench is no useable for other gate-level netlists with different parameters.
The gate simulation should pass. The major caution here is that with gate level
simulation, each testbench can only handle one variation of the parameter. You
may want to develop a script to generate the testbench as you need it.
SystemVerilog Interface Lab 2-11
SystemVerilog RTL Design Workshop
Lab 2
Because of the parameters and interfaces of the module, integrating this into a higher
level block (bottom up synthesis approach) requires a little tweak to the synthesis script.
For this part of the lab, the RTL design is an encryptor embedding the fifo:
encryptor full
empty
dout[WIDTH-1:0]
fifo_io
din[WIDTH-1:0] dout[WIDTH-1:0]
Barrel din[WIDTH-1:0]
key[$clog2(WIDTH)-1:0]
Shifter full fifo
empty
wr_n wr_n
rd_n rd_n
clk
reset_n
This encryptor module is very simple. The simplicity let us focus more on the task
at hand: integration of a module with parameterized interface.
You should see that the module has parameters. But, to keep focus on just the
integration of lower level block with parameterized interfaces, the encryptor
module port list is kept at the Verilog port list style. Inside the module there is
one instance of fifo_io interface and one instance of fifo module.
The content is similar to the fifo_run.tcl script. The key difference between
the two scripts are two lines:
If you already have a synthesized module that you want to use. You need to
remove the newly read in code from memory with the remove_design
command. Then, you need to read the saved synthesized module into the memory
with the read_ddc command.
To make sure everything at gate level works properly, modify the testbench to
enable gate level verification
Now you know how to deal with SystemVerilog interface and parameters for a
bottom up synthesis approach.
Answers / Solutions
fifo_io.sv Solution:
`ifndef SYNTHESIS
interface fifo_io #(WIDTH = 8) (input clk);
`else
interface fifo_io #(WIDTH = 8) (); // RTL does not need clk
`endif
logic rd_n,
wr_n,
empty,
full;
`ifndef SYNTHESIS
clocking drvWrClk @(posedge clk);
default input #1ns output #1ns;
output wr_n;
output din;
input full;
endclocking
endinterface
fifo.sv Solution:
endmodule
fifo_run.tcl Solution:
# fifo_run.tcl
source ../../../script/common_setup.tcl
source ../../../script/dc_setup.tcl
link
source ../../../script/constraint.tcl
compile_ultra
exit
encryptor_run.tcl Solution:
# encryptor_run.tcl
source ../../../script/common_setup.tcl
source ../../../script/dc_setup.tcl
link
read_ddc fifo_mapped.ddc
source ../../../script/constraint.tcl
compile_ultra
exit