# Implement mem v0 per D-092, with enforced read-latency semantics · versecafe/strata

[View on GitCafe](https://git.cafe/versecafe/strata/commit/7a7a1544c70f936a726ff076a85218064160dee5)

Repository: [versecafe/strata](https://git.cafe/versecafe/strata)

Visibility: public

Requested revision: 7a7a1544c70f936a726ff076a85218064160dee5

Requested commit: 7a7a1544c70f936a726ff076a85218064160dee5

Commit: 7a7a1544c70f936a726ff076a85218064160dee5

Tree: 58e1e26796d3528f622317d7d2e4d35863d4eb25

Author: versecafe

Committer: versecafe

## Message

```
Implement mem v0 per D-092, with enforced read-latency semantics

Real mem support across all four pipeline stages, not a permissive
placeholder — a body that uses a mem read's value before it crosses
the one-cycle latency boundary is rejected, not silently accepted.

- strata-hir: StatementKind::Memory{binding, ty, reset}. reset is
  captured (not dropped) specifically so strata-check can reject it
  with a real diagnostic rather than a generic parse error.
- strata-check: mem declaration checking (array-type requirement,
  reset rejection via mem_reset_not_allowed), CheckedExprKind::Index
  shared generically by reg-array and mem indexing, and the
  enforcement itself: a structural taint system where any mem-sourced
  Index read taints its expression tree, taint propagates through
  lets, and every combinational consumption site (return, output,
  address position, mem-write value slot) rejects a tainted value with
  mem_read_used_before_latency_boundary. A register update's value
  slot is the one deliberate exemption — that's the legal "assign to a
  reg, consume next cycle" crossing the diagnostic itself prescribes.
  A mem-write's value slot is NOT exempted (narrower reading of D-092
  than "any reg-shaped sink clears taint").
- strata-arch-ir: Binding::Memory, a real Memory node (element, depth,
  fixed one-read/one-write port list, read_latency: 1, no reset) per
  07-compiler-architecture.md's previously-unimplemented canonical
  node list.
- strata-circt: D-092's explicit fallback lowering (address-decoded
  write-enable mux driving N seq.firregs, priority-mux read decode) —
  the pinned firtool's seq-dialect memory-op surface couldn't be
  verified without toolchain/network access, so the fallback was used
  per the design doc rather than guessed. Deliberately no internal
  registered-address stage: strata-check already forces every mem read
  through an external register before use, and combinational-read +
  mandatory external register is exactly one cycle of latency: adding
  an internal stage too would over-deliver two cycles against D-092
  §2's literal one-cycle promise. No ram_style attribute was added —
  couldn't verify valid CIRCT attribute syntax without the toolchain,
  so a synthesis hint was dropped rather than emitting unverified MLIR.

Verified end-to-end via the real CLI, not just unit tests: the new
mem-lookup.strata fixture checks clean and compiles to well-formed
MLIR (4 firregs, correct write-enable decode, correct read-latency
register); its mem-lookup-bug-combinational-read.strata sibling is
rejected by both `strata check` and `strata compile` with the
expected diagnostic.

Honest scope boundary: reg-array indexed reads/writes are checked
generically (shared Index node) but not lowered in arch-ir/CIRCT —
that gap already existed and isn't closed here. mem/Index values have
no strata-arch-sim model yet (RuntimeError::UnsupportedMemory rather
than a panic). strata-structure keeps its existing "memory ports
unavailable" disclaimer rather than a new report schema section.
Both are out of D-092's stated v0 scope, not overlooked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

```

## Parents

- [06a4f98bcf687ad2c86b99aa7d0d12ecbd5a2bb9](https://git.cafe/versecafe/strata/commit/06a4f98bcf687ad2c86b99aa7d0d12ecbd5a2bb9?format=markdown)

[Source at this commit](https://git.cafe/versecafe/strata/tree/7a7a1544c70f936a726ff076a85218064160dee5?format=markdown)
