├── external.cpp ├── README.md ├── input_if.sv ├── output_if.sv ├── sequencer.sv ├── packet_out.sv ├── packet_in.sv ├── Makefile ├── sequence_in.sv ├── simple_test.sv ├── agent_out.sv ├── agent.sv ├── refmod.sv ├── LICENSE ├── driver_out.sv ├── adder.sv ├── top.sv ├── monitor_out.sv ├── monitor.sv ├── env.sv ├── comparator.sv └── driver.sv /external.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern "C" int sum(int a, int b){ 5 | return a+b; 6 | } 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # easyUVM 2 | A simple UVM example with DPI 3 | You can find an explanation of the UVM testbench of the adder module on https://sistenix.com/basic_uvm.html 4 | -------------------------------------------------------------------------------- /input_if.sv: -------------------------------------------------------------------------------- 1 | interface input_if(input clk, rst); 2 | logic [31:0] A, B; 3 | logic valid, ready; 4 | 5 | modport port(input clk, rst, A, B, valid, output ready); 6 | endinterface 7 | 8 | -------------------------------------------------------------------------------- /output_if.sv: -------------------------------------------------------------------------------- 1 | interface output_if(input clk, rst); 2 | logic [31:0] data; 3 | logic valid, ready; 4 | 5 | modport port(input clk, rst, output valid, data, ready); 6 | endinterface 7 | 8 | -------------------------------------------------------------------------------- /sequencer.sv: -------------------------------------------------------------------------------- 1 | class sequencer extends uvm_sequencer #(packet_in); 2 | `uvm_component_utils(sequencer) 3 | 4 | function new (string name = "sequencer", uvm_component parent = null); 5 | super.new(name, parent); 6 | endfunction 7 | endclass: sequencer 8 | -------------------------------------------------------------------------------- /packet_out.sv: -------------------------------------------------------------------------------- 1 | class packet_out extends uvm_sequence_item; 2 | integer data; 3 | 4 | `uvm_object_utils_begin(packet_out) 5 | `uvm_field_int(data, UVM_ALL_ON|UVM_HEX) 6 | `uvm_object_utils_end 7 | 8 | function new(string name="packet_out"); 9 | super.new(name); 10 | endfunction: new 11 | endclass: packet_out 12 | -------------------------------------------------------------------------------- /packet_in.sv: -------------------------------------------------------------------------------- 1 | class packet_in extends uvm_sequence_item; 2 | rand integer A; 3 | rand integer B; 4 | 5 | `uvm_object_utils_begin(packet_in) 6 | `uvm_field_int(A, UVM_ALL_ON|UVM_HEX) 7 | `uvm_field_int(B, UVM_ALL_ON|UVM_HEX) 8 | `uvm_object_utils_end 9 | 10 | function new(string name="packet_in"); 11 | super.new(name); 12 | endfunction: new 13 | endclass: packet_in 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | sim: clean 2 | g++ -c external.cpp -o external.o 3 | vcs -full64 -sverilog top.sv -dpi -ntb_opts uvm -debug_pp -timescale=1ns/10ps external.o 4 | $ ./simv +UVM_TR_RECORD +UVM_VERBOSITY=HIGH +UVM_TESTNAME=simple_test 5 | clean: 6 | rm -rf DVEfiles csrc simv simv.daidir ucli.key .vlogansetup.args .vlogansetup.env .vcs_lib_lock simv.vdb AN.DB vc_hdrs.h *.diag *.vpd *tar.gz external.o 7 | 8 | view_waves: 9 | dve & 10 | -------------------------------------------------------------------------------- /sequence_in.sv: -------------------------------------------------------------------------------- 1 | class sequence_in extends uvm_sequence #(packet_in); 2 | `uvm_object_utils(sequence_in) 3 | 4 | function new(string name="sequence_in"); 5 | super.new(name); 6 | endfunction: new 7 | 8 | task body; 9 | packet_in tx; 10 | 11 | forever begin 12 | tx = packet_in::type_id::create("tx"); 13 | start_item(tx); 14 | assert(tx.randomize()); 15 | finish_item(tx); 16 | end 17 | endtask: body 18 | endclass: sequence_in 19 | 20 | -------------------------------------------------------------------------------- /simple_test.sv: -------------------------------------------------------------------------------- 1 | class simple_test extends uvm_test; 2 | env env_h; 3 | sequence_in seq; 4 | 5 | `uvm_component_utils(simple_test) 6 | 7 | function new(string name, uvm_component parent = null); 8 | super.new(name, parent); 9 | endfunction 10 | 11 | virtual function void build_phase(uvm_phase phase); 12 | super.build_phase(phase); 13 | env_h = env::type_id::create("env_h", this); 14 | seq = sequence_in::type_id::create("seq", this); 15 | endfunction 16 | 17 | task run_phase(uvm_phase phase); 18 | seq.start(env_h.mst.sqr); 19 | endtask: run_phase 20 | 21 | endclass 22 | -------------------------------------------------------------------------------- /agent_out.sv: -------------------------------------------------------------------------------- 1 | class agent_out extends uvm_agent; 2 | driver_out drv; 3 | monitor_out mon; 4 | 5 | uvm_analysis_port #(packet_out) item_collected_port; 6 | 7 | `uvm_component_utils(agent_out) 8 | 9 | function new(string name = "agent_out", uvm_component parent = null); 10 | super.new(name, parent); 11 | item_collected_port = new("item_collected_port", this); 12 | endfunction 13 | 14 | virtual function void build_phase(uvm_phase phase); 15 | super.build_phase(phase); 16 | mon = monitor_out::type_id::create("mon_out", this); 17 | drv = driver_out::type_id::create("drv_out", this); 18 | endfunction 19 | 20 | virtual function void connect_phase(uvm_phase phase); 21 | super.connect_phase(phase); 22 | mon.item_collected_port.connect(item_collected_port); 23 | endfunction 24 | endclass: agent_out 25 | -------------------------------------------------------------------------------- /agent.sv: -------------------------------------------------------------------------------- 1 | class agent extends uvm_agent; 2 | sequencer sqr; 3 | driver drv; 4 | monitor mon; 5 | 6 | uvm_analysis_port #(packet_in) item_collected_port; 7 | 8 | `uvm_component_utils(agent) 9 | 10 | function new(string name = "agent", uvm_component parent = null); 11 | super.new(name, parent); 12 | item_collected_port = new("item_collected_port", this); 13 | endfunction 14 | 15 | virtual function void build_phase(uvm_phase phase); 16 | super.build_phase(phase); 17 | mon = monitor::type_id::create("mon", this); 18 | sqr = sequencer::type_id::create("sqr", this); 19 | drv = driver::type_id::create("drv", this); 20 | endfunction 21 | 22 | virtual function void connect_phase(uvm_phase phase); 23 | super.connect_phase(phase); 24 | mon.item_collected_port.connect(item_collected_port); 25 | drv.seq_item_port.connect(sqr.seq_item_export); 26 | endfunction 27 | endclass: agent 28 | -------------------------------------------------------------------------------- /refmod.sv: -------------------------------------------------------------------------------- 1 | import "DPI-C" context function int sum(int a, int b); 2 | 3 | class refmod extends uvm_component; 4 | `uvm_component_utils(refmod) 5 | 6 | packet_in tr_in; 7 | packet_out tr_out; 8 | integer a, b; 9 | uvm_get_port #(packet_in) in; 10 | uvm_put_port #(packet_out) out; 11 | 12 | function new(string name = "refmod", uvm_component parent); 13 | super.new(name, parent); 14 | in = new("in", this); 15 | out = new("out", this); 16 | endfunction 17 | 18 | virtual function void build_phase(uvm_phase phase); 19 | super.build_phase(phase); 20 | tr_out = packet_out::type_id::create("tr_out", this); 21 | endfunction: build_phase 22 | 23 | virtual task run_phase(uvm_phase phase); 24 | super.run_phase(phase); 25 | 26 | forever begin 27 | in.get(tr_in); 28 | tr_out.data = sum(tr_in.A, tr_in.B); 29 | out.put(tr_out); 30 | end 31 | endtask: run_phase 32 | endclass: refmod 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nelson C S Campos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /driver_out.sv: -------------------------------------------------------------------------------- 1 | typedef virtual output_if output_vif; 2 | 3 | class driver_out extends uvm_driver #(packet_out); 4 | `uvm_component_utils(driver_out) 5 | output_vif vif; 6 | 7 | function new(string name = "driver_out", uvm_component parent = null); 8 | super.new(name, parent); 9 | endfunction 10 | 11 | virtual function void build_phase(uvm_phase phase); 12 | super.build_phase(phase); 13 | assert(uvm_config_db#(output_vif)::get(this, "", "vif", vif)); 14 | endfunction 15 | 16 | virtual task run_phase(uvm_phase phase); 17 | super.run_phase(phase); 18 | fork 19 | reset_signals(); 20 | drive(phase); 21 | join 22 | endtask 23 | 24 | virtual protected task reset_signals(); 25 | wait (vif.rst === 1); 26 | forever begin 27 | vif.ready <= '0; 28 | @(posedge vif.rst); 29 | end 30 | endtask 31 | 32 | virtual protected task drive(uvm_phase phase); 33 | wait(vif.rst === 1); 34 | @(negedge vif.rst); 35 | forever begin 36 | @(posedge vif.clk); 37 | vif.ready <= 1; 38 | end 39 | endtask 40 | endclass 41 | -------------------------------------------------------------------------------- /adder.sv: -------------------------------------------------------------------------------- 1 | module adder(input_if.port inter, output_if.port out_inter, output state); 2 | enum logic [1:0] {INITIAL,WAIT,SEND} state; 3 | 4 | always_ff @(posedge inter.clk) 5 | if(inter.rst) begin 6 | inter.ready <= 0; 7 | out_inter.data <= 'x; 8 | out_inter.valid <= 0; 9 | state <= INITIAL; 10 | end 11 | else case(state) 12 | INITIAL: begin 13 | inter.ready <= 1; 14 | state <= WAIT; 15 | end 16 | 17 | WAIT: begin 18 | if(inter.valid) begin 19 | inter.ready <= 0; 20 | out_inter.data <= inter.A + inter.B; 21 | out_inter.valid <= 1; 22 | state <= SEND; 23 | end 24 | end 25 | 26 | SEND: begin 27 | if(out_inter.ready) begin 28 | out_inter.valid <= 0; 29 | inter.ready <= 1; 30 | state <= WAIT; 31 | end 32 | end 33 | endcase 34 | endmodule: adder 35 | -------------------------------------------------------------------------------- /top.sv: -------------------------------------------------------------------------------- 1 | import uvm_pkg::*; 2 | `include "uvm_macros.svh" 3 | `include "./input_if.sv" 4 | `include "./output_if.sv" 5 | `include "./adder.sv" 6 | `include "./packet_in.sv" 7 | `include "./packet_out.sv" 8 | `include "./sequence_in.sv" 9 | `include "./sequencer.sv" 10 | `include "./driver.sv" 11 | `include "./driver_out.sv" 12 | `include "./monitor.sv" 13 | `include "./monitor_out.sv" 14 | `include "./agent.sv" 15 | `include "./agent_out.sv" 16 | `include "./refmod.sv" 17 | `include "./comparator.sv" 18 | `include "./env.sv" 19 | `include "./simple_test.sv" 20 | 21 | //Top 22 | module top; 23 | logic clk; 24 | logic rst; 25 | 26 | initial begin 27 | clk = 0; 28 | rst = 1; 29 | #22 rst = 0; 30 | 31 | end 32 | 33 | always #5 clk = !clk; 34 | 35 | logic [1:0] state; 36 | 37 | input_if in(clk, rst); 38 | output_if out(clk, rst); 39 | 40 | adder sum(in, out, state); 41 | 42 | initial begin 43 | `ifdef INCA 44 | $recordvars(); 45 | `endif 46 | `ifdef VCS 47 | $vcdpluson; 48 | `endif 49 | `ifdef QUESTA 50 | $wlfdumpvars(); 51 | set_config_int("*", "recording_detail", 1); 52 | `endif 53 | 54 | uvm_config_db#(input_vif)::set(uvm_root::get(), "*.env_h.mst.*", "vif", in); 55 | uvm_config_db#(output_vif)::set(uvm_root::get(), "*.env_h.slv.*", "vif", out); 56 | 57 | run_test("simple_test"); 58 | end 59 | endmodule 60 | -------------------------------------------------------------------------------- /monitor_out.sv: -------------------------------------------------------------------------------- 1 | class monitor_out extends uvm_monitor; 2 | `uvm_component_utils(monitor_out) 3 | output_vif vif; 4 | event begin_record, end_record; 5 | packet_out tr; 6 | uvm_analysis_port #(packet_out) item_collected_port; 7 | 8 | function new(string name, uvm_component parent); 9 | super.new(name, parent); 10 | item_collected_port = new ("item_collected_port", this); 11 | endfunction 12 | 13 | virtual function void build_phase(uvm_phase phase); 14 | super.build_phase(phase); 15 | assert(uvm_config_db#(output_vif)::get(this, "", "vif", vif)); 16 | tr = packet_out::type_id::create("tr", this); 17 | endfunction 18 | 19 | virtual task run_phase(uvm_phase phase); 20 | super.run_phase(phase); 21 | fork 22 | collect_transactions(phase); 23 | record_tr(); 24 | join 25 | endtask 26 | 27 | virtual task collect_transactions(uvm_phase phase); 28 | wait(vif.rst === 1); 29 | @(negedge vif.rst); 30 | 31 | forever begin 32 | do begin 33 | @(posedge vif.clk); 34 | end while (vif.valid === 0 || vif.ready === 0); 35 | -> begin_record; 36 | 37 | tr.data = vif.data; 38 | item_collected_port.write(tr); 39 | 40 | @(posedge vif.clk); 41 | -> end_record; 42 | end 43 | endtask 44 | 45 | virtual task record_tr(); 46 | forever begin 47 | @(begin_record); 48 | begin_tr(tr, "monitor_out"); 49 | @(end_record); 50 | end_tr(tr); 51 | end 52 | endtask 53 | endclass 54 | -------------------------------------------------------------------------------- /monitor.sv: -------------------------------------------------------------------------------- 1 | class monitor extends uvm_monitor; 2 | input_vif vif; 3 | event begin_record, end_record; 4 | packet_in tr; 5 | uvm_analysis_port #(packet_in) item_collected_port; 6 | `uvm_component_utils(monitor) 7 | 8 | function new(string name, uvm_component parent); 9 | super.new(name, parent); 10 | item_collected_port = new ("item_collected_port", this); 11 | endfunction 12 | 13 | virtual function void build_phase(uvm_phase phase); 14 | super.build_phase(phase); 15 | assert(uvm_config_db#(input_vif)::get(this, "", "vif", vif)); 16 | tr = packet_in::type_id::create("tr", this); 17 | endfunction 18 | 19 | virtual task run_phase(uvm_phase phase); 20 | super.run_phase(phase); 21 | fork 22 | collect_transactions(phase); 23 | record_tr(); 24 | join 25 | endtask 26 | 27 | virtual task collect_transactions(uvm_phase phase); 28 | wait(vif.rst === 1); 29 | @(negedge vif.rst); 30 | 31 | forever begin 32 | do begin 33 | @(posedge vif.clk); 34 | end while (vif.valid === 0 || vif.ready === 0); 35 | -> begin_record; 36 | 37 | tr.A = vif.A; 38 | tr.B = vif.B; 39 | item_collected_port.write(tr); 40 | 41 | @(posedge vif.clk); 42 | -> end_record; 43 | end 44 | endtask 45 | 46 | virtual task record_tr(); 47 | forever begin 48 | @(begin_record); 49 | begin_tr(tr, "monitor"); 50 | @(end_record); 51 | end_tr(tr); 52 | end 53 | endtask 54 | endclass 55 | -------------------------------------------------------------------------------- /env.sv: -------------------------------------------------------------------------------- 1 | class env extends uvm_env; 2 | agent mst; 3 | refmod rfm; 4 | agent_out slv; 5 | comparator #(packet_out) comp; 6 | uvm_tlm_analysis_fifo #(packet_in) to_refmod; 7 | 8 | `uvm_component_utils(env) 9 | 10 | function new(string name, uvm_component parent = null); 11 | super.new(name, parent); 12 | to_refmod = new("to_refmod", this); 13 | endfunction 14 | 15 | virtual function void build_phase(uvm_phase phase); 16 | super.build_phase(phase); 17 | mst = agent::type_id::create("mst", this); 18 | slv = agent_out::type_id::create("slv", this); 19 | rfm = refmod::type_id::create("rfm", this); 20 | comp = comparator#(packet_out)::type_id::create("comp", this); 21 | endfunction 22 | 23 | virtual function void connect_phase(uvm_phase phase); 24 | super.connect_phase(phase); 25 | // Connect MST to FIFO 26 | mst.item_collected_port.connect(to_refmod.analysis_export); 27 | 28 | // Connect FIFO to REFMOD 29 | rfm.in.connect(to_refmod.get_export); 30 | 31 | //Connect scoreboard 32 | rfm.out.connect(comp.from_refmod); 33 | slv.item_collected_port.connect(comp.from_dut); 34 | endfunction 35 | 36 | virtual function void end_of_elaboration_phase(uvm_phase phase); 37 | super.end_of_elaboration_phase(phase); 38 | endfunction 39 | 40 | virtual function void report_phase(uvm_phase phase); 41 | super.report_phase(phase); 42 | `uvm_info(get_type_name(), $sformatf("Reporting matched %0d", comp.m_matches), UVM_NONE) 43 | if (comp.m_mismatches) begin 44 | `uvm_error(get_type_name(), $sformatf("Saw %0d mismatched samples", comp.m_mismatches)) 45 | end 46 | endfunction 47 | endclass 48 | -------------------------------------------------------------------------------- /comparator.sv: -------------------------------------------------------------------------------- 1 | class comparator #(type T = packet_out) extends uvm_scoreboard; 2 | typedef comparator #(T) this_type; 3 | `uvm_component_param_utils(this_type) 4 | 5 | const static string type_name = "comparator #(T)"; 6 | 7 | uvm_put_imp #(T, this_type) from_refmod; 8 | uvm_analysis_imp #(T, this_type) from_dut; 9 | 10 | typedef uvm_built_in_converter #( T ) convert; 11 | 12 | int m_matches, m_mismatches; 13 | T exp; 14 | bit free; 15 | event compared, end_of_simulation; 16 | 17 | function new(string name, uvm_component parent); 18 | super.new(name, parent); 19 | from_refmod = new("from_refmod", this); 20 | from_dut = new("from_dut", this); 21 | m_matches = 0; 22 | m_mismatches = 0; 23 | exp = new("exp"); 24 | free = 1; 25 | endfunction 26 | 27 | virtual function string get_type_name(); 28 | return type_name; 29 | endfunction 30 | 31 | task run_phase(uvm_phase phase); 32 | phase.raise_objection(this); 33 | @(end_of_simulation); 34 | phase.drop_objection(this); 35 | endtask 36 | 37 | virtual task put(T t); 38 | if(!free) @compared; 39 | exp.copy(t); 40 | free = 0; 41 | 42 | @compared; 43 | free = 1; 44 | endtask 45 | 46 | virtual function bit try_put(T t); 47 | if(free) begin 48 | exp.copy(t); 49 | free = 0; 50 | return 1; 51 | end 52 | else return 0; 53 | endfunction 54 | 55 | virtual function bit can_put(); 56 | return free; 57 | endfunction 58 | 59 | virtual function void write(T rec); 60 | if (free) 61 | uvm_report_fatal("No expect transaction to compare with", ""); 62 | 63 | if(!(exp.compare(rec))) begin 64 | uvm_report_warning("Comparator Mismatch", ""); 65 | m_mismatches++; 66 | end 67 | else begin 68 | uvm_report_info("Comparator Match", ""); 69 | m_matches++; 70 | end 71 | 72 | if(m_matches+m_mismatches > 100) 73 | -> end_of_simulation; 74 | 75 | -> compared; 76 | endfunction 77 | endclass 78 | -------------------------------------------------------------------------------- /driver.sv: -------------------------------------------------------------------------------- 1 | typedef virtual input_if input_vif; 2 | 3 | class driver extends uvm_driver #(packet_in); 4 | `uvm_component_utils(driver) 5 | input_vif vif; 6 | event begin_record, end_record; 7 | 8 | function new(string name = "driver", uvm_component parent = null); 9 | super.new(name, parent); 10 | endfunction 11 | 12 | virtual function void build_phase(uvm_phase phase); 13 | super.build_phase(phase); 14 | assert(uvm_config_db#(input_vif)::get(this, "", "vif", vif)); 15 | endfunction 16 | 17 | virtual task run_phase(uvm_phase phase); 18 | super.run_phase(phase); 19 | fork 20 | reset_signals(); 21 | get_and_drive(phase); 22 | record_tr(); 23 | join 24 | endtask 25 | 26 | virtual protected task reset_signals(); 27 | wait (vif.rst === 1); 28 | forever begin 29 | vif.valid <= '0; 30 | vif.A <= 'x; 31 | vif.B <= 'x; 32 | @(posedge vif.rst); 33 | end 34 | endtask 35 | 36 | virtual protected task get_and_drive(uvm_phase phase); 37 | wait(vif.rst === 1); 38 | @(negedge vif.rst); 39 | @(posedge vif.clk); 40 | 41 | forever begin 42 | seq_item_port.get(req); 43 | -> begin_record; 44 | drive_transfer(req); 45 | end 46 | endtask 47 | 48 | virtual protected task drive_transfer(packet_in tr); 49 | vif.A = tr.A; 50 | vif.B = tr.B; 51 | vif.valid = 1; 52 | 53 | @(posedge vif.clk) 54 | 55 | while(!vif.ready) 56 | @(posedge vif.clk); 57 | 58 | -> end_record; 59 | @(posedge vif.clk); //hold time 60 | vif.valid = 0; 61 | @(posedge vif.clk); 62 | endtask 63 | 64 | virtual task record_tr(); 65 | forever begin 66 | @(begin_record); 67 | begin_tr(req, "driver"); 68 | @(end_record); 69 | end_tr(req); 70 | end 71 | endtask 72 | endclass 73 | --------------------------------------------------------------------------------