Add UVM testbench

This adds a UVM testbench and associated tooling for Ibex. 
The tooling requires Synopsys VCS to run.
This commit is contained in:
taoliug 2019-06-03 08:45:00 -07:00 committed by Philipp Wagner
parent effa61c684
commit 2782ae9677
31 changed files with 1421 additions and 0 deletions

67
dv/uvm/Makefile Normal file
View file

@ -0,0 +1,67 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
DV_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
GEN_DIR := $(realpath ${DV_DIR}/../../vendor/google_riscv-dv)
TOOLCHAIN := ${RISCV_TOOLCHAIN}
OUT := "${DV_DIR}/out"
# Run time options for the instruction generator
GEN_OPTS :=
# Run time options for ibex RTL simulation
SIM_OPTS :=
# Enable waveform dumping
WAVES := 1
WAVE_CMP_OPTS := -debug_access+all -ucli -do vcs.tcl
# Enable coverage dump
COV := 0
ifeq (${WAVES}, 0)
WAVE_CMP_OPTS=
endif
SHELL=/bin/bash
export PRJ_DIR:= $(realpath ${DV_DIR}/../../..)
rand:=$(shell awk 'BEGIN{srand();printf("%d", 65536*rand())}')
.PHONY: rtl_sim clean iss_sim
all: clean gen iss_sim compile rtl_sim post_compare
clean:
rm -rf ${OUT}
# Generate random instructions
gen:
mkdir -p ${OUT}
cd ${GEN_DIR}; ./run -o ${OUT}/instr_gen ${GEN_OPTS};
# ISS simulation
iss_sim:
cd ${GEN_DIR}; \
./iss_sim -dir ${OUT}/instr_gen -toolchain ${TOOLCHAIN} -isa rv32imc -abi ilp32;
# Compile ibex core TB
compile:
mkdir -p ${OUT}/rtl_sim
vcs -f ibex_dv.f -full64 \
-l ${OUT}/rtl_sim/compile.log \
-sverilog -ntb_opts uvm-1.2 \
+define+UVM_REGEX_NO_DPI -timescale=1ns/10ps -licqueue \
-Mdir=${OUT}/rtl_sim/vcs_simv.csrc \
-o ${OUT}/rtl_sim/vcs_simv \
+define+BOOT_ADDR=32\'h8000_0000 \
+define+TRACE_EXECUTION \
-debug_access+pp \
${WAVE_CMP_OPTS} \
-lca -kdb
# Run ibex RTL simulation with random instructions
rtl_sim:
./sim ${SIM_OPTS} -dir ${OUT} -waves ${WAVES}
# Compare the regression result between ISS and RTL sim
post_compare:
./compare ${OUT}

View file

@ -0,0 +1,18 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
interface ibex_mem_intf#(ADDR_WIDTH = 32, DATA_WIDTH = 32);
logic clock;
logic reset;
logic request;
logic grant;
logic [ADDR_WIDTH-1:0] addr;
logic we;
logic [DATA_WIDTH/8-1:0] be;
logic rvalid;
logic [DATA_WIDTH-1:0] wdata;
logic [DATA_WIDTH-1:0] rdata;
endinterface : ibex_mem_intf

View file

@ -0,0 +1,30 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
`include "ibex_mem_intf.sv"
package ibex_mem_intf_agent_pkg;
import uvm_pkg::*;
import mem_model_pkg::*;
parameter DATA_WIDTH = 32;
parameter ADDR_WIDTH = 32;
typedef enum { READ, WRITE } rw_e;
`include "uvm_macros.svh"
`include "ibex_mem_intf_seq_item.sv"
typedef uvm_sequencer#(ibex_mem_intf_seq_item) ibex_mem_intf_master_sequencer;
`include "ibex_mem_intf_monitor.sv"
`include "ibex_mem_intf_slave_driver.sv"
`include "ibex_mem_intf_slave_sequencer.sv"
`include "ibex_mem_intf_slave_seq_lib.sv"
`include "ibex_mem_intf_slave_agent.sv"
`include "ibex_mem_intf_master_driver.sv"
`include "ibex_mem_intf_master_agent.sv"
endpackage

View file

