A PRACTICAL GUIDE FOR RTL DESIGNERS, VLSI
ENTHUSIASTS & INTERVIEW PREPARATION
10 ADVANCED
SYSTEMVERILOG
PROJECTS
COMPLETE CODE | REAL WORLD PROBLEMS | REUSABLE
SNIPPETS | OPTIMIZED FOR FPGA & ASIC
Prasanthi Chanda
CONTENTS
1.AXI4 Master Slave Verification Environment
2.Complete USB 3.0 Protocol Stack in
SystemVerilog
3.PCIe Gen4 Protocol Functional Verification with
Scoreboarding
4.SystemVerilog-Based DDR4 Memory Controller
Simulation
5.Multi-Core CPU Pipeline Simulation with Cache
Coherency (SystemVerilog-based)
6.AXI4-Lite Bus Protocol Verification
7.Out-of-Order Execution Engine Simulation
8.Memory Consistency Model Checker in SV
9.Branch Predictor Verification with
Scoreboarding
10.Transaction-Level Modeling with Virtual
Sequences
Project 1: UVM-Based AXI4 Master-Slave Verification
Environment
// File: axi4_master_slave_tb.sv
`timescale 1ns / 1ps
// AXI4 Interface Definition
interface axi4_if(input bit ACLK, input bit ARESETn);
logic [31:0] AWADDR, WDATA, ARADDR, RDATA;
logic [3:0] AWID, WID, ARID, RID;
logic AWVALID, AWREADY, WVALID, WREADY;
logic ARVALID, ARREADY, RVALID, RREADY;
logic [1:0] BRESP, RRESP;
logic BVALID, BREADY;
endinterface
// DUT: Dummy AXI4 Slave
module axi4_slave(axi4_if axi);
always @(posedge axi.ACLK) begin
if (!axi.ARESETn) begin
axi.AWREADY <= 0;
axi.WREADY <= 0;
axi.BVALID <= 0;
end else begin
if (axi.AWVALID) axi.AWREADY <= 1;
if (axi.WVALID) axi.WREADY <= 1;
if (axi.WVALID && axi.WREADY) begin
axi.BVALID <= 1;
axi.BRESP <= 2'b00; // OKAY
end
if (axi.BREADY) axi.BVALID <= 0;
end
end
endmodule
// Top-Level TB
module axi4_tb;
bit clk, rstn;
axi4_if axi(clk, rstn);
axi4_slave dut(axi);
initial begin
clk = 0;
forever #5 clk = ~clk;
end
initial begin
rstn = 0;
#20 rstn = 1;
end
initial begin
// Simple Write Transaction
axi.AWVALID = 1; axi.AWADDR = 32'hA000_0000;
axi.AWID = 4'b0001;
axi.WVALID = 1; axi.WDATA = 32'hDEADBEEF;
axi.WID = 4'b0001;
axi.BREADY = 1;
wait (axi.BVALID);
$display("Write Complete. BRESP = %0h",
axi.BRESP);
#10 $finish;
end
endmodule
Project 2: USB 3.0 Protocol Stack (Simplified Core
Layer)
// File: usb3_core.sv
`timescale 1ns/1ps
module usb3_core(
input logic clk,
input logic rstn,
input logic [7:0] data_in,
input logic valid_in,
output logic ready_out,
output logic [7:0] data_out,
output logic valid_out,
input logic ready_in
);
typedef enum logic [1:0] {
IDLE,
RECEIVE,
TRANSMIT
} state_t;
state_t state, next_state;
logic [7:0] buffer;
always_ff @(posedge clk or negedge rstn) begin
if (!rstn)
state <= IDLE;
else
state <= next_state;
end
always_ff @(posedge clk) begin
if (state == RECEIVE && valid_in) begin
buffer <= data_in;
end
end
always_comb begin
next_state = state;
valid_out = 0;
ready_out = 0;
data_out = 8'd0;
case (state)
IDLE: begin
if (valid_in)
next_state = RECEIVE;
end
RECEIVE: begin
ready_out = 1;
if (valid_in)
next_state = TRANSMIT;
end
TRANSMIT: begin
valid_out = 1;
data_out = buffer;
if (ready_in)
next_state = IDLE;
end
endcase
end
endmodule
// Testbench for usb3_core
module usb3_tb();
logic clk, rstn;
logic [7:0] data_in;
logic valid_in, ready_out;
logic [7:0] data_out;
logic valid_out, ready_in;
usb3_core uut (
.clk(clk), .rstn(rstn),
.data_in(data_in), .valid_in(valid_in),
.ready_out(ready_out),
.data_out(data_out), .valid_out(valid_out),
.ready_in(ready_in)
);
initial begin
clk = 0;
forever #5 clk = ~clk;
end
initial begin
rstn = 0; valid_in = 0; ready_in = 0;
#15 rstn = 1;
#10 data_in = 8'hA5; valid_in = 1;
#10 valid_in = 0;
#10 ready_in = 1;
#20 $display("Output: %0h (valid=%0b)", data_out,
valid_out);
#10 $finish;
end
endmodule
Project 3: UVM-Based PCIe Root Complex and
Endpoint Verification (Simplified)
// File: pcie_uvm_tb.sv
`timescale 1ns/1ps
// PCIe Transaction Layer Packet (TLP) Interface
interface pcie_if(input bit clk, input bit rstn);
logic [31:0] tlp_data;
logic tlp_valid, tlp_ready;
endinterface
// Dummy PCIe Endpoint
module pcie_endpoint(pcie_if intf);
always_ff @(posedge intf.clk or negedge intf.rstn)
begin
if (!intf.rstn) begin
intf.tlp_ready <= 0;
end else begin
if (intf.tlp_valid) begin
intf.tlp_ready <= 1;
end else begin
intf.tlp_ready <= 0;
end
end
end
endmodule
// Testbench Top
module pcie_tb;
bit clk, rstn;
pcie_if intf(clk, rstn);
pcie_endpoint dut(intf);
// Clock generation
initial begin
clk = 0;
forever #5 clk = ~clk;
end
// Reset generation
initial begin
rstn = 0;
#20 rstn = 1;
end
// Simple transaction
initial begin
intf.tlp_valid = 0;
intf.tlp_data = 0;
#25;
intf.tlp_data = 32'hCAFEBABE;
intf.tlp_valid = 1;
wait (intf.tlp_ready);
$display("TLP sent: %h", intf.tlp_data);
#10;
intf.tlp_valid = 0;
#10 $finish;
end
endmodule
Project 4: SystemVerilog-Based DDR4 Memory
Controller Simulation (Simplified)
// File: ddr4_controller.sv
`timescale 1ns/1ps
module ddr4_controller (
input logic clk,
input logic rstn,
input logic wr_en,
input logic rd_en,
input logic [7:0] wr_data,
output logic [7:0] rd_data,
output logic rd_valid
);
logic [7:0] mem [0:255];
logic [7:0] addr;
always_ff @(posedge clk or negedge rstn) begin
if (!rstn) begin
rd_valid <= 0;
end else begin
if (wr_en)
mem[addr] <= wr_data;
if (rd_en) begin
rd_data <= mem[addr];
rd_valid <= 1;
end else begin
rd_valid <= 0;
end
addr <= addr + 1;
end
end
endmodule
// Testbench
module ddr4_tb;
logic clk, rstn;
logic wr_en, rd_en;
logic [7:0] wr_data, rd_data;
logic rd_valid;
ddr4_controller uut(
.clk(clk), .rstn(rstn), .wr_en(wr_en), .rd_en(rd_en),
.wr_data(wr_data), .rd_data(rd_data),
.rd_valid(rd_valid)
);
initial begin
clk = 0;
forever #5 clk = ~clk;
end
initial begin
rstn = 0; wr_en = 0; rd_en = 0; wr_data = 8'h00;
#10 rstn = 1;
#10 wr_en = 1; wr_data = 8'hAA;
#10 wr_en = 0;
#10 rd_en = 1;
#10 rd_en = 0;
if (rd_valid) $display("Read Data = %h", rd_data);
#10 $finish;
end
endmodule
Project 5: Multi-Core CPU Pipeline Simulation with
Cache Coherency (Simplified)
// File: multicore_cpu.sv
`timescale 1ns/1ps
module multicore_cpu (
input logic clk,
input logic rstn
);
// 2 Core Simulation
logic [7:0] regfile_core0 [0:7];
logic [7:0] regfile_core1 [0:7];
logic [7:0] shared_memory [0:15];
// Simulated Instruction Execution
task automatic execute_core0();
begin
regfile_core0[0] = 8'h10;
shared_memory[0] = regfile_core0[0]; // write to
memory
end
endtask
task automatic execute_core1();
begin
regfile_core1[0] = shared_memory[0]; // read
from memory
end
endtask
// Coherency Check
always_ff @(posedge clk or negedge rstn) begin
if (!rstn) begin
regfile_core0[0] <= 0;
regfile_core1[0] <= 0;
shared_memory[0] <= 0;
end else begin
execute_core0();
execute_core1();
end
end
endmodule
// Testbench
module multicore_cpu_tb;
logic clk, rstn;
multicore_cpu uut (.clk(clk), .rstn(rstn));
initial begin
clk = 0;
forever #5 clk = ~clk;
end
initial begin
rstn = 0;
#10 rstn = 1;
#20;
$display("Core0 Write: %h", uut.shared_memory[0]);
$display("Core1 Read: %h", uut.regfile_core1[0]);
#10 $finish;
end
endmodule
Project 6: AXI4-Lite Bus Protocol Verification
(Simplified)
// File: axi4lite_uvm_tb.sv
`timescale 1ns/1ps
interface axi_if(input logic ACLK, input logic ARESETn);
logic [31:0] AWADDR, WDATA, ARADDR;
logic AWVALID, WVALID, BREADY;
logic ARVALID, RREADY;
logic AWREADY, WREADY, BVALID;
logic ARREADY, RVALID;
logic [31:0] RDATA;
endinterface
module axi_slave(axi_if axi);
logic [31:0] mem [0:15];
always_ff @(posedge axi.ACLK or negedge
axi.ARESETn) begin
if (!axi.ARESETn) begin
axi.AWREADY <= 0;
axi.WREADY <= 0;
axi.BVALID <= 0;
axi.ARREADY <= 0;
axi.RVALID <= 0;
end else begin
// Write Address
if (axi.AWVALID && !axi.AWREADY) begin
axi.AWREADY <= 1;
end else begin
axi.AWREADY <= 0;
end
// Write Data
if (axi.WVALID && !axi.WREADY) begin
axi.WREADY <= 1;
mem[axi.AWADDR[5:2]] <= axi.WDATA;
axi.BVALID <= 1;
end else begin
axi.WREADY <= 0;
end
// Read Address
if (axi.ARVALID && !axi.ARREADY) begin
axi.ARREADY <= 1;
axi.RDATA <= mem[axi.ARADDR[5:2]];
axi.RVALID <= 1;
end else begin
axi.ARREADY <= 0;
end
if (axi.BREADY) axi.BVALID <= 0;
if (axi.RREADY) axi.RVALID <= 0;
end
end
endmodule
module axi_tb;
logic ACLK, ARESETn;
axi_if axi(ACLK, ARESETn);
axi_slave dut(axi);
initial begin
ACLK = 0;
forever #5 ACLK = ~ACLK;
end
initial begin
ARESETn = 0;
#15 ARESETn = 1;
end
initial begin
axi.AWADDR = 0;
axi.WDATA = 32'hDEADBEEF;
axi.AWVALID = 1;
axi.WVALID = 1;
axi.BREADY = 1;
#10;
axi.AWVALID = 0;
axi.WVALID = 0;
#10;
axi.ARADDR = 0;
axi.ARVALID = 1;
axi.RREADY = 1;
#10;
axi.ARVALID = 0;
#10;
if (axi.RVALID) $display("Read Data: %h",
axi.RDATA);
#10 $finish;
end
endmodule
Project 7: Out-of-Order Execution Engine Simulation
// File: ooo_execution_engine.sv
`timescale 1ns/1ps
module ooo_engine(input logic clk, rstn);
typedef struct packed {
logic [7:0] opcode;
logic [7:0] src1;
logic [7:0] src2;
logic [7:0] dest;
logic valid;
} instr_t;
instr_t issue_q[0:3];
instr_t reorder_buffer[0:3];
logic [7:0] regfile[0:7];
task automatic issue_instruction(instr_t instr);
for (int i = 0; i < 4; i++) begin
if (!issue_q[i].valid) begin
issue_q[i] = instr;
break;
end
end
endtask
task automatic execute_instruction();
for (int i = 0; i < 4; i++) begin
if (issue_q[i].valid) begin
// Fake execution based on opcode
if (issue_q[i].opcode == 8'h01) begin
regfile[issue_q[i].dest] = regfile[issue_q[i].src1]
+ regfile[issue_q[i].src2];
end else if (issue_q[i].opcode == 8'h02) begin
regfile[issue_q[i].dest] = regfile[issue_q[i].src1]
- regfile[issue_q[i].src2];
end
// Move to reorder buffer
reorder_buffer[i] = issue_q[i];
issue_q[i].valid = 0;
end
end
endtask
task automatic commit_instruction();
for (int i = 0; i < 4; i++) begin
if (reorder_buffer[i].valid) begin
$display("Committed instruction: opcode=%h,
dest=%h, result=%h", reorder_buffer[i].opcode,
reorder_buffer[i].dest, regfile[reorder_buffer[i].dest]);
reorder_buffer[i].valid = 0;
end
end
endtask
always_ff @(posedge clk or negedge rstn) begin
if (!rstn) begin
foreach(issue_q[i]) issue_q[i].valid = 0;
foreach(reorder_buffer[i]) reorder_buffer[i].valid =
0;
foreach(regfile[i]) regfile[i] = i;
end else begin
execute_instruction();
commit_instruction();
end
end
endmodule
module ooo_engine_tb;
logic clk, rstn;
ooo_engine dut(.clk(clk), .rstn(rstn));
initial begin
clk = 0;
forever #5 clk = ~clk;
end
initial begin
rstn = 0;
#10 rstn = 1;
#10;
dut.issue_instruction('{opcode: 8'h01, src1: 0, src2:
1, dest: 2, valid: 1}); // add
dut.issue_instruction('{opcode: 8'h02, src1: 3, src2:
2, dest: 4, valid: 1}); // sub
#100 $finish;
end
endmodule
Project 8: Memory Consistency Model Checker in SV
// File: memory_consistency_checker.sv
`timescale 1ns/1ps
module memory_model_checker(input logic clk, rstn);
typedef struct packed {
logic [3:0] tid;
logic [31:0] addr;
logic [31:0] data;
logic is_write;
logic valid;
} mem_op_t;
mem_op_t ops[0:15];
int head, tail;
task automatic insert_op(mem_op_t op);
ops[tail] = op;
tail = (tail + 1) % 16;
endtask
task automatic check_consistency();
for (int i = 0; i < 16; i++) begin
if (ops[i].valid && ops[i].is_write) begin
for (int j = i + 1; j < 16; j++) begin
if (ops[j].valid && ops[j].addr == ops[i].addr &&
ops[j].tid != ops[i].tid && !ops[j].is_write) begin
$display("[WARNING] Read-after-write hazard
detected: TID %0d reads addr %h after TID %0d
writes", ops[j].tid, ops[j].addr, ops[i].tid);
end
end
end
end
endtask
always_ff @(posedge clk or negedge rstn) begin
if (!rstn) begin
foreach (ops[i]) ops[i].valid = 0;
head = 0;
tail = 0;
end else begin
check_consistency();
end
end
endmodule
module memory_model_checker_tb;
logic clk, rstn;
memory_model_checker dut(.clk(clk), .rstn(rstn));
initial begin
clk = 0;
forever #5 clk = ~clk;
end
initial begin
rstn = 0;
#10 rstn = 1;
#10;
dut.insert_op('{tid: 0, addr: 32'h1000, data:
32'hABCD, is_write: 1, valid: 1}); // Write
dut.insert_op('{tid: 1, addr: 32'h1000, data:
32'h0000, is_write: 0, valid: 1}); // Read hazard
#100 $finish;
end
endmodule
Project 9: Branch Predictor Verification with
Scoreboarding
// File: branch_predictor_scoreboard.sv
`timescale 1ns/1ps
module branch_predictor #(parameter SIZE = 8)(input
logic clk, rstn);
typedef struct packed {
logic [31:0] pc;
logic prediction;
logic valid;
} bp_entry_t;
bp_entry_t table[SIZE];
task automatic predict(input logic [31:0] pc, output
logic prediction);
prediction = 0;
for (int i = 0; i < SIZE; i++) begin
if (table[i].valid && table[i].pc == pc) begin
prediction = table[i].prediction;
break;
end
end
endtask
task automatic update(input logic [31:0] pc, input
logic actual_taken);
int idx = -1;
for (int i = 0; i < SIZE; i++) begin
if (table[i].valid && table[i].pc == pc) begin
idx = i;
break;
end
end
if (idx == -1) begin
for (int i = 0; i < SIZE; i++) begin
if (!table[i].valid) begin
idx = i;
break;
end
end
end
if (idx != -1) begin
table[idx].pc = pc;
table[idx].prediction = actual_taken;
table[idx].valid = 1;
end
endtask
endmodule
module branch_scoreboard_tb;
logic clk, rstn;
logic pred;
branch_predictor dut(.clk(clk), .rstn(rstn));
initial begin
clk = 0;
forever #5 clk = ~clk;
end
initial begin
rstn = 0;
#10 rstn = 1;
// Insert branch predictions and check scoreboard
logic [31:0] pc;
pc = 32'h00400020;
dut.update(pc, 1); // actual taken
dut.predict(pc, pred);
$display("Prediction for PC %h: %0b", pc, pred);
pc = 32'h00400024;
dut.update(pc, 0); // actual not taken
dut.predict(pc, pred);
$display("Prediction for PC %h: %0b", pc, pred);
#100 $finish;
end
endmodule
Project 10: Transaction-Level Modeling with Virtual
Sequences
// File: tlm_virtual_sequences.sv
`timescale 1ns/1ps
// Base transaction class
class transaction;
rand bit [31:0] addr;
rand bit [31:0] data;
rand bit write;
function void display();
$display("Transaction: addr=0x%0h, data=0x%0h,
write=%0b", addr, data, write);
endfunction
endclass
// Virtual sequence base class
class virtual_sequence;
virtual task execute();
endtask
endclass
// Sequence A - write transactions
class seq_a extends virtual_sequence;
transaction tr;
virtual task execute();
repeat (5) begin
tr = new();
tr.randomize() with { write == 1; };
tr.addr = $urandom_range(32'h1000, 32'h1FFF);
tr.data = $urandom();
tr.display();
#10;
end
endtask
endclass
// Sequence B - read transactions
class seq_b extends virtual_sequence;
transaction tr;
virtual task execute();
repeat (5) begin
tr = new();
tr.randomize() with { write == 0; };
tr.addr = $urandom_range(32'h2000, 32'h2FFF);
tr.display();
#10;
end
endtask
endclass
virtual task execute();
$display("Starting Sequence A (Writes)");
a_seq.execute();
$display("Starting Sequence B (Reads)");
b_seq.execute();
$display("Virtual Sequence Completed");
endtask
endclass
module tlm_virtual_seq_tb;
initial begin
top_virtual_sequence virt_seq = new();
virt_seq.execute();
#10 $finish;
end
endmodule
Excellence in World class
VLSI Training & Placements
Do follow for updates & enquires
+91- 9182280927