blob: f4f25f84bb639fdb83d96ec5abbb61815e4117ce [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// This is base class for on the fly mode test sequence.
// On the fly mode test checks data integrity per transaction (program or read),
// and doesn't rely on reference memory model in the test bench.
class flash_ctrl_otf_base_vseq extends flash_ctrl_base_vseq;
`uvm_object_utils(flash_ctrl_otf_base_vseq)
`uvm_object_new
// Used for tracing programmed data
bit [15:0] global_pat_cnt = 16'hA000;
// Double bit err is created
bit global_derr_is_set = 0;
// Trace host read ountstanding
int d_cnt1, d_cnt2;
// Number of controller transactions per a single task
// Min: 1 Max:32
rand int ctrl_num;
rand int ctrl_info_num;
rand bit is_addr_odd;
rand int fractions;
// flash op
// WIP. not all field is valid.
rand flash_op_t rand_op;
// Permission to access special partition
rand bit [2:0] allow_spec_info_acc;
rand bit all_entry_en;
// scramble and ecc config mode
rand otf_cfg_mode_e scr_ecc_cfg;
rand flash_mp_region_cfg_t rand_regions[MpRegions];
rand flash_bank_mp_info_page_cfg_t rand_info[NumBanks][InfoTypes][$];
flash_mem_init_e otf_flash_init = FlashMemInitEccMode;
constraint all_ent_c {
solve all_entry_en before rand_regions, rand_info;
if (cfg.en_always_any) all_entry_en == 1;
else all_entry_en dist { 1 := 1, 0 := 4};
}
constraint scr_ecc_c {
scr_ecc_cfg dist { OTFCfgRand := 5, OTFCfgTrue := 4};
}
constraint rand_regions_c {
foreach (rand_regions[i]) {
if (all_entry_en) rand_regions[i].en == MuBi4True;
rand_regions[i].start_page dist {
0 := 1,
[1 : FlashNumPages - 2] :/ 8,
FlashNumPages - 1 := 1
};
rand_regions[i].num_pages inside {[1 : FlashNumPages - rand_regions[i].start_page]};
rand_regions[i].num_pages <= 32;
rand_regions[i].scramble_en dist { MuBi4True := 4, MuBi4False := 1};
rand_regions[i].ecc_en dist { MuBi4True := 4, MuBi4False := 1};
}
}
constraint rand_info_c {
foreach (rand_info[i, j]) {
rand_info[i][j].size() == InfoTypeSize[j];
foreach (rand_info[i][j][k]) {
if (all_entry_en) rand_info[i][j][k].en == MuBi4True;
rand_info[i][j][k].en dist { MuBi4True := 4, MuBi4False :=1};
if (cfg.en_always_read) rand_info[i][j][k].read_en == MuBi4True;
rand_info[i][j][k].scramble_en dist { MuBi4True := 4, MuBi4False :=1};
rand_info[i][j][k].ecc_en dist { MuBi4True := 4, MuBi4False :=1};
}
}
}
constraint ctrl_num_c {
ctrl_num dist { CTRL_TRANS_MIN := 2, [2:31] :/ 1, CTRL_TRANS_MAX := 2};
}
constraint fractions_c {
fractions dist { [1:4] := 4, [5:15] := 1, 16 := 1};
}
constraint ctrl_info_num_c {
solve rand_op before ctrl_info_num;
ctrl_info_num inside {[1 : InfoTypeSize[rand_op.partition >> 1]]};
if (cfg.ecc_mode > FlashEccEnabled) ctrl_info_num * fractions <= 128;
}
constraint rand_op_c {
solve fractions before rand_op.addr;
solve flash_program_data before rand_op;
solve rand_op.partition before rand_op.prog_sel, rand_op.addr;
solve rand_op.addr before rand_op.otf_addr;
solve rand_op.addr before rand_op.num_words;
rand_op.partition dist { FlashPartData := 1, [FlashPartInfo:FlashPartInfo2] :/ 1};
rand_op.addr[TL_AW-1:BusAddrByteW] == 'h0;
rand_op.addr[1:0] == 'h0;
// If address starts from 0x4 and full prog_win size access(16),
// transaction creates prog_win error.
// To prevent that, make full size access always start from address[2:0] == 0.
if (fractions == 16) rand_op.addr[2] == 0;
if (rand_op.partition != FlashPartData) {
rand_op.addr inside {[0:InfoTypeBytes[rand_op.partition>>1]-1]};
rand_op.prog_sel == 1;
} else {
rand_op.prog_sel == 0;
}
rand_op.otf_addr == rand_op.addr[BusAddrByteW-2:0];
rand_op.num_words inside {[1:16]};
rand_op.addr[5:0] + ((rand_op.num_words - 1) * 4) < 64;
}
constraint special_info_acc_c {
allow_spec_info_acc dist { 3'h7 := 1, 3'h0 := 1, [1:6] :/ 2};
}
function void post_randomize();
super.post_randomize();
foreach (rand_regions[i]) begin
if (cfg.en_always_read) rand_regions[i].read_en = MuBi4True;
if (cfg.en_always_prog) rand_regions[i].program_en = MuBi4True;
if (cfg.en_always_erase) rand_regions[i].erase_en = MuBi4True;
end
foreach (rand_info[i, j, k]) begin
if (cfg.en_always_read) rand_info[i][j][k].read_en = MuBi4True;
if (cfg.en_always_prog) rand_info[i][j][k].program_en = MuBi4True;
if (cfg.en_always_erase) rand_info[i][j][k].erase_en = MuBi4True;
end
if (cfg.en_all_info_acc) allow_spec_info_acc = 3'h7;
// overwrite secret_partition cfg with hw_cfg
rand_info[0][0][1] = conv2env_mp_info(flash_ctrl_pkg::CfgAllowRead);
rand_info[0][0][2] = conv2env_mp_info(flash_ctrl_pkg::CfgAllowRead);
endfunction // post_randomize
virtual task pre_start();
bit csr_test_mode = 0;
string run_seq_name = "";
// Erased page doesn't go through descramble.
// To maintain high stress rate,
// keep flash_init to FlashMemInitRandomize
void'($value$plusargs("csr_test_mode=%0b", csr_test_mode));
void'($value$plusargs("run_%0s", run_seq_name));
if (csr_test_mode == 1 ||
run_seq_name inside{"tl_intg_err", "sec_cm_fi"}) begin
cfg.skip_init = 1;
super.pre_start();
end else begin
flash_init_c.constraint_mode(0);
if (cfg.ecc_mode > FlashEccEnabled) begin
foreach (cfg.tgt_pre[partition]) begin
cfg.tgt_pre[partition].shuffle();
`uvm_info("cfg_summary",
$sformatf("prefix:%s:rd:%2b dr:%2b wr:%2b er:%2b",
partition.name, cfg.tgt_pre[partition][TgtRd],
cfg.tgt_pre[partition][TgtDr], cfg.tgt_pre[partition][TgtWr],
cfg.tgt_pre[partition][TgtEr]),
UVM_MEDIUM)
end
end
flash_init = otf_flash_init;
init_p2r_map();
`uvm_info("cfg_summary",
$sformatf({"flash_init:%s ecc_mode %s allow_spec_info_acc:%3b",
" scr_ecc_cfg:%s always_read:%0d"},
flash_init.name, cfg.ecc_mode.name, allow_spec_info_acc,
scr_ecc_cfg.name, cfg.en_always_read),
UVM_MEDIUM)
configure_otf_mode();
super.pre_start();
if (cfg.seq_cfg.en_init_keys_seeds == 1) begin
`DV_SPINWAIT(while (otp_key_init_done != 2'b11) cfg.clk_rst_vif.wait_clks(1);,
"timeout waiting otp_key_init_done", 100_000)
end
// Need additional flash update after key init is done
case (cfg.ecc_mode)
FlashEccDisabled: begin
// In this mode, write and read are not separated.
// When write and read happen at the same address,
// unexpected ecc error can be created.
flash_otf_region_cfg();
end
FlashEccEnabled: begin
// This mode use tb memory model.
flash_otf_region_cfg(.scr_mode(scr_ecc_cfg), .ecc_mode(scr_ecc_cfg));
end
default: begin
flash_otf_region_cfg(.scr_mode(scr_ecc_cfg), .ecc_mode(OTFCfgTrue));
// update_secret_partition program random data to all secret partition.
// revert change and keep update only for read zone.
flash_otf_set_secret_part();
flash_otf_mem_read_zone_init();
end
endcase // case (cfg.ecc_mode)
if (cfg.ecc_mode > FlashSerrTestMode) begin
cfg.scb_h.do_alert_check = 0;
end
cfg.allow_spec_info_acc = allow_spec_info_acc;
update_partition_access(cfg.allow_spec_info_acc);
// Polling init wip is done
csr_spinwait(.ptr(ral.status.init_wip), .exp_data(1'b0));
cfg.m_fpp_agent_cfg.mon_start = 1;
`uvm_info("pre_start", "TEST PARAM SUMMARY", UVM_MEDIUM)
`uvm_info("pre_start", " ** sequence param", UVM_MEDIUM)
`uvm_info("pre_start", $sformatf({" otf_num_rw:%0d otf_num_hr:%0d",
" otf_wr_pct:%0d otf_rd_pct:%0d"},
cfg.otf_num_rw,
cfg.otf_num_hr,
cfg.otf_wr_pct,
cfg.otf_rd_pct), UVM_MEDIUM)
if (cfg.intr_mode == 1) begin
cfg.rd_lvl = $urandom_range(1,15);
cfg.wr_lvl = $urandom_range(1,3);
`uvm_info("pre_start", $sformatf("interrupt testmode. rd_lvl:%0d wr_lvl:%0d",
cfg.rd_lvl, cfg.wr_lvl), UVM_MEDIUM)
flash_ctrl_fifo_levels_cfg_intr(cfg.rd_lvl, cfg.wr_lvl);
flash_ctrl_intr_enable(6'h3f);
end
end
endtask
// On the fly scoreboard mode
// This will disable reference memory check in the end of the test
// as well as all intermediate transaction update for memory model.
function void configure_otf_mode();
cfg.flash_ctrl_vif.lc_creator_seed_sw_rw_en = lc_ctrl_pkg::On;
cfg.flash_ctrl_vif.lc_owner_seed_sw_rw_en = lc_ctrl_pkg::On;
cfg.flash_ctrl_vif.lc_iso_part_sw_rd_en = lc_ctrl_pkg::On;
cfg.flash_ctrl_vif.lc_iso_part_sw_wr_en = lc_ctrl_pkg::On;
cfg.seq_cfg.en_init_keys_seeds = 1;
cfg.scb_check = 0;
cfg.check_full_scb_mem_model = 0;
cfg.scb_otf_en = 1;
foreach (cfg.m_tl_agent_cfgs[i]) begin
cfg.m_tl_agent_cfgs[i].a_valid_delay_min = 0;
cfg.m_tl_agent_cfgs[i].a_valid_delay_max = 0;
cfg.m_tl_agent_cfgs[i].d_valid_delay_min = 0;
cfg.m_tl_agent_cfgs[i].d_valid_delay_max = 0;
cfg.m_tl_agent_cfgs[i].a_ready_delay_min = 0;
cfg.m_tl_agent_cfgs[i].a_ready_delay_max = 0;
cfg.m_tl_agent_cfgs[i].d_ready_delay_min = 0;
cfg.m_tl_agent_cfgs[i].d_ready_delay_max = 0;
end
endfunction
// Program flash in the unit of minimum resolution (4Byte)
// If data is not aligned to 8Byte, rtl pads all F to
// upper or lower 4Byte.
// @arg: flash_op_p : command struct return updated address after write
// @arg: bank: bank index to access flash
// @arg: num : number of 8 words range: [1 : 32]
// @arg: wd : number of 4byte (TL bus unit) : default : 16
// @arg: in_err : inject fatal error causes flash access disable
task prog_flash(ref flash_op_t flash_op, input int bank, int num, int wd = 16,
bit in_err = 0, bit store_prog_data = 0);
data_q_t flash_data_chunk;
flash_otf_item exp_item;
bit poll_fifo_status = ~in_err;
bit [15:0] lcnt = 0;
bit [flash_ctrl_pkg::BusAddrByteW-1:0] start_addr, end_addr;
data_4s_t tmp_data;
int tail, is_odd;
int unit_word;
int tot_wd;
int page;
bit overflow = 0;
bit drop = 0;
flash_mp_region_cfg_t my_region;
is_odd = flash_op.otf_addr[2];
tot_wd = wd * num + is_odd;
flash_op.op = FlashOpProgram;
flash_op.num_words = wd;
if (cfg.ecc_mode > FlashEccEnabled) begin
if (flash_op.partition == FlashPartData) begin
flash_op.otf_addr[18:17] = cfg.tgt_pre[flash_op.partition][TgtWr];
end else begin
flash_op.otf_addr[10:9] = cfg.tgt_pre[flash_op.partition][TgtWr];
end
end
start_addr = flash_op.otf_addr;
// last byte address in each program
end_addr = start_addr + (tot_wd * 4) - 1;
`uvm_info("prog_flash",$sformatf("begin addr:%x part:%s num:%0d wd:%0d st:%x ed:%x",
flash_op.otf_addr, flash_op.partition.name, num,
wd, start_addr, end_addr), UVM_MEDIUM)
if (cfg.ecc_mode > FlashEccEnabled) begin
if (flash_op.partition == FlashPartData) begin
overflow = (end_addr[18:17] != start_addr[18:17] ||
end_addr[16:0] > 17'h1_FE00);
end else begin
overflow = (end_addr[10:9] != start_addr[10:9]);
end
end else begin
overflow = (start_addr[OTFHostId] != end_addr[OTFHostId]);
end
if (overflow) begin
bit [flash_ctrl_pkg::BusAddrByteW-1:0] tmp_addr = start_addr;
if (flash_op.partition == FlashPartData) begin
start_addr[16:0] = 'h0;
end else begin
start_addr[8:0] = 'h0;
end
`uvm_info("prog_flash", $sformatf({"overflow!, start:%x end:%x part:%s",
" roll over start address to 0x%x"},
tmp_addr, end_addr, flash_op.partition.name,
start_addr), UVM_MEDIUM)
is_odd = flash_op.otf_addr[2];
tot_wd = wd * num + is_odd;
end_addr = start_addr + (tot_wd * 4) - 1;
end
// Check if end_addr overflows.
// Roll over start address if this is the case.
`uvm_info("prog_flash", $sformatf({"bank:%0d otf_addr:0x%0h,",
" part:%s size:%0d x %0d x 4B"},
bank, flash_op.otf_addr, flash_op.partition.name,
num, wd), UVM_MEDIUM)
flash_op.otf_addr = start_addr;
for (int i = 0; i < num; i++) begin
flash_program_data = '{};
tail = 0;
drop = 0;
is_odd = flash_op.otf_addr[2];
end_addr = flash_op.otf_addr + ((wd + is_odd) * 4) - 1;
// Check resolution error
// Current resolution : 0x40.
// Check if address[6] is same for start and end addr.
`uvm_info("prog_flash", $sformatf("start_addr:%x end_addr:%x",
flash_op.otf_addr, end_addr), UVM_HIGH)
if (flash_op.otf_addr[6] != end_addr[6]) begin
`uvm_info("prog_flash", $sformatf("prog_window violation, start_addr:0x%x end_addr:0x%x",
flash_op.otf_addr, end_addr), UVM_MEDIUM)
// Shift start addr window
if (flash_op.partition == FlashPartData) begin
flash_op.otf_addr[BusAddrByteW-1:6] = end_addr[BusAddrByteW-1:6];
flash_op.otf_addr[5:0] = 0;
end else begin
flash_op.otf_addr[8:0] = 'h0;
end
end_addr = flash_op.otf_addr + (wd * 4) -1;
`uvm_info("prog_flash", $sformatf("change to page:%0d start_addr to 0x%x end_addr:0x%x",
cfg.addr2page(flash_op.addr),
flash_op.otf_addr,
end_addr), UVM_MEDIUM)
is_odd = 0;
end
unit_word = wd;
// Each flash_program_data[] entry : 4B
// {global_cnt(16bits), lcnt(16bits)}
for (int j = 0; j < wd; j++) begin
if (cfg.wr_rnd_data) begin
flash_program_data.push_back($urandom);
end else begin
flash_program_data.push_back({global_pat_cnt, lcnt++});
end
end
flash_op.addr = flash_op.otf_addr;
// Bank : bit[19]
flash_op.addr[TL_AW-1:OTFBankId] = bank;
if (flash_op.partition == FlashPartData) begin
page = cfg.addr2page(flash_op.addr);
my_region = cfg.get_region(page);
end else begin
// for region, use per bank page number
page = cfg.addr2page(flash_op.otf_addr);
my_region = cfg.get_region_from_info(cfg.mp_info[bank][flash_op.partition>>1][page]);
drop = check_info_part(flash_op, "prog_flash");
end
drop |= validate_flash_op(flash_op, my_region);
if (drop) begin
`uvm_info("prog_flash", $sformatf("op:%s is not allowed in this region %p",
flash_op.op.name, my_region), UVM_MEDIUM)
set_otf_exp_alert("recov_err");
end
if (cfg.intr_mode) begin
flash_ctrl_intr_write(flash_op, flash_program_data);
end else begin
flash_ctrl_start_op(flash_op);
if (in_err) begin
cfg.tlul_core_exp_cnt += flash_op.num_words;
end
flash_ctrl_write(flash_program_data, poll_fifo_status);
if (!in_err) wait_flash_op_done(.timeout_ns(cfg.seq_cfg.prog_timeout_ns));
end
if (is_odd == 1) begin
tmp_data = {32{1'b1}};
flash_program_data.push_front(tmp_data);
unit_word++;
end
tail = unit_word % 2;
if (wd > 1 && tail == 1) begin
tmp_data = {32{1'b1}};
flash_program_data.push_back(tmp_data);
end
if (wd == 1 && is_odd == 0) begin
tmp_data = {32{1'b1}};
flash_program_data.push_back(tmp_data);
end
if (wd > 16) begin
csr_rd_check(.ptr(ral.err_code.prog_win_err), .compare_value(1));
drop = 1;
end
`uvm_info("prog_flash",$sformatf({"bank:%0d addr:%x otf_addr:%x part:%s",
" page:%0d num:%0d wd:%0d odd:%0d tail:%0d"},
bank, flash_op.addr, flash_op.otf_addr,
flash_op.partition.name, page, num,
wd, is_odd, tail), UVM_MEDIUM)
if (drop) begin
uvm_reg_data_t ldata;
csr_rd(.ptr(ral.err_code), .value(ldata), .backdoor(1));
`uvm_info("prog_flash", $sformatf("skip sb path due to err_code:%x", ldata), UVM_MEDIUM)
end else begin
if (store_prog_data) cfg.prog_data[flash_op] = flash_program_data;
flash_otf_print_data64(flash_program_data, "wdata");
`uvm_create_obj(flash_otf_item, exp_item)
exp_item.cmd = flash_op;
exp_item.dq = flash_program_data;
exp_item.region = my_region;
// Scramble data
exp_item.scramble(otp_addr_key, otp_data_key, flash_op.otf_addr);
p_sequencer.eg_exp_ctrl_port[bank].write(exp_item);
flash_phy_prim_agent_pkg::print_flash_data(exp_item.fq,
$sformatf("fdata_%0d bank%0d", cfg.otf_ctrl_wr_sent, bank));
end
flash_op.otf_addr = flash_op.otf_addr + (4 * wd);
global_pat_cnt++;
cfg.otf_ctrl_wr_sent++;
end // for (int i = 0; i < num; i++)
endtask // prog_flash
// Read flash in the unit of minimum resolution (4 Byte).
// 1 word : 8Byte
// @arg: flash_op_p : command struct return updated address after write
// @arg: bank: bank index to access flash
// @arg: num : number of 8 words range: [1 : 32]
// @arg: wd : number of 4byte (TL bus unit) : default : 16
// @arg: overrd : invoke oversize read
// @arg: in_err : inject fatal error causes flash access disable
task read_flash(ref flash_op_t flash_op, input int bank, int num, int wd = 16,
int overrd = 0, bit in_err = 0);
data_q_t flash_read_data;
flash_otf_item exp_item;
bit poll_fifo_status = ~in_err;
bit [flash_ctrl_pkg::BusAddrByteW-1:0] start_addr, end_addr;
int page;
bit overflow = 0;
uvm_reg_data_t reg_data;
bit derr_is_set;
bit drop;
int size, is_odd, tail;
addr_t tmp_addr;
flash_mp_region_cfg_t my_region;
rd_cache_t rd_entry;
// Exclude secret partition from non scrambled / ecc mode
if (cfg.ecc_mode == FlashEccDisabled &&
flash_op.partition == FlashPartInfo) return;
flash_op.op = FlashOpRead;
flash_op.num_words = wd;
if (cfg.ecc_mode > FlashEccEnabled) begin
if (flash_op.partition == FlashPartData) begin
flash_op.otf_addr[18:17] = cfg.tgt_pre[flash_op.partition][TgtRd];
end else begin
flash_op.otf_addr[10:9] = cfg.tgt_pre[flash_op.partition][TgtRd];
end
end else begin
flash_op.otf_addr[OTFHostId] = 0;
end
start_addr = flash_op.otf_addr;
end_addr = start_addr + (wd * 4 * num) - 1;
if (cfg.ecc_mode > FlashEccEnabled) begin
if (flash_op.partition == FlashPartData) begin
overflow = (end_addr[18:17] != start_addr[18:17] ||
end_addr[16:0] > 17'h1_FE00);
end else begin
overflow = (end_addr[10:9] != start_addr[10:9]);
end
end else begin
// Ctrl read takes lower half of each bank
// and host read takes upper half.
overflow = end_addr[OTFHostId];
end
if (overflow) begin
if (flash_op.partition == FlashPartData) begin
flash_op.otf_addr[16:0] = 'h0;
end else begin
flash_op.otf_addr[8:0] = 'h0;
end
`uvm_info("read_flash", $sformatf("overflow!, roll over start address to 0x%x",
flash_op.otf_addr), UVM_MEDIUM)
end
for (int i = 0; i < num; i++) begin
drop = 0;
if (cfg.ecc_mode > FlashEccEnabled) begin
if (flash_op.partition == FlashPartData) begin
flash_op.otf_addr[18:17] = cfg.tgt_pre[flash_op.partition][TgtRd];
end else begin
flash_op.otf_addr[10:9] = cfg.tgt_pre[flash_op.partition][TgtRd];
end
end
flash_op.addr = flash_op.otf_addr;
flash_op.addr[TL_AW-1:OTFBankId] = bank;
rd_entry.bank = bank;
is_odd = flash_op.addr[2];
size = (flash_op.num_words + is_odd) / 2;
tail = (flash_op.num_words + is_odd) % 2;
tmp_addr = flash_op.addr;
flash_op.addr[2:0] = 0;
// Per Qword loop
`uvm_create_obj(flash_otf_item, exp_item)
for (int i = 0; i < size; i++) begin
if (flash_op.partition == FlashPartData) begin
page = cfg.addr2page(flash_op.addr);
my_region = cfg.get_region(page);
end else begin
page = cfg.addr2page(flash_op.otf_addr);
my_region = cfg.get_region_from_info(cfg.mp_info[bank][flash_op.partition>>1][page]);
drop |= check_info_part(flash_op, "read_flash");
end
drop |= validate_flash_op(flash_op, my_region);
flash_op.addr += 8;
flash_op.otf_addr += 8;
exp_item.ctrl_rd_region_q.push_back(my_region);
end // for (int i = 0; i < size; i++)
if (tail) begin
if (flash_op.partition == FlashPartData) begin
page = cfg.addr2page(flash_op.addr);
my_region = cfg.get_region(page);
end else begin
page = cfg.addr2page(flash_op.otf_addr);
my_region = cfg.get_region_from_info(cfg.mp_info[bank][flash_op.partition>>1][page]);
drop |= check_info_part(flash_op, "read_flash");
end
drop |= validate_flash_op(flash_op, my_region);
exp_item.ctrl_rd_region_q.push_back(my_region);
end
flash_op.addr = tmp_addr;
// Bank id truncaded by otf_addr size
flash_op.otf_addr = tmp_addr;
// recalculate page and region based on start address
// for the debug print
if (flash_op.partition == FlashPartData) begin
page = cfg.addr2page(flash_op.addr);
my_region = cfg.get_region(page);
end else begin
page = cfg.addr2page(flash_op.otf_addr);
my_region = cfg.get_region_from_info(cfg.mp_info[bank][flash_op.partition>>1][page]);
end
if (drop) begin
`uvm_info("read_flash", $sformatf("op:%s is not allowed in the region:%p",
flash_op.op.name, my_region), UVM_MEDIUM)
set_otf_exp_alert("recov_err");
end
`uvm_info("read_flash",
$sformatf({"bank:%0d page:%0d otf_addr:0x%0h,",
" part:%s size:%0d x %0d x 4B start_addr:%x end_addr:%x",
" overrd:%0d"},
bank, page, flash_op.otf_addr,
flash_op.partition.name, num, wd, start_addr, end_addr, overrd),
UVM_MEDIUM)
exp_item.cmd = flash_op;
// per bank address is used for decryption in sbx
exp_item.start_addr = flash_op.otf_addr;
exp_item.addr_key = otp_addr_key;
exp_item.data_key= otp_data_key;
rd_entry.addr = flash_op.otf_addr;
// Address has to be 8byte aligned
rd_entry.addr[2:0] = 'h0;
rd_entry.part = flash_op.partition;
if (cfg.ecc_mode > FlashEccEnabled) begin
if (drop == 0) begin
if (cfg.ecc_mode == FlashSerrTestMode || flash_op.addr[2] == 0) begin
cfg.add_bit_err(flash_op, ReadTaskCtrl, exp_item);
end
end
end
cfg.otf_read_entry.insert(rd_entry, flash_op);
if (cfg.derr_once) begin
derr_is_set = cfg.derr_created[0] & ~global_derr_is_set;
end else begin
derr_is_set = cfg.derr_created[0];
end
if (derr_is_set) begin
`uvm_info("read_flash", $sformatf("assert_derr 0x%x",
{flash_op.addr[31:3], 3'h0}), UVM_MEDIUM)
global_derr_is_set = 1;
if (cfg.scb_h.do_alert_check == 1) begin
cfg.scb_h.expected_alert["fatal_err"].expected = 1;
cfg.scb_h.expected_alert["fatal_err"].max_delay = 2000;
cfg.scb_h.exp_alert_contd["fatal_err"] = 10000;
cfg.scb_h.expected_alert["recov_err"].expected = 1;
cfg.scb_h.expected_alert["recov_err"].max_delay = 2000;
cfg.scb_h.exp_alert_contd["recov_err"] = 10000;
end
end
if (cfg.intr_mode) begin
flash_ctrl_intr_read(flash_op, flash_read_data);
end else begin
flash_ctrl_start_op(flash_op);
if (in_err) begin
cfg.tlul_core_exp_cnt += flash_op.num_words;
end
flash_ctrl_read(flash_op.num_words, flash_read_data, poll_fifo_status);
if (overrd > 0) begin
overread(flash_op, bank, num, overrd);
end
if (!in_err) wait_flash_op_done();
end
if (derr_is_set | cfg.ierr_created[0]) begin
`uvm_info("read_flash", $sformatf({"bank:%0d addr: %x(otf:%x) derr_is_set:%0d",
" ierr_created[0]:%0d"}, bank, flash_op.addr,
flash_op.otf_addr, derr_is_set, cfg.ierr_created[0])
, UVM_MEDIUM)
csr_rd_check(.ptr(ral.op_status.err), .compare_value(1));
csr_rd_check(.ptr(ral.err_code.rd_err), .compare_value(1));
reg_data = get_csr_val_with_updated_field(ral.err_code.rd_err, reg_data, 1);
csr_wr(.ptr(ral.err_code), .value(reg_data));
reg_data = get_csr_val_with_updated_field(ral.op_status.err, reg_data, 0);
csr_wr(.ptr(ral.op_status), .value(reg_data));
if (cfg.derr_once == 0) cfg.derr_created[0] = 0;
cfg.ierr_created[0] = 0;
end
exp_item.exp_err |= in_err;
exp_item.dq = flash_read_data;
exp_item.fq = exp_item.dq2fq(flash_read_data);
if (drop) begin
`uvm_info("read_flash", "skip sb path due to err", UVM_MEDIUM)
end else begin
p_sequencer.eg_exp_ctrl_port[bank].write(exp_item);
end
cfg.otf_ctrl_rd_rcvd++;
flash_op.otf_addr = flash_op.otf_addr + (4 * wd);
end // for (int i = 0; i < num; i++)
endtask // read_flash
// Read error behavior task
// This task issue rd_fifo read without setting stat_op.
// Expected output is to received errored response from core_tl interface.
task overread(flash_op_t flash_op, int bank, int num, int wd);
data_q_t flash_read_data;
bit poll_fifo_status = 0;
addr_t addr = ral.rd_fifo.get_address();
repeat (wd) cfg.scb_h.over_rd_err[addr]++;
cfg.m_tl_agent_cfg.check_tl_errs = 0;
`uvm_info("overread", $sformatf("addr is set 0x%x wd:%0d", addr, wd),
UVM_MEDIUM)
flash_ctrl_read(wd, flash_read_data, poll_fifo_status, 1);
cfg.m_tl_agent_cfg.check_tl_errs = 1;
endtask // overread
// Direct access from the host. It returns multiple of
// 4bytes of data.
// @arg : addr : Direct access address.
// At the phy interface, address_phy = addr >> 3,
// because phy returns data in 8byte.
// At the host interface, addr[2] is used for
// word selector s.t.
// addr[2]? upper 4byte : lower 4byte of phy data.
// @arg : bank : bank index to access flash.
// @arg : num : number of 4byte data to read countinuously
// by 4 byte apart.
// @arg: in_err : inject fatal error causes flash access disable
task otf_direct_read(bit [OTFHostId-2:0] addr, int bank, int num, bit in_err);
bit[TL_AW-1:0] tl_addr, st_addr, end_addr;
data_4s_t rdata;
flash_otf_item exp_item;
int page;
flash_op_t flash_op;
bit completed;
bit derr_is_set;
bit derr, drop;
bit overflow = 0;
flash_mp_region_cfg_t my_region;
rd_cache_t rd_entry;
if (cfg.ecc_mode > FlashEccEnabled) begin
end_addr = addr + num * 4 - 1;
overflow = (end_addr[OTFHostId:0] > 18'h1_FE00);
tl_addr[OTFHostId-:2] = cfg.tgt_pre[FlashPartData][TgtDr];
end else begin
end_addr = addr + num * 4 - 1;
tl_addr[OTFHostId] = 1;
overflow = end_addr[OTFHostId];
end
`uvm_info("direct_read", $sformatf("addr: %x end_addr: %x overflow:% x",
addr, end_addr, overflow), UVM_HIGH)
rd_entry.bank = bank;
tl_addr[OTFBankId] = bank;
if (overflow) begin
addr = num * 4;
end
tl_addr[OTFHostId-2:2] = addr[OTFHostId-2:2];
`uvm_info("direct_read", $sformatf("bank:%0d tl_addr:0x%0h, num: %0d",
bank, tl_addr, num), UVM_MEDIUM)
// Capture for the print in sb.
st_addr = tl_addr;
for (int i = 0; i < num ; i++) begin
drop = 0;
derr = 0;
// force address wrap around
if (cfg.ecc_mode > FlashEccEnabled) tl_addr[18:17] = cfg.tgt_pre[FlashPartData][TgtDr];
`uvm_create_obj(flash_otf_item, exp_item)
page = cfg.addr2page(tl_addr[OTFBankId:0]);
my_region = cfg.get_region(page);
flash_op.op = FlashOpRead;
exp_item.page = page;
exp_item.region = my_region;
exp_item.start_addr = tl_addr;
exp_item.addr_key = otp_addr_key;
exp_item.data_key= otp_data_key;
// Address should be per bank addr
rd_entry.addr = tl_addr;
rd_entry.addr[OTFBankId] = 0;
// Address has to be 8byte aligned
rd_entry.addr[2:0] = 'h0;
rd_entry.part = FlashPartData;
if (cfg.ecc_mode > FlashEccEnabled) begin
if (exp_item.region.ecc_en == MuBi4True) begin
flash_op.addr = tl_addr;
// host can only access data partitions.
flash_op.partition = FlashPartData;
flash_op.num_words = 1;
if (cfg.ecc_mode == FlashSerrTestMode || tl_addr[2] == 0) begin
cfg.add_bit_err(flash_op, ReadTaskHost, exp_item);
end
if (cfg.derr_once) begin
derr_is_set = cfg.derr_created[1] & ~global_derr_is_set;
end else begin
derr_is_set = (cfg.derr_created[1] | cfg.ierr_created[1]);
end
if (derr_is_set) begin
`uvm_info("direct_read", $sformatf("assert_derr 0x%x", tl_addr), UVM_MEDIUM)
cfg.scb_h.ecc_error_addr[{tl_addr[31:3],3'h0}] = 1;
global_derr_is_set = 1;
end
if (cfg.derr_once == 0) cfg.derr_created[1] = 0;
`uvm_info("direct_read", $sformatf("ierr_created[1]:%0d derr_is_set:%0d exists:%0d",
cfg.ierr_created[1], derr_is_set,
cfg.scb_h.ecc_error_addr.exists({tl_addr[31:3],3'h0})),
UVM_MEDIUM)
cfg.ierr_created[1] = 0;
end
if (cfg.scb_h.ecc_error_addr.exists({tl_addr[31:3],3'h0}) | derr_is_set) derr = 1;
end
cfg.otf_read_entry.insert(rd_entry, flash_op);
`uvm_info("direct_read", $sformatf("num_i:%0d bank:%0d exec: 0x%x derr:%0d in_err:%0d",
i, bank, tl_addr, derr, in_err), UVM_MEDIUM)
if (in_err) cfg.scb_h.in_error_addr[{tl_addr[31:3],3'h0}] = 1;
derr |= in_err;
if (cfg.ecc_mode > FlashSerrTestMode) begin
if (derr & cfg.scb_h.do_alert_check) begin
cfg.scb_h.expected_alert["fatal_err"].expected = 1;
cfg.scb_h.expected_alert["fatal_err"].max_delay = 2000;
cfg.scb_h.exp_alert_contd["fatal_err"] = 10000;
end
end
// in_err is currently used to address error caused by disable flash.
if (in_err) begin
set_otf_exp_alert("fatal_err");
end
cfg.inc_otd_tbl(bank, tl_addr, FlashPartData);
d_cnt1++;
do_direct_read(.addr(tl_addr), .mask('1), .blocking(1), .rdata(rdata),
.completed(completed), .exp_err_rsp(derr));
d_cnt2++;
`uvm_info(`gfn, $sformatf("direct_read_trace: sent:%0d rcvd:%0d", d_cnt1, d_cnt2),
UVM_HIGH)
// issue csr rd to capture coverpoint at sb.
if (derr) begin
uvm_reg_data_t ldata;
csr_rd(.ptr(ral.err_code), .value(ldata));
end
if (completed) begin
exp_item.dq.push_back(rdata);
exp_item.exp_err |= in_err;
p_sequencer.eg_exp_host_port[bank].write(exp_item);
`uvm_info("direct_read",
$sformatf("SEQ:st_addr:%x addr:%x rcvd:%0d rdata:%x derr:%0d",
st_addr, tl_addr, cfg.otf_host_rd_rcvd, rdata, derr),
UVM_MEDIUM)
end else begin
`uvm_info("direct_read",
$sformatf("SEQ:st_addr:%x addr:%x rcvd:%0d aborted derr:%0d",
st_addr, tl_addr, cfg.otf_host_rd_rcvd, derr),
UVM_MEDIUM)
end
cfg.dec_otd_tbl(bank, tl_addr, FlashPartData);
cfg.otf_host_rd_rcvd++;
tl_addr += 4;
end
endtask // otf_direct_read
// Read flash in the unit of minimum resolution (4 Byte).
// This task has following difference from 'task read_flash'
// - This task doesn't use target prefix (tgt_pre).
// - If same address write happens, the address marked as error and
// set expected alert
// - num and wd is controlled by test sequence to keep read within
// 'loaded zone'
//
// @arg: flash_op_p : command struct return updated address after write
// @arg: bank: bank index to access flash
// @arg: num : number of 8 words range: [1 : 32]
// @arg: wd : number of 4byte (TL bus unit) : default : 16
task readback_flash(flash_op_t flash_op, int bank, int num, int wd = 16);
data_q_t flash_read_data;
flash_otf_item exp_item;
bit poll_fifo_status = 1;
int page;
uvm_reg_data_t reg_data;
bit derr_is_set;
bit drop;
int size, is_odd, tail;
addr_t tmp_addr;
flash_mp_region_cfg_t my_region;
rd_cache_t rd_entry;
flash_op.op = FlashOpRead;
flash_op.num_words = wd;
for (int i = 0; i < num; i++) begin
drop = 0;
flash_op.addr = flash_op.otf_addr;
flash_op.addr[TL_AW-1:OTFBankId] = bank;
rd_entry.bank = bank;
is_odd = flash_op.addr[2];
size = (flash_op.num_words + is_odd) / 2;
tail = (flash_op.num_words + is_odd) % 2;
tmp_addr = flash_op.addr;
flash_op.addr[2:0] = 0;
// Per Qword loop
`uvm_create_obj(flash_otf_item, exp_item)
for (int i = 0; i < size; i++) begin
if (flash_op.partition == FlashPartData) begin
page = cfg.addr2page(flash_op.addr);
my_region = cfg.get_region(page);
end else begin
page = cfg.addr2page(flash_op.otf_addr);
my_region = cfg.get_region_from_info(cfg.mp_info[bank][flash_op.partition>>1][page]);
drop |= check_info_part(flash_op, "readback_flash");
end
drop |= validate_flash_op(flash_op, my_region);
rd_entry.addr = flash_op.otf_addr;
// Address has to be 8byte aligned
rd_entry.addr[2:0] = 'h0;
rd_entry.part = flash_op.partition;
//`JDBG(("readback_flash: rd_entry:%p exist:%0d", rd_entry, cfg.otf_scb_h.corrupt_entry.exists(rd_entry)))
if (drop == 0 &&
my_region.ecc_en == MuBi4True &&
cfg.otf_scb_h.corrupt_entry.exists(rd_entry) == 1) begin
`uvm_info("readback_flash", $sformatf("read corrupted entry 0x%x",
{flash_op.addr[31:3], 3'h0}), UVM_MEDIUM)
derr_is_set |= 1;
end
flash_op.addr += 8;
flash_op.otf_addr += 8;
exp_item.ctrl_rd_region_q.push_back(my_region);
end // for (int i = 0; i < size; i++)
if (tail) begin
if (flash_op.partition == FlashPartData) begin
page = cfg.addr2page(flash_op.addr);
my_region = cfg.get_region(page);
end else begin
page = cfg.addr2page(flash_op.otf_addr);
my_region = cfg.get_region_from_info(cfg.mp_info[bank][flash_op.partition>>1][page]);
drop |= check_info_part(flash_op, "readback_flash");
end
drop |= validate_flash_op(flash_op, my_region);
exp_item.ctrl_rd_region_q.push_back(my_region);
rd_entry.addr = flash_op.otf_addr;
// Address has to be 8byte aligned
rd_entry.addr[2:0] = 'h0;
rd_entry.part = flash_op.partition;
if (drop == 0 &&
my_region.ecc_en == MuBi4True &&
cfg.otf_scb_h.corrupt_entry.exists(rd_entry) == 1) begin
`uvm_info("readback_flash", $sformatf("read corrupted entry 0x%x",
{flash_op.addr[31:3], 3'h0}), UVM_MEDIUM)
derr_is_set |= 1;
end
end
flash_op.addr = tmp_addr;
// Bank id truncaded by otf_addr size
flash_op.otf_addr = tmp_addr;
// recalculate page and region based on start address
// for the debug print
if (flash_op.partition == FlashPartData) begin
page = cfg.addr2page(flash_op.addr);
my_region = cfg.get_region(page);
end else begin
page = cfg.addr2page(flash_op.otf_addr);
my_region = cfg.get_region_from_info(cfg.mp_info[bank][flash_op.partition>>1][page]);
end
if (drop) begin
`uvm_info("readback_flash", $sformatf("op:%s is not allowed in the region:%p",
flash_op.op.name, my_region), UVM_MEDIUM)
set_otf_exp_alert("recov_err");
end
`uvm_info("readback_flash",
$sformatf({"bank:%0d page:%0d otf_addr:0x%0h,",
" part:%s size:%0d x %0d x 4B"},
bank, page, flash_op.otf_addr,
flash_op.partition.name, num, wd),
UVM_MEDIUM)
exp_item.cmd = flash_op;
// per bank address is used for decryption in sbx
exp_item.start_addr = flash_op.otf_addr;
exp_item.addr_key = otp_addr_key;
exp_item.data_key= otp_data_key;
rd_entry.addr = flash_op.otf_addr;
// Address has to be 8byte aligned
rd_entry.addr[2:0] = 'h0;
rd_entry.part = flash_op.partition;
if (cfg.ecc_mode > FlashEccEnabled) begin
if (exp_item.region.ecc_en == MuBi4True && drop == 0) begin
if (cfg.ecc_mode == FlashSerrTestMode || flash_op.addr[2] == 0) begin
cfg.add_bit_err(flash_op, ReadTaskCtrl, exp_item);
end
end
end
cfg.otf_read_entry.insert(rd_entry, flash_op);
if (derr_is_set) begin
`uvm_info("readback_flash", $sformatf("read corrupted entry 0x%x",
{flash_op.addr[31:3], 3'h0}), UVM_MEDIUM)
global_derr_is_set = 1;
derr_is_set = 1;
exp_item.derr = 1;
if (cfg.scb_h.do_alert_check == 1) begin
cfg.scb_h.expected_alert["fatal_err"].expected = 1;
cfg.scb_h.expected_alert["fatal_err"].max_delay = 2000;
cfg.scb_h.exp_alert_contd["fatal_err"] = 10000;
cfg.scb_h.expected_alert["recov_err"].expected = 1;
cfg.scb_h.expected_alert["recov_err"].max_delay = 2000;
cfg.scb_h.exp_alert_contd["recov_err"] = 10000;
end
end
if (cfg.intr_mode) begin
flash_ctrl_intr_read(flash_op, flash_read_data);
end else begin
flash_ctrl_start_op(flash_op);
flash_ctrl_read(flash_op.num_words, flash_read_data, poll_fifo_status);
wait_flash_op_done();
end
if (derr_is_set | cfg.ierr_created[0]) begin
uvm_reg_data_t ldata;
csr_rd(.ptr(ral.err_code), .value(ldata), .backdoor(1));
`uvm_info("readback_flash", $sformatf({"bank:%0d addr: %x(otf:%x) derr_is_set:%0d",
" ierr_created[0]:%0d"}, bank, flash_op.addr,
flash_op.otf_addr, derr_is_set, cfg.ierr_created[0])
, UVM_MEDIUM)
csr_rd_check(.ptr(ral.op_status.err), .compare_value(1));
csr_rd_check(.ptr(ral.err_code.rd_err), .compare_value(1));
reg_data = get_csr_val_with_updated_field(ral.err_code.rd_err, reg_data, 1);
csr_wr(.ptr(ral.err_code), .value(reg_data));
reg_data = get_csr_val_with_updated_field(ral.op_status.err, reg_data, 0);
csr_wr(.ptr(ral.op_status), .value(reg_data));
if (cfg.derr_once == 0) cfg.derr_created[0] = 0;
cfg.ierr_created[0] = 0;
end
exp_item.dq = flash_read_data;
exp_item.fq = exp_item.dq2fq(flash_read_data);
if (drop) begin
`uvm_info("read_flash", "skip sb path due to err", UVM_MEDIUM)
csr_wr(.ptr(ral.op_status), .value(0));
end else begin
p_sequencer.eg_exp_ctrl_port[bank].write(exp_item);
end
cfg.otf_ctrl_rd_rcvd++;
flash_op.otf_addr = flash_op.otf_addr + (4 * wd);
end // for (int i = 0; i < num; i++)
endtask
task direct_readback(bit [OTFBankId-1:0] addr, int bank, int num);
bit[TL_AW-1:0] tl_addr, st_addr, end_addr;
data_4s_t rdata;
flash_otf_item exp_item;
int page;
flash_op_t flash_op;
bit completed;
bit derr_is_set;
bit derr, drop;
bit overflow = 0;
flash_mp_region_cfg_t my_region;
rd_cache_t rd_entry;
rd_entry.bank = bank;
tl_addr[OTFBankId] = bank;
tl_addr[OTFHostId:2] = addr[OTFHostId:2];
`uvm_info("direct_readback", $sformatf("bank:%0d tl_addr:0x%0h, num: %0d",
bank, tl_addr, num), UVM_MEDIUM)
// Capture for the print in sb.
st_addr = tl_addr;
for (int i = 0; i < num ; i++) begin
drop = 0;
derr = 0;
`uvm_create_obj(flash_otf_item, exp_item)
page = cfg.addr2page(tl_addr[OTFBankId:0]);
`uvm_info("direct_readback", $sformatf("direct page: %0d", page), UVM_MEDIUM)
my_region = cfg.get_region(page);
flash_op.op = FlashOpRead;
exp_item.page = page;
exp_item.region = my_region;
exp_item.start_addr = tl_addr;
exp_item.addr_key = otp_addr_key;
exp_item.data_key= otp_data_key;
rd_entry.addr = tl_addr;
rd_entry.addr[OTFBankId] = 0;
// Address has to be 8byte aligned
rd_entry.addr[2:0] = 'h0;
rd_entry.part = FlashPartData;
if (cfg.ecc_mode > FlashEccEnabled) begin
if (exp_item.region.ecc_en == MuBi4True) begin
flash_op.addr = tl_addr;
// host can only access data partitions.
flash_op.partition = FlashPartData;
flash_op.num_words = 1;
if (cfg.ecc_mode == FlashSerrTestMode || tl_addr[2] == 0) begin
cfg.add_bit_err(flash_op, ReadTaskHost, exp_item);
end
if (cfg.derr_once) begin
derr_is_set = cfg.derr_created[1] & ~global_derr_is_set;
end else begin
derr_is_set = (cfg.derr_created[1] | cfg.ierr_created[1]);
end
if (derr_is_set) begin
`uvm_info("direct_readback", $sformatf("assert_derr 0x%x", tl_addr), UVM_MEDIUM)
cfg.scb_h.ecc_error_addr[{tl_addr[31:3],3'h0}] = 1;
global_derr_is_set = 1;
end
if (cfg.derr_once == 0) cfg.derr_created[1] = 0;
`uvm_info("direct_readback", $sformatf("ierr_created[1]:%0d derr_is_set:%0d exists:%0d",
cfg.ierr_created[1], derr_is_set,
cfg.scb_h.ecc_error_addr.exists({tl_addr[31:3],3'h0})),
UVM_MEDIUM)
cfg.ierr_created[1] = 0;
end
if (cfg.scb_h.ecc_error_addr.exists({tl_addr[31:3],3'h0}) | derr_is_set) derr = 1;
end
cfg.otf_read_entry.insert(rd_entry, flash_op);
if (my_region.ecc_en == MuBi4True && cfg.otf_scb_h.corrupt_entry.exists(rd_entry) == 1) begin
exp_item.derr = 1;
derr = 1;
cfg.scb_h.ecc_error_addr[{tl_addr[31:3],3'h0}] = 1;
if (derr & cfg.scb_h.do_alert_check) begin
cfg.scb_h.expected_alert["fatal_err"].expected = 1;
cfg.scb_h.expected_alert["fatal_err"].max_delay = 2000;
cfg.scb_h.exp_alert_contd["fatal_err"] = 10000;
end
end
`uvm_info("direct_readback", $sformatf("idx:%0d: bank:%0d exec: 0x%x page:%0d derr:%0d",
i, bank, tl_addr, page, derr), UVM_MEDIUM)
cfg.inc_otd_tbl(bank, tl_addr, FlashPartData);
do_direct_read(.addr(tl_addr), .mask('1), .blocking(1), .rdata(rdata),
.completed(completed), .exp_err_rsp(derr));
if (completed) begin
exp_item.dq.push_back(rdata);
p_sequencer.eg_exp_host_port[bank].write(exp_item);
`uvm_info("direct_readback",
$sformatf("SEQ:st_addr:%x addr:%x rcvd:%0d rdata:%x derr:%0d",
st_addr, tl_addr, cfg.otf_host_rd_rcvd, rdata, derr),
UVM_MEDIUM)
end else begin
`uvm_info("direct_readback",
$sformatf("SEQ:st_addr:%x addr:%x rcvd:%0d aborted derr:%0d",
st_addr, tl_addr, cfg.otf_host_rd_rcvd, derr),
UVM_MEDIUM)
end
cfg.dec_otd_tbl(bank, tl_addr, FlashPartData);
cfg.otf_host_rd_rcvd++;
tl_addr += 4;
end // for (int i = 0; i < num ; i++)
endtask
task erase_flash(flash_op_t flash_op, int bank, bit in_err = 0);
bit drop = 0;
int page;
flash_mp_region_cfg_t my_region;
flash_op.op = FlashOpErase;
flash_op.otf_addr[OTFBankId] = bank;
flash_op.addr = flash_op.otf_addr;
flash_op.erase_type = FlashErasePage;
if (cfg.ecc_mode > FlashEccEnabled) begin
if (flash_op.partition == FlashPartData) begin
flash_op.otf_addr[18:17] = cfg.tgt_pre[flash_op.partition][TgtEr];
end else begin
flash_op.otf_addr[10:9] = cfg.tgt_pre[flash_op.partition][TgtEr];
end
end
`uvm_info("erase_flash", $sformatf("{bank:%0d otf_addr:0x%0h, page:%0d part:%s erase_type:%s",
bank, flash_op.otf_addr, cfg.addr2page(flash_op.addr),
flash_op.partition.name, flash_op.erase_type.name),
UVM_MEDIUM)
if (flash_op.partition == FlashPartData) begin
page = cfg.addr2page(flash_op.addr);
my_region = cfg.get_region(page);
end else begin
page = cfg.addr2page(flash_op.otf_addr);
my_region = cfg.get_region_from_info(cfg.mp_info[bank][flash_op.partition>>1][page]);
drop = check_info_part(flash_op, "erase_flash");
end
drop |= validate_flash_op(flash_op, my_region);
if (drop) begin
`uvm_info("erase_flash", $sformatf("op:%s is not allowed in this region %p",
flash_op.op.name, my_region), UVM_MEDIUM)
set_otf_exp_alert("recov_err");
end
flash_ctrl_start_op(flash_op);
if (!in_err) wait_flash_op_done(.timeout_ns(cfg.seq_cfg.erase_timeout_ns));
endtask
function void update_otf_mem_read_zone(flash_dv_part_e part, int bank, addr_t addr);
flash_otf_item item;
int page;
bit [BankAddrW-1:0] mem_addr;
`uvm_create_obj(flash_otf_item, item)
item.dq.push_back($urandom());
item.dq.push_back($urandom());
if (part == FlashPartData) begin
addr[OTFBankId] = bank;
page = cfg.addr2page(addr);
item.region = cfg.get_region(page, 0);
// back to per bank addr
addr[OTFBankId] = 0;
end else begin
page = cfg.addr2page(addr);
item.region = cfg.get_region_from_info(cfg.mp_info[bank][part>>1][page]);
end
item.page = page;
item.scramble(otp_addr_key, otp_data_key, addr, 0);
cfg.mem_bkdr_util_h[part][bank].write(addr, item.fq[0]);
// address should be mem_addr
mem_addr = addr >> 3;
if (part == FlashPartData) cfg.otf_scb_h.data_mem[bank][mem_addr] = item.fq[0];
else cfg.otf_scb_h.info_mem[bank][part>>1][mem_addr] = item.fq[0];
endfunction // update_otf_mem_read_zone
function void load_otf_mem_page(flash_dv_part_e part,
int bank,
int page);
addr_t addr;
addr[OTFBankId-1:11] = page;
for (int i = 0; i < 256; i++) begin
update_otf_mem_read_zone(part, bank, addr);
addr += 8;
end
endfunction // load_otf_mem_page
// Update rd / dr tgt of the memory with their page profile
function void flash_otf_mem_read_zone_init();
// 8byte aligned
addr_t st_addr, ed_addr;
flash_dv_part_e part;
// read tgt region : cfg.tgt_pre[0]
// direct rd tgt region: cfg.tgt_pre[1]
part = part.first;
do begin
for (flash_tgt_prefix_e j = TgtRd; j <= TgtDr; j++) begin
st_addr = 'h0;
if (part == FlashPartData) begin
st_addr[18:17] = cfg.tgt_pre[part][j];
// I think this should be
// 64 * 256 * 8 -1 (= 0x1_FFFF)
// to maxout a quater of 1 bank
// Will be address the next PR
ed_addr = st_addr + 64 * 255 * 8; // + 0x1_FE00
// data partition 2 banks
for (int i = 0; i < 2; i++) begin
for (addr_t addr = st_addr; addr <= ed_addr; addr += 8) begin
update_otf_mem_read_zone(part, i, addr);
end
`uvm_info("flash_otf_init",
$sformatf("part:%s pre:%s bank:%0d st:%x ed:%x",
part.name, j.name, i, st_addr, ed_addr), UVM_MEDIUM)
end
end else begin // if (part == FlashPartData)
// While data part can be divided by pages, info part
// need finer resolution due to number of pages in each info
// is relatively small.
// So every page in info part will be divided into 4 parts.
st_addr[10:9] = cfg.tgt_pre[part][j];
for (int k = 0; k < InfoTypeSize[part>>1]; k++) begin
st_addr[DVPageMSB:DVPageLSB] = k; // page
ed_addr = st_addr + 511; // 0x1FF
for (int i = 0; i < 2; i++) begin
for (addr_t addr = st_addr; addr <= ed_addr; addr += 8) begin
update_otf_mem_read_zone(part, i, addr);
end
end
`uvm_info("flash_otf_init",
$sformatf("part:%s pre%0d page:%0d st:%x ed:%x",
part.name, j, k, st_addr, ed_addr), UVM_MEDIUM)
end
end
end // for (int j = TgtRd; j <= TgtDr; j++)
part = part.next;
end while (part != part.first);
endfunction // flash_otf_init
// Send direct host read to both bankds 'host_num' times.
virtual task send_rand_host_rd(int num = -1, bit in_err = 0);
flash_op_t host;
int host_num, host_bank;
host.otf_addr[OTFHostId-2:0] = $urandom();
host.otf_addr[1:0] = 'h0;
if (num >= 0) host_num = num;
else host_num = $urandom_range(1,128);
host_bank = $urandom_range(0,1);
otf_direct_read(host.otf_addr, host_bank, host_num, in_err);
endtask // send_rand_host_rd
// Clean up tb vars. Used for multiple sequence run.
task otf_tb_clean_up();
cfg.scb_h.alert_count["fatal_err"] = 0;
cfg.scb_h.exp_alert_contd["fatal_err"] = 0;
cfg.scb_h.alert_count["recov_err"] = 0;
cfg.scb_h.exp_alert_contd["recov_err"] = 0;
cfg.scb_h.eflash_addr_phase_queue = '{};
cfg.derr_created[0] = 0;
cfg.derr_created[1] = 0;
cfg.derr_addr_tbl.delete();
cfg.derr_otd.delete();
cfg.serr_addr_tbl.delete();
cfg.scb_h.ecc_error_addr.delete();
cfg.scb_h.in_error_addr.delete();
global_derr_is_set = 0;
cfg.otf_scb_h.stop = 1;
cfg.otf_read_entry.hash.delete();
foreach (cfg.otf_read_entry.prv_read[i]) cfg.otf_read_entry.prv_read[i] = '{};
cfg.otf_scb_h.clear_fifos();
for (int i = 0; i < NumBanks; i++) begin
cfg.otf_scb_h.data_mem[i].delete();
foreach (cfg.otf_scb_h.info_mem[i][j])
cfg.otf_scb_h.info_mem[i][j].delete();
end
endtask // otf_tb_clean_up
// Populate cfg.mp_info with default_info_page_cfg except scr, ecc.
// Then program each info region.
task flash_ctrl_default_info_cfg(otf_cfg_mode_e scr_mode = OTFCfgFalse,
otf_cfg_mode_e ecc_mode = OTFCfgFalse);
mubi4_t scr_en, ecc_en;
// If scr/ecc mode is random,
// follow rand_info_c
scr_en = get_mubi_val(scr_mode);
ecc_en = get_mubi_val(ecc_mode);
foreach (cfg.mp_info[i, j, k]) begin
if (cfg.ecc_mode == FlashEccDisabled) cfg.mp_info[i][j][k] = cfg.default_info_page_cfg;
else cfg.mp_info[i][j][k] = rand_info[i][j][k];
if (scr_mode != OTFCfgRand) cfg.mp_info[i][j][k].scramble_en = scr_en;
if (ecc_mode != OTFCfgRand) cfg.mp_info[i][j][k].ecc_en = ecc_en;
// overwrite secret_partition cfg with hw_cfg
cfg.mp_info[0][0][1] = conv2env_mp_info(flash_ctrl_pkg::CfgAllowRead);
cfg.mp_info[0][0][2] = conv2env_mp_info(flash_ctrl_pkg::CfgAllowRead);
flash_ctrl_mp_info_page_cfg(i, j, k, cfg.mp_info[i][j][k]);
`uvm_info("otf_info_cfg", $sformatf("bank:type:page:[%0d][%0d][%0d] = %p",
i, j, k, cfg.mp_info[i][j][k]), UVM_MEDIUM)
end
endtask // flash_ctrl_default_info_cfg
virtual task flash_otf_region_cfg(otf_cfg_mode_e scr_mode = OTFCfgFalse,
otf_cfg_mode_e ecc_mode = OTFCfgFalse);
mubi4_t scr_en, ecc_en;
// If scr/ecc mode is random,
// follow rand_regions_c
scr_en = get_mubi_val(scr_mode);
ecc_en = get_mubi_val(ecc_mode);
flash_ctrl_default_region_cfg(,,,scr_en, ecc_en);
foreach (cfg.mp_regions[i]) begin
cfg.mp_regions[i] = rand_regions[i];
// use default region in FlashEccDisabled mode.
if (cfg.ecc_mode == FlashEccDisabled) cfg.mp_regions[i].en = MuBi4False;
if (scr_mode != OTFCfgRand) cfg.mp_regions[i].scramble_en = scr_en;
if (ecc_mode != OTFCfgRand) cfg.mp_regions[i].ecc_en = ecc_en;
flash_ctrl_mp_region_cfg(i, cfg.mp_regions[i]);
`uvm_info("otf_region_cfg", $sformatf("region[%0d] = %p", i, cfg.mp_regions[i]), UVM_MEDIUM)
end
`uvm_info("otf_region_cfg", $sformatf("default = %p", cfg.default_region_cfg), UVM_MEDIUM)
flash_ctrl_default_info_cfg(scr_mode, ecc_mode);
update_p2r_map(cfg.mp_regions);
endtask // flash_otf_region_cfg
task send_rand_ops(int iter = 1, bit exp_err = 0, bit ctrl_only = 0);
flash_op_t ctrl;
int num, bank;
int host_pct = (ctrl_only)? 0 : 1;
repeat (iter) begin
`DV_CHECK_RANDOMIZE_FATAL(this)
ctrl = rand_op;
bank = rand_op.addr[OTFBankId];
if (ctrl.partition == FlashPartData) begin
num = ctrl_num;
end else begin
num = ctrl_info_num;
end
randcase
1: prog_flash(ctrl, bank, 1, fractions, exp_err);
1: read_flash(ctrl, bank, 1, fractions, 0, exp_err);
host_pct: send_rand_host_rd(.in_err(exp_err));
1: erase_flash(ctrl, bank, exp_err);
endcase // randcase
end
endtask
// Use this task only after flash is disabled.
task flash_access_after_disabled();
`uvm_info(`gfn, "Flash Access after disabled", UVM_LOW)
cfg.m_tl_agent_cfg.check_tl_errs = 0;
send_rand_ops(.iter(5), .exp_err(1), .ctrl_only(1));
// Disable tlul_err_cnt check
cfg.tlul_core_obs_cnt = cfg.tlul_core_exp_cnt;
endtask // flash_access_after_disabled
function void update_partition_access(bit[2:0] acc);
cfg.flash_ctrl_vif.lc_creator_seed_sw_rw_en = lc_ctrl_pkg::Off;
cfg.flash_ctrl_vif.lc_owner_seed_sw_rw_en = lc_ctrl_pkg::Off;
cfg.flash_ctrl_vif.lc_iso_part_sw_rd_en = lc_ctrl_pkg::Off;
cfg.flash_ctrl_vif.lc_iso_part_sw_wr_en = lc_ctrl_pkg::Off;
if (acc[0]) cfg.flash_ctrl_vif.lc_creator_seed_sw_rw_en = lc_ctrl_pkg::On;
if (acc[1]) cfg.flash_ctrl_vif.lc_owner_seed_sw_rw_en = lc_ctrl_pkg::On;
if (acc[2]) begin
cfg.flash_ctrl_vif.lc_iso_part_sw_rd_en = lc_ctrl_pkg::On;
cfg.flash_ctrl_vif.lc_iso_part_sw_wr_en = lc_ctrl_pkg::On;
end
endfunction // update_partition_access
function mubi4_t get_mubi_val(otf_cfg_mode_e mode);
case (mode)
OTFCfgRand: begin
// return true or false with 1:1 ratio
return get_rand_mubi4_val(.other_weight(0));
end
OTFCfgTrue: return MuBi4True;
default: return MuBi4False;
endcase
endfunction // get_mubi_val
function flash_dv_part_e get_dv_part_from_int(int page);
if (page < 1000) return FlashPartData;
else begin
if (page < 1010) begin
return FlashPartInfo;
end else if (page < 1011) begin
return FlashPartInfo1;
end
end
return FlashPartInfo2;
endfunction // get_dv_part_from_int
// return right page number from 1000+ number
function int get_info_page(flash_dv_part_e info, int num);
int page;
case (info)
FlashPartInfo: page = num - 1000;
FlashPartInfo1: page = num - 1010;
FlashPartInfo2: page = num - 1011;
default: `uvm_error("get_info_page", $sformatf("%s is not valid info page",
info.name))
endcase // case (info)
return page;
endfunction // get_info_page
// Write all 1 to secret partition for some write tests.
function void flash_otf_set_secret_part();
int page = 1;
repeat(2) begin
int page_st_addr = page*2048;
uvm_hdl_data_t data = '{default:1};
for (int addr = page_st_addr; addr < (page_st_addr + 8*256); addr += 8) begin
cfg.mem_bkdr_util_h[FlashPartInfo][0].write(addr, data);
end
page++;
end
endfunction
endclass // flash_ctrl_otf_base_vseq