-
Notifications
You must be signed in to change notification settings - Fork 0
spec: MUL chip
#122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
spec: MUL chip
#122
Changes from all commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
0223da9
spec: support "sum" expression
erik-3milabs f41d5f7
spec: introduce "QuadHL" type
erik-3milabs 14328e9
spec: introduce MUL chip
erik-3milabs ac02085
spec: Introduce QuadWL
erik-3milabs 025f409
spec: introduce B20[4]
erik-3milabs 214dc81
spec: simplify MUL to 26 columns
erik-3milabs 793c4ad
spec: Fix expr-sum bug
erik-3milabs 4375754
spec: simplify MUL to 22 columns
erik-3milabs 2f8fe0b
spec: improve MUL readability
erik-3milabs e469a5f
Merge branch 'spec/main' into spec/MUL
erik-3milabs 7caf14f
spec: MUL: fix indexing
erik-3milabs 971e3f5
spec: MUL: refactor
erik-3milabs 3ab33e8
spec: drop B20
erik-3milabs 274f6af
Merge branch 'spec/main' into spec/MUL
erik-3milabs 7be71ee
spec: MUL: fix raw_product relation
erik-3milabs 8eb8f3b
spec: MUL: fix IS_B19 check range
erik-3milabs ab2d752
spec: MUL: add missing res range check assumption
erik-3milabs 4413091
spec: MUL: remove superfluous/invalid constraints
erik-3milabs 05d6b36
spec: MUL: leverage SIGN template
erik-3milabs f42e7ca
spec: MUL: fix index mistake
erik-3milabs 46f74b1
spec: MUL: update description
erik-3milabs c6cb0ab
spec: permit non-constant exponents
erik-3milabs 3a20df6
spec: MUL: drop `limb_product`
erik-3milabs 38e5086
spec: MUL: minor tweaks
erik-3milabs 272006b
spec: MUL: bump headers
erik-3milabs f6797b9
spec: MUL: update description
erik-3milabs dbe8dc0
spec: MUL: update to IS_B20
erik-3milabs 54eb673
spec: MUL: remove 'eloquent'
erik-3milabs 0b7fc8a
Apply suggestions from code review
erik-3milabs 215f0e5
spec: MUL: define padding
erik-3milabs File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| #import "/book.typ": book-page | ||
| #import "/src.typ": load_config, load_chip | ||
| #import "/chip.typ": ( | ||
| render_chip_column_table, | ||
| total_nr_variables, | ||
| total_nr_instantiated_columns, | ||
| render_constraint_table, | ||
| render_chip_assumptions, | ||
| ) | ||
|
|
||
| #let config = load_config() | ||
| #let chip = load_chip("src/mul.toml", config) | ||
|
|
||
| #show: book-page.with(title: "MUL chip") | ||
|
|
||
| #let mul = raw(chip.name) | ||
|
|
||
| = #mul chip | ||
|
|
||
| == Columns | ||
| #let nr_variables = total_nr_variables(chip) | ||
| #let nr_columns = total_nr_instantiated_columns(chip, config) | ||
|
|
||
| The `MUL` chip is comprised of #nr_variables variables that are expressed using #nr_columns columns: | ||
| #render_chip_column_table(chip, config) | ||
|
|
||
| #let stackrel(top, bottom) = { | ||
| $mat(delim: #none, top; bottom)$ | ||
| } | ||
|
|
||
| == Assumptions | ||
| The following range checks are assumed to be performed/enforced outside of this chip: | ||
| #render_chip_assumptions(chip, config) | ||
|
|
||
| == Constraints | ||
|
|
||
| === Overview | ||
| When `lhs` and `rhs` are _unsigned_ integers, computing their product $mod 2^128$ comes down to evaluating | ||
| $ | ||
| (sum_(j=0)^3 2^(16j) dot #`lhs`_j) dot (sum_(i=0)^3 2^(16i) dot #`rhs`_i) mod 2^128. | ||
| $ | ||
| If `lhs` and `rhs` are signed instead, the computation remains nearly identical: | ||
| based on their signs, one must either zero or one-extend `lhs` and `rhs` --- forming `lhs_ext` and `rhs_ext` respectively --- and compute their product $mod 2^128$: | ||
| $ | ||
| (sum_(j=0)^7 2^(16j) dot #`lhs_ext`_j) dot (sum_(i=0)^7 2^(16i) dot #`rhs_ext`_i) mod 2^128. | ||
| $ | ||
| where `lhs_ext` and `rhs_ext` are treated as _unsigned_ integers. | ||
| Note that by setting the extension limbs of `lhs` and/or `rhs` to $0$ when the integer is (i) unsigned or (ii) signed and non-negative, this second formula still applies. | ||
| For the purposes of constraining the multiplication operation, we rewrite this formula as | ||
| #show math.equation: set block(breakable: true) | ||
| $ | ||
| &(sum_(j=0)^7 2^(16j) dot #`lhs_ext`_j) dot (sum_(i=0)^7 2^(16i) dot #`rhs_ext`_i) mod 2^128 \ | ||
| &equiv sum_(j=0)^7 sum_(i=0)^7 2^(16(i+j)) dot #`lhs_ext`_j dot #`rhs_ext`_i mod 2^128 \ | ||
| &stackrel(triangle, equiv) sum_(j=0)^7 sum_(i=0)^(7-j) 2^(16(i+j)) dot #`lhs_ext`_j dot #`rhs_ext`_i mod 2^128 \ | ||
| &stackrel(square, equiv) sum_(j=0)^7 sum_(i=j)^(7) 2^(16i) dot #`lhs_ext`_j dot #`rhs_ext`_(i-j) mod 2^128 \ | ||
| &stackrel(penta, equiv) sum_(i=0)^7 sum_(j=0)^(i) 2^(16i) dot #`lhs_ext`_j dot #`rhs_ext`_(i-j) mod 2^128 \ | ||
| &equiv sum_(i=0)^3 sum_(k=0)^1 sum_(j=0)^(2i+k) 2^(16(2i+k)) dot #`lhs_ext`_j dot #`rhs_ext`_(2i+k-j) mod 2^128 \ | ||
| &equiv sum_(i=0)^3 2^(32i) dot sum_(k=0)^1 2^(16k) dot sum_(j=0)^(2i+k) #`lhs_ext`_j dot #`rhs_ext`_(2i+k-j) mod 2^128 | ||
| $ | ||
| where at step | ||
| - $triangle$ we can ignore $i > 7-j$, since that makes $2^(16(i+j)) equiv 0 mod 2^128$, | ||
| - $square$ we rewrite the second summation such that $i$ iterates from $j$ to 7, rather than $0$ to $7-j$, and | ||
| - $penta$ we swap the sums. | ||
|
|
||
| We let `raw_product` capture the second summation in this last formula (see @mul:c:raw_product). | ||
| By construction, $#`raw_product`_i < 2^51$ for all $i in [0, 3]$, far exceeding the 32-bits that fit in a single `Word`-limb. | ||
| What remains then is to reduce each limb of `raw_product` $mod 2^32$, carrying the overflow of each limb to the next, constructing the output `res` in doing so. | ||
|
|
||
| This reduce-and-carry operation is constrained @mul:a:res and @mul:c:carry, combined with `carry`'s definition. | ||
| @mul:c:carry and `carry`'s definition enforce that | ||
| $ | ||
| forall i in [0, 3]: #`raw_product`_i + #`carry`_(i-1) - #`res`_i in { k dot 2^32 | k in [0, 2^20) } | ||
| $ | ||
|
RobinJadoul marked this conversation as resolved.
|
||
| with $#`carry`_(-1) = 0$ for simplicity. | ||
| In other words: $#`res`_i equiv #`raw_product`_i + #`carry`_(i-1) (mod 2^32)$. | ||
| With @mul:a:res forcing $#`res`_i < 2^32$, $#`res`_i$ can only assume one value: $#`raw_product`_i + #`carry`_(i-1) mod 2^32$. | ||
|
|
||
| *Note*: one may have observed that @mul:c:carry requires $#`carry`_i in [0, 2^20)$, while no limb of a valid carry value would ever exceed $2^19$. | ||
| This is indeed the case. | ||
| However, there is some slack in how tight one has to constrain the `carry` values. | ||
| In fact, in this situation it suffices to assert that $#`carry`_i < frac(p, 2^32, style: "skewed") approx 2^31$, where $p$ denotes the field's modulus. | ||
| Given that other chips also use 20-bit lookups, using `IS_B20` makes for a simpler design. | ||
|
|
||
| === Definitions | ||
| We constrain `lhs_is_negative` and `rhs_is_negative` according to their definition; `carry` is appropriately range checked. | ||
| #render_constraint_table(chip, config, groups: "def") | ||
|
|
||
| === Product | ||
| @mul:c:raw_product defines `raw_product` in terms of the (sign extended) input values `lhs` and `rhs`. | ||
| #render_constraint_table(chip, config, groups: "prod") | ||
|
|
||
| === Lookup | ||
| The #mul chip contributes the following to the lookup: | ||
| #render_constraint_table(chip, config, groups: "lookup") | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| name = "MUL" | ||
|
|
||
|
|
||
| # Input | ||
|
|
||
| [[variables.input]] | ||
| name = "lhs" | ||
| type = "DWordHL" | ||
| desc = "the left hand operator." | ||
| pad = 0 | ||
|
|
||
| [[variables.input]] | ||
| name = "lhs_signed" | ||
| type = "Bit" | ||
| desc = "whether to interpret `lhs` as a signed integer (1) or not (0)." | ||
| pad = 0 | ||
|
|
||
| [[variables.input]] | ||
| name = "rhs" | ||
| type = "DWordHL" | ||
| desc = "the right hand operator." | ||
| pad = 0 | ||
|
|
||
| [[variables.input]] | ||
| name = "rhs_signed" | ||
| type = "Bit" | ||
| desc = "whether to interpret `rhs` as a signed integer (1) or not (0)." | ||
| pad = 0 | ||
|
|
||
|
|
||
| # Output | ||
|
|
||
| [[variables.output]] | ||
| name = "res" | ||
| type = "QuadWL" | ||
| desc = "the (extended) multiplication result" | ||
| pad = 0 | ||
|
|
||
| # Auxiliary | ||
|
|
||
| [[variables.auxiliary]] | ||
| name = "lhs_is_negative" | ||
| type = "Bit" | ||
| desc = "whether `lhs` is negative (1) or not (0)" | ||
| pad = 0 | ||
|
|
||
| [[variables.auxiliary]] | ||
| name = "rhs_is_negative" | ||
| type = "Bit" | ||
| desc = "whether `rhs` is negative (1) or not (0)" | ||
| pad = 0 | ||
|
|
||
| [[variables.auxiliary]] | ||
| name = "raw_product" | ||
| type = ["B51", 4] | ||
| desc = "raw multiplication output" | ||
|
RobinJadoul marked this conversation as resolved.
|
||
| pad = 0 | ||
|
|
||
| # Virtual | ||
|
|
||
| [[variables.virtual]] | ||
| name = "lhs_ext" | ||
| type = ["Half", 8] | ||
| desc = "sign-extended value of `lhs`" | ||
| def = {idx="i", polys=[ | ||
| {range=[0, 3], poly=["idx", "lhs", "i"]}, | ||
| {range=[4, 7], poly=["*", 0xFFFF, "lhs_is_negative"]}, | ||
| ]} | ||
|
|
||
| [[variables.virtual]] | ||
| name = "rhs_ext" | ||
| type = ["Half", 8] | ||
| desc = "sign-extended value of `rhs`" | ||
| def = {idx="i", polys=[ | ||
| {range=[0, 3], poly=["idx", "rhs", "i"]}, | ||
| {range=[4, 7], poly=["*", 0xFFFF, "rhs_is_negative"]}, | ||
| ]} | ||
|
|
||
| [[variables.virtual]] | ||
| name = "carry" | ||
| type = ["B20", 4] | ||
| desc = "carry values" | ||
| def = {idx="i", polys=[ | ||
| {range=0, poly=["*", ["^", 2, -32], ["-", ["idx", "raw_product", 0], ["idx", "res", 0]]]}, | ||
| {range=[1, 3], poly=["*", ["^", 2, -32], ["-", ["+", ["idx", "raw_product", "i"], ["idx", "carry", ["-", "i", 1]]], ["idx", "res", "i"]]]}, | ||
| ]} | ||
|
|
||
| [[variables.virtual]] | ||
| name = "μ_sum" | ||
| type = "BaseField" | ||
| desc = "sum of multiplicies" | ||
| def = ["+", "μ_lo", "μ_hi"] | ||
|
|
||
| # Multiplicity | ||
|
|
||
| [[variables.multiplicity]] | ||
| name = "μ_lo" | ||
| type = "BaseField" | ||
| desc = "" | ||
| pad = 0 | ||
|
|
||
| [[variables.multiplicity]] | ||
| name = "μ_hi" | ||
| type = "BaseField" | ||
| desc = "" | ||
| pad = 0 | ||
|
|
||
| # Assumptions | ||
|
|
||
| [[assumptions]] | ||
| desc = "`IS_HALF[lhs[i]]`" | ||
| range = ["i", 0, 3] | ||
|
|
||
| [[assumptions]] | ||
| desc = "`IS_HALF[rhs[i]]`" | ||
| range = ["i", 0, 3] | ||
|
|
||
| [[assumptions]] | ||
| desc = "`IS_WORD[res[i]]`" | ||
| range = ["i", 0, 3] | ||
| ref = "mul:a:res" | ||
|
|
||
|
|
||
| # Constraints | ||
|
|
||
| [[constraint_groups]] | ||
| name = "def" | ||
|
|
||
| [[constraints.def]] | ||
| kind = "template" | ||
| tag = "SIGN" | ||
| input = [["idx", "lhs", 3], "lhs_signed"] | ||
| output = "lhs_is_negative" | ||
| ref = "mul:c:lhs_is_negative" | ||
|
|
||
| [[constraints.def]] | ||
| kind = "template" | ||
| tag = "SIGN" | ||
| input = [["idx", "rhs", 3], "rhs_signed"] | ||
| output = "rhs_is_negative" | ||
| ref = "mul:c:rhs_is_negative" | ||
|
|
||
| [[constraints.def]] | ||
| kind = "interaction" | ||
| tag = "IS_B20" | ||
| input = [["idx", "carry", "i"]] | ||
| range = ["i", 0, 3] | ||
| multiplicity = "μ_sum" | ||
| ref = "mul:c:carry" | ||
|
|
||
| [[constraint_groups]] | ||
| name = "prod" | ||
|
|
||
|
|
||
| [[constraints.prod]] | ||
| kind = "arith" | ||
| constraint = "$#`raw_product[i]` = sum_(#`k`=0)^1 2^(16k) sum_(#`j`=0)^(2i+k) #`lhs_ext[j]` dot #`rhs_ext[2i+k-j]`$" | ||
| poly = ["-", ["sum", ["=", "k", 0], "1", ["*", ["^", 2, ["*", 16, "k"]], ["sum", ["=", "j", 0], ["+", ["*", 2, "i"], "k"], ["*", ["idx", "lhs_ext", "j"], ["idx", "rhs_ext", ["-", ["+", ["*", 2, "i"], "k"], "j"]]]]]], ["idx", "raw_product", "i"]] | ||
| range = ["i", 0, 3] | ||
| ref = "mul:c:raw_product" | ||
|
|
||
| [[constraint_groups]] | ||
| name = "lookup" | ||
|
|
||
| [[constraints.lookup]] | ||
| kind = "interaction" | ||
| tag = "MUL" | ||
| input = ["lhs", "lhs_signed", "rhs", "rhs_signed", "0"] | ||
| output = ["idx", "res", "0:4"] | ||
| multiplicity = ["-", "μ_lo"] | ||
| ref = "mul:c:lookup_lo" | ||
|
|
||
| [[constraints.lookup]] | ||
| kind = "interaction" | ||
| tag = "MUL" | ||
| input = ["lhs", "lhs_signed", "rhs", "rhs_signed", "1"] | ||
| output = ["idx", "res", "4:8"] | ||
| multiplicity = ["-", "μ_hi"] | ||
| ref = "mul:c:lookup_hi" | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.