Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions NOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

Last updated: 2026-08-08

## gen-verilog: tuple literal elements and destructure regs width-cast (Refs #1948)

- A tuple return `(1, true)` packed each element via gen_verilog_expr -- a bare literal is unsized and iverilog rejected it in the concatenation ("operand has indefinite width"). Elements are width-cast to their declared tuple-element type now
- Tuple-DESTRUCTURE binding regs (`(a,b,c,d) = call()`) were declared 64-bit each, so `{d,c,b,a} = <32-bit>` sliced the packed return wrongly. Each element reg is declared at its element width from the callee's return tuple type
- tri-net corpus: icarus 88 -> 94 (fpga_synthesis_report, hello, integration_tests, lite_crypto, mesh_routing, packet_loss_injection)
- FROZEN_HASH resealed

## gen-verilog: hex/bin literals in array-literal text normalized; more SV keywords (Refs #1948)

- The array-literal concat path works on raw source TEXT (not AST), so `0x38`/`0b..` element literals reached Verilog verbatim -- illegal (`8'(0x38)`). A text-level normalizer converts them to decimal
Expand Down
80 changes: 65 additions & 15 deletions bootstrap/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8397,25 +8397,47 @@ impl VerilogCodegen {
));
}
// Tuple-destructure binding `(a, b) = call()`: every
// element identifier needs a reg too (t27#1948).
// element identifier needs a reg at its ELEMENT width.
// A 64-bit default made `{d,c,b,a} = call()` slice the
// 32-bit packed return wrongly (t27#1948).
if target.kind == NodeKind::ExprTuple {
let elems: Vec<String> = target
// Element types from the callee's tuple return type.
let elem_types: Vec<String> = stmt
.children
.iter()
.filter(|e| {
e.kind == NodeKind::ExprIdentifier
&& !e.name.is_empty()
&& e.name != "_"
.get(1)
.filter(|c| c.kind == NodeKind::ExprCall)
.and_then(|c| self.fn_return_types.get(&c.name))
.map(|rt| {
let t = rt.trim();
if t.starts_with('(') && t.ends_with(')') {
t[1..t.len() - 1]
.split(',')
.map(|e| e.trim().to_string())
.collect()
} else {
Vec::new()
}
})
.map(|e| e.name.clone())
.collect();
for name in elems {
if declared.insert(name.clone()) {
.unwrap_or_default();
for (idx, e) in target.children.iter().enumerate() {
if e.kind != NodeKind::ExprIdentifier
|| e.name.is_empty()
|| e.name == "_"
{
continue;
}
if declared.insert(e.name.clone()) {
let (w, signed) = elem_types
.get(idx)
.map(|t| {
(self.packed_width(t).max(1), self.packed_signed(t))
})
.unwrap_or((64, false));
self.write_indent();
self.write_line(&format!(
"{} {}; // t27#1948 tuple binding",
reg_decl(64, false),
Self::verilog_safe_identifier(&name)
reg_decl(w, signed),
Self::verilog_safe_identifier(&e.name)
));
}
}
Expand Down Expand Up @@ -9836,14 +9858,42 @@ impl VerilogCodegen {
NodeKind::ExprTuple => {
// Tuple literal -> packed concatenation with element 0 in the
// LSB: `(e0, e1)` -> `{e1, e0}`. Matches the packed_width sum and
// the destructuring slice order.
// the destructuring slice order. Each element is WIDTH-CAST to
// its declared type: a bare literal (`return (1, true)`) is
// unsized in Verilog and iverilog rejects it in a concatenation
// ("operand has indefinite width", t27#1948).
let elem_types: Option<Vec<String>> = {
let t = self.current_fn_return_type.trim();
if t.starts_with('(') && t.ends_with(')') && t.contains(',') {
Some(
t[1..t.len() - 1]
.split(',')
.map(|e| e.trim().to_string())
.collect(),
)
} else {
None
}
};
self.write("{");
let last = node.children.len().saturating_sub(1);
for (i, child) in node.children.iter().enumerate().rev() {
if i != last {
self.write(", ");
}
self.gen_verilog_expr(child);
let cast = elem_types
.as_ref()
.filter(|ts| ts.len() == node.children.len())
.map(|ts| {
let ty = ts[i].trim();
(self.packed_width(ty).max(1), self.packed_signed(ty))
});
if let Some((w, signed)) = cast {
let v = self.emit_packed_scalar_value(child, w, signed);
self.write(&v);
} else {
self.gen_verilog_expr(child);
}
}
self.write("}");
}
Expand Down
2 changes: 1 addition & 1 deletion bootstrap/stage0/FROZEN_HASH
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1cb67e85167378d36862a7f1294e1798f9bb8b432e77cec5a30584bc6ad3bbf3
3efebe5d3fb17c2f4de99d613d2bcc9ea3a41de0c1f2a4baa86a251aa5965406
7 changes: 7 additions & 0 deletions docs/NOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

Last updated: 2026-08-08

## gen-verilog: tuple literal elements and destructure regs width-cast (Refs #1948)

- A tuple return `(1, true)` packed each element via gen_verilog_expr -- a bare literal is unsized and iverilog rejected it in the concatenation ("operand has indefinite width"). Elements are width-cast to their declared tuple-element type now
- Tuple-DESTRUCTURE binding regs (`(a,b,c,d) = call()`) were declared 64-bit each, so `{d,c,b,a} = <32-bit>` sliced the packed return wrongly. Each element reg is declared at its element width from the callee's return tuple type
- tri-net corpus: icarus 88 -> 94 (fpga_synthesis_report, hello, integration_tests, lite_crypto, mesh_routing, packet_loss_injection)
- FROZEN_HASH resealed

## gen-verilog: hex/bin literals in array-literal text normalized; more SV keywords (Refs #1948)

- The array-literal concat path works on raw source TEXT (not AST), so `0x38`/`0b..` element literals reached Verilog verbatim -- illegal (`8'(0x38)`). A text-level normalizer converts them to decimal
Expand Down
4 changes: 2 additions & 2 deletions specs/fpga/bridge.v
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,12 @@ module FPGA_Bridge (
reg __t27_ret;
__t27_ret = 1'b0;
if ((tail == size)) begin
buffer_read = {0, 0};
buffer_read = {32'd0, 8'd0};
__t27_ret = 1'b1;
end else begin
data = \buf [tail];
new_tail = ((tail + 1) % size);
buffer_read = {new_tail, data};
buffer_read = {32'(new_tail), 8'(data)};
__t27_ret = 1'b1;
end
end
Expand Down
Loading