1use std::fmt::Write as _;
17
18use super::weights::{decoder_name, DECODERS, UNIT};
19
20pub fn matvec_verilog() -> String {
23 let mut v = format!(
24 "{}\n{}\n{}\n{}\n{}",
25 super::ram::ram_verilog(&act_ram_widths()),
26 super::lane::lane_verilog(),
27 super::array::array_verilog(),
28 super::weights::weights_verilog(),
29 MATVEC_VERILOG
30 );
31 for d in DECODERS {
32 let elems = d.ty.block_len();
33 let units = elems / UNIT;
34 let digits = if d.code_bits > 4 { 2 } else { 1 };
35 let name = d.ty.name().to_lowercase();
36 let bits = 8 * d.ty.block_bytes();
37 writeln!(
38 v,
39 r#"
40// Two rows' {fmt} blocks against one token-row.
41module logos_matvec_{name}(input [{top}:0] blk1, input [{top}:0] blk2, input [{act_top}:0] act, input two, input [7:0] m,
42 input signed [11:0] e, input [31:0] acc1, input [31:0] acc2, output [31:0] out1, output [31:0] out2);
43 wire [{code_top}:0] code1, code2;
44 wire [{unit_top}:0] c1, n1, c2, n2;
45 wire [15:0] a1, b1, a2, b2;
46 {dec} d1(.blk(blk1), .code(code1), .c(c1), .n(n1), .a(a1), .b(b1));
47 {dec} d2(.blk(blk2), .code(code2), .c(c2), .n(n2), .a(a2), .b(b2));
48 logos_matvec_block #(.UNITS({units}), .DIGITS({digits})) u(.code1(code1), .code2(code2), .c1(c1), .n1(n1), .c2(c2), .n2(n2),
49 .a1(a1), .b1(b1), .a2(a2), .b2(b2), .z(8'd{offset}), .act(act), .two(two), .m(m), .e(e), .acc1(acc1), .acc2(acc2),
50 .out1(out1), .out2(out2));
51endmodule"#,
52 fmt = d.ty.name(),
53 top = bits - 1,
54 act_top = 16 * elems - 1,
55 code_top = 8 * elems - 1,
56 unit_top = 16 * units - 1,
57 dec = decoder_name(d.ty),
58 offset = d.offset,
59 )
60 .unwrap();
61 writeln!(
62 v,
63 r#"
64// A {fmt} matrix against up to T_MAX token-rows: two rows' blocks a beat from the weight stream, each applied to every
65// token-row a cycle, every token-row's two rows emitted after their last block, then the next two rows.
66module logos_matvec_engine_{name} #(parameter T_MAX = 4, NB_MAX = 8)
67 (input clk, input rst,
68 input start, input [15:0] pairs, input [15:0] blocks, input [7:0] tokens, input two, output busy,
69 input act_wr, input [7:0] act_t, input [15:0] act_b, input [{act_top}:0] act_codes, input [7:0] act_m, input [11:0] act_e,
70 input w_valid, output w_ready, input [{top}:0] w_blk1, input [{top}:0] w_blk2,
71 output reg o_valid, output reg [15:0] o_pair, output reg [7:0] o_t, output reg [31:0] o_val1, output reg [31:0] o_val2);
72 reg [15:0] pair, b;
73 reg [7:0] t;
74 // Each token-row's blocks of activations in block RAM: the codes, the scale's m and its exponent.
75 localparam AN = T_MAX * NB_MAX, AA = (AN > 1) ? $clog2(AN) : 1;
76 wire [{act_top} + 24:0] act_word;
77 wire [31:0] act_ra = t * NB_MAX + b, act_wa = act_t * NB_MAX + act_b;
78 {act_ram} #(.D(AN), .A(AA)) act_ram(.clk(clk), .ra(act_ra[AA-1:0]), .rd(act_word), .we(act_wr),
79 .wa(act_wa[AA-1:0]), .wd({{4'd0, act_e, act_m, act_codes}}), .wm({{(({act_top} + 25) / 8){{1'b1}}}}));
80 (* ramstyle = "logic" *) reg [31:0] acc1 [0:T_MAX-1];
81 (* ramstyle = "logic" *) reg [31:0] acc2 [0:T_MAX-1];
82 reg [{top}:0] blk1, blk2;
83 localparam IDLE = 2'd0, WAIT = 2'd1, APPLY = 2'd2, EMIT = 2'd3;
84 reg [1:0] state;
85 assign busy = (state != IDLE);
86 assign w_ready = (state == WAIT);
87 wire [31:0] out1, out2;
88 logos_matvec_{name} u(.blk1(blk1), .blk2(blk2), .act(act_word[{act_top}:0]), .two(two), .m(act_word[{act_top} + 8:{act_top} + 1]),
89 .e($signed(act_word[{act_top} + 20:{act_top} + 9])), .acc1(acc1[t]), .acc2(acc2[t]), .out1(out1), .out2(out2));
90 integer k;
91 always @(posedge clk) begin
92 o_valid <= 1'b0;
93 if (rst) state <= IDLE;
94 else case (state)
95 IDLE: if (start) begin
96 pair <= 16'd0; b <= 16'd0; t <= 8'd0; state <= WAIT;
97 for (k = 0; k < T_MAX; k = k + 1) begin acc1[k] <= 32'd0; acc2[k] <= 32'd0; end
98 end
99 WAIT: if (w_valid) begin blk1 <= w_blk1; blk2 <= w_blk2; t <= 8'd0; state <= APPLY; end
100 APPLY: begin
101 acc1[t] <= out1; acc2[t] <= out2;
102 if (t + 8'd1 < tokens) t <= t + 8'd1;
103 else if (b + 16'd1 < blocks) begin b <= b + 16'd1; state <= WAIT; end
104 else begin t <= 8'd0; state <= EMIT; end
105 end
106 EMIT: begin
107 o_valid <= 1'b1; o_pair <= pair; o_t <= t; o_val1 <= acc1[t]; o_val2 <= acc2[t];
108 if (t + 8'd1 < tokens) t <= t + 8'd1;
109 else begin
110 for (k = 0; k < T_MAX; k = k + 1) begin acc1[k] <= 32'd0; acc2[k] <= 32'd0; end
111 b <= 16'd0;
112 if (pair + 16'd1 < pairs) begin pair <= pair + 16'd1; state <= WAIT; end
113 else state <= IDLE;
114 end
115 end
116 endcase
117 end
118endmodule"#,
119 fmt = d.ty.name(),
120 top = bits - 1,
121 act_top = 16 * elems - 1,
122 act_ram = super::ram::ram_name(16 * elems + 24),
123 )
124 .unwrap();
125 }
126 v
127}
128
129pub(super) fn act_ram_widths() -> Vec<usize> {
131 let mut w: Vec<usize> = DECODERS.iter().map(|d| 16 * d.ty.block_len() + 24).collect();
132 w.sort();
133 w.dedup();
134 w
135}
136
137const MATVEC_VERILOG: &str = r#"
138// One weight block of two rows against one token-row: the rows' unit sums from the processing elements, a 4-bit digit
139// at a time, and each row's lane.
140module logos_matvec_block #(parameter UNITS = 16, DIGITS = 2)
141 (input [128*UNITS-1:0] code1, input [128*UNITS-1:0] code2,
142 input [16*UNITS-1:0] c1, input [16*UNITS-1:0] n1, input [16*UNITS-1:0] c2, input [16*UNITS-1:0] n2,
143 input [15:0] a1, input [15:0] b1, input [15:0] a2, input [15:0] b2, input [7:0] z,
144 input [256*UNITS-1:0] act, input two, input [7:0] m, input signed [11:0] e,
145 input [31:0] acc1, input [31:0] acc2, output [31:0] out1, output [31:0] out2);
146 wire [24*UNITS-1:0] bp1_hi, bp2_hi, bp1_lo, bp2_lo;
147 wire [12*UNITS-1:0] bs_hi, bs_lo, bq1, bq2;
148 logos_matvec_partials #(.UNITS(UNITS), .DIGITS(DIGITS)) parts(.code1(code1), .code2(code2), .act(act), .two(two),
149 .p1_hi(bp1_hi), .p2_hi(bp2_hi), .p1_lo(bp1_lo), .p2_lo(bp2_lo), .s_hi(bs_hi), .s_lo(bs_lo), .q1(bq1), .q2(bq2));
150 logos_lane #(.UNITS(UNITS)) l1(.p_hi(bp1_hi), .s_hi(bs_hi), .p_lo(bp1_lo), .s_lo(bs_lo), .q(bq1), .c(c1), .n(n1), .z(z),
151 .a(a1), .b(b1), .m(m), .e(e), .two(two), .acc(acc1), .out(out1));
152 logos_lane #(.UNITS(UNITS)) l2(.p_hi(bp2_hi), .s_hi(bs_hi), .p_lo(bp2_lo), .s_lo(bs_lo), .q(bq2), .c(c2), .n(n2), .z(z),
153 .a(a2), .b(b2), .m(m), .e(e), .two(two), .acc(acc2), .out(out2));
154endmodule
155
156// A block of two rows' codes against one token-row's activation words: each 16-element unit's partial sums for both
157// rows from the processing elements, a 4-bit digit at a time, and the unit's sums of excess codes and of each row's
158// codes, the buses the lanes take.
159module logos_matvec_partials #(parameter UNITS = 16, DIGITS = 2)
160 (input [128*UNITS-1:0] code1, input [128*UNITS-1:0] code2, input [256*UNITS-1:0] act, input two,
161 output [24*UNITS-1:0] p1_hi, output [24*UNITS-1:0] p2_hi, output [24*UNITS-1:0] p1_lo, output [24*UNITS-1:0] p2_lo,
162 output [12*UNITS-1:0] s_hi, output [12*UNITS-1:0] s_lo, output [12*UNITS-1:0] q1, output [12*UNITS-1:0] q2);
163 // Each element's words: the excess high (or only) word, and the low byte of a two-word code.
164 wire [128*UNITS-1:0] w_hi, w_lo;
165 genvar i, u, d;
166 generate
167 for (i = 0; i < 16*UNITS; i = i + 1) begin : word
168 wire signed [15:0] code = act[16*i +: 16];
169 assign w_hi[8*i +: 8] = two ? (code[15:8] + 8'd128) : (code[7:0] + 8'd128);
170 assign w_lo[8*i +: 8] = two ? code[7:0] : 8'd0;
171 end
172 for (u = 0; u < UNITS; u = u + 1) begin : unit
173 // The unit's sums: the excess codes', each row's codes'.
174 reg [11:0] s_hi, s_lo, q1s, q2s;
175 integer k;
176 always @* begin
177 s_hi = 12'd0; s_lo = 12'd0; q1s = 12'd0; q2s = 12'd0;
178 for (k = 0; k < 16; k = k + 1) begin
179 s_hi = s_hi + w_hi[8*(16*u + k) +: 8];
180 s_lo = s_lo + w_lo[8*(16*u + k) +: 8];
181 q1s = q1s + code1[8*(16*u + k) +: 8];
182 q2s = q2s + code2[8*(16*u + k) +: 8];
183 end
184 end
185 // Per word and digit, the unit's two elements of eight: row one's partial in the low lanes, row two's in the
186 // high lanes with its offset restored.
187 wire [23:0] p1_hi, p2_hi, p1_lo, p2_lo;
188 wire [23:0] dig1_hi [0:1], dig2_hi [0:1], dig1_lo [0:1], dig2_lo [0:1];
189 for (d = 0; d < 2; d = d + 1) begin : digit
190 wire [31:0] q1a, q2a, q1b, q2b;
191 for (i = 0; i < 8; i = i + 1) begin : nib
192 assign q1a[4*i +: 4] = (d < DIGITS) ? code1[8*(16*u + i) + 4*d +: 4] : 4'd0;
193 assign q2a[4*i +: 4] = (d < DIGITS) ? code2[8*(16*u + i) + 4*d +: 4] : 4'd0;
194 assign q1b[4*i +: 4] = (d < DIGITS) ? code1[8*(16*u + 8 + i) + 4*d +: 4] : 4'd0;
195 assign q2b[4*i +: 4] = (d < DIGITS) ? code2[8*(16*u + 8 + i) + 4*d +: 4] : 4'd0;
196 end
197 wire [14:0] lo_ha, lo_hb, lo_la, lo_lb;
198 wire signed [16:0] hi_ha, hi_hb, hi_la, hi_lb;
199 logos_pe8 ha(.a(w_hi[8*16*u +: 64]), .q1(q1a), .q2(q2a), .lo(lo_ha), .hi(hi_ha));
200 logos_pe8 hb(.a(w_hi[8*(16*u + 8) +: 64]), .q1(q1b), .q2(q2b), .lo(lo_hb), .hi(hi_hb));
201 logos_pe8 la(.a(w_lo[8*16*u +: 64]), .q1(q1a), .q2(q2a), .lo(lo_la), .hi(hi_la));
202 logos_pe8 lb(.a(w_lo[8*(16*u + 8) +: 64]), .q1(q1b), .q2(q2b), .lo(lo_lb), .hi(hi_lb));
203 assign dig1_hi[d] = {9'd0, lo_ha} + {9'd0, lo_hb};
204 assign dig2_hi[d] = $signed(hi_ha) + $signed(hi_hb) + $signed({9'd0, s_hi, 3'd0});
205 assign dig1_lo[d] = {9'd0, lo_la} + {9'd0, lo_lb};
206 assign dig2_lo[d] = $signed(hi_la) + $signed(hi_lb) + $signed({9'd0, s_lo, 3'd0});
207 end
208 assign p1_hi = dig1_hi[0] + (dig1_hi[1] << 4);
209 assign p2_hi = dig2_hi[0] + (dig2_hi[1] << 4);
210 assign p1_lo = dig1_lo[0] + (dig1_lo[1] << 4);
211 assign p2_lo = dig2_lo[0] + (dig2_lo[1] << 4);
212 end
213 endgenerate
214 // The lanes' unit buses.
215 generate
216 for (u = 0; u < UNITS; u = u + 1) begin : bus
217 assign p1_hi[24*u +: 24] = unit[u].p1_hi;
218 assign p2_hi[24*u +: 24] = unit[u].p2_hi;
219 assign p1_lo[24*u +: 24] = unit[u].p1_lo;
220 assign p2_lo[24*u +: 24] = unit[u].p2_lo;
221 assign s_hi[12*u +: 12] = unit[u].s_hi;
222 assign s_lo[12*u +: 12] = unit[u].s_lo;
223 assign q1[12*u +: 12] = unit[u].q1s;
224 assign q2[12*u +: 12] = unit[u].q2s;
225 end
226 endgenerate
227endmodule
228"#;