[DV] Test nested interrupts (#560)

Signed-off-by: Udi <udij@google.com>
This commit is contained in:
udinator 2020-01-23 15:11:54 -08:00 committed by GitHub
parent 3d8089c235
commit f339f6b96b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 108 additions and 25 deletions

View file

@ -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));

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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