Update interrupt mode, add debug mode WFI test (#268)

This commit is contained in:
udinator 2019-08-28 10:00:36 -07:00 committed by GitHub
parent 2b93475864
commit 68b170638a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 248 additions and 96 deletions

View file

@ -6,6 +6,10 @@
interface core_ibex_dut_probe_if(input logic clk);
logic illegal_instr;
logic ecall;
logic wfi;
logic ebreak;
logic dret;
logic mret;
logic fetch_enable;
logic debug_req;
endinterface

View file

@ -25,7 +25,7 @@ class core_ibex_env extends uvm_env;
create("data_if_slave_agent", this);
instr_if_slave_agent = ibex_mem_intf_slave_agent::type_id::
create("instr_if_slave_agent", this);
if (cfg.enable_irq_seq) begin
if (cfg.enable_irq_stress_seq || cfg.enable_irq_single_seq) begin
irq_agent = irq_master_agent::type_id::create("irq_agent", this);
end
// Create virtual sequencer
@ -36,7 +36,7 @@ class core_ibex_env extends uvm_env;
super.connect_phase(phase);
vseqr.data_if_seqr = data_if_slave_agent.sequencer;
vseqr.instr_if_seqr = instr_if_slave_agent.sequencer;
if (cfg.enable_irq_seq) begin
if (cfg.enable_irq_stress_seq || cfg.enable_irq_single_seq) begin
vseqr.irq_seqr = irq_agent.sequencer;
end
endfunction : connect_phase

View file

@ -4,22 +4,31 @@
class core_ibex_env_cfg extends uvm_object;
bit enable_irq_seq;
bit enable_debug_seq;
bit enable_irq_stress_seq;
bit enable_irq_single_seq;
bit enable_debug_stress_seq;
bit enable_debug_single_seq;
bit[31:0] max_interval;
bit require_signature_addr;
bit[31:0] signature_addr;
`uvm_object_utils_begin(core_ibex_env_cfg)
`uvm_field_int(enable_irq_seq, UVM_DEFAULT)
`uvm_field_int(enable_debug_seq, UVM_DEFAULT)
`uvm_field_int(enable_irq_stress_seq, UVM_DEFAULT)
`uvm_field_int(enable_irq_single_seq, UVM_DEFAULT)
`uvm_field_int(enable_debug_single_seq, UVM_DEFAULT)
`uvm_field_int(enable_debug_stress_seq, UVM_DEFAULT)
`uvm_field_int(max_interval, UVM_DEFAULT)
`uvm_field_int(require_signature_addr, UVM_DEFAULT)
`uvm_field_int(signature_addr, UVM_DEFAULT)
`uvm_object_utils_end
function new(string name = "");
super.new(name);
void'($value$plusargs("enable_irq_seq=%0d", enable_irq_seq));
void'($value$plusargs("enable_debug_seq=%0d", enable_debug_seq));
void'($value$plusargs("enable_irq_stress_seq=%0d", enable_irq_stress_seq));
void'($value$plusargs("enable_irq_single_seq=%0d", enable_irq_single_seq));
void'($value$plusargs("enable_debug_stress_seq=%0d", enable_debug_stress_seq));
void'($value$plusargs("enable_debug_single_seq=%0d", enable_debug_single_seq));
void'($value$plusargs("max_interval=%0d", max_interval));
void'($value$plusargs("require_signature_addr=%0d", require_signature_addr));
void'($value$plusargs("signature_addr=%0h", signature_addr));
endfunction

View file

@ -47,7 +47,7 @@ def process_ibex_sim_log(ibex_log, csv):
print("Processed instruction count : %d" % instr_cnt)
def check_ibex_uvm_log(uvm_log, core_name, test_name, report):
def check_ibex_uvm_log(uvm_log, core_name, test_name, report, write=True):
"""Process Ibex UVM simulation log.
This function will be used when a test disables the normal post_compare step.
@ -58,14 +58,11 @@ def check_ibex_uvm_log(uvm_log, core_name, test_name, report):
core_name: the name of the core
test_name: name of the test being checked
report: the output report file
write: enables writing to the log file
Returns:
A boolean indicating whether the test passed or failed based on the signature
"""
if report:
fd = open(report, "a+")
else:
fd = sys.stdout
fd.write("%s uvm log : %s\n" % (core_name, uvm_log))
pass_cnt = 0
fail_cnt = 0
with open(uvm_log, "r") as log:
@ -76,12 +73,21 @@ def check_ibex_uvm_log(uvm_log, core_name, test_name, report):
elif 'RISC-V UVM TEST FAILED' in line:
fail_cnt += 1
break
if pass_cnt == 1:
fd.write("%s : PASSED\n" % test_name)
elif fail_cnt == 1:
fd.write("%s : FAILED\n" % test_name)
if report:
fd.close()
if write:
if report:
fd = open(report, "a+")
else:
fd = sys.stdout
fd.write("%s uvm log : %s\n" % (core_name, uvm_log))
if pass_cnt == 1:
fd.write("%s : PASSED\n" % test_name)
elif fail_cnt == 1:
fd.write("%s : FAILED\n" % test_name)
if report:
fd.close()
return pass_cnt == 1
def main():

View file

@ -119,7 +119,8 @@
rtl_test: core_ibex_debug_intr_test
sim_opts: >
+require_signature_addr=1
+enable_debug_seq=1
+max_interval=1000
+enable_debug_stress_seq=1
compare_opts:
compare_final_value_only: 1
verbose: 1
@ -137,7 +138,8 @@
rtl_test: core_ibex_debug_intr_test
iterations: 5
sim_opts: >
+frequent_debug=1
+max_interval=250
+enable_debug_stress_seq=1
+require_signature_addr=1
compare_opts:
compare_final_value_only: 1
@ -161,7 +163,28 @@
rtl_test: core_ibex_debug_intr_test
sim_opts: >
+require_signature_addr=1
+enable_debug_seq=1
+enable_debug_stress_seq=1
compare_opts:
compare_final_value_only: 1
verbose: 1
- test: riscv_debug_wfi_test
description: >
Assert debug_req while core is in WFI sleep state, should jump to debug mode
iterations: 5
gen_test: riscv_rand_instr_test
gen_opts: >
+require_signature_addr=1
+gen_debug_section=1
+no_ebreak=1
+instr_cnt=3000
+no_csr_instr=1
+no_fence=1
+no_wfi=0
rtl_test: core_ibex_debug_wfi_test
sim_opts: >
+require_signature_addr=1
+enable_debug_single_seq=1
compare_opts:
compare_final_value_only: 1
verbose: 1
@ -176,7 +199,7 @@
rtl_test: core_ibex_debug_intr_test
sim_opts: >
+require_signature_addr=1
+enable_irq_seq=1
+enable_irq_stress_seq=1
compare_opts:
compare_final_value_only: 1

View file

@ -168,8 +168,8 @@ def compare(test_list, iss, output_dir, verbose):
uvm_log = ("%s/rtl_sim/%s.%d/sim.log" % (output_dir, test['test'], i))
rtl_log = ("%s/rtl_sim/%s.%d/trace_core_00_0.log" % (output_dir, test['test'], i))
rtl_csv = ("%s/rtl_sim/%s.%d/trace_core_00_0.csv" % (output_dir, test['test'], i))
test_name = "%s.%d" % (test['test'], i)
if 'no_post_compare' in test and test['no_post_compare'] == 1:
test_name = "%s.%d" % (test['test'], i)
check_ibex_uvm_log(uvm_log, "ibex", test_name, report)
else:
process_ibex_sim_log(rtl_log, rtl_csv)
@ -182,18 +182,22 @@ def compare(test_list, iss, output_dir, verbose):
else:
print("Unsupported ISS" % iss)
sys.exit(1)
if 'compare_opts' in test:
compare_opts = test.get('compare_opts')
in_order_mode = compare_opts.get('in_order_mode', 1)
coalescing_limit = compare_opts.get('coalescing_limit', 0)
verbose = compare_opts.get('verbose', 0)
mismatch = compare_opts.get('mismatch_print_limit', 5)
compare_final = compare_opts.get('compare_final_value_only', 0)
compare_trace_csv(rtl_csv, iss_csv, "ibex", iss, report,
in_order_mode, coalescing_limit, verbose,
mismatch, compare_final)
uvm_result = check_ibex_uvm_log(uvm_log, "ibex", test_name, report, False)
if not uvm_result:
check_ibex_uvm_log(uvm_log, "ibex", test_name, report)
else:
compare_trace_csv(rtl_csv, iss_csv, "ibex", iss, report)
if 'compare_opts' in test:
compare_opts = test.get('compare_opts')
in_order_mode = compare_opts.get('in_order_mode', 1)
coalescing_limit = compare_opts.get('coalescing_limit', 0)
verbose = compare_opts.get('verbose', 0)
mismatch = compare_opts.get('mismatch_print_limit', 5)
compare_final = compare_opts.get('compare_final_value_only', 0)
compare_trace_csv(rtl_csv, iss_csv, "ibex", iss, report,
in_order_mode, coalescing_limit, verbose,
mismatch, compare_final)
else:
compare_trace_csv(rtl_csv, iss_csv, "ibex", iss, report)
passed_cnt = run_cmd("grep PASSED %s | wc -l" % report).strip()
failed_cnt = run_cmd("grep FAILED %s | wc -l" % report).strip()
summary = ("%s PASSED, %s FAILED" % (passed_cnt, failed_cnt))

View file

@ -69,7 +69,11 @@ module core_ibex_tb_top;
force irq_vif.reset = ~rst_n;
end
assign dut_if.ecall = dut.u_ibex_core.id_stage_i.ecall_insn_dec;
assign dut_if.ecall = dut.u_ibex_core.id_stage_i.ecall_insn_dec;
assign dut_if.wfi = dut.u_ibex_core.id_stage_i.wfi_insn_dec;
assign dut_if.ebreak = dut.u_ibex_core.id_stage_i.ebrk_insn;
assign dut_if.dret = dut.u_ibex_core.id_stage_i.dret_insn_dec;
assign dut_if.mret = dut.u_ibex_core.id_stage_i.mret_insn_dec;
initial begin

View file

@ -8,17 +8,16 @@ class core_ibex_base_test extends uvm_test;
core_ibex_env_cfg cfg;
virtual clk_if clk_vif;
virtual core_ibex_dut_probe_if dut_vif;
virtual ibex_mem_intf dmem_vif;
mem_model_pkg::mem_model mem;
core_ibex_vseq vseq;
bit enable_irq_seq;
bit enable_debug_seq;
irq_seq irq_seq_h;
int unsigned timeout_in_cycles = 2000000;
int unsigned timeout_in_cycles = 3000000;
// If no signature_addr handshake functionality is desired between the
// testbench and the generated code, the test will wait for the specified
// number of cycles before starting stimulus sequences (irq and debug)
int unsigned stimulus_delay = 800;
bit[ibex_mem_intf_agent_pkg::DATA_WIDTH] signature_data_q[$];
bit[ibex_mem_intf_agent_pkg::DATA_WIDTH] signature_data;
uvm_tlm_analysis_fifo #(ibex_mem_intf_seq_item) item_collected_port;
`uvm_component_utils(core_ibex_base_test)
@ -40,9 +39,6 @@ class core_ibex_base_test extends uvm_test;
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
if (!uvm_config_db#(virtual ibex_mem_intf)::get(null, "*data_if_slave*", "vif", dmem_vif)) begin
`uvm_fatal(get_full_name(), "Cannot get dmem_vif")
end
env = core_ibex_env::type_id::create("env", this);
cfg = core_ibex_env_cfg::type_id::create("cfg", this);
uvm_config_db#(core_ibex_env_cfg)::set(this, "*", "cfg", cfg);
@ -64,7 +60,7 @@ class core_ibex_base_test extends uvm_test;
clk_vif.wait_clks(100);
load_binary_to_mem();
dut_vif.fetch_enable = 1'b1;
vseq.start(env.vseqr);
send_stimulus();
wait_for_test_done();
phase.drop_objection(this);
endtask
@ -73,6 +69,10 @@ class core_ibex_base_test extends uvm_test;
super.report_phase(phase);
endfunction
virtual task send_stimulus();
vseq.start(env.vseqr);
endtask
function void load_binary_to_mem();
string bin;
bit [7:0] r8;
@ -115,11 +115,10 @@ class core_ibex_base_test extends uvm_test;
join_any
endtask
virtual task wait_for_mem_txn(input bit[ibex_mem_intf_agent_pkg::ADDR_WIDTH-1:0] ref_addr,
input signature_type_t ref_type,
output bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] data[$]);
input signature_type_t ref_type);
ibex_mem_intf_seq_item mem_txn;
bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] signature_data;
forever begin
// The first write to this address is guaranteed to contain the
// signature type in bits [7:0]
@ -127,11 +126,14 @@ class core_ibex_base_test extends uvm_test;
if (mem_txn.addr == ref_addr && mem_txn.data[7:0] === ref_type && mem_txn.read_write == WRITE) begin
signature_data = mem_txn.data;
case (ref_type)
// The very first write to the signature address in every test is
// guaranteed to be a write of CORE_STATUS, indicating the
// INITIALIZED state
CORE_STATUS: begin
data.push_back(signature_data >> 8);
signature_data_q.push_back(signature_data >> 8);
end
TEST_RESULT: begin
data.push_back(signature_data >> 8);
signature_data_q.push_back(signature_data >> 8);
end
// The next 32 writes to the address are guaranteed to be a dump of
// all GPRs
@ -140,17 +142,17 @@ class core_ibex_base_test extends uvm_test;
do begin
item_collected_port.get(mem_txn);
end while(!(mem_txn.addr == ref_addr && mem_txn.read_write == WRITE));
data.push_back(mem_txn.data);
signature_data_q.push_back(mem_txn.data);
end
end
// The next write to this address is guaranteed to be the data held
// in the CSR
WRITE_CSR: begin
data.push_back(signature_data >> 8);
signature_data_q.push_back(signature_data >> 8);
do begin
item_collected_port.get(mem_txn);
end while (!(mem_txn.addr == ref_addr && mem_txn.read_write == WRITE));
data.push_back(mem_txn.data);
signature_data_q.push_back(mem_txn.data);
end
default: begin
`uvm_fatal(`gfn, $sformatf("The data 0x%0h written to the signature address is formatted incorrectly.", signature_data))
@ -161,4 +163,23 @@ class core_ibex_base_test extends uvm_test;
end
endtask
// API of various tasks wrapping wait_for_mem_txn, for various common
// functionalities that might be needed for verification purposes.
// Will be expanded as needed.
virtual task check_next_core_status(core_status_t core_status, error_msg="");
wait_for_mem_txn(cfg.signature_addr, CORE_STATUS);
signature_data = signature_data_q.pop_front();
if (signature_data != core_status) begin
`uvm_error(`gfn, error_msg)
end
endtask
virtual task wait_for_core_status(core_status_t core_status);
do begin
wait_for_mem_txn(cfg.signature_addr, CORE_STATUS);
signature_data = signature_data_q.pop_front();
end while (signature_data != core_status);
endtask
endclass

View file

@ -9,7 +9,7 @@ class core_base_seq #(type REQ = uvm_sequence_item) extends uvm_sequence#(REQ);
rand int unsigned delay;
int unsigned num_of_iterations; // 0: infinite until stopped
int unsigned iteration_cnt;
int unsigned max_interval = 1000;
int unsigned max_interval;
int unsigned max_delay = 500;
virtual clk_if clk_vif;
bit stop_seq;
@ -98,7 +98,7 @@ class debug_seq extends core_base_seq;
virtual task send_req();
`uvm_info(get_full_name(), "Sending debug request", UVM_HIGH)
dut_vif.debug_req <= 1'b1;
clk_vif.wait_clks($urandom_range(1, 20));
clk_vif.wait_clks($urandom_range(10, 30));
dut_vif.debug_req <= 1'b0;
endtask

View file

@ -9,12 +9,11 @@ class core_ibex_csr_test extends core_ibex_base_test;
`uvm_component_new
virtual task wait_for_test_done();
bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] signature_data[$];
bit result;
fork
begin
wait_for_mem_txn(cfg.signature_addr, TEST_RESULT, signature_data);
result = signature_data.pop_front();
wait_for_mem_txn(cfg.signature_addr, TEST_RESULT);
result = signature_data_q.pop_front();
if (result == TEST_PASS) begin
`uvm_info(`gfn, "CSR test completed successfully!", UVM_LOW)
end else if (result == TEST_FAIL) begin
@ -35,43 +34,91 @@ endclass
// Debug test class
class core_ibex_debug_intr_test extends core_ibex_base_test;
bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] core_start_data[$];
bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] initialized_data;
`uvm_component_utils(core_ibex_debug_intr_test)
`uvm_component_new
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;
virtual task send_stimulus();
fork
vseq.start(env.vseqr);
begin
vseq.start(env.vseqr);
end
begin
if (cfg.require_signature_addr) begin
do begin
wait_for_mem_txn(cfg.signature_addr, CORE_STATUS, core_start_data);
initialized_data = core_start_data.pop_front();
end while(initialized_data != INITIALIZED);
wait_for_core_status(INITIALIZED);
end else begin
// If no signature_addr functionality is desired, then the test will
// simply wait for an adequate number of cycles
clk_vif.wait_clks(stimulus_delay);
end
fork
if (cfg.enable_irq_seq) begin
vseq.start_irq_seq();
begin
if (cfg.enable_irq_stress_seq) begin
vseq.start_irq_stress_seq();
end
end
if (cfg.enable_debug_seq) begin
vseq.start_debug_seq();
begin
if (cfg.enable_debug_stress_seq) begin
vseq.start_debug_stress_seq();
end
end
join_none
end
join_none
wait_for_test_done();
phase.drop_objection(this);
endtask
endclass
// Debug WFI test class
class core_ibex_debug_wfi_test extends core_ibex_base_test;
`uvm_component_utils(core_ibex_debug_wfi_test)
`uvm_component_new
virtual task send_stimulus();
fork
begin
vseq.start(env.vseqr);
end
begin
if (!cfg.require_signature_addr) begin
clk_vif.wait_clks(stimulus_delay);
fork
begin
if (cfg.enable_irq_stress_seq) begin
vseq.start_irq_stress_seq();
end
end
begin
if (cfg.enable_debug_stress_seq) begin
vseq.start_debug_stress_seq();
end
end
join_none
end else begin
// Wait for core initialization before starting the wfi stimulus
// loop - first write to signature address is guaranteed to be core
// initialization info
check_next_core_status(INITIALIZED, "Core initialization handshake failure");
// TODO(udi) - need to check that no other instruction fetches occur
// after the WFI is detected, and before any stimulus is sent to the
// core
forever begin
wait (dut_vif.wfi === 1'b1);
clk_vif.wait_clks($urandom_range(100));
vseq.start_debug_single_seq();
// After assserting this signal, core should wake up and jump into
// debug mode from WFI state - next handshake should
// be a notification that the core is now in debug mode
check_next_core_status(IN_DEBUG_MODE, "Core did not jump into debug mode from WFI state");
// We don't want to trigger debug stimulus for any WFI
// instructions encountered inside the debug rom - those should
// act as NOP instructions - so we wait until hitting the end of
// the debug rom
wait (dut_vif.dret === 1'b1);
end
end
end
join_none
endtask
endclass

View file

@ -11,8 +11,10 @@ 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;
irq_seq irq_seq_h;
debug_seq debug_seq_h;
irq_seq irq_seq_stress_h;
irq_seq irq_seq_single_h;
debug_seq debug_seq_stress_h;
debug_seq debug_seq_single_h;
core_ibex_env_cfg cfg;
bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] data;
@ -23,16 +25,32 @@ class core_ibex_vseq extends uvm_sequence;
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");
if (cfg.enable_irq_seq) begin
irq_seq_h = irq_seq::type_id::create("irq_seq_h");
if (cfg.enable_irq_stress_seq) begin
irq_seq_stress_h = irq_seq::type_id::create("irq_seq_stress_h");
irq_seq_stress_h.max_interval = cfg.max_interval;
end
if (cfg.enable_debug_seq) begin
debug_seq_h = debug_seq::type_id::create("debug_seq_h");
if (cfg.enable_irq_single_seq) begin
irq_seq_single_h = irq_seq::type_id::create("irq_seq_single_h");
irq_seq_single_h.num_of_iterations = 1;
irq_seq_single_h.max_interval = 1;
irq_seq_single_h.max_delay = 1;
irq_seq_single_h.interval.rand_mode(0);
irq_seq_single_h.interval = 0;
end
if (cfg.enable_debug_stress_seq) begin
debug_seq_stress_h = debug_seq::type_id::create("debug_seq_stress_h");
debug_seq_stress_h.max_interval = cfg.max_interval;
end
if (cfg.enable_debug_single_seq) begin
debug_seq_single_h = debug_seq::type_id::create("debug_seq_single_h");
debug_seq_single_h.num_of_iterations = 1;
debug_seq_single_h.max_interval = 1;
debug_seq_single_h.max_delay = 1;
debug_seq_single_h.interval.rand_mode(0);
debug_seq_single_h.interval = 0;
end
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);
@ -40,20 +58,36 @@ class core_ibex_vseq extends uvm_sequence;
endtask
virtual task stop();
if (cfg.enable_irq_seq) begin
irq_seq_h.stop();
if (cfg.enable_irq_stress_seq) begin
irq_seq_stress_h.stop();
end
if (cfg.enable_debug_seq) begin
debug_seq_h.stop();
if (cfg.enable_irq_single_seq) begin
irq_seq_single_h.stop();
end
if (cfg.enable_debug_stress_seq) begin
debug_seq_stress_h.stop();
end
if (cfg.enable_debug_single_seq) begin
debug_seq_single_h.stop();
end
endtask
virtual task start_debug_seq();
debug_seq_h.start(null);
// Helper tasks to allow the test fine grained control to start sequences
// through the vseq - necessary for testing directed stimulus scenarios
virtual task start_debug_stress_seq();
debug_seq_stress_h.start(null);
endtask
virtual task start_irq_seq();
irq_seq_h.start(p_sequencer.irq_seqr);
virtual task start_debug_single_seq();
debug_seq_single_h.start(null);
endtask
virtual task start_irq_stress_seq();
irq_seq_stress_h.start(p_sequencer.irq_seqr);
endtask
virtual task start_irq_single_seq();
irq_seq_single_h.start(p_sequencer.irq_seqr);
endtask
endclass