diff --git a/dv/uvm/env/core_ibex_env_cfg.sv b/dv/uvm/env/core_ibex_env_cfg.sv index 06f99852..4152230d 100644 --- a/dv/uvm/env/core_ibex_env_cfg.sv +++ b/dv/uvm/env/core_ibex_env_cfg.sv @@ -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)); diff --git a/dv/uvm/riscv_dv_extension/testlist.yaml b/dv/uvm/riscv_dv_extension/testlist.yaml index 351062a1..ac6104d4 100644 --- a/dv/uvm/riscv_dv_extension/testlist.yaml +++ b/dv/uvm/riscv_dv_extension/testlist.yaml @@ -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. diff --git a/dv/uvm/tests/core_ibex_seq_lib.sv b/dv/uvm/tests/core_ibex_seq_lib.sv index 165d1581..a90b6452 100644 --- a/dv/uvm/tests/core_ibex_seq_lib.sv +++ b/dv/uvm/tests/core_ibex_seq_lib.sv @@ -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 diff --git a/dv/uvm/tests/core_ibex_test_lib.sv b/dv/uvm/tests/core_ibex_test_lib.sv index efc594fe..daf26f31 100644 --- a/dv/uvm/tests/core_ibex_test_lib.sv +++ b/dv/uvm/tests/core_ibex_test_lib.sv @@ -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 diff --git a/dv/uvm/tests/core_ibex_vseq.sv b/dv/uvm/tests/core_ibex_vseq.sv index d8d91343..951bf3d8 100644 --- a/dv/uvm/tests/core_ibex_vseq.sv +++ b/dv/uvm/tests/core_ibex_vseq.sv @@ -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