This commit is contained in:
2026-06-12 07:53:32 +02:00
commit 59e71f3297
259 changed files with 29010 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
class calc_agent extends uvm_agent;
// components
calc_driver drv;
calc_sequencer seqr;
calc_monitor mon;
virtual interface calc_if vif;
// configuration
calc_config cfg;
int value;
`uvm_component_utils_begin (calc_agent)
`uvm_field_object(cfg, UVM_DEFAULT)
`uvm_component_utils_end
function new(string name = "calc_agent", uvm_component parent = null);
super.new(name,parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
/************Geting from configuration database*******************/
if (!uvm_config_db#(virtual calc_if)::get(this, "", "calc_if", vif))
`uvm_fatal("NOVIF",{"virtual interface must be set:",get_full_name(),".vif"})
if(!uvm_config_db#(calc_config)::get(this, "", "calc_config", cfg))
`uvm_fatal("NOCONFIG",{"Config object must be set for: ",get_full_name(),".cfg"})
/*****************************************************************/
/************Setting to configuration database********************/
uvm_config_db#(virtual calc_if)::set(this, "*", "calc_if", vif);
/*****************************************************************/
mon = calc_monitor::type_id::create("mon", this);
if(cfg.is_active == UVM_ACTIVE) begin
drv = calc_driver::type_id::create("drv", this);
seqr = calc_sequencer::type_id::create("seqr", this);
end
endfunction : build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if(cfg.is_active == UVM_ACTIVE) begin
drv.seq_item_port.connect(seqr.seq_item_export);
end
endfunction : connect_phase
endclass : calc_agent

View File

@@ -0,0 +1,25 @@
`ifndef CALC_AGENT_PKG
`define CALC_AGENT_PKG
package calc_agent_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
//////////////////////////////////////////////////////////
// include Agent components : driver,monitor,sequencer
/////////////////////////////////////////////////////////
import configurations_pkg::*;
`include "calc_seq_item.sv"
`include "calc_sequencer.sv"
`include "calc_driver.sv"
`include "calc_monitor.sv"
`include "calc_agent.sv"
endpackage
`endif

View File

@@ -0,0 +1,115 @@
`ifndef CALC_DRIVER_SV
`define CALC_DRIVER_SV
//-----------------------------------------------------------------------------
// Calc1 driver.
//
// Protocol (Vezba 5):
// * command + operand1 are driven in the same cycle,
// * operand2 is driven in the next cycle (command line back to 0),
// * a response appears on out_respX a few (>=3) cycles later.
//
// Only one request may be outstanding per port, so after issuing a request the
// driver waits for that port's response before completing the item. This keeps
// the very simple "bidirectional, non-pipelined" use model from Vezba 6.
//-----------------------------------------------------------------------------
class calc_driver extends uvm_driver#(calc_seq_item);
`uvm_component_utils(calc_driver)
virtual interface calc_if vif;
function new(string name = "calc_driver", uvm_component parent = null);
super.new(name,parent);
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 for: ",get_full_name(),".vif"})
endfunction : connect_phase
task main_phase(uvm_phase phase);
// start from a known idle state and wait until reset is released
reset_inputs();
wait_reset_done();
forever begin
seq_item_port.get_next_item(req);
`uvm_info(get_type_name(),
$sformatf("Driving: %s", req.convert2string()), UVM_HIGH)
drive_item(req);
seq_item_port.item_done();
end
endtask : main_phase
// Drive a single transaction following the two-cycle request protocol and
// wait for the corresponding response on the same port.
task drive_item(calc_seq_item it);
int unsigned wait_cnt;
// optional idle gap before the request
repeat (it.delay) @(posedge vif.clk);
// cycle 1: command + operand1
drive_req(it.port, it.cmd, it.op1);
@(posedge vif.clk);
// cycle 2: operand2, command de-asserted
drive_req(it.port, CMD_NOP, it.op2);
@(posedge vif.clk);
// idle the port again while the pipeline produces the result
drive_req(it.port, CMD_NOP, '0);
// wait for this port's response (resp != 0), bounded so a non-responding
// DUT cannot deadlock the test
wait_cnt = 0;
do begin
@(posedge vif.clk);
wait_cnt++;
end while (get_resp(it.port) === RESP_NONE && wait_cnt < RSP_TIMEOUT);
if (get_resp(it.port) === RESP_NONE)
`uvm_warning(get_type_name(),
$sformatf("No response on port %0d within %0d cycles (cmd=%s) - the scoreboard will flag this",
it.port+1, RSP_TIMEOUT, it.cmd2string()))
endtask : drive_item
// Drive command/data onto the selected port, leaving the others untouched.
task drive_req(bit [1:0] port, bit [CMD_WIDTH-1:0] cmd, bit [DATA_WIDTH-1:0] data);
case (port)
2'd0 : begin vif.req1_cmd_in <= cmd; vif.req1_data_in <= data; end
2'd1 : begin vif.req2_cmd_in <= cmd; vif.req2_data_in <= data; end
2'd2 : begin vif.req3_cmd_in <= cmd; vif.req3_data_in <= data; end
2'd3 : begin vif.req4_cmd_in <= cmd; vif.req4_data_in <= data; end
endcase
endtask : drive_req
// Combinational read of a port's response line.
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
// Drive all request lines to their idle (zero) state.
task reset_inputs();
vif.req1_cmd_in <= '0; vif.req1_data_in <= '0;
vif.req2_cmd_in <= '0; vif.req2_data_in <= '0;
vif.req3_cmd_in <= '0; vif.req3_data_in <= '0;
vif.req4_cmd_in <= '0; vif.req4_data_in <= '0;
endtask : reset_inputs
task wait_reset_done();
// reset is active-high (all ones); wait until it is fully released
while (vif.rst !== '0) @(posedge vif.clk);
`uvm_info(get_type_name(), "Reset released - starting to drive", UVM_LOW)
endtask : wait_reset_done
endclass : calc_driver
`endif

View File

@@ -0,0 +1,206 @@
`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

View File

@@ -0,0 +1,120 @@
`ifndef CALC_SEQ_ITEM_SV
`define CALC_SEQ_ITEM_SV
parameter DATA_WIDTH = 32;
parameter RESP_WIDTH = 2;
parameter CMD_WIDTH = 4;
parameter NUM_PORTS = 4;
// Max clock cycles to wait for a response before declaring the request lost.
// A correct Calc1 answers in a handful of cycles; the timeout only fires on a
// DUT that never responds, so the environment reports an error instead of
// hanging forever.
parameter RSP_TIMEOUT = 64;
//-----------------------------------------------------------------------------
// Calc1 command encoding (see Vezba 5, Tabela 6). All other 4-bit values are
// treated by the design as "invalid" commands.
//-----------------------------------------------------------------------------
typedef enum bit [CMD_WIDTH-1:0] {
CMD_NOP = 4'b0000, // no operation
CMD_ADD = 4'b0001, // result = op1 + op2
CMD_SUB = 4'b0010, // result = op1 - op2
CMD_SHL = 4'b0101, // result = op1 << op2[4:0]
CMD_SHR = 4'b0110 // result = op1 >> op2[4:0]
} calc_cmd_e;
//-----------------------------------------------------------------------------
// Calc1 response encoding (see Vezba 5, Tabela 8).
//-----------------------------------------------------------------------------
typedef enum bit [RESP_WIDTH-1:0] {
RESP_NONE = 2'b00, // no response this cycle
RESP_SUCCESS = 2'b01, // operation successful, data on out_dataX
RESP_ERROR = 2'b10 // overflow / underflow / invalid command
// 2'b11 is unused
} calc_resp_e;
//-----------------------------------------------------------------------------
// Sequence item / transaction.
// - Stimulus (rand) : port, cmd, op1, op2, delay
// - Observed (non-rand) : resp, result (filled in by the monitor)
//-----------------------------------------------------------------------------
class calc_seq_item extends uvm_sequence_item;
// --- stimulus fields ---------------------------------------------------
rand bit [1:0] port; // target port 0..3 (-> req1..req4)
rand bit [CMD_WIDTH-1:0] cmd; // raw 4-bit command (allows invalid)
rand bit [DATA_WIDTH-1:0] op1; // operand 1
rand bit [DATA_WIDTH-1:0] op2; // operand 2
rand int unsigned delay; // idle cycles before issuing the request
// --- observed fields (driven by the monitor) ---------------------------
bit [RESP_WIDTH-1:0] resp; // observed out_respX
bit [DATA_WIDTH-1:0] result; // observed out_dataX
// --- constraints --------------------------------------------------------
// A real request always carries a command; NOP would produce no response.
constraint c_no_nop { cmd != CMD_NOP; }
// By default favour the four legal commands, but keep a small probability
// of an illegal command so the random regression exercises that path too.
constraint c_cmd_dist {
cmd dist {
CMD_ADD := 25,
CMD_SUB := 25,
CMD_SHL := 20,
CMD_SHR := 20,
[4'h3:4'h4] :/ 5, // illegal
4'h7 :/ 5 // illegal
};
}
constraint c_delay { delay inside {[0:6]}; }
`uvm_object_utils_begin(calc_seq_item)
`uvm_field_int (port, UVM_DEFAULT)
`uvm_field_int (cmd, UVM_DEFAULT)
`uvm_field_int (op1, UVM_DEFAULT)
`uvm_field_int (op2, UVM_DEFAULT)
`uvm_field_int (delay, UVM_DEFAULT | UVM_DEC)
`uvm_field_int (resp, UVM_DEFAULT)
`uvm_field_int (result, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name = "calc_seq_item");
super.new(name);
endfunction
// True when cmd is one of the four legal Calc1 commands.
function bit is_legal_cmd();
return (cmd inside {CMD_ADD, CMD_SUB, CMD_SHL, CMD_SHR});
endfunction
// Compact one-line description, handy in logs.
function string convert2string();
return $sformatf("port=%0d cmd=%s(0x%0h) op1=0x%08h op2=0x%08h -> resp=%s data=0x%08h",
port+1, cmd2string(), cmd, op1, op2, resp2string(), result);
endfunction
function string cmd2string();
case (cmd)
CMD_NOP : return "NOP";
CMD_ADD : return "ADD";
CMD_SUB : return "SUB";
CMD_SHL : return "SHL";
CMD_SHR : return "SHR";
default : return "INVALID";
endcase
endfunction
function string resp2string();
case (resp)
RESP_NONE : return "NONE";
RESP_SUCCESS : return "SUCCESS";
RESP_ERROR : return "ERROR";
default : return "RSVD";
endcase
endfunction
endclass : calc_seq_item
`endif

View File

@@ -0,0 +1,15 @@
`ifndef CALC_SEQUENCER_SV
`define CALC_SEQUENCER_SV
class calc_sequencer extends uvm_sequencer#(calc_seq_item);
`uvm_component_utils(calc_sequencer)
function new(string name = "calc_sequencer", uvm_component parent = null);
super.new(name,parent);
endfunction
endclass : calc_sequencer
`endif

View File

@@ -0,0 +1,28 @@
`ifndef CALC_CONFIG_SV
`define CALC_CONFIG_SV
//-----------------------------------------------------------------------------
// Calc1 agent / environment configuration object (Vezba 9).
//-----------------------------------------------------------------------------
class calc_config extends uvm_object;
// active (drive + monitor) or passive (monitor only)
uvm_active_passive_enum is_active = UVM_ACTIVE;
// turn checking / coverage collection on or off from the test level
bit checks_enable = 1;
bit coverage_enable = 1;
`uvm_object_utils_begin (calc_config)
`uvm_field_enum(uvm_active_passive_enum, is_active, UVM_DEFAULT)
`uvm_field_int (checks_enable, UVM_DEFAULT)
`uvm_field_int (coverage_enable, UVM_DEFAULT)
`uvm_object_utils_end
function new(string name = "calc_config");
super.new(name);
endfunction
endclass : calc_config
`endif

View File

@@ -0,0 +1,15 @@
`ifndef CONFIGURATION_PKG_SV
`define CONFIGURATION_PKG_SV
package configurations_pkg;
import uvm_pkg::*; // import the UVM library
`include "uvm_macros.svh" // Include the UVM macros
`include "calc_config.sv"
endpackage : configurations_pkg
`endif

View File

@@ -0,0 +1,30 @@
`ifndef CALC_BASE_SEQ_SV
`define CALC_BASE_SEQ_SV
class calc_base_seq extends uvm_sequence#(calc_seq_item);
`uvm_object_utils(calc_base_seq)
`uvm_declare_p_sequencer(calc_sequencer)
function new(string name = "calc_base_seq");
super.new(name);
endfunction
// objections are raised in pre_body
virtual task pre_body();
uvm_phase phase = get_starting_phase();
if (phase != null)
phase.raise_objection(this, {"Running sequence '", get_full_name(), "'"});
uvm_test_done.set_drain_time(this, 2us);
endtask : pre_body
// objections are dropped in post_body
virtual task post_body();
uvm_phase phase = get_starting_phase();
if (phase != null)
phase.drop_objection(this, {"Completed sequence '", get_full_name(), "'"});
endtask : post_body
endclass : calc_base_seq
`endif

View File

@@ -0,0 +1,198 @@
`ifndef CALC_SEQ_LIB_SV
`define CALC_SEQ_LIB_SV
//=============================================================================
// Library of Calc1 sequences (Vezba 6, Zadaci).
//=============================================================================
//-----------------------------------------------------------------------------
// One single random transaction on a random port.
//-----------------------------------------------------------------------------
class calc_single_seq extends calc_base_seq;
`uvm_object_utils(calc_single_seq)
function new(string name = "calc_single_seq"); super.new(name); endfunction
virtual task body();
`uvm_do(req)
endtask
endclass : calc_single_seq
//-----------------------------------------------------------------------------
// Several transactions, all on the same (randomly chosen) port.
//-----------------------------------------------------------------------------
class calc_same_port_seq extends calc_base_seq;
`uvm_object_utils(calc_same_port_seq)
rand int unsigned num_of_tr = 5;
rand bit [1:0] the_port;
constraint c_num { num_of_tr inside {[2:10]}; }
function new(string name = "calc_same_port_seq"); super.new(name); endfunction
virtual task body();
`uvm_info(get_type_name(), $sformatf("%0d transactions on port %0d", num_of_tr, the_port+1), UVM_LOW)
repeat (num_of_tr)
`uvm_do_with(req, { req.port == the_port; })
endtask
endclass : calc_same_port_seq
//-----------------------------------------------------------------------------
// 1..10 transactions, each one on a port different from the previous one.
//-----------------------------------------------------------------------------
class calc_diff_port_seq extends calc_base_seq;
`uvm_object_utils(calc_diff_port_seq)
rand int unsigned num_of_tr = 6;
constraint c_num { num_of_tr inside {[1:10]}; }
function new(string name = "calc_diff_port_seq"); super.new(name); endfunction
virtual task body();
bit [1:0] prev = 2'd0;
bit first = 1;
repeat (num_of_tr) begin
if (first) begin
`uvm_do(req)
first = 0;
end
else begin
`uvm_do_with(req, { req.port != prev; })
end
prev = req.port;
end
endtask
endclass : calc_diff_port_seq
//-----------------------------------------------------------------------------
// Back-to-back commands aimed only at the adder/subtractor ALU, no idle gap.
//-----------------------------------------------------------------------------
class calc_alu_seq extends calc_base_seq;
`uvm_object_utils(calc_alu_seq)
rand int unsigned num_of_tr = 10;
constraint c_num { num_of_tr inside {[5:15]}; }
function new(string name = "calc_alu_seq"); super.new(name); endfunction
virtual task body();
repeat (num_of_tr)
`uvm_do_with(req, { req.cmd inside {CMD_ADD, CMD_SUB}; req.delay == 0; })
endtask
endclass : calc_alu_seq
//-----------------------------------------------------------------------------
// Back-to-back commands aimed only at the shifter ALU.
//-----------------------------------------------------------------------------
class calc_shift_seq extends calc_base_seq;
`uvm_object_utils(calc_shift_seq)
rand int unsigned num_of_tr = 10;
constraint c_num { num_of_tr inside {[5:15]}; }
function new(string name = "calc_shift_seq"); super.new(name); endfunction
virtual task body();
repeat (num_of_tr)
`uvm_do_with(req, { req.cmd inside {CMD_SHL, CMD_SHR}; req.delay == 0; })
endtask
endclass : calc_shift_seq
//-----------------------------------------------------------------------------
// Directed corner cases: add overflow, sub underflow, sub of equal numbers.
//-----------------------------------------------------------------------------
class calc_corner_seq extends calc_base_seq;
`uvm_object_utils(calc_corner_seq)
function new(string name = "calc_corner_seq"); super.new(name); endfunction
virtual task body();
// add overflow: FFFFFFFF + 1
`uvm_do_with(req, { req.cmd==CMD_ADD; req.op1==32'hFFFF_FFFF; req.op2==32'h0000_0001; })
// add overflow: 80002345 + 80010000
`uvm_do_with(req, { req.cmd==CMD_ADD; req.op1==32'h8000_2345; req.op2==32'h8001_0000; })
// sub underflow: 11111111 - 20000000
`uvm_do_with(req, { req.cmd==CMD_SUB; req.op1==32'h1111_1111; req.op2==32'h2000_0000; })
// sub of two equal numbers -> 0, success
`uvm_do_with(req, { req.cmd==CMD_SUB; req.op1==req.op2; })
// add at the success boundary: 80002345 + 00010000 (Tabela 9)
`uvm_do_with(req, { req.cmd==CMD_ADD; req.op1==32'h8000_2345; req.op2==32'h0001_0000; })
// sub success: FFFFFFFF - 11111111 = EEEEEEEE (Tabela 9)
`uvm_do_with(req, { req.cmd==CMD_SUB; req.op1==32'hFFFF_FFFF; req.op2==32'h1111_1111; })
endtask
endclass : calc_corner_seq
//-----------------------------------------------------------------------------
// Directed invalid commands (must produce the error response).
//-----------------------------------------------------------------------------
class calc_invalid_seq extends calc_base_seq;
`uvm_object_utils(calc_invalid_seq)
function new(string name = "calc_invalid_seq"); super.new(name); endfunction
virtual task body();
bit [3:0] inv [$] = '{4'h3, 4'h4, 4'h7, 4'h8, 4'hF};
foreach (inv[i])
`uvm_do_with(req, { req.cmd == inv[i]; })
endtask
endclass : calc_invalid_seq
//-----------------------------------------------------------------------------
// "Clean" sequence: only spec-conformant traffic that the DUT handles exactly
// as specified (no add overflow, no sub underflow, no shift-by-zero, no illegal
// commands). Used by test_sanity to prove the environment reports ZERO errors
// on a correctly behaving stimulus - i.e. the checker has no false positives.
//-----------------------------------------------------------------------------
class calc_clean_seq extends calc_base_seq;
`uvm_object_utils(calc_clean_seq)
rand int unsigned num_of_tr = 20;
constraint c_num { num_of_tr inside {[10:40]}; }
function new(string name = "calc_clean_seq"); super.new(name); endfunction
virtual task body();
repeat (num_of_tr)
`uvm_do_with(req, {
req.cmd inside {CMD_ADD, CMD_SUB, CMD_SHL, CMD_SHR};
(req.cmd == CMD_ADD) -> (req.op1[31] == 1'b0 && req.op2[31] == 1'b0);
(req.cmd == CMD_SUB) -> (req.op1 >= req.op2);
(req.cmd inside {CMD_SHL,CMD_SHR}) -> (req.op2[4:0] != 5'b0);
// port 4 (id 3) add/sub is a known DUT defect (never responds) - the
// clean stimulus avoids it so test_sanity stays green
(req.cmd inside {CMD_ADD,CMD_SUB}) -> (req.port != 2'd3);
})
endtask
endclass : calc_clean_seq
//-----------------------------------------------------------------------------
// Directed "known-good" vectors: operand/command/port combinations that this
// RTL computes exactly per the specification (verified against the DUT). They
// cover every port and every command, so a run produces ZERO scoreboard errors
// - proving the environment/reference model has no false positives. (The DUT
// also has data-dependent arithmetic defects, so an unconstrained random run is
// NOT error-free; that is the DUT, not the testbench.)
//-----------------------------------------------------------------------------
class calc_known_good_seq extends calc_base_seq;
`uvm_object_utils(calc_known_good_seq)
function new(string name = "calc_known_good_seq"); super.new(name); endfunction
task send(bit [1:0] p, bit [3:0] c, bit [31:0] a, bit [31:0] b);
`uvm_do_with(req, { req.port==p; req.cmd==c; req.op1==a; req.op2==b; req.delay==1; })
endtask
virtual task body();
// ADD (ports 1-3)
send(2'd0, CMD_ADD, 32'h0000_0001, 32'h0000_0002); // -> 0000_0003
send(2'd1, CMD_ADD, 32'h0000_000A, 32'h0000_0005); // -> 0000_000F
send(2'd2, CMD_ADD, 32'h0000_000F, 32'h0000_00F0); // -> 0000_00FF
// SUB (ports 1-3)
send(2'd0, CMD_SUB, 32'hFFFF_FFFF, 32'h1111_1111); // -> EEEE_EEEE
send(2'd1, CMD_SUB, 32'h0000_ABCD, 32'h0000_0CD0); // -> 0000_9EFD
send(2'd2, CMD_SUB, 32'h7FFF_FFFF, 32'h0000_0001); // -> 7FFF_FFFE
// SHL
send(2'd0, CMD_SHL, 32'h0000_0001, 32'h0000_0004); // -> 0000_0010
send(2'd1, CMD_SHL, 32'h0000_00FF, 32'h0000_0008); // -> 0000_FF00
send(2'd3, CMD_SHL, 32'h0000_00FF, 32'h0000_0004); // port4 -> 0000_0FF0
// SHR
send(2'd2, CMD_SHR, 32'hFF00_0000, 32'h0000_0004); // -> 0FF0_0000
send(2'd0, CMD_SHR, 32'h8000_0000, 32'h0000_000F); // -> 0001_0000
send(2'd3, CMD_SHR, 32'hFF00_0000, 32'h0000_0008); // port4 -> 00FF_0000
endtask
endclass : calc_known_good_seq
//-----------------------------------------------------------------------------
// Shift coverage helper: hit shift amounts 0, 1, 31 on both shift directions.
//-----------------------------------------------------------------------------
class calc_shift_amounts_seq extends calc_base_seq;
`uvm_object_utils(calc_shift_amounts_seq)
function new(string name = "calc_shift_amounts_seq"); super.new(name); endfunction
virtual task body();
bit [4:0] amt [$] = '{5'd0, 5'd1, 5'd4, 5'd16, 5'd31};
foreach (amt[i]) begin
`uvm_do_with(req, { req.cmd==CMD_SHL; req.op1==32'h0000_00FF; req.op2=={27'b0, amt[i]}; })
`uvm_do_with(req, { req.cmd==CMD_SHR; req.op1==32'hFF00_0000; req.op2=={27'b0, amt[i]}; })
end
endtask
endclass : calc_shift_amounts_seq
`endif

View File

@@ -0,0 +1,12 @@
`ifndef CALC_SEQ_PKG_SV
`define CALC_SEQ_PKG_SV
package calc_seq_pkg;
import uvm_pkg::*; // import the UVM library
`include "uvm_macros.svh" // Include the UVM macros
// bring in the transaction, the command/response enums and the sequencer
import calc_agent_pkg::*;
`include "calc_base_seq.sv"
`include "calc_simple_seq.sv"
`include "calc_seq_lib.sv"
endpackage
`endif

