Merge main (user mods) into no-fft

This commit is contained in:
2026-06-25 00:43:53 +08:00
11 changed files with 2521 additions and 448 deletions

32
rtl/ip/fft/Butterfly.v Normal file
View File

@@ -0,0 +1,32 @@
//----------------------------------------------------------------------
// Butterfly: Add/Sub and Scaling
//----------------------------------------------------------------------
module Butterfly #(
parameter WIDTH = 16,
parameter RH = 0 // Round Half Up
)(
input signed [WIDTH-1:0] x0_re, // Input Data #0 (Real)
input signed [WIDTH-1:0] x0_im, // Input Data #0 (Imag)
input signed [WIDTH-1:0] x1_re, // Input Data #1 (Real)
input signed [WIDTH-1:0] x1_im, // Input Data #1 (Imag)
output signed [WIDTH-1:0] y0_re, // Output Data #0 (Real)
output signed [WIDTH-1:0] y0_im, // Output Data #0 (Imag)
output signed [WIDTH-1:0] y1_re, // Output Data #1 (Real)
output signed [WIDTH-1:0] y1_im // Output Data #1 (Imag)
);
wire signed [WIDTH:0] add_re, add_im, sub_re, sub_im;
// Add/Sub
assign add_re = x0_re + x1_re;
assign add_im = x0_im + x1_im;
assign sub_re = x0_re - x1_re;
assign sub_im = x0_im - x1_im;
// Scaling
assign y0_re = (add_re + RH) >>> 1;
assign y0_im = (add_im + RH) >>> 1;
assign y1_re = (sub_re + RH) >>> 1;
assign y1_im = (sub_im + RH) >>> 1;
endmodule

34
rtl/ip/fft/DelayBuffer.v Normal file
View File

@@ -0,0 +1,34 @@
//----------------------------------------------------------------------
// DelayBuffer: Generate Constant Delay
//----------------------------------------------------------------------
module DelayBuffer #(
parameter DEPTH = 32,
parameter WIDTH = 16
)(
input clock, // Master Clock
input [WIDTH-1:0] di_re, // Data Input (Real)
input [WIDTH-1:0] di_im, // Data Input (Imag)
output [WIDTH-1:0] do_re, // Data Output (Real)
output [WIDTH-1:0] do_im // Data Output (Imag)
);
reg [WIDTH-1:0] buf_re[0:DEPTH-1];
reg [WIDTH-1:0] buf_im[0:DEPTH-1];
integer n;
// Shift Buffer
/* verilator lint_off BLKLOOPINIT */
always @(posedge clock) begin
for (n = DEPTH-1; n > 0; n = n - 1) begin
buf_re[n] <= buf_re[n-1];
buf_im[n] <= buf_im[n-1];
end
buf_re[0] <= di_re;
buf_im[0] <= di_im;
end
/* verilator lint_on BLKLOOPINIT */
assign do_re = buf_re[DEPTH-1];
assign do_im = buf_im[DEPTH-1];
endmodule

89
rtl/ip/fft/FFT1024_32B.v Normal file
View File

@@ -0,0 +1,89 @@
//----------------------------------------------------------------------
// FFT: 1024-Point FFT Using Radix-2^2 Single-Path Delay Feedback
//----------------------------------------------------------------------
module FFT #(
parameter WIDTH = 32
)(
input clock, // Master Clock
input reset, // Active High Asynchronous Reset
input di_en, // Input Data Enable
input [WIDTH-1:0] di_re, // Input Data (Real)
input [WIDTH-1:0] di_im, // Input Data (Imag)
output do_en, // Output Data Enable
output [WIDTH-1:0] do_re, // Output Data (Real)
output [WIDTH-1:0] do_im // Output Data (Imag)
);
//----------------------------------------------------------------------
// Data must be input consecutively in natural order.
// The result is scaled to 1/N and output in bit-reversed order.
//----------------------------------------------------------------------
wire su1_do_en;
wire[WIDTH-1:0] su1_do_re;
wire[WIDTH-1:0] su1_do_im;
wire su2_do_en;
wire[WIDTH-1:0] su2_do_re;
wire[WIDTH-1:0] su2_do_im;
wire su3_do_en;
wire[WIDTH-1:0] su3_do_re;
wire[WIDTH-1:0] su3_do_im;
wire su4_do_en;
wire[WIDTH-1:0] su4_do_re;
wire[WIDTH-1:0] su4_do_im;
SdfUnit #(.N(1024),.M(1024),.WIDTH(WIDTH)) SU1 (
.clock (clock ), // i
.reset (reset ), // i
.di_en (di_en ), // i
.di_re (di_re ), // i
.di_im (di_im ), // i
.do_en (su1_do_en ), // o
.do_re (su1_do_re ), // o
.do_im (su1_do_im ) // o
);
SdfUnit #(.N(1024),.M(256),.WIDTH(WIDTH)) SU2 (
.clock (clock ), // i
.reset (reset ), // i
.di_en (su1_do_en ), // i
.di_re (su1_do_re ), // i
.di_im (su1_do_im ), // i
.do_en (su2_do_en ), // o
.do_re (su2_do_re ), // o
.do_im (su2_do_im ) // o
);
SdfUnit #(.N(1024),.M(64),.WIDTH(WIDTH)) SU3 (
.clock (clock ), // i
.reset (reset ), // i
.di_en (su2_do_en ), // i
.di_re (su2_do_re ), // i
.di_im (su2_do_im ), // i
.do_en (su3_do_en ), // o
.do_re (su3_do_re ), // o
.do_im (su3_do_im ) // o
);
SdfUnit #(.N(1024),.M(16),.WIDTH(WIDTH)) SU4 (
.clock (clock ), // i
.reset (reset ), // i
.di_en (su3_do_en ), // i
.di_re (su3_do_re ), // i
.di_im (su3_do_im ), // i
.do_en (su4_do_en ), // o
.do_re (su4_do_re ), // o
.do_im (su4_do_im ) // o
);
SdfUnit #(.N(1024),.M(4),.WIDTH(WIDTH)) SU5 (
.clock (clock ), // i
.reset (reset ), // i
.di_en (su4_do_en ), // i
.di_re (su4_do_re ), // i
.di_im (su4_do_im ), // i
.do_en (do_en ), // o
.do_re (do_re ), // o
.do_im (do_im ) // o
);
endmodule

35
rtl/ip/fft/Multiply.v Normal file
View File

