自研PE单元AXI接口记录(3)
接下来就是B矩阵接口的设计了,这个是调试时间最久,最鲁棒,但是还有待优化的一个接口。目的是在复盘中找出一套实现接口高鲁棒性的方法论。
一、设计前观测
B矩阵虽然和A矩阵都是只读AXI接口。但是在波形上要求是完全不一样的,A矩阵的各个通道都是从0递增,步长为1,具有极大规律性,不具有普适性。B矩阵的ptr通道是每次读两个连续的地址,col和val通道每次都一定的范围内递增1,但是也常常跳地址。并且由于这种特性,还要考虑4kb边界问题。不过还是幸好三个通道在一定范围内递增1,不然要是一直跨地址,使用AXI4无疑是浪费性能。
二、设计思路
由于ptr每次读两个连续地址,因此每次都得等ptr_addrv到来时再发出读请求。由于每次访问HBM都要消耗上百周期时间,这导致了带宽利用率特别低(貌似没有什么好办法,有可能加Cache能改善一下),col和val就是偶尔发一个新地址,之后的地址就保持递增1,然后在连续递增几次后又会突然跳转地址。面对这种情况,就得对input的addr进行检测,检测是否为下一地址,如果是就继续读fifo,如果否,就重新向HBM发出读请求,并且把fifo的数据清空。这里有条件可以加缓存,因为地址可能被复用。这里不加缓存。
三、工程实现
1、在这里使用FIFO IP核来降低布线拥挤,
点击查看代码
new_fifo b_ptr_fifo (.clk(M_AXI_ACLK), .srst(!M_AXI_ARESETN || ptr_fifo_flush), .din(M_AXI_RDATA), .wr_en(M_AXI_RVALID && (M_AXI_RID == 2'b10)), .rd_en(b_ptr_fifo_re), .dout(b_ptr_data), .empty(b_ptr_empty), .wr_rst_busy(b_ptr_wr_busy), .rd_rst_busy(b_ptr_rd_busy), .data_count(b_ptr_cnt), .full(b_ptr_full));
点击查看代码
`timescale 1 ns / 1 ps
module pe_system_v1_0_m01_axi #
(
parameter integer C_M_AXI_ID_WIDTH = 2,
parameter integer C_M_AXI_ADDR_WIDTH = 32,
parameter integer C_M_AXI_DATA_WIDTH = 32
)
(
// --- Matrix B PE 接口 ---
input wire [31:0] b_ptr_addr, input wire b_ptr_addr_v,
input wire [31:0] b_col_addr, input wire b_col_addr_v,
input wire [31:0] b_val_addr, input wire b_val_addr_v,
output wire [31:0] b_ptr_data, output wire b_ptr_data_v,
output wire [31:0] b_col_data, output wire b_col_data_v,
output wire [31:0] b_val_data, output wire b_val_data_v,
input wire [31:0] b_ptr_base_addr,
input wire [31:0] b_col_base_addr,
input wire [31:0] b_val_base_addr,
// --- AXI 总线 ---
input wire M_AXI_ACLK, input wire M_AXI_ARESETN,
output wire [1:0] M_AXI_ARID, output wire [31:0] M_AXI_ARADDR,
output wire [7:0] M_AXI_ARLEN, output wire [2:0] M_AXI_ARSIZE,
output wire M_AXI_ARVALID, input wire M_AXI_ARREADY,
input wire [1:0] M_AXI_RID, input wire [31:0] M_AXI_RDATA,
input wire [1:0] M_AXI_RRESP,
input wire M_AXI_RLAST,
input wire M_AXI_RVALID, output wire M_AXI_RREADY,
output wire [1:0] M_AXI_ARBURST,
output wire TXN_DONE, output reg ERROR
);
// --- 静态 AXI 信号 ---
assign M_AXI_ARSIZE = 3'b010; // 4 Bytes
assign M_AXI_ARBURST = 2'b01; // INCR mode
assign M_AXI_RREADY = 1'b1;
assign TXN_DONE = 1'b0;
// ==============================================================================
// 1. 信号声明
// ==============================================================================
reg [31:0] last_ptr, last_col, last_val;
reg [31:0] ptr_addr_reg, col_addr_reg, val_addr_reg;
reg pending_ptr, pending_col, pending_val;
reg ptr_fifo_flush, col_fifo_flush, val_fifo_flush;
reg [2:0] data_used;
reg [7:0] read_index;
reg ptr_is_jumping, col_is_jumping, val_is_jumping;
wire b_ptr_empty, b_col_empty, b_val_empty;
// 跳转检测:统一使用流水线第二拍寄存器
wire ptr_addr_false = b_ptr_addrv_r2 && (b_ptr_empty || (b_ptr_addr_t != last_ptr + 1));
wire col_addr_false = b_col_addrv_r2 && (b_col_empty || (b_col_addr_t != last_col + 1));
wire val_addr_false = b_val_addrv_r2 && (b_val_empty || (b_val_addr_t != last_val + 1));
// ==============================================================================
// 2. 跳转识别逻辑 (组合逻辑)
// ==============================================================================
always @(*) begin
if (ptr_addr_false) ptr_is_jumping = 1'b1;
else if (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b10) && (read_index == 0)) ptr_is_jumping = 1'b0;
else ptr_is_jumping = ptr_is_jumping;
end
always @(*) begin
if (col_addr_false) col_is_jumping = 1'b1;
else if (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b01) && (read_index == 0)) col_is_jumping = 1'b0;
else col_is_jumping = col_is_jumping;
end
always @(*) begin
if (val_addr_false) val_is_jumping = 1'b1;
else if (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b00) && (read_index == 0)) val_is_jumping = 1'b0;
else val_is_jumping = val_is_jumping;
end
// ==============================================================================
// 3. FIFO 控制逻辑
// ==============================================================================
wire b_ptr_fifo_re = (b_ptr_addrv_r2 || pending_ptr) && !b_ptr_empty && !ptr_fifo_flush && !ptr_is_jumping && !b_ptr_rd_busy;
wire b_col_fifo_re = (b_col_addrv_r2 || pending_col) && !b_col_empty && !col_fifo_flush && !col_is_jumping && !b_col_rd_busy;
wire b_val_fifo_re = (b_val_addrv_r2 || pending_val) && !b_val_empty && !val_fifo_flush && !val_is_jumping && !b_val_rd_busy;
assign b_ptr_data_v = b_ptr_fifo_re;
assign b_col_data_v = b_col_fifo_re;
assign b_val_data_v = b_val_fifo_re;
always @(posedge M_AXI_ACLK) begin
if (!M_AXI_ARESETN || (M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST))
read_index <= 8'd0;
else if (M_AXI_RVALID && M_AXI_RREADY)
read_index <= read_index + 1'b1;
end
always @(posedge M_AXI_ACLK) begin
if (!M_AXI_ARESETN) begin
last_ptr <= 0; last_col <= 0; last_val <= 0;
data_used <= 3'b0;
end else begin
if (ptr_addr_false) last_ptr <= b_ptr_addr_t; else if (b_ptr_addrv_r2) last_ptr <= last_ptr + 1'b1;
if (col_addr_false) last_col <= b_col_addr_t; else if (b_col_addrv_r2) last_col <= last_col + 1'b1;
if (val_addr_false) last_val <= b_val_addr_t; else if (b_val_addrv_r2) last_val <= last_val + 1'b1;
if (ptr_addr_false) data_used[0] <= 1'b0;
else if (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b10) && (read_index == 0)) data_used[0] <= 1'b1;
if (col_addr_false) data_used[1] <= 1'b0;
else if (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b01) && (read_index == 0)) data_used[1] <= 1'b1;
if (val_addr_false) data_used[2] <= 1'b0;
else if (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b00) && (read_index == 0)) data_used[2] <= 1'b1;
end
end
always @(posedge M_AXI_ACLK) begin
if (!M_AXI_ARESETN) begin
pending_ptr <= 0; ptr_addr_reg <= 0; ptr_fifo_flush <= 0;
pending_col <= 0; col_addr_reg <= 0; col_fifo_flush <= 0;
pending_val <= 0; val_addr_reg <= 0; val_fifo_flush <= 0;
ERROR <= 0;
end else begin
if (ptr_addr_false) begin pending_ptr <= 1'b1; ptr_addr_reg <= b_ptr_addr_t; ptr_fifo_flush <= 1'b1; end
else if (b_ptr_fifo_re) pending_ptr <= 1'b0;
if (!ptr_active && ptr_fifo_flush) ptr_fifo_flush <= 1'b0;
if (col_addr_false) begin pending_col <= 1'b1; col_addr_reg <= b_col_addr_t; col_fifo_flush <= 1'b1; end
else if (b_col_fifo_re) pending_col <= 1'b0;
if (!col_active && col_fifo_flush) col_fifo_flush <= 1'b0;
if (val_addr_false) begin pending_val <= 1'b1; val_addr_reg <= b_val_addr_t; val_fifo_flush <= 1'b1; end
else if (b_val_fifo_re) pending_val <= 1'b0;
if (!val_active && val_fifo_flush) val_fifo_flush <= 1'b0;
end
end
// ==============================================================================
// 4. AXI 读状态机流水线 (阻塞赋值)
// ==============================================================================
parameter [1:0] IDLE = 2'b00, ISSUE = 2'b10;
reg [1:0] mst_exec_state;
reg [31:0] araddr_r; reg [7:0] arlen_r; reg [1:0] arid_r; reg arvalid_r;
// 内部流水线寄存器定义
reg [31:0] b_ptr_addr_final, b_ptr_addr_s, b_ptr_addr_t, b_ptr_addr_final_reg;
reg [31:0] b_col_addr_final, b_col_addr_s, b_col_addr_t, b_col_addr_final_reg;
reg [31:0] b_val_addr_final, b_val_addr_s, b_val_addr_t, b_val_addr_final_reg;
reg [11:0] b_ptr_limit, b_col_limit, b_val_limit;
reg b_ptr_addrv_r1, b_ptr_addrv_r2, b_col_addrv_r1, b_col_addrv_r2, b_val_addrv_r1, b_val_addrv_r2;
reg [7:0] b_arlen_ptr_final, b_arlen_col_final, b_arlen_val_final;
// --- 流水线周期 1:阻塞计算 ---
always @(posedge M_AXI_ACLK) begin
if (!M_AXI_ARESETN) begin
b_ptr_addr_final = 0; b_ptr_limit = 0; b_ptr_addrv_r1 <= 0; b_ptr_addr_s <= 0;
b_col_addr_final = 0; b_col_limit = 0; b_col_addrv_r1 <= 0; b_col_addr_s <= 0;
b_val_addr_final = 0; b_val_limit = 0; b_val_addrv_r1 <= 0; b_val_addr_s <= 0;
end else begin
// Ptr
b_ptr_addr_final = b_ptr_base_addr + ((b_ptr_addr_v ? b_ptr_addr : ptr_addr_reg) << 2);
b_ptr_limit = (12'hFFF - b_ptr_addr_final[11:0]) >> 2;
b_ptr_addrv_r1 <= b_ptr_addr_v;
b_ptr_addr_s <= b_ptr_addr;
// Col
b_col_addr_final = b_col_base_addr + ((b_col_addr_v ? b_col_addr : col_addr_reg) << 2);
b_col_limit = (12'hFFF - b_col_addr_final[11:0]) >> 2;
b_col_addrv_r1 <= b_col_addr_v;
b_col_addr_s <= b_col_addr;
// Val
b_val_addr_final = b_val_base_addr + ((b_val_addr_v ? b_val_addr : val_addr_reg) << 2);
b_val_limit = (12'hFFF - b_val_addr_final[11:0]) >> 2;
b_val_addrv_r1 <= b_val_addr_v;
b_val_addr_s <= b_val_addr;
end
end
// --- 流水线周期 2:打拍寄存 ---
always @(posedge M_AXI_ACLK) begin
if (!M_AXI_ARESETN) begin
b_arlen_ptr_final <= 0; b_ptr_addr_final_reg <= 0; b_ptr_addrv_r2 <= 0; b_ptr_addr_t <= 0;
b_arlen_col_final <= 0; b_col_addr_final_reg <= 0; b_col_addrv_r2 <= 0; b_col_addr_t <= 0;
b_arlen_val_final <= 0; b_val_addr_final_reg <= 0; b_val_addrv_r2 <= 0; b_val_addr_t <= 0;
end else begin
b_arlen_ptr_final <= (8'd255 > b_ptr_limit) ? b_ptr_limit[7:0] : 8'd255;
b_ptr_addr_final_reg <= b_ptr_addr_final;
b_ptr_addrv_r2 <= b_ptr_addrv_r1;
b_ptr_addr_t <= b_ptr_addr_s;
b_arlen_col_final <= (8'd255 > b_col_limit) ? b_col_limit[7:0] : 8'd255;
b_col_addr_final_reg <= b_col_addr_final;
b_col_addrv_r2 <= b_col_addrv_r1;
b_col_addr_t <= b_col_addr_s;
b_arlen_val_final <= (8'd255 > b_val_limit) ? b_val_limit[7:0] : 8'd255;
b_val_addr_final_reg <= b_val_addr_final;
b_val_addrv_r2 <= b_val_addrv_r1;
b_val_addr_t <= b_val_addr_s;
end
end
// --- AXI 读状态机 (使用流水线输出) ---
always @(posedge M_AXI_ACLK) begin
if (!M_AXI_ARESETN) begin
mst_exec_state <= IDLE; arvalid_r <= 0; arid_r <= 0; arlen_r <= 0; araddr_r <= 0;
end else begin
case (mst_exec_state)
IDLE: begin
if (!arvalid_r) begin
if ((b_ptr_addrv_r2 || pending_ptr) && (ptr_is_jumping || b_ptr_empty) && (!ptr_active) && !b_ptr_wr_busy && !ptr_addr_false && !ptr_fifo_flush && (b_ptr_addr_issued != b_ptr_addr_final_reg)) begin
arid_r <= 2'b10; arlen_r <= b_arlen_ptr_final; araddr_r <= b_ptr_addr_final_reg; mst_exec_state <= ISSUE;
end
else if ((b_col_addrv_r2 || pending_col) && (col_is_jumping || b_col_empty) && (!col_active) && !b_col_wr_busy && !col_addr_false && !col_fifo_flush && (b_col_addr_issued != b_col_addr_final_reg)) begin
arid_r <= 2'b01; arlen_r <= b_arlen_col_final; araddr_r <= b_col_addr_final_reg; mst_exec_state <= ISSUE;
end
else if ((b_val_addrv_r2 || pending_val) && (val_is_jumping || b_val_empty) && (!val_active) && !b_val_wr_busy && !val_addr_false && !val_fifo_flush && (b_val_addr_issued != b_val_addr_final_reg)) begin
arid_r <= 2'b00; arlen_r <= b_arlen_val_final; araddr_r <= b_val_addr_final_reg; mst_exec_state <= ISSUE;
end
end
end
ISSUE: begin
arvalid_r <= 1'b1;
if (M_AXI_ARREADY && arvalid_r) begin arvalid_r <= 1'b0; mst_exec_state <= IDLE; end
end
endcase
end
end
// --- 三路地址契约锁 (issued 寄存器) ---
reg [31:0] b_ptr_addr_issued;
reg [31:0] b_col_addr_issued;
reg [31:0] b_val_addr_issued;
always @(posedge M_AXI_ACLK) begin
if (!M_AXI_ARESETN) begin
b_ptr_addr_issued <= 32'hFFFFFFFF;
b_col_addr_issued <= 32'hFFFFFFFF;
b_val_addr_issued <= 32'hFFFFFFFF;
end
else begin
if(b_ptr_fifo_re || ptr_addr_false || b_col_fifo_re || col_addr_false || b_val_fifo_re || val_addr_false)begin
if(b_ptr_fifo_re || ptr_addr_false)begin
b_ptr_addr_issued <= 32'hFFFFFFFF;
end
if(b_col_fifo_re || col_addr_false)begin
b_col_addr_issued <= 32'hFFFFFFFF;
end
if(b_val_fifo_re || val_addr_false)begin
b_val_addr_issued <= 32'hFFFFFFFF;
end
end
else begin
if(!arvalid_r && (b_ptr_addrv_r2 || pending_ptr) && (ptr_is_jumping || b_ptr_empty) && (!ptr_active) && !b_ptr_wr_busy && !ptr_addr_false && !ptr_fifo_flush && (b_ptr_addr_issued != b_ptr_addr_final_reg) && (mst_exec_state == IDLE))begin
b_ptr_addr_issued <= b_ptr_addr_final_reg;
end
else if(!arvalid_r && (b_col_addrv_r2 || pending_col) && (col_is_jumping || b_col_empty) && (!col_active) && !b_col_wr_busy && !col_addr_false && !col_fifo_flush && (b_col_addr_issued != b_col_addr_final_reg)&&(mst_exec_state == IDLE))begin
b_col_addr_issued <= b_col_addr_final_reg;
end
else if(!arvalid_r && (b_val_addrv_r2 || pending_val) && (val_is_jumping || b_val_empty) && (!val_active) && !b_val_wr_busy && !val_addr_false && !val_fifo_flush && (b_val_addr_issued != b_val_addr_final_reg)&&(mst_exec_state == IDLE))begin
b_val_addr_issued <= b_val_addr_final_reg;
end
end
end
end
assign M_AXI_ARID = arid_r;
assign M_AXI_ARADDR = araddr_r;
assign M_AXI_ARLEN = arlen_r;
assign M_AXI_ARVALID = arvalid_r;
// ==============================================================================
// 5. FIFO IP 实例化
// ==============================================================================
wire [10:0] b_ptr_cnt, b_col_cnt, b_val_cnt;
wire b_ptr_full, b_col_full, b_val_full;
wire b_ptr_wr_busy, b_col_wr_busy, b_val_wr_busy;
wire b_ptr_rd_busy, b_col_rd_busy, b_val_rd_busy;
new_fifo b_ptr_fifo (.clk(M_AXI_ACLK), .srst(!M_AXI_ARESETN || ptr_fifo_flush), .din(M_AXI_RDATA), .wr_en(M_AXI_RVALID && (M_AXI_RID == 2'b10)), .rd_en(b_ptr_fifo_re), .dout(b_ptr_data), .empty(b_ptr_empty), .wr_rst_busy(b_ptr_wr_busy), .rd_rst_busy(b_ptr_rd_busy), .data_count(b_ptr_cnt), .full(b_ptr_full));
new_fifo b_col_fifo (.clk(M_AXI_ACLK), .srst(!M_AXI_ARESETN || col_fifo_flush), .din(M_AXI_RDATA), .wr_en(M_AXI_RVALID && (M_AXI_RID == 2'b01)), .rd_en(b_col_fifo_re), .dout(b_col_data), .empty(b_col_empty), .wr_rst_busy(b_col_wr_busy), .rd_rst_busy(b_col_rd_busy), .data_count(b_col_cnt), .full(b_col_full));
new_fifo b_val_fifo (.clk(M_AXI_ACLK), .srst(!M_AXI_ARESETN || val_fifo_flush), .din(M_AXI_RDATA), .wr_en(M_AXI_RVALID && (M_AXI_RID == 2'b00)), .rd_en(b_val_fifo_re), .dout(b_val_data), .empty(b_val_empty), .wr_rst_busy(b_val_wr_busy), .rd_rst_busy(b_val_rd_busy), .data_count(b_val_cnt), .full(b_val_full));
// ==============================================================================
// 6. 在途计数器逻辑
// ==============================================================================
reg [1:0] ptr_flight_cnt, col_flight_cnt, val_flight_cnt;
always @(posedge M_AXI_ACLK) begin
if (!M_AXI_ARESETN) {ptr_flight_cnt, col_flight_cnt, val_flight_cnt} <= 6'b0;
else begin
case ({ (M_AXI_ARVALID && M_AXI_ARREADY && (M_AXI_ARID == 2'b10)), (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b10) && M_AXI_RLAST) })
2'b10: ptr_flight_cnt <= ptr_flight_cnt + 1'b1;
2'b01: ptr_flight_cnt <= ptr_flight_cnt - 1'b1;
endcase
case ({ (M_AXI_ARVALID && M_AXI_ARREADY && (M_AXI_ARID == 2'b01)), (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b01) && M_AXI_RLAST) })
2'b10: col_flight_cnt <= col_flight_cnt + 1'b1;
2'b01: col_flight_cnt <= col_flight_cnt - 1'b1;
endcase
case ({ (M_AXI_ARVALID && M_AXI_ARREADY && (M_AXI_ARID == 2'b00)), (M_AXI_RVALID && M_AXI_RREADY && (M_AXI_RID == 2'b00) && M_AXI_RLAST) })
2'b10: val_flight_cnt <= val_flight_cnt + 1'b1;
2'b01: val_flight_cnt <= val_flight_cnt - 1'b1;
endcase
end
end
wire ptr_active = (ptr_flight_cnt > 2'b0);
wire col_active = (col_flight_cnt > 2'b0);
wire val_active = (val_flight_cnt > 2'b0);
endmodule
浙公网安备 33010602011771号