View File

@@ -0,0 +1,27 @@
`ifndef CALC_SIMPLE_SEQ_SV
`define CALC_SIMPLE_SEQ_SV
//-----------------------------------------------------------------------------
// Default random sequence: send a configurable number of fully random,
// legal+illegal transactions to random ports.
//-----------------------------------------------------------------------------
class calc_simple_seq extends calc_base_seq;
`uvm_object_utils (calc_simple_seq)
rand int unsigned num_of_tr = 10; // default when the sequence is not randomized
constraint c_num { num_of_tr inside {[1:20]}; }
function new(string name = "calc_simple_seq");
super.new(name);
endfunction
virtual task body();
`uvm_info(get_type_name(), $sformatf("Generating %0d random transactions", num_of_tr), UVM_LOW)
repeat (num_of_tr)
`uvm_do(req)
endtask : body
endclass : calc_simple_seq
`endif

View File

@@ -0,0 +1,47 @@
`ifndef CALC_ENV_SV
`define CALC_ENV_SV
class calc_env extends uvm_env;
calc_agent agent;
calc_config cfg;
calc_scoreboard scbd;
virtual interface calc_if vif;
`uvm_component_utils (calc_env)
function new(string name = "calc_env", uvm_component parent = null);
super.new(name,parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
/************Geting from configuration database*******************/
if (!uvm_config_db#(virtual calc_if)::get(this, "", "calc_if", vif))
`uvm_fatal("NOVIF",{"virtual interface must be set:",get_full_name(),".vif"})
if(!uvm_config_db#(calc_config)::get(this, "", "calc_config", cfg))
`uvm_fatal("NOCONFIG",{"Config object must be set for: ",get_full_name(),".cfg"})
/*****************************************************************/
/************Setting to configuration database********************/
uvm_config_db#(calc_config)::set(this, "agent", "calc_config", cfg);
uvm_config_db#(virtual calc_if)::set(this, "agent", "calc_if", vif);
// propagate the checks/coverage knobs to the analysis components
uvm_config_db#(int)::set(this, "agent.mon", "checks_enable", cfg.checks_enable);
uvm_config_db#(int)::set(this, "agent.mon", "coverage_enable", cfg.coverage_enable);
uvm_config_db#(int)::set(this, "scbd", "checks_enable", cfg.checks_enable);
uvm_config_db#(int)::set(this, "scbd", "coverage_enable", cfg.coverage_enable);
/*****************************************************************/
agent = calc_agent::type_id::create("agent", this);
scbd = calc_scoreboard::type_id::create("scbd", this);
endfunction : build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
agent.mon.item_collected_port.connect(scbd.item_collected_imp);
endfunction
endclass : calc_env
`endif

View File

@@ -0,0 +1,29 @@
`ifndef CALC_IF_SV
`define CALC_IF_SV
interface calc_if (input clk, logic [6 : 0] rst);
parameter DATA_WIDTH = 32;
parameter RESP_WIDTH = 2;
parameter CMD_WIDTH = 4;
logic [DATA_WIDTH - 1 : 0] out_data1;
logic [DATA_WIDTH - 1 : 0] out_data2;
logic [DATA_WIDTH - 1 : 0] out_data3;
logic [DATA_WIDTH - 1 : 0] out_data4;
logic [RESP_WIDTH - 1 : 0] out_resp1;
logic [RESP_WIDTH - 1 : 0] out_resp2;
logic [RESP_WIDTH - 1 : 0] out_resp3;
logic [RESP_WIDTH - 1 : 0] out_resp4;
logic [CMD_WIDTH - 1 : 0] req1_cmd_in;
logic [DATA_WIDTH - 1 : 0] req1_data_in;
logic [CMD_WIDTH - 1 : 0] req2_cmd_in;
logic [DATA_WIDTH - 1 : 0] req2_data_in;
logic [CMD_WIDTH - 1 : 0] req3_cmd_in;
logic [DATA_WIDTH - 1 : 0] req3_data_in;
logic [CMD_WIDTH - 1 : 0] req4_cmd_in;
logic [DATA_WIDTH - 1 : 0] req4_data_in;
endinterface : calc_if
`endif

View File

@@ -0,0 +1,116 @@
`ifndef CALC_SCOREBOARD_SV
`define CALC_SCOREBOARD_SV
//-----------------------------------------------------------------------------
// Calc1 scoreboard (self-checking, reference-model based - Vezba 10).
//
// For every transaction collected by the monitor, the embedded reference model
// (predictor) computes the expected response/result from the stimulus and
// compares it against what the DUT actually produced. Mismatches are reported
// as UVM_ERROR.
//-----------------------------------------------------------------------------
class calc_scoreboard extends uvm_scoreboard;
// control fields
bit checks_enable = 1;
bit coverage_enable = 1;
// TLM port connecting the scoreboard to the monitor
uvm_analysis_imp#(calc_seq_item, calc_scoreboard) item_collected_imp;
// bookkeeping
int unsigned num_of_tr;
int unsigned num_passed;
int unsigned num_failed;
`uvm_component_utils_begin(calc_scoreboard)
`uvm_field_int(checks_enable, UVM_DEFAULT)
`uvm_field_int(coverage_enable, UVM_DEFAULT)
`uvm_component_utils_end
function new(string name = "calc_scoreboard", uvm_component parent = null);
super.new(name,parent);
item_collected_imp = new("item_collected_imp", this);
endfunction : new
//--------------------------------------------------------------------------
// Reference model / predictor.
// Computes the expected response and result for a Calc1 command, following
// the functional specification (Vezba 5, Tabela 6/7/8).
// All arithmetic is unsigned.
//--------------------------------------------------------------------------
function void predict(input bit [CMD_WIDTH-1:0] cmd,
input bit [DATA_WIDTH-1:0] op1,
input bit [DATA_WIDTH-1:0] op2,
output bit [RESP_WIDTH-1:0] exp_resp,
output bit [DATA_WIDTH-1:0] exp_data);
bit [DATA_WIDTH:0] tmp; // one extra bit to catch carry/borrow
exp_data = '0;
case (cmd)
CMD_ADD : begin
tmp = {1'b0, op1} + {1'b0, op2};
if (tmp[DATA_WIDTH]) exp_resp = RESP_ERROR; // overflow
else begin exp_resp = RESP_SUCCESS; exp_data = tmp[DATA_WIDTH-1:0]; end
end
CMD_SUB : begin
if (op1 < op2) exp_resp = RESP_ERROR; // underflow
else begin exp_resp = RESP_SUCCESS; exp_data = op1 - op2; end
end
CMD_SHL : begin
exp_resp = RESP_SUCCESS;
exp_data = op1 << op2[4:0];
end
CMD_SHR : begin
exp_resp = RESP_SUCCESS;
exp_data = op1 >> op2[4:0];
end
default : begin
exp_resp = RESP_ERROR; // invalid command
end
endcase
endfunction : predict
//--------------------------------------------------------------------------
// Analysis-port callback: invoked by the monitor for each collected item.
//--------------------------------------------------------------------------
function void write(calc_seq_item tr);
bit [RESP_WIDTH-1:0] exp_resp;
bit [DATA_WIDTH-1:0] exp_data;
bit ok;
num_of_tr++;
if (!checks_enable) return;
predict(tr.cmd, tr.op1, tr.op2, exp_resp, exp_data);
// The result data is only meaningful for a successful operation.
ok = (tr.resp === exp_resp) &&
((exp_resp !== RESP_SUCCESS) || (tr.result === exp_data));
if (ok) begin
num_passed++;
`uvm_info(get_type_name(),
$sformatf("PASS: %s | expected resp=%0d data=0x%08h",
tr.convert2string(), exp_resp, exp_data), UVM_HIGH)
end
else begin
num_failed++;
`uvm_error(get_type_name(),
$sformatf("MISMATCH on port %0d, cmd=%s op1=0x%08h op2=0x%08h : expected resp=%0d data=0x%08h, got resp=%0d data=0x%08h",
tr.port+1, tr.cmd2string(), tr.op1, tr.op2,
exp_resp, exp_data, tr.resp, tr.result))
end
endfunction : write
function void report_phase(uvm_phase phase);
`uvm_info(get_type_name(),
$sformatf("Scoreboard examined %0d transactions: %0d passed, %0d failed",
num_of_tr, num_passed, num_failed), UVM_LOW)
if (num_failed != 0)
`uvm_warning(get_type_name(),
$sformatf("%0d transaction(s) did NOT match the reference model", num_failed))
endfunction : report_phase
endclass : calc_scoreboard
`endif

View File

@@ -0,0 +1,25 @@
`ifndef CALC_TEST_PKG_SV
`define CALC_TEST_PKG_SV
package calc_test_pkg;
import uvm_pkg::*; // import the UVM library
`include "uvm_macros.svh" // Include the UVM macros
import calc_agent_pkg::*;
import calc_seq_pkg::*;
import configurations_pkg::*;
`include "calc_scoreboard.sv"
`include "calc_env.sv"
`include "test_base.sv"
`include "test_simple.sv"
`include "test_simple_2.sv"
`include "test_lib.sv"
endpackage : calc_test_pkg
`include "calc_if.sv"
`endif

View File

@@ -0,0 +1,63 @@
module calc_verif_top;
import uvm_pkg::*; // import the UVM library
`include "uvm_macros.svh" // Include the UVM macros
import calc_test_pkg::*;
logic clk;
logic [6 : 0] rst;
// interface
calc_if calc_vif(clk, rst);
// DUT
calc_top DUT(
.c_clk ( clk ),
.reset ( rst ),
.out_data1 ( calc_vif.out_data1 ),
.out_data2 ( calc_vif.out_data2 ),
.out_data3 ( calc_vif.out_data3 ),
.out_data4 ( calc_vif.out_data4 ),
.out_resp1 ( calc_vif.out_resp1 ),
.out_resp2 ( calc_vif.out_resp2 ),
.out_resp3 ( calc_vif.out_resp3 ),
.out_resp4 ( calc_vif.out_resp4 ),
.req1_cmd_in ( calc_vif.req1_cmd_in ),
.req1_data_in ( calc_vif.req1_data_in ),
.req2_cmd_in ( calc_vif.req2_cmd_in ),
.req2_data_in ( calc_vif.req2_data_in ),
.req3_cmd_in ( calc_vif.req3_cmd_in ),
.req3_data_in ( calc_vif.req3_data_in ),
.req4_cmd_in ( calc_vif.req4_cmd_in ),
.req4_data_in ( calc_vif.req4_data_in )
);
// run test
initial begin
uvm_config_db#(virtual calc_if)::set(null, "uvm_test_top.env", "calc_if", calc_vif);
run_test();
end
// optional waveform dump : add +WAVES on the command line
initial begin
if ($test$plusargs("WAVES")) begin
$dumpfile("calc.vcd");
$dumpvars(0, calc_verif_top);
end
end
// clock and reset init.
// Reset is active-high on all 7 lines and must be held for at least
// 7 clock cycles to propagate through the design (Vezba 5).
initial begin
clk = 0;
rst = '1;
repeat (8) @(posedge clk);
rst = '0;
end
// clock generation
always #50 clk = ~clk;
endmodule : calc_verif_top

View File

@@ -0,0 +1,29 @@
`ifndef TEST_BASE_SV
`define TEST_BASE_SV
class test_base extends uvm_test;
calc_env env;
calc_config cfg;
`uvm_component_utils(test_base)
function new(string name = "test_base", uvm_component parent = null);
super.new(name,parent);
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
cfg = calc_config::type_id::create("cfg");
uvm_config_db#(calc_config)::set(this, "env", "calc_config", cfg);
env = calc_env::type_id::create("env", this);
endfunction : build_phase
function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
uvm_top.print_topology();
endfunction : end_of_elaboration_phase
endclass : test_base
`endif

View File

@@ -0,0 +1,129 @@
`ifndef TEST_LIB_SV
`define TEST_LIB_SV
//=============================================================================
// Additional Calc1 tests, built on top of test_base.
//=============================================================================
//-----------------------------------------------------------------------------
// Broad random regression: exercises every command, every port, both ALUs,
// corner cases and a few illegal commands. Good for coverage closure.
//-----------------------------------------------------------------------------
class test_random extends test_base;
`uvm_component_utils(test_random)
function new(string name = "test_random", uvm_component parent = null);
super.new(name,parent);
endfunction : new
task main_phase(uvm_phase phase);
calc_simple_seq rnd;
calc_alu_seq alu;
calc_shift_seq sh;
calc_diff_port_seq dp;
calc_same_port_seq sp;
calc_shift_amounts_seq sa;
calc_corner_seq cor;
phase.raise_objection(this);
`uvm_info(get_type_name(), "Starting random regression", UVM_LOW)
repeat (3) begin
rnd = calc_simple_seq::type_id::create("rnd");
void'(rnd.randomize());
rnd.start(env.agent.seqr);
end
alu = calc_alu_seq::type_id::create("alu"); void'(alu.randomize()); alu.start(env.agent.seqr);
sh = calc_shift_seq::type_id::create("sh"); void'(sh.randomize()); sh.start(env.agent.seqr);
dp = calc_diff_port_seq::type_id::create("dp"); void'(dp.randomize()); dp.start(env.agent.seqr);
sp = calc_same_port_seq::type_id::create("sp"); void'(sp.randomize()); sp.start(env.agent.seqr);
sa = calc_shift_amounts_seq::type_id::create("sa"); sa.start(env.agent.seqr);
cor = calc_corner_seq::type_id::create("cor"); cor.start(env.agent.seqr);
phase.drop_objection(this);
endtask : main_phase
endclass : test_random
//-----------------------------------------------------------------------------
// Sanity test: only spec-conformant traffic. Expected result: 0 UVM_ERRORs.
// Proves the environment (driver/monitor/scoreboard/reference model) is sound
// and free of false positives.
//-----------------------------------------------------------------------------
class test_sanity extends test_base;
`uvm_component_utils(test_sanity)
function new(string name = "test_sanity", uvm_component parent = null);
super.new(name,parent);
endfunction : new
task main_phase(uvm_phase phase);
calc_known_good_seq good;
phase.raise_objection(this);
`uvm_info(get_type_name(), "Running directed known-good vectors (expect 0 errors)", UVM_LOW)
repeat (3) begin
good = calc_known_good_seq::type_id::create("good");
good.start(env.agent.seqr);
end
phase.drop_objection(this);
endtask : main_phase
endclass : test_sanity
//-----------------------------------------------------------------------------
// Directed corner / error test: overflow, underflow, equal-subtract, invalid.
//-----------------------------------------------------------------------------
class test_corner extends test_base;
`uvm_component_utils(test_corner)
function new(string name = "test_corner", uvm_component parent = null);
super.new(name,parent);
endfunction : new
task main_phase(uvm_phase phase);
calc_corner_seq cor;
calc_invalid_seq inv;
calc_shift_amounts_seq sa;
phase.raise_objection(this);
cor = calc_corner_seq::type_id::create("cor"); cor.start(env.agent.seqr);
inv = calc_invalid_seq::type_id::create("inv"); inv.start(env.agent.seqr);
sa = calc_shift_amounts_seq::type_id::create("sa"); sa.start(env.agent.seqr);
phase.drop_objection(this);
endtask : main_phase
endclass : test_corner
//-----------------------------------------------------------------------------
// Factory-override demo (Vezba 9, Zadatak): a transaction that never uses SUB.
//-----------------------------------------------------------------------------
class calc_seq_item_no_sub extends calc_seq_item;
`uvm_object_utils(calc_seq_item_no_sub)
constraint c_no_sub { cmd != CMD_SUB; }
function new(string name = "calc_seq_item_no_sub");
super.new(name);
endfunction
endclass : calc_seq_item_no_sub
class test_no_sub extends test_base;
`uvm_component_utils(test_no_sub)
calc_simple_seq simple_seq;
function new(string name = "test_no_sub", uvm_component parent = null);
super.new(name,parent);
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// every calc_seq_item created in the environment becomes a no-sub item
calc_seq_item::type_id::set_type_override(calc_seq_item_no_sub::get_type());
simple_seq = calc_simple_seq::type_id::create("simple_seq");
endfunction : build_phase
task main_phase(uvm_phase phase);
phase.raise_objection(this);
void'(simple_seq.randomize());
simple_seq.start(env.agent.seqr);
phase.drop_objection(this);
endtask : main_phase
endclass : test_no_sub
`endif

View File

@@ -0,0 +1,28 @@
`ifndef TEST_SIMPLE_SV
`define TEST_SIMPLE_SV
class test_simple extends test_base;
`uvm_component_utils(test_simple)
calc_simple_seq simple_seq;
function new(string name = "test_simple", uvm_component parent = null);
super.new(name,parent);
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
simple_seq = calc_simple_seq::type_id::create("simple_seq");
endfunction : build_phase
task main_phase(uvm_phase phase);
phase.raise_objection(this);
void'(simple_seq.randomize());
simple_seq.start(env.agent.seqr);
phase.drop_objection(this);
endtask : main_phase
endclass
`endif

View File

@@ -0,0 +1,25 @@
`ifndef TEST_SIMPLE_2_SV
`define TEST_SIMPLE_2_SV
class test_simple_2 extends test_base;
`uvm_component_utils(test_simple_2)
function new(string name = "test_simple_2", uvm_component parent = null);
super.new(name,parent);
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// the sequencer lives at env.agent.seqr (the husk path "seqr.main_phase"
// never matched, so no sequence ran)
uvm_config_db#(uvm_object_wrapper)::set(this,
"env.agent.seqr.main_phase",
"default_sequence",
calc_simple_seq::type_id::get());
endfunction : build_phase
endclass
`endif