Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f38c1c7
spec/memw: read/write from/to -> read from/write to
erik-3milabs Mar 17, 2026
677b166
spec/memw: rename add_limb_overflow as carry
erik-3milabs Mar 17, 2026
ad3f093
spec/memw: minor var desc updates
erik-3milabs Mar 17, 2026
109489b
spec/memw: remove superfluous minus symbol
erik-3milabs Mar 17, 2026
03a2338
spec/memw: update description
erik-3milabs Mar 17, 2026
b8fc0b7
spec/memw_a: minor optimization
erik-3milabs Mar 17, 2026
b6b00c6
Apply suggestions from code review
erik-3milabs Mar 18, 2026
a099bbd
spec/MEMW: fix interaction typing
erik-3milabs Mar 18, 2026
448555d
spec/MEMW: drop superfluous notes
erik-3milabs Mar 18, 2026
4936d8c
spec/MEMW: update alignment requirement
erik-3milabs Mar 18, 2026
0e3d0f8
spec/MEMW: intentionally separate carry's prose and .toml descriptions
erik-3milabs Mar 18, 2026
46b9e97
spec/REGW: introduce REGW chip
erik-3milabs Mar 23, 2026
1e2cb68
spec/MEMW_R: move REG to MEMW_R
erik-3milabs Mar 23, 2026
e01db15
spec/MEMW_R: optimize
erik-3milabs Mar 23, 2026
5eaacc2
spec/MEMW_A: padding
erik-3milabs Mar 23, 2026
7c8927b
spec/MEMW_R: touch ups
erik-3milabs Mar 23, 2026
2a1d8e3
spec/MEMW_R: drop superfluous constraints
erik-3milabs Mar 23, 2026
ed889ff
spec/MEMW_R: minor fixes
erik-3milabs Mar 23, 2026
cc33da8
spec/MEMW_R: fix address mul 2 bug
erik-3milabs Mar 23, 2026
75199d5
spec/MEMW: fix multiplicities
erik-3milabs Mar 23, 2026
55ed2e6
spec/MEMW_A: padding
erik-3milabs Mar 23, 2026
0b99118
spec/MEMW: padding
erik-3milabs Mar 23, 2026
c7203e5
spec/MEMW: bit check multiplicities
erik-3milabs Mar 23, 2026
80886a6
Merge branch 'spec/memw_update2' into spec/MEMW_R
erik-3milabs Mar 23, 2026
6c6fcf0
Merge branch 'spec/main' into spec/MEMW_R
erik-3milabs Mar 23, 2026
ea69070
spec/MEMW_R: apply 2x to address
erik-3milabs Mar 23, 2026
f5a27aa
spec/MEMW_R: remove superfluous casts
erik-3milabs Mar 23, 2026
3750f40
spec/MEMW_R: list future optimization
erik-3milabs Mar 23, 2026
c9ed517
spec/MEMW_R: fix typo
erik-3milabs Mar 23, 2026
42b6d18
spec/MEMW_R: fix address
erik-3milabs Mar 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 57 additions & 3 deletions spec/memw.typ
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
total_nr_variables,
total_nr_instantiated_columns,
render_constraint_table,
render_chip_padding_table,
render_chip_padding_table
)

#let config = load_config()
Expand Down Expand Up @@ -96,8 +96,62 @@ The #aligned chip only needs #nr_variables variables, expressed through #nr_colu
The table can be padded to the next power of two with the following value assignments:
#render_chip_padding_table(alignedchip, config)

= Future optimization ideas
= Register fast-path

#let config = load_config()
#let register_chip = load_chip("src/memw_register.toml", config)
#let reg = raw(register_chip.name)

The #reg chip provides a fast-path for accessing registers.
This fast-path leverages that registers
+ can be addressed using a `Byte`, rather than a full `DWord`,
+ are constantly accessed, i.e., $#`timestamp` - #`old_timestamp`$ is small, and
+ have a fixed access pattern
to achieve a footprint that is significantly smaller than both #memw and #aligned.

Note: as a result of hard optimization, this chip can only be used for register accesses for which
+ $#`timestamp` - #`old_timestamp` in [1, 2^16]$, and
Comment thread
erik-3milabs marked this conversation as resolved.
+ $#`timestamp[0]` > #`old_timestamp[0]`$
If either of these rules does not apply to your access, you should fall back to using `MEMW_A`.

Note moreover that this chip does not guard against misaligned register access faults: to access register with a given `address`, one must provide $2 dot #`address`$ in the lookup.

== Columns
#let nr_variables = total_nr_variables(register_chip)
#let nr_columns = total_nr_instantiated_columns(register_chip, config)

The #reg chip is comprised of #nr_variables variables that are expressed using #nr_columns columns:
#render_chip_column_table(register_chip, config)

== Assumptions
The following range checks are assumed to be performed/enforced outside of this chip:
#render_chip_assumptions(register_chip, config)

== Constraints
Since most registers are frequently accessed, the difference between `timestamp` and `old_timestamp` is small most of the times.
Rather than storing their (nearly) identical upper limbs twice, it is instead assumed that
$#`old_timestamp[1]` = #`timestamp[1]`$; #aligned can be used for accesses where this is not the case.

Verifying that $#`timestamp` > #`old_timestamp`$ now simplifies to verifying that $#`timestamp[0]` - #`old_timestamp[0]` > 0$.
For most accesses, this value will be small enough to fit in a `Half`.
This chip thus enforces this by means of the following constraint:
#render_constraint_table(register_chip, config, groups: "diff")

