[dv, flash_ctrl] Fix the smoke test

- The smoke test writes and reads back a random chunk of memory within
the flash.
- The start address and the randomized number of words is bus-aligned
(i.e. 32 bits) and not flash word aligned (64-bits).
- Between each randomized configuration, the flash mem is invalidated.
This means if we write to a flash mem half word, while leaving the other
half word as Xs, that causes an X problem on reads, because flash ctrl
reads in the whole word, not just the half word.
This commit fixes that problem.

Signed-off-by: Srikrishna Iyer <sriyer@google.com>
diff --git a/hw/ip/flash_ctrl/dv/env/flash_ctrl_env_cfg.sv b/hw/ip/flash_ctrl/dv/env/flash_ctrl_env_cfg.sv
index 57eda60..c8ac3d7 100644
--- a/hw/ip/flash_ctrl/dv/env/flash_ctrl_env_cfg.sv
+++ b/hw/ip/flash_ctrl/dv/env/flash_ctrl_env_cfg.sv
@@ -84,14 +84,23 @@
   endfunction : flash_mem_bkdr_read
 
   // Writes the flash mem contents via backdoor.
-  // The addr arg need not be word aligned- its the same addr programmed into the `control` CSR.
+  //
+  // The addr need not be bus word aligned, Its the same addr programmed into the `control` CSR.
+  // The data queue is sized for the bus word.
   // TODO: support for partition.
   virtual function void flash_mem_bkdr_write(flash_op_t flash_op,
                                              flash_mem_init_e scheme,
-                                             logic [flash_ctrl_pkg::DataWidth-1:0] data[$] = {});
+                                             logic [TL_DW-1:0] data[$] = {});
     flash_mem_addr_attrs addr_attrs = new(flash_op.addr);
-    logic [flash_ctrl_pkg::DataWidth-1:0] wr_data;
-    int num_words;
+    logic [TL_DW-1:0] wr_data;
+
+    // Randomize the lower half-word (if Xs) if the first half-word written in the below loop is
+    // corresponding upper half-word.
+    if (addr_attrs.bank_addr[flash_ctrl_pkg::DataByteWidth-1]) begin
+      _randomize_uninitialized_half_word(.partition(flash_op.partition), .bank(addr_attrs.bank),
+                                         .addr(addr_attrs.word_addr));
+    end
+
     case (scheme)
       FlashMemInitCustom: begin
         flash_op.num_words = data.size();
@@ -106,24 +115,45 @@
         wr_data = {flash_ctrl_pkg::DataWidth{1'bx}};
       end
     endcase
-    // If start address is the 32 MSBs half word of some 64bits word and num_words is even, an
-    //  additional iteration is required to write all required words.
-    num_words = (addr_attrs.is_start_addr_msb && (flash_op.num_words % 2 == 0)) ?
-                    flash_op.num_words + 1 : flash_op.num_words;
-    for (int i = 0; i < num_words; i += 2) begin
-      logic [flash_ctrl_pkg::DataWidth-1:0] loc_data = (scheme == FlashMemInitCustom) ? data[i] :
-          (scheme == FlashMemInitRandomize) ? $urandom : wr_data;
 
-      mem_bkdr_util_h[flash_op.partition][addr_attrs.bank].write64(addr_attrs.full64_bank_addr,
-                                                                   loc_data);
+    for (int i = 0; i < flash_op.num_words; i++) begin
+      logic [TL_DW-1:0] loc_data = (scheme == FlashMemInitCustom) ? data[i] :
+          (scheme == FlashMemInitRandomize) ? $urandom() : wr_data;
+
+      mem_bkdr_util_h[flash_op.partition][addr_attrs.bank].write32(addr_attrs.bank_addr, loc_data);
       `uvm_info(`gfn, $sformatf("flash_mem_bkdr_write: {%s} = 0x%0h", addr_attrs.sprint(),
                                 loc_data), UVM_MEDIUM)
-      addr_attrs.incr(flash_ctrl_env_pkg::FlashBankBytesPerWord);
+      addr_attrs.incr(TL_DBW);
+    end
+
+    // Randomize the upper half-word (if Xs) if the last word written in the above loop is
+    // corresponding lower half-word.
+    if (addr_attrs.bank_addr[flash_ctrl_pkg::DataByteWidth-1]) begin
+      _randomize_uninitialized_half_word(.partition(flash_op.partition), .bank(addr_attrs.bank),
+                                         .addr(addr_attrs.bank_addr));
     end
   endfunction : flash_mem_bkdr_write
 
+  // Helper function that randomizes the half-word at the given address if unknown.
+  //
+  // When the 'other' flash half-word is being written by the flash_mem_bkdr_write() method, the
+  // half-word at the given address needs to also be updated, of the data at that address is
+  // unknown. This is needed because the flash_ctrl RTL internally fetches full words. This method
+  // randomizes the data at the given address via backdoor.
+  function void _randomize_uninitialized_half_word(flash_dv_part_e partition, uint bank,
+                                                   bit [TL_AW-1:0] addr);
+    logic [TL_DW-1:0] data = mem_bkdr_util_h[partition][bank].read32(addr);
+    if ($isunknown(data)) begin
+      `DV_CHECK_STD_RANDOMIZE_FATAL(data)
+      `uvm_info(`gfn, $sformatf("Data at 0x%0h is Xs, writing random 0x%0h", addr, data), UVM_HIGH)
+      mem_bkdr_util_h[partition][bank].write32(addr, data);
+    end
+  endfunction
+
   // Checks flash mem contents via backdoor.
-  // The addr arg need not be word aligned- its the same addr programmed into the `control` CSR.
+  //
+  // The addr need not be bus word aligned. Its the same addr programmed into the `control` CSR.
+  // The exp data queue is sized for the bus word.
   // TODO: support for partition.
   virtual function void flash_mem_bkdr_read_check(flash_op_t flash_op,
                                                   const ref bit [TL_DW-1:0] exp_data[$]);
@@ -134,7 +164,7 @@
     end
   endfunction : flash_mem_bkdr_read_check
 
-  // Ensure that the flash page / bank has indeed been erased.
+  // Verifies that the flash page / bank has indeed been erased.
   virtual function void flash_mem_bkdr_erase_check(flash_op_t flash_op);
     flash_mem_addr_attrs    addr_attrs = new(flash_op.addr);
     bit [TL_AW-1:0]         erase_check_addr;
diff --git a/hw/ip/flash_ctrl/dv/env/flash_mem_addr_attrs.sv b/hw/ip/flash_ctrl/dv/env/flash_mem_addr_attrs.sv
index d8fae9b..503b7cc 100644
--- a/hw/ip/flash_ctrl/dv/env/flash_mem_addr_attrs.sv
+++ b/hw/ip/flash_ctrl/dv/env/flash_mem_addr_attrs.sv
@@ -5,34 +5,28 @@
 // Provides abstraction for mapping a flash memory address in flash organization.
 class flash_mem_addr_attrs;
 
-    bit [TL_AW-1:0] addr;               // Input addr (this is aligned to the bus word)
-    bit [TL_AW-1:0] bank_addr;          // Addr within the bank.
-    bit [TL_AW-1:0] full64_addr;        // Input full word addr (this is aligned to 64bits word)
-    bit [TL_AW-1:0] full64_bank_addr;   // Full word addr within the bank.
+    bit [TL_AW-1:0] addr;               // Input addr, bus-word aligned.
+    bit [TL_AW-1:0] bank_addr;          // Addr within the bank, bus-word aligned.
+    bit [TL_AW-1:0] word_addr;          // Addr within the bank, flash word aligned.
+    int             offset;             // Byte offset within the flash word.
 
-    bit [TL_AW-1:0] bank_start_addr;    // Start addr of the bank.
+    bit [TL_AW-1:0] bank_start_addr;    // Start addr of the bank (bus word aligned).
     bit [TL_AW-1:0] page_start_addr;    // Start addr of the page within the bank.
 
     uint            bank;               // The bank the address belongs to.
     uint            page;               // The page within the bank.
     uint            line;               // The word line within the page.
-
-    bit             is_start_addr_msb;  // Tells whether the start address of this addr_attr is
-                                        //  the 32 MSBs of some 64 bits word or the 32 LSBs.
+    uint            byte_offset;        // Byte offset within the flash word.
 
   function new(bit [TL_AW-1:0] addr = 0);
-    is_start_addr_msb = addr[flash_ctrl_pkg::BusByteWidth];
     set_attrs(addr);
   endfunction
 
   // Set attributes from a sample input addr.
   function void set_attrs(bit [TL_AW-1:0] addr);
-
     this.addr = {addr[TL_AW-1:TL_SZW], {TL_SZW{1'b0}}};
     bank_addr = this.addr[FlashMemAddrPageMsbBit : 0];
-    full64_addr = {addr[TL_AW-1:flash_ctrl_env_pkg::FlashDataByteWidth],
-                 {flash_ctrl_env_pkg::FlashDataByteWidth{1'b0}}};
-    full64_bank_addr = full64_addr[FlashMemAddrPageMsbBit : 0];
+    word_addr = {bank_addr[TL_AW-1:FlashDataByteWidth], {FlashDataByteWidth{1'b0}}};
 
     bank_start_addr = {addr[TL_AW-1 : FlashMemAddrPageMsbBit+1], {FlashMemAddrPageMsbBit+1{1'b0}}};
     page_start_addr = {addr[FlashMemAddrPageMsbBit : FlashMemAddrLineMsbBit+1],
@@ -41,6 +35,7 @@
     bank = addr[FlashMemAddrBankMsbBit : FlashMemAddrPageMsbBit+1];
     page = addr[FlashMemAddrPageMsbBit : FlashMemAddrLineMsbBit+1];
     line = addr[FlashMemAddrLineMsbBit : FlashMemAddrWordMsbBit+1];
+    byte_offset = addr[FlashDataByteWidth-1 : 0];
   endfunction
 
   function void incr(bit [TL_AW-1:0] offset);
@@ -49,11 +44,11 @@
   endfunction
 
   function string sprint();
-    return $sformatf({{"addr_attrs: addr = 0x%0h, bank_addr = 0x%0h "},
+    return $sformatf({{"addr_attrs: addr = 0x%0h, word_addr = 0x%0h, bank_addr = 0x%0h "},
                       {"bank_start_addr = 0x%0h, page_start_addr = 0x%0h "},
-                      {"bank = %0d, page = %0d, line = %0d"}},
-                     addr, bank_addr, bank_start_addr, page_start_addr,
-                     bank, page, line);
+                      {"bank = %0d, page = %0d, line = %0d, byte_offset = %0d"}},
+                     addr, word_addr, bank_addr, bank_start_addr, page_start_addr,
+                     bank, page, line, byte_offset);
   endfunction
 
 endclass