@@ -0,0 +1,35 @@
//----------------------------------------------------------------------
// Multiply: Complex Multiplier
//----------------------------------------------------------------------
module Multiply #(
parameter WIDTH = 16
)(
input signed [WIDTH-1:0] a_re,
input signed [WIDTH-1:0] a_im,
input signed [WIDTH-1:0] b_re,
input signed [WIDTH-1:0] b_im,
output signed [WIDTH-1:0] m_re,
output signed [WIDTH-1:0] m_im
);
wire signed [WIDTH*2-1:0] arbr, arbi, aibr, aibi;
wire signed [WIDTH-1:0] sc_arbr, sc_arbi, sc_aibr, sc_aibi;
// Signed Multiplication
assign arbr = a_re * b_re;
assign arbi = a_re * b_im;
assign aibr = a_im * b_re;
assign aibi = a_im * b_im;
// Scaling
assign sc_arbr = arbr >>> (WIDTH-1);
assign sc_arbi = arbi >>> (WIDTH-1);
assign sc_aibr = aibr >>> (WIDTH-1);
assign sc_aibi = aibi >>> (WIDTH-1);
// Sub/Add
// These sub/add may overflow if unnormalized data is input.
assign m_re = sc_arbr - sc_aibi;
assign m_im = sc_arbi + sc_aibr;
endmodule

277
rtl/ip/fft/SdfUnit.v Normal file
View File

