Skip to content

[FIRRTL] Suspected incorrect memory mask lowering for aggregate partial write with dynamic subaccess #10105

@Waryc

Description

@Waryc

firtool-1.144.0 (LLVM 23.0.0git)

Reproducer

FIRRTL version 6.0.0
circuit MaskNested :
  public module MaskNested :
    input clock : Clock
    input wen : UInt<1>
    input addr : UInt<2>
    input sel : UInt<1>
    input in_a : UInt<3>
    input in_b : UInt<1>
    output out_a : UInt<3>
    output out_b : UInt<1>

    cmem mem : { a : UInt<3>, b : UInt<1>[2] }[4]
    infer mport rd = mem[addr], clock
    infer mport wr = mem[addr], clock

    when wen :
      connect wr.a, in_a
      connect wr.b[sel], in_b

    connect out_a, rd.a
    connect out_b, rd.b[sel]
./firtool m.fir -o m.sv

Generated Verilog

module MaskNested(
  input        clock, wen,
  input  [1:0] addr,
  input        sel,
  input  [2:0] in_a,
  input        in_b,
  output [2:0] out_a,
  output       out_b
);
  wire       mem_wr_mask_b_0 = wen & ~sel;
  wire       mem_wr_mask_b_1 = wen & sel;
  mem_4x5 mem_ext (
    ...
    .W0_data ({in_a, {2{in_b}}}),
    .W0_mask ({wen, mem_wr_mask_b_1, {3{mem_wr_mask_b_0}}})
  );
  assign out_a = _mem_ext_R0_data[4:2];
  assign out_b = sel ? _mem_ext_R0_data[1] : _mem_ext_R0_data[0];
endmodule

Memory writes are bit-granular:

  always @(posedge W0_clk) begin
    if (W0_en & W0_mask[0]) Memory[W0_addr][0] <= W0_data[0];
    if (W0_en & W0_mask[1]) Memory[W0_addr][1] <= W0_data[1];
    if (W0_en & W0_mask[2]) Memory[W0_addr][2] <= W0_data[2];
    if (W0_en & W0_mask[3]) Memory[W0_addr][3] <= W0_data[3];
    if (W0_en & W0_mask[4]) Memory[W0_addr][4] <= W0_data[4];
  end

The Bug

Read-side confirms the bit packing:

bit [4:2] = a       (3 bits)
bit [1]   = b[1]    (1 bit)
bit [0]   = b[0]    (1 bit)

Mask should match this layout. Expanding the generated mask concatenation:

Generated:  {wen, mem_wr_mask_b_1, mem_wr_mask_b_0, mem_wr_mask_b_0, mem_wr_mask_b_0}
             [4]       [3]              [2]               [1]               [0]

  bit 4  a[2]  → wen              ✓
  bit 3  a[1]  → wen & sel        ✗  (should be wen)
  bit 2  a[0]  → wen & ~sel       ✗  (should be wen)
  bit 1  b[1]  → wen & ~sel       ✗  (should be wen & sel)
  bit 0  b[0]  → wen & ~sel       ✓

Expected: {wen, wen, wen, (wen & sel), (wen & ~sel)}
           [4]  [3]  [2]     [1]           [0]
          a[2] a[1] a[0]    b[1]          b[0]

The b[sel] per-element masks bleed into the a field; only a's MSB and b[0] are correctly masked.

AI-Assisted-By

Claude-Opus-4.6

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions