自研PE单元AXI接口记录(2)
紧接着上一篇,这期记录我实现PE单元C矩阵的AXI接口的过程。
一、设计前
PE的C矩阵信号端为(outputc_ptr_addr ,c_ptr_addr_v ,c_data_v,c_data),并且也是地址递增,结合AXI4特性,我们应该怎么做呢
二、设计思路
这里就简要带过了,发出写使能时存入fifo,然后在almost_full时发出写请求,这里写请求必须独立发出了,幸好datav频率没那么高,不然超出ping-pong buffer的fifo发出写请求是一个很危险的事。
还要注意的是,如果我们这里使用的base_addr都是以0x000结尾的,因此只要按照固定2的指数个突发长度读或者写,就不会触发4kb边界报错。但是我们要注意pe计算结束时必须清空fifo,所以写请求条件除了almost_full还必须加上pe_done。
三、工程代码实现(让ai缩短了)
点击查看代码
`timescale 1 ns / 1 ps
module pe_system_v1_0_m02_axi #
(
parameter integer C_M_AXI_BURST_LEN = 256,
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 C PE Interface ---
input wire [C_M_AXI_ADDR_WIDTH-1:0] c_ptr_addr,
input wire c_ptr_addr_v,
input wire [C_M_AXI_ADDR_WIDTH-1:0] c_col_addr,
input wire c_col_addr_v,
input wire [C_M_AXI_ADDR_WIDTH-1:0] c_val_addr,
input wire c_val_addr_v,
input wire [C_M_AXI_DATA_WIDTH-1:0] c_ptr_data,
input wire c_ptr_data_v,
input wire [C_M_AXI_DATA_WIDTH-1:0] c_col_data,
input wire c_col_data_v,
input wire [C_M_AXI_DATA_WIDTH-1:0] c_val_data,
input wire c_val_data_v,
input wire [C_M_AXI_ADDR_WIDTH-1:0] c_ptr_base_addr,
input wire [C_M_AXI_ADDR_WIDTH-1:0] c_col_base_addr,
input wire [C_M_AXI_ADDR_WIDTH-1:0] c_val_base_addr,
input wire pe_done,
// --- AXI4 写物理接口 ---
input wire M_AXI_ACLK,
input wire M_AXI_ARESETN,
output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_AWID,
output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR,
output wire [7 : 0] M_AXI_AWLEN,
output wire [2 : 0] M_AXI_AWSIZE,
output wire [1 : 0] M_AXI_AWBURST,
output wire M_AXI_AWLOCK,
output wire [3 : 0] M_AXI_AWCACHE,
output wire [2 : 0] M_AXI_AWPROT,
output wire [3 : 0] M_AXI_AWQOS,
output wire M_AXI_AWVALID,
input wire M_AXI_AWREADY,
output wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_WDATA,
output wire [C_M_AXI_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB,
output wire M_AXI_WLAST,
output wire M_AXI_WVALID,
input wire M_AXI_WREADY,
input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_BID,
input wire [1 : 0] M_AXI_BRESP,
input wire M_AXI_BVALID,
output wire M_AXI_BREADY,
output wire TXN_DONE,
output reg ERROR
);
// --- 内部寄存器定义 ---
parameter [1:0] IDLE = 2'b00, BUSY = 2'b01;
reg [1:0] mst_exec_state;
reg [C_M_AXI_ADDR_WIDTH-1 : 0] axi_awaddr_reg;
reg axi_awvalid;
reg [7:0] axi_awlen_reg;
reg [C_M_AXI_ID_WIDTH-1 : 0] current_awid;
reg [7:0] write_index;
reg burst_write_active;
reg w_active; // 新增:控制 W 通道数据流的开启
// 三路独立偏移量 (复刻 M00 逻辑)
reg [C_M_AXI_ADDR_WIDTH-1:0] ptr_offset, col_offset, val_offset;
// FIFO 信号声明
wire [10:0] c_ptr_data_cnt, c_col_data_cnt, c_val_data_cnt;
wire c_ptr_fifo_rd_en, c_col_fifo_rd_en, c_val_fifo_rd_en;
wire [31:0] c_ptr_fifo_dout, c_col_fifo_dout, c_val_fifo_dout;
wire c_ptr_empty, c_col_empty, c_val_empty;
wire c_ptr_almost_full, c_col_almost_full, c_val_almost_full;
// --- AXI 物理接口赋值 ---
assign M_AXI_AWID = current_awid;
assign M_AXI_AWADDR = axi_awaddr_reg;
assign M_AXI_AWLEN = axi_awlen_reg;
assign M_AXI_AWSIZE = 3'b010; // 4 bytes
assign M_AXI_AWBURST = 2'b01;
assign M_AXI_AWLOCK = 1'b0;
assign M_AXI_AWCACHE = 4'b0011;
assign M_AXI_AWPROT = 3'h0;
assign M_AXI_AWQOS = 4'h0;
assign M_AXI_AWVALID = axi_awvalid;
assign M_AXI_WSTRB = 4'b1111;
assign M_AXI_BREADY = 1'b1;
assign TXN_DONE = pe_done && c_ptr_empty && c_col_empty && c_val_empty && !burst_write_active;
// --- W 通道核心控制 (解决 STABLE 错误) ---
// 只有在 w_active 期间且对应 FIFO 不为空时,才允许拉高 WVALID
assign M_AXI_WVALID = w_active &&
((current_awid == 2'b00 && !c_ptr_empty) ||
(current_awid == 2'b01 && !c_col_empty) ||
(current_awid == 2'b10 && !c_val_empty));
assign M_AXI_WDATA = (current_awid == 2'b00) ? c_ptr_fifo_dout :
(current_awid == 2'b01) ? c_col_fifo_dout : c_val_fifo_dout;
assign M_AXI_WLAST = (write_index == axi_awlen_reg);
// FIFO 读使能:只有总线真正握手成功(收走数据)时,才从 FIFO 弹出下一个
// 这保证了 empty 信号只在时钟沿更新,且 WVALID 不会在 WREADY 为低时掉下来
assign c_ptr_fifo_rd_en = (M_AXI_WVALID && M_AXI_WREADY && (current_awid == 2'b00));
assign c_col_fifo_rd_en = (M_AXI_WVALID && M_AXI_WREADY && (current_awid == 2'b01));
assign c_val_fifo_rd_en = (M_AXI_WVALID && M_AXI_WREADY && (current_awid == 2'b10));
// --- 地址偏移量自增 (由 AW 握手驱动) ---
always @(posedge M_AXI_ACLK) begin
if (!M_AXI_ARESETN) begin
ptr_offset <= 0; col_offset <= 0; val_offset <= 0;
end else if (M_AXI_AWVALID && M_AXI_AWREADY) begin
case(current_awid)
2'b00: ptr_offset <= ptr_offset + ((axi_awlen_reg + 1) << 2);
2'b01: col_offset <= col_offset + ((axi_awlen_reg + 1) << 2);
2'b10: val_offset <= val_offset + ((axi_awlen_reg + 1) << 2);
endcase
end
end
// --- 状态机控制 ---
always @(posedge M_AXI_ACLK) begin
if (!M_AXI_ARESETN) begin
mst_exec_state <= IDLE;
axi_awvalid <= 1'b0;
axi_awaddr_reg <= 0;
axi_awlen_reg <= 0;
current_awid <= 0;
end else begin
case (mst_exec_state)
IDLE: begin
if (!burst_write_active && !axi_awvalid) begin
if (c_ptr_almost_full || (pe_done && !c_ptr_empty)) begin
current_awid <= 2'b00;
axi_awaddr_reg <= c_ptr_base_addr + ptr_offset;
axi_awlen_reg <= (c_ptr_data_cnt >= 256) ? 8'd255 : (c_ptr_data_cnt > 0 ? c_ptr_data_cnt - 1 : 8'd0);
axi_awvalid <= 1'b1;
mst_exec_state <= BUSY;
end else if (c_col_almost_full || (pe_done && !c_col_empty)) begin
current_awid <= 2'b01;
axi_awaddr_reg <= c_col_base_addr + col_offset;
axi_awlen_reg <= (c_col_data_cnt >= 256) ? 8'd255 : (c_col_data_cnt > 0 ? c_col_data_cnt - 1 : 8'd0);
axi_awvalid <= 1'b1;
mst_exec_state <= BUSY;
end else if (c_val_almost_full || (pe_done && !c_val_empty)) begin
current_awid <= 2'b10;
axi_awaddr_reg <= c_val_base_addr + val_offset;
axi_awlen_reg <= (c_val_data_cnt >= 256) ? 8'd255 : (c_val_data_cnt > 0 ? c_val_data_cnt - 1 : 8'd0);
axi_awvalid <= 1'b1;
mst_exec_state <= BUSY;
end
end
end
BUSY: begin
if (M_AXI_AWREADY && axi_awvalid) axi_awvalid <= 1'b0;
// 当收到最后一个数据的写响应 BVALID 后,才返回 IDLE
if (!burst_write_active && !axi_awvalid) mst_exec_state <= IDLE;
end
endcase
end
end
// 写数据活跃状态:AW握手开始,WLAST握手结束
always @(posedge M_AXI_ACLK) begin
if (!M_AXI_ARESETN) w_active <= 0;
else if (axi_awvalid && M_AXI_AWREADY) w_active <= 1;
else if (M_AXI_WVALID && M_AXI_WREADY && M_AXI_WLAST) w_active <= 0;
end
// 整个写突发活跃状态:从 AWVALID 到 BVALID
always @(posedge M_AXI_ACLK) begin
if (!M_AXI_ARESETN) burst_write_active <= 0;
else if (axi_awvalid && M_AXI_AWREADY) burst_write_active <= 1;
else if (M_AXI_BVALID && M_AXI_BREADY) burst_write_active <= 0;
end
// 写索引计数器
always @(posedge M_AXI_ACLK) begin
if (!M_AXI_ARESETN || (axi_awvalid && M_AXI_AWREADY)) begin
write_index <= 0;
end else if (M_AXI_WVALID && M_AXI_WREADY) begin
write_index <= write_index + 1;
end
end
// --- FIFO 实例化集群 ---
// ==========================================================
// --- Matrix C FIFO 实例化集群 (替换 sync_fifo) ---
// ==========================================================
// C_PTR FIFO
new_fifo c_ptr_fifo (
.clk (M_AXI_ACLK),
.srst (!M_AXI_ARESETN), // 高电平复位
.din (c_ptr_data),
.wr_en (c_ptr_data_v),
.rd_en (c_ptr_fifo_rd_en),
.dout (c_ptr_fifo_dout),
.empty (c_ptr_empty),
.full (), // 留空
.almost_full (), // 留空(改用 prog_full)
.almost_empty (),
.valid (),
.data_count (c_ptr_data_cnt),
.prog_full (c_ptr_almost_full), // 对接逻辑用的 almost_full
.prog_empty (),
.wr_rst_busy (),
.rd_rst_busy ()
);
// C_COL FIFO
new_fifo c_col_fifo (
.clk (M_AXI_ACLK),
.srst (!M_AXI_ARESETN),
.din (c_col_data),
.wr_en (c_col_data_v),
.rd_en (c_col_fifo_rd_en),
.dout (c_col_fifo_dout),
.empty (c_col_empty),
.full (),
.almost_full (),
.almost_empty (),
.valid (),
.data_count (c_col_data_cnt),
.prog_full (c_col_almost_full),
.prog_empty (),
.wr_rst_busy (),
.rd_rst_busy ()
);
// C_VAL FIFO
new_fifo c_val_fifo (
.clk (M_AXI_ACLK),
.srst (!M_AXI_ARESETN),
.din (c_val_data),
.wr_en (c_val_data_v),
.rd_en (c_val_fifo_rd_en),
.dout (c_val_fifo_dout),
.empty (c_val_empty),
.full (),
.almost_full (),
.almost_empty (),
.valid (),
.data_count (c_val_data_cnt),
.prog_full (c_val_almost_full),
.prog_empty (),
.wr_rst_busy (),
.rd_rst_busy ()
);
endmodule
浙公网安备 33010602011771号