`ifndef CALC_MONITOR_SV `define CALC_MONITOR_SV //----------------------------------------------------------------------------- // Calc1 monitor. // // Passive component. One collector thread per port reconstructs a transaction // from the pin activity: // * a request starts when reqX_cmd_in != 0 -> capture cmd and operand1, // * operand2 is the data line on the following cycle, // * the matching response is the first cycle in which out_respX != 0. // // The completed transaction (stimulus + observed response/result) is broadcast // over the analysis port to the scoreboard, and functional coverage is sampled. //----------------------------------------------------------------------------- class calc_monitor extends uvm_monitor; // control fields bit checks_enable = 1; bit coverage_enable = 1; uvm_analysis_port #(calc_seq_item) item_collected_port; `uvm_component_utils_begin(calc_monitor) `uvm_field_int(checks_enable, UVM_DEFAULT) `uvm_field_int(coverage_enable, UVM_DEFAULT) `uvm_component_utils_end // The virtual interface used to view HDL signals. virtual interface calc_if vif; // number of transactions collected (per port and total) int unsigned num_collected; //-------------------------------------------------------------------------- // Functional coverage model (Vezba 11). //-------------------------------------------------------------------------- bit [CMD_WIDTH-1:0] cov_cmd; bit [1:0] cov_port; bit [RESP_WIDTH-1:0] cov_resp; bit [DATA_WIDTH-1:0] cov_op1; bit [DATA_WIDTH-1:0] cov_op2; covergroup calc_cg; option.per_instance = 1; option.name = "calc_functional_coverage"; cp_cmd : coverpoint cov_cmd { bins add = {CMD_ADD}; bins sub = {CMD_SUB}; bins shl = {CMD_SHL}; bins shr = {CMD_SHR}; bins invalid = {[4'h3:4'h4], 4'h7, [4'h8:4'hF]}; } cp_port : coverpoint cov_port { bins port1 = {2'd0}; bins port2 = {2'd1}; bins port3 = {2'd2}; bins port4 = {2'd3}; } // the monitor only emits an item once a real response is seen, so only // SUCCESS and ERROR are ever sampled here cp_resp : coverpoint cov_resp { bins success = {RESP_SUCCESS}; bins error = {RESP_ERROR}; } // interesting operand corners cp_op1 : coverpoint cov_op1 { bins zero = {32'h0000_0000}; bins one = {32'h0000_0001}; bins max = {32'hFFFF_FFFF}; bins msb = {32'h8000_0000}; bins others = default; } cp_op2 : coverpoint cov_op2 { bins zero = {32'h0000_0000}; bins one = {32'h0000_0001}; bins max = {32'hFFFF_FFFF}; bins shamt = {[32'h2:32'h1F]}; // small shift amounts bins others = default; } // every legal command must be exercised on every port cx_cmd_port : cross cp_cmd, cp_port; // every command must be seen producing both success and error responses cx_cmd_resp : cross cp_cmd, cp_resp; endgroup function new(string name = "calc_monitor", uvm_component parent = null); super.new(name,parent); item_collected_port = new("item_collected_port", this); calc_cg = new(); endfunction function void connect_phase(uvm_phase phase); super.connect_phase(phase); if (!uvm_config_db#(virtual calc_if)::get(this, "", "calc_if", vif)) `uvm_fatal("NOVIF",{"virtual interface must be set:",get_full_name(),".vif"}) endfunction : connect_phase task main_phase(uvm_phase phase); wait_reset_done(); // launch one independent collector per port for (int p = 0; p < NUM_PORTS; p++) begin automatic int port = p; fork collect_port(port[1:0]); join_none end endtask : main_phase // Collect every request/response pair seen on a single port. task collect_port(bit [1:0] port); calc_seq_item it; int unsigned wait_cnt; forever begin // wait for the start of a request on this port do @(posedge vif.clk); while (get_cmd(port) === CMD_NOP || vif.rst !== '0); it = calc_seq_item::type_id::create($sformatf("it_p%0d", port)); it.port = port; it.cmd = get_cmd(port); it.op1 = get_data(port); // operand2 is presented on the next cycle @(posedge vif.clk); it.op2 = get_data(port); // wait for the response on this port (bounded - a non-responding DUT // is reported, not waited on forever); resp stays NONE on timeout so // the scoreboard flags the missing response wait_cnt = 0; do begin @(posedge vif.clk); wait_cnt++; end while (get_resp(port) === RESP_NONE && wait_cnt < RSP_TIMEOUT); it.resp = get_resp(port); it.result = get_out_data(port); num_collected++; `uvm_info(get_type_name(), $sformatf("Collected: %s", it.convert2string()), UVM_MEDIUM) if (coverage_enable) sample_coverage(it); item_collected_port.write(it); end endtask : collect_port function void sample_coverage(calc_seq_item it); cov_cmd = it.cmd; cov_port = it.port; cov_resp = it.resp; cov_op1 = it.op1; cov_op2 = it.op2; calc_cg.sample(); endfunction //--- per-port signal accessors ------------------------------------------- function bit [CMD_WIDTH-1:0] get_cmd(bit [1:0] port); case (port) 2'd0 : return vif.req1_cmd_in; 2'd1 : return vif.req2_cmd_in; 2'd2 : return vif.req3_cmd_in; 2'd3 : return vif.req4_cmd_in; endcase endfunction function bit [DATA_WIDTH-1:0] get_data(bit [1:0] port); case (port) 2'd0 : return vif.req1_data_in; 2'd1 : return vif.req2_data_in; 2'd2 : return vif.req3_data_in; 2'd3 : return vif.req4_data_in; endcase endfunction function bit [RESP_WIDTH-1:0] get_resp(bit [1:0] port); case (port) 2'd0 : return vif.out_resp1; 2'd1 : return vif.out_resp2; 2'd2 : return vif.out_resp3; 2'd3 : return vif.out_resp4; endcase endfunction function bit [DATA_WIDTH-1:0] get_out_data(bit [1:0] port); case (port) 2'd0 : return vif.out_data1; 2'd1 : return vif.out_data2; 2'd2 : return vif.out_data3; 2'd3 : return vif.out_data4; endcase endfunction task wait_reset_done(); while (vif.rst !== '0) @(posedge vif.clk); endtask : wait_reset_done function void report_phase(uvm_phase phase); `uvm_info(get_type_name(), $sformatf("Monitor collected %0d transactions, functional coverage = %0.2f%%", num_collected, calc_cg.get_coverage()), UVM_LOW) endfunction : report_phase endclass : calc_monitor `endif