commit e4e8457f9573c3b40722186e25f7320c1846970c Author: Mahesh Asolkar Date: Sun Aug 11 21:48:11 2024 -0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..32f04b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +logs +obj_dir diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f55e862 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +# Make and run project +UVM_HOME=/home/mahesh/git/uvm-verilator + +PROJ=uvm_tb + +SV_FILES=$(shell ls *.sv) +SV_SRC=$(UVM_HOME)/src/uvm_pkg.sv tb_pkg.sv +SV_DEPS=$(SV_FILES) + +CPP_SRC=sim_$(PROJ).cpp + +TIMESCALE= --timescale '1ns/1ns' + +UVM_DEFINES=+define+UVM_NO_DPI \ + +define+UVM_REPORT_DISABLE_FILE_LINE +DISABLED_WARNINGS=-Wno-WIDTHTRUNC -Wno-WIDTHEXPAND \ + -Wno-CASTCONST -Wno-CONSTRAINTIGN \ + -Wno-MISINDENT -Wno-REALCVT \ + -Wno-SYMRSVDWORD -Wno-CASEINCOMPLETE + +ifndef TEST_NAME + TEST_NAME=test_base +endif + +build: $(SV_DEPS) + verilator -I$(UVM_HOME)/src -I. \ + -o $(PROJ).sim \ + --binary \ + -j 4 \ + --error-limit 10 \ + --timing $(TIMESCALE) \ + +define+SVA_ON \ + $(UVM_DEFINES) \ + $(DISABLED_WARNINGS) \ + $(SV_SRC) + +run: + if [ ! -d logs/$(TEST_NAME) ]; then mkdir -p logs/$(TEST_NAME); fi + cd logs/$(TEST_NAME) && ../../obj_dir/$(PROJ).sim +UVM_TESTNAME=$(TEST_NAME) |& tee sim.log + +clean: + rm -rf obj_dir diff --git a/agent_reset.sv b/agent_reset.sv new file mode 100644 index 0000000..6162b94 --- /dev/null +++ b/agent_reset.sv @@ -0,0 +1,123 @@ +class sequencer_reset extends uvm_sequencer; + `uvm_component_utils(sequencer_reset) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction +endclass + +class driver_reset extends uvm_driver; + virtual testbench_if tb_if; + + `uvm_component_utils(driver_reset) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + + if (!uvm_config_db#(virtual testbench_if)::get(this, "", "tb_vif", tb_if)) begin + `uvm_fatal("CFG_DB_FAIL", $sformatf("Failed to fetch interface for %0s", get_full_name())) + end + endfunction + + virtual task run_phase(uvm_phase phase); + forever begin + @(posedge tb_if.rst_n); + while (tb_if.rst_n != 0) begin + seq_item_port.get_next_item(req); + drive_item(req); + seq_item_port.item_done(); + end + end + endtask + + task drive_item(uvm_sequence_item req); + `uvm_info("drive_item", $sformatf("Initiating reset..."), UVM_LOW) + tb_if.rst_n = 0; + repeat(10) @(posedge tb_if.clk); + tb_if.rst_n = 1; + `uvm_info("drive_item", $sformatf("Reset done."), UVM_LOW) + endtask +endclass + +class monitor_reset extends uvm_monitor; + virtual testbench_if tb_if; + + `uvm_component_utils(monitor_reset) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + + if (!uvm_config_db#(virtual testbench_if)::get(this, "", "tb_vif", tb_if)) begin + `uvm_fatal("CFG_DB_FAIL", $sformatf("Failed to fetch interface for %0s", get_full_name())) + end + endfunction + + virtual task run_phase(uvm_phase phase); + fork + do_monitor(); + join + endtask + + task do_monitor(); + bit prev_val; + + if (tb_if.rst_n === 1'b0) begin + `uvm_info("do_monitor", $sformatf("Starting with reset asserted"), UVM_LOW) + end else if (tb_if.rst_n === 1'b1) begin + `uvm_info("do_monitor", $sformatf("Starting with reset de-asserted"), UVM_LOW) + end else begin + `uvm_info("do_monitor", $sformatf("Starting with reset unknown"), UVM_LOW) + end + + forever begin + prev_val = tb_if.rst_n; + + @(tb_if.rst_n); + if ((prev_val !== 1'b1) && (tb_if.rst_n !== 1'b1)) begin + `uvm_info("drive_item", $sformatf("Reset de-asserted"), UVM_LOW) + end + if ((prev_val !== 1'b0) && (tb_if.rst_n !== 1'b0)) begin + `uvm_info("drive_item", $sformatf("Reset asserted"), UVM_LOW) + end + end + endtask +endclass + +class agent_reset extends uvm_agent; + sequencer_reset sequencer; + driver_reset driver; + monitor_reset monitor; + + `uvm_component_utils(agent_reset) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + + sequencer = sequencer_reset::type_id::create("sequencer", this); + driver = driver_reset::type_id::create("driver", this); + monitor = monitor_reset::type_id::create("monitor", this); + + `uvm_info("build_phase", $sformatf("Building done"), UVM_LOW) + endfunction + + function void connect_phase(uvm_phase phase); + super.connect_phase(phase); + + driver.seq_item_port.connect(sequencer.seq_item_export); + + `uvm_info("connect_phase", $sformatf("Connecting done"), UVM_LOW) + endfunction + +endclass diff --git a/agent_tb.sv b/agent_tb.sv new file mode 100644 index 0000000..f2920c8 --- /dev/null +++ b/agent_tb.sv @@ -0,0 +1,7 @@ +class sequencer_tb extends uvm_sequencer; + `uvm_component_utils(sequencer_tb) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction +endclass diff --git a/design.sv b/design.sv new file mode 100644 index 0000000..402f5e9 --- /dev/null +++ b/design.sv @@ -0,0 +1,40 @@ +// Some simple design + +module rtl_design(design_if intf); + + logic [31:0] data_out_drv = 0; + assign intf.data_out = data_out_drv; + + string trk_name; + integer trk_h; + + initial begin + data_out_drv = 32'h0; + end + + always @(posedge intf.clk) begin + if (intf.rst_n) begin + data_out_drv = intf.data_in; + end + end + + always_latch @(intf.rst_n) begin + if (!intf.rst_n) begin + data_out_drv = 32'h0; + end + end + + initial begin + trk_name = $sformatf("%m.out"); + trk_h = $fopen(trk_name, "w"); + $fdisplay(trk_h, "Tracker: %s", trk_name); + $display("Starting tracker: %s", trk_name); + $fmonitor(trk_h, "@%6t: %b %b %h %h", $time, + intf.rst_n, intf.clk, intf.data_in, intf.data_out); + end + + function void be_done(); + $display("Closing tracker %s", trk_name); + $fclose(trk_h); + endfunction +endmodule diff --git a/interfaces.sv b/interfaces.sv new file mode 100644 index 0000000..ac17f14 --- /dev/null +++ b/interfaces.sv @@ -0,0 +1,23 @@ +// Design and Testbench interfaces +interface design_if ( + input clk); + + logic rst_n; + logic [31:0] data_in; + logic [31:0] data_out; + + modport DN (output data_out, input rst_n, clk, data_in); + modport TB (input clk, data_out, output rst_n, data_in); +endinterface + +interface testbench_if ( + input clk, + virtual design_if d1_if, + virtual design_if d2_if); + + logic rst_n; + + modport DN (input rst_n, clk); + modport TB (input clk, output rst_n); +endinterface + diff --git a/seq_basic.sv b/seq_basic.sv new file mode 100644 index 0000000..91f2d03 --- /dev/null +++ b/seq_basic.sv @@ -0,0 +1,70 @@ +class seq_base extends uvm_sequence; + testbench_env tb_env; + + `uvm_object_utils(seq_base) + + function new(string name = "seq_base"); + super.new(name); + endfunction + + function void set_handles(testbench_env env); + tb_env = env; + endfunction + + virtual task body(); + bit ok = uvm_config_db#(testbench_env)::get(m_sequencer, "", "tb_env", tb_env); + endtask +endclass + +class seq_basic extends seq_base; + `uvm_object_utils(seq_basic) + + function new(string name = "seq_basic"); + super.new(name); + endfunction + + virtual task body(); + super.body(); + + `uvm_info("body", $sformatf("Initiating stimulus ..."), UVM_LOW) + fork + begin + d_stimulus(tb_env.tb_if.d1_if, 1); + end + begin + d_stimulus(tb_env.tb_if.d2_if, 2); + end + join + `uvm_info("body", $sformatf("Stimulus done."), UVM_LOW) + endtask + + task d_stimulus(virtual design_if d_if, int inst_n); + `uvm_info("d_stimulus", $sformatf("Inst-%0d: Initiating stimulus...", inst_n), UVM_LOW); + repeat(20) @(negedge tb_env.tb_if.clk); + d_if.data_in = {inst_n[15:0], 16'hd1}; + `uvm_info("d_stimulus", $sformatf("Inst-%0d: Driving data_in=0x%h", inst_n, d_if.data_in), UVM_LOW); + repeat(50) @(negedge tb_env.tb_if.clk); + d_if.data_in = {inst_n[15:0], 16'hd2}; + `uvm_info("d_stimulus", $sformatf("Inst-%0d: Driving data_in=0x%h", inst_n, d_if.data_in), UVM_LOW); + repeat(20) @(negedge tb_env.tb_if.clk); + `uvm_info("d_stimulus", $sformatf("Inst-%0d: ... stimulus done", inst_n), UVM_LOW); + endtask +endclass + +class seq_reset extends seq_base; + `uvm_object_utils(seq_reset) + + function new(string name = "seq_reset"); + super.new(name); + endfunction + + virtual task body(); + super.body(); + + `uvm_info("body", $sformatf("Initiating reset..."), UVM_LOW) + tb_env.tb_if.rst_n = 0; + repeat(10) @(posedge tb_env.tb_if.clk); + tb_env.tb_if.rst_n = 1; + `uvm_info("body", $sformatf("Reset done."), UVM_LOW) + endtask +endclass diff --git a/tb_pkg.sv b/tb_pkg.sv new file mode 100644 index 0000000..a7b9fae --- /dev/null +++ b/tb_pkg.sv @@ -0,0 +1,17 @@ +`include "interfaces.sv" +`include "design.sv" +package tb_pkg; + import uvm_pkg::*; + + // Environment + `include "agent_reset.sv" + `include "agent_tb.sv" + `include "testbench_env.sv" + + // Sequences + `include "seq_basic.sv" + + // Tests + `include "test_base.sv" +endpackage +`include "uvm_tb.sv" diff --git a/test_base.sv b/test_base.sv new file mode 100644 index 0000000..f550f35 --- /dev/null +++ b/test_base.sv @@ -0,0 +1,76 @@ +class test_base extends uvm_test; + `uvm_component_utils(test_base) + + testbench_env tb_env; + uvm_table_printer tb_printer; + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + + tb_env = testbench_env::type_id::create("tb_env", this); + tb_printer = new("tb_printer"); + + uvm_config_db#(testbench_env)::set(uvm_root::get(), "*", "tb_env", tb_env); + endfunction + + virtual function void end_of_elaboration_phase(uvm_phase phase); + `uvm_info("end_of_elaboration_phase", $sformatf("Topology:\n%s", this.sprint(tb_printer)), UVM_LOW) + endfunction + + virtual task run_phase(uvm_phase phase); + uvm_objection objection; + + objection = phase.get_objection(); + + `uvm_info("run_phase", $sformatf("Raising objection"), UVM_LOW) + phase.raise_objection(this); + + run_reset_phase(phase); + + run_test_phase(phase); + + run_flush_phase(phase); + + objection.set_drain_time(this, 20); + `uvm_info("run_phase", $sformatf("Dropping objection"), UVM_LOW) + phase.drop_objection(this); + endtask + + virtual task run_reset_phase(uvm_phase phase); + seq_reset rst_seq; + + `uvm_info("run_reset_phase", $sformatf("Starting reset"), UVM_LOW) + rst_seq = seq_reset::type_id::create("reset_seq", this); + rst_seq.start(tb_env.tb_sequencer); + `uvm_info("run_reset_phase", $sformatf("Finishing reset"), UVM_LOW) + endtask + + virtual task run_test_phase(uvm_phase phase); + `uvm_warning("run_test_phase", $sformatf("This content is expected to be implemented in specific tests")) + endtask + + virtual task run_flush_phase(uvm_phase phase); + `uvm_info("run_flush_phase", $sformatf("Finishing test"), UVM_LOW) + endtask +endclass + +class test_basic extends test_base; + `uvm_component_utils(test_basic) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + virtual task run_test_phase(uvm_phase phase); + seq_basic tst_seq; + + tst_seq = seq_basic::type_id::create("test_seq_basic"); + `uvm_info("run_test_phase", $sformatf("Starting stimulus"), UVM_LOW) + tst_seq.start(tb_env.tb_sequencer); + `uvm_info("run_test_phase", $sformatf("Finishing stimulus"), UVM_LOW) + endtask +endclass diff --git a/testbench_env.sv b/testbench_env.sv new file mode 100644 index 0000000..269e73d --- /dev/null +++ b/testbench_env.sv @@ -0,0 +1,40 @@ +// Top class +class testbench_env extends uvm_component; + string name; + virtual testbench_if tb_if; + + agent_reset rst_agt; + sequencer_tb tb_sequencer; + + `uvm_component_utils(testbench_env) + + function new(string name, uvm_component parent); + super.new(name, parent); + `uvm_info("new", $sformatf("Initialized testbench %s", name), UVM_LOW) + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + + rst_agt = agent_reset::type_id::create("reset_agent", this); + tb_sequencer = sequencer_tb::type_id::create("tb_sequencer", this); + endfunction + + function void connect_phase(uvm_phase phase); + super.connect_phase(phase); + + if (!uvm_config_db#(virtual testbench_if)::get(this, "", "tb_vif", tb_if)) begin + `uvm_fatal("CFG_DB_FAIL", $sformatf("Failed to fetch interface for %0s", get_full_name())) + end + + `uvm_info("connect_phase", $sformatf("Build phase complete"), UVM_LOW) + endfunction + + function uvm_sequencer get_tb_sequencer(); + return tb_sequencer; + endfunction + + function uvm_sequencer get_rst_sequencer(); + return rst_agt.sequencer; + endfunction +endclass diff --git a/uvm_tb.sv b/uvm_tb.sv new file mode 100644 index 0000000..1cfd811 --- /dev/null +++ b/uvm_tb.sv @@ -0,0 +1,55 @@ +// uvm_tb in SystemVerilog +import uvm_pkg::*; + +module uvm_tb (input logic sys_clk); + logic clk; + logic rst_n; + logic [31:0] d1_data_i; + logic [31:0] d1_data_o; + logic [31:0] d2_data_i; + logic [31:0] d2_data_o; + + design_if d1_if(.clk(clk)); + assign d1_if.rst_n = rst_n; + assign d1_if.data_in = d1_data_i; + assign d1_data_o = d1_if.data_out; + + design_if d2_if(.clk(clk)); + assign d2_if.rst_n = rst_n; + assign d2_if.data_in = d2_data_i; + assign d2_data_o = d2_if.data_out; + + testbench_if uvm_tb_if( + .clk(clk), + .d1_if(d1_if), + .d2_if(d2_if) + ); + assign rst_n = uvm_tb_if.rst_n; + + rtl_design d1(d1_if.DN); + + rtl_design d2(d2_if.DN); + + initial begin + // TbEnv t = new(.name("uvm_tbTbEnv"), .parent(null)); + // // .intf(uvm_tb_if.TB)); + + // t.set_handles(uvm_tb_if); + // // t.run_phase(); + + // $display("Simulation with UVM done at %t", $time); + // $finish; + + uvm_config_db#(virtual testbench_if)::set(uvm_root::get(), "*", "tb_vif", uvm_tb_if); + + run_test(); + end + + // initial begin + // $dumpfile("wave.vcd"); + // $dumpvars(); + // end + + // TODO: Move to interface. Parameterize frequency + always #5 clk = ~clk; +endmodule