Skip to content

Commit

Permalink
Interrupt verif : Implement clear mechanism in interrupt's agent (#2527)
Browse files Browse the repository at this point in the history
* INTERRUPT VERIF : Implement interrupt clear mechanism

* Interrupt Verif : Add irq_timeout to exit when we failed to write into irq_add

Also change uvm_warining to uvm_info

* Fix comment
  • Loading branch information
AyoubJalali authored Oct 16, 2024
1 parent 1de0da8 commit 7394941
Show file tree
Hide file tree
Showing 21 changed files with 415 additions and 286 deletions.
113 changes: 84 additions & 29 deletions verif/env/corev-dv/cva6_asm_program_gen.sv
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
end else begin
// Push user mode GPR to kernel stack before executing exception handling, this is to avoid
// exception handling routine modify user program state unexpectedly
push_used_gpr_to_kernel_stack(status, scratch, 4, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr);
push_used_gpr_to_kernel_stack(status, scratch, 3, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr);
// Checking xStatus can be optional if ISS (like spike) has different implementation of
// certain fields compared with the RTL processor.
if (cfg_cva6.check_xstatus) begin
Expand All @@ -187,16 +187,19 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
// Check if the exception is caused by an interrupt, if yes, jump to interrupt
// handler Interrupt is indicated by xCause[XLEN-1]
$sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], cause, cause.name()),
$sformatf("srli x%0d, x%0d, %0d", cfg_cva6.gpr[0], cfg_cva6.gpr[0], XLEN-1),
$sformatf("bne x%0d, x0, %0s%0s_intr_handler",
cfg_cva6.gpr[0], hart_prefix(hart), mode),
$sformatf("csrr x%0d, mepc", cfg_cva6.gpr[0]),
$sformatf("lbu x%0d, 0(x%0d)", cfg_cva6.gpr[3],cfg_cva6.gpr[0]),
$sformatf("lbu x%0d, 0(x%0d)", cfg_cva6.gpr[2],cfg_cva6.gpr[0]),
$sformatf("li x%0d, 0x3", cfg_cva6.gpr[1]),
$sformatf("and x%0d, x%0d, x%0d", cfg_cva6.gpr[3], cfg_cva6.gpr[3], cfg_cva6.gpr[1]),
$sformatf("bne x%0d, x%0d, exception_handler_incr_mepc2", cfg_cva6.gpr[3], cfg_cva6.gpr[1]),
$sformatf("and x%0d, x%0d, x%0d", cfg_cva6.gpr[2], cfg_cva6.gpr[2], cfg_cva6.gpr[1]),
$sformatf("bne x%0d, x%0d, exception_handler_incr_mepc2", cfg_cva6.gpr[2], cfg_cva6.gpr[1]),
$sformatf("addi x%0d, x%0d, 2", cfg_cva6.gpr[0], cfg_cva6.gpr[0]),
str,
$sformatf("addi x%0d, x%0d, 2", cfg_cva6.gpr[0], cfg_cva6.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg_cva6.gpr[0])};
pop_used_gpr_from_kernel_stack(MSTATUS, MSCRATCH, 4, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
pop_used_gpr_from_kernel_stack(MSTATUS, MSCRATCH, 3, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr);
instr.push_back("mret");
end
// The trap handler will occupy one 4KB page, it will be allocated one entry in the page table
Expand All @@ -213,7 +216,49 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
if (cfg_cva6.mtvec_mode == VECTORED) begin
push_gpr_to_kernel_stack(status, scratch, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr);
end
gen_signature_handshake(instr, CORE_STATUS, HANDLING_EXCEPTION);
//~ push_used_gpr_to_kernel_stack(status, scratch, 3, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr);
instr = {instr,
// The trap is caused by an exception, read back xCAUSE, xEPC to see if these
// CSR values are set properly.
$sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], epc, epc.name()),
$sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], cause, cause.name()),
$sformatf("li x%0d, 0x8000000b", cfg_cva6.gpr[1]),
$sformatf("li x%0d, 0x80000007", cfg_cva6.gpr[2]),
$sformatf("beq x%0d, x%0d, ext_interrupt_handler", cfg_cva6.gpr[0], cfg_cva6.gpr[1]),
$sformatf("beq x%0d, x%0d, timer_interrupt_handler", cfg_cva6.gpr[0], cfg_cva6.gpr[2]),
$sformatf("j test_done")
};
gen_section(get_label($sformatf("%0s_intr_handler", mode), hart), instr);

