0% found this document useful (0 votes)
3 views46 pages

Module_3

The document discusses the limitations of raw Verilog signals and introduces SystemVerilog interfaces, which group related signals to improve modularity and scalability. It covers interface instantiation, parameters, modports for directional signal management, and the encapsulation of tasks/functions within interfaces. Additionally, it explains the phases of the Verilog event scheduler and provides examples of simulation control and signal monitoring.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views46 pages

Module_3

The document discusses the limitations of raw Verilog signals and introduces SystemVerilog interfaces, which group related signals to improve modularity and scalability. It covers interface instantiation, parameters, modports for directional signal management, and the encapsulation of tasks/functions within interfaces. Additionally, it explains the phases of the Verilog event scheduler and provides examples of simulation control and signal monitoring.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 46

Module 3:

Dr.Rekha Phadke
System Verilog Interfaces & Verification Examples
1. Limitations of Raw Verilog Signal
Lists

• Manually listing each signal in module ports leads to long, error-prone


declarations and poor scalability.
• // Verilog style—no grouping
module my_module (
input clk,
input rst_n,
input [7:0] data_in,
output data_ready,
output [7:0] data_out
);
// … design …
endmodule
2. SystemVerilog Interfaces

• 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

• // Code your design here for Interface example


• interface bus_if #(parameter WIDTH = 8) (input logic clk);
• logic [WIDTH-1:0] data;
• logic valid,ready;
• endinterface

• module producer(bus_if b);


• always @(posedge b.clk) begin
• b.data <= $urandom;
• b.valid <= 1;
• end
• endmodule

• module consumer(bus_if b);


• always @(posedge b.clk) begin
• if (b.valid) begin
• $display("Got %0d", b.data);
• b.ready <= 1;
• end
• end
• endmodule
4. Interface Instantiation &
Parameters
• Interfaces support parameters, allowing customization of signal widths or behavior at instantiation.
• interface axi_if #(parameter ADDR_W = 32, DATA_W = 64) (input logic clk);
logic [ADDR_W-1:0] addr;
logic [DATA_W-1:0] wdata;
logic valid, ready;
endinterface

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;

// Master drives data/valid, reads ready


modport master (output data, output valid, input ready);

// Slave drives ready, reads data/valid


modport slave (input data, input valid, output ready);
endinterface

Signal Direction (via


Module Role
modport)
Drives: data, valid
producer master
Reads: ready
Drives: ready
consumer slave
Reads: data, valid
• // Interface: bus_if
• // Top Module (Testbench) • interface bus_if(input logic clk);
• module top; • logic [7:0] data;
• logic clk = 0; • logic valid, ready;
• // Instantiate interface • // Master drives data/valid, reads ready
• bus_if b_if(.clk(clk));
• modport master (output data, output valid, input ready);
• // Instantiate modules
• // Slave drives ready, reads data/valid
• producer P(.b(b_if));
• modport slave (input data, input valid, output ready);
• consumer C(.b(b_if));
• Endinterface
• // Clock generation
• // Producer (Master)
• always #5 clk = ~clk;
• // Simulation control • module producer(bus_if.master b);

• initial begin • initial begin


• #200; • b.valid = 0;
• $finish; • b.data = 0;
• end • #10;
• endmodule
• repeat (10) begin
• module consumer(bus_if.slave b);
• @(posedge b.clk);
• always @(posedge b.clk) begin
• b.ready <= 1; • b.data <= $random;

• if (b.valid && b.ready) begin • b.valid <= 1;


