The SystemVerilog Hardware Description Language can be used to model a digital circuit during the design stage. Such a design may be simulated using the NC-Verilog digital simulator in order to make early descisions on the viability of a design. The design may then be further refined at this stage before any circuit is implemented. The final SystemVerilog HDL model may then be used as a base for circuit design or directly synthesized to produce a circuit implementation.
This page describes creation and refinement of SystemVerilog HDL model files. It also touches on their simulataion and synthesis. The details of simulation and synthesis are discussed elsewhere.
The SystemVerilog Hardware Description Language offers many more features than I could hope to cover in these pages. This page attempts to choose the most useful parts of the SystemVerilog HDL and illustrate their use with simple examples.
A definitive description of the SystemVerilog constructs used may be found in the Cadence Help on-line manual.
SystemVerilog HDL supports a number of built in logic gates from which larger systems can be constructed. The basic gates are:
Multi-input gates (two or more inputs) |
and or xor nand nor xnor |
e.g. and #delay inst (output, input1, input2, input3 .....); |
Single-input gates (one or more outputs) |
buf not |
e.g. buf #delay inst (output1, output2 ....., input); |
Tri-state gates | bufif1 bufif0 notif1 notif0 |
e.g. bufif1 #delay inst (output, input, enable); |
A simple strucural model is a netlist describing the interconnection of gates to create a module definition.
The following file, dtype.sv, is a structural model for the D type flip-flop:
// structural model of edge triggered D type module dtype( output wire Q, nQ, input wire D, Clk, nRst ); timeunit 1ns; timeprecision 10ps; wire net1, net2, net3, net4; nand #1 nand1 ( net1, net3, D, nRst ); nand #1 nand2 ( net2, net1, net4 ); nand #1 nand3 ( net3, Clk, net1, net4 ); nand #1 nand4 ( net4, Clk, net2, nRst ); nand #1 nand5 ( nQ, nRst, net3, Q ); nand #1 nand6 ( Q, nQ, net4 ); endmodule
The module...endmodule construct delimits the module definition.
The module statement defines the name of the module and is followed be an ordered list of ports in brackets.
Each port or group of ports has a direction and data type associated with it. Q and nQ are declared with direction output while D, Clk and nRst are declared with direction input (note that by convention the outputs are listed first).
The data type for all ports in this example is wire. Since this is the default data type, the keyword wire is optional and will usually be omitted from port lists in subsequent examples.
These should exist at the top of each Verilog module. The timeunit defines the default units for time delays within the module while the timeprecision defines the maximum resolution for such delays.
Here all signals which will be used are declared as single bit wires. This section is optional since an undeclared signal is assumed to be a single bit wire.
Each gate instance line consists of a gate type, a gate delay parameter (in this case #1 indicating 1ns), a unique instance name by which we can distiguish the different gates and an ordered list of wires to be connected to the gate instance. The ordering of these wires should correspond exactly with the ordering of the ports of the gate; for all these gates the only requirement is that the output is listed before the inputs.
The stimulus file for this HDL model is dtype_stim.sv. The corresponding SimVision command script is dtype.tcl.
To simulate this design using the NC-Verilog simulator, copy the relevant files (dtype.sv, dtype_stim.sv & dtype.tcl.) to a suitable directory (e.g. ~/design/verilog/lab/model) and type the following at the unix command prompt:
ncverilog +gui +access+r dtype_stim.sv dtype.sv +tcl+dtype.tcl &
If all goes well a waveform window should open:
The simulation can then be run by typing "run" at the ncsim> prompt in the simulator console window or by pressing the play button: in any of the SimVision windows. Finally select the full view icon, , to see the complete waveforms.
The following file, count.sv, is a structural model for the counter:
// structural model of an "up only" counter module count( output [3:0] count, input up, clock, not_reset ); timeunit 1ns; timeprecision 10ps; dtype dtype0 (count[0], nq0, d0, clock, not_reset); dtype dtype1 (count[1], nq1, d1, clock, not_reset); dtype dtype2 (count[2], nq2, d2, clock, not_reset); dtype dtype3 (count[3], nq3, d3, clock, not_reset); xor #1 xor0 (d0, count[0], up); xor #1 xor1 (d1, count[1], en1); and #1 and1 (en1, count[0], up); xor #1 xor2 (d2, count[2], en2); and #1 and2 (en2, count[1], en1); xor #1 xor3 (d3, count[3], en3); and #1 and3 (en3, count[2], en2); endmodule
More complex designs employ a hierarchy of structural models. This design defines a counter module in terms of basic gates and the d-type flipflop, which is a sub-module defined in the file dtype.sv.
Thus we can redefine the stuctural model as a netlist describing the interconnection of gates and sub-modules to create a module definition.
The stimulus file for this HDL model is count_stim.sv and the corresponding SimVision command script is count.tcl.
Type the following at the unix command prompt:
ncverilog +gui +access+r count_stim.sv count.sv dtype.sv +tcl+count.tcl &
Then run the simulation ().
SystemVerilog HDL supports a number of operators from which expressions can be constructed. The basic operators are:
binary | unary | example | ||
Logical | single bit inputs single bit output |
&& || | ! | Logical AND True && False = False |
Relational | multi-bit inputs single bit output |
== != >= <= | Greater or Equal 9 >= 7 = True |
|
Bit-wise Boolean | multi-bit inputs multi-bit output |
& | ^ ~^ | ~ | Bit-wise XOR 1012 ^ 1102 = 0112 |
Arithmetic | multi-bit inputs multi-bit output |
+ - * / % | - | Modulus 9 % 4 = 1 |
Shift | multi-bit inputs multi-bit output |
>> << | Right Shift 101102 >> 2 = 1012 |
Expressions can be used along with continuous assignment statements to provide behavioural models of combinational logic circuits.
More complex behavioural models, in particular those describing sequential circuits, will employ procedural assignments and more complex procedural statements. Such statements include descision statements such as if...else and case and even looping statements such as while and for.
The following file, window.sv, is a behavioural model of a digital window comparator:
// behavioural model of a digital window comparator module window( output Too_High, OK, Too_Low, input [3:0] Top_Limit, Value, Bottom_Limit ); timeunit 1ns; timeprecision 10ps; assign Too_High = Value >= Top_Limit; assign Too_Low = Value <= Bottom_Limit; assign OK = !Too_High && !Too_Low; endmodule
A single or multi-bit wire may have its value determined as the output of an expression using a continuous assignment statement. The assignment is described as continuous since the expression on the right hand side is continuously monitored allowing any changes to be propagated to the wire on the left hand side of the assignment.
The stimulus file for this HDL model is window_stim.sv and the corresponding SimVision command script is window.tcl.
Type the following at the unix command prompt:
ncverilog +gui +access+r window_stim.sv window.sv +tcl+window.tcl &
Then run the simulation ().
The following file, dff.sv, is a behavioural model of a D type flip-flop:
// behavioural model of edge triggered D type module dff( output logic Q, input D, Clk ); timeunit 1ns; timeprecision 10ps; // action on clock rising edge always_ff @(posedge Clk) Q <= D; endmodule
Q is defined as a logic variable. A variable may be assigned values at regular or irregular intervals. The variable will remember the value assigned until the next time an assignment takes place.
While continuous assignment may be used for wires or for variables, procedural assignments (within always or initial procedural blocks) may only be used for variables.
In SystemVerilog there are four variants to always. These are:
@(posedge Clk) is an event control. In this example we see that every time we have a rising edge on the Clk signal we will assign the value of D to the variable Q. Between rising clock edges, Q remains unchanged regardless of any changes in the value of D.
This is the normal method for the description of a synchronous sequential system.
The stimulus file for this HDL model is dff_stim.sv and the corresponding SimVision command script is dff.tcl.
Type the following at the unix command prompt:
ncverilog +gui +access+r dff_stim.sv dff.sv +tcl+dff.tcl &
Then run the simulation ().
The following file, up_down_count.sv, is a behavioural model of a 4-bit up/down counter:
// behavioural model of an up/down counter module up_down_count( output logic [3:0] count, input up, down, clock, not_reset ); timeunit 1ns; timeprecision 10ps; always_ff @(posedge clock, negedge not_reset) if (!not_reset) // asynchronous reset overrides synchronous action begin count <= 0; end else // action on clock rising edge begin if (up & ~down) count <= count + 1; else if (down & ~up) count <= count - 1; else if (up & down) count <= 0; end endmodule
Here we see that the event control, @(posedge clock, negedge not_reset), will wait for either a rising edge on clock or a falling edge on not_reset.
The subsequent code gives reset priority such that when reset is active, count is held at zero regardless of the clock or other inputs.
This is the normal strategy for the description of an asynchronous reset within a synchronous sequential system.
Note that this module uses a non-blocking type of procedural assignment, X <= Y, rather than a blocking procedural assignment, X = Y.
The non-blocking version evaluates all right-hand-side expressions in a sequence before assigning values to any of the left-hand-side variables.
The difference is indicated by the following piece of code which swaps the values of A and B:
begin A <= B; B <= A; endRather than the alternative which leaves A and B both equal to the old value of B:
begin A = B; B = A; endNote that the non-blocking type of procedural assignment was also used in the previous example for modelling a D-Type.
Failure to follow this convention may result in an individual block that appears to work but which fails to work when built onto a system because the operation of the simulated system depends on the order of evaluation of two statements which are effectively executed in parallel in the final (synthesized) system.
The stimulus file for this HDL model is up_down_count_stim.sv and the corresponding SimVision command script is up_down_count.tcl.
Type the following at the unix command prompt:
ncverilog +gui +access+r up_down_count_stim.sv up_down_count.sv +tcl+up_down_count.tcl &
Then run the simulation ( ).
The following file, alt_window.sv, is a behavioural model of a digital window comparator:
// alternative behavioural model of a digital window comparator module window( output logic Too_High, OK, Too_Low, input [3:0] Top_Limit, Value, Bottom_Limit ); timeunit 1ns; timeprecision 10ps; always_comb begin Too_High = Value >= Top_Limit; Too_Low = Value <= Bottom_Limit; OK = !Too_High && !Too_Low; end endmodule
An always_comb procedural block can be used to describe a combinational logic block provided that:
In early versions of Verilog this would be written as always @(*) indicating that any change on any input can trigger the block. With always_comb, the @(*) event control is implied and should not be explicitly written.
Type the following at the unix command prompt:
ncverilog +gui +access+r window_stim.sv alt_window.sv +tcl+window.tcl &
Then run the simulation ().
(note that in this example the file alt_window.sv contains a module name window rather than one named alt_window - this practice is generally to be avoided but it has meant that we can re-use the same stimulus file)The microprocessor is described in the following SystemVerilog files:
Most complex behavioural models are constructed as a hierarchy of simpler behavioural models.
The Processor module instances the Control, Datapath and Memory modules. It is in fact merely a structural descrition of their interconnection:
control Control ( Data_bus, Address, Operation, Mem_Write, Zflag, Clock, notReset ); datapath Datapath ( Data_bus, Zflag, Operation, Clock, notReset ); memory Memory ( Data_bus, Address, Mem_Write, Clock );
The Datapath module instances the ALU module but also contains the behavioural description of the Accumulator:
alu ALU ( zflag, ALU_output, Accumulator, data, func);
The following construct describes a multiplexor which selects input1 if select is `1' and input0 if select is `0':
The Control module includes an address multiplexor based on this construct:
assign address = (state == FETCH) ? Program_Counter : operand;
An always_comb procedural block can be used to describe a combinational logic block provided that: the values of all ouputs are defined for all possible combinations of inputs.
The ALU module uses such a construct to evaluate result:
always_comb case (operation) LDA : result = inputB; ADD : result = inputA + inputB; SUB : result = inputA - inputB; AND : result = inputA & inputB; OR : result = inputA | inputB; NOT : result = ~inputA; LSL : result = inputA << 1; LSR : result = inputA >> 1; default : result = inputA; endcase
Note that including a default case covers the requirement to specify the value of the output (result) for all values of the input (func).
The Datapath module may drive the Data_bus, locally known as data, from the Accumulator:
inout [15:0] data,
assign data = ( func == STA ) ? Accumulator : 16'bz;
Note the inout declaration for data indicating that it is both an input and an output for the module.
The Memory module may also drive the Data_bus:
inout [15:0] data,
assign data = (!write) ? Data_stored [address] : 16'bz;
The opcodes.sv file declares constants to be used in other modules:
localparam AND = 4'd7; localparam OR = 4'd8; localparam NOT = 4'd9;
The alu.sv file includes the opcodes.sv file and then uses the constants within the alu module:
module alu( output logic zflag, output logic [15:0] result, input [15:0] inputA, inputB, input [3:0] operation );
`include "opcodes.sv"
case (operation)
AND : result = inputA & inputB; OR : result = inputA | inputB; NOT : result = ~inputA;
endcase endmodule
The stimulus file for this HDL model is system.sv and the corresponding SimVision command script is system.tcl.
Before simulating this design, first copy the stimulus file and the SimVision command script to a suitable directory and then create a sub directory called library into which you should copy the model files for the microprocessor. The appropiate simulation command is:
ncverilog +gui +access+r -y library +libext+.sv +incdir+library system.sv +tcl+system.tcl &Notes:
Execute the simulation command and run the simulation ( ).
All SystemVerilog and Verilog documentation is available on-line via Cadence Help. Type the following at the unix command prompt in order to invoke Cadence Help:
cdnshelp_for ncverilog &
On-line manuals of particular interest to this tutorial are:
Iain McNally
24-10-2024