instr = {};
instr = {instr,
// The trap is caused by an external interrupt, read back xIP
// Write into int_ack 0x1 value
$sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], epc, ip.name()),
$sformatf("li x%0d, 0", cfg_cva6.gpr[0]),
$sformatf("addi x%0d, x%0d, 1", cfg_cva6.gpr[0], cfg_cva6.gpr[0]),
// Clean external pending interrupt
$sformatf("sw x%0d, int_ack, x%0d # %0s;",
cfg_cva6.gpr[0], cfg_cva6.gpr[1], ip.name())
};
pop_used_gpr_from_kernel_stack(MSTATUS, MSCRATCH, 3, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr);
instr.push_back("mret");
gen_section(get_label($sformatf("ext_interrupt_handler"), hart), instr);

instr = {};
instr = {instr,
// The trap is caused by a timer interrupt, read back xIP
// Write into int_ack 0x2 value
$sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], epc, ip.name()),
$sformatf("li x%0d, 0", cfg_cva6.gpr[0]),
$sformatf("addi x%0d, x%0d, 2", cfg_cva6.gpr[0], cfg_cva6.gpr[0]),
// Clean timer pending interrupt
$sformatf("sw x%0d, int_ack, x%0d",
cfg_cva6.gpr[0], cfg_cva6.gpr[1])
};
pop_used_gpr_from_kernel_stack(MSTATUS, MSCRATCH, 3, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr);
instr.push_back("mret");
gen_section(get_label($sformatf("timer_interrupt_handler"), hart), instr);
endfunction

// Push used general purpose register to stack, this is needed before trap handling
Expand Down Expand Up @@ -314,31 +359,31 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
test_result_t test_result = TEST_FAIL,
privileged_reg_t csr = MSCRATCH,
string addr_label = "");
if (cfg.require_signature_addr) begin
if (cfg_cva6.require_signature_addr) begin
string str[$];
str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[1], cfg.signature_addr)};
str = {$sformatf("li x%0d, 0x%0h", cfg_cva6.gpr[1], cfg_cva6.signature_addr)};
instr = {instr, str};
case (signature_type)
// A single data word is written to the signature address.
// Bits [7:0] contain the signature_type of CORE_STATUS, and the upper
// XLEN-8 bits contain the core_status_t data.
CORE_STATUS: begin
str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[0], core_status),
$sformatf("slli x%0d, x%0d, 8", cfg.gpr[0], cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg.gpr[0],
cfg.gpr[0], signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1])};
str = {$sformatf("li x%0d, 0x%0h", cfg_cva6.gpr[0], core_status),
$sformatf("slli x%0d, x%0d, 8", cfg_cva6.gpr[0], cfg_cva6.gpr[0]),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg_cva6.gpr[0],
cfg_cva6.gpr[0], signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg_cva6.gpr[0], cfg_cva6.gpr[1])};
instr = {instr, str};
end
// A single data word is written to the signature address.
// Bits [7:0] contain the signature_type of TEST_RESULT, and the upper
// XLEN-8 bits contain the test_result_t data.
TEST_RESULT: begin
str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[0], test_result),
$sformatf("slli x%0d, x%0d, 8", cfg.gpr[0], cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg.gpr[0],
cfg.gpr[0], signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1])};
str = {$sformatf("li x%0d, 0x%0h", cfg_cva6.gpr[0], test_result),
$sformatf("slli x%0d, x%0d, 8", cfg_cva6.gpr[0], cfg_cva6.gpr[0]),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg_cva6.gpr[0],
cfg_cva6.gpr[0], signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg_cva6.gpr[0], cfg_cva6.gpr[1])};
instr = {instr, str};
end
// The first write to the signature address contains just the
Expand All @@ -347,11 +392,11 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
// each writing the data contained in one GPR, starting from x0 as the
// first write, and ending with x31 as the 32nd write.
WRITE_GPR: begin
str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[0], signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1])};
str = {$sformatf("li x%0d, 0x%0h", cfg_cva6.gpr[0], signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg_cva6.gpr[0], cfg_cva6.gpr[1])};
instr = {instr, str};
for(int i = 0; i < 32; i++) begin
str = {$sformatf("sw x%0x, 0(x%0d)", i, cfg.gpr[1])};
str = {$sformatf("sw x%0x, 0(x%0d)", i, cfg_cva6.gpr[1])};
instr = {instr, str};
end
end
Expand All @@ -364,13 +409,13 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
if (!(csr inside {implemented_csr})) begin
return;
end
str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[0], csr),
$sformatf("slli x%0d, x%0d, 8", cfg.gpr[0], cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg.gpr[0],
cfg.gpr[0], signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1]),
$sformatf("csrr x%0d, 0x%0h", cfg.gpr[0], csr),
$sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1])};
str = {$sformatf("li x%0d, 0x%0h", cfg_cva6.gpr[0], csr),
$sformatf("slli x%0d, x%0d, 8", cfg_cva6.gpr[0], cfg_cva6.gpr[0]),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg_cva6.gpr[0],
cfg_cva6.gpr[0], signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg_cva6.gpr[0], cfg_cva6.gpr[1]),
$sformatf("csrr x%0d, 0x%0h", cfg_cva6.gpr[0], csr),
$sformatf("sw x%0d, 0(x%0d)", cfg_cva6.gpr[0], cfg_cva6.gpr[1])};
instr = {instr, str};
end
default: begin
Expand All @@ -385,9 +430,19 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
instr_stream.push_back(str);
instr_stream.push_back({indent, "li gp, 1"});
instr_stream.push_back({indent, "sw gp, tohost, t5"});
instr_stream.push_back({indent, "wfi"});
instr_stream.push_back({indent, "end_of_test: j end_of_test"});
endfunction


