Module_3
Module_3
Dr.Rekha Phadke
System Verilog Interfaces & Verification Examples
1. Limitations of Raw Verilog Signal
Lists
• Interfaces group related signals into a single bundle, improving modularity and reuse.
• interface bus_if #(parameter WIDTH = 8) (input logic clk);
logic [WIDTH-1:0] data;
logic valid, ready;
endinterface
module top;
logic clk = 0;
bus_if #(16) b_if_inst (.clk(clk));
always #5 clk = ~clk;
endmodule
3. Port Connections Using Interfaces
• interface bus_if #(parameter WIDTH = 8)
(input logic clk);
• Passing an interface instance as a single port simplifies wiring and ensures all signals stay logic [WIDTH-1:0] data;
together.
logic valid, ready;
• module producer(bus_if b); endinterface
always @(posedge b.clk) begin
b.data <= $urandom;
b.valid <= 1; module top;
end logic clk = 0;
endmodule bus_if #(16) b_if_inst (.clk(clk));
always #5 clk = ~clk;
module consumer(bus_if b);
always @(posedge b.clk) begin endmodule
if (b.valid) begin
$display("Got %0d", b.data);
b.ready <= 1;
end
end
endmodule
module top;
logic clk = 0;
bus_if b_if (.clk(clk));
producer P(.b(b_if));
consumer C(.b(b_if));
always #5 clk = ~clk;
endmodule
• //Example 1 on UNIT 3 //Run on EDA Playground
module top;
logic clk = 0;
axi_if #(.ADDR_W(16), .DATA_W(32)) axi0 (.clk(clk));
always #5 clk = ~clk;
endmodule
5. Interface Modports:
Without modport?
You'd have to:
Manually declare signal directions in each module.
Risk signal conflicts if both modules try to drive the same wire.
Lose clarity on which side "owns" each signal.
• Modports define directional views of the interface for different module roles, enforcing signal direction rules.
• interface bus_if(input logic clk);
logic [7:0] data;
logic valid, ready;
• end
• end
• endmodule
6. Interface References
• Passing an interface by reference to tasks/functions allows modifications to affect the original instance.
• interface bus_if;
logic [7:0] data;
logic valid, ready;
endinterface
program tb;
bus_if b_if();
initial begin
init_if(b_if);
$finish;
end
• @(posedge clk); • byte data_received = 8'h00; • // Instantiate master and slave modules
• mosi = tx_data[i]; • integer i; • spi_master master(spi);
• sclk = 1; @(posedge clk); • spi_slave slave(spi);
• rx_data[i] = miso; • always @(negedge spi.cs) begin
• sclk = 0; • i = 7; • // Simulation control
• end • data_received = 8'h00; • initial begin
• $display("Master sent: 0x%0h, received: 0x%0h", tx_data, • while (!spi.cs && i >= 0) begin • #200;
rx_data); • @(posedge spi.sclk); • $finish;
• endtask • data_received[i] = spi.mosi; • end
• endinterface • spi.miso = data_received[i]; // echo • endmodule
• i--;
• end
• $display("Slave received: 0x%0h", data_received);
• end
• endmodule
Module 3-Part 3:
Dr.Rekha Phadke
System Verilog Interfaces & Verification Examples
8. Verilog Event Scheduler Phases
• Simulation events occur in four phases: Active, Inactive (#0), Non-blocking update, and Monitor (display).
• module sched_test;
logic a, b;
initial begin
a = 0; b = 0;
a = 1; // Active
#0 b = 1; // Inactive stage
a <= 0; // Scheduled for non-blocking update
#1;
$display("Time=%0t a=%0b b=%0b", $time, a, b);
$finish;
end
endmodule
SystemVerilog Simulation Phases:
(
1.Active: Procedural statements = blocking assignments, if, for, etc.)
(
3.Non-blocking Update: Updates from <= non-blocking assignments happen here)
Breakdown:
• initial begin •a = 1 → executed in Active region
•#0 b = a → scheduled to Inactive, reads a = 1
•a <= 0 → scheduled to Non-blocking update, will update
• a = 0; b = 0; a to 0
•$display → happens in Monitor phase, but shows values
at the time it was called
• a = 1; // Active •$strobe → also in Monitor, but shows final values
after all updates at this time
• #0 b = a; // Inactive [$display] Time=0 a=1 b=1
• a <= 0; // Non-blocking update [$strobe ] Time=0 a=0 b=1
• $display("[$display] Time=%0t a=%0b b=
%0b", $time, a, b);
• $strobe ("[$strobe ] Time=%0t a=%0b b=
%0b", $time, a, b);
• #1;
• $finish;
• end
• endmodule
Example 3:
• module display_monitor_strobe_test;
• logic a, b;
• initial begin
• a = 0; b = 0;
• [$monitor] Time=0 a=1
b=0
• // Enable automatic monitoring of 'a' and 'b'
• $monitor("[$monitor] Time=%0t a=%0b b=%0b", $time, a, b);
•
•
a = 1;
#0 b = a;
// Active phase
// Inactive phase • [$monitor] Time=0 a=1
• a <= 0; // Non-blocking update (NB update)
b=1
• // Manual snapshot
• $display("[$display] Time=%0t a=%0b b=%0b", $time, a, b);
• [$monitor] Time=0 a=0
•
•
// Snapshot after NB updates
$strobe ("[$strobe ] Time=%0t a=%0b b=%0b", $time, a, b); b=1
•
•
#1;
$finish;
• [$display] Time=0 a=1 b=1
• end
• endmodule • [$strobe ] Time=0 a=0 b=1
Example 4
• module example4;
• reg a, b;
• initial begin
• a = 1;
• b = 0;
• a <= b;
• b <= a;
• $display("Value of a=%b, b=%b", a, b);
• end
• endmodule
Example 5
module example5;
reg a, b;
initial begin
a = 1;
b = 0;
a <= b;
b <= a;
$monitor("Value of a=%b, b=%b", a, b);
end
endmodule
Example 6 Simulation output:
• module example3; Time Action Value of a
• reg [2:0] a;
0 a <= 3'd5 101
• initial begin
• a <= 3'd5; // At time 0 a <= 3'd0 (delayed
5 000
assignment)
• a <= #5 3'd0; // Schedule a = 000 at time 5
• a <= #10 3'd7; // Schedule a = 111 at time 10 10
a <= 3'd7 (delayed
111
assignment)
• $monitor("Value of a=%b", a); // Continuously monitor 'a'
• end
• endmodule
Simulation output:
Example 8 Value of a=x
• module example3;
• reg [2:0] a;
• initial begin
• a <= 3'd5; // At time 0
• a <= #5 3'd0; // Schedule a = 000 at time 5
• a <= #10 3'd7; // Schedule a = 111 at time 10
• $display("Value of a=%b", a);
• end
• endmodule
Converge before time advances
Verilog keeps doing these zero‑time “delta” iterations—Active → Inactive → NBA
→ Postponed → back to Active—until no further events fire, and then it finally
moves on to the next real time slot.
• reg a, b;
• wire c;
• initial begin
• a = 0; // t=0 Active: a←0
• a <= 1; // t=0 NBA: schedule a←1
• // no always block here, but c will update when a does
• #1 $display("c=%b", c);
• end •At t=0, Active runs a=0.
•Still at t=0, Inactive (none), then NBA writes a←1.
•That write to a triggers the continuous assign c = a;, so c is scheduled into the Active queue (a new delta cycle at t=0).
•In that same delta cycle’s Active region, c gets updated to 1 before we ever advance to t=1.
•Finally at #1, your display will see c=1.
That chain—NBA update → event on a → continuous‑assign or always block in Active—is exactly what “NBA updates can re ‑trigger Active events” means.
DUT and Naïve Testbench:
• // DUT: simply registers its • Why this races
// testbench: drives data_in at the same edge it’s sampled
• module tb; On every posedge clk both
input to its output on each 1.the DUT’s always @(posedge clk) wants to sample data_in
• logic clk;
posedge clk • logic [7:0] data_in, data_out;
2.the TB’s always @(posedge clk) wants to drive data_in
But both happen in the same simulation time and in the same simulation
• module dut ( •
region (the “active” region), so the simulator gets to choose which one
dut u_dut(.clk(clk), .data_in(data_in), .data_out(data_out));
happens first. That gives you two possible outcomes:
• input logic clk, In other words, you can’t predict whether the register in the DUT will see
the “old” or “new” value of data_in—it’s a classic race.
• // free‐running clock
• input logic [7:0] data_in, • initial clk = 0;
• output logic [7:0] • always #5 clk = ~clk;
data_out
• // at each rising edge, pick a new random data_in
• ); • always @(posedge clk) begin Step 1 at Step 2 at
• always @(posedge clk) • data_in = $random; Scenario Result
posedge posedge
begin • end
TB then
DUT samples data_out ≤
• data_out <= data_in; A old
updates to
old data_in
• initial begin new
• end • #100 $finish; TB drives DUT then data_out ≤
B new first samples new new data_in
• endmodule • end
• endmodule
SV Event Control & Clocking Block
How a Clocking Block fixes it:
A clocking block is used to manage the timing of inputs and outputs in relation to the clock signal. It specifies when inputs and outputs are sampled or driven relative to a clock edge, and it can also apply skew (delays) to avoid issues like race conditions.
module tb;
logic [7:0] data_in, data_out;
assign cb.data_in = data_in;
assign data_out = cb.data_out;
initial begin
data_in = 8'hFF;
#20 data_in = 8'h00;
#20 $finish;
end
endmodule
• module dut(input logic [7:0] data_in, output logic [7:0] data_out); • // Initial stimulus
• always_comb begin • initial begin
• data_out = data_in + 1; // Simple operation • sent_value = 8'hFF;
• end
• cb.data_in <= sent_value;
• endmodule
• #20;
• module tb;
• sent_value = 8'h00;
• logic clk = 0;
• cb.data_in <= sent_value;
• logic [7:0] data_in;
• #20;
• logic [7:0] data_out;
• $finish;
• logic [7:0] sent_value;
• end
• // Instantiate DUT
• // Monitor sampled values (at correct phase)
• dut u_dut (
• always @cb begin
• .data_in(data_in),
• $display("Time=%0t | Sent: 0x%0h | Received: 0x%0h", $time,
• .data_out(data_out) sent_value, cb.data_out);
• ); • end
• endmodule
• // Generate clock: 10 time units period
• end
• $finish;
• end
• endmodule
DUT updates
TB drives data_in DUT samples CB samples
Cycle Clock Edge (t) data_out (NBA TB prints (@t+2)
(@t+1) data_in (@t) @t) data_out (@t+2)
•Branch Coverage
• Measures whether each branch of control statements (if, case, for, etc.) has been
taken.
• Ensures all decision points are tested.
•Condition Coverage
• Ensures all boolean sub-expressions in a decision have been evaluated to both true
and false.
in SystemVerilog
design has been exercised by your testbench. Unlike code coverage, which
is structural and automatic, functional coverage is explicitly defined by the
verification engineer to track important features, scenarios, or corner cases
of the design.
• Covergroups specify functional coverage points and crosses for a verification plan, driving metrics.
Covergroup definition:
covergroup cg_group;
<definition of covergroup>
endgroup: cg_group
• They can be placed inside a module or named block.
• Syntax for creating coverblock instances:
cg_group=new();
Example: file name on EDA: GRQG
• // Code your design here • // Instantiate covergroup
• module coverage_example;
• cg cov = new();
• // Declare signals • // Stimulus generation
• logic clk;
• logic [3:0] data; • initial begin
• logic valid;
• // Run for a certain number of cycles
• // Clock generation: 10 time units period • repeat (10) begin
• initial clk = 0;
• always #5 clk = ~clk;
• @(posedge clk);
• data = $urandom_range(0, 15); // Random 4-bit data
• // Covergroup Definition
• covergroup cg; • valid = $urandom_range(0, 1); // Random 1-bit valid
• coverpoint data { • cov.sample(); // Manually trigger coverage sampling
• bins low = {0,1,2,3};
• bins mid = {4,5,6,7,8,9,10,11}; • end
• bins high = {12,13,14,15};
• }
• // End simulation
• coverpoint valid; • $display("Simulation complete. Run coverage report to view
• cross data, valid; results.");
• endgroup
• $display("Coverage: %0.2f%%", cov.get_coverage());
• $finish;
• end
• endmodule
Thank You
• // Define a covergroup to track the functional coverage of the signals (same program , different approach)
• covergroup cg @(posedge clk); // Define the covergroup, triggered on the positive edge of the clock
• coverpoint data { // Coverpoint for the signal 'data'
• bins low = {0,1,2,3}; // Create a bin named 'low' for values 0, 1, 2, and 3
• bins mid = {4,5,6,7,8,9,10,11}; // Create a bin named 'mid' for values from 4 to 11 (using a list of values)
• bins high = {12,13,14,15}; // Create a bin named 'high' for values 12, 13, 14, and 15
• }
• coverpoint valid; // Coverpoint for the signal 'valid'
• cross data, valid; // Cross coverage between 'data' and 'valid'
• endgroup
• module tb;
• logic clk; // Clock signal
• logic [3:0] data; // 4-bit data signal (must be declared here to avoid errors)
• logic valid; // 1-bit valid signal
• } • d.drive();
• endgroup • c.sample();
• cg = new(); • endclass
• endfunction
• task sample();
• cg.sample();
• endtask endclass
• $display("Mismatch: %0d + %0d = %0d (Expected %0d)", tr.a,
//Mini Project (added covergroup for
class coverage; tr.b, tr.sum, tr.a + tr.b);
• else
class(adder dut and testbench ) • virtual adder_if vif; • $display("PASS: %0d + %0d = %0d", tr.a, tr.b, tr.sum);
• // Fixed and cleaned SystemVerilog UVM-
style testbench with coverage • end
• class transaction;
• covergroup cg; • endtask