453 lines
17 KiB
Verilog
453 lines
17 KiB
Verilog
// ═══════════════════════════════════════════════════════
|
||
// 基础时序乘法器模块 (无 DSP,移位累加,32个周期)
|
||
// ═══════════════════════════════════════════════════════
|
||
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
|
||
|
||
|
||
// ═══════════════════════════════════════════════════════
|
||
// 矩阵乘法器顶层模块 (AXI Burst Slave + Ping-Pong)
|
||
// ═══════════════════════════════════════════════════════
|
||
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_awvalid,
|
||
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 reg s_axi_wready,
|
||
|
||
// 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_arvalid,
|
||
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 reg s_axi_rlast,
|
||
output reg s_axi_rvalid,
|
||
input s_axi_rready
|
||
);
|
||
|
||
// ==========================================
|
||
// 内存地址映射 (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 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];
|
||
|
||
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
|
||
|
||
// ==========================================
|
||
// 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;
|
||
|
||
// 前向声明引擎的状态
|
||
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;
|
||
|
||
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
|
||
|
||
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
|
||
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
|
||
|
||
// ===== 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
|
||
|
||
// 修复:将 wire 声明提取到 always 块外部
|
||
wire [11:0] current_r_offset = (r_state == R_DATA) ? r_offset : s_axi_araddr[11:0];
|
||
|
||
// 组合逻辑准备 Read 数据
|
||
always @(*) begin
|
||
s_axi_rdata = 32'b0;
|
||
if (r_state == R_DATA || (r_state == R_IDLE && s_axi_arvalid)) begin
|
||
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
|
||
|
||
// ==========================================
|
||
// 计算引擎 (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 |