virtual function void gen_data_page_begin(int hart);
instr_stream.push_back(".section .data");
if (hart == 0) begin
instr_stream.push_back(".align 6; .global tohost; tohost: .dword 0;");
instr_stream.push_back(".align 6; .global fromhost; fromhost: .dword 0;");
instr_stream.push_back(".align 6; .global int_ack; int_ack: .dword 0;");
end
endfunction

endclass : cva6_asm_program_gen_c

`endif // __CVA6_ASM_PROGRAM_GEN_SV__
11 changes: 7 additions & 4 deletions verif/env/uvme/cov/uvme_interrupt_covg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
// Original Author: Ayoub JALALI (ayoub.jalali@external.thalesgroup.com)

covergroup cg_interrupt(
string name
string name,
bit sw_int_supported
) with function sample (
uvma_isacov_instr_c instr
);
Expand All @@ -29,6 +30,7 @@ covergroup cg_interrupt(
cp_interrupt: coverpoint instr.rvfi.name_csrs["mcause"].wdata {
bins NO_INTERRUPT = {0} iff (!instr.trap);

ignore_bins IGN_SOFTWARE_INTERRUPT = {32'h80000003} iff (!sw_int_supported);
bins MACHINE_MODE_EXTERNAL_INTERRUPT = {32'h8000000b} iff (instr.trap);
bins MACHINE_MODE_SOFTWARE_INTERRUPT = {32'h80000003} iff (instr.trap);
bins MACHINE_MODE_TIMER_INTERRUPT = {32'h80000007} iff (instr.trap);
Expand All @@ -40,6 +42,7 @@ covergroup cg_interrupt(
}

cp_msie: coverpoint instr.rvfi.name_csrs["mie"].wdata[3] {
ignore_bins IGN_MSIE = {1'h1} iff (!sw_int_supported);
bins MSIE = {1'h1};
}

Expand All @@ -52,6 +55,7 @@ covergroup cg_interrupt(
}

cp_msip: coverpoint instr.rvfi.name_csrs["mip"].wdata[3] {
ignore_bins IGN_MSIP = {1'h1} iff (!sw_int_supported);
bins MSIP = {1'h1};
}

Expand Down Expand Up @@ -106,8 +110,8 @@ function void uvme_interrupt_covg_c::build_phase(uvm_phase phase);
end

if (!cfg.disable_all_csr_checks)
interrupt_cg = new("interrupt_cg");
else
interrupt_cg = new("interrupt_cg",
.sw_int_supported(cfg.sw_int_supported)); else
`uvm_warning(get_type_name(), "Interrupt coverage will not be scored since config disable_all_csr_checks is true")

mon_trn_fifo = new("mon_trn_fifo" , this);
Expand All @@ -127,4 +131,3 @@ task uvme_interrupt_covg_c::run_phase(uvm_phase phase);
end

endtask : run_phase

