Skip to main content

ternlang_hdl/
verilog.rs

1//! Verilog module generator for BET primitives.
2//!
3//! Generates synthesisable Verilog-2001 that implements the BET ISA
4//! using 2-bit wire-pair trit encoding.
5
6/// A generated Verilog module: name + body text.
7#[derive(Debug, Clone)]
8pub struct VerilogModule {
9    pub name: String,
10    pub body: String,
11}
12
13/// Hardware targets for Verilog generation.
14#[derive(Debug, Clone, Copy)]
15pub enum TargetHardware {
16    BETNative,
17    FP5500Legacy, // March 2026 24-trit RISC
18    HuaweiT3,     // Patent-compliant low-power mode
19}
20
21impl VerilogModule {
22    /// Write to a string with a standard header comment.
23    pub fn render(&self) -> String {
24        self.render_with_target(TargetHardware::BETNative)
25    }
26
27    /// Write to a string with target-specific headers.
28    pub fn render_with_target(&self, target: TargetHardware) -> String {
29        let target_info = match target {
30            TargetHardware::BETNative => "Target: RFI-IRFOS BET-VM Native",
31            TargetHardware::FP5500Legacy => "Target: 5500FP Legacy (March 2026)",
32            TargetHardware::HuaweiT3 => "Target: Huawei-T3 Patent Optimization",
33        };
34        format!(
35            "// ternlang-hdl: generated BET Verilog\n// {}\n// Module: {}\n\n{}",
36            target_info, self.name, self.body
37        )
38    }
39}
40
41/// Generates Verilog modules for BET primitives.
42pub struct VerilogEmitter;
43
44impl VerilogEmitter {
45    /// trit_neg: inversion — maps +1↔-1, 0→0
46    /// Encoding: swap t[1] and t[0] bits.
47    pub fn trit_neg() -> VerilogModule {
48        VerilogModule {
49            name: "trit_neg".into(),
50            body: r#"module trit_neg (
51    input  [1:0] a,    // 2-bit trit: 01=-1, 10=+1, 11=0
52    output [1:0] y
53);
54    // Negate: swap the two bits
55    assign y = {a[0], a[1]};
56endmodule"#.into(),
57        }
58    }
59
60    /// trit_cons: consensus (ternary OR)
61    /// consensus(+1, 0) = +1, consensus(-1, 0) = -1, consensus(+1, -1) = 0
62    /// Truth table implemented as combinational logic.
63    pub fn trit_cons() -> VerilogModule {
64        VerilogModule {
65            name: "trit_cons".into(),
66            body: r#"module trit_cons (
67    input  [1:0] a,
68    input  [1:0] b,
69    output [1:0] y
70);
71    // Consensus: if a == b, output a; else output hold (11)
72    // Encoding: 01=-1, 10=+1, 11=0
73    assign y = (a == b) ? a : 2'b11;
74endmodule"#.into(),
75        }
76    }
77
78    /// trit_mul: balanced ternary multiply
79    /// Multiplication table: +1*+1=+1, +1*-1=-1, -1*-1=+1, 0*x=0
80    pub fn trit_mul() -> VerilogModule {
81        VerilogModule {
82            name: "trit_mul".into(),
83            body: r#"module trit_mul (
84    input  [1:0] a,
85    input  [1:0] b,
86    output [1:0] y
87);
88    // If either operand is hold (11), output is hold (11).
89    // If signs match, output is truth (10).
90    // If signs differ, output is conflict (01).
91    // Encoding: 01=-1, 10=+1, 11=0
92    wire a_hold = (a == 2'b11);
93    wire b_hold = (b == 2'b11);
94    wire same   = (a == b);
95    assign y = (a_hold || b_hold) ? 2'b11 :
96               same               ? 2'b10 :
97                                    2'b01;
98endmodule"#.into(),
99        }
100    }
101
102    /// trit_add: balanced ternary half-adder (sum + carry)
103    /// Returns two trits: sum and carry.
104    pub fn trit_add() -> VerilogModule {
105        VerilogModule {
106            name: "trit_add".into(),
107            body: r#"module trit_add (
108    input  [1:0] a,
109    input  [1:0] b,
110    output [1:0] sum,
111    output [1:0] carry
112);
113    // Balanced ternary addition truth table (16 combinations):
114    // Using case statement for clarity and synthesis friendliness.
115    // Encoding: 01=-1, 10=+1, 11=0
116    reg [3:0] result; // {carry[1:0], sum[1:0]}
117    always @(*) begin
118        case ({a, b})
119            // BET: -1 + -1: sum = +1, carry = -1
120            4'b0101: result = {2'b01, 2'b10};  // sum=+1, carry=-1
121            // a=-1, b=0 → sum=-1, carry=0
122            4'b0111: result = {2'b11, 2'b01};
123            // a=-1, b=+1 → sum=0, carry=0
124            4'b0110: result = {2'b11, 2'b11};
125            // a=0, b=-1 → sum=-1, carry=0
126            4'b1101: result = {2'b11, 2'b01};
127            // a=0, b=0 → sum=0, carry=0
128            4'b1111: result = {2'b11, 2'b11};
129            // a=0, b=+1 → sum=+1, carry=0
130            4'b1110: result = {2'b11, 2'b10};
131            // a=+1, b=-1 → sum=0, carry=0
132            4'b1001: result = {2'b11, 2'b11};
133            // a=+1, b=0 → sum=+1, carry=0
134            4'b1011: result = {2'b11, 2'b10};
135            // a=+1, b=+1 → sum=-1, carry=+1
136            4'b1010: result = {2'b10, 2'b01};
137            default: result = {2'b11, 2'b11}; // fault → hold
138        endcase
139    end
140    assign carry = result[3:2];
141    assign sum   = result[1:0];
142
143`ifdef FORMAL
144    // Recommendation 4: Formal Verification for Ternary Carry Logic
145    // Proves that valid ternary inputs never generate a FAULT (00) state.
146    always @(*) begin
147        if (a != 2'b00 && b != 2'b00) begin
148            assert(sum != 2'b00);
149            assert(carry != 2'b00);
150        end
151    end
152`endif
153endmodule"#.into(),
154        }
155    }
156
157    /// trit_reg: synchronous ternary D-register, resets to hold (0).
158    pub fn trit_reg() -> VerilogModule {
159        VerilogModule {
160            name: "trit_reg".into(),
161            body: r#"module trit_reg (
162    input        clk,
163    input        rst_n,  // active low reset
164    input  [1:0] d,
165    output reg [1:0] q
166);
167    always @(posedge clk or negedge rst_n) begin
168        if (!rst_n)
169            q <= 2'b11;  // reset to hold (0)
170        else
171            q <= d;
172    end
173endmodule"#.into(),
174        }
175    }
176
177    /// bet_alu: full BET ALU — supports ADD, MUL, NEG, CONS.
178    /// op: 2'b00=ADD, 2'b01=MUL, 2'b10=NEG(a), 2'b11=CONS
179    pub fn bet_alu() -> VerilogModule {
180        VerilogModule {
181            name: "bet_alu".into(),
182            body: r#"module bet_alu (
183    input  [1:0] a,
184    input  [1:0] b,
185    input  [1:0] op,     // 00=ADD, 01=MUL, 10=NEG, 11=CONS
186    output [1:0] result,
187    output [1:0] carry   // non-zero only for ADD
188);
189    wire [1:0] add_sum, add_carry;
190    wire [1:0] mul_out;
191    wire [1:0] neg_out;
192    wire [1:0] cons_out;
193
194    trit_add  u_add  (.a(a), .b(b), .sum(add_sum),  .carry(add_carry));
195    trit_mul  u_mul  (.a(a), .b(b), .y(mul_out));
196    trit_neg  u_neg  (.a(a),        .y(neg_out));
197    trit_cons u_cons (.a(a), .b(b), .y(cons_out));
198
199    assign result = (op == 2'b00) ? add_sum  :
200                    (op == 2'b01) ? mul_out  :
201                    (op == 2'b10) ? neg_out  :
202                                    cons_out;
203    assign carry  = (op == 2'b00) ? add_carry : 2'b11; // hold when not ADD
204endmodule"#.into(),
205        }
206    }
207
208    /// Parameterised N×N sparse matmul array.
209    /// Generates a Verilog module with N² trit_mul cells,
210    /// each with a zero-skip enable: zero-weighted cells clock-gate.
211    pub fn sparse_matmul(n: usize) -> VerilogModule {
212        let name = format!("sparse_matmul_{}x{}", n, n);
213        let mut body = format!(
214            r#"// N={n} sparse ternary matmul
215// Recommendation 1: Pipelined design for improved clock frequency.
216// Recommendation 3: Uses explicit clock enable (en) instead of clock gating.
217module {name} #(parameter N = {n}) (
218    input  clk,
219    input  rst_n,
220    input  en,                 // Clock enable
221    input  [N*2-1:0] a_flat,   // input vector (N trits, 2 bits each)
222    input  [N*N*2-1:0] w_flat, // weight matrix (N*N trits, 2 bits each)
223    output [N*2-1:0] out_flat  // output vector (N trits)
224);
225    genvar row, col;
226    // Pipelined partial products
227    reg [1:0] prod_pipe [0:N-1][0:N-1];
228    wire [1:0] prod [0:N-1][0:N-1];
229    wire skip [0:N-1][0:N-1];
230
231"#,
232            n = n,
233            name = name
234        );
235
236        body.push_str(r#"    // Generate multiply cells with zero-skip
237    generate
238        for (row = 0; row < N; row = row + 1) begin : gen_row
239            for (col = 0; col < N; col = col + 1) begin : gen_col
240                wire [1:0] w_ij = w_flat[(row*N+col)*2 +: 2];
241                wire [1:0] a_j  = a_flat[col*2 +: 2];
242                // Skip when weight is hold (2'b11)
243                assign skip[row][col] = (w_ij == 2'b11);
244                trit_mul u_mul (
245                    .a(a_j),
246                    .b(skip[row][col] ? 2'b11 : w_ij),
247                    .y(prod[row][col])
248                );
249            end
250        end
251    endgenerate
252
253    integer i, j;
254    always @(posedge clk or negedge rst_n) begin
255        if (!rst_n) begin
256            for (i = 0; i < N; i = i + 1)
257                for (j = 0; j < N; j = j + 1)
258                    prod_pipe[i][j] <= 2'b11; // hold
259        end else if (en) begin
260            for (i = 0; i < N; i = i + 1)
261                for (j = 0; j < N; j = j + 1)
262                    prod_pipe[i][j] <= prod[i][j];
263        end
264    end
265
266    // Recommendation 2: Standard Matmul Accumulation using trit_add.
267    // For synthesis, we chain addition combinatorially, then register the final sum.
268    wire [1:0] row_sum [0:N-1][0:N];
269    wire [1:0] row_carry [0:N-1][0:N];
270    
271    generate
272        for (row = 0; row < N; row = row + 1) begin : gen_acc
273            assign row_sum[row][0] = 2'b11; // initial sum = hold
274            assign row_carry[row][0] = 2'b11; // initial carry = hold
275            for (col = 0; col < N; col = col + 1) begin : gen_add
276                trit_add u_add (
277                    .a(row_sum[row][col]),
278                    .b(prod_pipe[row][col]),
279                    .sum(row_sum[row][col+1]),
280                    .carry(row_carry[row][col+1])
281                );
282            end
283            
284            // Output register (Pipeline stage 2)
285            reg [1:0] final_acc;
286            always @(posedge clk or negedge rst_n) begin
287                if (!rst_n) final_acc <= 2'b11;
288                else if (en) final_acc <= row_sum[row][N];
289            end
290            assign out_flat[row*2 +: 2] = final_acc;
291        end
292    endgenerate
293
294endmodule"#);
295
296        VerilogModule { name, body }
297    }
298
299    /// Emit all standard BET primitive modules as a single Verilog file.
300    pub fn emit_primitives() -> String {
301        let modules = vec![
302            Self::trit_neg(),
303            Self::trit_cons(),
304            Self::trit_mul(),
305            Self::trit_add(),
306            Self::trit_reg(),
307            Self::bet_alu(),
308        ];
309        let header = "// ternlang-hdl: BET Verilog Primitives\n// RFI-IRFOS Ternary Intelligence Stack\n// 2-bit wire-pair encoding: 01=-1, 10=+1, 11=0, 00=FAULT\n\n";
310        let mut out = header.to_string();
311        for m in modules {
312            out.push_str(&m.body);
313            out.push_str("\n\n");
314        }
315        out
316    }
317}
318
319#[cfg(test)]
320mod tests {
321    use super::*;
322
323    #[test]
324    fn test_trit_neg_renders() {
325        let m = VerilogEmitter::trit_neg();
326        let r = m.render();
327        assert!(r.contains("module trit_neg"));
328        assert!(r.contains("assign y = {a[0], a[1]}"));
329    }
330
331    #[test]
332    fn test_trit_mul_renders() {
333        let m = VerilogEmitter::trit_mul();
334        let r = m.render();
335        assert!(r.contains("module trit_mul"));
336        assert!(r.contains("a_hold || b_hold"));
337    }
338
339    #[test]
340    fn test_trit_add_renders() {
341        let m = VerilogEmitter::trit_add();
342        assert!(m.body.contains("module trit_add"));
343        assert!(m.body.contains("carry"));
344    }
345
346    #[test]
347    fn test_bet_alu_renders() {
348        let m = VerilogEmitter::bet_alu();
349        assert!(m.body.contains("module bet_alu"));
350        assert!(m.body.contains("trit_add"));
351        assert!(m.body.contains("trit_mul"));
352    }
353
354    #[test]
355    fn test_sparse_matmul_generates() {
356        let m = VerilogEmitter::sparse_matmul(4);
357        assert!(m.name.contains("4x4"));
358        assert!(m.body.contains("sparse_matmul_4x4"));
359        assert!(m.body.contains("skip"));
360    }
361
362    #[test]
363    fn test_emit_primitives_contains_all() {
364        let out = VerilogEmitter::emit_primitives();
365        assert!(out.contains("trit_neg"));
366        assert!(out.contains("trit_cons"));
367        assert!(out.contains("trit_mul"));
368        assert!(out.contains("trit_add"));
369        assert!(out.contains("trit_reg"));
370        assert!(out.contains("bet_alu"));
371    }
372}