LastHorizon Posted August 3, 2022 Report Posted August 3, 2022 Hi there, I am trying to produce some very small working examples of using UVM to verify designs so that I can understand the verification flow. I have one example that is causing me some problems. When I try and get a virtual pointer to the interface that should be loaded into the config db, I am returning an problem. (i.e. the interface appears to be not setting to the config db properly). I have stared so much as this code now and I still cannot see the problem! The code is below (I am so sorry it is all in one file, like I said this is meant to be just a thing for me to play with to decide what formal verification I wish to implement - This is not My Final Form!) `timescale 1ns/1ps `include "uvm_macros.svh" `define LENGTH 4 import uvm_pkg::*; class Item extends uvm_sequence_item; `uvm_object_utils(Item) rand bit in; bit out; virtual function string convert2str(); return $sformatf("in=%0d, out=%0d", in, out); endfunction function new(string name = "Item"); super.new(name); endfunction constraint c1 {in dist {0:/20, 1:/80}; } endclass class gen_item_seq extends uvm_sequence; `uvm_object_utils(gen_item_seq) function new(string name="gen_item_seq"); super.new(name); endfunction rand int num; // config total number of items to be sent constraint c1 { soft num inside {[10:50]}; } virtual task body (); for(int i = 0; i < num; i++) begin Item m_item = Item::type_id::create("m_item"); start_item(m_item); m_item.randomize(); `uvm_info("SEQ", $sformatf("Generate new item: %s", m_item.convert2str()), UVM_HIGH) finish_item(m_item); end `uvm_info("SEQ", $sformatf("Done generation of %0d items", num), UVM_LOW) endtask endclass //The driver is responsible for driving transactions to the DUT // All it does it to get a transaction from the mailbox if it is // available and drive it out into the DUT interface class driver extends uvm_driver #(Item); `uvm_component_utils(driver) function new(string name = "driver", uvm_component parent=null); super.new(name, parent); endfunction virtual des_if vif; virtual function void build_phase(uvm_phase phase); super.build_phase(phase); if(!uvm_config_db#(virtual des_if)::get(this, "", "des_if", vif)) `uvm_fatal("DRV", "Could not get vif") endfunction virtual task run_phase(uvm_phase phase); super.run_phase(phase); forever begin Item m_item; `uvm_info("DRV", $sformatf("Wait for item from sequencer"), UVM_HIGH) seq_item_port.get_next_item(m_item); drive_item(m_item); seq_item_port.item_done(); end endtask virtual task drive_item(Item m_item); @(vif.cb); vif.cb.in <= m_item.in; endtask endclass // The monitor has a virtual interface handl with which // it can monitor the events happening on the interface // It sees new transcations then captures information // into a packet and sends it to the scoreboard // using another mailbox class monitor extends uvm_monitor; `uvm_component_utils(monitor) function new(string name="monitor", uvm_component parent=null); super.new(name, parent); endfunction uvm_analysis_port #(Item) mon_analysis_port; virtual des_if vif; virtual function void build_phase(uvm_phase phase); super.build_phase(phase); if(!uvm_config_db#(virtual des_if)::get(this, "", "des_vif", vif)) `uvm_info("MON", "hello from monitor", UVM_LOW) `uvm_fatal("MON", "Could not get vif") mon_analysis_port = new ("mon_analysis_port", this); endfunction virtual task run_phase(uvm_phase phase); super.run_phase(phase); //This task monitors the interface for a complete // transaction and writes into analysis port when complete forever begin @(vif.cb); if(vif.rstn) begin Item item = Item::type_id::create("item"); item.in = vif.in; item.out = vif.cb.out; mon_analysis_port.write(item); `uvm_info("MON", $sformatf("Saw item %s", item.convert2str()), UVM_HIGH) end end endtask endclass // The scoreboard is reponsible for checking design functionality and // should track input and try to match the pattern and ensure that // the design has found the pattern as well. The scoreboard should // flag an error if the design didn't find the pattern and ensure // that "out" remains zero, and if the design found the pattern, // "out" is set to the correct value class scoreboard extends uvm_scoreboard; `uvm_component_utils(scoreboard) function new(string name="scoreboard", uvm_component parent=null); super.new(name, parent); endfunction bit [`LENGTH-1:0] ref_pattern; bit [`LENGTH-1:0] act_pattern; bit exp_out; uvm_analysis_imp #(Item, scoreboard) m_analysis_imp; virtual function void build_phase(uvm_phase phase); super.build_phase(phase); m_analysis_imp = new("m_analysis_imp", this); if(!uvm_config_db#(bit[`LENGTH-1:0])::get(this, "*", "ref_pattern", ref_pattern)) `uvm_fatal("SCBD", "Did not get ref_pattern !") endfunction virtual function write(Item item); act_pattern = act_pattern << 1 | item.in; `uvm_info("SCBD", $sformatf("in=%0d out=%0d ref=0b%0b act=0b%0b", item.in, item.out, ref_pattern, act_pattern), UVM_LOW) // Always check that expected out value is the actual obseved value, // Since it takes 1 clock for out to be updated after pattn match, //do the check first then update exp_out value if(item.out != exp_out) begin `uvm_error("SCBD", $sformatf("ERROR ! out=%0d exp=%0d", item.out, exp_out)) end else begin `uvm_info("SCBD", $sformatf("Pass ! out=%0d exp=%0d", item.out, exp_out), UVM_HIGH) end // If current index has reached the full pattern, then set exp_out to be 1 // which will be checked in the next clock. If pattern is not complete, keep // exp_out to zero if(!(ref_pattern ^ act_pattern)) begin `uvm_info("SCBD", $sformatf("Pattern found to match, next one should be 1!"), UVM_LOW) exp_out = 1; end else begin exp_out = 0; end endfunction endclass // Create an intermediate container called "agent" to hold // driver, monitor and sequencer class agent extends uvm_agent; `uvm_component_utils(agent) function new(string name="agent", uvm_component parent=null); super.new(name, parent); endfunction driver d0; //Driver handle monitor m0; //Monitor handle uvm_sequencer #(Item) s0; //Sequence Handle virtual function void build_phase(uvm_phase phase); super.build_phase(phase); s0 = uvm_sequencer#(Item)::type_id::create("s0", this); d0 = driver::type_id::create("d0", this); m0 = monitor::type_id::create("m0", this); endfunction virtual function void connect_phase(uvm_phase phase); super.connect_phase(phase); d0.seq_item_port.connect(s0.seq_item_export); endfunction endclass // The environment is a contain objet simply to hold // all verification components together. This environment can // then be reused later and all components in it would be // automatically connected and available for use class env extends uvm_env; `uvm_component_utils(env); function new(string name="env", uvm_component parent=null); super.new(name, parent); endfunction agent a0; //agent handle scoreboard sb0; //scoreboard handle virtual function void build_phase(uvm_phase phase); super.build_phase(phase); a0 = agent::type_id::create("a0", this); sb0 = scoreboard::type_id::create("sb0", this); endfunction virtual function void connect_phase(uvm_phase phase); super.connect_phase(phase); a0.m0.mon_analysis_port.connect(sb0.m_analysis_imp); endfunction endclass // Test class instantiates the environment and starts it. class base_test extends uvm_test; `uvm_component_utils(base_test) function new(string name="base_test", uvm_component parent=null); super.new(name, parent); endfunction env e0; bit[`LENGTH-1:0] pattern = 4'b1011; gen_item_seq seq; virtual des_if vif; virtual function void build_phase(uvm_phase phase); super.build_phase(phase); // create the environment e0 = env::type_id::create("e0", this); // Get virtual IF handle from the top level and pass it to everything // in the env level if(!uvm_config_db#(virtual des_if)::get(this, "", "des_if", vif)) `uvm_fatal("TEST", "Did not get vif") uvm_config_db#(virtual des_if)::set(this, "e0.a0.*", "des_if", vif); // Setp pattern queue and place into config db uvm_config_db#(bit[`LENGTH-1:0])::set(this, "*", "ref_pattern", pattern); // Create sequence and randomise it seq = gen_item_seq::type_id::create("seq"); seq.randomize(); endfunction virtual task run_phase(uvm_phase phase); phase.raise_objection(this); apply_reset(); seq.start(e0.a0.s0); #200; phase.drop_objection(this); endtask virtual task apply_reset(); vif.rstn <= 0; vif.in <= 0; repeat(5) @ (posedge vif.clk); vif.rstn <= 1; repeat(10) @ (posedge vif.clk); endtask endclass class test_1011 extends base_test; `uvm_component_utils(test_1011) function new (string name="test_1011", uvm_component parent=null); super.new(name, parent); endfunction virtual function void build_phase(uvm_phase phase); pattern = 4'b1011; super.build_phase(phase); // seq.randomize() with { num inside {[300:500]}; }; endfunction endclass // This interface allows verification components to access DUT signals // using a virtual interface handle interface des_if (input bit clk); logic rstn; logic in; logic out; clocking cb @ (posedge clk); default input #1step output #3ns; input out; output in; endclocking endinterface module tb(); reg clk; always #10 clk =~ clk; des_if _if(clk); det_1011 u0 ( .clk(clk), .rstn(_if.rstn), .in(_if.in), .out(_if.out) ); initial begin clk <= 0; uvm_config_db#(virtual des_if)::set(null, "*", "des_vif", _if); run_test("test_1011"); end endmodule : tb // Design v1.0 module det_1011 (input clk, input rstn, input in, input out); parameter IDLE = 0, S1 = 1, S10 = 2, S101 = 3, S1011 = 4; reg [2:0] cur_state, next_state; assign out = cur_state == S1011 ? 1 : 0; always @ (posedge clk) begin if(!rstn) cur_state <= IDLE; else cur_state <= next_state; end always @ (cur_state or in) begin case(cur_state) IDLE: begin if (in) next_state = S1; else next_state = IDLE; end S1 : begin if (in) next_state = IDLE; else next_state = S10; end S10 : begin if (in) next_state = S101; else next_state = IDLE; end S101: begin if (in) next_state = S1011; else next_state = IDLE; end S1011 : begin next_state = IDLE; end endcase end endmodule If anyone can see my problem I would be so grateful! Thanks!! Quote
David Black Posted August 3, 2022 Report Posted August 3, 2022 I got it to work after realizing that you were missing connect phase and had things in scrambled ordering within build phase. During build phase, which happens top-down uvm_config_db#(TYPE)::get things set previously in the object hierarchy if needed for the next step uvm_config_db#(TYPE)::set set_type overrides create child components During connect phase uvm_config_db#(TYPE)::get connections for virtual interfaces in monitor and driver (usually done in the agent) connect ports During the run-time phase (usually in env) Create, randomize, and start a top-level sequence Also, you had some confusion about the config_db names "des_if" vs "des_vif" in the string name. Also observed that you had some indented code intended for `if` statements; however, no surrounding begin/end. I ALWAYS use begin/end for EVERY if, case and loop. Avoids so many issues. Quote
LastHorizon Posted August 3, 2022 Author Report Posted August 3, 2022 Thankyou! Literally spent hours and could not see the typo of "des_vif" vs. "des_if" - talk about not seeing the wood for the trees! With reference to the scrambled order of doing things, could you please highlight the lines you are referring to? (Always keen to learn things!) Thanks Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.