Skip to content

AUTO_MEMORIES (experimental): wire up mask_lane_width, a write mask with a ragged last lane - #4776

Merged
maliberty merged 6 commits into
The-OpenROAD-Project:masterfrom
oharboe:memories-ragged-mask-lane
Nov 6, 2026
Merged

maliberty merged 6 commits into
The-OpenROAD-Project:masterfrom
oharboe:memories-ragged-mask-lane

Conversation

@oharboe

@oharboe oharboe commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Wires up one more experimental AUTO_MEMORIES feature: mask_lane_width on a .memories override entry (ADDITIONAL_MEMORIES). It sets the bits per write-mask lane, with the last lane holding the rest, so a RAM whose byte enables cover a word that is not whole bytes can become a macro. The case that needs it is pulp's tc_sram as cva6's I-cache tag RAM: 25 bits, with 4 byte enables of 8, 8, 8 and 1 bit (asap7/cva6 in #4777).

No current design is asked to change anything. AUTO_MEMORIES is experimental and asap7-only, and no design sets mask_lane_width. Without the field, behaviour is as before:

  • Lanes split the word evenly, and FakeRAM's Verilog model writes the same loop with the same LANE_WIDTH.
  • An uneven split is refused as before. It is now refused in the schema, naming the memory, instead of later in FakeRAM.

With the field:

  • lanes * width >= bits > (lanes - 1) * width; anything else is refused, as is a width without lanes.
  • FakeRAM builds the mask bus as before, one pin per lane, and its Verilog model writes each lane's own bit range.
  • docs/user/AutoMemories.md documents it next to the other overrides.

Smoke test it needs: asap7/tinyRocket, the one design with AUTO_MEMORIES = 1, builds as before, and the existing memories and FakeRAM tests pass.

New tests: flow/scripts/memories/schema_test.py covers the check and the field surviving merge and dump. tools/FakeRAM2.0/test/spsram_mask_test.py covers the config, the model's lanes and the even split. gen_memories_test's 512 × 17 fixture now has one write-enable bit and one data bit per bit, as yosys writes it; the earlier fixture only passed because the uneven split went unchecked.

Integration: The-OpenROAD-Project/bazel-orfs#1484 carries this and builds asap7/cva6 to global route with the tag RAM as a macro.

🤖 Generated with Claude Code

A memory's write mask lanes had to split its word evenly, so pulp's
tc_sram, whose byte enables cover a word that is not whole bytes,
could not become a macro: cva6's I-cache tag RAM is 25 bits with 4
byte enables, 8, 8, 8 and 1 bit wide.

A memory may now declare mask_lane_width: every lane is that wide but
the last, which holds the rest (lanes * width >= bits > (lanes - 1) *
width). FakeRAM builds the mask bus as before, one pin per lane, and
its Verilog model writes each lane's own bit range. Without a width the
lanes split the word evenly, as before, and an uneven split is refused
as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for ragged write mask lanes (where the last lane can be narrower than the others) in memory configurations and Verilog generation. It adds a mask_lane_width parameter to the schema, updates validation logic, and modifies the Verilog exporter to handle uneven splits. The review feedback suggests three key improvements: validating even splits in validate_emittable when mask_lane_width is zero, robustly handling potential null values in JSON parsing to prevent TypeError, and clarifying the error message in memory_config.py when mask_lane_width is provided without mask_lanes.

Comment thread flow/scripts/memories/schema.py Resolved
Comment thread tools/FakeRAM2.0/orfs_asap7/generate.py Resolved
Comment thread tools/FakeRAM2.0/utils/memory_config.py Resolved
validate_emittable checked the lanes only when mask_lane_width was
given, so a memory whose bits do not split evenly over mask_lanes got
past the schema and failed later in FakeRAM. It is now refused in the
schema, naming the memory. FakeRAM names a mask_lane_width given
without mask_lanes, instead of reporting zero lanes that do not cover
the word.

gen_memories_test's 512 x 17 memory kept the 4 write enable bits and 32
data bits of the 128 x 32 it was made from; it now has one of each per
bit, as yosys writes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
@openroad-ci

openroad-ci commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

🔍 QoR check

Metrics reflect the PR merge build — i.e. what will land on the target branch.

Commit 5e68479 · Jenkins build #5 · Baseline: build · View build on dashboard

62 design(s) checked — 0 with regression(s), 0 without a comparable baseline.

An override entry may set mask_lane_width for a word that is not a
whole number of write-mask lanes; say so where the overrides are
documented.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
@oharboe oharboe changed the title memories: a write mask with a ragged last lane AUTO_MEMORIES (experimental): wire up mask_lane_width, a write mask with a ragged last lane Nov 1, 2026
@maliberty

Copy link
Copy Markdown
Member
  1. The mask_lanes field and the mask pin's width are never compared, and the doc example falls into that gap.

    • The schema check (lane_width_check) tests mem.mask_lanes.
    • FakeRAM takes its lane count from the mask pin's width instead (single_port_pins in tools/FakeRAM2.0/orfs_asap7/generate.py).
    • merge keeps the detected pins unless the override brings its own.
    • So the doc's example, { "name": "tag_ram", "mask_lanes": 4, "mask_lane_width": 8 } applied to a detected memory, passes the schema. FakeRAM then sees a detected per-bit mask pin, or none at all for a yosys W0_* memory (lanes=0). It fails with an error that doesn't point at the real cause, such as "mask_lane_width without mask_lanes" when the user did set mask_lanes.
    • asap7/cva6: the RAMs as upstream has them, made macros #4777 avoids this only because its tag RAM entry spells out all its pins, with be 4 bits wide.

    Suggested fix: in validate_emittable, require each mask pin's width to equal mask_lanes, naming the memory. Also either change the doc example to show the full entry with pins, or say that an override of a detected memory must bring a matching mask pin.

  2. The new uneven-split refusal can't break existing flows. validate_emittable runs only on converted memories (gen_memories.py:64). For yosys-detected memories, mask_lanes = len(WR_EN)//wr_ports comes out as bits, so the split is always even. Firtool masks divide evenly too. The old 512×17 test fixture had 4 enable bits over 17 data bits and only passed because nothing checked it; the fix to the fixture is right.

  • In validate_emittable, the guard if mem.mask_lanes or mem.mask_lane_width: is redundant, because lane_width_check already returns when both are zero.

oharboe and others added 2 commits November 2, 2026 18:26
FakeRAM takes a memory's lane count from its mask pin's width and never
reads mask_lanes, and merge keeps the detected pins unless an override
brings its own. An override that set mask_lanes on a detected memory
passed the schema and then failed in FakeRAM with an error that did not
point at the cause, such as "mask_lane_width without mask_lanes" when
mask_lanes was set.

validate_emittable now refuses a mask pin whose width is not
mask_lanes, and mask_lanes on a memory with no mask pin, naming the
memory. Detected memories already agree: yosys's mask pin is
mask_lanes wide, and a firtool memory takes mask_lanes from its pin.
The guard before lane_width_check goes; that check returns by itself
when there are no lanes and no width.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
The mask_lane_width example was a bare override, which on a detected
memory keeps the detected pins and is now refused. Show the whole
entry, pins with a mask pin mask_lanes wide among them, and say that an
override changing mask_lanes must bring its pins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
@oharboe

oharboe commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks, all three check out against the code.

  1. Agreed. FakeRAM counts lanes from the mask pin's width and never reads mask_lanes, and merge keeps the detected pins, so the bare override in the doc passed the schema and failed later in FakeRAM. d70413d makes validate_emittable refuse, naming the memory, a mask pin whose width is not mask_lanes, and also mask_lanes on a memory with no mask pin at all, which is the doc example applied to a detected memory with no mask. Detected memories already agree (yosys's <port>_mask is mask_lanes wide, firtool takes mask_lanes from its pin), and so do the four entries in asap7/cva6: the RAMs as upstream has them, made macros #4777's cva6.memories. 59a9a4d makes the doc example a whole entry with its pins, and says that an override changing mask_lanes must bring its pins with a matching mask pin.
  2. Agreed, no change: only converted memories reach validate_emittable, and yosys's and firtool's masks split evenly.
  3. Agreed, the guard is gone in d70413d.

Tested: schema_test (the new test fails without the check), gen_memories_test, detect_test, idiomatic_test, spsram_mask_test, black 26.10.1; and asap7/cva6 synthesis with AUTO_MEMORIES plus the RAM LEC tests through bazel-orfs with the change applied.

@oharboe
oharboe requested a review from maliberty November 2, 2026 18:08
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
@maliberty
maliberty merged commit 01513bf into The-OpenROAD-Project:master Nov 6, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants