From 6441ffa163f8475cfe3fc1d5534f01aba4f49868 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Sun, 18 Oct 2026 11:04:24 +0700 Subject: [PATCH 1/4] wip(t27b): verilog -- pin attributes on ports (_LOC/_IOSTANDARD/_SLEW/_IN_TERM) Refs #10170 Co-Authored-By: Claude Opus 5.5 --- .../t27b/conformance/verilog_pin_attrs.t27 | 59 ++++++ specs/tri/t27b/verilog_emit.t27 | 175 +++++++++++++++++- specs/tri/t27b/verilog_plan.t27 | 66 +++++++ 3 files changed, 292 insertions(+), 8 deletions(-) create mode 100644 specs/tri/t27b/conformance/verilog_pin_attrs.t27 diff --git a/specs/tri/t27b/conformance/verilog_pin_attrs.t27 b/specs/tri/t27b/conformance/verilog_pin_attrs.t27 new file mode 100644 index 0000000000..7227353298 --- /dev/null +++ b/specs/tri/t27b/conformance/verilog_pin_attrs.t27 @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/tri/t27b/conformance/verilog_pin_attrs.t27 -- pin attributes for `t27b verilog` (gHashTag/t27#10170): +; a board top names the package pin and I/O standard of each port in its own spec, and t27b writes them as +; Verilog attributes on the port, which yosys keeps and nextpnr-xilinx reads. No constraint file is written +; by hand. +; The source form (specs/tri/t27b/verilog_plan.t27, PIN ATTRIBUTE): a const whose value is a string literal +; and whose name is a port's name, `_`, then LOC, IOSTANDARD, SLEW or IN_TERM. Here each kind of port carries +; one: the implicit clk, an on_clock parameter (btn), and a pub var (led). +; The pins are an example, not a board's pinout. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module T27bConformanceVerilogPinAttrs; + +const rst_n : bool = true; +const en : bool = true; + +const clk_LOC : str = "R4"; +const clk_IOSTANDARD : str = "LVCMOS33"; +const btn_LOC : str = "J21"; +const btn_IOSTANDARD : str = "LVCMOS33"; +const led_LOC : str = "B13"; +const led_IOSTANDARD : str = "LVCMOS33"; +const led_SLEW : str = "SLOW"; + +pub var led : bool = false; +var presses : u8 = 0; + +// Every var is read before it is written, so the test's order and the clock edge agree. +fn on_clock(btn: bool) { + if (btn) { + presses = presses + 1; + led = !led; + } +} + +test a_press_toggles_the_led { + led = false; + presses = 0; + on_clock(true); + assert led; + assert_eq(presses, 1); +} + +test no_press_leaves_the_led { + led = true; + presses = 3; + on_clock(false); + assert led; + assert_eq(presses, 3); +} + +test two_presses_bring_the_led_back { + led = false; + presses = 0; + on_clock(true); + on_clock(true); + assert !led; + assert_eq(presses, 2); +} diff --git a/specs/tri/t27b/verilog_emit.t27 b/specs/tri/t27b/verilog_emit.t27 index 3fdc9cca02..20ce3019d7 100644 --- a/specs/tri/t27b/verilog_emit.t27 +++ b/specs/tri/t27b/verilog_emit.t27 @@ -337,6 +337,88 @@ pub fn is_const_expr(nodes: []const u8, text: []const u8, meta: []const u8, ptex return fold_ok(nodes, text, meta, ptext, item, i, 0); } +// ---------------------------------------------------------------------------------------------------------- +// Pin attributes (#10170): a str const named `_` rides on that port as `(* KEY = "value" *)`. +// ---------------------------------------------------------------------------------------------------------- + +/// Is top-level node d a constant whose value is a string literal? +pub fn is_str_const(nodes: []const u8, text: []const u8, meta: []const u8, d: usize) -> bool { + if (kind(nodes, d) != K_CONST_DECL or is_mut(meta, d) or d + 1 >= end(nodes, d)) { return false; } + return kind(nodes, d + 1) == K_EXPR_LITERAL and field_eq(nodes, text, d + 1, F_KIND, "string"); +} + +/// The pin attribute of key k of the port named `port`: the top-level str const named `_`, or +/// NO_NODE. +pub fn pin_decl(nodes: []const u8, text: []const u8, meta: []const u8, k: usize, port: []const u8) -> i64 { + var d : usize = 1; + while (d < end(nodes, 0)) { + if (is_str_const(nodes, text, meta, d) and declares_pin(text, lo(nodes, d, F_NAME), hi(nodes, d, F_NAME), k, port)) { + const di : i64 = @intCast(d); + return di; + } + d = end(nodes, d); + } + return NO_NODE; +} + +/// Writes the head of the port line of the port named `port`: a newline and the indent, then each pin +/// attribute it has, `(* KEY = "value" *) `, in PIN_KEYS order. +pub fn put_pins(out: []u8, at: usize, nodes: []const u8, text: []const u8, meta: []const u8, port: []const u8) -> usize { + var q : usize = put_text(out, at, "\n "); + var k : usize = 0; + while (k < PIN_KEYS) { + const d : i64 = pin_decl(nodes, text, meta, k, port); + if (d != NO_NODE) { + const du : usize = @intCast(d); + q = put_text(out, q, "(* "); + q = put_text(out, q, pin_key(k)); + q = put_text(out, q, " = \""); + q = put_bytes(out, q, text, lo(nodes, du + 1, F_VALUE), hi(nodes, du + 1, F_VALUE)); + q = put_text(out, q, "\" *) "); + } + k = k + 1; + } + return q; +} + +/// Is `name` a port of the module, as ports() writes them: an implicit port of a clocked module that no net +/// replaces, an on_clock parameter bound to no instance, an input, an inout, an output? +pub fn is_port(nodes: []const u8, text: []const u8, meta: []const u8, ptext: []const u8, oc: i64, name: []const u8) -> bool { + if (oc != NO_NODE) { + if (same(name, "clk")) { return !has_net(nodes, text, meta, ptext, "clk"); } + if (same(name, "rst_n")) { return !has_net(nodes, text, meta, ptext, "rst_n"); } + if (same(name, "en")) { return !has_net(nodes, text, meta, ptext, "en"); } + const oci : usize = @intCast(oc); + const np : usize = param_count(ptext, params_lo(meta, oci), params_hi(meta, oci)); + var kx : usize = 0; + while (kx < np) { + const pa : usize = param_name_lo(meta, ptext, oci, kx); + const pb : usize = param_name_hi(meta, ptext, oci, kx); + if (pb - pa == name.len and has_at(ptext, pa, pb, name) and bound_instance(nodes, text, meta, ptext, pa, pb) == NO_NODE) { return true; } + kx = kx + 1; + } + } + const dp : bool = declares_ports(nodes, text, meta, ptext); + var c : usize = 1; + while (c < end(nodes, 0)) { + if (kind(nodes, c) == K_CONST_DECL and hi(nodes, c, F_NAME) - lo(nodes, c, F_NAME) == name.len and has_at(text, lo(nodes, c, F_NAME), hi(nodes, c, F_NAME), name)) { + const cl : u8 = item_class(nodes, text, meta, ptext, c); + if (cl == C_INPUT or cl == C_INOUT) { return true; } + return is_output(nodes, text, meta, ptext, c, dp); + } + c = end(nodes, c); + } + return false; +} + +/// Does the str const d, named as a pin attribute, name a port of the module? +pub fn pin_named(nodes: []const u8, text: []const u8, meta: []const u8, ptext: []const u8, oc: i64, d: usize) -> bool { + const ca : usize = lo(nodes, d, F_NAME); + const cb : usize = hi(nodes, d, F_NAME); + const key : str = pin_key(pin_kind(text, ca, cb)); + return is_port(nodes, text, meta, ptext, oc, text[ca .. cb - key.len - 1]); +} + // ---------------------------------------------------------------------------------------------------------- // Module items: the class of each top-level node, the clock, the ports. // ---------------------------------------------------------------------------------------------------------- @@ -1503,17 +1585,20 @@ pub fn ports(out: []u8, at: usize, nodes: []const u8, text: []const u8, meta: [] var first : bool = true; if (oc != NO_NODE) { if (!has_net(nodes, text, meta, ptext, "clk")) { - q = put_text(out, q, "\n input wire clk"); + q = put_pins(out, q, nodes, text, meta, "clk"); + q = put_text(out, q, "input wire clk"); first = false; } if (!has_net(nodes, text, meta, ptext, "rst_n")) { if (!first) { q = put_text(out, q, ","); } - q = put_text(out, q, "\n input wire rst_n"); + q = put_pins(out, q, nodes, text, meta, "rst_n"); + q = put_text(out, q, "input wire rst_n"); first = false; } if (!has_net(nodes, text, meta, ptext, "en")) { if (!first) { q = put_text(out, q, ","); } - q = put_text(out, q, "\n input wire en"); + q = put_pins(out, q, nodes, text, meta, "en"); + q = put_text(out, q, "input wire en"); first = false; } const oci : usize = @intCast(oc); @@ -1526,7 +1611,8 @@ pub fn ports(out: []u8, at: usize, nodes: []const u8, text: []const u8, meta: [] const pt : i64 = param_type(nodes, text, meta, ptext, oci, kx); if (!ty_scalar(pt)) { return fail(out, nodes, meta, text, oci, "on_clock parameter that is no integer or bool", pa, pb); } if (!first) { q = put_text(out, q, ","); } - q = put_text(out, q, "\n input wire "); + q = put_pins(out, q, nodes, text, meta, ptext[pa .. pb]); + q = put_text(out, q, "input wire "); q = put_range(out, q, pt); q = put_name(out, q, ptext, pa, pb); first = false; @@ -1541,7 +1627,8 @@ pub fn ports(out: []u8, at: usize, nodes: []const u8, text: []const u8, meta: [] const it : i64 = sym_type(nodes, text, meta, ptext, 0, sym_of(SYM_TOP, ci), 0); if (!ty_scalar(it)) { return fail(out, nodes, meta, text, ci, "input that is no integer or bool", lo(nodes, ci, F_NAME), hi(nodes, ci, F_NAME)); } if (!first) { q = put_text(out, q, ","); } - q = put_text(out, q, "\n input wire "); + q = put_pins(out, q, nodes, text, meta, text[lo(nodes, ci, F_NAME) .. hi(nodes, ci, F_NAME)]); + q = put_text(out, q, "input wire "); q = put_range(out, q, it); q = put_name(out, q, text, lo(nodes, ci, F_NAME), hi(nodes, ci, F_NAME)); first = false; @@ -1555,7 +1642,8 @@ pub fn ports(out: []u8, at: usize, nodes: []const u8, text: []const u8, meta: [] const iot : i64 = sym_type(nodes, text, meta, ptext, 0, sym_of(SYM_TOP, co), 0); if (!ty_scalar(iot)) { return fail(out, nodes, meta, text, co, "inout that is no integer or bool", lo(nodes, co, F_NAME), hi(nodes, co, F_NAME)); } if (!first) { q = put_text(out, q, ","); } - q = put_text(out, q, "\n inout wire "); + q = put_pins(out, q, nodes, text, meta, text[lo(nodes, co, F_NAME) .. hi(nodes, co, F_NAME)]); + q = put_text(out, q, "inout wire "); q = put_range(out, q, iot); q = put_name(out, q, text, lo(nodes, co, F_NAME), hi(nodes, co, F_NAME)); first = false; @@ -1569,10 +1657,11 @@ pub fn ports(out: []u8, at: usize, nodes: []const u8, text: []const u8, meta: [] const t : i64 = sym_type(nodes, text, meta, ptext, 0, sym_of(SYM_TOP, c), 0); if (!ty_scalar(t)) { return fail(out, nodes, meta, text, c, "port that is no integer or bool", lo(nodes, c, F_NAME), hi(nodes, c, F_NAME)); } if (!first) { q = put_text(out, q, ","); } + q = put_pins(out, q, nodes, text, meta, text[lo(nodes, c, F_NAME) .. hi(nodes, c, F_NAME)]); if (item_class(nodes, text, meta, ptext, c) == C_VAR) { - q = put_text(out, q, "\n output reg "); + q = put_text(out, q, "output reg "); } else { - q = put_text(out, q, "\n output wire "); + q = put_text(out, q, "output wire "); } q = put_range(out, q, t); q = put_name(out, q, text, lo(nodes, c, F_NAME), hi(nodes, c, F_NAME)); @@ -1874,6 +1963,9 @@ pub fn module_text(out: []u8, at: usize, nodes: []const u8, text: []const u8, me if (cl == C_CONST and is_period_name(text, lo(nodes, c, F_NAME), hi(nodes, c, F_NAME)) and !period_named(nodes, text, meta, ptext, c)) { return fail(out, nodes, meta, text, c, "clock period declared for no instance output", lo(nodes, c, F_NAME), hi(nodes, c, F_NAME)); } + if (is_str_const(nodes, text, meta, c) and pin_kind(text, lo(nodes, c, F_NAME), hi(nodes, c, F_NAME)) != PIN_KEYS and !pin_named(nodes, text, meta, ptext, oc, c)) { + return fail(out, nodes, meta, text, c, "pin attribute for no port of the module", lo(nodes, c, F_NAME), hi(nodes, c, F_NAME)); + } if (cl == C_VAR) { const t : i64 = sym_type(nodes, text, meta, ptext, 0, sym_of(SYM_TOP, c), 0); if (!ty_scalar(t)) { return fail(out, nodes, meta, text, c, "var that is no integer or bool", lo(nodes, c, F_NAME), hi(nodes, c, F_NAME)); } @@ -2665,3 +2757,70 @@ test a_lane_set_is_one_port_and_an_inout_is_handed_through { const n4 : usize = @intCast(0 - r4); assert holds(out[0 .. 4096], n4, "unsupported construct inout of another type than the port it is handed to `p` at line 12"); } + +test a_pin_attribute_rides_on_the_port_it_names { + // module P { const x : bool = undefined; const x_LOC : str = "R4"; const x_IOSTANDARD : str = "LVCMOS33"; + // pub const y : bool = x; const y_LOC : str = "T4"; } + // text: P 0..1, x 1..2, bool 2..6, undefined 6..15, x_LOC 15..20, str 20..23, R4 23..25, string 25..31, + // x_IOSTANDARD 31..43, LVCMOS33 43..51, y 51..52, y_LOC 52..57, T4 57..59, z_LOC 59..64 + const tx : str = "Pxboolundefinedx_LOCstrR4stringx_IOSTANDARDLVCMOS33yy_LOCT4z_LOC"; + var nb : [792]u8 = undefined; + tnode(nb[0 .. 792], 0, K_MODULE, 11, 0, 1); + tnode(nb[0 .. 792], 1, K_CONST_DECL, 3, 1, 2); + vset(nb[0 .. 792], 1, F_TYPE, 2, 6); + tnode(nb[0 .. 792], 2, K_EXPR_IDENTIFIER, 3, 6, 15); + tnode(nb[0 .. 792], 3, K_CONST_DECL, 5, 15, 20); + vset(nb[0 .. 792], 3, F_TYPE, 20, 23); + tnode(nb[0 .. 792], 4, K_EXPR_LITERAL, 5, 0, 0); + vset(nb[0 .. 792], 4, F_VALUE, 23, 25); + vset(nb[0 .. 792], 4, F_KIND, 25, 31); + tnode(nb[0 .. 792], 5, K_CONST_DECL, 7, 31, 43); + vset(nb[0 .. 792], 5, F_TYPE, 20, 23); + tnode(nb[0 .. 792], 6, K_EXPR_LITERAL, 7, 0, 0); + vset(nb[0 .. 792], 6, F_VALUE, 43, 51); + vset(nb[0 .. 792], 6, F_KIND, 25, 31); + tnode(nb[0 .. 792], 7, K_CONST_DECL, 9, 51, 52); + vset(nb[0 .. 792], 7, F_TYPE, 2, 6); + tnode(nb[0 .. 792], 8, K_EXPR_IDENTIFIER, 9, 1, 2); + tnode(nb[0 .. 792], 9, K_CONST_DECL, 11, 52, 57); + vset(nb[0 .. 792], 9, F_TYPE, 20, 23); + tnode(nb[0 .. 792], 10, K_EXPR_LITERAL, 11, 0, 0); + vset(nb[0 .. 792], 10, F_VALUE, 57, 59); + vset(nb[0 .. 792], 10, F_KIND, 25, 31); + var mb : [176]u8 = undefined; + var i : usize = 0; + while (i < 11) { + tmeta(mb[0 .. 176], i, 0, i + 1); + i = i + 1; + } + tmeta(mb[0 .. 176], 7, FLAG_PUB, 8); + var wk : [100]u8 = undefined; + i = 0; + while (i < 100) { + wk[i] = 0; + i = i + 1; + } + // x and y are ports, and a pin attribute is none + assert is_str_const(nb[0 .. 792], tx, mb[0 .. 176], 3) and !is_str_const(nb[0 .. 792], tx, mb[0 .. 176], 1); + assert is_port(nb[0 .. 792], tx, mb[0 .. 176], "", NO_NODE, "x") and is_port(nb[0 .. 792], tx, mb[0 .. 176], "", NO_NODE, "y"); + assert !is_port(nb[0 .. 792], tx, mb[0 .. 176], "", NO_NODE, "z") and !is_port(nb[0 .. 792], tx, mb[0 .. 176], "", NO_NODE, "x_LOC"); + assert pin_decl(nb[0 .. 792], tx, mb[0 .. 176], 1, "x") == 5 and pin_decl(nb[0 .. 792], tx, mb[0 .. 176], 1, "y") == NO_NODE; + var out : [4096]u8 = undefined; + const r : i64 = emit(nb[0 .. 792], tx, mb[0 .. 176], "", wk[0 .. 100], out[0 .. 4096]); + assert r > 0; + const n : usize = @intCast(r); + // each attribute in PIN_KEYS order, before the port it names, and nowhere else + assert holds(out[0 .. 4096], n, "\n (* LOC = \"R4\" *) (* IOSTANDARD = \"LVCMOS33\" *) input wire x,\n (* LOC = \"T4\" *) output wire y\n);\n"); + assert !holds(out[0 .. 4096], n, "x_LOC"); + // a pin attribute that names no port is refused by name + vset(nb[0 .. 792], 9, F_NAME, 59, 64); + i = 0; + while (i < 100) { + wk[i] = 0; + i = i + 1; + } + const r2 : i64 = emit(nb[0 .. 792], tx, mb[0 .. 176], "", wk[0 .. 100], out[0 .. 4096]); + assert r2 < 0; + const n2 : usize = @intCast(0 - r2); + assert holds(out[0 .. 4096], n2, "unsupported construct pin attribute for no port of the module `z_LOC` at line 10"); +} diff --git a/specs/tri/t27b/verilog_plan.t27 b/specs/tri/t27b/verilog_plan.t27 index 0363fa1fd1..9afc084086 100644 --- a/specs/tri/t27b/verilog_plan.t27 +++ b/specs/tri/t27b/verilog_plan.t27 @@ -51,6 +51,13 @@ ; so a divided clock is checked at the rate it runs at and no constraint file is written by hand. A ; declaration that names no instance output, or whose value is 0, is refused by name: a constraint that ; constrains nothing would leave its clock at the global target without a word. +; - A PIN ATTRIBUTE is a module-level `const` whose value is a string literal and whose name is a port's +; name, `_`, then one of PIN_KEYS (`LOC`, `IOSTANDARD`, `SLEW`, `IN_TERM`): `const ddram_a_0_LOC : str = +; "AA4";` (gHashTag/t27#10170). It is written as the attribute `(* LOC = "AA4" *)` before that port, which +; yosys keeps on the port and nextpnr-xilinx reads as the pin's placement and I/O standard, so the pins +; of a board top live in its spec and no constraint file is written by hand. One attribute per key and +; port, written in PIN_KEYS order; a pin attribute that names no port of the module is refused by name, +; since a misspelt pin would otherwise leave that port to the placer without a word. ; - A NAME THAT IS A T27 KEYWORD (#10061). The lexer reserves `in`, `for`, `test` and the rest of ; T27_KEYWORDS, so `const in : u8 = undefined;` does not parse, and a fixed interface that needs a port ; `in` (CVDP 8x3_priority_encoder) could not be declared. Such a name is spelled with one trailing `_`: @@ -671,6 +678,46 @@ pub fn declares_period(text: []const u8, ca: usize, cb: usize, ia: usize, ib: us return binds(text, ca, cb - PERIOD_SUFFIX.len, text, ia, ib, fa, fb); } +/// The number of pin attribute keys (#10170). +pub const PIN_KEYS : usize = 4; + +/// Pin attribute key k, 0 .. PIN_KEYS: the Verilog attribute a const named `_` writes. +pub fn pin_key(k: usize) -> str { + if (k == 0) { return "LOC"; } + if (k == 1) { return "IOSTANDARD"; } + if (k == 2) { return "SLEW"; } + return "IN_TERM"; +} + +/// Is the const name text[ca .. cb] a pin attribute of key k: a name of at least one byte, `_`, then +/// pin_key(k)? +pub fn is_pin_name(text: []const u8, ca: usize, cb: usize, k: usize) -> bool { + const key : str = pin_key(k); + const s : usize = key.len; + if (cb < ca + s + 2) { return false; } + if (text[cb - s - 1] != 95) { return false; } + return same(text[cb - s .. cb], key); +} + +/// The key of the pin attribute named text[ca .. cb], PIN_KEYS when it is none. +pub fn pin_kind(text: []const u8, ca: usize, cb: usize) -> usize { + var k : usize = 0; + while (k < PIN_KEYS) { + if (is_pin_name(text, ca, cb, k)) { return k; } + k = k + 1; + } + return PIN_KEYS; +} + +/// Is the const name text[ca .. cb] the pin attribute of key k of the port named `port`: exactly `port`, +/// `_`, then pin_key(k)? +pub fn declares_pin(text: []const u8, ca: usize, cb: usize, k: usize, port: []const u8) -> bool { + if (!is_pin_name(text, ca, cb, k)) { return false; } + const key : str = pin_key(k); + if (cb - ca != port.len + 1 + key.len) { return false; } + return has_at(text, ca, cb, port); +} + // ---------------------------------------------------------------------------------------------------------- // Loops. A `while` in the module lowers to `for (k = 0; k < bound; k = k + 1) if (cond) body`, and it is the // same loop exactly when the body cannot run more than `bound` times. trip_bound reads that bound from the @@ -1009,6 +1056,25 @@ test a_clock_period_is_declared_by_the_name_of_an_instance_output { assert binds(s, 0, 6, s, 16, 20, 20, 21) and !declares_period(s, 0, 6, 16, 20, 20, 21); } +test a_pin_attribute_is_named_by_its_port_and_key { + assert same(pin_key(0), "LOC") and same(pin_key(1), "IOSTANDARD") and same(pin_key(2), "SLEW") and same(pin_key(3), "IN_TERM"); + // "a_0_LOC" 0..7, "dq_IN_TERM" 7..17, "_LOC" 17..21, "xLOC" 21..25, "x_IOSTANDARD" 25..37 + const s : str = "a_0_LOCdq_IN_TERM_LOCxLOCx_IOSTANDARD"; + assert is_pin_name(s, 0, 7, 0) and pin_kind(s, 0, 7) == 0; + assert declares_pin(s, 0, 7, 0, "a_0"); + assert pin_kind(s, 7, 17) == 3 and declares_pin(s, 7, 17, 3, "dq"); + assert pin_kind(s, 25, 37) == 1 and declares_pin(s, 25, 37, 1, "x"); + // another port, a port that only starts the name, another key: no attribute of that port + assert !declares_pin(s, 0, 7, 0, "a_1"); + assert !declares_pin(s, 0, 7, 0, "a"); + assert !declares_pin(s, 0, 7, 0, "a_0_"); + assert !declares_pin(s, 0, 7, 1, "a_0"); + // a key with no port name before it, or no `_` between them, is no pin attribute + assert pin_kind(s, 17, 21) == PIN_KEYS and pin_kind(s, 21, 25) == PIN_KEYS; + // a name cut inside its key is none either + assert pin_kind(s, 0, 6) == PIN_KEYS; +} + test meta_records_and_parameter_lines { var m : [32]u8 = undefined; vput32(m[0 .. 32], 0, 3); From c44d1bb24eda5d02e3d6fc9be407e4499f9e8b62 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Sun, 18 Oct 2026 11:09:47 +0700 Subject: [PATCH 2/4] wip(t27b): pin attributes -- a non-string literal and an empty name are no pin or port Refs #10199 Co-Authored-By: Claude Opus 5.5 --- specs/tri/t27b/verilog_emit.t27 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/specs/tri/t27b/verilog_emit.t27 b/specs/tri/t27b/verilog_emit.t27 index 20ce3019d7..8bc0b66b95 100644 --- a/specs/tri/t27b/verilog_emit.t27 +++ b/specs/tri/t27b/verilog_emit.t27 @@ -2804,6 +2804,12 @@ test a_pin_attribute_rides_on_the_port_it_names { assert is_str_const(nb[0 .. 792], tx, mb[0 .. 176], 3) and !is_str_const(nb[0 .. 792], tx, mb[0 .. 176], 1); assert is_port(nb[0 .. 792], tx, mb[0 .. 176], "", NO_NODE, "x") and is_port(nb[0 .. 792], tx, mb[0 .. 176], "", NO_NODE, "y"); assert !is_port(nb[0 .. 792], tx, mb[0 .. 176], "", NO_NODE, "z") and !is_port(nb[0 .. 792], tx, mb[0 .. 176], "", NO_NODE, "x_LOC"); + // a name that only starts a port's name is no port + assert !is_port(nb[0 .. 792], tx, mb[0 .. 176], "", NO_NODE, ""); + // a const whose literal is no string is no pin attribute, whatever its name + vset(nb[0 .. 792], 4, F_KIND, 2, 6); + assert !is_str_const(nb[0 .. 792], tx, mb[0 .. 176], 3); + vset(nb[0 .. 792], 4, F_KIND, 25, 31); assert pin_decl(nb[0 .. 792], tx, mb[0 .. 176], 1, "x") == 5 and pin_decl(nb[0 .. 792], tx, mb[0 .. 176], 1, "y") == NO_NODE; var out : [4096]u8 = undefined; const r : i64 = emit(nb[0 .. 792], tx, mb[0 .. 176], "", wk[0 .. 100], out[0 .. 4096]); From 25b9ead93c9b30aa42f34d3bb12bbe36447fac8e Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Sun, 18 Oct 2026 11:18:04 +0700 Subject: [PATCH 3/4] wip(t27b): pin attributes -- a key with no separator before it is no pin attribute Refs #10199 Co-Authored-By: Claude Opus 5.5 --- specs/tri/t27b/verilog_plan.t27 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/tri/t27b/verilog_plan.t27 b/specs/tri/t27b/verilog_plan.t27 index 9afc084086..158bb2d07a 100644 --- a/specs/tri/t27b/verilog_plan.t27 +++ b/specs/tri/t27b/verilog_plan.t27 @@ -1058,19 +1058,19 @@ test a_clock_period_is_declared_by_the_name_of_an_instance_output { test a_pin_attribute_is_named_by_its_port_and_key { assert same(pin_key(0), "LOC") and same(pin_key(1), "IOSTANDARD") and same(pin_key(2), "SLEW") and same(pin_key(3), "IN_TERM"); - // "a_0_LOC" 0..7, "dq_IN_TERM" 7..17, "_LOC" 17..21, "xLOC" 21..25, "x_IOSTANDARD" 25..37 - const s : str = "a_0_LOCdq_IN_TERM_LOCxLOCx_IOSTANDARD"; + // "a_0_LOC" 0..7, "dq_IN_TERM" 7..17, "_LOC" 17..21, "xyLOC" 21..26, "x_IOSTANDARD" 26..38 + const s : str = "a_0_LOCdq_IN_TERM_LOCxyLOCx_IOSTANDARD"; assert is_pin_name(s, 0, 7, 0) and pin_kind(s, 0, 7) == 0; assert declares_pin(s, 0, 7, 0, "a_0"); assert pin_kind(s, 7, 17) == 3 and declares_pin(s, 7, 17, 3, "dq"); - assert pin_kind(s, 25, 37) == 1 and declares_pin(s, 25, 37, 1, "x"); + assert pin_kind(s, 26, 38) == 1 and declares_pin(s, 26, 38, 1, "x"); // another port, a port that only starts the name, another key: no attribute of that port assert !declares_pin(s, 0, 7, 0, "a_1"); assert !declares_pin(s, 0, 7, 0, "a"); assert !declares_pin(s, 0, 7, 0, "a_0_"); assert !declares_pin(s, 0, 7, 1, "a_0"); // a key with no port name before it, or no `_` between them, is no pin attribute - assert pin_kind(s, 17, 21) == PIN_KEYS and pin_kind(s, 21, 25) == PIN_KEYS; + assert pin_kind(s, 17, 21) == PIN_KEYS and pin_kind(s, 21, 26) == PIN_KEYS; // a name cut inside its key is none either assert pin_kind(s, 0, 6) == PIN_KEYS; } From a4289b9efcbabe4c495d468b2e0397f4e15dd55a Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Sun, 18 Oct 2026 11:24:20 +0700 Subject: [PATCH 4/4] t27b verilog: regenerate plan and emit, reseal, ledger row for verilog_pin_attrs Refs #10199 Co-Authored-By: Claude Opus 5.5 --- ...rmance_T27bConformanceVerilogPinAttrs.json | 28 +++ .trinity/seals/t27b_T27bVerilogEmit.json | 18 +- .trinity/seals/t27b_T27bVerilogPlan.json | 16 +- docs/reports/t27b_expectations.json | 1 + gen/rust/tri/t27b/verilog_emit.rs | 159 +++++++++++++++++- gen/rust/tri/t27b/verilog_plan.rs | 51 +++++- 6 files changed, 246 insertions(+), 27 deletions(-) create mode 100644 .trinity/seals/conformance_T27bConformanceVerilogPinAttrs.json diff --git a/.trinity/seals/conformance_T27bConformanceVerilogPinAttrs.json b/.trinity/seals/conformance_T27bConformanceVerilogPinAttrs.json new file mode 100644 index 0000000000..156c613e3f --- /dev/null +++ b/.trinity/seals/conformance_T27bConformanceVerilogPinAttrs.json @@ -0,0 +1,28 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+a86b691b1", + "closure": [], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:3c5e7789036c884c6db63087b6826ba7d0fceebf565a1117366e444297f96403", + "gen_hash_rust": "sha256:23a28b4b1a5f4f7a0f0f9a90d5da3153a8d378767d7ea29ad47c2bd85fa234d8", + "gen_hash_verilog": "sha256:f769e899129496c1fde0c54f94000d19299033e3554f86c0c8073cc3f7bda02c", + "gen_hash_zig": "sha256:7313bb5a528c122c2a2b8d745cbb7c41c64b72b3585e6d92a7e748de4e359202", + "module": "T27bConformanceVerilogPinAttrs", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T04:19:01Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:4319dbdf900eafa8d0bfc95fe7aaf87c0455152f7d92d4db5661143be11da83e", + "spec_path": "specs/tri/t27b/conformance/verilog_pin_attrs.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 3, + "total": 3, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/t27b_T27bVerilogEmit.json b/.trinity/seals/t27b_T27bVerilogEmit.json index f66efd15d9..d3f42d5952 100644 --- a/.trinity/seals/t27b_T27bVerilogEmit.json +++ b/.trinity/seals/t27b_T27bVerilogEmit.json @@ -11,27 +11,27 @@ }, { "spec": "specs/tri/t27b/verilog_plan.t27", - "spec_hash": "sha256:e83cfc469873ff2317ab1ec4afcf775e576028e572adc232052dbfc46ee26507" + "spec_hash": "sha256:c71ad7c23e4e17dc1289fad6c6e11b118d85bcff6002d5165263774200ea6bb0" } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:3b1d0af3d223ece81d76f46e7a17a4b7be4870207c15fb7b8fa0e277a8735f82", - "gen_hash_rust": "sha256:1fe1d2a06eef20f16e92c0d521185806db1cbb60e5779335e0f37eb6a6689f7f", - "gen_hash_verilog": "sha256:587cc7d8f59751c39692c49ffe252663cfe9a204f24725325f92d628cf1aed3b", - "gen_hash_zig": "sha256:09c7cbba8fe1c04837f3561924bffefff20a77e1d1ee03a36fe900da1d52f3ff", + "gen_hash_c": "sha256:584f47df1d6ade6798722fa33b3df656a95667420215ebe815ddb7421ca66444", + "gen_hash_rust": "sha256:49081e7dffcef5342fc40c42c2760db4a29e86a6b88badcccd5dad66fdfba811", + "gen_hash_verilog": "sha256:1a385b65b99093e2addc4adf6f382d110186574b7ad1fcf83a0c41e7e13d304e", + "gen_hash_zig": "sha256:404bcae3892f3be78ba2755b203d92d0b582de42653dc9bec5d90399c763704d", "module": "T27bVerilogEmit", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-18T03:40:42Z", + "sealed_at": "2026-10-18T04:19:01Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:4fadad47612defd88ad1e3e3160aac802ac5f9e0a122fc33f058105923a0c903", + "spec_hash": "sha256:c34cd680a2f832cf513fb55a3b19956d16649604e605b1f01eaa3cf4294380df", "spec_path": "specs/tri/t27b/verilog_emit.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 9, - "total": 9, + "passed": 10, + "total": 10, "vacuous": 0 }, "toolchain": { diff --git a/.trinity/seals/t27b_T27bVerilogPlan.json b/.trinity/seals/t27b_T27bVerilogPlan.json index 5ba03ea3b7..34ebff5429 100644 --- a/.trinity/seals/t27b_T27bVerilogPlan.json +++ b/.trinity/seals/t27b_T27bVerilogPlan.json @@ -7,23 +7,23 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:5a6a8b00e27811871acf66e81b547b3ba1e7a81b2388f0faae2487575c151f01", - "gen_hash_rust": "sha256:828a636e0d7dd3c1814667902d86436433ab6cc6e58e4ed163ee1b4a431b4c37", - "gen_hash_verilog": "sha256:b053716c93b085c72fbefadead49d3371ec4c11f3fa8cc9affc711770ad6174f", - "gen_hash_zig": "sha256:29602f8d57a5df9a641adb65e9b96f119bf74b018f97d57e1d621e6f68ec1c14", + "gen_hash_c": "sha256:f8813a08094b5fc36dc1fd089229cad13108eaf2b515a010ec213dd554052bed", + "gen_hash_rust": "sha256:e4860aa2da6eb2159bc505ac625c4880fba5fdb25ffe8953f2280c23be67d135", + "gen_hash_verilog": "sha256:71f948e58983e4e5390acbef7144e44371a76bd7b9cd55294df1d99865d92e7a", + "gen_hash_zig": "sha256:c59ac64c3064eaf401315d43ece71253b0917172f8337efbf01fd59a4f6ddacf", "module": "T27bVerilogPlan", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-18T03:40:41Z", + "sealed_at": "2026-10-18T04:18:58Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:e83cfc469873ff2317ab1ec4afcf775e576028e572adc232052dbfc46ee26507", + "spec_hash": "sha256:c71ad7c23e4e17dc1289fad6c6e11b118d85bcff6002d5165263774200ea6bb0", "spec_path": "specs/tri/t27b/verilog_plan.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 17, - "total": 17, + "passed": 18, + "total": 18, "vacuous": 0 }, "toolchain": { diff --git a/docs/reports/t27b_expectations.json b/docs/reports/t27b_expectations.json index 490ff328f9..0d89631fe5 100644 --- a/docs/reports/t27b_expectations.json +++ b/docs/reports/t27b_expectations.json @@ -1380,6 +1380,7 @@ {"path": "specs/tri/t27b/conformance/value_params.t27", "t27b": "pass"}, {"path": "specs/tri/t27b/conformance/verilog_lib.t27", "t27b": "pass"}, {"path": "specs/tri/t27b/conformance/verilog_odd_cast.t27", "t27b": "pass"}, + {"path": "specs/tri/t27b/conformance/verilog_pin_attrs.t27", "t27b": "pass"}, {"path": "specs/tri/t27b/conformance/verilog_prim.t27", "t27b": "pass"}, {"path": "specs/tri/t27b/conformance/verilog_use.t27", "t27b": "pass"}, {"path": "specs/tri/t27b/conformance/verilog_wide_inout.t27", "t27b": "pass"}, diff --git a/gen/rust/tri/t27b/verilog_emit.rs b/gen/rust/tri/t27b/verilog_emit.rs index 0956b24566..149b04d022 100644 --- a/gen/rust/tri/t27b/verilog_emit.rs +++ b/gen/rust/tri/t27b/verilog_emit.rs @@ -1,6 +1,6 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 9 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 10 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. @@ -382,6 +382,88 @@ pub fn is_const_expr(nodes: &[u8], text: &[u8], meta: &[u8], ptext: &[u8], item: return fold_ok(nodes, text, meta, ptext, item, i, 0); } +pub fn is_str_const(nodes: &[u8], text: &[u8], meta: &[u8], d: usize) -> bool { + if (((kind(nodes, d) != K_CONST_DECL) || is_mut(meta, d)) || ((d + 1) >= end(nodes, d))) { + return false; + } + return ((kind(nodes, (d + 1)) == K_EXPR_LITERAL) && field_eq(nodes, text, (d + 1), F_KIND, "string")); +} + +pub fn pin_decl(nodes: &[u8], text: &[u8], meta: &[u8], k: usize, port: &[u8]) -> i64 { + let mut d: usize = 1; + while (d < end(nodes, 0)) { + if (is_str_const(nodes, text, meta, d) && declares_pin(text, lo(nodes, d, F_NAME), hi(nodes, d, F_NAME), k, port)) { + let di: i64 = (d as _); + return di; + } + d = end(nodes, d); + } + return NO_NODE; +} + +pub fn put_pins(out: &mut [u8], at: usize, nodes: &[u8], text: &[u8], meta: &[u8], port: &[u8]) -> usize { + let mut q: usize = put_text(out, at, "\n "); + let mut k: usize = 0; + while (k < PIN_KEYS) { + let d: i64 = pin_decl(nodes, text, meta, k, port); + if (d != NO_NODE) { + let du: usize = (d as _); + q = put_text(out, q, "(* "); + q = put_text(out, q, pin_key(k)); + q = put_text(out, q, " = \""); + q = put_bytes(out, q, text, lo(nodes, (du + 1), F_VALUE), hi(nodes, (du + 1), F_VALUE)); + q = put_text(out, q, "\" *) "); + } + k = (k + 1); + } + return q; +} + +pub fn is_port(nodes: &[u8], text: &[u8], meta: &[u8], ptext: &[u8], oc: i64, name: &[u8]) -> bool { + if (oc != NO_NODE) { + if same(name, "clk") { + return !(has_net(nodes, text, meta, ptext, "clk")); + } + if same(name, "rst_n") { + return !(has_net(nodes, text, meta, ptext, "rst_n")); + } + if same(name, "en") { + return !(has_net(nodes, text, meta, ptext, "en")); + } + let oci: usize = (oc as _); + let np: usize = param_count(ptext, params_lo(meta, oci), params_hi(meta, oci)); + let mut kx: usize = 0; + while (kx < np) { + let pa: usize = param_name_lo(meta, ptext, oci, kx); + let pb: usize = param_name_hi(meta, ptext, oci, kx); + if ((((pb - pa) == name.len()) && has_at(ptext, pa, pb, name)) && (bound_instance(nodes, text, meta, ptext, pa, pb) == NO_NODE)) { + return true; + } + kx = (kx + 1); + } + } + let dp: bool = declares_ports(nodes, text, meta, ptext); + let mut c: usize = 1; + while (c < end(nodes, 0)) { + if (((kind(nodes, c) == K_CONST_DECL) && ((hi(nodes, c, F_NAME) - lo(nodes, c, F_NAME)) == name.len())) && has_at(text, lo(nodes, c, F_NAME), hi(nodes, c, F_NAME), name)) { + let cl: u8 = item_class(nodes, text, meta, ptext, c); + if ((cl == C_INPUT) || (cl == C_INOUT)) { + return true; + } + return is_output(nodes, text, meta, ptext, c, dp); + } + c = end(nodes, c); + } + return false; +} + +pub fn pin_named(nodes: &[u8], text: &[u8], meta: &[u8], ptext: &[u8], oc: i64, d: usize) -> bool { + let ca: usize = lo(nodes, d, F_NAME); + let cb: usize = hi(nodes, d, F_NAME); + let key: &'static str = pin_key(pin_kind(text, ca, cb)); + return is_port(nodes, text, meta, ptext, oc, &text[(ca) as usize..(((cb - key.len()) - 1)) as usize]); +} + pub fn item_class(nodes: &[u8], text: &[u8], meta: &[u8], ptext: &[u8], c: usize) -> u8 { let k: u8 = kind(nodes, c); if (k == K_FN_DECL) { @@ -1699,21 +1781,24 @@ pub fn ports(out: &mut [u8], at: usize, nodes: &[u8], text: &[u8], meta: &[u8], let mut first: bool = true; if (oc != NO_NODE) { if !(has_net(nodes, text, meta, ptext, "clk")) { - q = put_text(out, q, "\n input wire clk"); + q = put_pins(out, q, nodes, text, meta, b"clk"); + q = put_text(out, q, "input wire clk"); first = false; } if !(has_net(nodes, text, meta, ptext, "rst_n")) { if !(first) { q = put_text(out, q, ","); } - q = put_text(out, q, "\n input wire rst_n"); + q = put_pins(out, q, nodes, text, meta, b"rst_n"); + q = put_text(out, q, "input wire rst_n"); first = false; } if !(has_net(nodes, text, meta, ptext, "en")) { if !(first) { q = put_text(out, q, ","); } - q = put_text(out, q, "\n input wire en"); + q = put_pins(out, q, nodes, text, meta, b"en"); + q = put_text(out, q, "input wire en"); first = false; } let oci: usize = (oc as _); @@ -1730,7 +1815,8 @@ pub fn ports(out: &mut [u8], at: usize, nodes: &[u8], text: &[u8], meta: &[u8], if !(first) { q = put_text(out, q, ","); } - q = put_text(out, q, "\n input wire "); + q = put_pins(out, q, nodes, text, meta, &ptext[(pa) as usize..(pb) as usize]); + q = put_text(out, q, "input wire "); q = put_range(out, q, pt); q = put_name(out, q, ptext, pa, pb); first = false; @@ -1748,7 +1834,8 @@ pub fn ports(out: &mut [u8], at: usize, nodes: &[u8], text: &[u8], meta: &[u8], if !(first) { q = put_text(out, q, ","); } - q = put_text(out, q, "\n input wire "); + q = put_pins(out, q, nodes, text, meta, &text[(lo(nodes, ci, F_NAME)) as usize..(hi(nodes, ci, F_NAME)) as usize]); + q = put_text(out, q, "input wire "); q = put_range(out, q, it); q = put_name(out, q, text, lo(nodes, ci, F_NAME), hi(nodes, ci, F_NAME)); first = false; @@ -1765,7 +1852,8 @@ pub fn ports(out: &mut [u8], at: usize, nodes: &[u8], text: &[u8], meta: &[u8], if !(first) { q = put_text(out, q, ","); } - q = put_text(out, q, "\n inout wire "); + q = put_pins(out, q, nodes, text, meta, &text[(lo(nodes, co, F_NAME)) as usize..(hi(nodes, co, F_NAME)) as usize]); + q = put_text(out, q, "inout wire "); q = put_range(out, q, iot); q = put_name(out, q, text, lo(nodes, co, F_NAME), hi(nodes, co, F_NAME)); first = false; @@ -1783,10 +1871,11 @@ pub fn ports(out: &mut [u8], at: usize, nodes: &[u8], text: &[u8], meta: &[u8], if !(first) { q = put_text(out, q, ","); } + q = put_pins(out, q, nodes, text, meta, &text[(lo(nodes, c, F_NAME)) as usize..(hi(nodes, c, F_NAME)) as usize]); if (item_class(nodes, text, meta, ptext, c) == C_VAR) { - q = put_text(out, q, "\n output reg "); + q = put_text(out, q, "output reg "); } else { - q = put_text(out, q, "\n output wire "); + q = put_text(out, q, "output wire "); } q = put_range(out, q, t); q = put_name(out, q, text, lo(nodes, c, F_NAME), hi(nodes, c, F_NAME)); @@ -2120,6 +2209,9 @@ pub fn module_text(out: &mut [u8], at: usize, nodes: &[u8], text: &[u8], meta: & if (((cl == C_CONST) && is_period_name(text, lo(nodes, c, F_NAME), hi(nodes, c, F_NAME))) && !(period_named(nodes, text, meta, ptext, c))) { return fail(out, nodes, meta, text, c, "clock period declared for no instance output", lo(nodes, c, F_NAME), hi(nodes, c, F_NAME)); } + if ((is_str_const(nodes, text, meta, c) && (pin_kind(text, lo(nodes, c, F_NAME), hi(nodes, c, F_NAME)) != PIN_KEYS)) && !(pin_named(nodes, text, meta, ptext, oc, c))) { + return fail(out, nodes, meta, text, c, "pin attribute for no port of the module", lo(nodes, c, F_NAME), hi(nodes, c, F_NAME)); + } if (cl == C_VAR) { let t: i64 = sym_type(nodes, text, meta, ptext, 0, sym_of(SYM_TOP, c), 0); if !(ty_scalar(t)) { @@ -3122,6 +3214,8 @@ pub const OP_SHIFT: u8 = 2; pub const PERIOD_SUFFIX: &'static str = "_PERIOD_PS"; +pub const PIN_KEYS: usize = 4; + pub const ROLE_ATTR: u8 = 3; pub const ROLE_BAD: u8 = 0; @@ -3196,6 +3290,17 @@ pub fn declares_period(text: &[u8], ca: usize, cb: usize, ia: usize, ib: usize, return binds(text, ca, (cb - PERIOD_SUFFIX.len()), text, ia, ib, fa, fb); } +pub fn declares_pin(text: &[u8], ca: usize, cb: usize, k: usize, port: &[u8]) -> bool { + if !(is_pin_name(text, ca, cb, k)) { + return false; + } + let key: &'static str = pin_key(k); + if ((cb - ca) != ((port.len() + 1) + key.len())) { + return false; + } + return has_at(text, ca, cb, port); +} + pub fn digit_of(c: u8) -> u64 { if ((c >= 48) && (c <= 57)) { return ((c - 48) as u64); @@ -3389,6 +3494,18 @@ pub fn is_period_name(text: &[u8], ca: usize, cb: usize) -> bool { return same(&text[((cb - s)) as usize..(cb) as usize], PERIOD_SUFFIX); } +pub fn is_pin_name(text: &[u8], ca: usize, cb: usize, k: usize) -> bool { + let key: &'static str = pin_key(k); + let s: usize = key.len(); + if (cb < ((ca + s) + 2)) { + return false; + } + if (text[(((cb - s) - 1)) as usize] != 95) { + return false; + } + return same(&text[((cb - s)) as usize..(cb) as usize], key); +} + pub fn is_pub(meta: &[u8], i: usize) -> bool { return ((meta_word(meta, i, 0) & FLAG_PUB) == FLAG_PUB); } @@ -3656,6 +3773,30 @@ pub fn peer(a: i64, b: i64) -> i64 { return TY_BAD; } +pub fn pin_key(k: usize) -> &'static str { + if (k == 0) { + return "LOC"; + } + if (k == 1) { + return "IOSTANDARD"; + } + if (k == 2) { + return "SLEW"; + } + return "IN_TERM"; +} + +pub fn pin_kind(text: &[u8], ca: usize, cb: usize) -> usize { + let mut k: usize = 0; + while (k < PIN_KEYS) { + if is_pin_name(text, ca, cb, k) { + return k; + } + k = (k + 1); + } + return PIN_KEYS; +} + pub fn port_name_end(ps: &[u8], a: usize, b: usize) -> usize { if ((b > (a + 1)) && (ps[((b - 1)) as usize] == 95)) { return (b - 1); diff --git a/gen/rust/tri/t27b/verilog_plan.rs b/gen/rust/tri/t27b/verilog_plan.rs index 371b4b6e4e..c77735fabe 100644 --- a/gen/rust/tri/t27b/verilog_plan.rs +++ b/gen/rust/tri/t27b/verilog_plan.rs @@ -1,6 +1,6 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 17 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 18 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. @@ -725,6 +725,55 @@ pub fn declares_period(text: &[u8], ca: usize, cb: usize, ia: usize, ib: usize, return binds(text, ca, (cb - PERIOD_SUFFIX.len()), text, ia, ib, fa, fb); } +pub const PIN_KEYS: usize = 4; + +pub fn pin_key(k: usize) -> &'static str { + if (k == 0) { + return "LOC"; + } + if (k == 1) { + return "IOSTANDARD"; + } + if (k == 2) { + return "SLEW"; + } + return "IN_TERM"; +} + +pub fn is_pin_name(text: &[u8], ca: usize, cb: usize, k: usize) -> bool { + let key: &'static str = pin_key(k); + let s: usize = key.len(); + if (cb < ((ca + s) + 2)) { + return false; + } + if (text[(((cb - s) - 1)) as usize] != 95) { + return false; + } + return same(&text[((cb - s)) as usize..(cb) as usize], key); +} + +pub fn pin_kind(text: &[u8], ca: usize, cb: usize) -> usize { + let mut k: usize = 0; + while (k < PIN_KEYS) { + if is_pin_name(text, ca, cb, k) { + return k; + } + k = (k + 1); + } + return PIN_KEYS; +} + +pub fn declares_pin(text: &[u8], ca: usize, cb: usize, k: usize, port: &[u8]) -> bool { + if !(is_pin_name(text, ca, cb, k)) { + return false; + } + let key: &'static str = pin_key(k); + if ((cb - ca) != ((port.len() + 1) + key.len())) { + return false; + } + return has_at(text, ca, cb, port); +} + pub const NO_BOUND: i64 = -1; pub const MAX_UNROLL: i64 = 4096;