blob: e5510c13a931e05c0c8e27019f2ba9dc8724fac8 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
import alert_esc_agent_pkg::*;
class flash_ctrl_scoreboard #(
type CFG_T = flash_ctrl_env_cfg
) extends cip_base_scoreboard #(
.CFG_T(CFG_T),
.RAL_T(flash_ctrl_core_reg_block),
.COV_T(flash_ctrl_env_cov)
);
`uvm_component_param_utils(flash_ctrl_scoreboard#(CFG_T))
`uvm_component_new
uvm_reg_data_t data;
uvm_reg csr;
string csr_name;
flash_dv_part_e part = FlashPartData;
addr_t wr_addr;
addr_t rd_addr;
addr_t erase_addr;
bit [1:0] erase_bank_en;
int num_wr = 0;
int num_rd = 0;
int idx_wr = 0;
int idx_rd = 0;
bit part_sel = 0;
bit [1:0] info_sel = 2'b00;
bit wr_access = 1'b0;
bit rd_access = 1'b0;
bit erase_access = 1'b0;
bit erase_sel;
bit [1:0] curr_op;
tl_seq_item eflash_addr_phase_queue[$];
int num_erase_words;
int exp_alert_contd[string];
bit exp_alert_ff[string][$];
alert_handshake_e hs_state;
// ecc error expected
bit ecc_error_addr[bit [AddrWidth - 1 : 0]];
int over_rd_err[addr_t];
bit exp_tl_rsp_intg_err = 0;
//host error injection
bit in_error_addr[bit [AddrWidth - 1 : 0]];
// TLM agent fifos
uvm_tlm_analysis_fifo #(tl_seq_item) eflash_tl_a_chan_fifo;
uvm_tlm_analysis_fifo #(tl_seq_item) eflash_tl_d_chan_fifo;
bit skip_read_check = 0;
flash_phy_pkg::rd_buf_t evict_q[NumBanks][$];
// utility function to word-align an input TL address
function addr_t word_align_addr(addr_t addr);
return {addr[TL_AW-1:2], 2'b00};
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
eflash_tl_a_chan_fifo = new("eflash_tl_a_chan_fifo", this);
eflash_tl_d_chan_fifo = new("eflash_tl_d_chan_fifo", this);
hs_state = AlertComplete;
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
cfg.scb_h = this;
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
fork
process_eflash_tl_a_chan_fifo();
process_eflash_tl_d_chan_fifo();
mon_eviction();
mon_rma();
join_none
endtask
task mon_rma;
bit init_set = 0;
forever begin
@(negedge cfg.clk_rst_vif.clk);
if (init_set == 0 && cfg.flash_ctrl_vif.init == 1) begin
init_set = 1;
if (cfg.en_cov) cov.rma_init_cg.sample(cfg.flash_ctrl_vif.rma_state);
end
end
endtask // mon_rma
task mon_eviction;
flash_mp_region_cfg_t my_region;
bit ecc_en, scr_en;
int page;
bit [OTFBankId:0] addr;
forever begin
@(negedge cfg.clk_rst_vif.clk);
for (int i = 0;i < NumBanks; i++) begin
foreach (cfg.flash_ctrl_vif.hazard[i][j]) begin
if (cfg.flash_ctrl_vif.hazard[i][j]) begin
addr =cfg.flash_ctrl_vif.rd_buf[i][j].addr<<3;
page = cfg.addr2page(addr);
if (cfg.flash_ctrl_vif.rd_buf[i][j].part == 0) begin // data
my_region = cfg.get_region(page + 256*i);
end else begin // info
my_region =
cfg.get_region_from_info(
cfg.mp_info[i][cfg.flash_ctrl_vif.rd_buf[i][j].info_sel][page]);
end
ecc_en = (my_region.ecc_en == MuBi4True);
scr_en = (my_region.scramble_en == MuBi4True);
`uvm_info(`gfn,
$sformatf({"eviction bank%0d buffer%0d addr:0x%x(%x)",
" page:%0d ecc_en:%0d scr_en:%0d"},
i, j ,cfg.flash_ctrl_vif.rd_buf[i][j].addr,
addr, page, ecc_en, scr_en), UVM_MEDIUM)
if (cfg.en_cov) cov.eviction_cg.sample(j, {cfg.flash_ctrl_vif.evict_prog[i],
cfg.flash_ctrl_vif.evict_erase[i]},
{scr_en, ecc_en});
end
end
end
end
endtask // mon_eviction
// Task for receiving addr trans and storing them for later usage
virtual task process_eflash_tl_a_chan_fifo();
tl_seq_item item;
forever begin
eflash_tl_a_chan_fifo.get(item);
if (!cfg.en_scb) continue;
`uvm_info(`gfn, $sformatf(
"Received eflash_tl a_chan item:\n%0s", item.sprint(uvm_default_line_printer)),
UVM_HIGH)
// write the item into the addr queue
eflash_addr_phase_queue.push_back(item);
`uvm_info({`gfn, "::process_eflash_tl_a_chan_fifo()"}, $sformatf(
"Put ADDR_PHASE transaction into eflash_item_q: %0p", item), UVM_HIGH)
end
endtask
// Task for receiving data trans and checking if they matched with address trans
virtual task process_eflash_tl_d_chan_fifo();
tl_seq_item item;
tl_seq_item addr_item;
forever begin
eflash_tl_d_chan_fifo.get(item);
if (!cfg.en_scb) continue;
`uvm_info(`gfn, $sformatf(
"Received eflash_tl d_chan item:\n%0s", item.sprint(uvm_default_line_printer)),
UVM_HIGH)
// check tl packet integrity
void'(item.is_ok());
// check that address phase for this read is done
`DV_CHECK_GT_FATAL(eflash_addr_phase_queue.size(), 0)
addr_item = eflash_addr_phase_queue.pop_front();
`DV_CHECK_EQ(word_align_addr(item.a_addr), word_align_addr(addr_item.a_addr))
`DV_CHECK_EQ(item.a_source, addr_item.a_source)
if (cfg.block_host_rd) begin // blocking reads are checked with backdoor reads
check_trans(item);
end else begin // non blocking reads are pushed to the queue and in test checked
cfg.flash_rd_data.push_back(item.d_data);
end
end
endtask
// the TLUL response data are compared to the backdoor-read data using the bit-mask.
virtual function void check_trans(ref tl_seq_item trans);
flash_op_t flash_read;
logic [TL_DW-1:0] exp_data [$];
// Flash read trans
flash_read.partition = FlashPartData;
flash_read.erase_type = FlashErasePage;
flash_read.op = flash_ctrl_pkg::FlashOpRead;
flash_read.num_words = 1;
flash_read.addr = trans.a_addr;
//comparing backdoor read data and direct read data
cfg.flash_mem_bkdr_read(flash_read, exp_data);
`DV_CHECK_EQ(exp_data[0], trans.d_data)
// check data with internal memory
if (cfg.scb_check) begin
`uvm_info(`gfn, $sformatf("Direct read address 0x%0h data: 0x%0h", trans.a_addr, trans.d_data
), UVM_HIGH)
check_rd_data(flash_read.partition, trans.a_addr, trans.d_data);
`uvm_info(`gfn, $sformatf("Direct read successfully!!!"), UVM_HIGH)
end
endfunction
virtual task process_tl_access(tl_seq_item item, tl_channels_e channel, string ral_name);
uvm_reg csr;
string csr_wr_name = "";
bit do_read_check = 1'b1;
bit write = item.is_write();
uvm_reg_addr_t csr_addr = cfg.ral_models[ral_name].get_word_aligned_addr(item.a_addr);
bit addr_phase_read = (!write && channel == AddrChannel);
bit addr_phase_write = (write && channel == AddrChannel);
bit data_phase_read = (!write && channel == DataChannel);
bit data_phase_write = (write && channel == DataChannel);
bit erase_req;
if (skip_read_check) do_read_check = 0;
// if access was to a valid csr, get the csr handle
if ((is_mem_addr(
item, ral_name
) || (csr_addr inside {cfg.ral_models[ral_name].csr_addrs})) &&
!cfg.dir_rd_in_progress) begin
// if incoming access is a write to a valid csr, then make updates right away.
if (addr_phase_write) begin
if (is_mem_addr(item, ral_name) && cfg.scb_check) begin // prog fifo
if (idx_wr == 0) begin
csr_rd(.ptr(ral.addr), .value(data), .backdoor(1'b1));
wr_addr = word_align_addr(get_field_val(ral.addr.start, data));
csr_rd(.ptr(ral.control), .value(data), .backdoor(1'b1));
num_wr = get_field_val(ral.control.num, data);
part_sel = get_field_val(ral.control.partition_sel, data);
info_sel = get_field_val(ral.control.info_sel, data);
part = calc_part(part_sel, info_sel);
`uvm_info(`gfn, $sformatf("SCB WRITE ADDR: 0x%0h EXP ADDR: 0x%0h", csr_addr, wr_addr),
UVM_HIGH)
end else begin
wr_addr += 4;
end
write_allowed(part, wr_addr);
`uvm_info(`gfn, $sformatf("wr_access: 0x%0b wr_addr: 0x%0h", wr_access, wr_addr), UVM_LOW)
if (wr_access) begin
cfg.write_data_all_part(.part(part), .addr(wr_addr), .data(item.a_data));
end
if (idx_wr == num_wr) begin
idx_wr = 0;
end else begin
idx_wr += 1;
end
end else if (csr_addr inside {cfg.ral_models[ral_name].csr_addrs}) begin
csr = cfg.ral_models[ral_name].default_map.get_reg_by_offset(csr_addr);
`DV_CHECK_NE_FATAL(csr, null)
csr_wr_name = csr.get_name();
void'(csr.predict(.value(item.a_data), .kind(UVM_PREDICT_WRITE), .be(item.a_mask)));
`uvm_info(`gfn, $sformatf("SCB EXP FLASH REG: 0x%0h", csr_addr), UVM_HIGH)
if ((csr_wr_name == "control") && cfg.scb_check) begin
csr_rd(.ptr(ral.control), .value(data), .backdoor(1'b1));
curr_op = get_field_val(ral.control.op, data);
if (curr_op == 2) begin //erase op
erase_sel = get_field_val(ral.control.erase_sel, data);
part_sel = get_field_val(ral.control.partition_sel, data);
info_sel = get_field_val(ral.control.info_sel, data);
part = calc_part(part_sel, info_sel);
csr_rd(.ptr(ral.addr), .value(data), .backdoor(1'b1));
erase_addr = word_align_addr(get_field_val(ral.addr.start, data));
csr_rd(.ptr(ral.mp_bank_cfg_shadowed[0]), .value(data), .backdoor(1'b1));
`uvm_info(`gfn, $sformatf("UVM_REG_DATA: 0x%0p", data), UVM_HIGH)
erase_bank_en = data;
`uvm_info(
`gfn, $sformatf(
"erase_sel: 0x%0b part sel: 0x%0b info sel 0x%0d", erase_sel, part_sel, info_sel),
UVM_LOW)
`uvm_info(
`gfn, $sformatf(
"part: %0s addr: 0x%0h erase_bank_en: 0x%0h", part.name, erase_addr, erase_bank_en
), UVM_LOW)
erase_allowed(part, erase_sel, erase_addr, erase_bank_en);
`uvm_info(`gfn, $sformatf(
"erase_access: 0x%0b part:%0s erase_addr: 0x%0h",
erase_access,
part.name,
erase_addr
), UVM_LOW)
if (erase_access) begin
erase_data(part, erase_addr, erase_sel);
end
end
end
// coverage collection
case (csr_wr_name)
"control": begin
if (skip_read_check == 0) begin
csr_rd(.ptr(ral.control), .value(data), .backdoor(1'b1));
curr_op = get_field_val(ral.control.op, data);
erase_sel = get_field_val(ral.control.erase_sel, data);
part_sel = get_field_val(ral.control.partition_sel, data);
info_sel = get_field_val(ral.control.info_sel, data);
part = calc_part(part_sel, info_sel);
if (cfg.en_cov) begin
flash_op_t flash_op_cov;
flash_op_cov.partition = part;
flash_op_cov.erase_type = erase_sel;
flash_op_cov.op = curr_op;
cov.control_cg.sample(flash_op_cov);
end
end
end
"erase_suspend": begin
csr_rd(.ptr(ral.erase_suspend), .value(data), .backdoor(1'b1));
erase_req = get_field_val(ral.erase_suspend.req, data);
if (cfg.en_cov) begin
cov.erase_susp_cg.sample(erase_req);
end
end
"intr_test": begin
bit [TL_DW-1:0] intr_en = `gmv(ral.intr_enable);
bit [NumFlashCtrlIntr-1:0] intr_exp = `gmv(ral.intr_state);
intr_exp |= item.a_data;
foreach (intr_exp[i]) begin
if (cfg.en_cov) begin
cov.intr_test_cg.sample(i, item.a_data[i], intr_en[i], intr_exp[i]);
end
end
end
default: begin
// TODO: Uncomment once func cover is implemented
// `uvm_info(`gfn, $sformatf("Not for func coverage: %0s", csr.get_full_name()))
end
endcase
end
end
if (data_phase_read) begin
if (csr_addr inside {cfg.ral_models[ral_name].csr_addrs}) begin
csr = cfg.ral_models[ral_name].default_map.get_reg_by_offset(csr_addr);
`DV_CHECK_NE_FATAL(csr, null)
// process the csr req
// for write, update local variable and fifo at address phase
// for read, update predication at address phase and compare at data phase
if(!uvm_re_match("err_code*",csr.get_name())) begin
if (cfg.en_cov) begin
cov.sw_error_cg.sample(item.d_data);
end
end
case (csr.get_name())
// add individual case item for each csr
"intr_state": begin
bit [TL_DW-1:0] intr_en = `gmv(ral.intr_enable);
bit [NumFlashCtrlIntr-1:0] intr_exp = `gmv(ral.intr_state);
csr_rd(.ptr(ral.curr_fifo_lvl), .value(data), .backdoor(1'b1));
if (cfg.en_cov) begin
foreach (intr_exp[i]) begin
flash_ctrl_intr_e intr = flash_ctrl_intr_e'(i);
cov.intr_cg.sample(i, intr_en[i], item.d_data[i]);
cov.intr_pins_cg.sample(i, cfg.intr_vif.pins[i]);
end
cov.fifo_lvl_cg.sample(data[4:0], data[12:8]);
end
// Skip read check on intr_state CSR, since it is WO.
do_read_check = 1'b0;
end
"op_status", "status", "erase_suspend", "curr_fifo_lvl", "debug_state",
"ecc_single_err_cnt", "ecc_single_err_addr_0", "ecc_single_err_addr_1",
"std_fault_status": begin
do_read_check = 1'b0;
end
"err_code", "fault_status": begin
do_read_check = 1'b0;
end
default: begin
// TODO: uncomment when all CSRs are specified
// `uvm_fatal(`gfn, $sformatf("CSR access not processed: %0s", csr.get_full_name()))
end
endcase
// On reads, if do_read_check, is set, then check mirrored_value against item.d_data
if (do_read_check) begin
`DV_CHECK_EQ(csr.get_mirrored_value(), item.d_data, $sformatf(
"reg name: %0s", csr.get_full_name()))
end
void'(csr.predict(.value(item.d_data), .kind(UVM_PREDICT_READ)));
end else if (is_mem_addr(item, ral_name) && cfg.scb_check) begin // rd fifo
if (idx_rd == 0) begin
csr_rd(.ptr(ral.addr), .value(data), .backdoor(1'b1));
rd_addr = word_align_addr(get_field_val(ral.addr.start, data));
csr_rd(.ptr(ral.control), .value(data), .backdoor(1'b1));
num_rd = get_field_val(ral.control.num, data);
part_sel = get_field_val(ral.control.partition_sel, data);
info_sel = get_field_val(ral.control.info_sel, data);
part = calc_part(part_sel, info_sel);
`uvm_info(`gfn, $sformatf("SCB READ ADDR: 0x%0h EXP ADDR: 0x%0h", csr_addr, rd_addr),
UVM_HIGH)
end else begin
rd_addr += 4;
end
read_allowed(part, rd_addr);
`uvm_info(`gfn, $sformatf("rd_access: 0x%0b", rd_access), UVM_LOW)
if (rd_access) begin
check_rd_data(part, rd_addr, item.d_data);
end
if (idx_rd == num_rd) begin
idx_rd = 0;
end else begin
idx_rd += 1;
end
end
end
end
endtask
virtual function void reset(string kind = "HARD");
super.reset(kind);
foreach(cfg.list_of_alerts[i]) begin
exp_alert_contd[i] = 0;
end
// reset local fifos queues and variables
eflash_tl_a_chan_fifo.flush();
eflash_tl_d_chan_fifo.flush();
cfg.scb_flash_data.delete();
cfg.scb_flash_info.delete();
cfg.scb_flash_info1.delete();
cfg.scb_flash_info2.delete();
endfunction
virtual function void check_phase(uvm_phase phase);
super.check_phase(phase);
// post test checks - ensure that all local fifos and queues are empty
`DV_EOT_PRINT_TLM_FIFO_CONTENTS(tl_seq_item, eflash_tl_a_chan_fifo)
`DV_EOT_PRINT_TLM_FIFO_CONTENTS(tl_seq_item, eflash_tl_d_chan_fifo)
if (cfg.en_scb) begin
`DV_CHECK_EQ(eflash_addr_phase_queue.size, 0)
end
if (cfg.scb_check && cfg.check_full_scb_mem_model) begin
cfg.check_mem_model();
end
`DV_CHECK_EQ(cfg.tlul_core_obs_cnt, cfg.tlul_core_exp_cnt,
"core_tlul_error_cnt mismatch")
endfunction
virtual function flash_dv_part_e calc_part(bit part_sel, bit [1:0] info_sel);
if (!part_sel) begin
return FlashPartData;
end else begin
case (info_sel)
2'b00: return FlashPartInfo;
2'b01: return FlashPartInfo1;
2'b10: return FlashPartInfo2;
default: begin
`uvm_fatal("flash_ctrl_scoreboard", $sformatf("unknown info part sel 0x%0h", info_sel))
end
endcase
end
endfunction
virtual function void check_rd_data(flash_dv_part_e part, addr_t addr,
ref data_t data);
case (part)
FlashPartData: begin
check_rd_part(cfg.scb_flash_data, addr, data);
end
FlashPartInfo: begin
check_rd_part(cfg.scb_flash_info, addr, data);
end
FlashPartInfo1: begin
check_rd_part(cfg.scb_flash_info1, addr, data);
end
FlashPartInfo2: begin
check_rd_part(cfg.scb_flash_info2, addr, data);
end
default: `uvm_fatal(`gfn, "flash_ctrl_scoreboard: Partition type not supported!")
endcase
endfunction
virtual function void erase_data(flash_dv_part_e part, addr_t addr, bit sel);
case (part)
FlashPartData: begin
erase_page_bank(NUM_BK_DATA_WORDS, addr, sel, cfg.scb_flash_data, "scb_flash_data");
end
FlashPartInfo: begin
if (sel) begin
erase_page_bank(NUM_BK_DATA_WORDS, addr, sel, cfg.scb_flash_data, "scb_flash_data");
end
erase_page_bank(NUM_BK_INFO_WORDS, addr, sel, cfg.scb_flash_info, "scb_flash_info");
end
FlashPartInfo1: begin
if (!sel) begin
erase_page_bank(NUM_PAGE_WORDS, addr, sel, cfg.scb_flash_info1, "scb_flash_info1");
end else begin
`uvm_fatal(`gfn, "flash_ctrl_scoreboard: Bank erase for INFO1 part not supported!")
end
end
FlashPartInfo2: begin
if (!sel) begin
erase_page_bank(NUM_PAGE_WORDS, addr, sel, cfg.scb_flash_info2, "scb_flash_info2");
end else begin
`uvm_fatal(`gfn, "flash_ctrl_scoreboard: Bank erase for INFO2 part not supported!")
end
end
default: `uvm_fatal(`gfn, "flash_ctrl_scoreboard: Partition type not supported!")
endcase
endfunction
virtual task write_allowed(ref flash_dv_part_e part, ref addr_t in_addr);
bit en;
bit prog_en;
bit prog_en_def;
bit [8:0] base;
bit [9:0] size;
bit bk_idx;
int pg_idx;
bit wr_access_found;
wr_access_found = 1'b0;
wr_access = 1'b0;
case (part)
FlashPartData: begin
for (int i = 0; i < cfg.seq_cfg.num_en_mp_regions; i++) begin
if (!wr_access_found) begin
csr_rd(.ptr(ral.mp_region_cfg[i]), .value(data), .backdoor(1'b1));
en = mubi4_test_true_strict(
get_field_val(ral.mp_region_cfg[i].en, data));
prog_en = mubi4_test_true_strict(
get_field_val(ral.mp_region_cfg[i].prog_en, data));
csr_rd(.ptr(ral.mp_region[i]), .value(data), .backdoor(1'b1));
base = get_field_val(ral.mp_region[i].base, data);
size = get_field_val(ral.mp_region[i].size, data);
if (in_addr inside {[base*BytesPerPage:base*BytesPerPage+size*BytesPerPage]}) begin
if (en) begin
wr_access = prog_en;
wr_access_found = 1'b1;
end
end
end
end
if (!wr_access_found) begin
csr_rd(.ptr(ral.default_region), .value(data), .backdoor(1'b1));
prog_en_def = mubi4_test_true_strict(
get_field_val(ral.default_region.prog_en, data));
wr_access = prog_en_def;
wr_access_found = 1'b1;
end
end
FlashPartInfo: begin
bk_idx = in_addr[19];
pg_idx = in_addr[18:11];
csr_name = $sformatf("bank%0d_info0_page_cfg_%0d", bk_idx, pg_idx);
write_access_info();
end
FlashPartInfo1: begin
bk_idx = in_addr[19];
csr_name = $sformatf("bank%0d_info1_page_cfg", bk_idx);
write_access_info();
end
FlashPartInfo2: begin
bk_idx = in_addr[19];
pg_idx = in_addr[18:11];
csr_name = $sformatf("bank%0d_info2_page_cfg_%0d", bk_idx, pg_idx);
write_access_info();
end
default: `uvm_fatal(`gfn, "flash_ctrl_scoreboard: Partition type not supported!")
endcase
endtask
virtual task read_allowed(ref flash_dv_part_e part, ref addr_t in_rd_addr);
bit en;
bit read_en;
bit read_en_def;
bit [8:0] base;
bit [9:0] size;
bit bk_idx;
int pg_idx;
bit rd_access_found;
rd_access_found = 1'b0;
rd_access = 1'b0;
case (part)
FlashPartData: begin
for (int i = 0; i < cfg.seq_cfg.num_en_mp_regions; i++) begin
if (!rd_access_found) begin
csr_rd(.ptr(ral.mp_region_cfg[i]), .value(data), .backdoor(1'b1));
en = mubi4_test_true_strict(
get_field_val(ral.mp_region_cfg[i].en, data));
read_en = mubi4_test_true_strict(
get_field_val(ral.mp_region_cfg[i].rd_en, data));
csr_rd(.ptr(ral.mp_region[i]), .value(data), .backdoor(1'b1));
base = get_field_val(ral.mp_region[i].base, data);
size = get_field_val(ral.mp_region[i].size, data);
if (in_rd_addr inside {[base*BytesPerPage:base*BytesPerPage+size*BytesPerPage]}) begin
if (en) begin
rd_access_found = 1'b1;
end
end
end
end
if (!rd_access_found) begin
csr_rd(.ptr(ral.default_region), .value(data), .backdoor(1'b1));
read_en_def = mubi4_test_true_strict(
get_field_val(ral.default_region.rd_en, data));
rd_access = read_en_def;
rd_access_found = 1'b1;
end
end
FlashPartInfo: begin
bk_idx = in_rd_addr[19];
pg_idx = in_rd_addr[18:11];
csr_name = $sformatf("bank%0d_info0_page_cfg_%0d", bk_idx, pg_idx);
read_access_info();
end
FlashPartInfo1: begin
bk_idx = in_rd_addr[19];
csr_name = $sformatf("bank%0d_info1_page_cfg", bk_idx);
read_access_info();
end
FlashPartInfo2: begin
bk_idx = in_rd_addr[19];
pg_idx = in_rd_addr[18:11];
csr_name = $sformatf("bank%0d_info2_page_cfg_%0d", bk_idx, pg_idx);
read_access_info();
end
default: `uvm_fatal(`gfn, "flash_ctrl_scoreboard: Partition type not supported!")
endcase
endtask
virtual task erase_allowed(ref flash_dv_part_e part, bit erase_sel,
ref addr_t in_erase_addr, bit [1:0] bk_en);
bit en;
bit erase_en;
bit erase_en_def;
bit [8:0] base;
bit [9:0] size;
bit bk_idx;
int pg_idx;
bit erase_access_found;
erase_access_found = 1'b0;
erase_access = 1'b0;
if (!erase_sel) begin // page erase
case (part)
FlashPartData: begin
for (int i = 0; i < cfg.seq_cfg.num_en_mp_regions; i++) begin
if (!erase_access_found) begin
csr_rd(.ptr(ral.mp_region_cfg[i]), .value(data), .backdoor(1'b1));
en = mubi4_test_true_strict(get_field_val(ral.mp_region_cfg[i].en, data));
erase_en = mubi4_test_true_strict(get_field_val(ral.mp_region_cfg[i].erase_en, data));
csr_rd(.ptr(ral.mp_region[i]), .value(data), .backdoor(1'b1));
base = get_field_val(ral.mp_region[i].base, data);
size = get_field_val(ral.mp_region[i].size, data);
if (in_erase_addr
inside {[base*BytesPerPage:base*BytesPerPage+size*BytesPerPage-1]}) begin
if (en) begin
erase_access = erase_en;
erase_access_found = 1'b1;
end
end
end
end
if (!erase_access_found) begin
csr_rd(.ptr(ral.default_region), .value(data), .backdoor(1'b1));
erase_en_def = mubi4_test_true_strict(get_field_val(ral.default_region.erase_en, data));
erase_access = erase_en_def;
erase_access_found = 1'b1;
end
end
FlashPartInfo: begin
bk_idx = in_erase_addr[19];
pg_idx = in_erase_addr[18:11];
csr_name = $sformatf("bank%0d_info0_page_cfg_%0d", bk_idx, pg_idx);
erase_access_info();
end
FlashPartInfo1: begin
bk_idx = in_erase_addr[19];
csr_name = $sformatf("bank%0d_info1_page_cfg", bk_idx);
erase_access_info();
end
FlashPartInfo2: begin
bk_idx = in_erase_addr[19];
pg_idx = in_erase_addr[18:11];
csr_name = $sformatf("bank%0d_info2_page_cfg_%0d", bk_idx, pg_idx);
erase_access_info();
end
default: `uvm_fatal(`gfn, "flash_ctrl_scoreboard: Partition type not supported!")
endcase
end else begin // bank erase
bk_idx = in_erase_addr[19];
erase_access = bk_en[bk_idx];
`uvm_info(`gfn, $sformatf("erase_access bank: 0x%0b", erase_access), UVM_LOW)
end
endtask
virtual function void check_rd_part(const ref data_model_t exp_data_part,
addr_t addr, ref data_t data);
if (exp_data_part.exists(addr)) begin
`uvm_info(
`gfn, $sformatf(
"addr: 0x%0h scb data: 0x%0h data 0: 0x%0h", addr, exp_data_part[addr], exp_data_part[0]),
UVM_HIGH)
`DV_CHECK_EQ(exp_data_part[addr], data, $sformatf("read addr:0x%0h data: 0x%0h", addr, data))
end else begin
`uvm_info(`gfn, $sformatf("addr %0h is not written!", addr), UVM_MEDIUM)
end
endfunction
virtual task write_access_info();
bit en;
bit prog_en;
csr = ral.get_reg_by_name(csr_name);
csr_rd(.ptr(csr), .value(data), .backdoor(1'b1));
en = mubi4_test_true_strict(
get_field_val(csr.get_field_by_name("en"), data));
prog_en = mubi4_test_true_strict(
get_field_val(csr.get_field_by_name("prog_en"), data));
if (en) begin
wr_access = prog_en;
end else begin
wr_access = 0; //protected
end
endtask
virtual task read_access_info();
bit en;
bit read_en;
csr = ral.get_reg_by_name(csr_name);
csr_rd(.ptr(csr), .value(data), .backdoor(1'b1));
en = mubi4_test_true_strict(
get_field_val(csr.get_field_by_name("en"), data));
read_en = mubi4_test_true_strict(
get_field_val(csr.get_field_by_name("rd_en"), data));
if (en) begin
rd_access = read_en;
end else begin
rd_access = 0; //protected
end
endtask
virtual task erase_access_info();
bit en;
bit erase_en;
csr = ral.get_reg_by_name(csr_name);
csr_rd(.ptr(csr), .value(data), .backdoor(1'b1));
en = mubi4_test_true_strict(
get_field_val(csr.get_field_by_name("en"), data));
erase_en = mubi4_test_true_strict(
get_field_val(csr.get_field_by_name("erase_en"),data));
if (en) begin
erase_access = erase_en;
end else begin
erase_access = 0; //protected
end
endtask
virtual function void erase_page_bank(int num_bk_words, addr_t addr, bit sel,
ref data_model_t exp_part,
input string scb_mem_name);
int num_wr;
if (sel) begin // bank sel
num_wr = num_bk_words;
`uvm_info(`gfn, $sformatf("num_wr: %0d", num_wr), UVM_LOW)
if (addr[19]) begin // bank 1
addr = BytesPerBank;
end else begin // bank 0
addr = 0;
end
end else begin // page sel
num_wr = NUM_PAGE_WORDS;
addr = {addr[19:11], {11{1'b0}}};
end
for (int i = 0; i < num_wr; i++) begin
if (exp_part.exists(addr)) begin
exp_part[addr] = {TL_DW{1'b1}};
`uvm_info(`gfn, $sformatf("ERASE ADDR:0x%0h %s: 0x%0h", addr, scb_mem_name, exp_part[addr]),
UVM_LOW)
end
addr = addr + 4;
end
endfunction
// Overriden function from cip_base_scoreboard, to handle TL/UL Error seen on Hardware Interface
// when using Code Access Restrictions (EXEC)
virtual function bit predict_tl_err(tl_seq_item item, tl_channels_e channel, string ral_name);
bit ecc_err, in_err;
// For flash, address has to be 8byte aligned.
ecc_err = ecc_error_addr.exists({item.a_addr[AddrWidth-1:3],3'b0});
in_err = in_error_addr.exists({item.a_addr[AddrWidth-1:3],3'b0});
`uvm_info("predict_tl_err_dbg",
$sformatf({"addr:0x%x(%x) ecc_err:%0d in_err:%0d channel:%s ral_name:%s",
" tlul_exp_cnt:%0d"},
{item.a_addr[AddrWidth-1:3],3'b0},
item.a_addr, ecc_err, in_err,
channel.name, ral_name,
cfg.tlul_core_exp_cnt
), UVM_HIGH)
if (over_rd_err.exists(item.a_addr)) begin
if (channel == DataChannel) begin
over_rd_err[item.a_addr]--;
if (over_rd_err[item.a_addr] == 0) over_rd_err.delete(item.a_addr);
`uvm_info(`gfn, $sformatf("addr is clear 0x%x", item.a_addr), UVM_HIGH)
end
return 1;
end
if (ral_name == cfg.flash_ral_name) begin
if (get_flash_instr_type_err(item, channel)) return (1);
if (cfg.tlul_eflash_exp_cnt > 0 && item.d_error == 1) begin
cfg.tlul_eflash_obs_cnt++;
return 1;
end
end else begin
if (cfg.tlul_core_exp_cnt > 0 && item.d_error == 1) begin
cfg.tlul_core_obs_cnt++;
return 1;
end
end
if (ecc_err | in_err) begin
if (channel == DataChannel) begin
`DV_CHECK_EQ(item.d_error, 1,
$sformatf("On interface %s, TL item: %s, ecc_err:%0d in_err:%0d",
ral_name, item.sprint(uvm_default_line_printer),
ecc_err, in_err))
return 1;
end
end
if (exp_tl_rsp_intg_err) begin
return (!item.is_d_chan_intg_ok(.throw_error(0)));
end
return (super.predict_tl_err(item, channel, ral_name));
endfunction : predict_tl_err
// Check if the input tl_seq_item has any tl errors.
virtual function bit get_flash_instr_type_err(tl_seq_item item, tl_channels_e channel);
bit is_exec_key = `gmv(ral.exec) == CODE_EXEC_KEY;
// Local Variable
tlul_pkg::tl_a_user_t a_user = item.a_user;
if (cfg.en_cov) begin
if (channel == AddrChannel) begin
cov.fetch_code_cg.sample(is_exec_key, a_user.instr_type);
end
end
// If Data Access, or a Write, or the CODE_EXEC_KEY Matches
if (((a_user.instr_type == MuBi4False) || (item.a_opcode != tlul_pkg::Get)) ||
(`gmv(ral.exec) == CODE_EXEC_KEY)) return(0); // No Error Predicted
// Error is Predicted, Expect an Error if Channel==DataChannel
if (channel == DataChannel) begin
`uvm_info(`gfn, "TL Error Expected", UVM_HIGH)
`DV_CHECK_EQ(item.d_error, 1)
end
return (1); // Error Predicted
endfunction : get_flash_instr_type_err
function void process_alert(string alert_name, alert_esc_seq_item item);
bit pop_out;
if (!(alert_name inside {cfg.list_of_alerts})) begin
`uvm_fatal(`gfn, $sformatf("alert_name %0s is not in cfg.list_of_alerts!", alert_name))
end
hs_state = item.alert_handshake_sta;
`uvm_info(`gfn, $sformatf("alert %0s detected, alert_status is %s exp:%0d contd:%0d",
alert_name,
item.alert_handshake_sta,
expected_alert[alert_name].expected,
exp_alert_contd[alert_name]
), UVM_MEDIUM)
if (item.alert_handshake_sta == AlertReceived) begin
under_alert_handshake[alert_name] = 1;
if (exp_alert_ff[alert_name].size > 0) expected_alert[alert_name].expected = 1;
on_alert(alert_name, item);
alert_count[alert_name]++;
end else begin
if (!cfg.under_reset && under_alert_handshake[alert_name] == 0) begin
`uvm_error(`gfn, $sformatf("alert %0s is not received!", alert_name))
end
pop_out = exp_alert_ff[alert_name].pop_front();
if (exp_alert_ff[alert_name].size() == 0) expected_alert[alert_name].expected = 0;
under_alert_handshake[alert_name] = 0;
if (exp_alert_contd[alert_name] > 0) begin
expected_alert[alert_name].expected = 1;
exp_alert_contd[alert_name]--;
end
end
endfunction
endclass : flash_ctrl_scoreboard