Есть решение по-лучше, чем каждый раз проектировать конвейер на AXI4 Stream с большим риском ошибиться, т.к. все мы - люди.
Проектируете конвейер как обычно, без какого-либо хэндшейка вообще, просто input, output и cen. А потом оборачиваете его в обёртку, которая превращает его в AXI4 Stream.
Работает обёртка без "пузырей", т.е. без потери пропускной способности шины.
Есть четыре правила для конвейера, которые нужно соблюдать, они очень простые:
1. Должна быть возможность остановить конвейер в любой момент без каких-либо поломок и потерь данных. Делается это легко, с помощью clock enable:
always @(posedge clk) begin
if (cen) begin
...
end
end
2. Конвейер должен иметь константную задержку (глубину), которая заранее известна. Параметр PIPE_STAGES.
3. Ширина входа и выхода может быть разной, но постоянной. Это параметры PIPE_DATA_IN_WIDTH и PIPE_DATA_OUT_WIDTH.
4. Если нужно пропускать через конвейер без обработки какие-то квалификаторы данных, то используйте tuser, ширина задаётся через PIPE_QUAL_WIDTH.
Если все правила соблюдаются, то конвейер легко превращается в AXI4 stream с этой обёрткой:
`default_nettype none
`timescale 1ps / 1ps
module axis_pipeliner #
(
parameter integer PIPE_DATA_IN_WIDTH = 32,
parameter integer PIPE_DATA_OUT_WIDTH = 32,
parameter integer PIPE_QUAL_WIDTH = 4,
parameter integer PIPE_STAGES = 8
)
(
input wire axis_aclk,
input wire axis_aresetn,
input wire [PIPE_DATA_IN_WIDTH - 1:0] s_axis_tdata,
input wire [PIPE_QUAL_WIDTH - 1:0] s_axis_tuser,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
output wire [PIPE_DATA_OUT_WIDTH - 1:0] m_axis_tdata,
output wire [PIPE_QUAL_WIDTH - 1:0] m_axis_tuser,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire pipe_cen,
output wire [PIPE_DATA_IN_WIDTH - 1:0] pipe_in_data,
input wire [PIPE_DATA_OUT_WIDTH - 1:0] pipe_out_data
);
/*-------------------------------------------------------------------------------------------------------------------------------------*/
reg [PIPE_STAGES - 1:0] tvalid_pipe = {PIPE_STAGES{1'b0}};
reg [PIPE_STAGES - 1:0] tlast_pipe = {PIPE_STAGES{1'b0}};
always @(posedge axis_aclk) begin
if (~axis_aresetn) begin
tvalid_pipe <= {PIPE_STAGES{1'b0}};
tlast_pipe <= {PIPE_STAGES{1'b0}};
end else begin
if (pipe_cen) begin
tvalid_pipe <= (PIPE_STAGES > 1)? {tvalid_pipe[PIPE_STAGES - 2:0], s_axis_tvalid} : s_axis_tvalid;
tlast_pipe <= (PIPE_STAGES > 1)? {tlast_pipe[PIPE_STAGES - 2:0], s_axis_tlast} : s_axis_tlast;
end
end
end
assign s_axis_tready = s_axis_tvalid & (~tvalid_pipe[PIPE_STAGES - 1] | m_axis_tready);
assign pipe_cen = s_axis_tready | ~tvalid_pipe[PIPE_STAGES - 1] | m_axis_tready;
assign pipe_in_data = s_axis_tdata;
assign m_axis_tdata = pipe_out_data;
assign m_axis_tvalid = tvalid_pipe[PIPE_STAGES - 1];
assign m_axis_tlast = tlast_pipe[PIPE_STAGES - 1];
/*-------------------------------------------------------------------------------------------------------------------------------------*/
genvar i;
generate
for (i = 0; i < PIPE_QUAL_WIDTH; i = i + 1) begin: loop
reg [PIPE_STAGES - 1:0] pipe_gen = {PIPE_STAGES{1'b0}};
always @(posedge axis_aclk) begin
if (pipe_cen) begin
pipe_gen <= (PIPE_STAGES > 1)? {pipe_gen[PIPE_STAGES - 2:0], s_axis_tuser[i]} : s_axis_tuser[i];
end
end
assign m_axis_tuser[i] = pipe_gen[PIPE_STAGES - 1];
end
endgenerate
endmodule
/*-------------------------------------------------------------------------------------------------------------------------------------*/
`default_nettype wire