mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-22 04:47:25 -04:00
Add interrupt testing, and update some debug test checks (#324)
This commit is contained in:
parent
369f56bad0
commit
80e231dd8b
15 changed files with 225 additions and 76 deletions
|
@ -10,6 +10,9 @@ class ibex_mem_intf_slave_driver extends uvm_driver #(ibex_mem_intf_seq_item);
|
|||
|
||||
protected virtual ibex_mem_intf vif;
|
||||
|
||||
int unsigned min_grant_delay = 0;
|
||||
int unsigned max_grant_delay = 10;
|
||||
|
||||
`uvm_component_utils(ibex_mem_intf_slave_driver)
|
||||
`uvm_component_new
|
||||
|
||||
|
@ -55,15 +58,20 @@ class ibex_mem_intf_slave_driver extends uvm_driver #(ibex_mem_intf_seq_item);
|
|||
join_none
|
||||
endtask : get_and_drive
|
||||
|
||||
// TODO(udinator) - this direct send_grant logic is temporary until instruction fetch protocol
|
||||
// issue is clarified (https://github.com/lowRISC/ibex/pull/293). After resolution, will re-add
|
||||
// random delays insertion before driving grant to the ibex core.
|
||||
virtual protected task send_grant();
|
||||
int gnt_delay;
|
||||
forever begin
|
||||
while(vif.request !== 1'b1) begin
|
||||
@(negedge vif.clock);
|
||||
end
|
||||
std::randomize(gnt_delay) with {
|
||||
gnt_delay dist {
|
||||
min_grant_delay :/ 1,
|
||||
[min_grant_delay+1 : max_grant_delay-1] :/ 1,
|
||||
max_grant_delay :/ 1
|
||||
};
|
||||
};
|
||||
repeat(gnt_delay) @(negedge vif.clock);
|
||||
if(~vif.reset) begin
|
||||
vif.grant = 1'b1;
|
||||
@(negedge vif.clock);
|
||||
|
|
|
@ -52,11 +52,6 @@ class irq_master_driver extends uvm_driver #(irq_seq_item);
|
|||
vif.irq_external <= trans.irq_external;
|
||||
vif.irq_fast <= trans.irq_fast;
|
||||
vif.irq_nm <= trans.irq_nm;
|
||||
// We hold the interrupt high for two cycles as Ibex is level sensitive,
|
||||
// so this guarantees that Ibex will respond appropriately to the
|
||||
// interrupt
|
||||
repeat (2) @(posedge vif.clock);
|
||||
drive_reset_value();
|
||||
endtask : drive_seq_item
|
||||
|
||||
task drive_reset_value();
|
||||
|
|
|
@ -25,11 +25,23 @@ class irq_monitor extends uvm_monitor;
|
|||
collect_irq();
|
||||
endtask : run_phase
|
||||
|
||||
// We know that for Ibex, any given interrupt stimulus will be asserted until the core signals the
|
||||
// testbench that it has finished handling, and this stimulus will not change until the testbench
|
||||
// receives the signal, at which point it will drop.
|
||||
// Given this, as well as how the interrupt handshakes are designed, sending an irq_seq_item every
|
||||
// cycle is not useful at all.
|
||||
// In order to not send unnecessary sequence items, but to also send enough information that the
|
||||
// testbench can handle nested interrupt scenarios, the monitor will send out a sequence
|
||||
// item every time the interrupt lines change.
|
||||
virtual protected task collect_irq();
|
||||
irq_seq_item irq;
|
||||
bit[DATA_WIDTH-1:0] stored_irq_val = '0;
|
||||
bit[DATA_WIDTH-1:0] current_irq = '0;
|
||||
forever begin
|
||||
if (|{vif.irq_software, vif.irq_timer, vif.irq_external,
|
||||
vif.irq_fast, vif.irq_nm}) begin
|
||||
current_irq = {vif.irq_nm, vif.irq_fast, 4'b0, vif.irq_external, 3'b0,
|
||||
vif.irq_timer, 3'b0, vif.irq_software, 3'b0};
|
||||
if (current_irq !== stored_irq_val) begin
|
||||
stored_irq_val = current_irq;
|
||||
irq = irq_seq_item::type_id::create("irq");
|
||||
irq.irq_software = vif.irq_software;
|
||||
irq.irq_timer = vif.irq_timer;
|
||||
|
|
|
@ -12,7 +12,7 @@ class irq_seq_item extends uvm_sequence_item;
|
|||
rand int num_of_interrupt;
|
||||
|
||||
constraint num_of_interrupt_c {
|
||||
num_of_interrupt inside {[1:19]};
|
||||
num_of_interrupt inside {[0:DATA_WIDTH-1]};
|
||||
$countones({irq_software, irq_timer, irq_external, irq_fast, irq_nm}) == num_of_interrupt;
|
||||
}
|
||||
|
||||
|
|
1
dv/uvm/env/core_ibex_dut_probe_if.sv
vendored
1
dv/uvm/env/core_ibex_dut_probe_if.sv
vendored
|
@ -11,5 +11,6 @@ interface core_ibex_dut_probe_if(input logic clk);
|
|||
logic dret;
|
||||
logic mret;
|
||||
logic fetch_enable;
|
||||
logic core_sleep;
|
||||
logic debug_req;
|
||||
endinterface
|
||||
|
|
4
dv/uvm/env/core_ibex_env.sv
vendored
4
dv/uvm/env/core_ibex_env.sv
vendored
|
@ -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_stress_seq || cfg.enable_irq_single_seq) begin
|
||||
if (cfg.enable_irq_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_stress_seq || cfg.enable_irq_single_seq) begin
|
||||
if (cfg.enable_irq_seq) begin
|
||||
vseqr.irq_seqr = irq_agent.sequencer;
|
||||
end
|
||||
endfunction : connect_phase
|
||||
|
|
9
dv/uvm/env/core_ibex_env_cfg.sv
vendored
9
dv/uvm/env/core_ibex_env_cfg.sv
vendored
|
@ -4,8 +4,7 @@
|
|||
|
||||
class core_ibex_env_cfg extends uvm_object;
|
||||
|
||||
bit enable_irq_stress_seq;
|
||||
bit enable_irq_single_seq;
|
||||
bit enable_irq_seq;
|
||||
bit enable_debug_stress_seq;
|
||||
bit enable_debug_single_seq;
|
||||
bit[31:0] max_interval;
|
||||
|
@ -13,8 +12,7 @@ class core_ibex_env_cfg extends uvm_object;
|
|||
bit[31:0] signature_addr;
|
||||
|
||||
`uvm_object_utils_begin(core_ibex_env_cfg)
|
||||
`uvm_field_int(enable_irq_stress_seq, UVM_DEFAULT)
|
||||
`uvm_field_int(enable_irq_single_seq, UVM_DEFAULT)
|
||||
`uvm_field_int(enable_irq_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)
|
||||
|
@ -24,8 +22,7 @@ class core_ibex_env_cfg extends uvm_object;
|
|||
|
||||
function new(string name = "");
|
||||
super.new(name);
|
||||
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_irq_seq=%0d", enable_irq_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));
|
||||
|
|
|
@ -37,6 +37,10 @@ riscv_instr_group_t supported_isa[$] = {RV32I, RV32M, RV32C};
|
|||
// Interrupt mode support
|
||||
mtvec_mode_t supported_interrupt_mode[$] = {VECTORED};
|
||||
|
||||
// The number of interrupt vectors to be generated, only used if VECTORED interrupt mode is
|
||||
// supported
|
||||
int max_interrupt_vector_num = 32;
|
||||
|
||||
// Debug mode support
|
||||
bit support_debug_mode = 1;
|
||||
|
||||
|
@ -46,8 +50,6 @@ bit support_umode_trap = 0;
|
|||
// Support sfence.vma instruction
|
||||
bit support_sfence = 0;
|
||||
|
||||
int max_interrupt_vector_num = 32;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Kernel section setting, used by supervisor mode programs
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -201,16 +201,6 @@
|
|||
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,
|
||||
|
@ -259,14 +249,15 @@
|
|||
- test: riscv_interrupt_test
|
||||
description: >
|
||||
Random instruction test with complete interrupt handling
|
||||
iterations: 0
|
||||
iterations: 10
|
||||
gen_test: riscv_rand_instr_test
|
||||
gen_opts: >
|
||||
+require_signature_addr=1
|
||||
+enable_interrupt=1
|
||||
rtl_test: core_ibex_debug_intr_basic_test
|
||||
sim_opts: >
|
||||
+require_signature_addr=1
|
||||
+enable_irq_stress_seq=1
|
||||
+enable_irq_seq=1
|
||||
compare_opts:
|
||||
compare_final_value_only: 1
|
||||
|
||||
|
|
|
@ -143,6 +143,7 @@ def rtl_sim(sim_cmd, test_list, output_dir, bin_dir, lsf_cmd, seed, opts):
|
|||
rand_seed = get_seed(seed)
|
||||
test_sim_cmd = re.sub("<seed>", str(rand_seed), sim_cmd)
|
||||
if "sim_opts" in test:
|
||||
test_sim_cmd += ' '
|
||||
test_sim_cmd += test['sim_opts']
|
||||
sim_dir = output_dir + ("/%s.%d" %(test['test'], i))
|
||||
run_cmd(("mkdir -p %s" % sim_dir))
|
||||
|
|
|
@ -68,11 +68,12 @@ 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.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;
|
||||
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;
|
||||
assign dut_if.core_sleep = dut.u_ibex_core.core_sleep_o;
|
||||
|
||||
|
||||
initial begin
|
||||
|
|
|
@ -10,7 +10,6 @@ class core_ibex_base_test extends uvm_test;
|
|||
virtual core_ibex_dut_probe_if dut_vif;
|
||||
mem_model_pkg::mem_model mem;
|
||||
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 specifield number of cycles before starting stimulus
|
||||
|
@ -19,6 +18,7 @@ class core_ibex_base_test extends uvm_test;
|
|||
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_tlm_analysis_fifo #(irq_seq_item) irq_collected_port;
|
||||
|
||||
`uvm_component_utils(core_ibex_base_test)
|
||||
|
||||
|
@ -28,6 +28,7 @@ class core_ibex_base_test extends uvm_test;
|
|||
ibex_report_server = new();
|
||||
uvm_report_server::set_server(ibex_report_server);
|
||||
item_collected_port = new("item_collected_port_test", this);
|
||||
irq_collected_port = new("irq_collected_port_test", this);
|
||||
endfunction
|
||||
|
||||
virtual function void build_phase(uvm_phase phase);
|
||||
|
@ -52,6 +53,9 @@ class core_ibex_base_test extends uvm_test;
|
|||
virtual function void connect_phase(uvm_phase phase);
|
||||
super.connect_phase(phase);
|
||||
env.data_if_slave_agent.monitor.item_collected_port.connect(this.item_collected_port.analysis_export);
|
||||
if (cfg.enable_irq_seq) begin
|
||||
env.irq_agent.monitor.irq_port.connect(this.irq_collected_port.analysis_export);
|
||||
end
|
||||
endfunction
|
||||
|
||||
virtual task run_phase(uvm_phase phase);
|
||||
|
@ -165,7 +169,7 @@ class core_ibex_base_test extends uvm_test;
|
|||
|
||||
// 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="");
|
||||
virtual task check_next_core_status(core_status_t core_status, string error_msg="");
|
||||
wait_for_mem_txn(cfg.signature_addr, CORE_STATUS);
|
||||
signature_data = signature_data_q.pop_front();
|
||||
if (signature_data != core_status) begin
|
||||
|
|
|
@ -61,17 +61,38 @@ class core_base_seq #(type REQ = uvm_sequence_item) extends uvm_sequence#(REQ);
|
|||
|
||||
endclass
|
||||
|
||||
// Interrupt sequence
|
||||
class irq_seq extends core_base_seq#(irq_seq_item);
|
||||
// Interrupt sequences
|
||||
class irq_raise_single_seq extends core_base_seq#(irq_seq_item);
|
||||
|
||||
`uvm_object_utils(irq_seq)
|
||||
`uvm_object_utils(irq_raise_single_seq)
|
||||
`uvm_object_new
|
||||
|
||||
virtual task send_req();
|
||||
irq_seq_item irq;
|
||||
irq = irq_seq_item::type_id::create($sformatf("irq[%0d]", iteration_cnt));
|
||||
irq = irq_seq_item::type_id::create($sformatf("irq_raise_single[%0d]", iteration_cnt));
|
||||
start_item(irq);
|
||||
`DV_CHECK_RANDOMIZE_FATAL(irq)
|
||||
// TODO(udinator) - constrain irq_timer to 0 for now due to timer interrupt causing spike
|
||||
// simulator to trap to irq handler
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(irq, irq_timer==0;)
|
||||
finish_item(irq);
|
||||
get_response(irq);
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
// Irq sequence to deassert all interrupt lines, since Ibex interrupts are level sensitive
|
||||
class irq_drop_seq extends core_base_seq#(irq_seq_item);
|
||||
|
||||
`uvm_object_utils(irq_drop_seq)
|
||||
`uvm_object_new
|
||||
|
||||
// TODO(udinator) - for nested interrupt tests, test scenarios where a random number of interrupts
|
||||
// are dropped
|
||||
virtual task send_req();
|
||||
irq_seq_item irq;
|
||||
irq = irq_seq_item::type_id::create($sformatf("irq_drop[%0d]", iteration_cnt));
|
||||
start_item(irq);
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt == 0;)
|
||||
finish_item(irq);
|
||||
get_response(irq);
|
||||
endtask
|
||||
|
|
|
@ -37,6 +37,10 @@ class core_ibex_debug_intr_basic_test extends core_ibex_base_test;
|
|||
`uvm_component_utils(core_ibex_debug_intr_basic_test)
|
||||
`uvm_component_new
|
||||
|
||||
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;
|
||||
bit [$clog2(irq_agent_pkg::DATA_WIDTH)-1:0] irq_id;
|
||||
|
||||
virtual task send_stimulus();
|
||||
fork
|
||||
begin
|
||||
|
@ -44,7 +48,7 @@ class core_ibex_debug_intr_basic_test extends core_ibex_base_test;
|
|||
end
|
||||
begin
|
||||
if (cfg.require_signature_addr) begin
|
||||
wait_for_core_status(INITIALIZED);
|
||||
wait_for_core_setup();
|
||||
end else begin
|
||||
// If no signature_addr functionality is desired, then the test will simply wait for an
|
||||
// adequate number of cycles
|
||||
|
@ -52,13 +56,15 @@ class core_ibex_debug_intr_basic_test extends core_ibex_base_test;
|
|||
end
|
||||
fork
|
||||
begin
|
||||
if (cfg.enable_irq_stress_seq) begin
|
||||
vseq.start_irq_stress_seq();
|
||||
if (cfg.enable_irq_seq) begin
|
||||
forever begin
|
||||
send_irq_stimulus();
|
||||
end
|
||||
end
|
||||
end
|
||||
begin
|
||||
if (cfg.enable_debug_stress_seq) begin
|
||||
vseq.start_debug_stress_seq();
|
||||
send_debug_stimulus();
|
||||
end
|
||||
end
|
||||
join_none
|
||||
|
@ -66,10 +72,119 @@ class core_ibex_debug_intr_basic_test extends core_ibex_base_test;
|
|||
join_none
|
||||
endtask
|
||||
|
||||
virtual task wait_for_core_setup();
|
||||
wait_for_csr_write(CSR_MSTATUS);
|
||||
core_init_mstatus = signature_data;
|
||||
wait_for_csr_write(CSR_MIE);
|
||||
core_init_mie = signature_data;
|
||||
check_next_core_status(INITIALIZED, "Core initialization handshake failure");
|
||||
endtask
|
||||
|
||||
// TODO(udi) - much of this checking logic is based on the current design only implementing
|
||||
// MACHINE_MODE, the checking will have to be modified once USER_MODE is implemented and merged,
|
||||
// e.g. need to also check mideleg for correct privilege mode context switch
|
||||
virtual task send_irq_stimulus();
|
||||
irq_seq_item irq_txn;
|
||||
bit [irq_agent_pkg::DATA_WIDTH-1:0] irq;
|
||||
bit [ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] mstatus;
|
||||
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;
|
||||
// send the interrupt
|
||||
vseq.start_irq_single_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};
|
||||
// Get the bit position of the highest priority interrupt - ibex will only handle this one if
|
||||
// there are multiple irqs asserted at once
|
||||
irq_id = get_max_irq_id(irq);
|
||||
// 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
|
||||
if (!irq_txn.irq_nm && !core_init_mie[irq_id]) begin
|
||||
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")
|
||||
return;
|
||||
end
|
||||
check_next_core_status(HANDLING_IRQ, "Core did not jump to vectored interrupt handler");
|
||||
// check mstatus
|
||||
wait_for_csr_write(CSR_MSTATUS);
|
||||
mstatus = signature_data;
|
||||
`DV_CHECK_EQ_FATAL(mstatus[12:11], PRIV_LVL_M, "Incorrect privilege mode")
|
||||
`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
|
||||
wait_for_csr_write(CSR_MCAUSE);
|
||||
mcause = signature_data;
|
||||
// check that mcause.interrupt is set
|
||||
`DV_CHECK_EQ_FATAL(mcause[ibex_mem_intf_agent_pkg::DATA_WIDTH-1], 1'b1,
|
||||
"mcause.interrupt is not set to 1'b1")
|
||||
// check that mcause.exception_code matches the current interrupt's ID
|
||||
`DV_CHECK_EQ_FATAL(mcause[ibex_mem_intf_agent_pkg::DATA_WIDTH-2:0], irq_id,
|
||||
"mcause.exception_code is encoding the wrong interrupt type")
|
||||
// Wait for MIE and MIP to be written regardless of what interrupt ibex is dealing with, to
|
||||
// prevent the case where MIP/MIE stays at 0 due to a nonmaskable interrupt, which will falsely
|
||||
// trigger the following call of check_next_core_status()
|
||||
wait_for_csr_write(CSR_MIE);
|
||||
mie = signature_data;
|
||||
wait_for_csr_write(CSR_MIP);
|
||||
mip = signature_data;
|
||||
// only check mip, and mie if the interrupt is not irq_nm, as Ibex's implementation of MIP and
|
||||
// MIE CSRs do not contain a bit for irq_nm
|
||||
if (!irq_txn.irq_nm) begin
|
||||
// check that the proper bit in MIE is high
|
||||
`DV_CHECK_EQ_FATAL(mie[irq_id], 1'b1,
|
||||
$sformatf("mie[%0d] is not set, but core responded to corresponding interrupt", irq_id))
|
||||
// check that the proper bit in MIP is high
|
||||
`DV_CHECK_EQ_FATAL(mip[irq_id], 1'b1,
|
||||
$sformatf("mip[%0d] is not set, but core responded to corresponding interrupt", irq_id))
|
||||
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");
|
||||
// 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")
|
||||
wait (dut_vif.mret === 1'b1);
|
||||
endtask
|
||||
|
||||
function int get_max_irq_id(bit [irq_agent_pkg::DATA_WIDTH-1:0] irq);
|
||||
int i;
|
||||
for (i = irq_agent_pkg::DATA_WIDTH-1; i >= 0; i = i - 1) begin
|
||||
if (irq[i] === 1'b1) begin
|
||||
return i;
|
||||
break;
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Basic debug stimulus check for Ibex for debug stimulus stress tests: check that Ibex enters
|
||||
// debug mode properly after stimulus is sent and then check that a dret is encountered signifying
|
||||
// the end of debug mode.
|
||||
virtual task send_debug_stimulus();
|
||||
fork
|
||||
begin
|
||||
vseq.start_debug_stress_seq();
|
||||
end
|
||||
begin
|
||||
forever begin
|
||||
check_next_core_status(IN_DEBUG_MODE, "Core did not enter debug mode properly");
|
||||
wait(dut_vif.dret === 1'b1);
|
||||
end
|
||||
end
|
||||
join_none
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
// Base class for directed debug and irq test scenarios
|
||||
class core_ibex_directed_test extends core_ibex_base_test;
|
||||
class core_ibex_directed_test extends core_ibex_debug_intr_basic_test;
|
||||
|
||||
`uvm_component_utils(core_ibex_directed_test)
|
||||
`uvm_component_new
|
||||
|
@ -84,20 +199,22 @@ class core_ibex_directed_test extends core_ibex_base_test;
|
|||
clk_vif.wait_clks(stimulus_delay);
|
||||
fork
|
||||
begin
|
||||
if (cfg.enable_irq_stress_seq) begin
|
||||
vseq.start_irq_stress_seq();
|
||||
if (cfg.enable_irq_seq) begin
|
||||
forever begin
|
||||
send_irq_stimulus();
|
||||
end
|
||||
end
|
||||
end
|
||||
begin
|
||||
if (cfg.enable_debug_stress_seq) begin
|
||||
vseq.start_debug_stress_seq();
|
||||
send_debug_stimulus();
|
||||
end
|
||||
end
|
||||
join_none
|
||||
end else begin
|
||||
// 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");
|
||||
wait_for_core_setup();
|
||||
// Should be extended by derived classes.
|
||||
// DO NOT use this test class directly.
|
||||
check_stimulus();
|
||||
|
|
|
@ -11,8 +11,8 @@ 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_stress_h;
|
||||
irq_seq irq_seq_single_h;
|
||||
irq_raise_single_seq irq_single_seq_h;
|
||||
irq_drop_seq irq_drop_seq_h;
|
||||
debug_seq debug_seq_stress_h;
|
||||
debug_seq debug_seq_single_h;
|
||||
core_ibex_env_cfg cfg;
|
||||
|
@ -25,17 +25,18 @@ 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_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_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;
|
||||
if (cfg.enable_irq_seq) begin
|
||||
irq_single_seq_h = irq_raise_single_seq::type_id::create("irq_seq_single_h");
|
||||
irq_single_seq_h.num_of_iterations = 1;
|
||||
irq_single_seq_h.max_interval = 1;
|
||||
irq_single_seq_h.max_delay = 500;
|
||||
irq_single_seq_h.interval = 0;
|
||||
|
||||
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.interval = 0;
|
||||
end
|
||||
if (cfg.enable_debug_stress_seq) begin
|
||||
debug_seq_stress_h = debug_seq::type_id::create("debug_seq_stress_h");
|
||||
|
@ -58,11 +59,9 @@ class core_ibex_vseq extends uvm_sequence;
|
|||
endtask
|
||||
|
||||
virtual task stop();
|
||||
if (cfg.enable_irq_stress_seq) begin
|
||||
irq_seq_stress_h.stop();
|
||||
end
|
||||
if (cfg.enable_irq_single_seq) begin
|
||||
irq_seq_single_h.stop();
|
||||
if (cfg.enable_irq_seq) begin
|
||||
irq_single_seq_h.stop();
|
||||
irq_drop_seq_h.stop();
|
||||
end
|
||||
if (cfg.enable_debug_stress_seq) begin
|
||||
debug_seq_stress_h.stop();
|
||||
|
@ -82,12 +81,12 @@ class core_ibex_vseq extends uvm_sequence;
|
|||
debug_seq_single_h.start(null);
|
||||
endtask
|
||||
|
||||
virtual task start_irq_stress_seq();
|
||||
irq_seq_stress_h.start(p_sequencer.irq_seqr);
|
||||
virtual task start_irq_single_seq();
|
||||
irq_single_seq_h.start(p_sequencer.irq_seqr);
|
||||
endtask
|
||||
|
||||
virtual task start_irq_single_seq();
|
||||
irq_seq_single_h.start(p_sequencer.irq_seqr);
|
||||
virtual task start_irq_drop_seq();
|
||||
irq_drop_seq_h.start(p_sequencer.irq_seqr);
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue