EDA Playground lets you type in and run HDL code (using a selection of free and commercial simulators and synthesizers).
It's great for learning HDLs, it's great for testing out unfamiliar things and it's great for sharing code.
You can start typing straight away. But to run your code, you'll need to sign or log in. Logging in with a Google account gives you access to all non-commercial simulators and some commercial simulators:
To run commercial simulators, you need to register and log in with a username and password. Registration is free, and only pre-approved email's will have access to the commercial simulators.
205
// UVM Testbench for APB Master RTL
// Declare package to include all the files
package apb_slave_pkg;
`include "apb_slave_item.sv"
`include "apb_slave_basic_seq.sv"
`include "apb_slave_driver.sv"
`include "apb_slave_monitor.sv"
`include "apb_slave_agent.sv"
`include "apb_slave_scoreboard.sv"
`include "apb_slave_env.sv"
`include "apb_slave_test.sv"
endpackage
`include "uvm_macros.svh"
import uvm_pkg::*;
import apb_slave_pkg::*;
`include "apb_intf.sv"
module top ();
logic clk;
logic reset;
// Instantiate RTL
apb_master APB_MASTER (
.clk (clk),
.reset (reset),
.psel_o (apb_slave_intf.psel),
.penable_o (apb_slave_intf.penable),
.paddr_o (apb_slave_intf.paddr),
.pwrite_o (apb_slave_intf.pwrite),
.pwdata_o (apb_slave_intf.pwdata),
.pready_i (apb_slave_intf.pready),
.prdata_i (apb_slave_intf.prdata)
);
// Physical interface
apb_slave_if apb_slave_intf (clk, reset);
// Generate clock
always begin
clk = 1'b0;
#5;
clk = 1'b1;
#5;
end
// Generate reset sequence and start the test
initial begin
reset = 1'b1;
@(posedge clk);
reset = 1'b0;
end
initial begin
// Set the interface handle
uvm_config_db#(virtual apb_slave_if)::set(null, "*", "apb_slave_vif", apb_slave_intf);
`uvm_info("TOP", "apb_slave_vif set in the configdb", UVM_LOW)
run_test ("apb_slave_test");
#200;
$finish();
end
initial begin
$dumpfile ("apb_slave_tb.vcd");
$dumpvars (0, top);
end
endmodule
xxxxxxxxxx
`include "uvm_macros.svh"
import uvm_pkg::*;
class apb_slave_item extends uvm_sequence_item;
bit psel;
bit penable;
bit pwrite;
bit [9:0] paddr;
bit [31:0] pwdata;
rand bit pready;
rand bit[31:0] prdata;
`uvm_object_utils (apb_slave_item);
function new (string name = "apb_slave_item");
super.new(name);
endfunction
// Helper function to get the transaction as a string
virtual function string tx2string ();
string tx;
tx = $sformatf("psel=%b penable=%b paddr=0x%x pwrite=%b pwdata=0x%x, pready=%b prdata=0x%8x",
psel, penable, paddr, pwrite, pwdata, pready, prdata);
return tx;
endfunction
endclass
xxxxxxxxxx
`include "uvm_macros.svh"
import uvm_pkg::*;
class apb_slave_driver extends uvm_driver #(apb_slave_item);
`uvm_component_utils(apb_slave_driver);
virtual apb_slave_if vif;
function new (string name = "apb_slave_driver", uvm_component parent);
super.new(name, parent);
endfunction
// Get the virtual interface pointer in the build phase from configdb
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
if (!uvm_config_db#(virtual apb_slave_if)::get(this, "", "apb_slave_vif", vif)) begin
`uvm_fatal("DRIVER", "Could not get the handle to the virtual interface");
end
endfunction
// Drive the transaction in the run phase
virtual task run_phase (uvm_phase phase);
super.run_phase(phase);
// Always try to get a new transaction from the sequencer
forever begin
apb_slave_item d_item;
`uvm_info("DRIVER", "Waiting to get the item from the sequencer", UVM_LOW);
seq_item_port.get_next_item(d_item);
// Drive the sequence item on the RTL ports
// Only need to drive the pready and prdata signals
vif.cb.pready <= d_item.pready;
vif.cb.prdata <= d_item.prdata;
@(vif.cb);
seq_item_port.item_done();
end
endtask
endclass
xxxxxxxxxx
`include "uvm_macros.svh"
import uvm_pkg::*;
class apb_slave_monitor extends uvm_monitor;
`uvm_component_utils(apb_slave_monitor);
virtual apb_slave_if vif;
uvm_analysis_port#(apb_slave_item) mon_analysis_port;
function new (string name="apb_slave_monitor", uvm_component parent);
super.new (name, parent);
endfunction
// Get the virtual interface handle in the build phase
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
mon_analysis_port = new("mon_analysis_port", this);
if (!uvm_config_db#(virtual apb_slave_if)::get(this, "", "apb_slave_vif", vif)) begin
`uvm_fatal("MONITOR", "Could not get a handle to the virtual interface");
end
endfunction
// Monitor the interface in the run_phase
virtual task run_phase (uvm_phase phase);
apb_slave_item item = new;
super.run_phase (phase);
forever begin
item.psel = vif.cb.psel;
item.penable = vif.cb.penable;
item.paddr = vif.cb.paddr;
item.prdata = vif.cb.prdata;
item.pwrite = vif.cb.pwrite;
item.pwdata = vif.cb.pwdata;
item.pready = vif.cb.pready;
`uvm_info(get_type_name(), item.tx2string(), UVM_LOW)
// Broadcast to all the subscriber class
mon_analysis_port.write(item);
@(vif.cb);
end
endtask
endclass
xxxxxxxxxx
`include "uvm_macros.svh"
import uvm_pkg::*;
class apb_slave_agent extends uvm_agent;
`uvm_component_utils (apb_slave_agent);
apb_slave_driver d0;
apb_slave_monitor m0;
uvm_sequencer#(apb_slave_item) s0;
function new (string name="apb_slave_agen", uvm_component parent);
super.new(name, parent);
endfunction
// Create the driver/sequencer and monitor in the build phase
virtual function void build_phase (uvm_phase phase);
super.build_phase(phase);
s0 = uvm_sequencer#(apb_slave_item)::type_id::create("s0", this);
d0 = apb_slave_driver::type_id::create("d0", this);
m0 = apb_slave_monitor::type_id::create("m0", this);
endfunction
// Connect driver port to the sequencer
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
d0.seq_item_port.connect(s0.seq_item_export);
endfunction
endclass
xxxxxxxxxx
`include "uvm_macros.svh"
import uvm_pkg::*;
class apb_slave_scoreboard extends uvm_scoreboard;
`uvm_component_utils(apb_slave_scoreboard);
// Associative array to store write data
bit[31:0] mem [bit[9:0]];
uvm_analysis_imp #(apb_slave_item, apb_slave_scoreboard) m_analysis_imp;
function new (string name="apb_slave_scoreboard", uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase(phase);
m_analysis_imp = new("apb_slave_imp", this);
endfunction
// Implement the write function
virtual function write (apb_slave_item item);
logic [31:0] mem_data;
`uvm_info("SCOREBOARD", "Got a new transaction", UVM_LOW)
// Write data should be previous read data + 1
if (item.psel & item.penable & item.pwrite & item.pready) begin
mem_data = mem[item.paddr] + 1;
if (item.pwdata !== mem_data) begin
`uvm_fatal(get_type_name(), $sformatf("Read data doesn't match the expected data. Data Read: 0x%8x, Expected: 0x%8x", item.pwdata, mem_data))
end
end
// On a read, store the read data to be compared on next write
if (item.psel & item.penable & ~item.pwrite & item.pready) begin
`uvm_info(get_type_name(), $sformatf("Storing read data (%x) into memory", item.prdata), UVM_LOW)
mem[item.paddr] = item.prdata;
end
endfunction
endclass
xxxxxxxxxx
`include "uvm_macros.svh"
import uvm_pkg::*;
class apb_slave_env extends uvm_env;
`uvm_component_utils (apb_slave_env);
apb_slave_agent a0;
apb_slave_scoreboard sb0;
function new (string name="apb_slave_env", uvm_component parent);
super.new(name, parent);
endfunction
// Create agent and scoreboard in the build phase
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
a0 = apb_slave_agent::type_id::create("a0", this);
sb0 = apb_slave_scoreboard::type_id::create("sb0", this);
endfunction
// Connect monitor analysis export with scoreboar
virtual function void connect_phase (uvm_phase phase);
super.connect_phase(phase);
a0.m0.mon_analysis_port.connect(sb0.m_analysis_imp);
endfunction
endclass
xxxxxxxxxx
`include "uvm_macros.svh"
import uvm_pkg::*;
class apb_slave_test extends uvm_test;
`uvm_component_utils(apb_slave_test)
apb_slave_env e0;
virtual apb_slave_if vif;
function new (string name="apb_slave_test", uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase(phase);
e0 = apb_slave_env::type_id::create("e0", this);
// Get the virtual interface handle from config db
if (!uvm_config_db#(virtual apb_slave_if)::get(this, "", "apb_slave_vif", vif)) begin
`uvm_fatal("TEST", "Unable to get handle to the virtual interface")
end
// Set the interface in config db
uvm_config_db#(virtual apb_slave_if)::set(this, "e0.a0.*", "apb_slave_vif", vif);
endfunction
// Start the sequence in the run_phase
task run_phase(uvm_phase phase);
apb_slave_basic_seq seq = apb_slave_basic_seq::type_id::create("seq");
phase.raise_objection(this);
seq.randomize();
seq.start(e0.a0.s0);
phase.drop_objection(this);
endtask
endclass
`include "uvm_macros.svh"
import uvm_pkg::*;
class apb_slave_basic_seq extends uvm_sequence;
`uvm_object_utils(apb_slave_basic_seq);
// Number of transactions to be sent
rand int num_txn;
function new (string name="apb_slave_basic_seq");
super.new(name);
endfunction
// Allow anywhere betweeen 20-100 APB transactions
constraint apb_num_txn {num_txn inside {[100:500]}; }
// Generate the item in the body
virtual task body();
string tx;
for (int i=0; i<num_txn; i++) begin
apb_slave_item seq_item = apb_slave_item::type_id::create("seq_item");
`uvm_info("SEQUENCE", "Starting a new APB Slave item", UVM_LOW);
start_item(seq_item);
void'(seq_item.randomize());
tx = seq_item.tx2string();
`uvm_info("SEQUENCE", $sformatf("Generated a new APB Slave item:\n%s", tx), UVM_LOW);
finish_item(seq_item);
end
`uvm_info("SEQUENCE", "Finished sending APB Slave items", UVM_LOW);
endtask
endclass
xxxxxxxxxx
// APB Master
module apb_master (
input wire clk,
input wire reset,
output wire psel_o,
output wire penable_o,
output wire[9:0] paddr_o,
output wire pwrite_o,
output wire[31:0] pwdata_o,
input wire pready_i,
input wire[31:0] prdata_i
);
// Enum for the APB state
typedef enum logic[1:0] {ST_IDLE = 2'b00, ST_SETUP = 2'b01, ST_ACCESS = 2'b10} apb_state_t;
apb_state_t nxt_state;
apb_state_t state_q;
logic[31:0] rdata_q;
// Load a counter with LFSR value every time a pready is seen
// Wait for the counter to be 0 before starting a new APB request
// This removed the need of the `cmd_i` signal for starting the request
logic [3:0] count_ff;
logic [3:0] nxt_count;
logic [3:0] lfsr_val;
logic [3:0] count;
always_ff @(posedge clk or posedge reset)
if (reset)
count_ff <= 4'hF;
else
count_ff <= nxt_count;
assign nxt_count = penable_o & pready_i ? lfsr_val:
count_ff - 4'h1;
assign count = count_ff;
// Generate a random load value
day7 DAY7 (
.clk (clk),
.reset (reset),
.lfsr_o (lfsr_val)
);
always_ff @(posedge clk or posedge reset)
if (reset)
state_q <= ST_IDLE;
else
state_q <= nxt_state;
always_comb begin
nxt_state = state_q;
case (state_q)
ST_IDLE : if (count_ff == 4'h0) nxt_state = ST_SETUP; else nxt_state = ST_IDLE;
ST_SETUP : nxt_state = ST_ACCESS;
ST_ACCESS : begin
if (pready_i) nxt_state = ST_IDLE;
end
endcase
end
logic ping_pong;
always_ff @(posedge clk or posedge reset)
if (reset)
ping_pong <= 1'b1;
else if (state_q == ST_SETUP)
ping_pong <= ~ping_pong;
assign psel_o = (state_q == ST_SETUP) | (state_q == ST_ACCESS);
assign penable_o = (state_q == ST_ACCESS);
assign pwrite_o = ping_pong;
assign paddr_o = 10'h3FE;
assign pwdata_o = rdata_q + 32'h1;
// Capture the read data to store it for the next write
always_ff @(posedge clk or posedge reset)
if (reset)
rdata_q <= 32'h0;
else if (penable_o && pready_i)
rdata_q <= prdata_i;
endmodule
// LFSR
module day7 (
input wire clk,
input wire reset,
output wire[3:0] lfsr_o
);
logic [3:0] lfsr_ff;
logic [3:0] nxt_lfsr;
always_ff @(posedge clk or posedge reset)
if (reset)
lfsr_ff <= 4'hE;
else
lfsr_ff <= nxt_lfsr;
assign nxt_lfsr = {lfsr_ff[2:0], lfsr_ff[1] ^ lfsr_ff[3]};
assign lfsr_o = lfsr_ff;
endmodule
interface apb_slave_if (
input logic clk,
input logic reset
);
logic psel;
logic penable;
logic [9:0] paddr;
logic pwrite;
logic [31:0] pwdata;
logic pready;
logic [31:0] prdata;
clocking cb @(posedge clk);
input psel;
input penable;
input paddr;
input pwrite;
input pwdata;
inout pready;
inout prdata;
endclocking
endinterface
Your account is not validated. If you wish to use commercial simulators, you need a validated account.
If you have already registered (or have recently changed your email address), but have not clicked on the link in the email we sent you, please do so. If you cannot find the email, please check your spam/junk folder. Or click here to resend the email.
If you have not already registered for a full account, you can do so by clicking below. You will then need to provide us with some identification information. You may wish to save your code first.
Creating, deleting, and renaming files is not supported during Collaboration. To encourage development of these features for Collaboration, tweet to @EDAPlayground
This playground may have been modified. Please save or copy before starting collaboration.
Your exercise has been submitted.