TFE4171: Design of Digital Systems 2
This compendium is based on the subject’s book as well as Donn Morrison’s lecture slides.
SystemVerilog Assertions and Functional Coverage. A. Mehta. Springer. Free download (while connected through NTNU) ISBN 978-1- 4614-7324-4
SystemC from the Ground Up. David C. Black, Jack Donovan, Bill Bunton, Anna Keist. Free download (while connected through NTNU) ISBN 978-0-387-69958-5
What is SytemVerilog?
SystemVerilog is a Hardware description and verification language (HDVL). It has an extensive set of enhancements to IEEE 1364 Verilog-2001 standards. It uses features inherited from Verilog HDL, VHDL, C, C++.
Some advantages using systemVerilog:
- Constrained randomisation
- OOP support
- Assertions (SVA - this course!)
- Coverage support
- New data types (logic)
- Easy C model integration
- Narrows the gap between design and verification engineer
Verilog vs. SystemVerilog
Verilog is strict about usage of wire & register data types. Variable types are 4-state (0,1,X,Z). SystemVerilog’s logic data type can be used so no need to worry about register & wire. It also has 2-state data type added (0,1), 2-state variable that can be used in testbenches, where X, Z not required and 2-state variable in RTL model that may enable simulators to be more efficient.
Memories in Verilog are static in nature, while SystemVerilog’s memories are dynamic. They are also allocated at runtime.
SystemVerilog is OOP.
And a lot more.
SystemVerilog vs. C++
C++ is unrelated to Verilog and require interface to interact with Verilog. SystemVerilog is a superset of Verilog as well as being a RTL/verification language, Assertion language, Constraint language and Code coverage language.
Example of OOP
class A; // Attributes: int i; // Methods: task print; endclass
class simple; int i; int j; task printf(); \$display( i, j ); endtask endclass
program main; initial begin simple obj_1; simple obj_2; obj_1 = new(); obj_2 = new(); obj_1.i = 2; obj_2.i = 4; obj_1.printf(); obj_2.printf(); end endprogram
2 0 4 0
SystemVerilog can do random generation of stimuli and random setting of parameters to reach hard-to-reach corner cases. This makes a shift from direct to random debugging.
Why allow randomisation? Direct debugging allows us to detect expected bugs and it is time consuming. By adding randomisation we will detect unexpected bugs (corner cases) while drastically reduces effort.
Constrained randomisation does not only improves the result, but also speed up bug finding process so more interesting cases can be achieved within the constrained boundary.
- A positive statement about a property. Should the statement be evaluated as false, it indicates an error.
Since the property only states the behaviour, it is often used to ensure that the design implementation of the behaviour matches the assertion.
- Primarily used to validate design behaviour
- A statement about a design's intended behaviour
- Inline assertions best added by design engineers
- Interface assertions best added by verification engineers
- Sole purpose is to ensure consistency between designer’s intention and design implementation
- Increases the bug detection possibility during RTL design phase
Example shows that when FRAME_ asserted, LDP_ must be deasserted.
By using assertions in SystemVerilog:
property ldpcheck; @(posedge clk) \$rose (FRAME_) |-> ##[1:2] \$fell(LDP_); endproperty aP: assert property (ldpcheck) else \$display("ldpcheck failed"); cP: cover property (ldpcheck) \$displey("ldpcheck pass");
At posedge, if FRAME_ rises, it implies that within the next 2 clocks LDP_ falls
One way to write the same in Verilog:
always @(posedge FRAME_) begin: ldpcheck @(posedge clk); fork begin @(negedge LDP_) disable ldpcheck; end begin repeat (2) @(posedge clk); \$display("ldpcheck fail"); disable ldpcheck; end join end
SVA increase productivity of design/debug/simulate/cover loop compared to using testbenches in traditional HDLs. It also makes it easier to code compared to plain Verilog or SystemVerilog. It will shorten time to deveop, improve observability and provide temporal domain functional coverage.
Guidelines on adding assertions
- Don’t duplicate RTL
- Add assertions throughout RTL design process
- If test failed and no assertions fired, you need more assertions!
- Assertions for every critical function
- Reuse: create libraries of generic properties for future projects
Assertion building blocks
Temporal delay ##n (where n is an integer).
Overlapping sequence implication operator uses |->. Non-overlapping sequence implication operator uses |=>.
start |=> go
is the same as
start |-> ##1 go
Creating named properties Example using SystemVerilog:
property p_valid_transaction; @(posedge clk) ready ##1 start|=> go ##1 done; endproperty ... assert module (p_valid_property);
Arbiter is a way to automatomate the selection of what resource should get priority.
Verilog code for arbiter checking:
module arbiter (clk, rst_n, req0, req1, grant0, grant1); ... always @(posedge clk or negedge rst_n) begin if (rst_n != 1'b0) if (grant0 & grant1) \$display("Error: Grant not mutex); ... endmodule
SVA code for arbiter checking:
module arbiter (clk, rst_n, req0, req1, grant0, grant1); ... assert property ( @(posedge clk) disable iff ( ~rst_n ) !(grant0 & grant1) ); ... endmodule
There are three types of assertions supported by SVA; Immediate assertions, Concurrent assertions and Deferred assertions.
- Simple non-temporal domain, executed like statements in a procedural block
- Interpreted like 'if' conditionals
- Can be specified only in procedures
- Temporal domain assertions
- Allow creation of complex sequences using clock-based semantics
- Type of immediate assertion
- Evaluated at end of timestamp
Assertion types example
module m2(input logic c, d, clk); logic a, b; always_comb begin a = c & d; b = c | d; // immediate assertion a1: assert (a -> b); // deferred assertion a2: assert #0 (a -> b); // concurrent assertion a3: assert property ( @clk a != b ) end // deferred assertion a4: assert #0 (a -> b); // concurrent assertion a5: assert property ( @clk a != b ) endmodule : m2
Immediate assertions is Simple non-temporal domain, executed like statements in a procedural block. It can be interpreted like 'if' conditionals and can be specified only in procedures.
It is used to check to see if a conditional expression holds.
module my_arb (...); ... always @* begin // arbiter code ... if (rst_n) assert @* begin // arbiter code ... end endmodule
More about immediate assertions
- Do not place RTL code in the assert block, it will be ignored by synthesizer
- Immediate assertion cannot be used in a continuous assign statement → not a procedural block
- Glitch prone - assertion may be evaulated multiple times before expression variable values settle down
Immediate assertion cannot be used in continuous assign because it is a non-procedural statement. This will result in a compile time error. For example:
assign arb = assert ( a || b ) ; // Illegal statement
- Temporal domain assertions that allow complex sequences using clock edge semantics
- They are the gist of SVA
- Execute in parallel with the rest of the design logic
- Concurrent assertions evaluated only at the occurrence of a clock tick
- Clock tick definition explicitly specified by user
- Assertion without a clock will result in a compile error
- Clock expression can be more complex than a single signal name (e.g., CLK && Gating_signal)
At posedge clk, if cStart is high, ‘req’ should be high in the same clock and ‘gnt’ becomes high 2 clocks later.
sequence sr1; req ##2 gnt; endsequence property pr1; @(posedge clk) cStart |-> sr1; endproperty reqGnt: assert property (pr1) \$display($time,,,"\t\t %m Pass"); else \$display($stime,,,"\t\t %m Fail");
sr1 states that if req is true this clock cycle, then gnt must be true two clock cycles later. pr1 trigger sr1 at clock event. reqGnt just label the property, and is optional.
In this example, req, gnt and cStart are sampled at the same sample edge. In this example that is at a rising clock edge.
Example 2: Antecedents, consequents, implications
Let’s modify the sequence ‘sr1’ slightly to highlight boolean expression in a sequence or property. Three parts of the expression determine when an assertion will fire:
- The condition (LHS) under which assertion will be fired is called an antecedent
- RHS of the assertion that executes once antecedent matches is called the consequent
The implication operator determines time duration that will lapse between antecedent and consequent in two ways: overlapping and non-overlapping
property pr1; @(posedge clk) cStart |-> req ##2 ( gnt == 1 && req == 0 ); endproperty reqGnt: assert property (pr1) \$display($stime,,,"\t\t %m Pass"); else \$display($stime,,,"\t\t %m Fail");