@@ -0,0 +1,277 @@
//----------------------------------------------------------------------
// SdfUnit: Radix-2^2 Single-Path Delay Feedback Unit for N-Point FFT
//----------------------------------------------------------------------
module SdfUnit #(
parameter N = 64, // Number of FFT Point
parameter M = 64, // Twiddle Resolution
parameter WIDTH = 16 // Data Bit Length
)(
input clock, // Master Clock
input reset, // Active High Asynchronous Reset
input di_en, // Input Data Enable
input [WIDTH-1:0] di_re, // Input Data (Real)
input [WIDTH-1:0] di_im, // Input Data (Imag)
output do_en, // Output Data Enable
output [WIDTH-1:0] do_re, // Output Data (Real)
output [WIDTH-1:0] do_im // Output Data (Imag)
);
// log2 constant function
function integer log2;
input integer x;
integer value;
begin
value = x-1;
for (log2=0; value>0; log2=log2+1)
value = value>>1;
end
endfunction
localparam LOG_N = log2(N); // Bit Length of N
localparam LOG_M = log2(M); // Bit Length of M
//----------------------------------------------------------------------
// Internal Regs and Nets
//----------------------------------------------------------------------
// 1st Butterfly
reg [LOG_N-1:0] di_count; // Input Data Count
wire bf1_bf; // Butterfly Add/Sub Enable
wire[WIDTH-1:0] bf1_x0_re; // Data #0 to Butterfly (Real)
wire[WIDTH-1:0] bf1_x0_im; // Data #0 to Butterfly (Imag)
wire[WIDTH-1:0] bf1_x1_re; // Data #1 to Butterfly (Real)
wire[WIDTH-1:0] bf1_x1_im; // Data #1 to Butterfly (Imag)
wire[WIDTH-1:0] bf1_y0_re; // Data #0 from Butterfly (Real)
wire[WIDTH-1:0] bf1_y0_im; // Data #0 from Butterfly (Imag)
wire[WIDTH-1:0] bf1_y1_re; // Data #1 from Butterfly (Real)
wire[WIDTH-1:0] bf1_y1_im; // Data #1 from Butterfly (Imag)
wire[WIDTH-1:0] db1_di_re; // Data to DelayBuffer (Real)
wire[WIDTH-1:0] db1_di_im; // Data to DelayBuffer (Imag)
wire[WIDTH-1:0] db1_do_re; // Data from DelayBuffer (Real)
wire[WIDTH-1:0] db1_do_im; // Data from DelayBuffer (Imag)
wire[WIDTH-1:0] bf1_sp_re; // Single-Path Data Output (Real)
wire[WIDTH-1:0] bf1_sp_im; // Single-Path Data Output (Imag)
reg bf1_sp_en; // Single-Path Data Enable
reg [LOG_N-1:0] bf1_count; // Single-Path Data Count
wire bf1_start; // Single-Path Output Trigger
wire bf1_end; // End of Single-Path Data
wire bf1_mj; // Twiddle (-j) Enable
reg [WIDTH-1:0] bf1_do_re; // 1st Butterfly Output Data (Real)
reg [WIDTH-1:0] bf1_do_im; // 1st Butterfly Output Data (Imag)
// 2nd Butterfly
reg bf2_bf; // Butterfly Add/Sub Enable
wire[WIDTH-1:0] bf2_x0_re; // Data #0 to Butterfly (Real)
wire[WIDTH-1:0] bf2_x0_im; // Data #0 to Butterfly (Imag)
wire[WIDTH-1:0] bf2_x1_re; // Data #1 to Butterfly (Real)
wire[WIDTH-1:0] bf2_x1_im; // Data #1 to Butterfly (Imag)
wire[WIDTH-1:0] bf2_y0_re; // Data #0 from Butterfly (Real)
wire[WIDTH-1:0] bf2_y0_im; // Data #0 from Butterfly (Imag)
wire[WIDTH-1:0] bf2_y1_re; // Data #1 from Butterfly (Real)
wire[WIDTH-1:0] bf2_y1_im; // Data #1 from Butterfly (Imag)
wire[WIDTH-1:0] db2_di_re; // Data to DelayBuffer (Real)
wire[WIDTH-1:0] db2_di_im; // Data to DelayBuffer (Imag)
wire[WIDTH-1:0] db2_do_re; // Data from DelayBuffer (Real)
wire[WIDTH-1:0] db2_do_im; // Data from DelayBuffer (Imag)
wire[WIDTH-1:0] bf2_sp_re; // Single-Path Data Output (Real)
wire[WIDTH-1:0] bf2_sp_im; // Single-Path Data Output (Imag)
reg bf2_sp_en; // Single-Path Data Enable
reg [LOG_N-1:0] bf2_count; // Single-Path Data Count
reg bf2_start; // Single-Path Output Trigger
wire bf2_end; // End of Single-Path Data
reg [WIDTH-1:0] bf2_do_re; // 2nd Butterfly Output Data (Real)
reg [WIDTH-1:0] bf2_do_im; // 2nd Butterfly Output Data (Imag)
reg bf2_do_en; // 2nd Butterfly Output Data Enable
// Multiplication
wire[1:0] tw_sel; // Twiddle Select (2n/n/3n)
wire[LOG_N-3:0] tw_num; // Twiddle Number (n)
wire[LOG_N-1:0] tw_addr; // Twiddle Table Address
wire[WIDTH-1:0] tw_re; // Twiddle Factor (Real)
wire[WIDTH-1:0] tw_im; // Twiddle Factor (Imag)
reg mu_en; // Multiplication Enable
wire[WIDTH-1:0] mu_a_re; // Multiplier Input (Real)
wire[WIDTH-1:0] mu_a_im; // Multiplier Input (Imag)
wire[WIDTH-1:0] mu_m_re; // Multiplier Output (Real)
wire[WIDTH-1:0] mu_m_im; // Multiplier Output (Imag)
reg [WIDTH-1:0] mu_do_re; // Multiplication Output Data (Real)
reg [WIDTH-1:0] mu_do_im; // Multiplication Output Data (Imag)
reg mu_do_en; // Multiplication Output Data Enable
//----------------------------------------------------------------------
// 1st Butterfly
//----------------------------------------------------------------------
always @(posedge clock or posedge reset) begin
if (reset) begin
di_count <= {LOG_N{1'b0}};
end else begin
di_count <= di_en ? (di_count + 1'b1) : {LOG_N{1'b0}};
end
end
assign bf1_bf = di_count[LOG_M-1];
// Set unknown value x for verification
assign bf1_x0_re = bf1_bf ? db1_do_re : {WIDTH{1'bx}};
assign bf1_x0_im = bf1_bf ? db1_do_im : {WIDTH{1'bx}};
assign bf1_x1_re = bf1_bf ? di_re : {WIDTH{1'bx}};
assign bf1_x1_im = bf1_bf ? di_im : {WIDTH{1'bx}};
Butterfly #(.WIDTH(WIDTH),.RH(0)) BF1 (
.x0_re (bf1_x0_re ), // i
.x0_im (bf1_x0_im ), // i
.x1_re (bf1_x1_re ), // i
.x1_im (bf1_x1_im ), // i
.y0_re (bf1_y0_re ), // o
.y0_im (bf1_y0_im ), // o
.y1_re (bf1_y1_re ), // o
.y1_im (bf1_y1_im ) // o
);
DelayBuffer #(.DEPTH(2**(LOG_M-1)),.WIDTH(WIDTH)) DB1 (
.clock (clock ), // i
.di_re (db1_di_re ), // i
.di_im (db1_di_im ), // i
.do_re (db1_do_re ), // o
.do_im (db1_do_im ) // o
);
assign db1_di_re = bf1_bf ? bf1_y1_re : di_re;
assign db1_di_im = bf1_bf ? bf1_y1_im : di_im;
assign bf1_sp_re = bf1_bf ? bf1_y0_re : bf1_mj ? db1_do_im : db1_do_re;
assign bf1_sp_im = bf1_bf ? bf1_y0_im : bf1_mj ? -db1_do_re : db1_do_im;
always @(posedge clock or posedge reset) begin
if (reset) begin
bf1_sp_en <= 1'b0;
bf1_count <= {LOG_N{1'b0}};
end else begin
bf1_sp_en <= bf1_start ? 1'b1 : bf1_end ? 1'b0 : bf1_sp_en;
bf1_count <= bf1_sp_en ? (bf1_count + 1'b1) : {LOG_N{1'b0}};
end
end
assign bf1_start = (di_count == (2**(LOG_M-1)-1));
assign bf1_end = (bf1_count == (2**LOG_N-1));
assign bf1_mj = (bf1_count[LOG_M-1:LOG_M-2] == 2'd3);
always @(posedge clock) begin
bf1_do_re <= bf1_sp_re;
bf1_do_im <= bf1_sp_im;
end
//----------------------------------------------------------------------
// 2nd Butterfly
//----------------------------------------------------------------------
always @(posedge clock) begin
bf2_bf <= bf1_count[LOG_M-2];
end
// Set unknown value x for verification
assign bf2_x0_re = bf2_bf ? db2_do_re : {WIDTH{1'bx}};
assign bf2_x0_im = bf2_bf ? db2_do_im : {WIDTH{1'bx}};
assign bf2_x1_re = bf2_bf ? bf1_do_re : {WIDTH{1'bx}};
assign bf2_x1_im = bf2_bf ? bf1_do_im : {WIDTH{1'bx}};
// Negative bias occurs when RH=0 and positive bias occurs when RH=1.
// Using both alternately reduces the overall rounding error.
Butterfly #(.WIDTH(WIDTH),.RH(1)) BF2 (
.x0_re (bf2_x0_re ), // i
.x0_im (bf2_x0_im ), // i
.x1_re (bf2_x1_re ), // i
.x1_im (bf2_x1_im ), // i
.y0_re (bf2_y0_re ), // o
.y0_im (bf2_y0_im ), // o
.y1_re (bf2_y1_re ), // o
.y1_im (bf2_y1_im ) // o
);
DelayBuffer #(.DEPTH(2**(LOG_M-2)),.WIDTH(WIDTH)) DB2 (
.clock (clock ), // i
.di_re (db2_di_re ), // i
.di_im (db2_di_im ), // i
.do_re (db2_do_re ), // o
.do_im (db2_do_im ) // o
);
assign db2_di_re = bf2_bf ? bf2_y1_re : bf1_do_re;
assign db2_di_im = bf2_bf ? bf2_y1_im : bf1_do_im;
assign bf2_sp_re = bf2_bf ? bf2_y0_re : db2_do_re;
assign bf2_sp_im = bf2_bf ? bf2_y0_im : db2_do_im;
always @(posedge clock or posedge reset) begin
if (reset) begin
bf2_sp_en <= 1'b0;
bf2_count <= {LOG_N{1'b0}};
end else begin
bf2_sp_en <= bf2_start ? 1'b1 : bf2_end ? 1'b0 : bf2_sp_en;
bf2_count <= bf2_sp_en ? (bf2_count + 1'b1) : {LOG_N{1'b0}};
end
end
always @(posedge clock) begin
bf2_start <= (bf1_count == (2**(LOG_M-2)-1)) & bf1_sp_en;
end
assign bf2_end = (bf2_count == (2**LOG_N-1));
always @(posedge clock) begin
bf2_do_re <= bf2_sp_re;
bf2_do_im <= bf2_sp_im;
end
always @(posedge clock or posedge reset) begin
if (reset) begin
bf2_do_en <= 1'b0;
end else begin
bf2_do_en <= bf2_sp_en;
end
end
//----------------------------------------------------------------------
// Multiplication
//----------------------------------------------------------------------
assign tw_sel[1] = bf2_count[LOG_M-2];
assign tw_sel[0] = bf2_count[LOG_M-1];
assign tw_num = bf2_count << (LOG_N-LOG_M);
assign tw_addr = tw_num * tw_sel;
Twiddle TW (
.clock (clock ), // i
.addr (tw_addr), // i
.tw_re (tw_re ), // o
.tw_im (tw_im ) // o
);
// Multiplication is bypassed when twiddle address is 0.
always @(posedge clock) begin
mu_en <= (tw_addr != {LOG_N{1'b0}});
end
// Set unknown value x for verification
assign mu_a_re = mu_en ? bf2_do_re : {WIDTH{1'bx}};
assign mu_a_im = mu_en ? bf2_do_im : {WIDTH{1'bx}};
Multiply #(.WIDTH(WIDTH)) MU (
.a_re (mu_a_re), // i
.a_im (mu_a_im), // i
.b_re (tw_re ), // i
.b_im (tw_im ), // i
.m_re (mu_m_re), // o
.m_im (mu_m_im) // o
);
always @(posedge clock) begin
mu_do_re <= mu_en ? mu_m_re : bf2_do_re;
mu_do_im <= mu_en ? mu_m_im : bf2_do_im;
end
always @(posedge clock or posedge reset) begin
if (reset) begin
mu_do_en <= 1'b0;
end else begin
mu_do_en <= bf2_do_en;
end
end
// No multiplication required at final stage
assign do_en = (LOG_M == 2) ? bf2_do_en : mu_do_en;
assign do_re = (LOG_M == 2) ? bf2_do_re : mu_do_re;
assign do_im = (LOG_M == 2) ? bf2_do_im : mu_do_im;
endmodule

1064
rtl/ip/fft/Twiddle1024_32B.v Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,383 @@
`define FFT_CSR_ADDR 16'hf000
`define FFT_IN_RE_BASE 16'h1000
`define FFT_IN_IM_BASE 16'h2000
`define FFT_OUT_RE_BASE 16'h3000
`define FFT_OUT_IM_BASE 16'h4000
module fft_controller #(
parameter FFT_LEN = 1024, // FFT处理长度
parameter BRAM_RD_LATENCY = 2 // 适配 BRAM 的读延迟 (通常 Block RAM 开启输出寄存器为 2)
)(
input aclk,
input aresetn,
// AXI Lite / AXI4 Wrapper ports
input [4 :0] s_awid,
input [31:0] s_awaddr,
input [7 :0] s_awlen,
input [2 :0] s_awsize,
input [1 :0] s_awburst,
input s_awlock,
input [3 :0] s_awcache,
input [2 :0] s_awprot,
input s_awvalid,
output reg s_awready,
input [4 :0] s_wid,
input [31:0] s_wdata,
input [3 :0] s_wstrb,
input s_wlast,
input s_wvalid,
output reg s_wready,
output [4 :0] s_bid,
output [1 :0] s_bresp,
output reg s_bvalid,
input s_bready,
input [4 :0] s_arid,
input [31:0] s_araddr,
input [7 :0] s_arlen,
input [2 :0] s_arsize,
input [1 :0] s_arburst,
input s_arlock,
input [3 :0] s_arcache,
input [2 :0] s_arprot,
input s_arvalid,
output reg s_arready,
output [4 :0] s_rid,
output reg [31:0] s_rdata,
output [1 :0] s_rresp,
output reg s_rlast,
output reg s_rvalid,
input s_rready,
output fft_finish
);
// AXI Burst 内部寄存器
reg [31:0] aw_addr_reg;
reg [1 :0] aw_burst_reg;
reg [4 :0] aw_id_reg;
reg [31:0] ar_addr_reg;
reg [7 :0] ar_len_reg;
reg [1 :0] ar_burst_reg;
reg [4 :0] ar_id_reg;
reg [7 :0] ar_cnt;
// 写通道状态机 (AW, W, B)
localparam W_IDLE = 2'd0;
localparam W_DATA = 2'd1;
localparam W_RESP = 2'd2;
reg [1:0] w_state;
always @(posedge aclk) begin
if (~aresetn) begin
w_state <= W_IDLE;
s_awready <= 1'b1;
s_wready <= 1'b0;
s_bvalid <= 1'b0;
aw_addr_reg <= 32'h0;
aw_burst_reg <= 2'h0;
aw_id_reg <= 5'h0;
end else begin
case (w_state)
W_IDLE: begin
s_awready <= 1'b1;
if (s_awvalid && s_awready) begin
s_awready <= 1'b0;
s_wready <= 1'b1;
aw_addr_reg <= s_awaddr;
aw_burst_reg <= s_awburst;
aw_id_reg <= s_awid;
w_state <= W_DATA;
end
end
W_DATA: begin
// 接收到有效写数据
if (s_wvalid && s_wready) begin
// INCR 模式下地址递增
if (aw_burst_reg == 2'b01) begin
aw_addr_reg <= aw_addr_reg + 4;
end
// 若是最后一拍,跳转到发送响应
if (s_wlast) begin
s_wready <= 1'b0;
s_bvalid <= 1'b1;
w_state <= W_RESP;
end
end
end
W_RESP: begin
if (s_bvalid && s_bready) begin
s_bvalid <= 1'b0;
s_awready <= 1'b1; // 准备接收下一次写请求
w_state <= W_IDLE;
end
end
default: w_state <= W_IDLE;
endcase
end
end
// 产生有效的写动作脉冲,用于触发 BRAM 写使能
wire w_active = (w_state == W_DATA) && s_wvalid && s_wready;
assign s_bid = aw_id_reg;
assign s_bresp = 2'b00;
// 读通道状态机(AR, R)
localparam R_IDLE = 2'd0;
localparam R_WAIT = 2'd1;
localparam R_DATA = 2'd2;
reg [1:0] r_state;
reg [3:0] r_wait_cnt;
always @(posedge aclk) begin
if (~aresetn) begin
r_state <= R_IDLE;
s_arready <= 1'b1;
s_rvalid <= 1'b0;
s_rlast <= 1'b0;
ar_addr_reg <= 32'h0;
ar_len_reg <= 8'h0;
ar_burst_reg <= 2'h0;
ar_id_reg <= 5'h0;
ar_cnt <= 8'h0;
r_wait_cnt <= 4'h0;
end else begin
case (r_state)
R_IDLE: begin
s_arready <= 1'b1;
if (s_arvalid && s_arready) begin
s_arready <= 1'b0;
ar_addr_reg <= s_araddr;
ar_len_reg <= s_arlen;
ar_burst_reg <= s_arburst;
ar_id_reg <= s_arid;
ar_cnt <= 8'h0;
r_wait_cnt <= BRAM_RD_LATENCY;
r_state <= R_WAIT; // 进入等待以拉取第一笔 BRAM 数据
end
end
R_WAIT: begin
// 等待 BRAM 固定潜伏期结束
if (r_wait_cnt <= 1) begin
s_rvalid <= 1'b1;
s_rlast <= (ar_cnt == ar_len_reg);
r_state <= R_DATA;
r_wait_cnt <= 4'h0;
end else begin
r_wait_cnt <= r_wait_cnt - 1;
end
end
R_DATA: begin
// 数据被总线成功读取
if (s_rvalid && s_rready) begin
if (ar_cnt == ar_len_reg) begin
// Burst 结束
s_rvalid <= 1'b0;
s_rlast <= 1'b0;
s_arready <= 1'b1;
r_state <= R_IDLE;
end else begin
// 准备读取下一拍数据
ar_cnt <= ar_cnt + 1;
// INCR 模式下地址递增
if (ar_burst_reg == 2'b01) begin
ar_addr_reg <= ar_addr_reg + 4;
end
s_rvalid <= 1'b0;
s_rlast <= 1'b0;
r_wait_cnt <= BRAM_RD_LATENCY;
r_state <= R_WAIT; // 再次进入等待状态
end
end
end
default: r_state <= R_IDLE;
endcase
end
end
assign s_rid = ar_id_reg;
assign s_rresp = 2'b00;
// 地址译码
wire hit_in_re = (aw_addr_reg[15:12] == 4'h1);
wire hit_in_im = (aw_addr_reg[15:12] == 4'h2);
wire hit_out_re = (ar_addr_reg[15:12] == 4'h3);
wire hit_out_im = (ar_addr_reg[15:12] == 4'h4);
// 寄存器控制
reg [3:0] fft_ctrl;
reg [3:0] fft_status;
wire [31:0] fft_csr = {24'h0, fft_ctrl, fft_status};
// 只有发生有效的写握手且地址对应 CSR 时,才写入控制寄存器
wire write_fft_csr = w_active && (aw_addr_reg[15:0] == `FFT_CSR_ADDR);
// 状态机
localparam IDLE = 2'd0;
localparam LOAD = 2'd1;
localparam WAIT_OUT = 2'd2;
localparam DONE = 2'd3;
reg [1:0] state, next_state;
reg [10:0] in_cnt;
reg [10:0] out_cnt;
wire do_en;
wire [31:0] do_re;
wire [31:0] do_im;
always @(posedge aclk) begin
if (~aresetn) state <= IDLE;
else state <= next_state;
end
always @(*) begin
case (state)
IDLE: if (fft_ctrl[0]) next_state = LOAD; else next_state = IDLE;
LOAD: if (in_cnt == FFT_LEN - 1) next_state = WAIT_OUT; else next_state = LOAD;
WAIT_OUT: if (do_en && out_cnt == FFT_LEN - 1) next_state = DONE; else next_state = WAIT_OUT;
DONE: next_state = IDLE;
default: next_state = IDLE;
endcase
end
always @(posedge aclk) begin
if (~aresetn) begin
in_cnt <= 0;
out_cnt <= 0;
end else if (state == IDLE) begin
in_cnt <= 0;
out_cnt <= 0;
end else begin
if (state == LOAD) in_cnt <= in_cnt + 1;
if (do_en) out_cnt <= out_cnt + 1;
end
end
// 更新寄存器与状态
always @(posedge aclk) begin
if (~aresetn) begin
fft_ctrl <= 4'h0;
fft_status <= 4'h0;
end else begin
if (write_fft_csr) begin
fft_ctrl <= s_wdata[7:4];
end else if (state != IDLE) begin
fft_ctrl[0] <= 1'b0;
end
fft_status[0] <= (state != IDLE && state != DONE);
if (write_fft_csr && s_wdata[4]) begin
fft_status[1] <= 1'b0;
end else if (state == DONE) begin
fft_status[1] <= 1'b1;
end
end
end
assign fft_finish = fft_status[1];
// BRAM 接口连线
wire [31:0] in_re_dout;
wire [31:0] in_im_dout;
wire [31:0] out_re_dout;
wire [31:0] out_im_dout;
my_bram0 u_in0(
.clka (aclk),
.ena (w_active && hit_in_re), // 由 burst写逻辑产生
.wea (1'b1),
.addra (aw_addr_reg[11:2]), // 取当前递增的突发写地址
.dina (s_wdata),
.clkb (aclk),
.enb (state == LOAD),
.addrb (in_cnt[9:0]),
.doutb (in_re_dout)
);
my_bram0 u_in1(
.clka (aclk),
.ena (w_active && hit_in_im),
.wea (1'b1),
.addra (aw_addr_reg[11:2]),
.dina (s_wdata),
.clkb (aclk),
.enb (state == LOAD),
.addrb (in_cnt[9:0]),
.doutb (in_im_dout)
);
// 对于输出 BRAM,采用原先的位反转(Bit-reverse)读取逻辑
wire [9:0] b_rd_addr = ar_addr_reg[11:2];
wire [9:0] bit_reversed_index = {
b_rd_addr[0], b_rd_addr[1], b_rd_addr[2], b_rd_addr[3], b_rd_addr[4],
b_rd_addr[5], b_rd_addr[6], b_rd_addr[7], b_rd_addr[8], b_rd_addr[9]
};
my_bram0 u_out0(
.clka (aclk),
.ena (do_en),
.wea (1'b1),
.addra (out_cnt[9:0]),
.dina (do_re),
.clkb (aclk),
.enb (r_state != R_IDLE), // 当处于读突发事务时始终使能 BRAM
.addrb (bit_reversed_index),
.doutb (out_re_dout)
);
my_bram0 u_out1(
.clka (aclk),
.ena (do_en),
.wea (1'b1),
.addra (out_cnt[9:0]),
.dina (do_im),
.clkb (aclk),
.enb (r_state != R_IDLE),
.addrb (bit_reversed_index),
.doutb (out_im_dout)
);
// 支持 1 到 N 拍的读延迟,让 di_en 严格对齐 BRAM 的吐出数据
reg [3:0] di_en_shift;
always @(posedge aclk) begin
if (~aresetn) di_en_shift <= 0;
else di_en_shift <= {di_en_shift[2:0], (state == LOAD)};
end
wire di_en_r = (BRAM_RD_LATENCY == 1) ? di_en_shift[0] :
(BRAM_RD_LATENCY == 2) ? di_en_shift[1] :
(BRAM_RD_LATENCY == 3) ? di_en_shift[2] : di_en_shift[3];
FFT #(
.WIDTH(32)
) u_FFT (
.clock (aclk),
.reset (~aresetn),
.di_en (di_en_r),
.di_re (in_re_dout),
.di_im (in_im_dout),
.do_en (do_en),
.do_re (do_re),
.do_im (do_im)
);
// 读取返回数据选择
wire [31:0] rdata_d =
(ar_addr_reg[15:0] == `FFT_CSR_ADDR) ? fft_csr :
hit_out_re ? out_re_dout :
hit_out_im ? out_im_dout :
32'h0;
always @(*) begin
s_rdata = rdata_d;
end
endmodule

View File

@@ -2,17 +2,16 @@ 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 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,
@@ -20,22 +19,22 @@ module matmul_axi_slave (
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_arlock,
input [3:0] s_axi_arcache,
input [2:0] s_axi_arprot,
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,
@@ -44,334 +43,353 @@ module matmul_axi_slave (
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;
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];
// Bank 0 (可完全当作原来的单缓冲使用)
localparam ADDR_B0_A = 12'h100;
localparam ADDR_B0_B = 12'h140;
localparam ADDR_B0_C = 12'h180;
// ══ Write burst state machine (same pattern as fft_controller) ══
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;
// Bank 1 (供无等待计算扩展使用)
localparam ADDR_B1_A = 12'h300;
localparam ADDR_B1_B = 12'h340;
localparam ADDR_B1_C = 12'h380;
wire [11:0] w_offset = w_addr[11:0];
wire w_active = (w_state == W_DATA) && s_axi_wvalid && s_axi_wready;
wire w_start = (w_offset == ADDR_CTRL) && s_axi_wdata[0] && !busy;
// ==========================================
// 寄存器与数据缓冲
// ==========================================
reg req_0, req_1;
reg busy;
reg done;
reg error;
assign s_axi_bid = w_id;
assign s_axi_bresp = 2'b00;
reg [31:0] b0_a [0:15];
reg [31:0] b0_b [0:15];
reg [65:0] b0_c [0:15];
// ══ Read burst state machine ══
localparam R_IDLE = 2'd0;
localparam R_DATA = 2'd1;
reg [1:0] r_state;
reg [31:0] r_addr;
reg [4:0] r_id;
reg [7:0] r_cnt;
reg [31:0] b1_a [0:15];
reg [31:0] b1_b [0:15];
reg [65:0] b1_c [0:15];
wire [11:0] r_offset = r_addr[11:0];
assign s_axi_rresp = 2'b00;
// ══ Compute engine ══
reg busy;
reg done;
reg error;
reg [3:0] calc_index;
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);
end
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
end
endfunction
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
// ==========================================
// AXI Write 通道逻辑 (支持 INCR Burst)
// ==========================================
localparam W_IDLE = 2'd0;
localparam W_DATA = 2'd1;
localparam W_RESP = 2'd2;
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
reg [1:0] w_state;
reg [31:0] w_addr;
reg [4:0] w_id;
reg [1:0] w_burst;
integer i;
always @(posedge clk) begin
if (!resetn) begin
src_base <= 32'b0;
dst_base <= 32'b0;
group_num <= 32'd1;
w_state <= W_IDLE;
s_axi_awready <= 1'b1;
s_axi_wready <= 1'b0;
s_axi_bvalid <= 1'b0;
w_addr <= 32'b0;
w_id <= 5'b0;
r_state <= R_IDLE;
s_axi_arready <= 1'b1;
s_axi_rvalid <= 1'b0;
s_axi_rlast <= 1'b0;
r_addr <= 32'b0;
r_id <= 5'b0;
r_cnt <= 8'b0;
s_axi_rid <= 5'b0;
s_axi_rdata <= 32'b0;
busy <= 1'b0;
done <= 1'b0;
error <= 1'b0;
calc_index <= 4'b0;
wire [11:0] w_offset = w_addr[11:0];
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
// ═══════════════════════════════════════════════════════
// Write burst FSM (FFT pattern)
// ═══════════════════════════════════════════════════════
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_state <= W_DATA;
end
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
W_DATA: begin
if (s_axi_wvalid && s_axi_wready) begin
// Register write
if (w_offset == ADDR_SRC_BASE) begin
src_base <= apply_wstrb(src_base, s_axi_wdata, s_axi_wstrb);
end else if (w_offset == ADDR_DST_BASE) begin
dst_base <= apply_wstrb(dst_base, s_axi_wdata, s_axi_wstrb);
end else if (w_offset == ADDR_GROUP_NUM) begin
group_num <= apply_wstrb(group_num, s_axi_wdata, s_axi_wstrb);
end else if ((w_offset >= ADDR_A_BASE) && (w_offset < ADDR_B_BASE)) begin
a_data[(w_offset - ADDR_A_BASE) >> 2] <=
apply_wstrb(a_data[(w_offset - ADDR_A_BASE) >> 2], s_axi_wdata, s_axi_wstrb);
end else if ((w_offset >= ADDR_B_BASE) && (w_offset < ADDR_C_BASE)) begin
b_data[(w_offset - ADDR_B_BASE) >> 2] <=
apply_wstrb(b_data[(w_offset - ADDR_B_BASE) >> 2], s_axi_wdata, s_axi_wstrb);
end else if (w_offset == ADDR_STATUS) begin
if (s_axi_wdata[1]) done <= 1'b0;
if (s_axi_wdata[2]) error <= 1'b0;
end else if ((w_offset != ADDR_CTRL) && (w_offset < ADDR_C_BASE)) begin
error <= 1'b1;
end
// Auto-increment address for next beat
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;
case (w_state)
W_IDLE: begin
s_axi_awready <= 1'b1;
w_state <= W_IDLE;
end
end
default: w_state <= W_IDLE;
endcase
// ═══════════════════════════════════════════════════════
// Read burst FSM (FFT pattern, no BRAM latency)
// ═══════════════════════════════════════════════════════
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_id <= s_axi_arid;
r_cnt <= 8'd0;
s_axi_rvalid <= 1'b1;
s_axi_rlast <= (s_axi_arlen == 8'd0);
// Output data for current address
if (r_offset == ADDR_CTRL)
s_axi_rdata <= 32'b0;
else if (r_offset == ADDR_STATUS)
s_axi_rdata <= {29'b0, error, done, busy};
else if (r_offset == ADDR_SRC_BASE)
s_axi_rdata <= src_base;
else if (r_offset == ADDR_DST_BASE)
s_axi_rdata <= dst_base;
else if (r_offset == ADDR_GROUP_NUM)
s_axi_rdata <= group_num;
else if ((r_offset >= ADDR_A_BASE) && (r_offset < ADDR_B_BASE))
s_axi_rdata <= a_data[(r_offset - ADDR_A_BASE) >> 2];
else if ((r_offset >= ADDR_B_BASE) && (r_offset < ADDR_C_BASE))
s_axi_rdata <= b_data[(r_offset - ADDR_B_BASE) >> 2];
else if ((r_offset >= ADDR_C_BASE) && (r_offset < (ADDR_C_BASE + 12'h0c0)))
s_axi_rdata <= c_read_word((r_offset - ADDR_C_BASE) >> 2);
else
s_axi_rdata <= 32'b0;
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 == s_axi_arlen) 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;
r_addr <= r_addr + 32'd4;
s_axi_rlast <= (r_cnt + 8'd1 == s_axi_arlen);
// Output next beat data
if ((r_offset + 12'd4) == ADDR_CTRL)
s_axi_rdata <= 32'b0;
else if ((r_offset + 12'd4) == ADDR_STATUS)
s_axi_rdata <= {29'b0, error, done, busy};
else if ((r_offset + 12'd4) == ADDR_SRC_BASE)
s_axi_rdata <= src_base;
else if ((r_offset + 12'd4) == ADDR_DST_BASE)
s_axi_rdata <= dst_base;
else if ((r_offset + 12'd4) == ADDR_GROUP_NUM)
s_axi_rdata <= group_num;
else if (((r_offset + 12'd4) >= ADDR_A_BASE) && ((r_offset + 12'd4) < ADDR_B_BASE))
s_axi_rdata <= a_data[((r_offset + 12'd4) - ADDR_A_BASE) >> 2];
else if (((r_offset + 12'd4) >= ADDR_B_BASE) && ((r_offset + 12'd4) < ADDR_C_BASE))
s_axi_rdata <= b_data[((r_offset + 12'd4) - ADDR_B_BASE) >> 2];
else if (((r_offset + 12'd4) >= ADDR_C_BASE) && ((r_offset + 12'd4) < (ADDR_C_BASE + 12'h0c0)))
s_axi_rdata <= c_read_word(((r_offset + 12'd4) - ADDR_C_BASE) >> 2);
else
s_axi_rdata <= 32'b0;
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
default: r_state <= R_IDLE;
endcase
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
// ═══════════════════════════════════════════════════════
// Compute engine (unchanged)
// ═══════════════════════════════════════════════════════
if (w_start) 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

@@ -525,7 +525,7 @@ wire [1 :0] confreg_bresp ;
wire confreg_bvalid ;
wire confreg_bready ;
//slave 6 — removed (FFT), tie off
//slave 6 FFT/IFFT
wire [4 :0] fft_arid ;
wire [31:0] fft_araddr ;
wire [7 :0] fft_arlen ;
@@ -535,6 +535,7 @@ wire fft_arlock ;
wire [3 :0] fft_arcache;
wire [2 :0] fft_arprot ;
wire fft_arvalid;
wire fft_arready;
wire [4 :0] fft_rid ;
wire [31:0] fft_rdata ;
wire [1 :0] fft_rresp ;
@@ -550,19 +551,18 @@ wire fft_awlock ;
wire [3 :0] fft_awcache;
wire [2 :0] fft_awprot ;
wire fft_awvalid;
wire fft_awready;
wire [4 :0] fft_wid ;
wire [31:0] fft_wdata ;
wire [3 :0] fft_wstrb ;
wire fft_wlast ;
wire fft_wvalid ;
wire fft_wready ;
wire [4 :0] fft_bid ;
wire [1 :0] fft_bresp ;
wire fft_bvalid ;
wire fft_bready ;
wire fft_finish = 1'b0;
assign fft_arready = 1'b1;
assign fft_awready = 1'b1;
assign fft_wready = 1'b1;
wire fft_finish ;
//slave 7
wire [4 :0] axiOut_7_arid ;
@@ -1078,7 +1078,7 @@ AxiCrossbar_2x8 u_AxiCrossbar_2x8 (
);
// add your code
core_top u_cpu(
.intrpt ({7'b0, confreg_int}), //high active
@@ -1470,15 +1470,47 @@ axi_dvi u_axi_dvi (
.video_blue ( video_blue )
);
// slave 6 (FFT) removed — tie off unused outputs
assign fft_rid = 5'b0;
assign fft_rdata = 32'b0;
assign fft_rresp = 2'b0;
assign fft_rlast = 1'b0;
assign fft_rvalid = 1'b0;
assign fft_bid = 5'b0;
assign fft_bresp = 2'b0;
assign fft_bvalid = 1'b0;
fft_controller u_fft_controller(
.aclk (sys_clk ),
.aresetn (sys_resetn ),
.s_awid (fft_awid ),
.s_awaddr (fft_awaddr ),
.s_awlen (fft_awlen ),
.s_awsize (fft_awsize ),
.s_awburst (fft_awburst ),
.s_awlock (fft_awlock ),
.s_awcache (fft_awcache ),
.s_awprot (fft_awprot ),
.s_awvalid (fft_awvalid ),
.s_awready (fft_awready ),
.s_wid (fft_wid ),
.s_wdata (fft_wdata ),
.s_wstrb (fft_wstrb ),
.s_wlast (fft_wlast ),
.s_wvalid (fft_wvalid ),
.s_wready (fft_wready ),
.s_bid (fft_bid ),
.s_bresp (fft_bresp ),
.s_bvalid (fft_bvalid ),
.s_bready (fft_bready ),
.s_arid (fft_arid ),
.s_araddr (fft_araddr ),
.s_arlen (fft_arlen ),
.s_arsize (fft_arsize ),
.s_arburst (fft_arburst ),
.s_arlock (fft_arlock ),
.s_arcache (fft_arcache ),
.s_arprot (fft_arprot ),
.s_arvalid (fft_arvalid ),
.s_arready (fft_arready ),
.s_rid (fft_rid ),
.s_rdata (fft_rdata ),
.s_rresp (fft_rresp ),
.s_rlast (fft_rlast ),
.s_rvalid (fft_rvalid ),
.s_rready (fft_rready ),
.fft_finish (fft_finish )
);
wire [31:0] cdma_s_awaddr;
wire [2:0] cdma_s_awprot;

View File

@@ -1,166 +1,226 @@
/**
* user-sample — CIC-IEC 2026 Stage 3 (DMA-driven)
* 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 using Ping-Pong buffering,
* writes results back, computes CRC32 over the result region, and outputs
* the required identifiers.
*/
#include <stdio.h>
#include <stdint.h>
#include "dma.h"
/* ─── Configurable group count (default 10 for fast local testing) ─── */
#ifndef MATMUL_GROUP_NUM
#define MATMUL_GROUP_NUM 5000
#endif
/* ─── BSP globals ──────────────────────────────────────────────────── */
/* ─── BSP globals ─────────────────────────────────────────────────── */
unsigned long UART_BASE = 0xbf000000;
unsigned long CONFREG_TIMER_BASE = 0xbf20f100;
unsigned long CONFREG_CLOCKS_PER_SEC = 50000000L;
unsigned long CORE_CLOCKS_PER_SEC = 33000000L;
/* ─── Memory layout ─────────────────────────────────────────────────── */
/* ─── Memory layout ────────────────────────────────────────────────── */
#define EXTRAM_BASE_PHYS 0x1c400000
#define EXTRAM_BASE (0xA0000000 | (EXTRAM_BASE_PHYS & 0x1FFFFFFF))
#define SRC_BASE 0
#define DST_BASE (SRC_BASE + MATMUL_GROUP_NUM * 32)
#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)
/* DMA needs physical addresses (mask high bits) */
#define MATMUL_A_BASE_PHYS ((MATMUL_A_BASE) & 0x1FFFFFFF)
#define MATMUL_C_BASE_PHYS ((MATMUL_C_BASE) & 0x1FFFFFFF)
// 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)
#define INPUT_BYTES_PER_GROUP 128 /* 32 words of A+B */
#define OUTPUT_BYTES_PER_GROUP 192 /* 48 words of C */
// 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)
#define CTRL_START 0x1
// 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
/* ─── Convenience ──────────────────────────────────────────────────── */
/* ─── Convenience macros ──────────────────────────────────────────── */
#define REG_WR(addr, val) (*(volatile uint32_t *)(addr) = (uint32_t)(val))
#define REG_RD(addr) (*(volatile uint32_t *)(addr))
/* ─── CRC32 table ──────────────────────────────────────────────────── */
/* ─── 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, 0x5CB36A04,
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
};
static void wait_done(void)
/* ─── Read N words from IP register space ─────────────────────────── */
static void read_ip(uint32_t base, volatile uint32_t *dst, int n)
{
uint32_t status;
do { status = REG_RD(MATMUL_STATUS); }
while (!(status & (STATUS_DONE | STATUS_ERROR)));
for (int i = 0; i < n; i++)
dst[i] = REG_RD(base + i * 4);
}
int main(void)
/* ─── Write N words to IP register space ──────────────────────────── */
static void write_ip(uint32_t base, const volatile uint32_t *src, int n)
{
volatile uint32_t *extram = (volatile uint32_t *)EXTRAM_BASE;
uint32_t crc;
int g, i;
for (int i = 0; i < n; i++)
REG_WR(base + i * 4, src[i]);
}
printf("MATMUL_START\n");
/* ─── 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)));
for (g = 0; g < MATMUL_GROUP_NUM; g++) {
uint32_t src_phys = EXTRAM_BASE_PHYS + g * INPUT_BYTES_PER_GROUP;
uint32_t dst_phys = EXTRAM_BASE_PHYS + DST_BASE * 4 + g * OUTPUT_BYTES_PER_GROUP;
// Write 1 to Clear the DONE bit so it's ready for the next interrupt/poll
REG_WR(MATMUL_STATUS, STATUS_DONE);
}
/* DMA ch0: ExtRAM → IP (A[16]+B[16] in one burst) */
dma_start_transfer(0, src_phys, MATMUL_A_BASE_PHYS, INPUT_BYTES_PER_GROUP, 0);
dma_wait_polling(0);
/* ─── CRC32 over ExtRAM result region (little-endian byte stream) ─── */
static uint32_t compute_crc32(volatile uint32_t *extram)
{
uint32_t crc = 0xFFFFFFFF;
uint32_t i, w;
/* Start IP */
REG_WR(MATMUL_STATUS, STATUS_DONE | STATUS_ERROR);
REG_WR(MATMUL_CTRL, CTRL_START);
wait_done();
/* DMA ch1: IP → ExtRAM (C[48]) */
dma_start_transfer(1, MATMUL_C_BASE_PHYS, dst_phys, OUTPUT_BYTES_PER_GROUP, 0);
dma_wait_polling(1);
}
/* ── CRC32 from ExtRAM result region ────────────────────────── */
crc = 0xFFFFFFFF;
for (i = 0; i < RESULT_WORDS; i++) {
uint32_t w = extram[DST_BASE + i];
w = extram[DST_BASE + i];
crc = crc32_table[(crc ^ (w & 0xFF)) & 0xFF] ^ (crc >> 8);
crc = crc32_table[(crc ^ ((w >> 8) & 0xFF)) & 0xFF] ^ (crc >> 8);
crc = crc32_table[(crc ^ ((w >> 16) & 0xFF)) & 0xFF] ^ (crc >> 8);
crc = crc32_table[(crc ^ (w >> 24) ) & 0xFF] ^ (crc >> 8);
}
crc ^= 0xFFFFFFFF;
return crc ^ 0xFFFFFFFF;
}
/* ═══════════════════════════════════════════════════════════════════ */
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");
/* ── 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(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 + 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);
/* ── Final markers ────────────────────────────────────────── */
printf("MATMUL_CRC32=%08lx\n", (unsigned long)crc);
printf("MATMUL_DONE\n");
return 0;
}