User mods: matmul RTL + shift_add_mult + C program
This commit is contained in:
@@ -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
|
||||
49
rtl/ip/matmul/shift_add_mult.v
Normal file
49
rtl/ip/matmul/shift_add_mult.v
Normal 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
|
||||
Reference in New Issue
Block a user