mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-22 04:47:25 -04:00
[DV] Test nested interrupts (#560)
Signed-off-by: Udi <udij@google.com>
This commit is contained in:
parent
3d8089c235
commit
f339f6b96b
5 changed files with 108 additions and 25 deletions
3
dv/uvm/env/core_ibex_env_cfg.sv
vendored
3
dv/uvm/env/core_ibex_env_cfg.sv
vendored
|
@ -6,6 +6,7 @@ class core_ibex_env_cfg extends uvm_object;
|
|||
|
||||
bit enable_irq_single_seq;
|
||||
bit enable_irq_multiple_seq;
|
||||
bit enable_nested_irq;
|
||||
bit enable_debug_seq;
|
||||
bit[31:0] max_interval;
|
||||
bit require_signature_addr;
|
||||
|
@ -15,6 +16,7 @@ class core_ibex_env_cfg extends uvm_object;
|
|||
`uvm_object_utils_begin(core_ibex_env_cfg)
|
||||
`uvm_field_int(enable_irq_single_seq, UVM_DEFAULT)
|
||||
`uvm_field_int(enable_irq_multiple_seq, UVM_DEFAULT)
|
||||
`uvm_field_int(enable_nested_irq, UVM_DEFAULT)
|
||||
`uvm_field_int(enable_debug_seq, UVM_DEFAULT)
|
||||
`uvm_field_int(max_interval, UVM_DEFAULT)
|
||||
`uvm_field_int(require_signature_addr, UVM_DEFAULT)
|
||||
|
@ -25,6 +27,7 @@ class core_ibex_env_cfg extends uvm_object;
|
|||
super.new(name);
|
||||
void'($value$plusargs("enable_irq_single_seq=%0d", enable_irq_single_seq));
|
||||
void'($value$plusargs("enable_irq_multiple_seq=%0d", enable_irq_multiple_seq));
|
||||
void'($value$plusargs("enable_nested_irq=%0d", enable_nested_irq));
|
||||
void'($value$plusargs("enable_debug_seq=%0d", enable_debug_seq));
|
||||
void'($value$plusargs("max_interval=%0d", max_interval));
|
||||
void'($value$plusargs("require_signature_addr=%0d", require_signature_addr));
|
||||
|
|
|
@ -343,6 +343,26 @@
|
|||
compare_opts:
|
||||
compare_final_value_only: 1
|
||||
|
||||
- test: riscv_nested_interrupt_test
|
||||
description: >
|
||||
Assert interrupt, and then assert another interrupt during the first irq_handler routine
|
||||
iterations: 10
|
||||
gen_test: riscv_rand_instr_test
|
||||
gen_opts: >
|
||||
+instr_cnt=10000
|
||||
+require_signature_addr=1
|
||||
+enable_interrupt=1
|
||||
+enable_nested_interrupt=1
|
||||
+randomize_csr=1
|
||||
+no_csr_instr=1
|
||||
rtl_test: core_ibex_nested_irq_test
|
||||
sim_opts: >
|
||||
+require_signature_addr=1
|
||||
+enable_irq_multiple_seq=1
|
||||
+enable_nested_irq=1
|
||||
compare_opts:
|
||||
compare_final_value_only: 1
|
||||
|
||||
- test: riscv_interrupt_wfi_test
|
||||
description: >
|
||||
Inject interrupts after a encountering wfi instructions.
|
||||
|
|
|
@ -70,12 +70,13 @@ class irq_raise_seq extends core_base_seq#(irq_seq_item);
|
|||
|
||||
`uvm_object_utils(irq_raise_seq)
|
||||
`uvm_object_new
|
||||
bit no_nmi;
|
||||
|
||||
virtual task send_req();
|
||||
irq_seq_item irq;
|
||||
irq = irq_seq_item::type_id::create($sformatf("irq_raise_single[%0d]", iteration_cnt));
|
||||
start_item(irq);
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt > 1;)
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt > 1; irq_nm == ~no_nmi;)
|
||||
finish_item(irq);
|
||||
get_response(irq);
|
||||
endtask
|
||||
|
@ -86,12 +87,13 @@ class irq_raise_single_seq extends core_base_seq#(irq_seq_item);
|
|||
|
||||
`uvm_object_utils(irq_raise_single_seq)
|
||||
`uvm_object_new
|
||||
bit no_nmi;
|
||||
|
||||
virtual task send_req();
|
||||
irq_seq_item irq;
|
||||
irq = irq_seq_item::type_id::create($sformatf("irq_raise_single[%0d]", iteration_cnt));
|
||||
start_item(irq);
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt == 1;)
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt == 1; irq_nm == ~no_nmi;)
|
||||
finish_item(irq);
|
||||
get_response(irq);
|
||||
endtask
|
||||
|
|
|
@ -154,6 +154,7 @@ class core_ibex_debug_intr_basic_test extends core_ibex_base_test;
|
|||
|
||||
bit [ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] core_init_mstatus;
|
||||
bit [ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] core_init_mie;
|
||||
priv_lvl_e init_operating_mode;
|
||||
priv_lvl_e operating_mode;
|
||||
bit [$clog2(irq_agent_pkg::DATA_WIDTH)-1:0] irq_id;
|
||||
irq_seq_item irq_txn;
|
||||
|
@ -162,6 +163,7 @@ class core_ibex_debug_intr_basic_test extends core_ibex_base_test;
|
|||
bit [ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] mcause;
|
||||
bit [ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] mip;
|
||||
bit [ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] mie;
|
||||
bit in_nested_trap;
|
||||
|
||||
virtual task send_stimulus();
|
||||
fork
|
||||
|
@ -194,27 +196,35 @@ class core_ibex_debug_intr_basic_test extends core_ibex_base_test;
|
|||
join_none
|
||||
endtask
|
||||
|
||||
function priv_lvl_e select_mode();
|
||||
if (in_nested_trap) return operating_mode;
|
||||
else return init_operating_mode;
|
||||
endfunction
|
||||
|
||||
virtual task wait_for_core_setup();
|
||||
wait_for_csr_write(CSR_MSTATUS, 1000);
|
||||
core_init_mstatus = signature_data;
|
||||
// capture the initial privilege mode ibex will boot into
|
||||
operating_mode = core_init_mstatus[12:11];
|
||||
init_operating_mode = core_init_mstatus[12:11];
|
||||
wait_for_csr_write(CSR_MIE, 500);
|
||||
core_init_mie = signature_data;
|
||||
check_next_core_status(INITIALIZED, "Core initialization handshake failure", 500);
|
||||
endtask
|
||||
|
||||
virtual task send_irq_stimulus();
|
||||
virtual task send_irq_stimulus_start(input bit no_nmi,
|
||||
output bit ret_val);
|
||||
bit irq_valid;
|
||||
// send the interrupt
|
||||
if (cfg.enable_irq_single_seq) vseq.start_irq_raise_single_seq();
|
||||
else if (cfg.enable_irq_multiple_seq) vseq.start_irq_raise_seq();
|
||||
if (cfg.enable_irq_single_seq) vseq.start_irq_raise_single_seq(no_nmi);
|
||||
else if (cfg.enable_irq_multiple_seq) vseq.start_irq_raise_seq(no_nmi);
|
||||
irq_collected_port.get(irq_txn);
|
||||
irq = {irq_txn.irq_nm, irq_txn.irq_fast, 4'b0, irq_txn.irq_external, 3'b0,
|
||||
irq_txn.irq_timer, 3'b0, irq_txn.irq_software, 3'b0};
|
||||
`uvm_info(`gfn, $sformatf("irq: 0x%0x", irq), UVM_LOW)
|
||||
// Get the bit position of the highest priority interrupt - ibex will only handle this one if
|
||||
// there are multiple irqs asserted at once.
|
||||
irq_valid = get_max_valid_irq_id(irq);
|
||||
`uvm_info(`gfn, $sformatf("irq_id: 0x%0x", irq_id), UVM_LOW)
|
||||
// If the interrupt is maskable, and the corresponding bit in MIE is not set, skip the next
|
||||
// checks, as it means the interrupt in question is not enabled by Ibex, and drop the interrupt
|
||||
// lines to avoid locking up the simulation.
|
||||
|
@ -224,15 +234,16 @@ class core_ibex_debug_intr_basic_test extends core_ibex_base_test;
|
|||
irq = {irq_txn.irq_nm, irq_txn.irq_fast, 4'b0, irq_txn.irq_external, 3'b0,
|
||||
irq_txn.irq_timer, 3'b0, irq_txn.irq_software, 3'b0};
|
||||
`DV_CHECK_EQ_FATAL(irq, 0, "Interrupt lines have not been dropped")
|
||||
ret_val = irq_valid;
|
||||
return;
|
||||
end
|
||||
`uvm_info(`gfn, $sformatf("irq: 0b%0b", irq), UVM_LOW)
|
||||
check_next_core_status(HANDLING_IRQ, "Core did not jump to vectored interrupt handler", 750);
|
||||
check_priv_mode(PRIV_LVL_M);
|
||||
operating_mode = dut_vif.priv_mode;
|
||||
// check mstatus
|
||||
wait_for_csr_write(CSR_MSTATUS, 500);
|
||||
mstatus = signature_data;
|
||||
`DV_CHECK_EQ_FATAL(mstatus[12:11], operating_mode, "Incorrect privilege mode")
|
||||
`DV_CHECK_EQ_FATAL(mstatus[12:11], select_mode(), "Incorrect mstatus.mpp")
|
||||
`DV_CHECK_EQ_FATAL(mstatus[7], 1'b1, "mstatus.mpie was not set to 1'b1 after entering handler")
|
||||
`DV_CHECK_EQ_FATAL(mstatus[3], 1'b0, "mstatus.mie was not set to 1'b0 after entering handler")
|
||||
// check mcause against the interrupt id
|
||||
|
@ -254,18 +265,32 @@ class core_ibex_debug_intr_basic_test extends core_ibex_base_test;
|
|||
`DV_CHECK_EQ_FATAL(mip[irq_id], 1'b1,
|
||||
$sformatf("mip[%0d] is not set, but core responded to corresponding interrupt", irq_id))
|
||||
end
|
||||
ret_val = irq_valid;
|
||||
return;
|
||||
endtask
|
||||
|
||||
virtual task send_irq_stimulus_end();
|
||||
// As Ibex interrupts are level sensitive, core must write to memory mapped address to
|
||||
// indicate that irq stimulus be dropped
|
||||
check_next_core_status(FINISHED_IRQ, "Core did not signal end of interrupt properly", 300);
|
||||
// Will receive irq_seq_item indicating that lines have been dropped
|
||||
vseq.start_irq_drop_seq();
|
||||
irq_collected_port.get(irq_txn);
|
||||
irq = {irq_txn.irq_nm, irq_txn.irq_fast, 4'b0, irq_txn.irq_external, 3'b0,
|
||||
irq_txn.irq_timer, 3'b0, irq_txn.irq_software, 3'b0};
|
||||
`DV_CHECK_EQ_FATAL(irq, 0, "Interrupt lines have not been dropped")
|
||||
// Want to skip this .get() call on the second MRET of nested interrupt scenarios
|
||||
if (!(cfg.enable_nested_irq && !in_nested_trap)) begin
|
||||
irq_collected_port.get(irq_txn);
|
||||
irq = {irq_txn.irq_nm, irq_txn.irq_fast, 4'b0, irq_txn.irq_external, 3'b0,
|
||||
irq_txn.irq_timer, 3'b0, irq_txn.irq_software, 3'b0};
|
||||
`DV_CHECK_EQ_FATAL(irq, 0, "Interrupt lines have not been dropped")
|
||||
end
|
||||
wait_ret("mret", 1000);
|
||||
endtask
|
||||
|
||||
virtual task send_irq_stimulus(bit no_nmi = 1'b0);
|
||||
bit ret_val;
|
||||
send_irq_stimulus_start(no_nmi, ret_val);
|
||||
if (ret_val) send_irq_stimulus_end();
|
||||
endtask
|
||||
|
||||
function int get_max_valid_irq_id(bit [irq_agent_pkg::DATA_WIDTH-1:0] irq);
|
||||
int i;
|
||||
// Ibex implementation of MIE does not mask NM interrupts, so need to check this separately
|
||||
|
@ -288,6 +313,7 @@ class core_ibex_debug_intr_basic_test extends core_ibex_base_test;
|
|||
bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] mcause;
|
||||
wait_for_csr_write(CSR_MCAUSE, 1000);
|
||||
mcause = signature_data;
|
||||
`uvm_info(`gfn, $sformatf("mcause: 0x%0x", mcause), UVM_LOW)
|
||||
`DV_CHECK_EQ_FATAL(mcause[ibex_mem_intf_agent_pkg::DATA_WIDTH-1], irq_or_exc,
|
||||
$sformatf("mcause.interrupt is not set to 0x%0x", irq_or_exc))
|
||||
`DV_CHECK_EQ_FATAL(mcause[ibex_mem_intf_agent_pkg::DATA_WIDTH-2:0], cause,
|
||||
|
@ -328,7 +354,7 @@ class core_ibex_debug_intr_basic_test extends core_ibex_base_test;
|
|||
`uvm_fatal(`gfn, $sformatf("Invalid xRET instruction %0s", ret))
|
||||
end
|
||||
endcase
|
||||
wait (dut_vif.priv_mode === operating_mode);
|
||||
wait (dut_vif.priv_mode === select_mode());
|
||||
end
|
||||
begin : ret_timeout
|
||||
clk_vif.wait_clks(timeout);
|
||||
|
@ -521,6 +547,36 @@ class core_ibex_debug_irq_test extends core_ibex_directed_test;
|
|||
|
||||
endclass
|
||||
|
||||
// Nested interrupt test class (with multiple interrupts)
|
||||
class core_ibex_nested_irq_test extends core_ibex_directed_test;
|
||||
|
||||
`uvm_component_utils(core_ibex_nested_irq_test)
|
||||
`uvm_component_new
|
||||
|
||||
virtual task check_stimulus();
|
||||
bit valid_irq;
|
||||
bit valid_nested_irq;
|
||||
int unsigned initial_irq_delay;
|
||||
forever begin
|
||||
send_irq_stimulus_start(1'b1, valid_irq);
|
||||
if (valid_irq) begin
|
||||
initial_irq_delay = vseq.irq_raise_seq_h.max_delay;
|
||||
vseq.irq_raise_seq_h.max_delay = 0;
|
||||
// Send nested interrupt after the checks of the first interrupt have finished
|
||||
in_nested_trap = 1'b1;
|
||||
// wait until we are setting mstatus.mie to 1'b1 to send the next set of interrupts
|
||||
wait(csr_vif.csr_access === 1'b1 && csr_vif.csr_addr === CSR_MSTATUS &&
|
||||
csr_vif.csr_op != CSR_OP_READ);
|
||||
send_irq_stimulus(1'b0);
|
||||
vseq.irq_raise_seq_h.max_delay = initial_irq_delay;
|
||||
in_nested_trap = 1'b0;
|
||||
send_irq_stimulus_end();
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
// Debug WFI test class
|
||||
class core_ibex_debug_wfi_test extends core_ibex_directed_test;
|
||||
|
||||
|
@ -543,7 +599,7 @@ class core_ibex_debug_wfi_test extends core_ibex_directed_test;
|
|||
// debug rom.
|
||||
// We also want to check that dcsr.cause has been set correctly
|
||||
wait_for_csr_write(CSR_DCSR, 500);
|
||||
check_dcsr_prv(operating_mode);
|
||||
check_dcsr_prv(init_operating_mode);
|
||||
check_dcsr_cause(DBG_CAUSE_HALTREQ);
|
||||
wait_ret("dret", 5000);
|
||||
end
|
||||
|
@ -567,7 +623,7 @@ class core_ibex_debug_csr_test extends core_ibex_directed_test;
|
|||
1000);
|
||||
check_priv_mode(PRIV_LVL_M);
|
||||
wait_for_csr_write(CSR_DCSR, 500);
|
||||
check_dcsr_prv(operating_mode);
|
||||
check_dcsr_prv(init_operating_mode);
|
||||
check_dcsr_cause(DBG_CAUSE_HALTREQ);
|
||||
wait_ret("dret", 5000);
|
||||
// wait for a dummy write to mie in the init code
|
||||
|
@ -578,7 +634,7 @@ class core_ibex_debug_csr_test extends core_ibex_directed_test;
|
|||
1000);
|
||||
check_priv_mode(PRIV_LVL_M);
|
||||
wait_for_csr_write(CSR_DCSR, 500);
|
||||
check_dcsr_prv(operating_mode);
|
||||
check_dcsr_prv(init_operating_mode);
|
||||
check_dcsr_cause(DBG_CAUSE_HALTREQ);
|
||||
wait_ret("dret", 5000);
|
||||
endtask
|
||||
|
@ -616,7 +672,7 @@ class core_ibex_debug_ebreak_test extends core_ibex_directed_test;
|
|||
// capture the first write of dcsr
|
||||
check_priv_mode(PRIV_LVL_M);
|
||||
wait_for_csr_write(CSR_DCSR, 500);
|
||||
check_dcsr_prv(operating_mode);
|
||||
check_dcsr_prv(init_operating_mode);
|
||||
dcsr = signature_data;
|
||||
// We also want to check that dcsr.cause has been set correctly
|
||||
check_dcsr_cause(DBG_CAUSE_HALTREQ);
|
||||
|
@ -653,7 +709,7 @@ class core_ibex_debug_ebreakmu_test extends core_ibex_directed_test;
|
|||
// 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, 500);
|
||||
check_dcsr_prv(operating_mode);
|
||||
check_dcsr_prv(init_operating_mode);
|
||||
check_dcsr_ebreak();
|
||||
check_dcsr_cause(DBG_CAUSE_HALTREQ);
|
||||
wait_ret("dret", 5000);
|
||||
|
@ -664,7 +720,7 @@ class core_ibex_debug_ebreakmu_test extends core_ibex_directed_test;
|
|||
check_priv_mode(PRIV_LVL_M);
|
||||
// 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, 500);
|
||||
check_dcsr_prv(operating_mode);
|
||||
check_dcsr_prv(init_operating_mode);
|
||||
check_dcsr_ebreak();
|
||||
check_dcsr_cause(DBG_CAUSE_EBREAK);
|
||||
wait_ret("dret", 5000);
|
||||
|
@ -693,7 +749,7 @@ class core_ibex_debug_single_step_test extends core_ibex_directed_test;
|
|||
wait_for_csr_write(CSR_DSCRATCH0, 500);
|
||||
next_counter = signature_data;
|
||||
wait_for_csr_write(CSR_DCSR, 1000);
|
||||
check_dcsr_prv(operating_mode);
|
||||
check_dcsr_prv(init_operating_mode);
|
||||
check_dcsr_cause(DBG_CAUSE_HALTREQ);
|
||||
`DV_CHECK_EQ_FATAL(signature_data[2], 1'b1, "dcsr.step is not set")
|
||||
wait_ret("dret", 5000);
|
||||
|
@ -714,7 +770,7 @@ class core_ibex_debug_single_step_test extends core_ibex_directed_test;
|
|||
wait_for_csr_write(CSR_DSCRATCH0, 500);
|
||||
next_counter = signature_data;
|
||||
wait_for_csr_write(CSR_DCSR, 500);
|
||||
check_dcsr_prv(operating_mode);
|
||||
check_dcsr_prv(init_operating_mode);
|
||||
check_dcsr_cause(DBG_CAUSE_STEP);
|
||||
if (counter === 0) begin
|
||||
`DV_CHECK_EQ_FATAL(signature_data[2], 1'b0, "dcsr.step is set")
|
||||
|
@ -854,7 +910,7 @@ class core_ibex_invalid_csr_test extends core_ibex_directed_test;
|
|||
// Wait for a CSR access
|
||||
wait (csr_vif.csr_access == 1'b1);
|
||||
check_illegal_insn($sformatf("Core did not treat access to CSR 0x%0x from %0s as illegal",
|
||||
csr_vif.csr_addr, operating_mode));
|
||||
csr_vif.csr_addr, init_operating_mode));
|
||||
end
|
||||
endtask
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class core_ibex_vseq extends uvm_sequence;
|
|||
irq_drop_seq_h = irq_drop_seq::type_id::create("irq_drop_seq_h");
|
||||
irq_drop_seq_h.num_of_iterations = 1;
|
||||
irq_drop_seq_h.max_interval = 1;
|
||||
irq_drop_seq_h.max_delay = 1;
|
||||
irq_drop_seq_h.max_delay = 0;
|
||||
irq_drop_seq_h.interval = 0;
|
||||
end
|
||||
if (cfg.enable_debug_seq) begin
|
||||
|
@ -93,11 +93,13 @@ class core_ibex_vseq extends uvm_sequence;
|
|||
debug_seq_single_h.start(null);
|
||||
endtask
|
||||
|
||||
virtual task start_irq_raise_single_seq();
|
||||
virtual task start_irq_raise_single_seq(bit no_nmi = 1'b0);
|
||||
irq_raise_single_seq_h.no_nmi = no_nmi;
|
||||
irq_raise_single_seq_h.start(p_sequencer.irq_seqr);
|
||||
endtask
|
||||
|
||||
virtual task start_irq_raise_seq();
|
||||
virtual task start_irq_raise_seq(bit no_nmi = 1'b0);
|
||||
irq_raise_seq_h.no_nmi = no_nmi;
|
||||
irq_raise_seq_h.start(p_sequencer.irq_seqr);
|
||||
endtask
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue