User mods: matmul RTL + shift_add_mult + C program

This commit is contained in:
2026-06-25 00:43:16 +08:00
parent f05c445ef3
commit 641d055f18
3 changed files with 512 additions and 381 deletions

View File

@@ -2,329 +2,394 @@ module matmul_axi_slave (
input clk,
input resetn,
// AXI Write Address Channel
input [4:0] s_axi_awid,
input [31:0] s_axi_awaddr,
input [7:0] s_axi_awlen,
input [2:0] s_axi_awsize,
input [1:0] s_axi_awburst,
input s_axi_awlock,
input [3:0] s_axi_awcache,
input [2:0] s_axi_awprot,
input s_axi_awvalid,
output s_axi_awready,
output reg s_axi_awready,
// AXI Write Data Channel
input [4:0] s_axi_wid,
input [31:0] s_axi_wdata,
input [3:0] s_axi_wstrb,
input s_axi_wlast,
input s_axi_wvalid,
output s_axi_wready,
output reg s_axi_wready,
output reg [4:0] s_axi_bid,
// AXI Write Response Channel
output [4:0] s_axi_bid,
output [1:0] s_axi_bresp,
output reg s_axi_bvalid,
input s_axi_bready,
// AXI Read Address Channel
input [4:0] s_axi_arid,
input [31:0] s_axi_araddr,
input [7:0] s_axi_arlen,
input [2:0] s_axi_arsize,
input [1:0] s_axi_arburst,
input s_axi_arlock,
input [3:0] s_axi_arcache,
input [2:0] s_axi_arprot,
input s_axi_arvalid,
output s_axi_arready,
output reg s_axi_arready,
// AXI Read Data Channel
output reg [4:0] s_axi_rid,
output reg [31:0] s_axi_rdata,
output [1:0] s_axi_rresp,
output s_axi_rlast,
output reg s_axi_rlast,
output reg s_axi_rvalid,
input s_axi_rready
);
localparam ADDR_CTRL = 12'h000;
localparam ADDR_STATUS = 12'h004;
localparam ADDR_SRC_BASE = 12'h008;
localparam ADDR_DST_BASE = 12'h00c;
localparam ADDR_GROUP_NUM = 12'h010;
localparam ADDR_A_BASE = 12'h020;
localparam ADDR_B_BASE = 12'h060;
localparam ADDR_C_BASE = 12'h0a0;
// ==========================================
// 内存地址映射 (Memory Map)
// ==========================================
localparam ADDR_CTRL = 12'h000;
localparam ADDR_STATUS = 12'h004;
// Bank 0 (可完全当作原来的单缓冲使用)
localparam ADDR_B0_A = 12'h100;
localparam ADDR_B0_B = 12'h140;
localparam ADDR_B0_C = 12'h180;
// Bank 1 (供无等待计算扩展使用)
localparam ADDR_B1_A = 12'h300;
localparam ADDR_B1_B = 12'h340;
localparam ADDR_B1_C = 12'h380;
reg [31:0] src_base;
reg [31:0] dst_base;
reg [31:0] group_num;
reg [31:0] a_data [0:15];
reg [31:0] b_data [0:15];
reg [65:0] c_data [0:15];
// ==========================================
// 寄存器与数据缓冲
// ==========================================
reg req_0, req_1;
reg busy;
reg done;
reg error;
reg [31:0] b0_a [0:15];
reg [31:0] b0_b [0:15];
reg [65:0] b0_c [0:15];
reg [31:0] b1_a [0:15];
reg [31:0] b1_b [0:15];
reg [65:0] b1_c [0:15];
reg [31:0] awaddr_hold;
reg [4:0] awid_hold;
reg [7:0] awlen_hold;
reg [7:0] wbeat_cnt;
reg aw_hold_valid;
reg rburst_active;
reg [7:0] rburst_rem;
reg [31:0] wdata_hold;
reg [3:0] wstrb_hold;
reg w_hold_valid;
function [31:0] apply_wstrb;
input [31:0] old_value;
input [31:0] new_value;
input [3:0] strobe;
begin
apply_wstrb = old_value;
if (strobe[0]) apply_wstrb[7:0] = new_value[7:0];
if (strobe[1]) apply_wstrb[15:8] = new_value[15:8];
if (strobe[2]) apply_wstrb[23:16] = new_value[23:16];
if (strobe[3]) apply_wstrb[31:24] = new_value[31:24];
end
endfunction
reg busy;
reg done;
reg error;
reg [3:0] calc_index;
// ==========================================
// AXI Write 通道逻辑 (支持 INCR Burst)
// ==========================================
localparam W_IDLE = 2'd0;
localparam W_DATA = 2'd1;
localparam W_RESP = 2'd2;
reg [1:0] w_state;
reg [31:0] w_addr;
reg [4:0] w_id;
reg [1:0] w_burst;
wire [11:0] w_offset = w_addr[11:0];
assign s_axi_bid = w_id;
assign s_axi_bresp = 2'b00;
wire write_fire;
wire start_fire;
wire [11:0] write_offset = awaddr_hold[11:0];
wire [11:0] read_offset = s_axi_araddr[11:0];
// 前向声明引擎的状态
localparam C_IDLE = 2'd0;
localparam C_CALC = 2'd1;
localparam C_WAIT_MULT = 2'd2;
localparam C_ACCUM = 2'd3;
reg [1:0] state;
reg cur_bank;
reg calc_done_pulse;
assign s_axi_awready = !aw_hold_valid;
assign s_axi_wready = !w_hold_valid;
assign s_axi_bresp = 2'b00;
assign s_axi_arready = !s_axi_rvalid;
assign s_axi_rresp = 2'b00;
assign s_axi_rlast = 1'b1;
assign write_fire = aw_hold_valid && w_hold_valid && !s_axi_bvalid;
assign start_fire = write_fire && (write_offset == ADDR_CTRL) && wdata_hold[0] && !busy;
function [31:0] apply_wstrb;
input [31:0] old_value;
input [31:0] new_value;
input [3:0] strobe;
begin
apply_wstrb = old_value;
if (strobe[0]) apply_wstrb[7:0] = new_value[7:0];
if (strobe[1]) apply_wstrb[15:8] = new_value[15:8];
if (strobe[2]) apply_wstrb[23:16] = new_value[23:16];
if (strobe[3]) apply_wstrb[31:24] = new_value[31:24];
end
endfunction
function [63:0] mul_u32_shift_add;
input [31:0] lhs;
input [31:0] rhs;
integer bit_index;
begin
mul_u32_shift_add = 64'b0;
for (bit_index = 0; bit_index < 32; bit_index = bit_index + 1) begin
if (rhs[bit_index]) begin
mul_u32_shift_add = mul_u32_shift_add + ({32'b0, lhs} << bit_index);
always @(posedge clk) begin
if (!resetn) begin
w_state <= W_IDLE;
s_axi_awready <= 1'b1;
s_axi_wready <= 1'b0;
s_axi_bvalid <= 1'b0;
req_0 <= 1'b0;
req_1 <= 1'b0;
done <= 1'b0;
error <= 1'b0;
end else begin
// 引擎接管请求后清零
if (req_0 && state == C_CALC && cur_bank == 1'b0) req_0 <= 1'b0;
if (req_1 && state == C_CALC && cur_bank == 1'b1) req_1 <= 1'b0;
// 引擎计算完成,拉高通用 done 标志
if (calc_done_pulse) begin
done <= 1'b1;
end
end
end
endfunction
function [65:0] calc_c_element;
input [3:0] index;
reg [1:0] row;
reg [1:0] col;
begin
row = index[3:2];
col = index[1:0];
calc_c_element = {2'b0, mul_u32_shift_add(a_data[{row, 2'd0}], b_data[{2'd0, col}])}
+ {2'b0, mul_u32_shift_add(a_data[{row, 2'd1}], b_data[{2'd1, col}])}
+ {2'b0, mul_u32_shift_add(a_data[{row, 2'd2}], b_data[{2'd2, col}])}
+ {2'b0, mul_u32_shift_add(a_data[{row, 2'd3}], b_data[{2'd3, col}])};
end
endfunction
function [31:0] c_read_word;
input [5:0] word_index;
begin
case (word_index)
6'd0: c_read_word = c_data[0][31:0];
6'd1: c_read_word = c_data[0][63:32];
6'd2: c_read_word = {30'b0, c_data[0][65:64]};
6'd3: c_read_word = c_data[1][31:0];
6'd4: c_read_word = c_data[1][63:32];
6'd5: c_read_word = {30'b0, c_data[1][65:64]};
6'd6: c_read_word = c_data[2][31:0];
6'd7: c_read_word = c_data[2][63:32];
6'd8: c_read_word = {30'b0, c_data[2][65:64]};
6'd9: c_read_word = c_data[3][31:0];
6'd10: c_read_word = c_data[3][63:32];
6'd11: c_read_word = {30'b0, c_data[3][65:64]};
6'd12: c_read_word = c_data[4][31:0];
6'd13: c_read_word = c_data[4][63:32];
6'd14: c_read_word = {30'b0, c_data[4][65:64]};
6'd15: c_read_word = c_data[5][31:0];
6'd16: c_read_word = c_data[5][63:32];
6'd17: c_read_word = {30'b0, c_data[5][65:64]};
6'd18: c_read_word = c_data[6][31:0];
6'd19: c_read_word = c_data[6][63:32];
6'd20: c_read_word = {30'b0, c_data[6][65:64]};
6'd21: c_read_word = c_data[7][31:0];
6'd22: c_read_word = c_data[7][63:32];
6'd23: c_read_word = {30'b0, c_data[7][65:64]};
6'd24: c_read_word = c_data[8][31:0];
6'd25: c_read_word = c_data[8][63:32];
6'd26: c_read_word = {30'b0, c_data[8][65:64]};
6'd27: c_read_word = c_data[9][31:0];
6'd28: c_read_word = c_data[9][63:32];
6'd29: c_read_word = {30'b0, c_data[9][65:64]};
6'd30: c_read_word = c_data[10][31:0];
6'd31: c_read_word = c_data[10][63:32];
6'd32: c_read_word = {30'b0, c_data[10][65:64]};
6'd33: c_read_word = c_data[11][31:0];
6'd34: c_read_word = c_data[11][63:32];
6'd35: c_read_word = {30'b0, c_data[11][65:64]};
6'd36: c_read_word = c_data[12][31:0];
6'd37: c_read_word = c_data[12][63:32];
6'd38: c_read_word = {30'b0, c_data[12][65:64]};
6'd39: c_read_word = c_data[13][31:0];
6'd40: c_read_word = c_data[13][63:32];
6'd41: c_read_word = {30'b0, c_data[13][65:64]};
6'd42: c_read_word = c_data[14][31:0];
6'd43: c_read_word = c_data[14][63:32];
6'd44: c_read_word = {30'b0, c_data[14][65:64]};
6'd45: c_read_word = c_data[15][31:0];
6'd46: c_read_word = c_data[15][63:32];
6'd47: c_read_word = {30'b0, c_data[15][65:64]};
default: c_read_word = 32'b0;
endcase
end
endfunction
integer i;
always @(posedge clk) begin
if (!resetn) begin
src_base <= 32'b0;
dst_base <= 32'b0;
group_num <= 32'd1;
awaddr_hold <= 32'b0;
awid_hold <= 5'b0;
aw_hold_valid <= 1'b0;
wdata_hold <= 32'b0;
wstrb_hold <= 4'b0;
w_hold_valid <= 1'b0;
s_axi_bid <= 5'b0;
s_axi_bvalid <= 1'b0;
wbeat_cnt <= 8'd0;
rburst_active <= 1'b0;
s_axi_rid <= 5'b0;
s_axi_rdata <= 32'b0;
s_axi_rvalid <= 1'b0;
busy <= 1'b0;
done <= 1'b0;
error <= 1'b0;
calc_index <= 4'b0;
for (i = 0; i < 16; i = i + 1) begin
a_data[i] <= 32'b0;
b_data[i] <= 32'b0;
c_data[i] <= 66'b0;
end
end else begin
if (s_axi_awvalid && s_axi_awready) begin
awaddr_hold <= s_axi_awaddr;
awid_hold <= s_axi_awid;
awlen_hold <= s_axi_awlen;
aw_hold_valid <= 1'b1;
end
if (s_axi_wvalid && s_axi_wready) begin
wdata_hold <= s_axi_wdata;
wstrb_hold <= s_axi_wstrb;
w_hold_valid <= 1'b1;
end
if (s_axi_bvalid && s_axi_bready) begin
s_axi_bvalid <= 1'b0;
end
if (write_fire) begin
wbeat_cnt <= wbeat_cnt + 8'd1;
w_hold_valid <= 1'b0;
if (s_axi_wlast) begin
s_axi_bid <= awid_hold;
s_axi_bvalid <= 1'b1;
aw_hold_valid <= 1'b0;
wbeat_cnt <= 8'd0;
end
if (write_offset == ADDR_SRC_BASE) begin
src_base <= apply_wstrb(src_base, wdata_hold, wstrb_hold);
end else if (write_offset == ADDR_DST_BASE) begin
dst_base <= apply_wstrb(dst_base, wdata_hold, wstrb_hold);
end else if (write_offset == ADDR_GROUP_NUM) begin
group_num <= apply_wstrb(group_num, wdata_hold, wstrb_hold);
end else if ((write_offset >= ADDR_A_BASE) && (write_offset < ADDR_B_BASE)) begin
a_data[(write_offset - ADDR_A_BASE) >> 2] <=
apply_wstrb(a_data[(write_offset - ADDR_A_BASE) >> 2], wdata_hold, wstrb_hold);
end else if ((write_offset >= ADDR_B_BASE) && (write_offset < ADDR_C_BASE)) begin
b_data[(write_offset - ADDR_B_BASE) >> 2] <=
apply_wstrb(b_data[(write_offset - ADDR_B_BASE) >> 2], wdata_hold, wstrb_hold);
end else if (write_offset == ADDR_STATUS) begin
if (wdata_hold[1]) done <= 1'b0;
if (wdata_hold[2]) error <= 1'b0;
end else if ((write_offset != ADDR_CTRL) && (write_offset < ADDR_C_BASE)) begin
error <= 1'b1;
end
awaddr_hold[31:0] <= awaddr_hold[31:0] + 32'd4;
end
if (s_axi_arvalid && s_axi_arready) begin
s_axi_rid <= s_axi_arid;
s_axi_rvalid <= 1'b1;
s_axi_rlast <= (s_axi_arlen == 8'd0);
rburst_rem <= s_axi_arlen;
rburst_active <= (s_axi_arlen > 8'd0);
if (read_offset == ADDR_CTRL) begin
s_axi_rdata <= 32'b0;
end else if (read_offset == ADDR_STATUS) begin
s_axi_rdata <= {29'b0, error, done, busy};
end else if (read_offset == ADDR_SRC_BASE) begin
s_axi_rdata <= src_base;
end else if (read_offset == ADDR_DST_BASE) begin
s_axi_rdata <= dst_base;
end else if (read_offset == ADDR_GROUP_NUM) begin
s_axi_rdata <= group_num;
end else if ((read_offset >= ADDR_A_BASE) && (read_offset < ADDR_B_BASE)) begin
s_axi_rdata <= a_data[(read_offset - ADDR_A_BASE) >> 2];
end else if ((read_offset >= ADDR_B_BASE) && (read_offset < ADDR_C_BASE)) begin
s_axi_rdata <= b_data[(read_offset - ADDR_B_BASE) >> 2];
end else if ((read_offset >= ADDR_C_BASE) && (read_offset < (ADDR_C_BASE + 12'h0c0))) begin
s_axi_rdata <= c_read_word((read_offset - ADDR_C_BASE) >> 2);
end else begin
s_axi_rdata <= 32'b0;
end
end else if (s_axi_rvalid && s_axi_rready) begin
if (rburst_active && rburst_rem > 8'd0) begin
s_axi_rdata <= c_read_word((s_axi_araddr[11:0] - ADDR_C_BASE + 12'd4*(s_axi_arlen - rburst_rem + 8'd1)) >> 2);
rburst_rem <= rburst_rem - 8'd1;
s_axi_rlast <= (rburst_rem == 8'd1);
if (rburst_rem == 8'd1) begin
s_axi_rvalid <= 1'b0;
rburst_active <= 1'b0;
case (w_state)
W_IDLE: begin
s_axi_awready <= 1'b1;
if (s_axi_awvalid && s_axi_awready) begin
s_axi_awready <= 1'b0;
s_axi_wready <= 1'b1;
w_addr <= s_axi_awaddr;
w_id <= s_axi_awid;
w_burst <= s_axi_awburst;
w_state <= W_DATA;
end
end
end else begin
s_axi_rvalid <= 1'b0;
end
end
W_DATA: begin
if (s_axi_wvalid && s_axi_wready) begin
// ===== 写地址译码与数据装载 =====
if (w_offset == ADDR_CTRL) begin
if (s_axi_wdata[0]) req_0 <= 1'b1; // 启动 Bank 0
if (s_axi_wdata[1]) req_1 <= 1'b1; // 启动 Bank 1
end
else if (w_offset == ADDR_STATUS) begin
if (s_axi_wdata[1]) done <= 1'b0; // 写 1 清零 Done (兼容旧版TB)
if (s_axi_wdata[2]) error <= 1'b0; // 写 1 清零 Error
end
else if (w_offset >= ADDR_B0_A && w_offset < ADDR_B0_B)
b0_a[(w_offset - ADDR_B0_A) >> 2] <= apply_wstrb(b0_a[(w_offset - ADDR_B0_A) >> 2], s_axi_wdata, s_axi_wstrb);
else if (w_offset >= ADDR_B0_B && w_offset < ADDR_B0_C)
b0_b[(w_offset - ADDR_B0_B) >> 2] <= apply_wstrb(b0_b[(w_offset - ADDR_B0_B) >> 2], s_axi_wdata, s_axi_wstrb);
else if (w_offset >= ADDR_B1_A && w_offset < ADDR_B1_B)
b1_a[(w_offset - ADDR_B1_A) >> 2] <= apply_wstrb(b1_a[(w_offset - ADDR_B1_A) >> 2], s_axi_wdata, s_axi_wstrb);
else if (w_offset >= ADDR_B1_B && w_offset < ADDR_B1_C)
b1_b[(w_offset - ADDR_B1_B) >> 2] <= apply_wstrb(b1_b[(w_offset - ADDR_B1_B) >> 2], s_axi_wdata, s_axi_wstrb);
else begin
// 访问无效地址,拉高 error 标志
error <= 1'b1;
end
if (start_fire) begin
busy <= 1'b1;
done <= 1'b0;
error <= 1'b0;
calc_index <= 4'd0;
end else if (busy) begin
c_data[calc_index] <= calc_c_element(calc_index);
if (calc_index == 4'd15) begin
busy <= 1'b0;
done <= 1'b1;
calc_index <= 4'd0;
end else begin
calc_index <= calc_index + 4'd1;
// ===== Burst 地址累加 =====
if (w_burst == 2'b01) w_addr <= w_addr + 32'd4;
if (s_axi_wlast) begin
s_axi_wready <= 1'b0;
s_axi_bvalid <= 1'b1;
w_state <= W_RESP;
end
end
end
W_RESP: begin
if (s_axi_bvalid && s_axi_bready) begin
s_axi_bvalid <= 1'b0;
s_axi_awready <= 1'b1;
w_state <= W_IDLE;
end
end
default: w_state <= W_IDLE;
endcase
end
end
// ==========================================
// AXI Read 通道逻辑 (支持 INCR Burst)
// ==========================================
localparam R_IDLE = 2'd0;
localparam R_DATA = 2'd1;
reg [1:0] r_state;
reg [31:0] r_addr;
reg [7:0] r_cnt;
reg [7:0] r_len;
reg [1:0] r_burst;
wire [11:0] r_offset = r_addr[11:0];
assign s_axi_rresp = 2'b00;
wire [5:0] c0_offset = (r_offset - ADDR_B0_C) >> 2;
wire [3:0] c0_elem = c0_offset / 3;
wire [1:0] c0_part = c0_offset % 3;
wire [65:0] target_c0 = b0_c[c0_elem];
wire [5:0] c1_offset = (r_offset - ADDR_B1_C) >> 2;
wire [3:0] c1_elem = c1_offset / 3;
wire [1:0] c1_part = c1_offset % 3;
wire [65:0] target_c1 = b1_c[c1_elem];
always @(posedge clk) begin
if (!resetn) begin
r_state <= R_IDLE;
s_axi_arready <= 1'b1;
s_axi_rvalid <= 1'b0;
s_axi_rlast <= 1'b0;
s_axi_rid <= 5'b0;
end else begin
case (r_state)
R_IDLE: begin
s_axi_arready <= 1'b1;
if (s_axi_arvalid && s_axi_arready) begin
s_axi_arready <= 1'b0;
r_addr <= s_axi_araddr;
r_len <= s_axi_arlen;
r_burst <= s_axi_arburst;
r_cnt <= 8'd0;
s_axi_rvalid <= 1'b1;
s_axi_rlast <= (s_axi_arlen == 8'd0);
s_axi_rid <= s_axi_arid;
r_state <= R_DATA;
end
end
R_DATA: begin
if (s_axi_rvalid && s_axi_rready) begin
if (r_cnt == r_len) begin
s_axi_rvalid <= 1'b0;
s_axi_rlast <= 1'b0;
s_axi_arready <= 1'b1;
r_state <= R_IDLE;
end else begin
r_cnt <= r_cnt + 8'd1;
s_axi_rlast <= (r_cnt + 8'd1 == r_len);
if (r_burst == 2'b01) r_addr <= r_addr + 32'd4;
end
end
end
default: r_state <= R_IDLE;
endcase
end
end
// 组合逻辑准备 Read 数据
always @(*) begin
s_axi_rdata = 32'b0;
if (r_state == R_DATA || (r_state == R_IDLE && s_axi_arvalid)) begin
wire [11:0] current_r_offset = (r_state == R_DATA) ? r_offset : s_axi_araddr[11:0];
if (current_r_offset == ADDR_CTRL)
s_axi_rdata = {30'b0, req_1, req_0};
else if (current_r_offset == ADDR_STATUS)
s_axi_rdata = {29'b0, error, done, busy}; // 完美兼容旧 TB
else if (current_r_offset >= ADDR_B0_A && current_r_offset < ADDR_B0_B)
s_axi_rdata = b0_a[(current_r_offset - ADDR_B0_A) >> 2];
else if (current_r_offset >= ADDR_B0_B && current_r_offset < ADDR_B0_C)
s_axi_rdata = b0_b[(current_r_offset - ADDR_B0_B) >> 2];
else if (current_r_offset >= ADDR_B0_C && current_r_offset < (ADDR_B0_C + 12'h0C0)) begin
if (c0_part == 2'd0) s_axi_rdata = target_c0[31:0];
else if (c0_part == 2'd1) s_axi_rdata = target_c0[63:32];
else s_axi_rdata = {30'b0, target_c0[65:64]};
end
else if (current_r_offset >= ADDR_B1_A && current_r_offset < ADDR_B1_B)
s_axi_rdata = b1_a[(current_r_offset - ADDR_B1_A) >> 2];
else if (current_r_offset >= ADDR_B1_B && current_r_offset < ADDR_B1_C)
s_axi_rdata = b1_b[(current_r_offset - ADDR_B1_B) >> 2];
else if (current_r_offset >= ADDR_B1_C && current_r_offset < (ADDR_B1_C + 12'h0C0)) begin
if (c1_part == 2'd0) s_axi_rdata = target_c1[31:0];
else if (c1_part == 2'd1) s_axi_rdata = target_c1[63:32];
else s_axi_rdata = {30'b0, target_c1[65:64]};
end
end
end
end
// ==========================================
// 计算引擎 (Compute FSM)
// ==========================================
reg [3:0] calc_idx;
wire [1:0] row = calc_idx[3:2];
wire [1:0] col = calc_idx[1:0];
reg mult_start;
wire mult_done [0:3];
wire [63:0] mult_p [0:3];
reg [31:0] m_in_a [0:3];
reg [31:0] m_in_b [0:3];
genvar i;
generate
for (i = 0; i < 4; i = i + 1) begin : mult_inst
shift_add_mult u_mult (
.clk (clk),
.rstn (resetn),
.start (mult_start),
.a (m_in_a[i]),
.b (m_in_b[i]),
.p (mult_p[i]),
.done (mult_done[i])
);
end
endgenerate
always @(posedge clk) begin
if (!resetn) begin
state <= C_IDLE;
busy <= 1'b0;
cur_bank <= 1'b0;
calc_idx <= 4'd0;
mult_start <= 1'b0;
calc_done_pulse <= 1'b0;
end else begin
calc_done_pulse <= 1'b0;
case (state)
C_IDLE: begin
calc_idx <= 4'd0;
if (req_0) begin
cur_bank <= 1'b0;
busy <= 1'b1;
state <= C_CALC;
end else if (req_1) begin
cur_bank <= 1'b1;
busy <= 1'b1;
state <= C_CALC;
end else begin
busy <= 1'b0;
end
end
C_CALC: begin
if (cur_bank == 1'b0) begin
m_in_a[0] <= b0_a[{row, 2'd0}]; m_in_b[0] <= b0_b[{2'd0, col}];
m_in_a[1] <= b0_a[{row, 2'd1}]; m_in_b[1] <= b0_b[{2'd1, col}];
m_in_a[2] <= b0_a[{row, 2'd2}]; m_in_b[2] <= b0_b[{2'd2, col}];
m_in_a[3] <= b0_a[{row, 2'd3}]; m_in_b[3] <= b0_b[{2'd3, col}];
end else begin
m_in_a[0] <= b1_a[{row, 2'd0}]; m_in_b[0] <= b1_b[{2'd0, col}];
m_in_a[1] <= b1_a[{row, 2'd1}]; m_in_b[1] <= b1_b[{2'd1, col}];
m_in_a[2] <= b1_a[{row, 2'd2}]; m_in_b[2] <= b1_b[{2'd2, col}];
m_in_a[3] <= b1_a[{row, 2'd3}]; m_in_b[3] <= b1_b[{2'd3, col}];
end
mult_start <= 1'b1;
state <= C_WAIT_MULT;
end
C_WAIT_MULT: begin
mult_start <= 1'b0;
// 4个乘法器同时启动,监控第一个即可
if (mult_done[0]) begin
state <= C_ACCUM;
end
end
C_ACCUM: begin
if (cur_bank == 1'b0) begin
b0_c[calc_idx] <= {2'b0, mult_p[0]} + {2'b0, mult_p[1]} +
{2'b0, mult_p[2]} + {2'b0, mult_p[3]};
end else begin
b1_c[calc_idx] <= {2'b0, mult_p[0]} + {2'b0, mult_p[1]} +
{2'b0, mult_p[2]} + {2'b0, mult_p[3]};
end
if (calc_idx == 4'd15) begin
calc_done_pulse <= 1'b1;
busy <= 1'b0;
state <= C_IDLE;
end else begin
calc_idx <= calc_idx + 4'd1;
state <= C_CALC;
end
end
endcase
end
end
endmodule

View File

@@ -0,0 +1,49 @@
module shift_add_mult (
input clk,
input rstn,
input start,
input [31:0] a,
input [31:0] b,
output reg [63:0] p,
output reg done
);
reg [5:0] cnt;
reg [63:0] a_reg;
reg [31:0] b_reg;
reg busy;
always @(posedge clk) begin
if (!rstn) begin
busy <= 1'b0;
done <= 1'b0;
p <= 64'b0;
cnt <= 6'd0;
a_reg<= 64'b0;
b_reg<= 32'b0;
end else begin
if (start && !busy) begin
a_reg <= {32'b0, a};
b_reg <= b;
p <= 64'b0;
cnt <= 6'd0;
busy <= 1'b1;
done <= 1'b0;
end else if (busy) begin
if (cnt == 6'd32) begin
busy <= 1'b0;
done <= 1'b1;
end else begin
// 如果 b 的当前最低位为 1,则累加 a
if (b_reg[0]) begin
p <= p + a_reg;
end
a_reg <= a_reg << 1;
b_reg <= b_reg >> 1;
cnt <= cnt + 6'd1;
end
end else begin
done <= 1'b0;
end
end
end
endmodule

View File

@@ -1,17 +1,12 @@
/**
* user-sample - CIC-IEC 2026 Stage 3 Evaluation Program
*
* (UPDATED FOR PING-PONG BUFFER ARCHITECTURE)
*
* Reads N groups of 4x4 unsigned matrix multiplications from ExtRAM,
* computes C = A x B via the hardware matmul IP, writes results back,
* computes CRC32 over the result region, and outputs the required identifiers.
*
* N is controlled by MATMUL_GROUP_NUM (Makefile / CI passes -DMATMUL_GROUP_NUM=5000).
* Local default: 10 (fast testing). For full eval: make MATMUL_GROUP_NUM=5000
*
* Data layout (word offsets in ExtRAM):
* Input: SRC_BASE = 0, N * 32 words
* Result: DST_BASE = SRC_BASE + N * 32, N * 48 words
* CRC32 over result region byte stream (N * 192 bytes, little-endian words)
* computes C = A x B via the hardware matmul IP using Ping-Pong buffering,
* writes results back, computes CRC32 over the result region, and outputs
* the required identifiers.
*/
#include <stdio.h>
@@ -37,15 +32,24 @@ unsigned long CORE_CLOCKS_PER_SEC = 33000000L;
#define RESULT_WORDS (MATMUL_GROUP_NUM * 48)
#define RESULT_BYTES (RESULT_WORDS * 4)
/* ─── MATMUL IP registers ─────────────────────────────────────────── */
/* ─── MATMUL IP registers (Ping-Pong Memory Map) ──────────────────── */
#define MATMUL_BASE 0xbf500000
#define MATMUL_CTRL (MATMUL_BASE + 0x000)
#define MATMUL_STATUS (MATMUL_BASE + 0x004)
#define MATMUL_A_BASE (MATMUL_BASE + 0x020)
#define MATMUL_B_BASE (MATMUL_BASE + 0x060)
#define MATMUL_C_BASE (MATMUL_BASE + 0x0a0)
#define CTRL_START 0x1
// Bank 0 Memory Space
#define MATMUL_B0_A (MATMUL_BASE + 0x100)
#define MATMUL_B0_B (MATMUL_BASE + 0x140)
#define MATMUL_B0_C (MATMUL_BASE + 0x180)
// Bank 1 Memory Space
#define MATMUL_B1_A (MATMUL_BASE + 0x300)
#define MATMUL_B1_B (MATMUL_BASE + 0x340)
#define MATMUL_B1_C (MATMUL_BASE + 0x380)
// Control & Status Bit Masks
#define CTRL_START_B0 0x1
#define CTRL_START_B1 0x2
#define STATUS_BUSY 0x1
#define STATUS_DONE 0x2
#define STATUS_ERROR 0x4
@@ -56,70 +60,38 @@ unsigned long CORE_CLOCKS_PER_SEC = 33000000L;
/* ─── CRC32 table (IEEE 802.3, polynomial 0xEDB88320) ─────────────── */
static const uint32_t crc32_table[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB30A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB30A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
/* ─── Read N words from IP register space ─────────────────────────── */
@@ -136,16 +108,19 @@ static void write_ip(uint32_t base, const volatile uint32_t *src, int n)
REG_WR(base + i * 4, src[i]);
}
/* ─── Poll IP STATUS until done or error ──────────────────────────── */
static void wait_done(void)
/* ─── Poll IP STATUS until done and clear it (W1C) ────────────────── */
static void wait_and_clear_done(void)
{
uint32_t status;
do {
status = REG_RD(MATMUL_STATUS);
} while (!(status & (STATUS_DONE | STATUS_ERROR)));
// Write 1 to Clear the DONE bit so it's ready for the next interrupt/poll
REG_WR(MATMUL_STATUS, STATUS_DONE);
}
/* ─── CRC32 over ExtRAM result region (little-endian word → byte stream) ─ */
/* ─── CRC32 over ExtRAM result region (little-endian byte stream) ─── */
static uint32_t compute_crc32(volatile uint32_t *extram)
{
uint32_t crc = 0xFFFFFFFF;
@@ -164,40 +139,82 @@ static uint32_t compute_crc32(volatile uint32_t *extram)
/* ═══════════════════════════════════════════════════════════════════ */
int main(void)
{
if (MATMUL_GROUP_NUM <= 0) return 0;
volatile uint32_t *extram = (volatile uint32_t *)EXTRAM_BASE;
uint32_t a_buf[16], b_buf[16], c_buf[48];
uint32_t crc;
int g, i;
// Ping-Pong Buffer Register Addresses
uint32_t bank_A[2] = {MATMUL_B0_A, MATMUL_B1_A};
uint32_t bank_B[2] = {MATMUL_B0_B, MATMUL_B1_B};
uint32_t bank_C[2] = {MATMUL_B0_C, MATMUL_B1_C};
uint32_t ctrl_start[2] = {CTRL_START_B0, CTRL_START_B1};
int cur_bank = 0;
/* ── Start marker (must be first output) ──────────────────── */
printf("MATMUL_START\n");
/* ── Process all groups ───────────────────────────────────── */
for (g = 0; g < MATMUL_GROUP_NUM; g++) {
/* ── Prologue: Load and Start the very first group ────────── */
for (i = 0; i < 16; i++) {
a_buf[i] = extram[SRC_BASE + 0 * 32 + i];
b_buf[i] = extram[SRC_BASE + 0 * 32 + 16 + i];
}
write_ip(bank_A[0], a_buf, 16);
write_ip(bank_B[0], b_buf, 16);
// Clear any pending statuses before starting
REG_WR(MATMUL_STATUS, STATUS_DONE | STATUS_ERROR);
REG_WR(MATMUL_CTRL, ctrl_start[0]);
/* ── Main Loop: Pipelined Execution ───────────────────────── */
for (g = 1; g < MATMUL_GROUP_NUM; g++) {
cur_bank = g % 2;
int prev_bank = (g - 1) % 2;
int finished_g = g - 1;
// 1. Prepare/Load data for the NEXT group into the idle bank
// (This happens concurrently while hardware calculates the previous group)
for (i = 0; i < 16; i++) {
a_buf[i] = extram[SRC_BASE + g * 32 + i];
b_buf[i] = extram[SRC_BASE + g * 32 + 16 + i];
}
write_ip(MATMUL_A_BASE, a_buf, 16);
write_ip(MATMUL_B_BASE, b_buf, 16);
REG_WR(MATMUL_STATUS, STATUS_DONE | STATUS_ERROR);
REG_WR(MATMUL_CTRL, CTRL_START);
wait_done();
read_ip(MATMUL_C_BASE, c_buf, 48);
if (g == 1) {
printf("DBG A[0]=%08lx B[0]=%08lx C[0..2]=%08lx %08lx %08lx\n",
(unsigned long)a_buf[0], (unsigned long)b_buf[0],
(unsigned long)c_buf[0], (unsigned long)c_buf[1],
(unsigned long)c_buf[2]);
write_ip(bank_A[cur_bank], a_buf, 16);
write_ip(bank_B[cur_bank], b_buf, 16);
// 2. Wait for the PREVIOUS group hardware calculation to finish
wait_and_clear_done();
// 3. Immediately start the current bank to keep hardware busy
REG_WR(MATMUL_CTRL, ctrl_start[cur_bank]);
// 4. Read the PREVIOUS group's result and write to memory
read_ip(bank_C[prev_bank], c_buf, 48);
// Required DBG prints logic modified to match the pipelined flow
if (finished_g == 1) {
printf("DBG C[0..2]=%08lx %08lx %08lx\n",
(unsigned long)c_buf[0], (unsigned long)c_buf[1], (unsigned long)c_buf[2]);
}
for (i = 0; i < 48; i++)
extram[DST_BASE + g * 48 + i] = c_buf[i];
if (g == 0) {
for (i = 0; i < 48; i++) {
extram[DST_BASE + finished_g * 48 + i] = c_buf[i];
}
if (finished_g == 0) {
printf("DBG wrote C[0]=%08lx @ ExtRAM[%d]\n",
(unsigned long)extram[DST_BASE], DST_BASE);
}
}
/* ── Epilogue: Wait for and read the very last group ──────── */
wait_and_clear_done();
read_ip(bank_C[cur_bank], c_buf, 48);
for (i = 0; i < 48; i++) {
extram[DST_BASE + (MATMUL_GROUP_NUM - 1) * 48 + i] = c_buf[i];
}
/* ── CRC32 over result region ─────────────────────────────── */
crc = compute_crc32(extram);
@@ -206,4 +223,4 @@ int main(void)
printf("MATMUL_DONE\n");
return 0;
}
}