1.DUT
module alu_top (
input wire clk_i,
input wire rst_i,
input wire [31:0] dataA_i,
input wire [31:0] dataB_i,
input wire [ 2:0] ALUCtrl_i, // Operation code
output logic [31:0] ALUResult_o,
output logic Zero_o
);
reg [31:0] temp_alu_result;
reg temp_zero;
always @(*) begin
if (rst_i == 1'b1) begin
temp_alu_result = 32'b0;
temp_zero = 1'b0;
end else begin
case (ALUCtrl_i)
3'b010: // ADD
begin
temp_alu_result = dataA_i + dataB_i;
end
3'b110: // SUB
begin
temp_alu_result = dataA_i - dataB_i;
end
3'b000: // AND
begin
temp_alu_result = dataA_i & dataB_i;
end
3'b001: // OR
begin
temp_alu_result = dataA_i | dataB_i;
end
3'b011: // XOR
begin
temp_alu_result = dataA_i ^ dataB_i;
end
3'b100: // NOR
begin
temp_alu_result = ~(dataA_i | dataB_i);
end
3'b111: // SLT
begin
if ((dataA_i - dataB_i) < 0)
temp_alu_result = 32'b1;
else
temp_alu_result = 32'b0;
end
default:
begin
temp_alu_result = 32'b0;
temp_zero = 1'b0;
end
endcase
// The zero signal is mainly used by the branch
if (temp_alu_result == 32'b0) begin
temp_zero = 1'b1;
end else begin
temp_zero = 1'b0;
end
end // NOT reset
end // always
always @(posedge clk_i or posedge rst_i) begin
if (rst_i == 1'b1) begin
ALUResult_o <= 32'b0;
Zero_o <= 1'b1;
end else begin
ALUResult_o <= temp_alu_result;
Zero_o <= temp_zero;
end
end // always
endmodule : alu_top
2.transaction
`ifndef ALU_BASE_SEQ_ITEM__SV
`define ALU_BASE_SEQ_ITEM__SV
class alu_base_seq_item extends uvm_sequence_item;
rand bit [31:0] dataA_i;
rand bit [31:0] dataB_i;
randc bit [ 2:0] ALUCtrl_i;
bit [31:0] ALUResult_o;
bit Zero_o;
rand int wait_cycles;
`uvm_object_utils_begin(alu_base_seq_item)
`uvm_field_int(dataA_i, UVM_DEFAULT)
`uvm_field_int(dataB_i, UVM_DEFAULT)
`uvm_field_int(ALUCtrl_i, UVM_DEFAULT)
`uvm_field_int(ALUResult_o, UVM_DEFAULT)
`uvm_field_int(Zero_o, UVM_DEFAULT)
`uvm_object_utils_end
//------------------------------------------
// Constraints
//------------------------------------------
constraint delay_loops {wait_cycles inside {[0:5]};}
constraint valid_alu_op {ALUCtrl_i inside {3'b000, 3'b001, 3'b010, 3'b011, 3'b100, 3'b110, 3'b111};}
function new (string name = "alu_base_seq_item");
super.new(name);
endfunction: new
endclass: alu_base_seq_item
`endif
3.sequence
`ifndef ALU_BASE_SEQ__SV
`define ALU_BASE_SEQ__SV
class alu_base_seq extends uvm_sequence#(alu_base_seq_item);
`uvm_object_utils(alu_base_seq)
// Each sequence has a random number of operations.
// There is a constraint to keep the number reasonable.
rand int unsigned num_of_ops;
constraint num_of_ops_con {num_of_ops inside {[16:24]};}
function new(string name = "alu_base_seq");
super.new(name);
endfunction: new
// This task generates a sequence of transactions.
task body();
alu_base_seq_item m_alu_base_seq_item_h;
repeat (num_of_ops) begin
m_alu_base_seq_item_h = alu_base_seq_item::type_id::create(.name("m_alu_base_seq_item_h"));
start_item(m_alu_base_seq_item_h);
if (!m_alu_base_seq_item_h.randomize()) begin
`uvm_error("m_alu_base_seq_item_h", "Randomize failed.");
end
finish_item(m_alu_base_seq_item_h);
end
endtask: body
endclass: alu_base_seq
`endif
4.sequencer
直接使用参数化的uvm_sequencer
5.interface
`ifndef ALU_IF__SV
`define ALU_IF__SV
interface alu_if (input bit clk);
// ALU signals
logic [31:0] dataA_i, dataB_i, ALUResult_o;
logic [2:0] ALUCtrl_i;
logic rst_i, Zero_o;
endinterface : alu_if
`endif
6.driver
`ifndef ALU_DRIVER__SV
`define ALU_DRIVER__SV
class alu_driver extends uvm_driver#(alu_base_seq_item);
`uvm_component_utils(alu_driver)
virtual alu_if m_alu_vif_h;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction: new
// In the build phase, the driver is connected to the virtual interface.
// The DUT is also connected to the same interface.
function void build_phase(uvm_phase phase);
super.build_phase(phase);
assert(uvm_config_db#(virtual alu_if)::get
(.cntxt(this), .inst_name(""), .field_name("alu_if"), .value(m_alu_vif_h)));
endfunction: build_phase
task run_phase(uvm_phase phase);
alu_base_seq_item m_req_h;
m_alu_vif_h.dataA_i <= 32'b0;
m_alu_vif_h.dataB_i <= 32'b0;
m_alu_vif_h.ALUCtrl_i <= 3'b0;
// Generate a reset sequence.
// FIXME: Move this to the pre-run phase.
m_alu_vif_h.rst_i <= 1'b1;
repeat (2) @(posedge m_alu_vif_h.clk);
m_alu_vif_h.rst_i <= 1'b0;
// Keep receiving the next transaction from the sequence until you run out.
// Drive each transaction on the interface as per the protocol.
// In the alu_agent, the seq_item_port of the alu_driver is hooked up to the
// seq_item_export port on the sequencer (alu_base_seq_item).
// The alu_driver accepts alu_transactions with the get_next_item function.
// Sample timing diagram
// ___ ___ ___
// CLK ___| |___| |___| |___
//
// INP _______########------------
//
// OUT -------------------#####----
forever begin
seq_item_port.get_next_item(m_req_h);
`uvm_info("DRIVER_START:", $sformatf("DATA:: dataA_i: %32x, dataB_i: %32x, ALU_OP: %3b", m_req_h.dataA_i, m_req_h.dataB_i, m_req_h.ALUCtrl_i), UVM_DEBUG)
@(negedge m_alu_vif_h.clk);
m_alu_vif_h.dataA_i = m_req_h.dataA_i;
m_alu_vif_h.dataB_i = m_req_h.dataB_i;
m_alu_vif_h.ALUCtrl_i = m_req_h.ALUCtrl_i;
@(negedge m_alu_vif_h.clk);
m_alu_vif_h.dataA_i = 32'bz;
m_alu_vif_h.dataB_i = 32'bz;
m_alu_vif_h.ALUCtrl_i = 3'bz;
`uvm_info("DRIVER_DONE:", $sformatf("DATA:: dataA_i: %32x, dataB_i: %32x, ALU_OP: %3b", m_alu_vif_h.dataA_i, m_alu_vif_h.dataB_i, m_alu_vif_h.ALUCtrl_i), UVM_DEBUG)
`uvm_info("DRIVER_WAIT:", $sformatf("Waiting for %1d cycles", m_req_h.wait_cycles), UVM_DEBUG)
repeat (m_req_h.wait_cycles) @(negedge m_alu_vif_h.clk);
seq_item_port.item_done();
end // forever
endtask: run_phase
endclass: alu_driver
`endif
7.monitor
`ifndef ALU_MONITOR__SV
`define ALU_MONITOR__SV
class alu_monitor extends uvm_monitor;
`uvm_component_utils(alu_monitor)
virtual alu_if m_alu_vif_h;
uvm_analysis_port #(alu_base_seq_item) m_analysis_port_h;
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
m_analysis_port_h = new("m_analysis_port_h", this);
endfunction: build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
assert(uvm_config_db#(virtual alu_if)::get
(.cntxt(this), .inst_name(""), .field_name("alu_if"), .value(m_alu_vif_h)));
endfunction: connect_phase
task run_phase(uvm_phase phase);
alu_base_seq_item m_req_h = alu_base_seq_item::type_id::create("monitor_m_req_h");
alu_base_seq_item m_req_clone_h;
@(negedge m_alu_vif_h.rst_i);
forever begin
@(posedge m_alu_vif_h.clk);
m_req_h.dataA_i = m_alu_vif_h.dataA_i;
m_req_h.dataB_i = m_alu_vif_h.dataB_i;
m_req_h.ALUCtrl_i = m_alu_vif_h.ALUCtrl_i;
`uvm_info("MONITOR:", $sformatf("DATA:: dataA_i: %32x, dataB_i: %32x, ALU_OP: %3b", m_req_h.dataA_i, m_req_h.dataB_i, m_req_h.ALUCtrl_i), UVM_DEBUG)
@(posedge m_alu_vif_h.clk);
m_req_h.ALUResult_o = m_alu_vif_h.ALUResult_o;
m_req_h.Zero_o = m_alu_vif_h.Zero_o;
`uvm_info("MONITOR:", $sformatf("RESULT:: ALUResult_o: %32x, Zero_o: %1x", m_req_h.ALUResult_o, m_req_h.Zero_o), UVM_DEBUG)
$cast(m_req_clone_h, m_req_h.clone());
m_analysis_port_h.write(m_req_clone_h);
end // forever
endtask: run_phase
endclass: alu_monitor
`endif
8.agent
`ifndef ALU_AGENT__SV
`define ALU_AGENT__SV
class alu_agent extends uvm_agent;
`uvm_component_utils(alu_agent)
uvm_analysis_port #(alu_base_seq_item) m_analysis_port_h;
uvm_sequencer #(alu_base_seq_item) m_sequencer_h;
alu_driver m_driver_h;
alu_monitor m_monitor_h;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction: new
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// Monitor should always be present.
m_monitor_h = alu_monitor::type_id::create("m_monitor_h", this);
// Analysis port should always be present.
m_analysis_port_h = new("m_analysis_port_h", this);
// Only build the driver and sequencer if agent is active.
m_driver_h = alu_driver::type_id::create("m_driver_h", this);
m_sequencer_h = uvm_sequencer#(alu_base_seq_item)::type_id::create("m_sequencer_h", this);
endfunction: build_phase
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
m_monitor_h.m_analysis_port_h.connect(m_analysis_port_h);
// Only connect the driver and the sequencer if agent is active.
m_driver_h.seq_item_port.connect(m_sequencer_h.seq_item_export);
endfunction: connect_phase
endclass: alu_agent
`endif
9.monitor
`ifndef SCOREBOARD__SV
`define SCOREBOARD__SV
class scoreboard extends uvm_component;
`uvm_component_utils(scoreboard)
uvm_tlm_analysis_fifo #(alu_base_seq_item) m_alu_fifo_h;
function new(string name = "scoreboard", uvm_component parent = null);
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase);
m_alu_fifo_h = new("m_alu_fifo_h", this);
endfunction: build_phase
task run_phase(uvm_phase phase);
alu_base_seq_item alu_op;
bit [31:0] expALUResult_o;
bit expZero_o;
forever begin
m_alu_fifo_h.get(alu_op);
`uvm_info("SCOREBOARD:", $sformatf("DATA:: alu_op.dataA_i: %32x, alu_op.dataB_i: %32x, ALU_OP: %3b", alu_op.dataA_i, alu_op.dataB_i, alu_op.ALUCtrl_i), UVM_NONE)
case (alu_op.ALUCtrl_i)
3'b010: // ADD
begin
expALUResult_o = alu_op.dataA_i + alu_op.dataB_i;
end
3'b110: // SUB
begin
expALUResult_o = alu_op.dataA_i - alu_op.dataB_i;
end
3'b000: // AND
begin
expALUResult_o = alu_op.dataA_i & alu_op.dataB_i;
end
3'b001: // OR
begin
expALUResult_o = alu_op.dataA_i | alu_op.dataB_i;
end
3'b011: // XOR
begin
expALUResult_o = alu_op.dataA_i ^ alu_op.dataB_i;
end
3'b100: // NOR
begin
expALUResult_o = ~(alu_op.dataA_i | alu_op.dataB_i);
end
3'b111: // SLT
begin
if ((alu_op.dataA_i - alu_op.dataB_i) < 0)
expALUResult_o = 32'b1;
else
expALUResult_o = 32'b0;
end
default:
begin
expALUResult_o = 32'b0;
expZero_o = 1'b0;
end
endcase
// The zero signal is mainly used by the branch
if (expALUResult_o == 32'b0) begin
expZero_o = 1'b1;
end else begin
expZero_o = 1'b0;
end
`uvm_info("SCOREBOARD:", $sformatf("DATA:: alu_op.ALUResult_o: %32x, expALUResult_o: %32x, alu_op.Zero_o: %1b, expZero_o: %1b", alu_op.ALUResult_o, expALUResult_o, alu_op.Zero_o, expZero_o), UVM_NONE)
if(alu_op.ALUResult_o == expALUResult_o && alu_op.Zero_o == expZero_o) begin
`uvm_info("SCOREBOARD:", $sformatf("CHECK_OK"), UVM_NONE)
end else begin
`uvm_error("SCOREBOARD:", $sformatf("CHECK_FAILED"))
end
end // forever
endtask: run_phase
endclass: scoreboard
`endif
10.env
`ifndef ENV__SV
`define ENV__SV
class alu_env extends uvm_env;
`uvm_component_utils(alu_env)
alu_agent m_alu_agent_h;
scoreboard m_scoreboard_h;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase);
m_alu_agent_h = alu_agent::type_id::create("m_alu_agent_h", this);
m_scoreboard_h = scoreboard::type_id::create("m_scoreboard_h", this);
endfunction: build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
m_alu_agent_h.m_analysis_port_h.connect(m_scoreboard_h.m_alu_fifo_h.analysis_export);
endfunction: connect_phase
endclass: alu_env
`endif
11.testcase
`ifndef ALU_BASE_TEST__SV
`define ALU_BASE_TEST__SV
class alu_base_test extends uvm_test;
`uvm_component_utils(alu_base_test)
alu_env m_alu_env_h;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
m_alu_env_h = alu_env::type_id::create(.name("m_alu_env_h"), .parent(this));
endfunction: build_phase
task run_phase(uvm_phase phase);
alu_base_seq m_alu_seq_h;
// Raise objection so the test will not end. Dropping it will end the test.
phase.raise_objection(.obj(this));
m_alu_seq_h = alu_base_seq::type_id::create(.name("m_alu_seq_h"));
assert(m_alu_seq_h.randomize());
`uvm_info("alu_base_test",{"\n", m_alu_seq_h.sprint()}, UVM_LOW)
// Set the generated sequence on the sequencer port of the agent.
// The driver will pick up a transaction each and driver it.
m_alu_seq_h.start(m_alu_env_h.m_alu_agent_h.m_sequencer_h);
#10ns;
phase.drop_objection(.obj(this));
endtask: run_phase
endclass: alu_base_test
`endif
12.testbench
`ifndef TB_TOP__SV
`define TB_TOP__SV
import uvm_pkg::*;
`include "uvm_macros.svh"
module top;
reg clk;
// Interface to connect the ALU agent with the DUT
alu_if m_alu_if_h (clk);
// DUT connections
alu_top DUT (
.clk_i(clk),
.rst_i(m_alu_if_h.rst_i),
.dataA_i(m_alu_if_h.dataA_i),
.dataB_i(m_alu_if_h.dataB_i),
.ALUCtrl_i(m_alu_if_h.ALUCtrl_i),
.ALUResult_o(m_alu_if_h.ALUResult_o),
.Zero_o(m_alu_if_h.Zero_o)
);
// Clock generator
initial begin
clk = 0;
#5ns;
forever #5ns clk = !clk;
end
// Set the interface to the DUT to be used by UVM TB.
initial begin
uvm_config_db#(virtual alu_if)::set(null, "*", "alu_if", m_alu_if_h);
run_test();
end
endmodule: top
`endif