mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-22 21:27:10 -04:00
Interrupt verif : Implement clear mechanism in interrupt's agent (#2527)
* 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
This commit is contained in:
parent
1de0da8d96
commit
7394941220
21 changed files with 418 additions and 289 deletions
113
verif/env/corev-dv/cva6_asm_program_gen.sv
vendored
113
verif/env/corev-dv/cva6_asm_program_gen.sv
vendored
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
verif/env/uvme/cov/uvme_interrupt_covg.sv
vendored
11
verif/env/uvme/cov/uvme_interrupt_covg.sv
vendored
|
@ -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
|
||||
);
|
||||
|
@ -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);
|
||||
|
@ -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};
|
||||
}
|
||||
|
||||
|
@ -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};
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -127,4 +131,3 @@ task uvme_interrupt_covg_c::run_phase(uvm_phase phase);
|
|||
end
|
||||
|
||||
endtask : run_phase
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
|
|
@ -17,36 +17,23 @@
|
|||
*/
|
||||
class uvma_interrupt_seq_item_c extends uvml_trn_seq_item_c;
|
||||
|
||||
rand uvma_interrupt_cntrl_enum irq_cntrl;
|
||||
rand int unsigned irq_delay; // Delay before applying individual interrupt
|
||||
rand int unsigned irq_time; // How many cycles take an interrupt
|
||||
rand int unsigned irq_set_delay; // Delay after set individual interrupt
|
||||
rand int unsigned irq_clear_delay; // Delay after clear individual interrupt
|
||||
|
||||
rand bit[NUM_IRQ-1:0] interrupt_valid; //the valid interrupts for the core under test
|
||||
rand bit [15:0] interrupt_vector; //the vector interrupts for the core under test
|
||||
rand bit [15:0] interrupt_channel_mask; //the vector interrupts for the core under test
|
||||
|
||||
`uvm_object_utils_begin(uvma_interrupt_seq_item_c)
|
||||
`uvm_field_enum(uvma_interrupt_cntrl_enum, irq_cntrl, UVM_DEFAULT)
|
||||
`uvm_field_int(irq_delay, UVM_DEFAULT)
|
||||
`uvm_field_int(irq_time, UVM_DEFAULT)
|
||||
`uvm_field_int(interrupt_valid, UVM_DEFAULT)
|
||||
`uvm_field_int(irq_set_delay, UVM_DEFAULT)
|
||||
`uvm_field_int(irq_clear_delay, UVM_DEFAULT)
|
||||
`uvm_field_int(interrupt_vector, UVM_DEFAULT)
|
||||
`uvm_field_int(interrupt_channel_mask, UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
|
||||
constraint default_irq_delay_c {
|
||||
irq_delay inside {[150:250]};
|
||||
}
|
||||
|
||||
constraint default_irq_time_c {
|
||||
irq_time inside {[5:10]};
|
||||
}
|
||||
|
||||
constraint irq_mode_c {
|
||||
|
||||
if (irq_cntrl == UVMA_INTERRUPT_ONE_BY_ONE) {
|
||||
$countones(interrupt_valid) == 1;
|
||||
}
|
||||
if (irq_cntrl == UVMA_INTERRUPT_MORE_THAN_ONE) {
|
||||
$countones(interrupt_valid) > 1;
|
||||
}
|
||||
irq_set_delay inside {[300: 500]};
|
||||
irq_clear_delay inside {0, 25, 50};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -127,7 +127,7 @@ function void uvma_interrupt_agent_c::get_and_set_cntxt();
|
|||
|
||||
void'(uvm_config_db#(uvma_interrupt_cntxt_c)::get(this, "", "cntxt", cntxt));
|
||||
if (cntxt == null) begin
|
||||
`uvm_info("CNTXT", "Context handle is null; creating.", UVM_DEBUG)
|
||||
`uvm_info(get_type_name(), "Context handle is null; creating.", UVM_DEBUG)
|
||||
cntxt = uvma_interrupt_cntxt_c::type_id::create("cntxt");
|
||||
end
|
||||
uvm_config_db#(uvma_interrupt_cntxt_c)::set(this, "*", "cntxt", cntxt);
|
||||
|
@ -138,10 +138,10 @@ endfunction : get_and_set_cntxt
|
|||
function void uvma_interrupt_agent_c::retrieve_vif();
|
||||
|
||||
if (!uvm_config_db#(virtual uvma_interrupt_if)::get(this, "", "interrupt_vif", cntxt.interrupt_vif)) begin
|
||||
`uvm_fatal("VIF", $sformatf("Could not find vif handle of type %s in uvm_config_db", $typename(cntxt.interrupt_vif)))
|
||||
`uvm_fatal(get_type_name(), $sformatf("Could not find vif handle of type %s in uvm_config_db", $typename(cntxt.interrupt_vif)))
|
||||
end
|
||||
else begin
|
||||
`uvm_info("VIF", $sformatf("Found vif handle of type %s in uvm_config_db", $typename(cntxt.interrupt_vif)), UVM_DEBUG)
|
||||
`uvm_info(get_type_name(), $sformatf("Found vif handle of type %s in uvm_config_db", $typename(cntxt.interrupt_vif)), UVM_DEBUG)
|
||||
end
|
||||
|
||||
endfunction : retrieve_vif
|
||||
|
|
|
@ -24,11 +24,17 @@ class uvma_interrupt_cfg_c extends uvm_object;
|
|||
rand bit enable_interrupt;
|
||||
bit interrupt_plusarg_valid;
|
||||
|
||||
// Interrupt request latency modes
|
||||
rand uvma_interrupt_drv_req_enum drv_req_mode;
|
||||
rand int unsigned drv_req_fixed_latency;
|
||||
rand int unsigned drv_req_random_latency_min;
|
||||
rand int unsigned drv_req_random_latency_max;
|
||||
// Number of Interrupt vector supported
|
||||
rand int unsigned num_irq_supported;
|
||||
|
||||
// Interrupt memory ack
|
||||
rand bit [XLEN-1:0] irq_addr;
|
||||
|
||||
// enbale/disable clear mechanism
|
||||
rand bit enable_clear_irq;
|
||||
|
||||
// Number of cycle before Timeout if the agent failed to write into irq_add
|
||||
rand int unsigned irq_timeout;
|
||||
|
||||
`uvm_object_utils_begin(uvma_interrupt_cfg_c)
|
||||
`uvm_field_int ( enabled , UVM_DEFAULT)
|
||||
|
@ -37,10 +43,10 @@ class uvma_interrupt_cfg_c extends uvm_object;
|
|||
`uvm_field_int ( trn_log_enabled , UVM_DEFAULT)
|
||||
`uvm_field_int ( enable_interrupt , UVM_DEFAULT)
|
||||
`uvm_field_int ( interrupt_plusarg_valid , UVM_DEFAULT)
|
||||
`uvm_field_enum(uvma_interrupt_drv_req_enum, drv_req_mode , UVM_DEFAULT)
|
||||
`uvm_field_int ( drv_req_fixed_latency , UVM_DEFAULT)
|
||||
`uvm_field_int ( drv_req_random_latency_min , UVM_DEFAULT)
|
||||
`uvm_field_int ( drv_req_random_latency_max , UVM_DEFAULT)
|
||||
`uvm_field_int ( num_irq_supported , UVM_DEFAULT)
|
||||
`uvm_field_int ( irq_addr , UVM_DEFAULT)
|
||||
`uvm_field_int ( enable_clear_irq , UVM_DEFAULT)
|
||||
`uvm_field_int ( irq_timeout , UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
|
||||
|
@ -53,24 +59,10 @@ class uvma_interrupt_cfg_c extends uvm_object;
|
|||
}
|
||||
|
||||
constraint default_enable_irq_cons {
|
||||
soft enable_interrupt == 0;
|
||||
}
|
||||
|
||||
constraint default_drive_req_mode_cons {
|
||||
soft drv_req_mode == UVMA_INTERRUPT_DRV_REQ_MODE_FIXED_LATENCY;
|
||||
}
|
||||
|
||||
constraint default_fixed_req_latency_cons {
|
||||
soft drv_req_fixed_latency inside {[250:300]};
|
||||
}
|
||||
|
||||
constraint valid_random_req_latency_cons {
|
||||
drv_req_random_latency_min < drv_req_random_latency_max;
|
||||
}
|
||||
|
||||
constraint default_random_latency_cons {
|
||||
soft drv_req_random_latency_min inside {[50:100]};
|
||||
soft drv_req_random_latency_max inside {[150:200]};
|
||||
soft enable_interrupt == 0;
|
||||
soft num_irq_supported == 2;
|
||||
soft enable_clear_irq == 1;
|
||||
soft irq_timeout == 10_000;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,11 +70,6 @@ class uvma_interrupt_cfg_c extends uvm_object;
|
|||
*/
|
||||
extern function new(string name="uvma_interrupt_cfg");
|
||||
|
||||
/**
|
||||
* Calculate a new random gnt latency
|
||||
*/
|
||||
extern function int unsigned calc_random_req_latency();
|
||||
|
||||
endclass : uvma_interrupt_cfg_c
|
||||
|
||||
function uvma_interrupt_cfg_c::new(string name="uvma_interrupt_cfg");
|
||||
|
@ -96,20 +83,4 @@ function uvma_interrupt_cfg_c::new(string name="uvma_interrupt_cfg");
|
|||
|
||||
endfunction : new
|
||||
|
||||
function int unsigned uvma_interrupt_cfg_c::calc_random_req_latency();
|
||||
|
||||
int unsigned req_latency;
|
||||
|
||||
case (drv_req_mode)
|
||||
UVMA_INTERRUPT_DRV_REQ_MODE_CONSTANT : req_latency = 0;
|
||||
UVMA_INTERRUPT_DRV_REQ_MODE_FIXED_LATENCY : req_latency = drv_req_fixed_latency;
|
||||
UVMA_INTERRUPT_DRV_REQ_MODE_RANDOM_LATENCY: begin
|
||||
req_latency = $urandom_range(drv_req_random_latency_min, drv_req_random_latency_max);
|
||||
end
|
||||
endcase
|
||||
|
||||
return req_latency;
|
||||
|
||||
endfunction : calc_random_req_latency
|
||||
|
||||
`endif // __UVMA_INTERRUPT_CFG_SV__
|
||||
|
|
|
@ -20,7 +20,13 @@ class uvma_interrupt_cntxt_c extends uvm_object;
|
|||
|
||||
// Handle to agent interface
|
||||
virtual uvma_interrupt_if interrupt_vif;
|
||||
|
||||
|
||||
// Handle to memory storage to check clear condition
|
||||
uvml_mem_c#(MAX_ADDR_WIDTH) mem;
|
||||
|
||||
// Integrals
|
||||
uvma_interrupt_reset_state_enum reset_state = UVMA_INTERRUPT_RESET_STATE_PRE_RESET;
|
||||
|
||||
// Events
|
||||
uvm_event sample_cfg_e;
|
||||
uvm_event sample_cntxt_e;
|
||||
|
@ -28,6 +34,8 @@ class uvma_interrupt_cntxt_c extends uvm_object;
|
|||
`uvm_object_utils_begin(uvma_interrupt_cntxt_c)
|
||||
`uvm_field_event(sample_cfg_e , UVM_DEFAULT)
|
||||
`uvm_field_event(sample_cntxt_e, UVM_DEFAULT)
|
||||
`uvm_field_enum(uvma_interrupt_reset_state_enum, reset_state, UVM_DEFAULT)
|
||||
`uvm_field_object(mem, UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
/**
|
||||
|
@ -35,34 +43,17 @@ class uvma_interrupt_cntxt_c extends uvm_object;
|
|||
*/
|
||||
extern function new(string name="uvma_interrupt_cntxt");
|
||||
|
||||
/**
|
||||
* TODO Describe uvma_interrupt_cntxt_c::reset()
|
||||
*/
|
||||
extern function void reset();
|
||||
|
||||
endclass : uvma_interrupt_cntxt_c
|
||||
|
||||
|
||||
`pragma protect begin
|
||||
|
||||
|
||||
function uvma_interrupt_cntxt_c::new(string name="uvma_interrupt_cntxt");
|
||||
|
||||
super.new(name);
|
||||
|
||||
mem = uvml_mem_c#(MAX_ADDR_WIDTH)::type_id::create("mem");
|
||||
|
||||
sample_cfg_e = new("sample_cfg_e" );
|
||||
sample_cntxt_e = new("sample_cntxt_e");
|
||||
|
||||
endfunction : new
|
||||
|
||||
function void uvma_interrupt_cntxt_c::reset();
|
||||
|
||||
// TODO Implement uvma_interrupt_cntxt_c::reset()
|
||||
|
||||
endfunction : reset
|
||||
|
||||
|
||||
`pragma protect end
|
||||
|
||||
|
||||
`endif // __UVMA_INTERRUPT_CNTXT_SV__
|
||||
|
|
106
verif/env/uvme/uvma_interrupt/uvma_interrupt_drv.sv
vendored
106
verif/env/uvme/uvma_interrupt/uvma_interrupt_drv.sv
vendored
|
@ -19,7 +19,7 @@ class uvma_interrupt_drv_c extends uvm_driver#(uvma_interrupt_seq_item_c);
|
|||
uvma_interrupt_cfg_c cfg;
|
||||
uvma_interrupt_cntxt_c cntxt;
|
||||
|
||||
uvma_interrupt_seq_item_c req_item;
|
||||
uvma_interrupt_seq_item_c req_item;
|
||||
|
||||
`uvm_component_utils_begin(uvma_interrupt_drv_c)
|
||||
`uvm_field_object(cfg , UVM_DEFAULT)
|
||||
|
@ -43,24 +43,24 @@ class uvma_interrupt_drv_c extends uvm_driver#(uvma_interrupt_seq_item_c);
|
|||
extern virtual task run_phase(uvm_phase phase);
|
||||
|
||||
/**
|
||||
* Drives the virtual interface's (cntxt.interrupt_vif) signals using req's contents.
|
||||
* Called by run_phase() while agent is in pre-reset state.
|
||||
*/
|
||||
extern task drv_irq(uvma_interrupt_seq_item_c req);
|
||||
extern task drv_irq_pre_reset();
|
||||
|
||||
/**
|
||||
* Assert only one interrupt each time
|
||||
* Called by run_phase() while agent is in reset state.
|
||||
*/
|
||||
extern task assert_irq_one_by_one(uvma_interrupt_seq_item_c req);
|
||||
extern task drv_irq_in_reset();
|
||||
|
||||
/**
|
||||
* Assert one or more interrupt each time
|
||||
* Called by run_phase() while agent is in post-reset state.
|
||||
*/
|
||||
extern task assert_irq_more(uvma_interrupt_seq_item_c req);
|
||||
extern task drv_irq_post_reset(uvma_interrupt_seq_item_c req);
|
||||
|
||||
/**
|
||||
* Randomize interrupt signal
|
||||
* Assert interrupt request
|
||||
*/
|
||||
extern task assert_irq_randomize(uvma_interrupt_seq_item_c req);
|
||||
extern task assert_irq(uvma_interrupt_seq_item_c req);
|
||||
|
||||
endclass : uvma_interrupt_drv_c
|
||||
|
||||
|
@ -77,13 +77,13 @@ function void uvma_interrupt_drv_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
|
||||
uvm_config_db#(uvma_interrupt_cfg_c)::set(this, "*", "cfg", cfg);
|
||||
|
||||
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
|
||||
uvm_config_db#(uvma_interrupt_cntxt_c)::set(this, "*", "cntxt", cntxt);
|
||||
|
||||
|
@ -99,67 +99,61 @@ task uvma_interrupt_drv_c::run_phase(uvm_phase phase);
|
|||
cntxt.interrupt_vif.irq <= 'h0;
|
||||
|
||||
if(!cfg.enable_interrupt) begin
|
||||
`uvm_warning(get_type_name(), "Driving Interrupt reqeust is disabled");
|
||||
`uvm_info(get_type_name(), "Driving Interrupt request is disabled", UVM_NONE);
|
||||
return;
|
||||
end
|
||||
|
||||
forever begin
|
||||
case (cntxt.reset_state)
|
||||
UVMA_INTERRUPT_RESET_STATE_PRE_RESET : drv_irq_pre_reset ();
|
||||
UVMA_INTERRUPT_RESET_STATE_IN_RESET : drv_irq_in_reset ();
|
||||
UVMA_INTERRUPT_RESET_STATE_POST_RESET: begin
|
||||
if (cfg.enable_interrupt) begin
|
||||
seq_item_port.get_next_item(req_item);
|
||||
drv_irq_post_reset(req_item);
|
||||
seq_item_port.item_done();
|
||||
end
|
||||
end
|
||||
|
||||
seq_item_port.get_next_item(req_item);
|
||||
drv_irq(req_item);
|
||||
seq_item_port.item_done();
|
||||
|
||||
default: `uvm_fatal(get_type_name(), $sformatf("Invalid reset_state: %0d", cntxt.reset_state))
|
||||
endcase
|
||||
end
|
||||
|
||||
endtask : run_phase
|
||||
|
||||
task uvma_interrupt_drv_c::drv_irq(uvma_interrupt_seq_item_c req);
|
||||
|
||||
task uvma_interrupt_drv_c::drv_irq_pre_reset();
|
||||
|
||||
cntxt.interrupt_vif.irq <= 1'b0;
|
||||
@(posedge cntxt.interrupt_vif.clk);
|
||||
|
||||
endtask : drv_irq_pre_reset
|
||||
|
||||
|
||||
task uvma_interrupt_drv_c::drv_irq_in_reset();
|
||||
|
||||
cntxt.interrupt_vif.irq <= 1'b0;
|
||||
@(posedge cntxt.interrupt_vif.clk);
|
||||
|
||||
endtask : drv_irq_in_reset
|
||||
|
||||
task uvma_interrupt_drv_c::drv_irq_post_reset(uvma_interrupt_seq_item_c req);
|
||||
`uvm_info(get_type_name(), $sformatf("Driving:\n%s", req.sprint()), UVM_HIGH);
|
||||
|
||||
case (req.irq_cntrl)
|
||||
UVMA_INTERRUPT_ONE_BY_ONE: begin
|
||||
assert_irq_one_by_one(req);
|
||||
end
|
||||
UVMA_INTERRUPT_MORE_THAN_ONE: begin
|
||||
assert_irq_more(req);
|
||||
end
|
||||
UVMA_INTERRUPT_RANDOMIZE: begin
|
||||
assert_irq_randomize(req);
|
||||
end
|
||||
endcase
|
||||
assert_irq(req);
|
||||
|
||||
endtask : drv_irq
|
||||
endtask : drv_irq_post_reset
|
||||
|
||||
task uvma_interrupt_drv_c::assert_irq_one_by_one(uvma_interrupt_seq_item_c req);
|
||||
task uvma_interrupt_drv_c::assert_irq(uvma_interrupt_seq_item_c req);
|
||||
|
||||
#req.irq_delay;
|
||||
cntxt.interrupt_vif.irq <= req.interrupt_valid;
|
||||
`uvm_info(get_type_name(), $sformatf("Assert interrupt channel(s) %0b", req.interrupt_valid), UVM_HIGH)
|
||||
#req.irq_time;
|
||||
cntxt.interrupt_vif.irq <= 'h0;
|
||||
|
||||
endtask : assert_irq_one_by_one
|
||||
|
||||
task uvma_interrupt_drv_c::assert_irq_more(uvma_interrupt_seq_item_c req);
|
||||
|
||||
#req.irq_delay;
|
||||
cntxt.interrupt_vif.irq <= req.interrupt_valid;
|
||||
#req.irq_time;
|
||||
cntxt.interrupt_vif.irq <= 'h0;
|
||||
|
||||
endtask : assert_irq_more
|
||||
|
||||
task uvma_interrupt_drv_c::assert_irq_randomize(uvma_interrupt_seq_item_c req);
|
||||
|
||||
repeat(5) begin
|
||||
#req.irq_delay;
|
||||
cntxt.interrupt_vif.irq <= req.interrupt_valid;
|
||||
#req.irq_time;
|
||||
for (int i = 0; i < cfg.num_irq_supported; i++) begin
|
||||
if (req.interrupt_channel_mask[i] == 1) begin
|
||||
cntxt.interrupt_vif.irq[i] <= req.interrupt_vector[i];
|
||||
end
|
||||
end
|
||||
|
||||
cntxt.interrupt_vif.irq <= 'h0;
|
||||
cfg.calc_random_req_latency();
|
||||
`uvm_info(get_type_name(), $sformatf("Assert interrupt channel(s) %0b", req.interrupt_vector), UVM_HIGH)
|
||||
|
||||
endtask : assert_irq_randomize
|
||||
endtask : assert_irq
|
||||
|
||||
`endif // __UVMA_INTERRUPT_DRV_SV__
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
*/
|
||||
interface uvma_interrupt_if
|
||||
(
|
||||
input clk,
|
||||
input reset_n
|
||||
);
|
||||
|
||||
logic [uvma_interrupt_pkg::NUM_IRQ-1:0] irq;
|
||||
logic [15:0] irq;
|
||||
|
||||
endinterface : uvma_interrupt_if
|
||||
|
||||
|
|
142
verif/env/uvme/uvma_interrupt/uvma_interrupt_mon.sv
vendored
142
verif/env/uvme/uvma_interrupt/uvma_interrupt_mon.sv
vendored
|
@ -22,7 +22,7 @@ class uvma_interrupt_mon_c extends uvm_monitor;
|
|||
`uvm_component_utils_end
|
||||
|
||||
// TLM
|
||||
uvm_analysis_port #(uvma_interrupt_seq_item_c) ap;
|
||||
uvm_analysis_port #(uvma_interrupt_seq_item_c) ap;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
|
@ -42,73 +42,119 @@ class uvma_interrupt_mon_c extends uvm_monitor;
|
|||
extern virtual task run_phase(uvm_phase phase);
|
||||
|
||||
/**
|
||||
* Monitor interrupt
|
||||
* Monitors passive_mp for asynchronous reset and updates the context's reset state.
|
||||
*/
|
||||
extern virtual task mon_irq();
|
||||
extern task observe_reset();
|
||||
|
||||
/**
|
||||
* Monitor pre-reset phase
|
||||
*/
|
||||
extern virtual task mon_irq_pre_reset();
|
||||
|
||||
/**
|
||||
* Monitor in-reset phase
|
||||
*/
|
||||
extern virtual task mon_irq_in_reset();
|
||||
|
||||
/**
|
||||
* Monitor post-reset phase
|
||||
*/
|
||||
extern virtual task mon_irq_post_reset();
|
||||
|
||||
//~ /**
|
||||
//~ * Monitor interrupt
|
||||
//~ */
|
||||
//~ extern virtual task mon_irq();
|
||||
|
||||
endclass : uvma_interrupt_mon_c
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
function uvma_interrupt_mon_c::new(string name = "uvma_interrupt_mon", uvm_component parent);
|
||||
super.new(name, parent);
|
||||
|
||||
endfunction
|
||||
function uvma_interrupt_mon_c::new(string name = "uvma_interrupt_mon", uvm_component parent);
|
||||
super.new(name, parent);
|
||||
|
||||
/**
|
||||
* 1. Ensures cfg & cntxt handles are not null.
|
||||
* 2. Builds ap.
|
||||
*/
|
||||
function void uvma_interrupt_mon_c::build_phase(uvm_phase phase);
|
||||
endfunction
|
||||
|
||||
super.build_phase(phase);
|
||||
|
||||
void'(uvm_config_db#(uvma_interrupt_cntxt_c)::get(this, "", "cntxt", cntxt));
|
||||
if (cntxt == null) begin
|
||||
`uvm_fatal("build_phase", "monitor cntxt class failed")
|
||||
end
|
||||
function void uvma_interrupt_mon_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")
|
||||
end
|
||||
|
||||
ap = new("ap", this);
|
||||
super.build_phase(phase);
|
||||
|
||||
endfunction
|
||||
void'(uvm_config_db#(uvma_interrupt_cntxt_c)::get(this, "", "cntxt", cntxt));
|
||||
if (cntxt == null) begin
|
||||
`uvm_fatal(get_type_name(), "monitor cntxt class failed")
|
||||
end
|
||||
|
||||
/**
|
||||
* TODO Describe uvma_interrupt_mon_c::run_phase()
|
||||
*/
|
||||
task uvma_interrupt_mon_c::run_phase(uvm_phase phase);
|
||||
super.run_phase(phase);
|
||||
void'(uvm_config_db#(uvma_interrupt_cfg_c)::get(this, "", "cfg", cfg));
|
||||
if (cfg == null) begin
|
||||
`uvm_fatal(get_type_name(), "Configuration handle is null")
|
||||
end
|
||||
|
||||
ap = new("ap", this);
|
||||
|
||||
endfunction
|
||||
|
||||
|
||||
task uvma_interrupt_mon_c::run_phase(uvm_phase phase);
|
||||
super.run_phase(phase);
|
||||
|
||||
|
||||
if (cfg.enabled) begin
|
||||
fork
|
||||
begin
|
||||
mon_irq();
|
||||
observe_reset();
|
||||
|
||||
forever begin
|
||||
case (cntxt.reset_state)
|
||||
UVMA_INTERRUPT_RESET_STATE_PRE_RESET: mon_irq_pre_reset();
|
||||
UVMA_INTERRUPT_RESET_STATE_IN_RESET: mon_irq_in_reset();
|
||||
UVMA_INTERRUPT_RESET_STATE_POST_RESET: mon_irq_post_reset();
|
||||
endcase
|
||||
end
|
||||
join_none
|
||||
join
|
||||
end
|
||||
|
||||
endtask: run_phase
|
||||
endtask: run_phase
|
||||
|
||||
/**
|
||||
* TODO Describe uvma_interrupt_mon_c::mon_post_reset()
|
||||
*/
|
||||
task uvma_interrupt_mon_c::mon_irq();
|
||||
|
||||
uvma_interrupt_seq_item_c irq_item;
|
||||
task uvma_interrupt_mon_c::mon_irq_pre_reset();
|
||||
|
||||
while(1) begin
|
||||
@(cntxt.interrupt_vif.irq);
|
||||
@(cntxt.interrupt_vif.clk);
|
||||
|
||||
irq_item = uvma_interrupt_seq_item_c::type_id::create("irq_item");
|
||||
irq_item.interrupt_valid = cntxt.interrupt_vif.irq;
|
||||
`uvm_info(get_type_name(), $sformatf("monitor interrupt : %0d", irq_item.interrupt_valid), UVM_LOW)
|
||||
ap.write(irq_item);
|
||||
endtask : mon_irq_pre_reset
|
||||
|
||||
end
|
||||
task uvma_interrupt_mon_c::mon_irq_in_reset();
|
||||
|
||||
endtask: mon_irq
|
||||
@(cntxt.interrupt_vif.clk);
|
||||
|
||||
endtask : mon_irq_in_reset
|
||||
|
||||
task uvma_interrupt_mon_c::mon_irq_post_reset();
|
||||
|
||||
uvma_interrupt_seq_item_c irq_item;
|
||||
|
||||
while(1) begin
|
||||
@(cntxt.interrupt_vif.irq);
|
||||
|
||||
irq_item = uvma_interrupt_seq_item_c::type_id::create("irq_item");
|
||||
irq_item.interrupt_vector = cntxt.interrupt_vif.irq;
|
||||
`uvm_info(get_type_name(), $sformatf("monitor interrupt : %0d", irq_item.interrupt_vector), UVM_LOW)
|
||||
ap.write(irq_item);
|
||||
|
||||
end
|
||||
|
||||
endtask: mon_irq_post_reset
|
||||
|
||||
|
||||
task uvma_interrupt_mon_c::observe_reset();
|
||||
|
||||
forever begin
|
||||
wait (cntxt.interrupt_vif.reset_n === 0);
|
||||
cntxt.reset_state = UVMA_INTERRUPT_RESET_STATE_IN_RESET;
|
||||
`uvm_info(get_type_name(), $sformatf("RESET_STATE_IN_RESET"), UVM_NONE)
|
||||
wait (cntxt.interrupt_vif.reset_n === 1);
|
||||
cntxt.reset_state = UVMA_INTERRUPT_RESET_STATE_POST_RESET;
|
||||
`uvm_info(get_type_name(), $sformatf("RESET_STATE_POST_RESET"), UVM_NONE)
|
||||
end
|
||||
|
||||
endtask : observe_reset
|
||||
|
||||
`endif
|
||||
|
|
|
@ -25,11 +25,15 @@
|
|||
package uvma_interrupt_pkg;
|
||||
|
||||
import uvm_pkg ::*;
|
||||
import uvml_mem_pkg::*;
|
||||
import uvml_hrtbt_pkg::*;
|
||||
import uvml_trn_pkg ::*;
|
||||
import uvml_logs_pkg ::*;
|
||||
import uvma_isacov_pkg ::*;
|
||||
import uvma_rvfi_pkg ::*;
|
||||
|
||||
parameter NUM_IRQ = 3;
|
||||
parameter XLEN = 32;
|
||||
parameter int MAX_ADDR_WIDTH = `UVMA_AXI_ADDR_MAX_WIDTH ; // subjective maximum
|
||||
|
||||
// Constants / Structs / Enums
|
||||
`include "uvma_interrupt_constants.sv"
|
||||
|
@ -48,7 +52,7 @@ package uvma_interrupt_pkg;
|
|||
`include "uvma_interrupt_drv.sv"
|
||||
`include "uvma_interrupt_sqr.sv"
|
||||
`include "uvma_interrupt_agent.sv"
|
||||
|
||||
|
||||
// Sequences
|
||||
`include "uvma_interrupt_base_seq.sv"
|
||||
`include "uvma_interrupt_seq.sv"
|
||||
|
|
|
@ -26,9 +26,6 @@ class uvma_interrupt_sqr_c extends uvm_sequencer#(uvma_interrupt_seq_item_c);
|
|||
uvma_interrupt_cfg_c cfg;
|
||||
uvma_interrupt_cntxt_c cntxt;
|
||||
|
||||
// Analysis port to receive
|
||||
uvm_tlm_analysis_fifo #(uvma_interrupt_seq_item_c) mm_req_fifo;
|
||||
|
||||
`uvm_component_utils_begin(uvma_interrupt_sqr_c)
|
||||
`uvm_field_object(cfg , UVM_DEFAULT)
|
||||
`uvm_field_object(cntxt, UVM_DEFAULT)
|
||||
|
@ -61,16 +58,14 @@ function void uvma_interrupt_sqr_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
|
||||
|
||||
mm_req_fifo = new("mm_req_fifo", this);
|
||||
|
||||
endfunction : build_phase
|
||||
|
||||
|
||||
|
|
|
@ -10,16 +10,11 @@
|
|||
`ifndef __UVMA_INTERRUPT_TDEFS_SV__
|
||||
`define __UVMA_INTERRUPT_TDEFS_SV__
|
||||
|
||||
typedef enum {
|
||||
UVMA_INTERRUPT_ONE_BY_ONE,
|
||||
UVMA_INTERRUPT_MORE_THAN_ONE,
|
||||
UVMA_INTERRUPT_RANDOMIZE
|
||||
} uvma_interrupt_cntrl_enum;
|
||||
|
||||
typedef enum {
|
||||
UVMA_INTERRUPT_DRV_REQ_MODE_CONSTANT,
|
||||
UVMA_INTERRUPT_DRV_REQ_MODE_FIXED_LATENCY,
|
||||
UVMA_INTERRUPT_DRV_REQ_MODE_RANDOM_LATENCY
|
||||
} uvma_interrupt_drv_req_enum;
|
||||
UVMA_INTERRUPT_RESET_STATE_PRE_RESET,
|
||||
UVMA_INTERRUPT_RESET_STATE_IN_RESET,
|
||||
UVMA_INTERRUPT_RESET_STATE_POST_RESET
|
||||
} uvma_interrupt_reset_state_enum;
|
||||
|
||||
`endif // __UVMA_INTERRUPT_TDEFS_SV__
|
||||
|
|
30
verif/env/uvme/uvme_cva6_cfg.sv
vendored
30
verif/env/uvme/uvme_cva6_cfg.sv
vendored
|
@ -62,6 +62,9 @@ class uvme_cva6_cfg_c extends uvma_core_cntrl_cfg_c;
|
|||
// MMU support
|
||||
rand bit MmuPresent;
|
||||
|
||||
// Software interrupt supported
|
||||
rand bit sw_int_supported;
|
||||
|
||||
`uvm_object_utils_begin(uvme_cva6_cfg_c)
|
||||
`uvm_field_int ( enabled , UVM_DEFAULT )
|
||||
`uvm_field_enum(uvm_active_passive_enum, is_active , UVM_DEFAULT )
|
||||
|
@ -75,6 +78,7 @@ class uvme_cva6_cfg_c extends uvma_core_cntrl_cfg_c;
|
|||
`uvm_field_int ( nr_pmp_entries , UVM_DEFAULT )
|
||||
`uvm_field_int ( ext_zihpm_supported , UVM_DEFAULT )
|
||||
`uvm_field_int ( MmuPresent , UVM_DEFAULT )
|
||||
`uvm_field_int ( sw_int_supported , UVM_DEFAULT )
|
||||
`uvm_field_int ( sys_clk_period , UVM_DEFAULT + UVM_DEC)
|
||||
`uvm_field_int ( performance_mode , UVM_DEFAULT )
|
||||
|
||||
|
@ -124,6 +128,8 @@ class uvme_cva6_cfg_c extends uvma_core_cntrl_cfg_c;
|
|||
HPDCache_supported == (RTLCVA6Cfg.DCacheType == 2);
|
||||
|
||||
MmuPresent == RTLCVA6Cfg.MmuPresent;
|
||||
// TODO : add RTL paramater related to this field fix issue#2500
|
||||
sw_int_supported == 0;
|
||||
}
|
||||
|
||||
constraint ext_const {
|
||||
|
@ -225,6 +231,11 @@ class uvme_cva6_cfg_c extends uvma_core_cntrl_cfg_c;
|
|||
|
||||
extern virtual function void read_disable_csr_check_plusargs();
|
||||
|
||||
/**
|
||||
* Get irq_addr ack
|
||||
*/
|
||||
extern virtual function bit [XLEN-1:0] get_irq_addr();
|
||||
|
||||
endclass : uvme_cva6_cfg_c
|
||||
|
||||
|
||||
|
@ -280,6 +291,25 @@ function void uvme_cva6_cfg_c::sample_parameters(uvma_core_cntrl_cntxt_c cntxt);
|
|||
|
||||
endfunction : sample_parameters
|
||||
|
||||
function bit [XLEN-1:0] uvme_cva6_cfg_c::get_irq_addr();
|
||||
|
||||
int unsigned IRQ_ADDR;
|
||||
string binary;
|
||||
|
||||
if (!$value$plusargs("irq_addr=%h", IRQ_ADDR)) IRQ_ADDR = '0;
|
||||
if (IRQ_ADDR == '0) begin
|
||||
if (!$value$plusargs("elf_file=%s", binary)) binary = "";
|
||||
if (binary != "") begin
|
||||
read_elf(binary);
|
||||
read_symbol("int_ack", IRQ_ADDR);
|
||||
end
|
||||
`uvm_info(get_type_name(), $sformatf("[IRQ] INFO: int_ack_addr: %h", IRQ_ADDR), UVM_NONE)
|
||||
end
|
||||
|
||||
return IRQ_ADDR;
|
||||
|
||||
endfunction : get_irq_addr
|
||||
|
||||
function void uvme_cva6_cfg_c::set_unsupported_csr_mask();
|
||||
|
||||
super.set_unsupported_csr_mask();
|
||||
|
|
9
verif/env/uvme/uvme_cva6_env.sv
vendored
9
verif/env/uvme/uvme_cva6_env.sv
vendored
|
@ -163,7 +163,7 @@ function void uvme_cva6_env_c::build_phase(uvm_phase phase);
|
|||
`uvm_fatal("CFG", "Configuration handle is null")
|
||||
end
|
||||
else begin
|
||||
`uvm_info("CFG", $sformatf("Found configuration handle:\n%s", cfg.sprint()), UVM_DEBUG)
|
||||
`uvm_info("CFG", $sformatf("Found configuration handle:\n%s", cfg.sprint()), UVM_NONE)
|
||||
end
|
||||
|
||||
cfg.rvfi_cfg.nret = RTLCVA6Cfg.NrCommitPorts;
|
||||
|
@ -175,7 +175,10 @@ function void uvme_cva6_env_c::build_phase(uvm_phase phase);
|
|||
cntxt = uvme_cva6_cntxt_c::type_id::create("cntxt");
|
||||
end
|
||||
|
||||
cntxt.axi_cntxt.mem = cntxt.mem;
|
||||
cntxt.axi_cntxt.mem = cntxt.mem;
|
||||
cntxt.interrupt_cntxt.mem = cntxt.mem;
|
||||
// get irq_addr ack from CVA6 UVM env
|
||||
cfg.interrupt_cfg.irq_addr = cfg.get_irq_addr();
|
||||
|
||||
if ($test$plusargs("tandem_enabled"))
|
||||
$value$plusargs("tandem_enabled=%b",cfg.tandem_enabled);
|
||||
|
@ -399,10 +402,10 @@ function void uvme_cva6_env_c::connect_coverage_model();
|
|||
isacov_agent.monitor.ap.connect(cov_model.isa_covg.mon_trn_fifo.analysis_export);
|
||||
isacov_agent.monitor.ap.connect(cov_model.illegal_covg.mon_trn_fifo.analysis_export);
|
||||
isacov_agent.monitor.ap.connect(cov_model.exception_covg.mon_trn_fifo.analysis_export);
|
||||
rvfi_agent.rvfi_core_ap.connect(isacov_agent.monitor.rvfi_instr_imp);
|
||||
end
|
||||
|
||||
clknrst_agent.mon_ap.connect(cov_model.reset_export);
|
||||
rvfi_agent.rvfi_core_ap.connect(isacov_agent.monitor.rvfi_instr_imp);
|
||||
|
||||
if(cfg.axi_cfg.cov_model_enabled) begin
|
||||
axi_agent.monitor.m_axi_superset_write_rsp_packets_collected.connect(cov_model.axi_covg.uvme_axi_cov_b_resp_fifo.analysis_export);
|
||||
|
|
1
verif/env/uvme/uvme_cva6_pkg.sv
vendored
1
verif/env/uvme/uvme_cva6_pkg.sv
vendored
|
@ -58,6 +58,7 @@ package uvme_cva6_pkg;
|
|||
import "DPI-C" function void read_elf(input string filename);
|
||||
import "DPI-C" function byte get_section(output longint address, output longint len);
|
||||
import "DPI-C" context function void read_section_sv(input longint address, inout byte buffer[]);
|
||||
import "DPI-C" function byte read_symbol (input string symbol_name, inout longint unsigned address);
|
||||
|
||||
// Default legal opcode and funct7 for RV32I instructions
|
||||
bit [6:0] legal_i_opcode[$] = '{7'b0000011,
|
||||
|
|
6
verif/env/uvme/uvme_cva6_sb.sv
vendored
6
verif/env/uvme/uvme_cva6_sb.sv
vendored
|
@ -303,17 +303,17 @@ function void uvme_cva6_sb_c::check_mepc(uvma_isacov_instr_c instr);
|
|||
`uvm_info(get_type_name(), $sformatf("Trap is compressed ? : %h ", trap_is_compressed), UVM_DEBUG)
|
||||
if (trap_is_compressed) begin
|
||||
if (mepc_value != trap_pc + 'h2) begin
|
||||
`uvm_warning(get_type_name(), $sformatf("BE CAREFUL -> MEPC hasn't the next instruction's PC 2"))
|
||||
`uvm_info(get_type_name(), $sformatf("BE CAREFUL -> MEPC hasn't the next instruction's PC 2"), UVM_DEBUG)
|
||||
end
|
||||
end
|
||||
else begin
|
||||
if (mepc_value != trap_pc + 'h4) begin
|
||||
`uvm_warning(get_type_name(), $sformatf("BE CAREFUL -> MEPC hasn't the next instruction's PC 4"))
|
||||
`uvm_info(get_type_name(), $sformatf("BE CAREFUL -> MEPC hasn't the next instruction's PC 4"), UVM_DEBUG)
|
||||
end
|
||||
end
|
||||
end
|
||||
else begin
|
||||
`uvm_warning(get_type_name(), $sformatf("BE CAREFUL -> MEPC still has the trap pc, this could create an infinite loop "))
|
||||
`uvm_info(get_type_name(), $sformatf("BE CAREFUL -> MEPC still has the trap pc, this could create an infinite loop if the trap has been raised by an exception"), UVM_DEBUG)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -72,7 +72,7 @@ module cva6_tb_wrapper import uvmt_cva6_pkg::*; #(
|
|||
output logic [31:0] tb_exit_o,
|
||||
output rvfi_instr_t [CVA6Cfg.NrCommitPorts-1:0] rvfi_o,
|
||||
output rvfi_csr_t rvfi_csr_o,
|
||||
input logic [2:0] irq_i,
|
||||
input logic [15:0] irq_i,
|
||||
uvma_debug_if debug_if,
|
||||
uvma_axi_intf axi_slave,
|
||||
uvmt_axi_switch_intf axi_switch_vif,
|
||||
|
@ -105,8 +105,8 @@ module cva6_tb_wrapper import uvmt_cva6_pkg::*; #(
|
|||
.boot_addr_i ( boot_addr_i ),//Driving the boot_addr value from the core control agent
|
||||
.hart_id_i ( default_inputs_vif.hart_id ),
|
||||
.irq_i ( {1'b0, irq_i[0]} ),
|
||||
.ipi_i ( irq_i[1] ),
|
||||
.time_irq_i ( irq_i[2] ),
|
||||
.ipi_i ( 1'b0 ),
|
||||
.time_irq_i ( irq_i[1] ),
|
||||
.debug_req_i ( debug_if.debug_req ),
|
||||
.rvfi_probes_o ( rvfi_probes ),
|
||||
.cvxif_req_o ( cvxif_req ),
|
||||
|
|
|
@ -65,9 +65,10 @@ module uvmt_cva6_tb;
|
|||
.rst_n(clknrst_if.reset_n)
|
||||
);
|
||||
|
||||
uvma_interrupt_if
|
||||
interrupt_vif(
|
||||
);
|
||||
uvma_interrupt_if interrupt_vif(
|
||||
.clk(clknrst_if.clk),
|
||||
.reset_n(clknrst_if.reset_n)
|
||||
);
|
||||
|
||||
uvmt_axi_switch_intf axi_switch_vif();
|
||||
uvme_cva6_core_cntrl_if core_cntrl_if();
|
||||
|
|
|
@ -28,5 +28,5 @@
|
|||
- test: jump_to_zero
|
||||
iterations: 1
|
||||
path_var: TESTS_PATH
|
||||
gcc_opts: "-static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles ../tests/custom/common/syscalls.c ../tests/custom/common/crt.S -I../tests/custom/env -I../tests/custom/common -T ../tests/custom/common/test.ld -lgcc"
|
||||
gcc_opts: "-static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles ../tests/custom/common/syscalls.c ../tests/custom/common/crt.S -I../tests/custom/env -I../tests/custom/common -lgcc"
|
||||
asm_tests: <path_var>/custom/interrupt/jump_to_zero.S
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue