[otbn, rtl] Only calculate CRC for 32-bit aligned writes

Signed-off-by: Greg Chadwick <gac@lowrisc.org>
diff --git a/hw/ip/otbn/doc/_index.md b/hw/ip/otbn/doc/_index.md
index ab5c626..a9cd68f 100644
--- a/hw/ip/otbn/doc/_index.md
+++ b/hw/ip/otbn/doc/_index.md
@@ -940,6 +940,7 @@
 Here, `imem` is a single bit flag which is one for writes to IMEM and zero for writes to DMEM.
 The `idx` value is the index of the word within the memory, zero extended from 10b to 15b.
 Finally, `wdata` is the 32b word that was written.
+Writes that are less than 32b or not aligned on a 32b boundary are ignored and not factored into the CRC calculation.
 
 The host processor can also write to the register.
 Typically, this will be to clear the value to `32'h00000000`, the traditional starting value for a 32-bit CRC.
diff --git a/hw/ip/otbn/rtl/otbn.sv b/hw/ip/otbn/rtl/otbn.sv
index 5fb0dfe..c28dc4d 100644
--- a/hw/ip/otbn/rtl/otbn.sv
+++ b/hw/ip/otbn/rtl/otbn.sv
@@ -191,6 +191,7 @@
   logic [38:0] imem_wdata_bus;
   logic [38:0] imem_wmask_bus;
   logic [38:0] imem_rdata_bus;
+  logic [top_pkg::TL_DBW-1:0] imem_byte_mask_bus;
   logic imem_rvalid_bus;
   logic [1:0] imem_rerror_bus;
 
@@ -355,6 +356,8 @@
   assign imem_rvalid_bus  = (~imem_access_core & imem_rvalid) | imem_dummy_response_q;
   assign imem_rvalid_core = imem_access_core ? imem_rvalid : 1'b0;
 
+  assign imem_byte_mask_bus = tl_win_h2d[TlWinImem].a_mask;
+
   // No imem errors reported for bus reads. Integrity is carried through on the bus so integrity
   // checking on TL responses will pick up any errors.
   assign imem_rerror_bus = 2'b00;
@@ -397,6 +400,7 @@
   logic [ExtWLEN-1:0] dmem_rdata_bus;
   logic [top_pkg::TL_AW-1:0] dmem_addr_bus;
   logic [31:0] dmem_wdata_narrow_bus;
+  logic [top_pkg::TL_DBW-1:0] dmem_byte_mask_bus;
   logic dmem_rvalid_bus;
   logic [1:0] dmem_rerror_bus;
 
@@ -543,11 +547,16 @@
 
   assign dmem_addr_bus = tl_win_h2d[TlWinDmem].a_address;
   assign dmem_wdata_narrow_bus = tl_win_h2d[TlWinDmem].a_data[31:0];
+  assign dmem_byte_mask_bus = tl_win_h2d[TlWinDmem].a_mask;
 
   // Memory Load Integrity =====================================================
+  // CRC logic below assumes a incoming data bus width of 32 bits
+  `ASSERT_INIT(TLDWIs32Bit_A, top_pkg::TL_DW == 32)
 
-  assign mem_crc_data_in_valid   = (imem_req_bus | dmem_req_bus) &
-                                  ~(dmem_access_core | imem_access_core);
+  // Only advance CRC calculation on full 32-bit writes;
+  assign mem_crc_data_in_valid   = ~(dmem_access_core | imem_access_core) &
+      ((imem_req_bus & (imem_byte_mask_bus == 4'hf)) |
+       (dmem_req_bus & (dmem_byte_mask_bus == 4'hf)));
 
   assign mem_crc_data_in.wr_data = imem_req_bus ? imem_wdata_bus[31:0] :
                                                   dmem_wdata_narrow_bus[31:0];