With $#`old_timestamp`<#`timestamp`$ asserted, `old` is read from the register (@regw:c:read_old) and `val` is written back (@regw:c:write_val).
#render_constraint_table(register_chip, config, groups: "interactions")

This chip can either just write ($#`μ_write` = 1$), or both read and write ($#`μ_read` = 1$) in the same cycle.
It must be asserted that at most one of these two options is selected:
#render_constraint_table(register_chip, config, groups: "multiplicities")

Lastly, this chip contributes the following interactions to the logup:
#render_constraint_table(register_chip, config, groups: "output")

== Padding
The table can be padded to the next power of two with the following value assignments:
#render_chip_padding_table(register_chip, config)

= Future optimization ideas
- `MEMB` chip that does a one-byte write to remove old_timestamp from here (uncertain tradeoffs)
- Additional fast path for registers? (Always guaranteed same timestamp, alignment could be an assumption, always only two values)
- Adding `μ_sum`/`w2`/`w4`/`write8` multiplicities to the `IS_HALF` lookups may make some GKR things faster if there are known zeroes.
- For the register fast-path, one may upgrade the `IS_HALF` check to an `IS_B20` check for extended range at the cost of looking through a larger table.
141 changes: 141 additions & 0 deletions spec/src/memw_register.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
name = "MEMW_R"

# Variables

[[variables.input]]
name = "address"
type = "Byte"
desc = "address of the register being accessed"
pad = 0

[[variables.input]]
name = "timestamp"
type = "DWordWL"
desc = "timestamp at which the access takes place"
pad = 0

[[variables.input]]
name = "val"
type = "DWordWL"
desc = "value being written to this register"
pad = 0

[[variables.output]]
name = "old"
type = "DWordWL"
desc = "value of this register at `old_timestamp`."
pad = 0

[[variables.auxiliary]]
name = "old_timestamp_lo"
type = "Word"
desc = "the lower limb of `old_timestamp`"
pad = 0

[[variables.virtual]]
name = "old_timestamp"
type = "DWordWL"
desc = "timestamp at which this register was last accessed"
def = ["cast", ["arr", "old_timestamp_lo", ["idx", "timestamp", 1]], "DWordWL"]
Comment thread
erik-3milabs marked this conversation as resolved.
Comment thread
RobinJadoul marked this conversation as resolved.

[[variables.virtual]]
name = "μ_sum"
type = "Bit"
desc = ""
def = ["+", "μ_read", "μ_write"]

[[variables.multiplicity]]
name = "μ_read"
type = "Bit"
desc = "Whether we are performing a read (and hence return `out`)"
pad = 0

[[variables.multiplicity]]
name = "μ_write"
type = "Bit"
desc = "Whether we are performing a write (and hence not return `out`)"
pad = 0



# Assumptions

[[assumptions]]
desc = "`IS_WORD[val[i]]`"
iter = ["i", 0, 1]
ref = "regw:a:val"

[[assumptions]]
desc = "`IS_WORD[timestamp[i]]`"
iter = ["i", 0, 1]
ref = "regw:a:timestamp"

# Constraints

[[constraint_groups]]
name = "diff"

[[constraints.diff]]
kind = "interaction"
tag = "IS_HALF"
Comment thread
RobinJadoul marked this conversation as resolved.
input = [["-", ["idx", "timestamp", 0], ["idx", "old_timestamp", 0], 1]]
multiplicity = "μ_sum"
ref = "regw:c:diff"


[[constraint_groups]]
name = "multiplicities"

[[constraints.multiplicities]]
kind = "template"
tag = "IS_BIT"
input = ["μ_read"]
ref = "regw:c:μ_read_is_bit"

[[constraints.multiplicities]]
Comment thread
erik-3milabs marked this conversation as resolved.
kind = "template"
tag = "IS_BIT"
input = ["μ_write"]
ref = "regw:c:μ_write_is_bit"

[[constraints.multiplicities]]
kind = "template"
tag = "IS_BIT"
input = ["μ_sum"]
ref = "regw:c:μ_sum_is_bit"

[[constraint_groups]]
name = "interactions"

[[constraints.interactions]]
kind = "interaction"
tag = "memory"
input = [1, ["arr", ["cast", ["+", ["*", 2, "address"], "i"], "Word"], 0], "old_timestamp", ["idx", "old", "i"]]
iter = ["i", 0, 1]
multiplicity = "μ_sum"
Comment thread
erik-3milabs marked this conversation as resolved.
ref = "regw:c:read_old"

[[constraints.interactions]]
kind = "interaction"
tag = "memory"
input = [1, ["arr", ["cast", ["+", ["*", 2, "address"], "i"], "Word"], 0], "timestamp", ["idx", "val", "i"]]
iter = ["i", 0, 1]
multiplicity = ["-", "μ_sum"]
ref = "regw:c:write_val"


[[constraint_groups]]
name = "output"

[[constraints.output]]
kind = "interaction"
tag = "MEMW"
input = [1, ["arr", ["cast", ["*", 2, "address"], "Word"], 0], ["arr", ["idx", "val", 0], ["idx", "val", 1], 0, 0, 0, 0, 0, 0], "timestamp", 1, 0, 0]
output = ["arr", ["idx", "old", 0], ["idx", "old", 1], 0, 0, 0, 0, 0, 0]
multiplicity = ["-", "μ_read"]

[[constraints.output]]
kind = "interaction"
tag = "MEMW"
input = [1, ["arr", ["cast", ["*", 2, "address"], "Word"], 0], ["arr", ["idx", "val", 0], ["idx", "val", 1], 0, 0, 0, 0, 0, 0], "timestamp", 1, 0, 0]
multiplicity = ["-", "μ_write"]
Loading