blob: 0f4b9e9e3f321130e61c8f8c204717a4833daa71 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Write and readback test. If write location is not erased, write 0's
// to mimic generic filesystem behavior.
class flash_ctrl_filesystem_support_vseq extends flash_ctrl_otf_base_vseq;
`uvm_object_utils(flash_ctrl_filesystem_support_vseq)
`uvm_object_new
typedef bit [BusBankAddrW-1:0] mem_addr_q_t[$];
// Written memory address ( flash_op.addr[19:3])
bit written_addr[bit[BusBankAddrW-1:0]];
flash_op_t wr_ent_q[$];
flash_op_t pre_wr_ent_q[$];
flash_op_t my_op;
data_q_t readback_data[flash_op_t];
flash_op_t check_ent_q[$];
mem_addr_q_t addr_q;
virtual task body();
int round;
// Don't select a partition defined as read-only
cfg.seq_cfg.avoid_ro_partitions = 1'b1;
special_info_acc_c.constraint_mode(0);
flash_program_data_c.constraint_mode(0);
// Disable scramble, enable ECC
flash_otf_region_cfg(.scr_mode(OTFCfgFalse), .ecc_mode(OTFCfgTrue));
// Initial write to empty locations.
repeat (100) begin
bit ok;
// Create uniq write address.
// Check uniqueness for write location from start address
// up to start_address + (num_words * 4) in 8 byte resolution.
// Then store flash_op to 'wr_ent_q'.
while (!ok) begin
bit redun;
mem_addr_q_t laddr_q;
`DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL(rand_op,
rand_op.op == FlashOpProgram;)
// make sure all address are 8 byte aligned.
rand_op.addr[2:0] = 'h0;
rand_op.otf_addr[2:0] = 'h0;
// Secret partition is hard coded to scr/ecc enable during init.
// Cannot participate this test.
if (is_secret_part(rand_op)) continue;
if (rand_op.num_words % 2) rand_op.num_words++;
laddr_q = generate_all_addr(rand_op);
foreach (laddr_q[i]) begin
if (written_addr.exists(laddr_q[i])) begin
redun = 1;
break;
end else begin
written_addr[laddr_q[i]] = 1;
end
end
ok = ~redun;
if (ok) begin
addr_q = {addr_q, laddr_q};
my_op = rand_op;
wr_ent_q.push_back(my_op);
pre_wr_ent_q.push_back(my_op);
end
end // while (!ok)
prog_flash(.flash_op(rand_op), .bank(rand_op.addr[OTFBankId]),
.num(1), .wd(rand_op.num_words), .store_prog_data(1));
`uvm_info(`gfn, $sformatf("round:%0d ent:%p addr:size:%0d %p", round++, my_op,
addr_q.size(), addr_q), UVM_HIGH)
end
`uvm_info(`gfn, $sformatf("readback check stage 1"), UVM_HIGH)
// Readback check;
pre_wr_ent_q.shuffle();
foreach(pre_wr_ent_q[i]) begin
filesys_read_check(.flash_op_r(pre_wr_ent_q[i]), .use_cfg_rdata(1));
end
`uvm_info(`gfn, $sformatf("readback check stage 2 with 1bit error"), UVM_HIGH)
// Readback check with 1 bit error
pre_wr_ent_q.shuffle();
foreach(pre_wr_ent_q[i]) begin
filesys_read_check(.flash_op_r(pre_wr_ent_q[i]), .use_cfg_rdata(1), .serr(1));
end
wr_ent_q.shuffle();
round = 0;
// Mimicing file system behavior here.
// If locations are from 'wr_ent_q', write all zeros.
// Otherwise write random data.
repeat (20) begin
`uvm_info(`gfn, $sformatf("TEST: round:%0d", round++), UVM_MEDIUM)
randcase
2: begin
my_op = wr_ent_q.pop_front();
filesys_ctrl_prgm(.flash_op_p(my_op), .all_zero(1),
.wdata(readback_data[my_op]));
end
1: begin
`DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL(rand_op,
rand_op.op == FlashOpProgram;)
// make sure all address are 8 byte aligned.
rand_op.addr[2:0] = 'h0;
rand_op.otf_addr[2:0] = 'h0;
if (rand_op.num_words % 2) rand_op.num_words++;
my_op = rand_op;
if (is_secret_part(my_op)) continue;
filesys_ctrl_prgm(.flash_op_p(my_op), .all_zero(0),
.wdata(readback_data[my_op]));
end
endcase
check_ent_q.push_back(my_op);
end
`uvm_info(`gfn, $sformatf("readback check stage 3"), UVM_HIGH)
// Readback check;
foreach(check_ent_q[op]) begin
`uvm_info("read_check", $sformatf("%p", check_ent_q[op]), UVM_MEDIUM)
filesys_read_check(.flash_op_r(check_ent_q[op]));
end
`uvm_info(`gfn, $sformatf("readback check stage 4 with 1 bit error"), UVM_HIGH)
// Readback check with 1 bit error
foreach(check_ent_q[op]) begin
`uvm_info("read_check", $sformatf("%p", check_ent_q[op]), UVM_MEDIUM)
filesys_read_check(.flash_op_r(check_ent_q[op]), .serr(1));
end
endtask
// Program flash.
// Program all 0 or random data depends on 'all_zero' value.
// If program address is in global 'addr_q', program all zero
// to the address.
// Afte program, store write data for readback check.
task filesys_ctrl_prgm(flash_op_t flash_op_p, bit all_zero, output data_q_t wdata);
bit poll_fifo_status = 1;
bit [BusBankAddrW-1:0] mem_addr = (flash_op_p.addr >> 3);
bit ovwr_zero = 0;
`uvm_info(`gfn, $sformatf("PROGRAM ADDRESS: 0x%0h flash_op:%p is_secret:%b",
flash_op_p.addr, flash_op_p, is_secret_part(flash_op_p)), UVM_MEDIUM)
// Randomize Write Data
for (int j = 0; j < flash_op_p.num_words; j++) begin
if (j % 2 == 0) begin
foreach (addr_q[i]) begin
if (mem_addr == addr_q[i]) begin
ovwr_zero = 1;
break;
end
end
end
flash_program_data[j] = (all_zero | ovwr_zero)? 'h0 : $urandom();
wdata.push_back(flash_program_data[j]);
if (j % 2 == 1) begin
ovwr_zero = 0;
mem_addr++;
end
end
flash_ctrl_start_op(flash_op_p);
flash_ctrl_write(flash_program_data, poll_fifo_status);
wait_flash_op_done(.timeout_ns(cfg.seq_cfg.prog_timeout_ns));
endtask // filesys_ctrl_prgm_page
// Read data either from host or controller, and compare with
// readback_data[flash_op_t].
task filesys_read_check(flash_op_t flash_op_r, bit use_cfg_rdata = 0, bit serr = 0);
data_q_t read_data, ref_data;
flash_op_r.op = FlashOpRead;
if (flash_op_r.partition == FlashPartData) begin
randcase
1: begin
`uvm_info(`gfn, "send ctrl_read", UVM_MEDIUM)
filesys_ctrl_read(flash_op_r, read_data, serr);
end
1: begin
`uvm_info(`gfn, "send host_read", UVM_MEDIUM)
filesys_host_read(flash_op_r, read_data, serr);
end
endcase // randcase
end else begin // if (flash_op_r.partition == FlashPartData)
`uvm_info(`gfn, "send ctrl_read", UVM_MEDIUM)
filesys_ctrl_read(flash_op_r, read_data, serr);
end
ref_data = (use_cfg_rdata)? cfg.prog_data[flash_op_r] : readback_data[flash_op_r];
for (int i = 0; i < ref_data.size(); i++) begin
`DV_CHECK_EQ(read_data[i], ref_data[i],
$sformatf("read_check:%0d ", i))
end
endtask // filesys_read_check
// All addresses are 8byte aligned.
task filesys_ctrl_read(flash_op_t flash_op_r, output data_q_t rdata, input bit serr);
bit poll_fifo_status = 1;
int size;
`uvm_info(`gfn, $sformatf("filesys_ctrl_read: flash_op:%p is_secret:%b",
flash_op_r, is_secret_part(flash_op_r)), UVM_MEDIUM)
rdata = '{};
size = flash_op_r.num_words / 2;
if (serr) begin
filesystem_add_bit_err(flash_op_r.partition, flash_op_r.addr, size);
end
flash_ctrl_start_op(flash_op_r);
flash_ctrl_read(flash_op_r.num_words, rdata, poll_fifo_status);
wait_flash_op_done();
endtask // filesys_ctrl_read
task filesys_host_read(flash_op_t flash_op_r, output data_q_t rdata, input bit serr);
bit [TL_AW-1:0] tl_addr;
bit completed;
data_4s_t unit_rdata;
tl_addr = flash_op_r.addr;
rdata = '{};
for (int i = 0; i < flash_op_r.num_words; i++) begin
if (serr & (i % 2 == 0)) begin
filesystem_add_bit_err(flash_op_r.partition, tl_addr, 1);
end
do_direct_read(.addr(tl_addr), .mask('1), .blocking(1), .rdata(unit_rdata),
.completed(completed));
rdata.push_back(unit_rdata);
tl_addr += 4;
end
endtask // filesys_host_read
// Create all addresses for the flash_op from the start address
// in the resolution of 8 bytes
function mem_addr_q_t generate_all_addr(flash_op_t flash_op);
// For odd numbers, add one more address.
int addr_end = (flash_op.num_words + 1) / 2;
for (int i = 0; i < addr_end; i++) begin
generate_all_addr.push_back((flash_op.addr >> 3));
flash_op.addr += 8;
end
endfunction // generate_all_addr
function void filesystem_add_bit_err(flash_dv_part_e partition, addr_t addr, int size);
int err_idx, bank;
addr_t per_bank_addr;
bit [flash_phy_pkg::FullDataWidth-1:0] mem_data;
bank = addr[OTFBankId];
for (int i = 0; i < size; i++) begin
per_bank_addr = addr;
per_bank_addr[31:OTFBankId] = 'h0;
if (!cfg.serr_addr_tbl[addr].exists(partition)) begin
cfg.serr_addr_tbl[addr][partition] = 1;
err_idx = $urandom_range(0, 75);
cfg.flash_bit_flip(cfg.mem_bkdr_util_h[partition][bank], per_bank_addr, err_idx);
// Flip the same bit in the reference mem model.
if (partition == FlashPartData) begin
mem_data = cfg.otf_scb_h.data_mem[bank][per_bank_addr>>3];
mem_data[err_idx] = ~mem_data[err_idx];
cfg.otf_scb_h.data_mem[bank][per_bank_addr>>3] = mem_data;
end else begin
mem_data = cfg.otf_scb_h.info_mem[bank][partition>>1][per_bank_addr>>3];
mem_data[err_idx] = ~mem_data[err_idx];
cfg.otf_scb_h.info_mem[bank][partition>>1][per_bank_addr>>3] = mem_data;
end
end
addr += 8;
end
endfunction // filesystem_add_bit_err
endclass // flash_ctrl_filesystem_support_vseq