14 changes: 9 additions & 5 deletions verif/env/uvme/uvma_interrupt/cov/uvma_interrupt_cov_model.sv
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
`define __UVMA_INTERRUPT_COV_MODEL_SV__

covergroup cg_interrupt(
string name
string name,
int unsigned num_irq_supported
) with function
sample(uvma_interrupt_seq_item_c req_item);

option.per_instance = 1;
option.name = name;

cp_interrupt_req : coverpoint req_item.interrupt_valid;
cp_interrupt_req : coverpoint req_item.interrupt_vector {
bins INTERRUPTS[] = {[0:$]} with (item inside {[0:(2**(num_irq_supported))-1]});
}

endgroup: cg_interrupt

Expand Down Expand Up @@ -77,15 +80,16 @@ function void uvma_interrupt_cov_model_c::build_phase(uvm_phase phase);

void'(uvm_config_db#(uvma_interrupt_cfg_c)::get(this, "", "cfg", cfg));
if (cfg == null) begin
`uvm_fatal("CFG", "Configuration handle is null")
`uvm_fatal(get_type_name(), "Configuration handle is null")
end

void'(uvm_config_db#(uvma_interrupt_cntxt_c)::get(this, "", "cntxt", cntxt));
if (cntxt == null) begin
`uvm_fatal("CNTXT", "Context handle is null")
`uvm_fatal(get_type_name(), "Context handle is null")
end

interrupt_cg = new("interrupt_cg");
interrupt_cg = new("interrupt_cg",
.num_irq_supported(cfg.num_irq_supported));

seq_item_fifo = new("seq_item_fifo", this);

Expand Down
89 changes: 68 additions & 21 deletions verif/env/uvme/uvma_interrupt/seq/uvma_interrupt_seq.sv
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,47 @@
`ifndef __UVMA_INTERRUPT_SEQ_SV__
`define __UVMA_INTERRUPT_SEQ_SV__


/**
* Abstract object from which all other Interrupt agent sequences must extend.
* Subclasses must be run on Interrupt sequencer (uvma_interrupt_sqr_c) instance.
*/
class uvma_interrupt_seq_c extends uvma_interrupt_base_seq_c;

`uvm_object_utils(uvma_interrupt_seq_c)
`uvm_declare_p_sequencer(uvma_interrupt_sqr_c)

bit [XLEN-1:0] IRQ_ACK_VALUE = 'h0;
int unsigned IRQ_TIMEOUT;

/**
* Default constructor.
*/
extern function new(string name="uvma_interrupt_seq");

extern virtual task automatic clear_irq_channel(int channel, uvma_interrupt_seq_item_c req_item);

extern virtual task body();

endclass : uvma_interrupt_seq_c

task automatic uvma_interrupt_seq_c::clear_irq_channel(int channel, uvma_interrupt_seq_item_c req_item);

IRQ_TIMEOUT = cfg.irq_timeout;
while(1) begin
IRQ_ACK_VALUE = cntxt.mem.read(cfg.irq_addr);
if (IRQ_ACK_VALUE[channel]) begin
req_item.interrupt_vector[channel] = 1'h0;
`uvm_info(get_type_name(), $sformatf("Clear interrupt channel N-%2d -> mem = 0x%x",channel, IRQ_ACK_VALUE), UVM_NONE);
IRQ_TIMEOUT = cfg.irq_timeout;
break;
end
else begin
if (IRQ_TIMEOUT == 0) begin
`uvm_fatal(get_type_name(), $sformatf("Timeout : failed to write into irq_add to clear pending interrupts"));
end
IRQ_TIMEOUT = IRQ_TIMEOUT - 1;
end
@(posedge cntxt.interrupt_vif.clk);
end

endtask : clear_irq_channel

function uvma_interrupt_seq_c::new(string name="uvma_interrupt_seq");

super.new(name);
Expand All @@ -38,22 +60,47 @@ endfunction : new

task uvma_interrupt_seq_c::body();

forever begin
req_item = uvma_interrupt_seq_item_c::type_id::create("req_item");

start_item(req_item);
assert(req_item.randomize() with {
if(!cfg.enable_interrupt){
req_item.interrupt_valid == 'h0;
}
else {
req_item.irq_cntrl != UVMA_INTERRUPT_RANDOMIZE;
}
})
cfg.calc_random_req_latency();

finish_item(req_item);
end
if (cfg.enable_interrupt) begin
for (int i = 0; i < cfg.num_irq_supported; i++) begin
automatic int ii = i;
automatic uvma_interrupt_seq_item_c req_item_c;
fork begin
forever begin
// Set interrupt request per channel
req_item_c = uvma_interrupt_seq_item_c::type_id::create("req_item_c");
start_item(req_item_c);
if (!req_item_c.randomize() with {
req_item_c.interrupt_vector[ii] inside {0 , 1};
req_item_c.interrupt_channel_mask == 1<<ii;
})
`uvm_error(get_type_name(), "req_item_c.randomize() failed!")
finish_item(req_item_c);
#req_item_c.irq_set_delay;
// Clear interrupt request per channel
if (req_item_c.interrupt_vector[ii]) begin
req_item_c = uvma_interrupt_seq_item_c::type_id::create("req_item_c");
if (cfg.enable_clear_irq) begin
clear_irq_channel(ii, req_item_c);
start_item(req_item_c);
finish_item(req_item_c);
#req_item_c.irq_clear_delay;
end
else begin
req_item_c.interrupt_vector[ii] = 'h0;
start_item(req_item_c);
finish_item(req_item_c);
#req_item_c.irq_clear_delay;
end
end
end
end
join_none
end
wait fork;
end
else begin
`uvm_info(get_type_name(), $sformatf("Interrupts are disabled"), UVM_NONE);
end

endtask : body

Expand Down
Loading

0 comments on commit 7394941

Please sign in to comment.