diff --git a/rtl/int2_quant/int2_packer.sv b/rtl/int2_quant/int2_packer.sv new file mode 100644 index 000000000..f518add54 --- /dev/null +++ b/rtl/int2_quant/int2_packer.sv @@ -0,0 +1,92 @@ +// ============================================================================= +// Wave 43 -- Lane MM -- S-180 + S-181 +// File : rtl/int2_quant/int2_packer.sv +// Anchor : phi^2+phi^-2=3 (three-path witness) +// DOI : 10.5281/zenodo.19227877 +// +// INT2 Codebook +// State Code INT4 approx +// -1 2'b00 4'sb1111 (-1) +// 0 2'b01 4'sb0000 ( 0) +// +phi^-1 2'b10 4'b0011 (~+0.618 quantized to +3 in 4-bit scale) +// +1 2'b11 4'sb0001 (+1) +// +// phi^-1 = (sqrt(5)-1)/2 ~ 0.618 +// +phi^-1 quantized to nearest INT4 value: 3 in range [-8..+7] -> 4'b0011 +// +// R-SI-1 compliance: zero star, zero slash, zero unsigned-right-shift sign-loss. +// Only assign, case, comparison, addition, bitwise operators used. +// ============================================================================= + +// ----------------------------------------------------------------------------- +// Module: int2_unpacker +// Combinational lookup: INT2 code -> sign-extended INT4 activation +// ----------------------------------------------------------------------------- +module int2_unpacker ( + input logic [1:0] code, + output logic [3:0] int4_act +); + // Decode INT2 code to INT4 representation. + // Code 2'b00 => -1 => 4'sb1111 + // Code 2'b01 => 0 => 4'sb0000 + // Code 2'b10 => +phi^-1 (~0.618) quantized => 4'b0011 (+3 in INT4) + // Code 2'b11 => +1 => 4'sb0001 + always_comb begin + case (code) + 2'b00: int4_act = 4'sb1111; // -1 + 2'b01: int4_act = 4'sb0000; // 0 + 2'b10: int4_act = 4'b0011; // +phi^-1, quantized to +3 + 2'b11: int4_act = 4'sb0001; // +1 + default: int4_act = 4'sb0000; + endcase + end +endmodule + +// ----------------------------------------------------------------------------- +// Module: int2_pack_sram_iface +// Packs four INT2 codes into one 8-bit SRAM word (little-endian bit order). +// act0 occupies bits [1:0], act1 bits [3:2], act2 bits [5:4], act3 bits [7:6]. +// ----------------------------------------------------------------------------- +module int2_pack_sram_iface ( + input logic [1:0] act0, + input logic [1:0] act1, + input logic [1:0] act2, + input logic [1:0] act3, + output logic [7:0] sram_word +); + assign sram_word = {act3, act2, act1, act0}; +endmodule + +// ----------------------------------------------------------------------------- +// Module: int2_col13_gate +// Quantizes a signed INT4 activation back to INT2 code. +// Thresholds (approximate, integer arithmetic only -- R-SI-1 compliant): +// act < -1 => code 2'b00 (-1 bucket) +// act == 0 => code 2'b01 ( 0 bucket) +// 0 < act < 2 => code 2'b10 (phi^-1 bucket, mid-positive) +// act >= 2 => code 2'b11 (+1 bucket) +// act < 0 and act != -1 ... rounded to -1 bucket +// +// Decision boundaries chosen to partition [-8..+7] into four regions: +// Region 2'b00: act_int4 <= -1 (any negative) +// Region 2'b01: act_int4 == 0 +// Region 2'b10: act_int4 == 1 or act_int4 == 2 (small positive) +// Region 2'b11: act_int4 >= 3 (large positive) +// +// No star, no slash operators used. +// ----------------------------------------------------------------------------- +module int2_col13_gate ( + input logic signed [3:0] act_int4, + output logic [1:0] code +); + always_comb begin + if (act_int4 <= 4'sb1111) // act_int4 <= -1 (negative) + code = 2'b00; + else if (act_int4 == 4'sb0000) // act_int4 == 0 + code = 2'b01; + else if (act_int4 <= 4'sb0010) // 1 <= act_int4 <= 2 (phi^-1 region) + code = 2'b10; + else // act_int4 >= 3 (+1 region) + code = 2'b11; + end +endmodule diff --git a/rtl/int2_quant/int2_packer_tb.sv b/rtl/int2_quant/int2_packer_tb.sv new file mode 100644 index 000000000..32eafa056 --- /dev/null +++ b/rtl/int2_quant/int2_packer_tb.sv @@ -0,0 +1,130 @@ +// ============================================================================= +// Testbench: int2_packer_tb +// Wave 43, Lane MM -- S-180 + S-181 +// Tests: int2_unpacker, int2_pack_sram_iface, int2_col13_gate +// ============================================================================= +module int2_packer_tb; + + // ------------------------------------------------------------------------- + // DUT signals + // ------------------------------------------------------------------------- + logic [1:0] code_in; + logic [3:0] int4_out; + + logic [1:0] act0, act1, act2, act3; + logic [7:0] sram_word; + + logic signed [3:0] act_int4_in; + logic [1:0] code_out; + + integer errors; + + // ------------------------------------------------------------------------- + // Instantiations + // ------------------------------------------------------------------------- + int2_unpacker u_unpacker ( + .code (code_in), + .int4_act(int4_out) + ); + + int2_pack_sram_iface u_packer ( + .act0 (act0), + .act1 (act1), + .act2 (act2), + .act3 (act3), + .sram_word(sram_word) + ); + + int2_col13_gate u_gate ( + .act_int4(act_int4_in), + .code (code_out) + ); + + // ------------------------------------------------------------------------- + // Test tasks + // ------------------------------------------------------------------------- + task check_unpack; + input [1:0] c; + input signed [3:0] expected; + input [63:0] test_id; + begin + code_in = c; + #1; + if (int4_out !== expected) begin + $display("FAIL Test %0d: int2_unpacker(2'b%02b) = 4'b%04b, expected 4'b%04b", + test_id, c, int4_out, expected); + errors = errors + 1; + end else begin + $display("PASS Test %0d: int2_unpacker(2'b%02b) = 4'b%04b", test_id, c, int4_out); + end + end + endtask + + task check_gate; + input signed [3:0] act; + input [1:0] expected_code; + input [63:0] test_id; + begin + act_int4_in = act; + #1; + if (code_out !== expected_code) begin + $display("FAIL Test %0d: int2_col13_gate(4'sb%04b) = 2'b%02b, expected 2'b%02b", + test_id, act, code_out, expected_code); + errors = errors + 1; + end else begin + $display("PASS Test %0d: int2_col13_gate(4'sb%04b) = 2'b%02b", test_id, act, code_out); + end + end + endtask + + // ------------------------------------------------------------------------- + // Main test sequence + // ------------------------------------------------------------------------- + initial begin + errors = 0; + + // Test 1: int2_unpacker code=2'b00 -> -1 => 4'sb1111 + check_unpack(2'b00, 4'sb1111, 1); + + // Test 2: int2_unpacker code=2'b01 -> 0 => 4'sb0000 + check_unpack(2'b01, 4'sb0000, 2); + + // Test 3: int2_unpacker code=2'b10 -> phi^-1 approx => 4'b0011 (+3) + check_unpack(2'b10, 4'b0011, 3); + + // Test 4: int2_unpacker code=2'b11 -> +1 => 4'sb0001 + check_unpack(2'b11, 4'sb0001, 4); + + // Test 5: int2_pack_sram_iface -- verify bit packing + // act0=2'b01, act1=2'b10, act2=2'b11, act3=2'b00 -> sram_word = 8'b00_11_10_01 + act0 = 2'b01; + act1 = 2'b10; + act2 = 2'b11; + act3 = 2'b00; + #1; + if (sram_word !== 8'b00111001) begin + $display("FAIL Test 5: pack({00,11,10,01}) = 8'b%08b, expected 8'b00111001", sram_word); + errors = errors + 1; + end else begin + $display("PASS Test 5: pack({00,11,10,01}) = 8'b%08b", sram_word); + end + + // Test 6: int2_col13_gate(4'sb0000 = 0) -> code 2'b01 + check_gate(4'sb0000, 2'b01, 6); + + // Test 7: int2_col13_gate(4'sb0111 = +7, max positive) -> code 2'b11 (+1 region) + check_gate(4'sb0111, 2'b11, 7); + + // Test 8: int2_col13_gate(4'sb1001 = -7, negative) -> code 2'b00 (-1 region) + check_gate(4'sb1001, 2'b00, 8); + + // Summary + if (errors == 0) + $display("ALL TESTS PASSED (8/8)"); + else + $display("FAILURES: %0d / 8 tests failed", errors); + + $finish; + end + +endmodule