1
0
mirror of https://github.com/corundum/corundum.git synced 2025-01-16 08:12:53 +08:00
corundum/rtl/axis_ram_switch.v
2021-05-31 01:32:02 -07:00

1103 lines
45 KiB
Verilog

/*
Copyright (c) 2020 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* AXI4-Stream RAM switch
*/
module axis_ram_switch #
(
// FIFO depth in words (each virtual FIFO)
// KEEP_WIDTH words per cycle if KEEP_ENABLE set
// Rounded up to nearest power of 2 cycles
parameter FIFO_DEPTH = 4096,
// Command FIFO depth (each virtual FIFO)
// Rounded up to nearest power of 2
parameter CMD_FIFO_DEPTH = 32,
// Speedup factor (internal data width scaling factor)
// Speedup of 0 scales internal width to provide maximum bandwidth
parameter SPEEDUP = 0,
// Number of AXI stream inputs
parameter S_COUNT = 4,
// Number of AXI stream outputs
parameter M_COUNT = 4,
// Width of input AXI stream interfaces in bits
parameter S_DATA_WIDTH = 8,
// Propagate tkeep signal
parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8),
// Width of output AXI stream interfaces in bits
parameter M_DATA_WIDTH = 8,
// Propagate tkeep signal
parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8),
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// tdest signal width
// must be wide enough to uniquely address outputs
parameter DEST_WIDTH = $clog2(M_COUNT),
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1,
// tuser value for bad frame marker
parameter USER_BAD_FRAME_VALUE = 1'b1,
// tuser mask for bad frame marker
parameter USER_BAD_FRAME_MASK = 1'b1,
// Drop frames marked bad
parameter DROP_BAD_FRAME = 0,
// Drop incoming frames when full
// When set, s_axis_tready is always asserted
parameter DROP_WHEN_FULL = 0,
// Output interface routing base tdest selection
// Concatenate M_COUNT DEST_WIDTH sized constants
// Port selected if M_BASE <= tdest <= M_TOP
// set to zero for default routing with tdest MSBs as port index
parameter M_BASE = 0,
// Output interface routing top tdest selection
// Concatenate M_COUNT DEST_WIDTH sized constants
// Port selected if M_BASE <= tdest <= M_TOP
// set to zero to inherit from M_BASE
parameter M_TOP = 0,
// Interface connection control
// M_COUNT concatenated fields of S_COUNT bits
parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}},
// arbitration type: "PRIORITY" or "ROUND_ROBIN"
parameter ARB_TYPE = "ROUND_ROBIN",
// LSB priority: "LOW", "HIGH"
parameter LSB_PRIORITY = "HIGH",
// RAM read data output pipeline stages
parameter RAM_PIPELINE = 2
)
(
input wire clk,
input wire rst,
/*
* AXI Stream inputs
*/
input wire [S_COUNT*S_DATA_WIDTH-1:0] s_axis_tdata,
input wire [S_COUNT*S_KEEP_WIDTH-1:0] s_axis_tkeep,
input wire [S_COUNT-1:0] s_axis_tvalid,
output wire [S_COUNT-1:0] s_axis_tready,
input wire [S_COUNT-1:0] s_axis_tlast,
input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid,
input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest,
input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser,
/*
* AXI Stream outputs
*/
output wire [M_COUNT*M_DATA_WIDTH-1:0] m_axis_tdata,
output wire [M_COUNT*M_KEEP_WIDTH-1:0] m_axis_tkeep,
output wire [M_COUNT-1:0] m_axis_tvalid,
input wire [M_COUNT-1:0] m_axis_tready,
output wire [M_COUNT-1:0] m_axis_tlast,
output wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid,
output wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest,
output wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser,
/*
* Status
*/
output wire [S_COUNT-1:0] status_overflow,
output wire [S_COUNT-1:0] status_bad_frame,
output wire [S_COUNT-1:0] status_good_frame
);
parameter CL_S_COUNT = $clog2(S_COUNT);
parameter CL_M_COUNT = $clog2(M_COUNT);
// force keep width to 1 when disabled
parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1;
parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1;
// bus word sizes (must be identical)
parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT;
parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT;
// total data and keep widths
parameter MIN_DATA_WIDTH = (M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT ? M_DATA_WIDTH : S_DATA_WIDTH);
parameter MIN_KEEP_WIDTH = (M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT);
// speedup factor
parameter M_TOTAL_DATA_WIDTH = M_DATA_WIDTH*M_COUNT;
parameter S_TOTAL_DATA_WIDTH = S_DATA_WIDTH*S_COUNT;
parameter SPEEDUP_INT = SPEEDUP > 0 ? SPEEDUP : (M_TOTAL_DATA_WIDTH > S_TOTAL_DATA_WIDTH ? (M_TOTAL_DATA_WIDTH / MIN_DATA_WIDTH) : (S_TOTAL_DATA_WIDTH / MIN_DATA_WIDTH));
parameter DATA_WIDTH = MIN_DATA_WIDTH*SPEEDUP_INT;
parameter KEEP_WIDTH = MIN_KEEP_WIDTH*SPEEDUP_INT;
parameter ADDR_WIDTH = $clog2(FIFO_DEPTH/KEEP_WIDTH);
parameter RAM_ADDR_WIDTH = $clog2(S_COUNT*FIFO_DEPTH/KEEP_WIDTH);
parameter CMD_ADDR_WIDTH = $clog2(CMD_FIFO_DEPTH);
integer i, j;
// check configuration
initial begin
if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin
$error("Error: input data width not evenly divisble (instance %m)");
$finish;
end
if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin
$error("Error: output data width not evenly divisble (instance %m)");
$finish;
end
if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin
$error("Error: word size mismatch (instance %m)");
$finish;
end
if (DEST_WIDTH < CL_M_COUNT) begin
$error("Error: DEST_WIDTH too small for port count (instance %m)");
$finish;
end
if (M_BASE == 0) begin
// M_BASE is zero, route with tdest as port index
$display("Addressing configuration for axis_switch instance %m");
for (i = 0; i < M_COUNT; i = i + 1) begin
$display("%d: %08x-%08x (connect mask %b)", i, i << (DEST_WIDTH-CL_M_COUNT), ((i+1) << (DEST_WIDTH-CL_M_COUNT))-1, M_CONNECT[i*S_COUNT +: S_COUNT]);
end
end else if (M_TOP == 0) begin
// M_TOP is zero, assume equal to M_BASE
$display("Addressing configuration for axis_switch instance %m");
for (i = 0; i < M_COUNT; i = i + 1) begin
$display("%d: %08x (connect mask %b)", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH], M_CONNECT[i*S_COUNT +: S_COUNT]);
end
for (i = 0; i < M_COUNT; i = i + 1) begin
for (j = i+1; j < M_COUNT; j = j + 1) begin
if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] == M_BASE[j*DEST_WIDTH +: DEST_WIDTH]) begin
$display("%d: %08x", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH]);
$display("%d: %08x", j, M_BASE[j*DEST_WIDTH +: DEST_WIDTH]);
$error("Error: ranges overlap (instance %m)");
$finish;
end
end
end
end else begin
$display("Addressing configuration for axis_switch instance %m");
for (i = 0; i < M_COUNT; i = i + 1) begin
$display("%d: %08x-%08x (connect mask %b)", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH], M_TOP[i*DEST_WIDTH +: DEST_WIDTH], M_CONNECT[i*S_COUNT +: S_COUNT]);
end
for (i = 0; i < M_COUNT; i = i + 1) begin
if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] > M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin
$error("Error: invalid range (instance %m)");
$finish;
end
end
for (i = 0; i < M_COUNT; i = i + 1) begin
for (j = i+1; j < M_COUNT; j = j + 1) begin
if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[j*DEST_WIDTH +: DEST_WIDTH] && M_BASE[j*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin
$display("%d: %08x-%08x", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH], M_TOP[i*DEST_WIDTH +: DEST_WIDTH]);
$display("%d: %08x-%08x", j, M_BASE[j*DEST_WIDTH +: DEST_WIDTH], M_TOP[j*DEST_WIDTH +: DEST_WIDTH]);
$error("Error: ranges overlap (instance %m)");
$finish;
end
end
end
end
end
// Shared RAM
reg [DATA_WIDTH-1:0] mem[(2**RAM_ADDR_WIDTH)-1:0];
reg [DATA_WIDTH-1:0] mem_read_data_reg[RAM_PIPELINE-1:0];
reg [M_COUNT-1:0] mem_read_data_valid_reg[RAM_PIPELINE-1:0];
wire [S_COUNT*DATA_WIDTH-1:0] port_ram_wr_data;
wire [S_COUNT*RAM_ADDR_WIDTH-1:0] port_ram_wr_addr;
wire [S_COUNT-1:0] port_ram_wr_en;
wire [S_COUNT-1:0] port_ram_wr_ack;
wire [M_COUNT*RAM_ADDR_WIDTH-1:0] port_ram_rd_addr;
wire [M_COUNT-1:0] port_ram_rd_en;
wire [M_COUNT-1:0] port_ram_rd_ack;
wire [M_COUNT*DATA_WIDTH-1:0] port_ram_rd_data;
wire [M_COUNT-1:0] port_ram_rd_data_valid;
assign port_ram_rd_data = {M_COUNT{mem_read_data_reg[RAM_PIPELINE-1]}};
assign port_ram_rd_data_valid = mem_read_data_valid_reg[RAM_PIPELINE-1];
wire [CL_S_COUNT-1:0] ram_wr_sel;
wire ram_wr_en;
wire [CL_M_COUNT-1:0] ram_rd_sel;
wire ram_rd_en;
generate
if (S_COUNT > 1) begin
arbiter #(
.PORTS(S_COUNT),
.TYPE("ROUND_ROBIN"),
.BLOCK("NONE"),
.LSB_PRIORITY("HIGH")
)
ram_write_arb_inst (
.clk(clk),
.rst(rst),
.request(port_ram_wr_en & ~port_ram_wr_ack),
.acknowledge({S_COUNT{1'b0}}),
.grant(port_ram_wr_ack),
.grant_valid(ram_wr_en),
.grant_encoded(ram_wr_sel)
);
end else begin
assign ram_wr_en = port_ram_wr_en;
assign port_ram_wr_ack = port_ram_wr_en;
assign ram_wr_sel = 0;
end
endgenerate
always @(posedge clk) begin
if (ram_wr_en) begin
mem[port_ram_wr_addr[ram_wr_sel*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]] <= port_ram_wr_data[ram_wr_sel*DATA_WIDTH +: DATA_WIDTH];
end
end
generate
if (M_COUNT > 1) begin
arbiter #(
.PORTS(M_COUNT),
.TYPE("ROUND_ROBIN"),
.BLOCK("NONE"),
.LSB_PRIORITY("HIGH")
)
ram_read_arb_inst (
.clk(clk),
.rst(rst),
.request(port_ram_rd_en & ~port_ram_rd_ack),
.acknowledge({M_COUNT{1'b0}}),
.grant(port_ram_rd_ack),
.grant_valid(ram_rd_en),
.grant_encoded(ram_rd_sel)
);
end else begin
assign ram_rd_en = port_ram_rd_en;
assign port_ram_rd_ack = port_ram_rd_en;
assign ram_rd_sel = 0;
end
endgenerate
integer s;
always @(posedge clk) begin
mem_read_data_valid_reg[0] <= 0;
for (s = RAM_PIPELINE-1; s > 0; s = s - 1) begin
mem_read_data_reg[s] <= mem_read_data_reg[s-1];
mem_read_data_valid_reg[s] <= mem_read_data_valid_reg[s-1];
end
if (ram_rd_en) begin
mem_read_data_reg[0] <= mem[port_ram_rd_addr[ram_rd_sel*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]];
mem_read_data_valid_reg[0] <= 1 << ram_rd_sel;
end
if (rst) begin
mem_read_data_valid_reg[0] <= 0;
for (s = 0; s < RAM_PIPELINE; s = s + 1) begin
mem_read_data_valid_reg[s] <= 0;
end
end
end
// Interconnect
wire [S_COUNT*RAM_ADDR_WIDTH-1:0] int_cmd_addr;
wire [S_COUNT*ADDR_WIDTH-1:0] int_cmd_len;
wire [S_COUNT*CMD_ADDR_WIDTH-1:0] int_cmd_id;
wire [S_COUNT*KEEP_WIDTH-1:0] int_cmd_tkeep;
wire [S_COUNT*ID_WIDTH-1:0] int_cmd_tid;
wire [S_COUNT*DEST_WIDTH-1:0] int_cmd_tdest;
wire [S_COUNT*USER_WIDTH-1:0] int_cmd_tuser;
wire [S_COUNT*M_COUNT-1:0] int_cmd_valid;
wire [M_COUNT*S_COUNT-1:0] int_cmd_ready;
wire [M_COUNT*CMD_ADDR_WIDTH-1:0] int_cmd_status_id;
wire [M_COUNT*S_COUNT-1:0] int_cmd_status_valid;
wire [S_COUNT*M_COUNT-1:0] int_cmd_status_ready;
generate
genvar m, n;
for (m = 0; m < S_COUNT; m = m + 1) begin : s_ifaces
wire [DATA_WIDTH-1:0] port_axis_tdata;
wire [KEEP_WIDTH-1:0] port_axis_tkeep;
wire port_axis_tvalid;
wire port_axis_tready;
wire port_axis_tlast;
wire [ID_WIDTH-1:0] port_axis_tid;
wire [DEST_WIDTH-1:0] port_axis_tdest;
wire [USER_WIDTH-1:0] port_axis_tuser;
axis_adapter #(
.S_DATA_WIDTH(S_DATA_WIDTH),
.S_KEEP_ENABLE(S_KEEP_ENABLE),
.S_KEEP_WIDTH(S_KEEP_WIDTH),
.M_DATA_WIDTH(DATA_WIDTH),
.M_KEEP_ENABLE(1),
.M_KEEP_WIDTH(KEEP_WIDTH),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(1),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH)
)
adapter_inst (
.clk(clk),
.rst(rst),
// AXI input
.s_axis_tdata(s_axis_tdata[S_DATA_WIDTH*m +: S_DATA_WIDTH]),
.s_axis_tkeep(s_axis_tkeep[S_KEEP_WIDTH*m +: S_KEEP_WIDTH]),
.s_axis_tvalid(s_axis_tvalid[m]),
.s_axis_tready(s_axis_tready[m]),
.s_axis_tlast(s_axis_tlast[m]),
.s_axis_tid(s_axis_tid[ID_WIDTH*m +: ID_WIDTH]),
.s_axis_tdest(s_axis_tdest[DEST_WIDTH*m +: DEST_WIDTH]),
.s_axis_tuser(s_axis_tuser[USER_WIDTH*m +: USER_WIDTH]),
// AXI output
.m_axis_tdata(port_axis_tdata),
.m_axis_tkeep(port_axis_tkeep),
.m_axis_tvalid(port_axis_tvalid),
.m_axis_tready(port_axis_tready),
.m_axis_tlast(port_axis_tlast),
.m_axis_tid(port_axis_tid),
.m_axis_tdest(port_axis_tdest),
.m_axis_tuser(port_axis_tuser)
);
// decoding
reg [CL_M_COUNT-1:0] select_reg = 0, select_next;
reg drop_reg = 1'b0, drop_next;
reg select_valid_reg = 1'b0, select_valid_next;
integer k;
always @* begin
select_next = select_reg;
drop_next = drop_reg && !(port_axis_tvalid && port_axis_tready && port_axis_tlast);
select_valid_next = select_valid_reg && !(port_axis_tvalid && port_axis_tready && port_axis_tlast);
if (port_axis_tvalid && !select_valid_reg && !drop_reg) begin
select_next = 0;
select_valid_next = 1'b0;
drop_next = 1'b1;
for (k = 0; k < M_COUNT; k = k + 1) begin
if (M_BASE == 0) begin
if (M_COUNT == 1) begin
// M_BASE is zero with only one output port, ignore tdest
select_next = 0;
select_valid_next = 1'b1;
drop_next = 1'b0;
end else begin
// M_BASE is zero, route with $clog2(M_COUNT) MSBs of tdest as port index
if (port_axis_tdest[DEST_WIDTH-CL_M_COUNT +: CL_M_COUNT] == k && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin
select_next = k;
select_valid_next = 1'b1;
drop_next = 1'b0;
end
end
end else if (M_TOP == 0) begin
// M_TOP is zero, assume equal to M_BASE
if (port_axis_tdest == M_BASE[k*DEST_WIDTH +: DEST_WIDTH] && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin
select_next = k;
select_valid_next = 1'b1;
drop_next = 1'b0;
end
end else begin
if (port_axis_tdest >= M_BASE[k*DEST_WIDTH +: DEST_WIDTH] && port_axis_tdest <= M_TOP[k*DEST_WIDTH +: DEST_WIDTH] && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin
select_next = k;
select_valid_next = 1'b1;
drop_next = 1'b0;
end
end
end
end
end
always @(posedge clk) begin
select_reg <= select_next;
drop_reg <= drop_next;
select_valid_reg <= select_valid_next;
if (rst) begin
select_valid_reg <= 1'b0;
end
end
// status arbitration
wire [M_COUNT-1:0] request;
wire [M_COUNT-1:0] acknowledge;
wire [M_COUNT-1:0] grant;
wire grant_valid;
wire [CL_M_COUNT-1:0] grant_encoded;
arbiter #(
.PORTS(M_COUNT),
.TYPE(ARB_TYPE),
.BLOCK("ACKNOWLEDGE"),
.LSB_PRIORITY(LSB_PRIORITY)
)
cmd_status_arb_inst (
.clk(clk),
.rst(rst),
.request(request),
.acknowledge(acknowledge),
.grant(grant),
.grant_valid(grant_valid),
.grant_encoded(grant_encoded)
);
// mux
wire [CMD_ADDR_WIDTH-1:0] cmd_status_id_mux = int_cmd_status_id[grant_encoded*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH];
wire cmd_status_valid_mux = int_cmd_status_valid[grant_encoded*S_COUNT+m] && grant_valid;
wire cmd_status_ready_mux;
assign int_cmd_status_ready[m*M_COUNT +: M_COUNT] = (grant_valid && cmd_status_ready_mux) << grant_encoded;
for (n = 0; n < M_COUNT; n = n + 1) begin
assign request[n] = int_cmd_status_valid[m+n*S_COUNT] && !grant[n];
assign acknowledge[n] = grant[n] && int_cmd_status_valid[m+n*S_COUNT] && cmd_status_ready_mux;
end
reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next;
reg [ADDR_WIDTH:0] wr_ptr_cur_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_next;
reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next;
reg [ADDR_WIDTH-1:0] len_reg = {ADDR_WIDTH{1'b0}}, len_next;
// full when first MSB different but rest same
wire full = wr_ptr_cur_reg == (rd_ptr_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}});
// empty when pointers match exactly
wire empty = wr_ptr_reg == rd_ptr_reg;
// overflow within packet
wire full_wr = wr_ptr_cur_reg == (wr_ptr_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}});
reg drop_frame_reg = 1'b0, drop_frame_next;
reg overflow_reg = 1'b0, overflow_next;
reg bad_frame_reg = 1'b0, bad_frame_next;
reg good_frame_reg = 1'b0, good_frame_next;
reg [DATA_WIDTH-1:0] ram_wr_data_reg = {DATA_WIDTH{1'b0}}, ram_wr_data_next;
reg [ADDR_WIDTH-1:0] ram_wr_addr_reg = {ADDR_WIDTH{1'b0}}, ram_wr_addr_next;
reg ram_wr_en_reg = 1'b0, ram_wr_en_next;
wire ram_wr_ack;
reg [2**CMD_ADDR_WIDTH-1:0] cmd_table_active = 0;
reg [2**CMD_ADDR_WIDTH-1:0] cmd_table_commit = 0;
reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_addr_start[2**CMD_ADDR_WIDTH-1:0];
reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_addr_end[2**CMD_ADDR_WIDTH-1:0];
reg [ADDR_WIDTH-1:0] cmd_table_len[2**CMD_ADDR_WIDTH-1:0];
reg [CL_M_COUNT-1:0] cmd_table_select[2**CMD_ADDR_WIDTH-1:0];
reg [KEEP_WIDTH-1:0] cmd_table_tkeep[2**CMD_ADDR_WIDTH-1:0];
reg [ID_WIDTH-1:0] cmd_table_tid[2**CMD_ADDR_WIDTH-1:0];
reg [DEST_WIDTH-1:0] cmd_table_tdest[2**CMD_ADDR_WIDTH-1:0];
reg [USER_WIDTH-1:0] cmd_table_tuser[2**CMD_ADDR_WIDTH-1:0];
reg [CMD_ADDR_WIDTH+1-1:0] cmd_table_start_ptr_reg = 0;
reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_start_addr_start;
reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_start_addr_end;
reg [ADDR_WIDTH-1:0] cmd_table_start_len;
reg [CL_M_COUNT-1:0] cmd_table_start_select;
reg [KEEP_WIDTH-1:0] cmd_table_start_tkeep;
reg [ID_WIDTH-1:0] cmd_table_start_tid;
reg [DEST_WIDTH-1:0] cmd_table_start_tdest;
reg [USER_WIDTH-1:0] cmd_table_start_tuser;
reg cmd_table_start_en;
reg [CMD_ADDR_WIDTH+1-1:0] cmd_table_read_ptr_reg = 0;
reg cmd_table_read_en;
reg [CMD_ADDR_WIDTH-1:0] cmd_table_commit_ptr;
reg cmd_table_commit_en;
reg [CMD_ADDR_WIDTH+1-1:0] cmd_table_finish_ptr_reg = 0;
reg cmd_table_finish_en;
reg [RAM_ADDR_WIDTH-1:0] cmd_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, cmd_addr_next;
reg [ADDR_WIDTH-1:0] cmd_len_reg = {ADDR_WIDTH{1'b0}}, cmd_len_next;
reg [CMD_ADDR_WIDTH-1:0] cmd_id_reg = {CMD_ADDR_WIDTH{1'b0}}, cmd_id_next;
reg [KEEP_WIDTH-1:0] cmd_tkeep_reg = {KEEP_WIDTH{1'b0}}, cmd_tkeep_next;
reg [ID_WIDTH-1:0] cmd_tid_reg = {ID_WIDTH{1'b0}}, cmd_tid_next;
reg [DEST_WIDTH-1:0] cmd_tdest_reg = {DEST_WIDTH{1'b0}}, cmd_tdest_next;
reg [USER_WIDTH-1:0] cmd_tuser_reg = {USER_WIDTH{1'b0}}, cmd_tuser_next;
reg [M_COUNT-1:0] cmd_valid_reg = 0, cmd_valid_next;
reg cmd_status_ready_reg = 1'b0, cmd_status_ready_next;
wire [M_COUNT-1:0] port_cmd_ready;
for (n = 0; n < M_COUNT; n = n + 1) begin
assign port_cmd_ready[n] = int_cmd_ready[m+n*S_COUNT];
end
assign port_axis_tready = (select_valid_reg && (!ram_wr_en_reg || ram_wr_ack) && (!full || full_wr || DROP_WHEN_FULL) && ($unsigned(cmd_table_start_ptr_reg - cmd_table_finish_ptr_reg) < 2**CMD_ADDR_WIDTH)) || drop_reg;
assign port_ram_wr_data[m*DATA_WIDTH +: DATA_WIDTH] = ram_wr_data_reg;
assign port_ram_wr_addr[m*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH] = ram_wr_addr_reg[ADDR_WIDTH-1:0] | (m << ADDR_WIDTH);
assign port_ram_wr_en[m] = ram_wr_en_reg;
assign ram_wr_ack = port_ram_wr_ack[m];
assign int_cmd_addr[m*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH] = cmd_addr_reg[ADDR_WIDTH-1:0] | (m << ADDR_WIDTH);
assign int_cmd_len[m*ADDR_WIDTH +: ADDR_WIDTH] = cmd_len_reg;
assign int_cmd_id[m*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH] = cmd_id_reg;
assign int_cmd_tkeep[m*KEEP_WIDTH +: KEEP_WIDTH] = cmd_tkeep_reg;
assign int_cmd_tid[m*ID_WIDTH +: ID_WIDTH] = cmd_tid_reg;
assign int_cmd_tdest[m*DEST_WIDTH +: DEST_WIDTH] = cmd_tdest_reg;
assign int_cmd_tuser[m*USER_WIDTH +: USER_WIDTH] = cmd_tuser_reg;
assign int_cmd_valid[m*M_COUNT +: M_COUNT] = cmd_valid_reg;
assign cmd_status_ready_mux = cmd_status_ready_reg;
assign status_overflow[m] = overflow_reg;
assign status_bad_frame[m] = bad_frame_reg;
assign status_good_frame[m] = good_frame_reg;
always @* begin
wr_ptr_next = wr_ptr_reg;
wr_ptr_cur_next = wr_ptr_cur_reg;
rd_ptr_next = rd_ptr_reg;
len_next = len_reg;
drop_frame_next = drop_frame_reg;
overflow_next = 1'b0;
bad_frame_next = 1'b0;
good_frame_next = 1'b0;
ram_wr_data_next = ram_wr_data_reg;
ram_wr_addr_next = ram_wr_addr_reg;
ram_wr_en_next = ram_wr_en_reg && !ram_wr_ack;
cmd_table_start_addr_start = wr_ptr_reg;
cmd_table_start_addr_end = wr_ptr_cur_reg + 1;
cmd_table_start_len = len_reg;
cmd_table_start_select = select_reg;
cmd_table_start_tkeep = S_KEEP_ENABLE ? port_axis_tkeep : 1'b1;
cmd_table_start_tid = port_axis_tid;
cmd_table_start_tdest = port_axis_tdest;
cmd_table_start_tuser = port_axis_tuser;
cmd_table_start_en = 1'b0;
cmd_table_read_en = 1'b0;
cmd_table_commit_ptr = 0;
cmd_table_commit_en = 1'b0;
cmd_table_finish_en = 1'b0;
cmd_addr_next = cmd_addr_reg;
cmd_len_next = cmd_len_reg;
cmd_id_next = cmd_id_reg;
cmd_tkeep_next = cmd_tkeep_reg;
cmd_tid_next = cmd_tid_reg;
cmd_tdest_next = cmd_tdest_reg;
cmd_tuser_next = cmd_tuser_reg;
cmd_valid_next = cmd_valid_reg;
cmd_status_ready_next = 1'b0;
// issue memory writes and commands
if (port_axis_tready && port_axis_tvalid && select_valid_reg && !drop_reg) begin
if (full || full_wr || drop_frame_reg) begin
// full, packet overflow, or currently dropping frame
// drop frame
drop_frame_next = 1'b1;
if (port_axis_tlast) begin
// end of frame, reset write pointer
wr_ptr_cur_next = wr_ptr_reg;
drop_frame_next = 1'b0;
overflow_next = 1'b1;
end
end else begin
wr_ptr_cur_next = wr_ptr_cur_reg + 1;
len_next = len_reg + 1;
// issue write operation
ram_wr_data_next = port_axis_tdata;
ram_wr_addr_next = wr_ptr_cur_reg;
ram_wr_en_next = 1'b1;
if (port_axis_tlast) begin
// end of frame
len_next = 0;
if (DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(port_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin
// bad packet, reset write pointer
wr_ptr_cur_next = wr_ptr_reg;
bad_frame_next = 1'b1;
end else begin
// good packet, update write pointer
wr_ptr_next = wr_ptr_cur_reg + 1;
good_frame_next = 1'b1;
cmd_table_start_addr_start = wr_ptr_reg;
cmd_table_start_addr_end = wr_ptr_cur_reg + 1;
cmd_table_start_len = len_reg;
cmd_table_start_select = select_reg;
cmd_table_start_tkeep = port_axis_tkeep;
cmd_table_start_tid = port_axis_tid;
cmd_table_start_tdest = port_axis_tdest;
cmd_table_start_tuser = port_axis_tuser;
cmd_table_start_en = 1'b1;
end
end
end
end
// read
cmd_valid_next = cmd_valid_reg & ~port_cmd_ready;
if (!cmd_valid_reg && cmd_table_active[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_read_ptr_reg != cmd_table_start_ptr_reg && (!ram_wr_en_reg || ram_wr_ack)) begin
cmd_table_read_en = 1'b1;
cmd_addr_next = cmd_table_addr_start[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_len_next = cmd_table_len[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_id_next = cmd_table_read_ptr_reg;
cmd_tkeep_next = cmd_table_tkeep[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_tid_next = cmd_table_tid[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_tdest_next = cmd_table_tdest[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_tuser_next = cmd_table_tuser[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_valid_next = 1 << cmd_table_select[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
end
// commit
if (cmd_status_valid_mux) begin
cmd_status_ready_next = 1'b1;
cmd_table_commit_ptr = cmd_status_id_mux;
cmd_table_commit_en = 1'b1;
end
// clean-up
if (cmd_table_active[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_commit[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_finish_ptr_reg != cmd_table_start_ptr_reg) begin
// update read pointer
rd_ptr_next = cmd_table_addr_end[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_table_finish_en = 1'b1;
end
end
always @(posedge clk) begin
wr_ptr_reg <= wr_ptr_next;
wr_ptr_cur_reg <= wr_ptr_cur_next;
rd_ptr_reg <= rd_ptr_next;
len_reg <= len_next;
drop_frame_reg <= drop_frame_next;
overflow_reg <= overflow_next;
bad_frame_reg <= bad_frame_next;
good_frame_reg <= good_frame_next;
ram_wr_data_reg <= ram_wr_data_next;
ram_wr_addr_reg <= ram_wr_addr_next;
ram_wr_en_reg <= ram_wr_en_next;
cmd_addr_reg <= cmd_addr_next;
cmd_len_reg <= cmd_len_next;
cmd_id_reg <= cmd_id_next;
cmd_tkeep_reg <= cmd_tkeep_next;
cmd_tid_reg <= cmd_tid_next;
cmd_tdest_reg <= cmd_tdest_next;
cmd_tuser_reg <= cmd_tuser_next;
cmd_valid_reg <= cmd_valid_next;
cmd_status_ready_reg <= cmd_status_ready_next;
if (cmd_table_start_en) begin
cmd_table_start_ptr_reg <= cmd_table_start_ptr_reg + 1;
cmd_table_active[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b1;
cmd_table_commit[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b0;
cmd_table_addr_start[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_addr_start;
cmd_table_addr_end[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_addr_end;
cmd_table_len[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_len;
cmd_table_select[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_select;
cmd_table_tkeep[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tkeep;
cmd_table_tid[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tid;
cmd_table_tdest[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tdest;
cmd_table_tuser[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tuser;
end
if (cmd_table_read_en) begin
cmd_table_read_ptr_reg <= cmd_table_read_ptr_reg + 1;
end
if (cmd_table_commit_en) begin
cmd_table_commit[cmd_table_commit_ptr] <= 1'b1;
end
if (cmd_table_finish_en) begin
cmd_table_finish_ptr_reg <= cmd_table_finish_ptr_reg + 1;
cmd_table_active[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b0;
end
if (rst) begin
wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}};
rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
len_reg <= {ADDR_WIDTH{1'b0}};
drop_frame_reg <= 1'b0;
overflow_reg <= 1'b0;
bad_frame_reg <= 1'b0;
good_frame_reg <= 1'b0;
ram_wr_en_reg <= 1'b0;
cmd_valid_reg <= 1'b0;
cmd_status_ready_reg <= 1'b0;
cmd_table_start_ptr_reg <= 0;
cmd_table_read_ptr_reg <= 0;
cmd_table_finish_ptr_reg <= 0;
end
end
end // s_ifaces
for (n = 0; n < M_COUNT; n = n + 1) begin : m_ifaces
// command arbitration
wire [S_COUNT-1:0] request;
wire [S_COUNT-1:0] acknowledge;
wire [S_COUNT-1:0] grant;
wire grant_valid;
wire [CL_S_COUNT-1:0] grant_encoded;
arbiter #(
.PORTS(S_COUNT),
.TYPE(ARB_TYPE),
.BLOCK("ACKNOWLEDGE"),
.LSB_PRIORITY(LSB_PRIORITY)
)
cmd_arb_inst (
.clk(clk),
.rst(rst),
.request(request),
.acknowledge(acknowledge),
.grant(grant),
.grant_valid(grant_valid),
.grant_encoded(grant_encoded)
);
// mux
wire [RAM_ADDR_WIDTH-1:0] cmd_addr_mux = int_cmd_addr[grant_encoded*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH];
wire [ADDR_WIDTH-1:0] cmd_len_mux = int_cmd_len[grant_encoded*ADDR_WIDTH +: ADDR_WIDTH];
wire [CMD_ADDR_WIDTH-1:0] cmd_id_mux = int_cmd_id[grant_encoded*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH];
wire [KEEP_WIDTH-1:0] cmd_tkeep_mux = int_cmd_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH];
wire [ID_WIDTH-1:0] cmd_tid_mux = int_cmd_tid[grant_encoded*ID_WIDTH +: ID_WIDTH];
wire [DEST_WIDTH-1:0] cmd_tdest_mux = int_cmd_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH];
wire [USER_WIDTH-1:0] cmd_tuser_mux = int_cmd_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH];
wire cmd_valid_mux = int_cmd_valid[grant_encoded*M_COUNT+n] && grant_valid;
wire cmd_ready_mux;
assign int_cmd_ready[n*S_COUNT +: S_COUNT] = (grant_valid && cmd_ready_mux) << grant_encoded;
for (m = 0; m < S_COUNT; m = m + 1) begin
assign request[m] = int_cmd_valid[m*M_COUNT+n] && !grant[m];
assign acknowledge[m] = grant[m] && int_cmd_valid[m*M_COUNT+n] && cmd_ready_mux;
end
reg [RAM_ADDR_WIDTH-1:0] rd_ptr_reg = {RAM_ADDR_WIDTH-1{1'b0}}, rd_ptr_next;
reg [ADDR_WIDTH-1:0] len_reg = {ADDR_WIDTH{1'b0}}, len_next;
reg [CL_S_COUNT-1:0] src_reg = 0, src_next;
reg [CMD_ADDR_WIDTH-1:0] id_reg = 0, id_next;
reg [KEEP_WIDTH-1:0] last_cycle_tkeep_reg = {KEEP_WIDTH{1'b0}}, last_cycle_tkeep_next;
reg [ID_WIDTH-1:0] tid_reg = {ID_WIDTH{1'b0}}, tid_next;
reg [DEST_WIDTH-1:0] tdest_reg = {DEST_WIDTH{1'b0}}, tdest_next;
reg [USER_WIDTH-1:0] tuser_reg = {USER_WIDTH{1'b0}}, tuser_next;
reg [DATA_WIDTH-1:0] out_axis_tdata_reg = {DATA_WIDTH{1'b0}}, out_axis_tdata_next;
reg [KEEP_WIDTH-1:0] out_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}, out_axis_tkeep_next;
reg out_axis_tvalid_reg = 1'b0, out_axis_tvalid_next;
wire out_axis_tready;
reg out_axis_tlast_reg = 1'b0, out_axis_tlast_next;
reg [ID_WIDTH-1:0] out_axis_tid_reg = {ID_WIDTH{1'b0}}, out_axis_tid_next;
reg [DEST_WIDTH-1:0] out_axis_tdest_reg = {DEST_WIDTH{1'b0}}, out_axis_tdest_next;
reg [USER_WIDTH-1:0] out_axis_tuser_reg = {USER_WIDTH{1'b0}}, out_axis_tuser_next;
reg [RAM_ADDR_WIDTH-1:0] ram_rd_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, ram_rd_addr_next;
reg ram_rd_en_reg = 1'b0, ram_rd_en_next;
wire ram_rd_ack;
wire [DATA_WIDTH-1:0] ram_rd_data;
wire ram_rd_data_valid;
reg cmd_valid_reg = 1'b0, cmd_valid_next;
reg [CMD_ADDR_WIDTH-1:0] cmd_status_id_reg = {CMD_ADDR_WIDTH{1'b0}}, cmd_status_id_next;
reg [S_COUNT-1:0] cmd_status_valid_reg = 0, cmd_status_valid_next;
wire [S_COUNT-1:0] port_cmd_status_ready;
for (m = 0; m < S_COUNT; m = m + 1) begin
assign port_cmd_status_ready[m] = int_cmd_status_ready[m*M_COUNT+n];
end
reg [DATA_WIDTH-1:0] out_fifo_tdata[31:0];
reg [KEEP_WIDTH-1:0] out_fifo_tkeep[31:0];
reg out_fifo_tlast[31:0];
reg [ID_WIDTH-1:0] out_fifo_tid[31:0];
reg [DEST_WIDTH-1:0] out_fifo_tdest[31:0];
reg [USER_WIDTH-1:0] out_fifo_tuser[31:0];
reg [5:0] out_fifo_data_wr_ptr_reg = 0;
reg [DATA_WIDTH-1:0] out_fifo_data_wr_tdata;
reg out_fifo_data_wr_en;
reg [5:0] out_fifo_ctrl_wr_ptr_reg = 0;
reg [KEEP_WIDTH-1:0] out_fifo_ctrl_wr_tkeep;
reg out_fifo_ctrl_wr_tlast;
reg [ID_WIDTH-1:0] out_fifo_ctrl_wr_tid;
reg [DEST_WIDTH-1:0] out_fifo_ctrl_wr_tdest;
reg [USER_WIDTH-1:0] out_fifo_ctrl_wr_tuser;
reg out_fifo_ctrl_wr_en;
reg [5:0] out_fifo_rd_ptr_reg = 0;
reg out_fifo_rd_en;
assign port_ram_rd_addr[n*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH] = ram_rd_addr_reg;
assign port_ram_rd_en[n] = ram_rd_en_reg;
assign ram_rd_ack = port_ram_rd_ack[n];
assign ram_rd_data = port_ram_rd_data[n*DATA_WIDTH +: DATA_WIDTH];
assign ram_rd_data_valid = port_ram_rd_data_valid[n];
assign cmd_ready_mux = !cmd_valid_reg;
assign int_cmd_status_id[n*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH] = cmd_status_id_reg;
assign int_cmd_status_valid[n*S_COUNT +: S_COUNT] = cmd_status_valid_reg;
always @* begin
rd_ptr_next = rd_ptr_reg;
len_next = len_reg;
src_next = src_reg;
id_next = id_reg;
last_cycle_tkeep_next = last_cycle_tkeep_reg;
tid_next = tid_reg;
tdest_next = tdest_reg;
tuser_next = tuser_reg;
out_axis_tdata_next = out_axis_tdata_reg;
out_axis_tkeep_next = out_axis_tkeep_reg;
out_axis_tvalid_next = out_axis_tvalid_reg && !out_axis_tready;
out_axis_tlast_next = out_axis_tlast_reg;
out_axis_tid_next = out_axis_tid_reg;
out_axis_tdest_next = out_axis_tdest_reg;
out_axis_tuser_next = out_axis_tuser_reg;
ram_rd_addr_next = ram_rd_addr_reg;
ram_rd_en_next = ram_rd_en_reg && !ram_rd_ack;
cmd_valid_next = cmd_valid_reg;
cmd_status_id_next = cmd_status_id_reg;
cmd_status_valid_next = cmd_status_valid_reg & ~port_cmd_status_ready;
out_fifo_data_wr_tdata = ram_rd_data;
out_fifo_data_wr_en = 1'b0;
out_fifo_ctrl_wr_tkeep = len_reg == 0 ? last_cycle_tkeep_reg : {KEEP_WIDTH{1'b1}};
out_fifo_ctrl_wr_tlast = len_reg == 0;
out_fifo_ctrl_wr_tid = tid_reg;
out_fifo_ctrl_wr_tdest = tdest_reg;
out_fifo_ctrl_wr_tuser = tuser_reg;
out_fifo_ctrl_wr_en = 1'b0;
out_fifo_rd_en = 1'b0;
// receive commands
if (!cmd_valid_reg && cmd_valid_mux) begin
cmd_valid_next = 1'b1;
rd_ptr_next = cmd_addr_mux;
len_next = cmd_len_mux;
last_cycle_tkeep_next = cmd_tkeep_mux;
id_next = cmd_id_mux;
tid_next = cmd_tid_mux;
tdest_next = cmd_tdest_mux;
tuser_next = cmd_tuser_mux;
src_next = grant_encoded;
end
// process commands and issue memory reads
if (cmd_valid_reg && !cmd_status_valid_next && (!ram_rd_en_reg || ram_rd_ack) && ($unsigned(out_fifo_ctrl_wr_ptr_reg - out_fifo_rd_ptr_reg) < 32)) begin
// update counters
rd_ptr_next[ADDR_WIDTH-1:0] = rd_ptr_reg[ADDR_WIDTH-1:0] + 1;
len_next = len_reg - 1;
// issue memory read
ram_rd_addr_next = rd_ptr_reg;
ram_rd_en_next = 1'b1;
// write output control FIFO
out_fifo_ctrl_wr_tkeep = len_reg == 0 ? last_cycle_tkeep_reg : {KEEP_WIDTH{1'b1}};
out_fifo_ctrl_wr_tlast = len_reg == 0;
out_fifo_ctrl_wr_tid = tid_reg;
out_fifo_ctrl_wr_tdest = tdest_reg;
out_fifo_ctrl_wr_tuser = tuser_reg;
out_fifo_ctrl_wr_en = 1'b1;
if (len_reg == 0) begin
// indicate operation complete
cmd_status_id_next = id_reg;
cmd_status_valid_next = 1 << src_reg;
cmd_valid_next = 1'b0;
end
end
// write RAM read data to output data FIFO
if (ram_rd_data_valid) begin
out_fifo_data_wr_tdata = ram_rd_data;
out_fifo_data_wr_en = 1'b1;
end
// generate output AXI stream data from control and data FIFOs
if ((out_axis_tready || !out_axis_tvalid_reg) && (out_fifo_rd_ptr_reg != out_fifo_ctrl_wr_ptr_reg) && (out_fifo_rd_ptr_reg != out_fifo_data_wr_ptr_reg)) begin
out_fifo_rd_en = 1'b1;
out_axis_tdata_next = out_fifo_tdata[out_fifo_rd_ptr_reg[4:0]];
out_axis_tkeep_next = out_fifo_tkeep[out_fifo_rd_ptr_reg[4:0]];
out_axis_tvalid_next = 1'b1;
out_axis_tlast_next = out_fifo_tlast[out_fifo_rd_ptr_reg[4:0]];
out_axis_tid_next = out_fifo_tid[out_fifo_rd_ptr_reg[4:0]];
out_axis_tdest_next = out_fifo_tdest[out_fifo_rd_ptr_reg[4:0]];
out_axis_tuser_next = out_fifo_tuser[out_fifo_rd_ptr_reg[4:0]];
end
end
always @(posedge clk) begin
rd_ptr_reg <= rd_ptr_next;
len_reg <= len_next;
src_reg <= src_next;
id_reg <= id_next;
last_cycle_tkeep_reg <= last_cycle_tkeep_next;
tid_reg <= tid_next;
tdest_reg <= tdest_next;
tuser_reg <= tuser_next;
out_axis_tdata_reg <= out_axis_tdata_next;
out_axis_tkeep_reg <= out_axis_tkeep_next;
out_axis_tvalid_reg <= out_axis_tvalid_next;
out_axis_tlast_reg <= out_axis_tlast_next;
out_axis_tid_reg <= out_axis_tid_next;
out_axis_tdest_reg <= out_axis_tdest_next;
out_axis_tuser_reg <= out_axis_tuser_next;
ram_rd_addr_reg <= ram_rd_addr_next;
ram_rd_en_reg <= ram_rd_en_next;
cmd_valid_reg <= cmd_valid_next;
cmd_status_id_reg <= cmd_status_id_next;
cmd_status_valid_reg <= cmd_status_valid_next;
if (out_fifo_data_wr_en) begin
out_fifo_data_wr_ptr_reg <= out_fifo_data_wr_ptr_reg + 1;
out_fifo_tdata[out_fifo_data_wr_ptr_reg[4:0]] <= out_fifo_data_wr_tdata;
end
if (out_fifo_ctrl_wr_en) begin
out_fifo_ctrl_wr_ptr_reg <= out_fifo_ctrl_wr_ptr_reg + 1;
out_fifo_tkeep[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tkeep;
out_fifo_tlast[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tlast;
out_fifo_tid[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tid;
out_fifo_tdest[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tdest;
out_fifo_tuser[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tuser;
end
if (out_fifo_rd_en) begin
out_fifo_rd_ptr_reg <= out_fifo_rd_ptr_reg + 1;
end
if (rst) begin
len_reg <= 0;
out_axis_tvalid_reg <= 1'b0;
ram_rd_en_reg <= 1'b0;
cmd_valid_reg <= 1'b0;
cmd_status_valid_reg <= 0;
out_fifo_data_wr_ptr_reg <= 0;
out_fifo_ctrl_wr_ptr_reg <= 0;
out_fifo_rd_ptr_reg <= 0;
end
end
axis_adapter #(
.S_DATA_WIDTH(DATA_WIDTH),
.S_KEEP_ENABLE(1),
.S_KEEP_WIDTH(KEEP_WIDTH),
.M_DATA_WIDTH(M_DATA_WIDTH),
.M_KEEP_ENABLE(M_KEEP_ENABLE),
.M_KEEP_WIDTH(M_KEEP_WIDTH),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(1),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH)
)
adapter_inst (
.clk(clk),
.rst(rst),
// AXI input
.s_axis_tdata(out_axis_tdata_reg),
.s_axis_tkeep(out_axis_tkeep_reg),
.s_axis_tvalid(out_axis_tvalid_reg),
.s_axis_tready(out_axis_tready),
.s_axis_tlast(out_axis_tlast_reg),
.s_axis_tid(out_axis_tid_reg),
.s_axis_tdest(out_axis_tdest_reg),
.s_axis_tuser(out_axis_tuser_reg),
// AXI output
.m_axis_tdata(m_axis_tdata[M_DATA_WIDTH*n +: M_DATA_WIDTH]),
.m_axis_tkeep(m_axis_tkeep[M_KEEP_WIDTH*n +: M_KEEP_WIDTH]),
.m_axis_tvalid(m_axis_tvalid[n]),
.m_axis_tready(m_axis_tready[n]),
.m_axis_tlast(m_axis_tlast[n]),
.m_axis_tid(m_axis_tid[ID_WIDTH*n +: ID_WIDTH]),
.m_axis_tdest(m_axis_tdest[DEST_WIDTH*n +: DEST_WIDTH]),
.m_axis_tuser(m_axis_tuser[USER_WIDTH*n +: USER_WIDTH])
);
end // m_ifaces
endgenerate
endmodule