UCSBarchlab / PyRTL

A collection of classes providing simple hardware specification, simulation, tracing, and testing suitable for teaching and research. Simplicity, usability, clarity, and extensibility are the overarching goals, rather than performance or optimization.
http://ucsbarchlab.github.io/PyRTL
BSD 3-Clause "New" or "Revised" License
251 stars 76 forks source link

Add bitmasked write to memories #418

Open timsherwood opened 2 years ago

timsherwood commented 2 years ago

Add a bit-masked write operations for memories that only update a subset of the bits in the wordline.

An example from openpiton that's meant to infer properly on xilinx FPGAs (this chunk specifically is for a one read-write port bram) https://github.com/PrincetonUniversity/openpiton/blob/openpiton/piton/tools/bin/pyhplib_sram.py#L196-L246

pyhplib_sram.py

def Get1RWTemplate():
  return '''
`include "define.tmp.h"
`ifdef DEFAULT_NETTYPE_NONE
`default_nettype none
`endif
module _PARAMS_NAME
(
input wire MEMCLK,
input wire RESET_N,
input wire CE,
input wire [_PARAMS_HEIGHT_LOG-1:0] A,
input wire RDWEN,
input wire [_PARAMS_WIDTH-1:0] BW,
input wire [_PARAMS_WIDTH-1:0] DIN,
output wire [_PARAMS_WIDTH-1:0] DOUT,
input wire [`BIST_OP_WIDTH-1:0] BIST_COMMAND,
input wire [`SRAM_WRAPPER_BUS_WIDTH-1:0] BIST_DIN,
output reg [`SRAM_WRAPPER_BUS_WIDTH-1:0] BIST_DOUT,
input wire [`BIST_ID_WIDTH-1:0] SRAMID
);
reg [_PARAMS_WIDTH-1:0] cache [_PARAMS_HEIGHT-1:0];

integer i;
initial
begin
   for (i = 0; i < _PARAMS_HEIGHT; i = i + 1)
   begin
      cache[i] = 0;
   end
end

   reg [_PARAMS_WIDTH-1:0] dout_f;

   assign DOUT = dout_f;

   always @ (posedge MEMCLK)
   begin
      if (CE)
      begin
         if (RDWEN == 1'b0)
            cache[A] <= (DIN & BW) | (cache[A] & ~BW);
         else
            dout_f <= cache[A];
      end
   end

endmodule

BW is "bit write", it's a mask of the bits you want to replace in the line you're writing to

so the key part is the bottom bit:

   reg [_PARAMS_WIDTH-1:0] dout_f;
   assign DOUT = dout_f;
   always @ (posedge MEMCLK)
   begin
      if (CE)
      begin
         if (RDWEN == 1'b0)
            cache[A] <= (DIN & BW) | (cache[A] & ~BW);
         else
            dout_f <= cache[A];
      end
   end

CE is chip enable, so just whether you should do any access at all RDWEN is read vs write DIN is your input data

The thought is you could have something, MaskedWrite, similar to EnabledWrite, which gave the bitmask instead of a single bit. If any bit were 1, then it's an enabled write, and for the particular bits that are 1, they're replaced with the bits from the data you provide.

timsherwood commented 2 years ago

There are two different classes of question to answer here: 1) How do we expose such a change to the user (e.g. MaskedWrite as discussed above or extend EnabledWrite to simply allow for multiple bits?) 2) How do we implement this in pyrtl hardware model given that right now a 1-bit enable is hard coded into the "core" (e.g. we could modify core write-ports @ to extend enable to multiple bits for all memories, we could extend core with both 1-bit and multi-bit enables, some other way?)