How To Simulate With ModelSim
How To Simulate With ModelSim
Quartus includes a limited simulation feature but for more complex simulations, the answer
is ModelSim. Two big advantages of ModelSim are that, compared to Quartus, compiles are
lightning quick and you don’t need an FPGA board to run your code.
To use ModelSim, you need to add a testbench module to your Verilog file and a few script
files to your Quartus project directory. The examples shown here have been posted as
Simulation.zip to files area in Canvas. Download and unzip it.
Here is the basic skeleton you’re using for the lab 2 adding machine project, suitable for
compiling and running on the DE1-SoC boards. SevenSegment is a partially specified seven
segment decoder module.
module SevenSegment(
input [ 3:0 ] hexDigit,
output [ 6:0 ] segments );
assign b0 = hexDigit[ 0 ];
assign b1 = hexDigit[ 1 ];
assign b2 = hexDigit[ 2 ];
assign b3 = hexDigit[ 3 ];
endmodule
1
The AddingMachine module is stubbed to exercise driving one of seven segment modules
on the DE1-SoC with a four-bit value entered on the switches.
module AddingMachine(
output [ 6:0 ] HEX0, HEX1, HEX2, HEX3, HEX4, HEX5,
output [ 9:0 ] LEDR,
input [ 9:0 ] SW );
endmodule
The list of ports has been cleaned up somewhat from a template created by SystemBuilder.
Any DE1-SoC signals your module refers to are matched up by name, not order, against
signals defined in AddingMachine.qsf (the Quartus Settings File).
2
The testbench
To simulate this, exercising the SevenSegment module, several testbenches are offered that
all do the same thing but are coded slightly differently. This first one simply lists the 16
possible values, waiting 10 ps between assignments.
module testbench1( );
initial
begin
hex = 0; #10;
hex = 1; #10;
hex = 2; #10;
hex = 3; #10;
hex = 4; #10;
hex = 5; #10;
hex = 6; #10;
hex = 7; #10;
hex = 8; #10;
hex = 9; #10;
hex = 10; #10;
hex = 11; #10;
hex = 12; #10;
hex = 13; #10;
hex = 14; #10;
hex = 15; #10;
end
endmodule
The initial block tells the simulator what to do when it starts. It does not result in any
circuitry when compiled to the FPGA. The #10; statement says wait 10 ticks = 10 ps before
going to the next statement.
3
In this variation, each assignment itself is delayed 10 ps. (If you write #10; with nothing
between the 10 and semicolon, what you get is a null statement that’s delayed 10 ps.)
module testbench2( );
initial
begin
hex = 0;
#10 hex = 1;
#10 hex = 2;
#10 hex = 3;
#10 hex = 4;
#10 hex = 5;
#10 hex = 6;
#10 hex = 7;
#10 hex = 8;
#10 hex = 9;
#10 hex = 10;
#10 hex = 11;
#10 hex = 12;
#10 hex = 13;
#10 hex = 14;
#10 hex = 15;
end
endmodule
4
As delivered, this is the one that’s set up to be used. In this variation, an integer i is used
as a control variable in for loop to walk through the 16 states, waiting 10 ps after each.
module testbench( );
initial
for ( i = 0; i < 16; i = i + 1 )
begin
hex = i; #10;
end
endmodule
These are the important files added for the simulation, based on the originals created by
Professor Scott Hauck at UW Seattle. All of them are editable text.
1. Launch_ModelSim.bat
2. Simulate.do
3. wave.do
To add simulation to a project of your own, copy these files into that directory, then edit
them as appropriate, as shown below.
1. Start the simulator using LaunchModelSim.bat. (You may have to fix the path
specified in this file.)
2. In the ModelSim Transcript window (its command line) type "do Simulate.do".
5
You should see it display a simulation Wave window showing the hex input changing and
individual segment outputs.
Here’s a zoom in on a part of it. Because the equations haven't been filled in yet for
segments[6:0], they are in red, meaning their values are unknown.
You can change the format, e.g., magnifying the timescale or adding or deleting signals or
how they’re displayed, then save the result through File Save Format … on the menu bar.
It will suggest overwriting the wave.do file so that the next time you type “do Simulate.do”,
the wave display will open automatically in that new format.
6
The simulator files
Here are the contents of the files. Note the sections highlighted in yellow that may have to
be fixed to match your system or will have to be changed if you copy them into your own
project.
Launch_ModelSim.bat
C:\altera\16.1\modelsim_ase\win32aloem\modelsim.exe
Simulate.do
# Compile Verilog
# All Verilog files that are part of this design should have
# their own "vlog" line below.
vlog "./AddingMachine.v"
# End
7
wave.do
onerror {resume}
quietly WaveActivateNextPane {} 0
add wave -noupdate /testbench/hex
add wave -noupdate -expand /testbench/segments
TreeUpdate [SetDefaultTree]
WaveRestoreCursors {{Cursor 1} {40 ps} 0}
quietly wave cursor active 1
configure wave -namecolwidth 150
configure wave -valuecolwidth 100
configure wave -justifyvalue left
configure wave -signalnamewidth 1
configure wave -snapdistance 10
configure wave -datasetprefix 0
configure wave -rowmargin 4
configure wave -childrowmargin 2
configure wave -gridoffset 0
configure wave -gridperiod 300
configure wave -griddelta 40
configure wave -timeline 0
configure wave -timelineunits ps
update
WaveRestoreZoom {3 ps} {67 ps}
8
Mux2To1 example
This example shows a number of different ways of coding a 2-to-1 mux. Following the
same procedure, launching ModelSim, then typing “do Simulate.do”, you should get
something approximating the following result.
9
The testbed
Here’s the testbed for the six different mux implementations. The first three are variations
on a theme, using individual gate primitives. The next two use continuous assignments and
the last uses a behavioral specification.
module testbench( );
initial
begin
// test with all possible inputs
{ s, x1, x2 } = 0;
for ( i = 0; i < 8; i = i + 1 )
#10 { s, x1, x2 } = i;
endmodule
As you already know from lab 1, module A1, shown here, contains a static 1 hazard: When
both x1 and x2 equal 1 and s switches from 1 0, the output f briefly glitches to 0.
module Mux2To1A1(
input x1, x2, s,
output f );
wire g, h, k;
not ( k, s );
and ( g, k, x1 );
and ( h, s, x2 );
or ( f, g, h );
endmodule
10
You can’t easily see the glitch in the output of A1 because it’s just one missing pixel in the
green trace.
Here it is magnified.
11
To see what’s happening more clearly, right-click on the name of the signal, f, and choose
Format Literal to make it clearer what's happening.
12
In literal format as shown here, the X shows where the output glitches at 90 ps even though
both x1 and x2 are 1.
In A2, the gates are simulated with 1-tick = 1 ps delays. Again, assume x1 and x2 are 1.
Now the glitch caused by the static 1 hazard is clearly visible.
module Mux2To1A2(
input x1, x2, s,
output f );
wire g, h, k;
not #1 ( k, s );
and #1 ( g, k, x1 );
and #1 ( h, s, x2 );
or #1 ( f, g, h );
endmodule
13
Zooming in, k and h switch 1 ps after s transitions
from 1 to 0. g switches 2 ps after s, creating a 1 ps
window when both g and h are 0, creating the glitch
in f one gate delay = 1 ps later.
In A3, the hazard is eliminated by adding back the non-essential prime implicant x1 x2
shown in green in the Karnaugh map. When both x1 and x2 are 1, it doesn't matter which
is selected. Adding this term back eliminates the hazard caused by switching between
adjacent 1s in the Karnaugh map that aren’t in the same prime implicant.
module Mux2To1A3( x1 x2
input x1, x2, s, 00 01 11 10
output f );
s 0 0 0 1 1
wire g, h, j, k; 1
1 0 1 0
not #1 ( k, s );
and #1 ( g, k, x1 );
and #1 ( h, s, x2 );
and #1 ( j, x1, x2 ); // Eliminate hazard
or #1 ( f, g, h, j );
endmodule
14
All of the remaining mux implementations are hazard-free because the Verilog compiler
detects the hazard and automatically adds in the extra term.
module Mux2To1B(
input x1, x2, s,
output f );
assign f = s ? x2 : x1;
endmodule
module Mux2To1C(
input x1, x2, s,
output f );
endmodule
module Mux2To1D(
input x1, x2, s,
output reg f );
always @( * )
if ( s )
f = x2;
else
f = x1;
endmodule
15
The simulator files
Here are the contents of the files. As before, sections highlighted in yellow that may have
to be fixed to match your system or will have to be changed if you copy them into your own
project.
Launch_ModelSim.bat
C:\altera\16.1\modelsim_ase\win32aloem\modelsim.exe
Simulate.do
# Compile Verilog
# All Verilog files that are part of this design should have
# their own "vlog" line below.
vlog "./Mux2To1.v"
# End
16
wave.do
onerror {resume}
quietly WaveActivateNextPane {}
add wave -noupdate /testbench/s
add wave -noupdate /testbench/x1
add wave -noupdate /testbench/x2
add wave -noupdate -expand -group A1 /testbench/A1/s
add wave -noupdate -expand -group A1 /testbench/A1/k
add wave -noupdate -expand -group A1 /testbench/A1/g
add wave -noupdate -expand -group A1 /testbench/A1/h
add wave -noupdate -expand -group A1 /testbench/A1/f
add wave -noupdate -expand -group A2 /testbench/A2/s
add wave -noupdate -expand -group A2 /testbench/A2/k
add wave -noupdate -expand -group A2 /testbench/A2/g
add wave -noupdate -expand -group A2 /testbench/A2/h
add wave -noupdate -expand -group A2 /testbench/A2/f
add wave -noupdate -expand -group A3 /testbench/A3/s
add wave -noupdate -expand -group A3 /testbench/A3/k
add wave -noupdate -expand -group A3 /testbench/A3/g
add wave -noupdate -expand -group A3 /testbench/A3/h
add wave -noupdate -expand -group A3 /testbench/A3/j
add wave -noupdate -expand -group A3 /testbench/A3/f
add wave -noupdate -expand -group B /testbench/B/f
add wave -noupdate -expand -group C /testbench/C/f
add wave -noupdate -expand -group D /testbench/D/f
TreeUpdate [SetDefaultTree]
WaveRestoreCursors
quietly wave cursor active 0
configure wave -namecolwidth 85
configure wave -valuecolwidth 81
configure wave -justifyvalue left
configure wave -signalnamewidth 1
configure wave -snapdistance 10
configure wave -datasetprefix 0
configure wave -rowmargin 4
configure wave -childrowmargin 2
configure wave -gridoffset 0
configure wave -gridperiod 300
configure wave -griddelta 40
configure wave -timeline 0
configure wave -timelineunits ps
update
WaveRestoreZoom {84 ps} {175 ps}
17
Simple counter example
This example shows how a clocked sequential circuit might be simulated, where it’s
necessary to provide a clock signal.
Here are two simple variations on a counter, one with a synchronous reset and the other
with an asynchronous reset. Notice the use of initial values for the reg variables.
module CounterA(
input clock, reset,
input [ 31:0 ] resetValue,
output reg [ 31:0 ] count = 0 );
endmodule
module CounterB(
input clock, reset,
input [ 31:0 ] resetValue,
output reg [ 31:0 ] count = 0 );
endmodule
18
Here is the simple wrapper for the DE1-SoC board, tying the high-order 10 bits of the
counter to the LEDs, the reset value to the switches and the reset button to KEY[ 3 ]. As
written here, it uses the counter with the synchronous reset. (To a human observer, there’s
no difference.)
module SimpleCounter(
input CLOCK_50,
input [ 3:0 ] KEY,
output [ 9:0 ] LEDR,
input [ 9:0 ] SW );
endmodule
This example can also be used to demonstrate the SignalTap II logic analyzer for debugging
with the DE1-SoC.
19
Here is the testbench. Notice the use of multiple initial blocks. They all start when the
simulation starts, then run independently in no guaranteed order of execution. When
$stop; is encountered inside any block, the entire simulation ends.
module testbench( );
// Create a clock
parameter clockPeriod = 4;
initial
forever
#( clockPeriod / 2 ) clock <= ~clock;
initial
begin
value = 'h123; // @ t = 0
# 9 reset = 1; // @ t = 9
# 8 reset = 0; // @ t = 9 + 8 = 17
#14 reset = 1; // @ t = 17 + 14 = 31
# 4 reset = 0; // @ t = 31 + 4 = 35
#20; // @ t = 35 + 20 = 55
$stop; // @ t = 55
end
endmodule
20
Launching ModelSim and typing “do Simulate.do” as before, you should get something like
this.
Zooming in:
CounterA CounterB
always @( posedge clock ) always @( posedge reset, posedge clock )
count <= reset ? count <= reset ?
resetValue : count + 1; resetValue : count + 1;
Notice how the resets behave differently based on how the always blocks have been
written. In CounterA, the reset doesn’t happen until the next clock edge in CounterA
because it doesn’t enter the always until then. This is a synchronous reset because it
happens with the clock.
In CounterB, where the reset was listed in the always sensitivity list, the reset happens
immediately. This is an asynchronous reset because it’s not synchronized to (i.e., made to
happen at the same time as) the clock.
21
The simulator files
Here are the contents of the files. As before, sections highlighted in yellow that may have
to be fixed to match your system or will have to be changed if you copy them into your own
project.
Launch_ModelSim.bat
C:\altera\16.1\modelsim_ase\win32aloem\modelsim.exe
Simulate.do
# Compile Verilog
# All Verilog files that are part of this design should have
# their own "vlog" line below.
vlog "./SimpleCounter.sv"
# End
22
wave.do
onerror {resume}
quietly WaveActivateNextPane {} 0
add wave -noupdate /testbench/clock
add wave -noupdate /testbench/reset
add wave -noupdate -radix hexadecimal /testbench/value
add wave -noupdate -radix hexadecimal /testbench/countA
add wave -noupdate -radix hexadecimal /testbench/countB
TreeUpdate [SetDefaultTree]
WaveRestoreCursors {{Cursor 1} {0 ps} 0}
quietly wave cursor active 0
configure wave -namecolwidth 107
configure wave -valuecolwidth 81
configure wave -justifyvalue left
configure wave -signalnamewidth 1
configure wave -snapdistance 10
configure wave -datasetprefix 0
configure wave -rowmargin 4
configure wave -childrowmargin 2
configure wave -gridoffset 0
configure wave -gridperiod 300
configure wave -griddelta 40
configure wave -timeline 0
configure wave -timelineunits ps
update
WaveRestoreZoom {0 ps} {20 ps}
23