@ -0,0 +1,34 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//------------------------------------------------------------------------------
// CLASS: ibex_mem_intf_master_agent
//------------------------------------------------------------------------------
class ibex_mem_intf_master_agent extends uvm_agent;
ibex_mem_intf_master_driver driver;
ibex_mem_intf_master_sequencer sequencer;
ibex_mem_intf_monitor monitor;
`uvm_component_utils(ibex_mem_intf_master_agent)
`uvm_component_new
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
monitor = ibex_mem_intf_monitor::type_id::create("monitor", this);
if(get_is_active() == UVM_ACTIVE) begin
driver = ibex_mem_intf_master_driver::type_id::create("driver", this);
sequencer = ibex_mem_intf_master_sequencer::type_id::create("sequencer", this);
end
endfunction : build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if(get_is_active() == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect_phase
endclass : ibex_mem_intf_master_agent

View file

@ -0,0 +1,88 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//------------------------------------------------------------------------------
// CLASS: ibex_mem_intf_master_driver
//------------------------------------------------------------------------------
class ibex_mem_intf_master_driver extends uvm_driver #(ibex_mem_intf_seq_item);
protected virtual ibex_mem_intf vif;
`uvm_component_utils(ibex_mem_intf_master_driver)
`uvm_component_new
mailbox #(ibex_mem_intf_seq_item) rdata_queue;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
rdata_queue = new();
if(!uvm_config_db#(virtual ibex_mem_intf)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
endfunction: build_phase
virtual task run_phase(uvm_phase phase);
fork
get_and_drive();
reset_signals();
collect_response();
join
endtask : run_phase
virtual protected task get_and_drive();
@(negedge vif.reset);
forever begin
@(posedge vif.clock);
seq_item_port.get_next_item(req);
repeat(req.req_delay) @(posedge vif.clock);
$cast(rsp, req.clone());
rsp.set_id_info(req);
drive_transfer(rsp);
seq_item_port.item_done();
end
endtask : get_and_drive
virtual protected task reset_signals();
forever begin
@(posedge vif.reset);
vif.request <= 'h0;
vif.addr <= 'hz;
vif.wdata <= 'hz;
vif.be <= 'bz;
vif.we <= 'bz;
end
endtask : reset_signals
virtual protected task drive_transfer (ibex_mem_intf_seq_item trans);
if (trans.req_delay > 0) begin
repeat(trans.req_delay) @(posedge vif.clock);
end
vif.request <= 1'b1;
vif.addr <= trans.addr;
vif.be <= trans.be;
vif.we <= trans.read_write;
vif.wdata <= trans.data;
wait(vif.grant === 1'b1);
@(posedge vif.clock);
vif.request <= 'h0;
vif.addr <= 'hz;
vif.wdata <= 'hz;
vif.be <= 'bz;
vif.we <= 'bz;
rdata_queue.put(trans);
endtask : drive_transfer
virtual protected task collect_response();
ibex_mem_intf_seq_item tr;
forever begin
rdata_queue.get(tr);
@(posedge vif.clock);
while(vif.rvalid !== 1'b1) @(posedge vif.clock);
if(tr.read_write == READ)
tr.data = vif.rdata;
seq_item_port.put_response(tr);
end
endtask : collect_response
endclass : ibex_mem_intf_master_driver

View file

@ -0,0 +1,75 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//------------------------------------------------------------------------------
// CLASS: ibex_mem_intf_monitor
//------------------------------------------------------------------------------
class ibex_mem_intf_monitor extends uvm_monitor;
protected virtual ibex_mem_intf vif;
mailbox #(ibex_mem_intf_seq_item) collect_data_queue;
uvm_analysis_port#(ibex_mem_intf_seq_item) item_collected_port;
uvm_analysis_port#(ibex_mem_intf_seq_item) addr_ph_port;
`uvm_component_utils(ibex_mem_intf_monitor)
`uvm_component_new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
item_collected_port = new("item_collected_port", this);
addr_ph_port = new("addr_ph_port_monitor", this);
collect_data_queue = new();
if(!uvm_config_db#(virtual ibex_mem_intf)::get(this, "", "vif", vif)) begin
`uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
end
endfunction: build_phase
virtual task run_phase(uvm_phase phase);
wait(vif.reset === 1'b0);
fork
collect_address_phase();
collect_data_phase();
join
endtask : run_phase
virtual protected task collect_address_phase();
ibex_mem_intf_seq_item trans_collected;
forever begin
trans_collected = ibex_mem_intf_seq_item::type_id::create("trans_collected");
while(!(vif.request && vif.grant)) @(posedge vif.clock);
trans_collected.addr = vif.addr;
trans_collected.be = vif.be;
`uvm_info(get_full_name(), $sformatf("Detect request with address: %0x",
trans_collected.addr), UVM_HIGH)
if(vif.we) begin
trans_collected.read_write = WRITE;
trans_collected.data = vif.wdata;
end else begin
trans_collected.read_write = READ;
end
addr_ph_port.write(trans_collected);
`uvm_info(get_full_name(),"Send through addr_ph_port", UVM_HIGH)
if(trans_collected.read_write == WRITE)
item_collected_port.write(trans_collected);
else
collect_data_queue.put(trans_collected);
@(posedge vif.clock);
end
endtask : collect_address_phase
virtual protected task collect_data_phase();
ibex_mem_intf_seq_item trans_collected;
forever begin
collect_data_queue.get(trans_collected);
do
@(posedge vif.clock);
while(vif.rvalid === 0);
trans_collected.data = vif.rdata;
item_collected_port.write(trans_collected);
end
endtask : collect_data_phase
endclass : ibex_mem_intf_monitor

View file

@ -0,0 +1,30 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//------------------------------------------------------------------------------
// CLASS: ibex_mem_intf_seq_item
//------------------------------------------------------------------------------
class ibex_mem_intf_seq_item extends uvm_sequence_item;
rand bit [ADDR_WIDTH-1:0] addr;
rand rw_e read_write;
rand bit [DATA_WIDTH-1:0] data;
rand bit [DATA_WIDTH/8-1:0] be;
rand bit [3:0] gnt_delay;
rand bit [3:0] req_delay;
rand bit [5:0] rvalid_delay;
`uvm_object_utils_begin(ibex_mem_intf_seq_item)
`uvm_field_int (addr, UVM_DEFAULT)
`uvm_field_enum (rw_e, read_write, UVM_DEFAULT)
`uvm_field_int (be, UVM_DEFAULT)
`uvm_field_int (data, UVM_DEFAULT)
`uvm_field_int (gnt_delay, UVM_DEFAULT)
`uvm_field_int (rvalid_delay, UVM_DEFAULT)
`uvm_object_utils_end
`uvm_object_new
endclass : ibex_mem_intf_seq_item

View file

@ -0,0 +1,35 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//------------------------------------------------------------------------------
// CLASS: ibex_mem_intf_slave_agent
//------------------------------------------------------------------------------
class ibex_mem_intf_slave_agent extends uvm_agent;
ibex_mem_intf_slave_driver driver;
ibex_mem_intf_slave_sequencer sequencer;
ibex_mem_intf_monitor monitor;
`uvm_component_utils(ibex_mem_intf_slave_agent)
`uvm_component_new
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
monitor = ibex_mem_intf_monitor::type_id::create("monitor", this);
if(get_is_active() == UVM_ACTIVE) begin
driver = ibex_mem_intf_slave_driver::type_id::create("driver", this);
sequencer = ibex_mem_intf_slave_sequencer::type_id::create("sequencer", this);
end
endfunction : build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if(get_is_active() == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
monitor.addr_ph_port.connect(sequencer.addr_ph_port.analysis_export);
end
endfunction : connect_phase
endclass : ibex_mem_intf_slave_agent

View file

@ -0,0 +1,99 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//------------------------------------------------------------------------------
// CLASS: ibex_mem_intf_slave_driver
//------------------------------------------------------------------------------
class ibex_mem_intf_slave_driver extends uvm_driver #(ibex_mem_intf_seq_item);
protected virtual ibex_mem_intf vif;
int unsigned min_grant_delay = 0;
int unsigned max_grant_delay = 10;
`uvm_component_utils(ibex_mem_intf_slave_driver)
`uvm_component_new
mailbox #(ibex_mem_intf_seq_item) grant_queue;
mailbox #(ibex_mem_intf_seq_item) rdata_queue;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
grant_queue = new();
rdata_queue = new();
if(!uvm_config_db#(virtual ibex_mem_intf)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
endfunction: build_phase
virtual task run_phase(uvm_phase phase);
reset_signals();
fork
send_grant();
join_none
get_and_drive();
endtask : run_phase
virtual protected task reset_signals();
vif.rvalid <= 1'b0;
vif.grant <= 1'b0;
vif.rdata <= 'b0;
endtask : reset_signals
virtual protected task get_and_drive();
@(negedge vif.reset);
fork
forever begin
ibex_mem_intf_seq_item req, req_c;
@(posedge vif.clock);
seq_item_port.get_next_item(req);
$cast(req_c, req.clone());
if(~vif.reset) begin
rdata_queue.put(req_c);
end
seq_item_port.item_done();
end
send_read_data();
join_none
endtask : get_and_drive
virtual protected task send_grant();
int gnt_delay;
forever begin
while(vif.request !== 1'b1) begin
@(negedge vif.clock);
end
std::randomize(gnt_delay) with {
gnt_delay dist {
min_grant_delay :/ 1,
[min_grant_delay+1 : max_grant_delay-1] :/ 1,
max_grant_delay :/ 1
};
};
repeat(gnt_delay) @(negedge vif.clock);
if(~vif.reset) begin
vif.grant = 1'b1;
@(negedge vif.clock);
vif.grant = 1'b0;
end
end
endtask : send_grant
virtual protected task send_read_data();
ibex_mem_intf_seq_item tr;
forever begin
@(posedge vif.clock);
vif.rvalid <= 1'b0;
vif.rdata <= 'x;
rdata_queue.get(tr);
if(vif.reset) continue;
repeat(tr.rvalid_delay) @(posedge vif.clock);
if(~vif.reset) begin
vif.rvalid <= 1'b1;
vif.rdata <= tr.data;
end
end
endtask : send_read_data
endclass : ibex_mem_intf_slave_driver

View file

@ -0,0 +1,59 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//------------------------------------------------------------------------------
// SEQUENCE: ibex_mem_intf_slave_seq
//------------------------------------------------------------------------------
class ibex_mem_intf_slave_seq extends uvm_sequence #(ibex_mem_intf_seq_item);
int max_rvalid_delay = 20;
int min_rvalid_delay = 0;
ibex_mem_intf_seq_item item;
mem_model m_mem;
`uvm_object_utils(ibex_mem_intf_slave_seq)
`uvm_declare_p_sequencer(ibex_mem_intf_slave_sequencer)
`uvm_object_new
virtual task body();
if(m_mem == null)
`uvm_fatal(get_full_name(), "Cannot get memory model")
forever
begin
bit [ADDR_WIDTH-1:0] aligned_addr;
p_sequencer.addr_ph_port.get(item);
req = ibex_mem_intf_seq_item::type_id::create("req");
req.randomize() with {
addr == item.addr;
read_write == item.read_write;
data == item.data;
be == item.be;
rvalid_delay dist {
min_rvalid_delay :/ 5,
[min_rvalid_delay+1 : max_rvalid_delay/2-1] :/ 3,
[max_rvalid_delay/2 : max_rvalid_delay-1] :/ 1,
max_rvalid_delay :/ 1
};
};
aligned_addr = {req.addr[DATA_WIDTH-1:2], 2'b0};
if(req.read_write == READ) begin : READ_block
req.data = m_mem.read(aligned_addr);
end
`uvm_info(get_full_name(), $sformatf("Response transfer:\n%0s", req.sprint()), UVM_HIGH)
start_item(req);
finish_item(req);
if(item.read_write == WRITE) begin : WRITE_block
bit [DATA_WIDTH-1:0] data;
data = req.data;
for(int i = 0; i < DATA_WIDTH/8; i++) begin
if(req.be[i])
m_mem.write_byte(aligned_addr + i, data[7:0]);
data = data >> 8;
end
end
end
endtask : body
endclass : ibex_mem_intf_slave_seq

View file

@ -0,0 +1,21 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//------------------------------------------------------------------------------
// CLASS: ibex_mem_intf_slave_sequencer
//------------------------------------------------------------------------------
class ibex_mem_intf_slave_sequencer extends uvm_sequencer #(ibex_mem_intf_seq_item);
// TLM port to peek the address phase from the slave monitor
uvm_tlm_analysis_fifo #(ibex_mem_intf_seq_item) addr_ph_port;
`uvm_component_utils(ibex_mem_intf_slave_sequencer)
function new (string name, uvm_component parent);
super.new(name, parent);
addr_ph_port = new("addr_ph_port_sequencer", this);
endfunction : new
endclass : ibex_mem_intf_slave_sequencer

View file

@ -0,0 +1,56 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class mem_model#(Addr_width = 32, Data_width = 32) extends uvm_object;
typedef bit [Addr_width-1:0] mem_addr_t;
typedef bit [Data_width-1:0] mem_data_t;
bit [7:0] system_memory[mem_addr_t];
`uvm_object_param_utils(mem_model#(Addr_width, Data_width))
function new(string name="");
super.new(name);
endfunction
function bit [7:0] read_byte(mem_addr_t addr);
bit [7:0] data;
if(system_memory.exists(addr)) begin
data = system_memory[addr];
`uvm_info(get_full_name(),
$sformatf("Read Mem : Addr[0x%0h], Data[0x%0h]", addr, data), UVM_HIGH)
end
else begin
std::randomize(data);
`uvm_error(get_full_name(), $sformatf("read to uninitialzed addr 0x%0h", addr))
end
return data;
endfunction
function void write_byte(mem_addr_t addr, bit[7:0] data);
`uvm_info(get_full_name(),
$sformatf("Write Mem : Addr[0x%0h], Data[0x%0h]", addr, data), UVM_HIGH)
system_memory[addr] = data;
endfunction
function void write(input mem_addr_t addr, mem_data_t data);
bit [7:0] byte_data;
for(int i=0; i<Data_width/8; i++) begin
byte_data = data[7:0];
write_byte(addr+i, byte_data);
data = data >> 8;
end
endfunction
function mem_data_t read(mem_addr_t addr);
mem_data_t data;
for(int i=Data_width/8-1; i>=0; i--) begin
data = data << 8;
data[7:0] = read_byte(addr+i);
end
return data;
endfunction
endclass

View file

@ -0,0 +1,12 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
package mem_model_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "mem_model.sv"
endpackage

View file

@ -0,0 +1,29 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//----------------------------- DESCRIPTION ------------------------------------
//
// Generic clock interface for clock events in various utilities
//
//------------------------------------------------------------------------------
interface clk_if(input logic clk);
clocking cb @(posedge clk);
endclocking
clocking cbn @(negedge clk);
endclocking
// Wait for 'n' clocks based of postive clock edge
task wait_clks(int num_clks);
repeat (num_clks) @cb;
endtask
// Wait for 'n' clocks based of negative clock edge
task wait_n_clks(int num_clks);
repeat (num_clks) @cbn;
endtask
endinterface

View file

@ -0,0 +1,232 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// UVM speficic macros
`ifndef gfn
`define gfn get_full_name()
`endif
`ifndef gtn
`define gtn get_type_name()
`endif
`ifndef gn
`define gn get_name()
`endif
`ifndef gmv
`define gmv(csr) csr.get_mirrored_value()
`endif
// cast base class obj holding extended class handle to extended class handle;
// throw error if cast fails
`ifndef downcast
`define downcast(EXT_, BASE_, MSG_="", SEV_=fatal, ID_=`gfn) \
if (!$cast(EXT_, BASE_)) begin \
`uvm_``SEV_(ID_, $sformatf({"Cast failed: base class variable %0s ", \
"does not hold extended class %0s handle %s"}, \
`"BASE_`", `"EXT_`", MSG_)) \
end
`endif
// Note, UVM provides a macro `uvm_new_func -- which only applies to uvm_components
`ifndef uvm_object_new
`define uvm_object_new \
function new (string name=""); \
super.new(name); \
endfunction : new
`endif
`ifndef uvm_create_obj
`define uvm_create_obj(_type_name_, _inst_name_) \
_inst_name_ = _type_name_::type_id::create(`"_inst_name_`");
`endif
`ifndef uvm_component_new
`define uvm_component_new \
function new (string name="", uvm_component parent=null); \
super.new(name, parent); \
endfunction : new
`endif
`ifndef uvm_create_comp
`define uvm_create_comp(_type_name_, _inst_name_) \
_inst_name_ = _type_name_::type_id::create(`"_inst_name_`", this);
`endif
// Common check macros used by DV_CHECK error and fatal macros.
// Note: Should not be called by user code
`ifndef DV_CHECK
`define DV_CHECK(T_, MSG_="", SEV_=error, ID_=`gfn) \
if (!(T_)) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed (%s) %s ", `"T_`", MSG_)) \
end
`endif
`ifndef DV_CHECK_EQ
`define DV_CHECK_EQ(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
if (!(ACT_ == EXP_)) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s == %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end
`endif
`ifndef DV_CHECK_NE
`define DV_CHECK_NE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
if (!(ACT_ != EXP_)) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s != %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end
`endif
`ifndef DV_CHECK_LT
`define DV_CHECK_LT(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
if (!(ACT_ < EXP_)) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s < %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end
`endif
`ifndef DV_CHECK_GT
`define DV_CHECK_GT(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
if (!(ACT_ > EXP_)) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s > %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end
`endif
`ifndef DV_CHECK_LE
`define DV_CHECK_LE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
if (!(ACT_ <= EXP_)) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s <= %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end
`endif
`ifndef DV_CHECK_GE
`define DV_CHECK_GE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
if (!(ACT_ >= EXP_)) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s >= %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end
`endif
// Fatal version of the checks
`ifndef DV_CHECK_FATAL
`define DV_CHECK_FATAL(T_, MSG_="", ID_=`gfn) \
`DV_CHECK(T_, MSG_, fatal, ID_)
`endif
`ifndef DV_CHECK_EQ_FATAL
`define DV_CHECK_EQ_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
`DV_CHECK_EQ(ACT_, EXP_, MSG_, fatal, ID_)
`endif
`ifndef DV_CHECK_NE_FATAL
`define DV_CHECK_NE_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
`DV_CHECK_NE(ACT_, EXP_, MSG_, fatal, ID_)
`endif
`ifndef DV_CHECK_LT_FATAL
`define DV_CHECK_LT_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
`DV_CHECK_LT(ACT_, EXP_, MSG_, fatal, ID_)
`endif
`ifndef DV_CHECK_GT_FATAL
`define DV_CHECK_GT_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
`DV_CHECK_GT(ACT_, EXP_, MSG_, fatal, ID_)
`endif
`ifndef DV_CHECK_LE_FATAL
`define DV_CHECK_LE_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
`DV_CHECK_LE(ACT_, EXP_, MSG_, fatal, ID_)
`endif
`ifndef DV_CHECK_GE_FATAL
`define DV_CHECK_GE_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
`DV_CHECK_GE(ACT_, EXP_, MSG_, fatal, ID_)
`endif
// Shorthand for common foo.randomize() + fatal check
`ifndef DV_CHECK_RANDOMIZE_FATAL
`define DV_CHECK_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
`DV_CHECK_FATAL(VAR_.randomize(), MSG_, ID_)
`endif
// Shorthand for common foo.randomize() with { } + fatal check
`ifndef DV_CHECK_RANDOMIZE_WITH_FATAL
`define DV_CHECK_RANDOMIZE_WITH_FATAL(VAR_, WITH_C_, MSG_="Randomization failed!", ID_=`gfn) \
`DV_CHECK_FATAL(VAR_.randomize() with {WITH_C_}, MSG_, ID_)
`endif
// Shorthand for common std::randomize(foo) + fatal check
`ifndef DV_CHECK_STD_RANDOMIZE_FATAL
`define DV_CHECK_STD_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
`DV_CHECK_FATAL(std::randomize(VAR_), MSG_, ID_)
`endif
// Shorthand for common std::randomize(foo) with { } + fatal check
`ifndef DV_CHECK_STD_RANDOMIZE_WITH_FATAL
`define DV_CHECK_STD_RANDOMIZE_WITH_FATAL(VAR_, WITH_C_,MSG_="Randomization failed!",ID_=`gfn) \
`DV_CHECK_FATAL(std::randomize(VAR_) with {WITH_C_}, MSG_, ID_)
`endif
// Shorthand for common this.randomize(foo) + fatal check
`ifndef DV_CHECK_MEMBER_RANDOMIZE_FATAL
`define DV_CHECK_MEMBER_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
`DV_CHECK_FATAL(this.randomize(VAR_), MSG_, ID_)
`endif
// Shorthand for common this.randomize(foo) with { } + fatal check
`ifndef DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL
`define DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL(VAR_, C_, MSG_="Randomization failed!", ID_=`gfn) \
`DV_CHECK_FATAL(this.randomize(VAR_) with {C_}, MSG_, ID_)
`endif
// print non-empty tlm fifos that were uncompared at end of test
`ifndef DV_EOT_PRINT_TLM_FIFO_CONTENTS
`define DV_EOT_PRINT_TLM_FIFO_CONTENTS(TYP_, FIFO_, SEV_=error, ID_=`gfn) \
while (!FIFO_.is_empty()) begin \
TYP_ item; \
void'(FIFO_.try_get(item)); \
`uvm_``SEV_(ID_, $sformatf("%s item uncompared:\n%s", `"FIFO_`", item.sprint())) \
end
`endif
// print non-empty tlm fifos that were uncompared at end of test
`ifndef DV_EOT_PRINT_TLM_FIFO_ARR_CONTENTS
`define DV_EOT_PRINT_TLM_FIFO_ARR_CONTENTS(TYP_, FIFO_, SEV_=error, ID_=`gfn) \
foreach (FIFO_[i]) begin \
while (!FIFO_[i].is_empty()) begin \
TYP_ item; \
void'(FIFO_[i].try_get(item)); \
`uvm_``SEV_(ID_, $sformatf("%s[%0d] item uncompared:\n%s", `"FIFO_`", i, item.sprint())) \
end \
end
`endif
// print non-empty tlm fifos that were uncompared at end of test
`ifndef DV_EOT_PRINT_Q_CONTENTS
`define DV_EOT_PRINT_Q_CONTENTS(TYP_, Q_, SEV_=error, ID_=`gfn) \
while (Q_.size() != 0) begin \
TYP_ item = Q_.pop_front(); \
`uvm_``SEV_(ID_, $sformatf("%s item uncompared:\n%s", `"Q_`", item.sprint())) \
end
`endif
// print non-empty tlm fifos that were uncompared at end of test
`ifndef DV_EOT_PRINT_Q_ARR_CONTENTS
`define DV_EOT_PRINT_Q_ARR_CONTENTS(TYP_, Q_, SEV_=error, ID_=`gfn) \
foreach (Q_[i]) begin \
while (Q_[i].size() != 0) begin \
TYP_ item = Q[i].pop_front(); \
`uvm_``SEV_(ID_, $sformatf("%s[%0d] item uncompared:\n%s", `"Q_`", i, item.sprint())) \
end \
end
`endif
// get parity - implemented as a macro so that it can be invoked in constraints as well
`ifndef get_parity
`define get_parity(val, odd=0) (^val ^ odd)
`endif

View file

@ -0,0 +1,17 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
package dv_utils_pkg;
// dep packages
import uvm_pkg::*;
// macro includes
`include "dv_macros.svh"
`include "uvm_macros.svh"
// types & variables
typedef bit [31:0] uint;
typedef bit [63:0] uint64;
endpackage

57
dv/uvm/compare Executable file
View file

@ -0,0 +1,57 @@
#!/bin/bash
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
RUN_DIR="$1"
report_file="$1/regr.log"
rm -rf "$report_file"
script_path="../../vendor/google_riscv-dv"
compare_log () {
spike_log="$1"
ibex_log="$2"
# -----------------------------------------------------------------------------
# Convert spike log to standard instruction trace csv
# -----------------------------------------------------------------------------
# Remove all the init spike boot instructions
# 0xffffffff80000080 is the first user instruction
sed -i '/0xffffffff80000080/,$!d' "$spike_log"
# Remove all instructions after ecall (end of program excecution)
sed -i '/ecall/q' "$spike_log"
# Convert the spike log to riscv_instr_trace.proto format
spike_csv=$(echo "$spike_log" | sed 's/\.log/.csv/g')
python $script_path/scripts/spike_log_to_trace_csv.py \
--log $spike_log --csv $spike_csv >> $report_file
# -----------------------------------------------------------------------------
# Convert ibex log to standard instruction trace csv
# -----------------------------------------------------------------------------
# Remove all instructions after ecall (end of program excecution)
sed -i '/ecall/q' "$ibex_log"
# Convert the spike log to riscv_instr_trace.proto format
ibex_csv=$(echo "$ibex_log" | sed 's/\.log/.csv/g')
python $script_path/scripts/ibex_log_to_trace_csv.py \
--log $ibex_log --csv $ibex_csv >> $report_file
# -----------------------------------------------------------------------------
# Compare the trace log
# -----------------------------------------------------------------------------
python $script_path/scripts/instr_trace_compare.py $spike_csv $ibex_csv \
"spike" "ibex" >> $report_file
}
echo "compare simulation result under $RUN_DIR"
while read asm_test; do
SRC=$(echo "$asm_test" | sed 's/^.*\///g' | sed 's/\.S>*$//g')
echo "Test: $asm_test" >> $report_file
compare_log $RUN_DIR/instr_gen/spike_sim/$SRC.S.o.log \
$RUN_DIR/rtl_sim/$SRC/trace_core_00_0.log
done <"$RUN_DIR/asm_test_list"
passed_cnt="$(grep -c PASS $report_file)"
failed_cnt="$(grep -c FAIL $report_file)"
echo "$passed_cnt tests PASSED, $failed_cnt tests FAILED" >> $report_file
cat $report_file

10
dv/uvm/env/core_ibex_dut_probe_if.sv vendored Normal file
View file

@ -0,0 +1,10 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Interface to probe DUT internal signal
interface core_ibex_dut_probe_if(input logic clk);
logic illegal_instr;
logic ecall;
logic fetch_enable;
endinterface

33
dv/uvm/env/core_ibex_env.sv vendored Normal file
View file

@ -0,0 +1,33 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// ---------------------------------------------
// ibex processor core environment class
// ---------------------------------------------
class core_ibex_env extends uvm_env;
ibex_mem_intf_slave_agent data_if_slave_agent;
ibex_mem_intf_slave_agent instr_if_slave_agent;
core_ibex_vseqr vseqr;
`uvm_component_utils(core_ibex_env)
`uvm_component_new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
data_if_slave_agent = ibex_mem_intf_slave_agent::type_id::
create("data_if_slave_agent", this);
instr_if_slave_agent = ibex_mem_intf_slave_agent::type_id::
create("instr_if_slave_agent", this);
// Create virtual sequencer
vseqr = core_ibex_vseqr::type_id::create("vseqr", this);
endfunction : build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
vseqr.data_if_seqr = data_if_slave_agent.sequencer;
vseqr.instr_if_seqr = instr_if_slave_agent.sequencer;
endfunction : connect_phase
endclass

19
dv/uvm/env/core_ibex_env_pkg.sv vendored Normal file
View file

@ -0,0 +1,19 @@
// Copyright 2018 lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// ---------------------------------------------
// Core ibex environment package
// ---------------------------------------------
`include "core_ibex_dut_probe_if.sv"
package core_ibex_env_pkg;
import uvm_pkg::*;
import ibex_mem_intf_agent_pkg::*;
`include "core_ibex_vseqr.sv"
`include "core_ibex_env.sv"
endpackage

16
dv/uvm/env/core_ibex_vseqr.sv vendored Normal file
View file

@ -0,0 +1,16 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// ---------------------------------------------
// Core ibex environment virtual sequencer
// ---------------------------------------------
class core_ibex_vseqr extends uvm_sequencer;
ibex_mem_intf_slave_sequencer data_if_seqr;
ibex_mem_intf_slave_sequencer instr_if_seqr;
`uvm_component_utils(core_ibex_vseqr)
`uvm_component_new
endclass

41
dv/uvm/ibex_dv.f Normal file
View file

@ -0,0 +1,41 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
${PRJ_DIR}/ibex/dv/uvm/tb/prim_clock_gating.sv
// ibex CORE RTL files
+incdir+${PRJ_DIR}/ibex/rtl
${PRJ_DIR}/ibex/rtl/ibex_defines.sv
${PRJ_DIR}/ibex/rtl/ibex_tracer_defines.sv
${PRJ_DIR}/ibex/rtl/ibex_tracer.sv
${PRJ_DIR}/ibex/rtl/ibex_alu.sv
${PRJ_DIR}/ibex/rtl/ibex_compressed_decoder.sv
${PRJ_DIR}/ibex/rtl/ibex_controller.sv
${PRJ_DIR}/ibex/rtl/ibex_cs_registers.sv
${PRJ_DIR}/ibex/rtl/ibex_decoder.sv
${PRJ_DIR}/ibex/rtl/ibex_int_controller.sv
${PRJ_DIR}/ibex/rtl/ibex_ex_block.sv
${PRJ_DIR}/ibex/rtl/ibex_id_stage.sv
${PRJ_DIR}/ibex/rtl/ibex_if_stage.sv
${PRJ_DIR}/ibex/rtl/ibex_load_store_unit.sv
${PRJ_DIR}/ibex/rtl/ibex_multdiv_slow.sv
${PRJ_DIR}/ibex/rtl/ibex_multdiv_fast.sv
${PRJ_DIR}/ibex/rtl/ibex_prefetch_buffer.sv
${PRJ_DIR}/ibex/rtl/ibex_fetch_fifo.sv
${PRJ_DIR}/ibex/rtl/ibex_register_file_ff.sv
${PRJ_DIR}/ibex/rtl/ibex_core.sv
// Core DV files
+incdir+${PRJ_DIR}/ibex/dv/uvm/env
+incdir+${PRJ_DIR}/ibex/dv/uvm/tests
+incdir+${PRJ_DIR}/ibex/dv/uvm/common/ibex_mem_intf_agent
+incdir+${PRJ_DIR}/ibex/dv/uvm/common/mem_model
+incdir+${PRJ_DIR}/ibex/dv/uvm/common/utils
${PRJ_DIR}/ibex/dv/uvm/common/utils/clk_if.sv
${PRJ_DIR}/ibex/dv/uvm/common/utils/dv_utils_pkg.sv
${PRJ_DIR}/ibex/dv/uvm/common/mem_model/mem_model_pkg.sv
${PRJ_DIR}/ibex/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_agent_pkg.sv
${PRJ_DIR}/ibex/dv/uvm/env/core_ibex_env_pkg.sv
${PRJ_DIR}/ibex/dv/uvm/tests/core_ibex_test_pkg.sv
${PRJ_DIR}/ibex/dv/uvm/tb/core_ibex_tb_top.sv

79
dv/uvm/sim Executable file
View file

@ -0,0 +1,79 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
# Script to run the assembly tests generated by the riscv-dv instruction generator.
# Test directory
RUN_DIR="./"
# Assembly test file name
TEST=""
# Seed
RAND_SEED=1
SEED=""
# Wavform dump options
WAVES=0
WAVES_OPTS=""
# Process command line options
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-dir)
RUN_DIR="$2"
shift
;;
-test)
TEST="$2"
shift
;;
-waves)
WAVES="$2"
shift
;;
-seed)
SEED="$2"
RAND_SEED=0
shift
;;
*)
echo "unknown option $1"
return
;;
esac
shift
done
# If the test is specified through "-test" option, run a single test rather
# than all tests under RUN_DIR.
if [[ $TEST == "" ]]; then
find "$RUN_DIR" -name "*.S" > "$RUN_DIR/asm_test_list"
else
echo "$TEST" > "$RUN_DIR/asm_test_list"
fi
OUT="$RUN_DIR/rtl_sim"
# Run each test
while read asm_test; do
SRC=$(echo "$asm_test" | sed 's/^.*\///g' | sed 's/\.S>*$//g')
BINFILE="$asm_test.bin"
mkdir -p $OUT/$SRC
cd $OUT/$SRC
if [[ $RAND_SEED == 1 ]]; then
SEED=$RANDOM
fi
if [[ $WAVES == 1 ]]; then
WAVES_OPTS="-ucli -do $RUN_DIR/../vcs.tcl"
fi
CMD="$OUT/vcs_simv +UVM_TESTNAME=core_ibex_base_test \
${WAVES_OPTS} +ntb_random_seed=${SEED} \
+bin=$BINFILE -l sim.log"
echo "Running simulation for : $CMD"
$CMD
done <"$RUN_DIR/asm_test_list"

View file

@ -0,0 +1,87 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
module core_ibex_tb_top;
import uvm_pkg::*;
logic clk;
logic rst_n;
logic fetch_enable;
clk_if ibex_clk_if(.clk(clk));
// TODO(taliu) Resolve the tied-off ports
ibex_core dut(
.clk_i(clk),
.rst_ni(rst_n),
.test_en_i(1'b1),
.core_id_i('0),
.cluster_id_i('0),
.boot_addr_i(`BOOT_ADDR), // align with spike boot address
.irq_i('0),
.irq_id_i('0),
.debug_req_i('0),
.fetch_enable_i(fetch_enable),
.ext_perf_counters_i('0)
);
ibex_mem_intf data_mem_vif();
ibex_mem_intf instr_mem_vif();
initial begin
//data load/store vif connection
force data_mem_vif.clock = clk;
force data_mem_vif.reset = ~rst_n;
force data_mem_vif.request = dut.data_req_o;
force dut.data_gnt_i = data_mem_vif.grant;
force dut.data_rvalid_i = data_mem_vif.rvalid;
force data_mem_vif.we = dut.data_we_o;
force data_mem_vif.be = dut.data_be_o;
force data_mem_vif.addr = dut.data_addr_o;
force data_mem_vif.wdata = dut.data_wdata_o;
force dut.data_rdata_i = data_mem_vif.rdata;
force dut.data_err_i = 0; // TODO(taliu) Support interface error
//instruction fetch vif connnection
force instr_mem_vif.clock = clk;
force instr_mem_vif.reset = ~rst_n;
force instr_mem_vif.request = dut.instr_req_o;
force dut.instr_gnt_i = instr_mem_vif.grant;
force instr_mem_vif.we = 0;
force instr_mem_vif.be = 0;
force instr_mem_vif.wdata = 0;
force dut.instr_rvalid_i = instr_mem_vif.rvalid;
force instr_mem_vif.addr = dut.instr_addr_o;
force dut.instr_rdata_i = instr_mem_vif.rdata;
end
// DUT probe interface
core_ibex_dut_probe_if dut_if(.clk(clk));
assign dut_if.ecall = dut.id_stage_i.ecall_insn_dec;
assign fetch_enable = dut_if.fetch_enable;
initial begin
uvm_config_db#(virtual clk_if)::set(null, "*", "clk_if", ibex_clk_if);
uvm_config_db#(virtual core_ibex_dut_probe_if)::set(null, "*", "dut_if", dut_if);
uvm_config_db#(virtual ibex_mem_intf)::set(null, "*data_if_slave*", "vif", data_mem_vif);
uvm_config_db#(virtual ibex_mem_intf)::set(null, "*instr_if_slave*", "vif", instr_mem_vif);
run_test();
end
// Generate clk
initial begin
clk = 1'b0;
forever begin
#10 clk = ~clk;
end
end
// Generate reset
initial begin
rst_n = 1'b0;
repeat(100) @(posedge clk);
rst_n = 1'b1;
end
endmodule

View file

@ -0,0 +1,18 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Dummy clock gating module
module prim_clock_gating #(
parameter Impl = "default"
) (
input clk_i,
input en_i,
input test_en_i,
output logic clk_o
);
assign clk_o = en_i ? clk_i : 0;
endmodule

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,84 @@
class core_ibex_base_test extends uvm_test;
core_ibex_env env;
virtual clk_if clk_vif;
virtual core_ibex_dut_probe_if dut_vif;
mem_model_pkg::mem_model mem;
core_ibex_vseq vseq;
int unsigned timeout_in_cycles = 1000000;
`uvm_component_utils(core_ibex_base_test)
function new(string name="", uvm_component parent=null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
$value$plusargs("timeout_in_cycles=%0d", timeout_in_cycles);
if (!uvm_config_db#(virtual clk_if)::get(null, "", "clk_if", clk_vif)) begin
`uvm_fatal(get_full_name(), "Cannot get clk_if")
end
if (!uvm_config_db#(virtual core_ibex_dut_probe_if)::get(null, "", "dut_if", dut_vif)) begin
`uvm_fatal(get_full_name(), "Cannot get dut_if")
end
env = core_ibex_env::type_id::create("env", this);
mem = mem_model_pkg::mem_model#()::type_id::create("mem");
// Create virtual sequence and assign memory handle
vseq = core_ibex_vseq::type_id::create("vseq");
vseq.mem = mem;
endfunction
virtual task run_phase(uvm_phase phase);
phase.raise_objection(this);
dut_vif.fetch_enable = 1'b0;
clk_vif.wait_clks(100);
load_binary_to_mem();
dut_vif.fetch_enable = 1'b1;
vseq.start(env.vseqr);
wait_for_test_done();
phase.drop_objection(this);
endtask
function void load_binary_to_mem();
string bin;
bit [7:0] r8;
bit [31:0] addr = `BOOT_ADDR;
int f_bin;
void'($value$plusargs("bin=%0s", bin));
if (bin == "")
`uvm_fatal(get_full_name(), "Please specify test binary by +bin=binary_name")
`uvm_info(get_full_name(), $sformatf("Running test : %0s", bin), UVM_LOW)
f_bin = $fopen(bin,"rb");
if (!f_bin)
`uvm_fatal(get_full_name(), $sformatf("Cannot open file %0s", bin))
while ($fread(r8,f_bin)) begin
`uvm_info(`gfn, $sformatf("Init mem [0x%h] = 0x%0h", addr, r8), UVM_FULL)
mem.write(addr, r8);
addr++;
end
endfunction
virtual task wait_for_test_done();
// TODO(taliu): We need a consistent approach to determine the test is completed for both
// random instruction test and firmware based test. For example, it could be done by writing to
// a specific memory location of the test signature. Right now the random instruction generator
// use ecall instruction to indicate the end of the program. It could be changed to align with
// firmware test completion mechanism.
fork
begin
wait (dut_vif.ecall === 1'b1);
`uvm_info(`gfn, "ECALL instruction is detected, test done", UVM_LOW)
// De-assert fetch enable to finish the test
dut_vif.fetch_enable = 1'b0;
// Wait some time for the remaining instruction to finish
clk_vif.wait_clks(100);
end
begin
clk_vif.wait_clks(timeout_in_cycles);
`uvm_fatal(`gfn, "TEST TIMEOUT!!")
end
join_any
endtask
endclass

View file

@ -0,0 +1,17 @@
// Copyright 2018 lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// ---------------------------------------------
// Core ibex test package
// ---------------------------------------------
package core_ibex_test_pkg;
import uvm_pkg::*;
import core_ibex_env_pkg::*;
import ibex_mem_intf_agent_pkg::*;
`include "core_ibex_vseq.sv"
`include "core_ibex_base_test.sv"
endpackage

View file

@ -0,0 +1,30 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// ---------------------------------------------
// Ibex core virtual sequence
// ---------------------------------------------
class core_ibex_vseq extends uvm_sequence;
ibex_mem_intf_slave_seq instr_intf_seq;
ibex_mem_intf_slave_seq data_intf_seq;
mem_model_pkg::mem_model mem;
`uvm_object_utils(core_ibex_vseq)
`uvm_declare_p_sequencer(core_ibex_vseqr)
`uvm_object_new
virtual task body();
instr_intf_seq = ibex_mem_intf_slave_seq::type_id::create("instr_intf_seq");
data_intf_seq = ibex_mem_intf_slave_seq::type_id::create("data_intf_seq");
instr_intf_seq.m_mem = mem;
data_intf_seq.m_mem = mem;
fork
instr_intf_seq.start(p_sequencer.instr_if_seqr);
data_intf_seq.start(p_sequencer.data_if_seqr);
join_none
endtask
endclass

28
dv/uvm/vcs.tcl Normal file
View file

@ -0,0 +1,28 @@
# TCL file invoked from VCS's simv at run-time using this: -ucli -do <this file>
# Syntax: fsdbDumpfile FSDB_Name [Limit_Size]
fsdbDumpfile "waves.fsdb"
# Syntax: fsdbDumpvars [depth] [instance] [option]*
##############################################################################
# Option Description
##############################################################################
# +mda Dumps memory and MDA signals in all scopes.
# +packedmda Dumps packed signals
# +struct Dumps structs
# +skip_cell_instance=mode Enables or disables cell dumping
# +strength Enables strength dumping
# +parameter Dumps parameters
# +power Dumps power-related signals
# +trace_process Dumps VHDL processes
# +no_functions Disables dumping of functions
# +sva Dumps assertions
# +Reg_Only Dumps only reg type signals
# +IO_Only Dumps only IO port signals
# +by_file=<filename> File to specify objects to add
# +all Dumps memories, MDA signals, structs, unions,power, and packed structs
fsdbDumpvars 0 core_ibex_tb_top +all
fsdbDumpSVA 0 core_ibex_tb_top.dut
run
quit