• $display("Consumer received: • @(posedge b.clk);
%0h", b.data); • if (b.ready) begin
• end
• $display("Producer sent: %0h", b.data);
• end
• b.valid <= 0;
• endmodule
• end

• 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

task automatic init_if(ref bus_if b);


b.data = 8'hAA;
b.valid = 1;
b.ready = 0;
endtask
endprogram
Module 3-Part 2:
Dr.Rekha Phadke
System Verilog Interfaces & Verification Examples
7. Tasks & Functions Inside
Interfaces
• Encapsulating protocol tasks/functions in interfaces keeps behavior with the signal definitions.

• interface spi_if(input logic clk);


logic mosi, miso, sclk, cs;

task automatic send_byte(input byte d);


integer i;
for (i = 7; i >= 0; i--) begin
mosi = d[i];
@(posedge clk);
sclk = 1; @(posedge clk); sclk = 0;
end
endtask
endinterface
// SPI Master
// SPI Interface //-------------------------- //--------------------------
//-------------------------- module spi_master(spi_if spi); // Top-level Testbench
interface spi_if(input logic clk); initial begin //--------------------------
spi.cs = 0; // Select the slave module top;
logic mosi, miso, sclk, cs; #10; logic clk = 0;
$display("Master sending: 0xA5");
// SPI send byte task spi.send_byte(8'hA5); // Clock generation
#10; always #5 clk = ~clk;
task automatic send_byte(input byte d); spi.cs = 1; // Deselect the slave
integer i; end // Instantiate SPI interface
for (i = 7; i >= 0; i--) begin endmodule spi_if spi(clk);
@(posedge clk); // SPI Slave // Instantiate master and slave
mosi = d[i]; //-------------------------- spi_master master(spi);
module spi_slave(spi_if spi); spi_slave slave(spi);
sclk = 1; @(posedge clk);
byte data_received = 8'h00;
sclk = 0; integer i; // End simulation
end initial begin
endtask always @(negedge spi.cs) begin #200;
// Wait for CS to go low $finish;
endinterface i = 7; end
data_received = 8'h00; endmodule

while (!spi.cs && i >= 0) begin


@(posedge spi.sclk);
data_received[i] = spi.mosi;
$display("received=%
d",data_received[i]);
// Simple echo: send back received bit
spi.miso = data_received[i];
i--;
end

$display("Slave received: 0x%0h",


data_received);
end
endmodule
Mini project • module spi_master(spi_if spi); • // Top-level Testbench
• byte received; • //--------------------------
• initial begin • module top;
• // Code for full duplex master slave setup
• spi.cs = 0; // Select the slave • logic clk = 0;
• interface spi_if(input logic clk);
• #10;
• logic mosi, miso, sclk, cs;
• spi.transfer_byte(8'hA5, received); • // Clock generation: 10ns period
• // Full-duplex SPI transfer: send tx_data, receive rx_data
• #10; • always #5 clk = ~clk;
• task automatic transfer_byte(input byte tx_data, output byte
• spi.cs = 1; // Deselect the slave
rx_data);
• integer i; • end • // Instantiate SPI interface

• rx_data = 8'b0; • endmodule • spi_if spi(clk);

• for (i = 7; i >= 0; i--) begin • module spi_slave(spi_if spi);

• @(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:

At each time step, events are executed in four ordered regions:

(
1.Active: Procedural statements = blocking assignments, if, for, etc.)

2.Inactive (#0 delay): Scheduled after all active events


at same time

(
3.Non-blocking Update: Updates from <= non-blocking assignments happen here)

4.Monitor: $display, $monitor (reads values after all changes)


• module sched_test;
• logic a, b;
• Time 0:
• [Active] a = 0; b = 0; a = 1; =>
• initial begin a=1, b=0
• a = 0; b = 0; // Time 0, Active
• [Monitor] $display =>
• a = 1; // Active: immediate blocking assignment
a=1, b=0
• $display("Time=%0t a=%0b b=%0b", $time, a, b); //Monitor
• [Inactive] b = 1; => a=1,
• #0 b = 1; // Inactive: delay 0, goes into inactive region b=1
• a <= 0; // Schedules non-blocking update • [NB Update] a <= 0; => a=0
• $display("Time=%0t a=%0b b=%0b", $time, a, b); //Monitor
• [Monitor] $display =>
• #1; // Advance time to 1
a=1, b=1
• $display("Time=%0t a=%0b b=%0b", $time, a, b); //Monitor
• $finish;
• Time 1:
• end
• endmodule • [Monitor] $display =>
a=0, b=1
Example 2:
• module sched_display_test;
• logic a, b;

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;

• assign c = a; // continuous assign, sensitive to `a`

• 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.

If you wrap your TB’s stimulus in:


clocking cb @(posedge clk);
Then by construction:
output #1 data_in; // drive 1 time‐unit *after* 1.t = posedge clk
the edge 2.t + 1 – TB drives data_in (in the “skewed” output region)
endclocking 3.t + Δ – DUT samples data_in on its own posedge-blocking update
Now there’s a guaranteed 1-unit gap between “when the clock ticks”
and “when your TB writes,” so the DUT can never collide with your stimulus.
initial begin That removes the nondeterminism altogether.
forever @(posedge clk) begin
cb.data_in <= $random;
end
end
• // DUT Module
• module simple_dut(input logic clk,
• input logic [7:0] data_in,
• output logic [7:0] data_out);
• always_ff @(posedge clk)
• data_out <= data_in;
• endmodule

• // Drive data using clocking block


• // Connect DUT Key Things to Notice
• // Top Testbench •cb.data_in <= $random; drives data one nanosecond after the posedge c
• simple_dut dut ( thanks to output #1.
• module tb;
• .clk(clk),
•data_out is updated on the next clock edge in the DUT.
• logic clk; •#2 wait ensures the output has had time to stabilize before printing.
• .data_in(data_in),
• logic [7:0] data_in, data_out;
• .data_out(data_out) Compiler version U-2023.03-SP2_Full64; Runtime version U-2023.03-
• // Clock generation: 10ns period SP2_Full64;
• ); Apr 23 12:00 2025
• initial clk = 0;
• initial begin 7ns: IN = 0x24, OUT = 0xxx
• always #5 clk = ~clk; 17ns: IN = 0x81, OUT = 0x24
• repeat (5) begin 27ns: IN = 0x9, OUT = 0x81
• // Clocking block to control delays
• @(posedge clk); // wait for posedge 37ns: IN = 0x63, OUT = 0x9
• clocking cb @(posedge clk);
47ns: IN = 0xd, OUT = 0x63
• cb.data_in <= $random; // schedule drive at t+1 $finish called from file "design.sv", line 44.
• output #1 data_in; // TB drives data_in 1ns after clk edge
• #2; // allow time for data_out to reflect updated value (next clk) $finish at simulation time 47
• endclocking
VCSSimulationReport
• $display("%0dns: IN = 0x%0h, OUT = 0x%0h", $time, data_in, cb. data_out);
Time: 47 ns
• end
• $finish;
• end
• endmodule
9. SV Event Control & Clocking Blocks
Clocking Block
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.
• Clocking blocks manage signal sampling/driving with skew specifications to avoid race conditions.
• logic clk = 0;
always #5 clk = ~clk;

clocking cb @(posedge clk);


input #1 data_in;
output #2 data_out;
endclocking

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

• always #5 clk = ~clk; • Compiler version U-2023.03-SP2_Full64; Runtime version U-


2023.03-SP2_Full64; Apr 23 13:00 2025
Time=5 | Sent: 0xff | Received: 0xxx
• // Declare clocking block for communication Time=15 | Sent: 0xff | Received: 0x0
Time=25 | Sent: 0x0 | Received: 0x0
• clocking cb @(posedge clk); Time=35 | Sent: 0x0 | Received: 0x1
$finish called from file "design.sv", line 36.
• output #2 data_in; // Testbench drives data_in $finish at simulation time 40
VCSSimulationReport
• input #1 data_out; // Testbench samples data_out Time: 40 ns
• endclocking
MiniProject Compiler version U-2023.03-SP2_Full64; Runtime
version U-2023.03-SP2_Full64; Apr 23 12:30 2025
• // ==================== • // Clocking block
• // DUT Module • clocking cb @(posedge clk);
7ns: IN = 0x51, OUT = 0xxx
• // ====================
• output #1 data_in; // TB drives 1ns after clk
17ns: IN = 0xcd, OUT = 0xxx
• module simple_dut(input logic clk,
• input #2 data_out; // TB samples 2ns after clk
27ns: IN = 0xe, OUT = 0x51
• input logic [7:0] data_in,
• output logic [7:0] data_out); • endclocking
37ns: IN = 0xdb, OUT = 0xcd
47ns: IN = 0x71, OUT = 0xe
• always_ff @(posedge clk)
• data_out <= data_in; • // Connect DUT 57ns: IN = 0x63, OUT = 0xdb
• simple_dut dut ( 67ns: IN = 0xe9, OUT = 0x71
• endmodule
• .clk(clk), 77ns: IN = 0x98, OUT = 0x63
• // ==================== • .data_in(data_in), 87ns: IN = 0x3, OUT = 0xe9
• // Testbench • .data_out(data_out) 97ns: IN = 0xa4, OUT = 0x98
• // ====================
• ); $finish called from file "design.sv", line 47.
• module tb;
• logic clk; • // Stimulus $finish at simulation time 97
• logic [7:0] data_in, data_out; • initial begin VCSSimulationReport
• // Clock generation: 10ns period
• repeat (10) begin Time: 97 ns
• initial clk = 0; • @(posedge clk); // wait for rising edge CPU Time: 0.450 seconds; Data structure size:
• always #5 clk = ~clk; • cb.data_in <= $urandom; // drive at clk + 1ns 0.0Mb
• #2; // wait to sample Wed Apr 23 12:30:56 2025
• $display("%0dns: IN = 0x%0h, OUT = 0x%0h", $time, data_in, cb.data_out);

• 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)

1 5 ns 6 ns ⟶ 0x51 samples initial data_out <= X 7 ns ⟶ latches X 7 ns: IN=0x51


data_in X ⟶X OUT=X

data_out <= 0x24 17 ns ⟶


samples driven 17 ns: IN=0xcd
2 15 ns 16 ns ⟶ 0xcd at 6 ns (0x24) ⟶ (latches 0x24) OUT=X¹
(becomes)0x24

data_out <= 0x81 27 ns ⟶


samples driven 27 ns: IN=0x0e
3 25 ns 26 ns ⟶ 0x0e ⟶
at 16 ns (0x81) (latches 0x24) OUT=0x51
(becomes)0x81
• Summary:
Feature Description
A construct used to define the timing behavior of
Definition signals, synchronized with a specific clock edge in a
testbench.

clocking <name> @(posedge <clock>);


input <signal>;
Syntax
output <signal>;
endclocking

- Synchronize the sampling and driving of signals with


Purpose a clock edge.
- Group signals that share the same timing behavior.
- Input: Signals sampled during simulation.
Clocking Direction
- Output: Signals driven to the DUT during simulation.
Signals inside the clocking block are sampled or driven
Timing
on a specific clock edge, typically posedge <clock>.
- Reduces timing errors in testbenches.
Advantages - Improves code readability and maintainability.
- Simplifies timing specification.
clocking <name> @(posedge <clock>);
input <signal>;
Example
output <signal>;
endclocking

Usage Context Typically used in testbenches to ensure synchronized


signal handling with respect to the clock domain.
Thank you
Module 3-Part 4:
Dr.Rekha Phadke
System Verilog Interfaces & Verification Examples
10. Typical Testbench Environment
Skeleton
• A testbench typically includes a generator, driver, monitor, scoreboard, and DUT, interconnected via interfaces.
• // environment class • initial begin
• // transaction class(adder dut•and testbench)
//Monitor • class env; • e = new();
• class transaction; • Class Monitor;
• driver drv; • e.vif = intf; // Connect interface to env
• rand bit [3:0] a, b; • virtual adder_if vif;
• monitor mon; • e.build();
• bit [4:0] sum; // output • mailbox #(transaction) mon2scb;
• transaction tr; • scoreboard scb; • e.run();
• endclass
• forever begin • mailbox #(transaction) gen2drv, mon2scb; • repeat (10) begin
• // interface • #5; • virtual adder_if vif; • transaction tr = new();
• interface adder_if; • tr = new(); • if (!tr.randomize()) $fatal("Randomization
• logic [3:0] a, b; • tr.a = vif.a; failed!");
• function void build(); • e.gen2drv.put(tr);
• logic [4:0] sum; • tr.b = vif.b;
• tr.sum = vif.sum; • gen2drv = new(); • #10;
• endinterface
• mon2scb.put(tr); • mon2scb = new(); • end
• // driver class • end • drv = new(); drv.vif = vif; drv.gen2drv = • #100; // wait for last checks
• class driver; • endtask gen2drv;
• $finish;
• virtual adder_if vif; • endclass • mon = new(); mon.vif = vif; mon.mon2scb =
mon2scb; • end
• mailbox #(transaction) gen2drv;
• endclass
• // scoreboard class • scb = new(); scb.mon2scb = mon2scb; • endmodule
• task run(); • class scoreboard; • // adder DUT
• endfunction
• transaction tr; • mailbox #(transaction) mon2scb;
• task run(); • task run(); • module adder(
• forever begin • transaction tr; • input logic [3:0] a,
• fork
• gen2drv.get(tr); • forever begin • input logic [3:0] b,
• mon2scb.get(tr); • drv.run();
• vif.a <= tr.a; • if (tr.sum != tr.a + tr.b) • • output logic [4:0] sum
mon.run();
• vif.b <= tr.b; • $display("Mismatch” tr.a, • ); assign sum = a + b;
tr.b, tr.sum, tr.a + tr.b); • scb.run();
• #5; // Wait for result • else •
• endmodule
join_none
• end • $display("PASS: %0d + • // top module
%0d = • endtask
• endtask • %0d", tr.a, tr.b, tr.sum);
• endclass
• module top;
• end endtask endclass
• Endclass • // test module
• adder_if intf();
• adder dut (.a(intf.a), .b(intf.b), .sum(intf.sum));
• module test(adder_if intf); • test t1 (.intf(intf)); endmodule
• env e;
Module 3-Part 5:
Dr.Rekha Phadke
System Verilog Interfaces & Verification Examples

Code and Functional Coverages
Code coverage in SystemVerilog is a metric used to determine how much of your code has been exercised
during simulation. It helps you understand how thoroughly your tests are verifying the design. It's a key part
of functional verification and is often used in combination with functional coverage.

Types of Code Coverage in SystemVerilog • How to Enable Code Coverage ?


• Here are the main types of code coverage:
• Line Coverage
• In tools like Synopsys VCS, Cadence Xcelium, or Mentor Questa,
• Measures whether each line of code has been executed.
• Example: If you have a conditional statement with two branches, line coverage only
code coverage is enabled via simulation options.
checks if the lines were executed, not both branches.
• Toggle Coverage
• Checks if each bit of a register or wire toggles from 0 to 1 and from 1 to 0.
• Useful for ensuring all signals are actively changing.

•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.

•FSM (Finite State Machine) Coverage

• Tracks state transitions and state occupancy.


• Ensures all states and transitions are exercised.
• Functional coverage in SystemVerilog is a user- • Functional Coverage Constructs
defined metric that measures how much of the intended functionality of a

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.

• Why Functional Coverage? • SystemVerilog provides two




Functional coverage helps answer:
Have all critical features or scenarios been tested? main constructs:
• Are there any untested combinations of inputs or sequences?
• Are we meeting our verification goals? • Covergroups
• Coverpoints and Cross coverage
11. Verification Plan via Covergroups

• 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

• // Create an instance of the covergroup


• cg cg_inst = new;

• // Clock generation: Toggle the clock every 5 time units


• always #5 clk = ~clk;

• // Initial block for stimulus generation


• initial begin
• // Initialize signals
• data = 0;
• valid = 0;

• // Stimulate the data and valid signals over time


• #10 data = 2; valid = 1;
• #10 data = 5; valid = 1;
• #10 data = 12; valid = 0;
• #10 data = 13; valid = 1;

• #20 $finish; // End the simulation after 20 time units
• end

• // Always block to sample the covergroup at each clock cycle


• always @(posedge clk) begin
• cg_inst.sample(); // Sample the covergroup at each clock cycle
• end
• endmodule
• //Example 2:

• // Define the interface • // Driver class • // Environment class


• class driver;
• // Testbench
• interface and_if; • class env;
• virtual and_if vif;
• module tb;
• logic a, b, y; • virtual and_if vif;
• and_if intf();
• endinterface • driver d;
• function new(virtual and_if vif);
• and_gate dut(intf);
• // Define the DUT • monitor m;
• this.vif = vif;
• env e;
• module and_gate(and_if intf); • coverage c;

• assign intf.y = intf.a & intf.b; • endfunction


• initial begin
• endmodule • function new(virtual and_if vif);
• task drive();
• e = new(intf);
• // Define coverage class • this.vif = vif;
• vif.a = $random % 2; // Random 0 or 1 • e.run();
• class coverage; • d = new(vif);
• vif.b = $random % 2; // Random 0 or 1 • $display("Coverage = %0.2f%%", e.c.cg.get_coverage());
• virtual and_if vif; • m = new(vif);
• #5; // Wait 5 time units
• $finish;
• covergroup cg; • c = new(vif);
• endtask
• end
• coverpoint {vif.a, vif.b} { • endfunction
• endclass • endmodule
• bins both_low = {2'b00};

• bins one_high = {2'b01, 2'b10}; • task run();

• bins both_high = {2'b11}; • repeat (4) begin

• } • d.drive();

• cross vif.a, vif.b; • m.display();

• endgroup • c.sample();

• function new(virtual and_if vif); • end

• this.vif = vif; • endtask

• 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

• rand bit[3:0] a; • coverpoint vif.a { • endclass



• rand bit[3:0] b; • bins all_a[] = {[0:15]}; class env;
• driver drv;
• bit[4:0] sum; • } • monitor mon;
• endclass
• coverpoint vif.b { • scoreboard scb;
• interface adder_if;
• logic [3:0] a, b; • bins all_b[] = {[0:15]}; • coverage c;
• mailbox #(transaction) gen2drv, mon2scb;
• logic [4:0] sum; • }
• virtual adder_if vif;
• endinterface • coverpoint vif.sum {
• class driver;
• bins all_sum[] = {[0:30]}; • function void build();
• virtual adder_if vif;
• mailbox #(transaction) gen2drv; • } • gen2drv = new();
• mon2scb = new();
• task run(); • cross vif.a, vif.b;
• c = new();
• transaction tr; • endgroup • c.vif = vif;
• forever begin
• gen2drv.get(tr);
• vif.a <= tr.a; • function new(); • drv = new();
• drv.vif = vif;
• vif.b <= tr.b; • cg = new();
• drv.gen2drv = gen2drv;
• #5; • endfunction
• end
• endclass • mon = new();
• endtask
• mon.vif = vif;
• endclass
• mon.mon2scb = mon2scb;
• mon.c = c;

You might also like