mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-22 04:47:25 -04:00
Added dret and ebreak tests (#281)
This commit is contained in:
parent
d14312c3cc
commit
e9c2b2ecb3
7 changed files with 268 additions and 56 deletions
|
@ -81,9 +81,9 @@ def check_ibex_uvm_log(uvm_log, core_name, test_name, report, write=True):
|
|||
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)
|
||||
fd.write("%s : [PASSED]\n\n" % test_name)
|
||||
elif fail_cnt == 1:
|
||||
fd.write("%s : FAILED\n" % test_name)
|
||||
fd.write("%s : [FAILED]\n\n" % test_name)
|
||||
if report:
|
||||
fd.close()
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@
|
|||
+no_csr_instr=1
|
||||
+no_fence=1
|
||||
+num_of_sub_program=0
|
||||
rtl_test: core_ibex_debug_intr_test
|
||||
rtl_test: core_ibex_debug_intr_basic_test
|
||||
sim_opts: >
|
||||
+require_signature_addr=1
|
||||
+max_interval=1000
|
||||
|
@ -135,14 +135,13 @@
|
|||
+instr_cnt=6000
|
||||
+no_csr_instr=1
|
||||
+no_fence=1
|
||||
rtl_test: core_ibex_debug_intr_test
|
||||
rtl_test: core_ibex_debug_intr_basic_test
|
||||
iterations: 5
|
||||
sim_opts: >
|
||||
+max_interval=250
|
||||
+enable_debug_stress_seq=1
|
||||
+require_signature_addr=1
|
||||
compare_opts:
|
||||
compare_final_value_only: 1
|
||||
verbose: 1
|
||||
|
||||
- test: riscv_debug_branch_jump_test
|
||||
|
@ -158,11 +157,12 @@
|
|||
+instr_cnt=6000
|
||||
+no_csr_instr=1
|
||||
+no_fence=1
|
||||
+num_of_sub_program=5
|
||||
+num_debug_sub_program=5
|
||||
rtl_test: core_ibex_debug_intr_test
|
||||
+num_of_sub_program=0
|
||||
+num_debug_sub_program=3
|
||||
rtl_test: core_ibex_debug_intr_basic_test
|
||||
sim_opts: >
|
||||
+require_signature_addr=1
|
||||
+max_interval=2000
|
||||
+enable_debug_stress_seq=1
|
||||
compare_opts:
|
||||
compare_final_value_only: 1
|
||||
|
@ -189,6 +189,75 @@
|
|||
compare_final_value_only: 1
|
||||
verbose: 1
|
||||
|
||||
- test: riscv_dret_test
|
||||
description: >
|
||||
Dret instructions will be inserted into M-mode code, ibex should treat these
|
||||
like illegal instructions.
|
||||
iterations: 5
|
||||
gen_test: riscv_rand_instr_test
|
||||
gen_opts: >
|
||||
+require_signature_addr=1
|
||||
+no_dret=0
|
||||
+instr_cnt=6000
|
||||
rtl_test: core_ibex_dret_test
|
||||
sim_opts: >
|
||||
+require_signature_addr=1
|
||||
|
||||
- test: riscv_ebreak_test
|
||||
description: >
|
||||
Ebreak instructions will be inserted into the M mode code, ibex should handle them normally.
|
||||
iterations: 5
|
||||
gen_test: riscv_rand_instr_test
|
||||
gen_opts: >
|
||||
+no_ebreak=0
|
||||
+instr_cnt=6000
|
||||
rtl_test: core_ibex_base_test
|
||||
|
||||
- test: riscv_debug_ebreak_test
|
||||
description: >
|
||||
A directed ebreak sequence will be inserted into the debug rom, upon encountering it,
|
||||
ibex should jump back to the beginning of debug mode. The sequence is designed to avoid an
|
||||
infinite loop.
|
||||
iterations: 5
|
||||
gen_test: riscv_rand_instr_test
|
||||
gen_opts: >
|
||||
+require_signature_addr=1
|
||||
+gen_debug_section=1
|
||||
+enable_ebreak_in_debug_rom=1
|
||||
+no_csr_instr=1
|
||||
+no_fence=1
|
||||
+no_wfi=1
|
||||
+no_ebreak=0
|
||||
+instr_cnt=6000
|
||||
rtl_test: core_ibex_debug_ebreak_test
|
||||
sim_opts: >
|
||||
+require_signature_addr=1
|
||||
+enable_debug_single_seq=1
|
||||
compare_opts:
|
||||
compare_final_value_only: 1
|
||||
|
||||
- test: riscv_debug_ebreakm_test
|
||||
description: >
|
||||
dcsr.ebreakm will be set at the beginning of the test upon the first entry into the debug rom.
|
||||
From then on, every ebreak instruction should cause debug mode to be entered.
|
||||
iterations: 5
|
||||
gen_test: riscv_rand_instr_test
|
||||
gen_opts: >
|
||||
+require_signature_addr=1
|
||||
+gen_debug_section=1
|
||||
+set_dcsr_ebreak=1
|
||||
+no_ebreak=0
|
||||
+no_csr_instr=1
|
||||
+no_fence=1
|
||||
+no_wfi=1
|
||||
+instr_cnt=6000
|
||||
rtl_test: core_ibex_debug_ebreakm_test
|
||||
sim_opts: >
|
||||
+require_signature_addr=1
|
||||
+enable_debug_single_seq=1
|
||||
compare_opts:
|
||||
compare_final_value_only: 1
|
||||
|
||||
- test: riscv_interrupt_test
|
||||
description: >
|
||||
Random instruction test with complete interrupt handling
|
||||
|
@ -196,7 +265,7 @@
|
|||
gen_test: riscv_rand_instr_test
|
||||
gen_opts: >
|
||||
+require_signature_addr=1
|
||||
rtl_test: core_ibex_debug_intr_test
|
||||
rtl_test: core_ibex_debug_intr_basic_test
|
||||
sim_opts: >
|
||||
+require_signature_addr=1
|
||||
+enable_irq_stress_seq=1
|
||||
|
|
|
@ -166,8 +166,8 @@ def compare(test_list, iss, output_dir, verbose):
|
|||
print("Comparing %s/DUT sim result : %s" % (iss, elf))
|
||||
run_cmd(("echo 'Test binary: %s' >> %s" % (elf, report)))
|
||||
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))
|
||||
rtl_log = ("%s/rtl_sim/%s.%d/trace_core_00000000.log" % (output_dir, test['test'], i))
|
||||
rtl_csv = ("%s/rtl_sim/%s.%d/trace_core_00000000.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:
|
||||
check_ibex_uvm_log(uvm_log, "ibex", test_name, report)
|
||||
|
|
|
@ -12,12 +12,12 @@ class core_ibex_base_test extends uvm_test;
|
|||
core_ibex_vseq vseq;
|
||||
irq_seq irq_seq_h;
|
||||
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)
|
||||
// If no signature_addr handshake functionality is desired between the testbench and the generated
|
||||
// code, the test will wait for the specifield 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;
|
||||
bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] signature_data_q[$];
|
||||
bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] signature_data;
|
||||
uvm_tlm_analysis_fifo #(ibex_mem_intf_seq_item) item_collected_port;
|
||||
|
||||
`uvm_component_utils(core_ibex_base_test)
|
||||
|
@ -120,23 +120,20 @@ class core_ibex_base_test extends uvm_test;
|
|||
input signature_type_t ref_type);
|
||||
ibex_mem_intf_seq_item mem_txn;
|
||||
forever begin
|
||||
// The first write to this address is guaranteed to contain the
|
||||
// signature type in bits [7:0]
|
||||
// The first write to this address is guaranteed to contain the signature type in bits [7:0]
|
||||
item_collected_port.get(mem_txn);
|
||||
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
|
||||
// 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
|
||||
signature_data_q.push_back(signature_data >> 8);
|
||||
end
|
||||
TEST_RESULT: begin
|
||||
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
|
||||
// The next 32 writes to the address are guaranteed to be a dump of all GPRs
|
||||
WRITE_GPR: begin
|
||||
for(int i = 0; i < 32; i++) begin
|
||||
do begin
|
||||
|
@ -145,8 +142,7 @@ class core_ibex_base_test extends uvm_test;
|
|||
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
|
||||
// The next write to this address is guaranteed to be the data held in the CSR
|
||||
WRITE_CSR: begin
|
||||
signature_data_q.push_back(signature_data >> 8);
|
||||
do begin
|
||||
|
@ -163,10 +159,12 @@ 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.
|
||||
// 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.
|
||||
|
||||
// Gets the next CORE_STATUS signature write and compares it against the provided core_status
|
||||
// type, throws uvm_error on mismatch
|
||||
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();
|
||||
|
@ -175,6 +173,17 @@ class core_ibex_base_test extends uvm_test;
|
|||
end
|
||||
endtask
|
||||
|
||||
// Waits for a write to the address of the specified CSR and retrieves the csr data
|
||||
virtual task wait_for_csr_write(csr_num_e csr);
|
||||
bit [11:0] csr_addr;
|
||||
do begin
|
||||
wait_for_mem_txn(cfg.signature_addr, WRITE_CSR);
|
||||
csr_addr = signature_data_q.pop_front();
|
||||
signature_data = signature_data_q.pop_front();
|
||||
end while (csr_addr != csr);
|
||||
endtask
|
||||
|
||||
// Waits until the next time the given core_status is written to the signature address
|
||||
virtual task wait_for_core_status(core_status_t core_status);
|
||||
do begin
|
||||
wait_for_mem_txn(cfg.signature_addr, CORE_STATUS);
|
||||
|
|
|
@ -32,9 +32,9 @@ class core_ibex_csr_test extends core_ibex_base_test;
|
|||
endclass
|
||||
|
||||
// Debug test class
|
||||
class core_ibex_debug_intr_test extends core_ibex_base_test;
|
||||
class core_ibex_debug_intr_basic_test extends core_ibex_base_test;
|
||||
|
||||
`uvm_component_utils(core_ibex_debug_intr_test)
|
||||
`uvm_component_utils(core_ibex_debug_intr_basic_test)
|
||||
`uvm_component_new
|
||||
|
||||
virtual task send_stimulus();
|
||||
|
@ -46,8 +46,8 @@ class core_ibex_debug_intr_test extends core_ibex_base_test;
|
|||
if (cfg.require_signature_addr) begin
|
||||
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
|
||||
// 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
|
||||
|
@ -68,10 +68,10 @@ class core_ibex_debug_intr_test extends core_ibex_base_test;
|
|||
|
||||
endclass
|
||||
|
||||
// Debug WFI test class
|
||||
class core_ibex_debug_wfi_test extends core_ibex_base_test;
|
||||
// Base class for directed debug and irq test scenarios
|
||||
class core_ibex_directed_test extends core_ibex_base_test;
|
||||
|
||||
`uvm_component_utils(core_ibex_debug_wfi_test)
|
||||
`uvm_component_utils(core_ibex_directed_test)
|
||||
`uvm_component_new
|
||||
|
||||
virtual task send_stimulus();
|
||||
|
@ -95,30 +95,163 @@ class core_ibex_debug_wfi_test extends core_ibex_base_test;
|
|||
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
|
||||
// Wait for core initialization before starting the stimulus check 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
|
||||
// Should be extended by derived classes.
|
||||
// DO NOT use this test class directly.
|
||||
check_stimulus();
|
||||
end
|
||||
end
|
||||
join_none
|
||||
endtask
|
||||
|
||||
virtual task check_stimulus();
|
||||
`uvm_fatal(`gfn, "Base class task should not be used")
|
||||
endtask
|
||||
|
||||
//------------------------------------------------------
|
||||
// Checker functions/tasks that might be commonly used
|
||||
//------------------------------------------------------
|
||||
|
||||
// compares dcsr.ebreak against the privilege mode encoded in dcsr.prv
|
||||
virtual function check_dcsr_ebreak();
|
||||
// dcsr.prv is the bottom two bits.
|
||||
case (signature_data[1:0])
|
||||
2'b11: begin
|
||||
`DV_CHECK_EQ_FATAL(signature_data[15], 1'b1, "dcsr.ebreakm is not set")
|
||||
end
|
||||
2'b01: begin
|
||||
`DV_CHECK_EQ_FATAL(signature_data[13], 1'b1, "dcsr.ebreaks is not set")
|
||||
end
|
||||
2'b00: begin
|
||||
`DV_CHECK_EQ_FATAL(signature_data[12], 1'b1, "dcsr.ebreaku is not set")
|
||||
end
|
||||
default: begin
|
||||
`uvm_fatal(`gfn, "dcsr.prv is an unsupported privilege mode")
|
||||
end
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
virtual function check_dcsr_cause(dbg_cause_e cause);
|
||||
`DV_CHECK_EQ_FATAL(cause, signature_data[8:6], "dcsr.cause has been incorrectly updated")
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
// Debug WFI test class
|
||||
class core_ibex_debug_wfi_test extends core_ibex_directed_test;
|
||||
|
||||
`uvm_component_utils(core_ibex_debug_wfi_test)
|
||||
`uvm_component_new
|
||||
|
||||
virtual task check_stimulus();
|
||||
// TODO(udi) - need to check that no other instruction fetches occur after 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.
|
||||
// We also want to check that dcsr.cause has been set correctly
|
||||
wait_for_csr_write(CSR_DCSR);
|
||||
check_dcsr_cause(DBG_CAUSE_HALTREQ);
|
||||
wait (dut_vif.dret === 1'b1);
|
||||
end
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
// DRET test class
|
||||
class core_ibex_dret_test extends core_ibex_directed_test;
|
||||
|
||||
`uvm_component_utils(core_ibex_dret_test)
|
||||
`uvm_component_new
|
||||
|
||||
virtual task check_stimulus();
|
||||
forever begin
|
||||
wait (dut_vif.dret === 1'b1);
|
||||
// After hitting a dret, the core will jump to the vectored trap handler, which sends a
|
||||
// handshake write to the bench
|
||||
check_next_core_status(HANDLING_EXCEPTION, "Core did not jump to vectored exception handler");
|
||||
// The core will receive an illegal instruction handshake after jumping from the vectored trap
|
||||
// handler to the illegal instruction exception handler
|
||||
check_next_core_status(ILLEGAL_INSTR_EXCEPTION, "Core did not treat dret like illegal instruction");
|
||||
end
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
// Normal debug ebreak test class
|
||||
class core_ibex_debug_ebreak_test extends core_ibex_directed_test;
|
||||
|
||||
`uvm_component_utils(core_ibex_debug_ebreak_test)
|
||||
`uvm_component_new
|
||||
|
||||
bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] dpc;
|
||||
bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] dcsr;
|
||||
|
||||
virtual task check_stimulus();
|
||||
forever begin
|
||||
wait (dut_vif.ebreak === 1'b1);
|
||||
check_next_core_status(HANDLING_EXCEPTION, "Core did not jump to exception handler");
|
||||
check_next_core_status(EBREAK_EXCEPTION, "Core did not jump from exception handler to ebreak handler");
|
||||
wait (dut_vif.mret === 1'b1);
|
||||
// Want to wait until after the ebreak handler has finished to send debug stimulus, to avoid
|
||||
// nested trap scenarios
|
||||
clk_vif.wait_clks($urandom_range(5, 11));
|
||||
vseq.start_debug_single_seq();
|
||||
// capture the first write of dcsr
|
||||
wait_for_csr_write(CSR_DCSR);
|
||||
dcsr = signature_data;
|
||||
// We also want to check that dcsr.cause has been set correctly
|
||||
check_dcsr_cause(DBG_CAUSE_HALTREQ);
|
||||
// capture the first write of dpc
|
||||
wait_for_csr_write(CSR_DPC);
|
||||
dpc = signature_data;
|
||||
check_next_core_status(IN_DEBUG_MODE, "Core did not properly jump into debug mode");
|
||||
wait (dut_vif.ebreak === 1'b1);
|
||||
// compare the second writes of dcsr and dpc against the captured values
|
||||
wait_for_csr_write(CSR_DCSR);
|
||||
`DV_CHECK_EQ_FATAL(dcsr, signature_data, "ebreak inside the debug rom has changed the value of DCSR")
|
||||
wait_for_csr_write(CSR_DPC);
|
||||
`DV_CHECK_EQ_FATAL(dpc, signature_data, "ebreak inside the debug rom has changed the value of DPC")
|
||||
wait (dut_vif.dret === 1'b1);
|
||||
end
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
// Debug ebreak test with dcsr.ebreak(m/s/u) set
|
||||
class core_ibex_debug_ebreakm_test extends core_ibex_directed_test;
|
||||
|
||||
`uvm_component_utils(core_ibex_debug_ebreakm_test)
|
||||
`uvm_component_new
|
||||
|
||||
virtual task check_stimulus();
|
||||
// send a single debug request after core initialization to configure dcsr
|
||||
vseq.start_debug_single_seq();
|
||||
check_next_core_status(IN_DEBUG_MODE, "Core did not enter debug mode after debug_req stimulus");
|
||||
// Read dcsr and verify the appropriate ebreak(m/s/u) bit has been set based on the prv field,
|
||||
// as well as the cause field
|
||||
wait_for_csr_write(CSR_DCSR);
|
||||
check_dcsr_ebreak();
|
||||
check_dcsr_cause(DBG_CAUSE_HALTREQ);
|
||||
wait (dut_vif.dret === 1'b1);
|
||||
forever begin
|
||||
wait (dut_vif.ebreak === 1'b1);
|
||||
check_next_core_status(IN_DEBUG_MODE, "Core did not enter debug mode after execution of ebreak");
|
||||
// Read dcsr and verify the appropriate ebreak(m/s/u) bit has been set based on the prv field
|
||||
wait_for_csr_write(CSR_DCSR);
|
||||
check_dcsr_ebreak();
|
||||
check_dcsr_cause(DBG_CAUSE_EBREAK);
|
||||
wait (dut_vif.dret === 1'b1);
|
||||
end
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
|
|
@ -12,6 +12,7 @@ package core_ibex_test_pkg;
|
|||
import ibex_mem_intf_agent_pkg::*;
|
||||
import irq_agent_pkg::*;
|
||||
import riscv_signature_pkg::*;
|
||||
import ibex_pkg::*;
|
||||
|
||||
`include "core_ibex_report_server.sv"
|
||||
`include "core_ibex_seq_lib.sv"
|
||||
|
|
|
@ -72,8 +72,8 @@ class core_ibex_vseq extends uvm_sequence;
|
|||
end
|
||||
endtask
|
||||
|
||||
// Helper tasks to allow the test fine grained control to start sequences
|
||||
// through the vseq - necessary for testing directed stimulus scenarios
|
||||
// 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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue