verilog-axi/rtl/axi_vfifo_enc.v
Alex Forencich 59d37ee850 Add AXI virtual FIFO
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-03-28 20:59:47 -07:00

795 lines
28 KiB
Verilog

/*
Copyright (c) 2023 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
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* AXI4 virtual FIFO (encoder)
*/
module axi_vfifo_enc #
(
// Width of input segment
parameter SEG_WIDTH = 32,
// Segment count
parameter SEG_CNT = 2,
// Width of AXI stream interfaces in bits
parameter AXIS_DATA_WIDTH = SEG_WIDTH*SEG_CNT/2,
// Use AXI stream tkeep signal
parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8),
// AXI stream tkeep signal width (words per cycle)
parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8),
// Use AXI stream tlast signal
parameter AXIS_LAST_ENABLE = 1,
// Propagate AXI stream tid signal
parameter AXIS_ID_ENABLE = 0,
// AXI stream tid signal width
parameter AXIS_ID_WIDTH = 8,
// Propagate AXI stream tdest signal
parameter AXIS_DEST_ENABLE = 0,
// AXI stream tdest signal width
parameter AXIS_DEST_WIDTH = 8,
// Propagate AXI stream tuser signal
parameter AXIS_USER_ENABLE = 1,
// AXI stream tuser signal width
parameter AXIS_USER_WIDTH = 1
)
(
input wire clk,
input wire rst,
/*
* AXI stream data input
*/
input wire [AXIS_DATA_WIDTH-1:0] s_axis_tdata,
input wire [AXIS_KEEP_WIDTH-1:0] s_axis_tkeep,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire [AXIS_ID_WIDTH-1:0] s_axis_tid,
input wire [AXIS_DEST_WIDTH-1:0] s_axis_tdest,
input wire [AXIS_USER_WIDTH-1:0] s_axis_tuser,
/*
* Segmented data output (to virtual FIFO channel)
*/
input wire fifo_rst_in,
output wire [SEG_CNT*SEG_WIDTH-1:0] output_data,
output wire [SEG_CNT-1:0] output_valid,
input wire fifo_watermark_in
);
parameter AXIS_KEEP_WIDTH_INT = AXIS_KEEP_ENABLE ? AXIS_KEEP_WIDTH : 1;
parameter AXIS_BYTE_LANES = AXIS_KEEP_WIDTH_INT;
parameter AXIS_BYTE_SIZE = AXIS_DATA_WIDTH/AXIS_BYTE_LANES;
parameter CL_AXIS_BYTE_LANES = $clog2(AXIS_BYTE_LANES);
parameter BYTE_SIZE = AXIS_BYTE_SIZE;
parameter SEG_BYTE_LANES = SEG_WIDTH / BYTE_SIZE;
parameter EXPAND_OUTPUT = SEG_CNT < 2;
parameter SEG_CNT_INT = EXPAND_OUTPUT ? SEG_CNT*2 : SEG_CNT;
parameter SEG_IDX_WIDTH = $clog2(SEG_CNT_INT);
parameter SEG_BYTE_IDX_WIDTH = $clog2(SEG_BYTE_LANES);
parameter AXIS_SEG_CNT = (AXIS_DATA_WIDTH + SEG_WIDTH-1) / SEG_WIDTH;
parameter AXIS_SEG_IDX_WIDTH = AXIS_SEG_CNT > 1 ? $clog2(AXIS_SEG_CNT) : 1;
parameter AXIS_LEN_MASK = AXIS_BYTE_LANES-1;
parameter IN_OFFS_WIDTH = AXIS_SEG_IDX_WIDTH;
parameter META_ID_OFFSET = 0;
parameter META_DEST_OFFSET = META_ID_OFFSET + (AXIS_ID_ENABLE ? AXIS_ID_WIDTH : 0);
parameter META_USER_OFFSET = META_DEST_OFFSET + (AXIS_DEST_ENABLE ? AXIS_DEST_WIDTH : 0);
parameter META_WIDTH = META_USER_OFFSET + (AXIS_USER_ENABLE ? AXIS_USER_WIDTH : 0);
parameter HDR_SIZE = (16 + META_WIDTH + BYTE_SIZE-1) / BYTE_SIZE;
parameter HDR_WIDTH = HDR_SIZE * BYTE_SIZE;
parameter HDR_LEN_WIDTH = 12;
parameter HDR_SEG_LEN_WIDTH = HDR_LEN_WIDTH-SEG_BYTE_IDX_WIDTH;
parameter INPUT_FIFO_ADDR_WIDTH = 5;
parameter HDR_FIFO_ADDR_WIDTH = INPUT_FIFO_ADDR_WIDTH + SEG_IDX_WIDTH;
parameter INPUT_FIFO_PTR_WIDTH = INPUT_FIFO_ADDR_WIDTH + SEG_IDX_WIDTH;
parameter HDR_FIFO_PTR_WIDTH = HDR_FIFO_ADDR_WIDTH;
parameter INPUT_FIFO_SIZE = SEG_BYTE_LANES * SEG_CNT_INT * 2**INPUT_FIFO_ADDR_WIDTH;
parameter MAX_BLOCK_LEN = INPUT_FIFO_SIZE / 2 > 4096 ? 4096 : INPUT_FIFO_SIZE / 2;
// validate parameters
initial begin
if (AXIS_BYTE_SIZE * AXIS_KEEP_WIDTH_INT != AXIS_DATA_WIDTH) begin
$error("Error: AXI stream data width not evenly divisible (instance %m)");
$finish;
end
if (AXIS_SEG_CNT * SEG_WIDTH != AXIS_DATA_WIDTH) begin
$error("Error: AXI stream data width not evenly divisible into segments (instance %m)");
$finish;
end
if (SEG_WIDTH < HDR_SIZE*BYTE_SIZE) begin
$error("Error: Segment smaller than header (instance %m)");
$finish;
end
end
reg [INPUT_FIFO_PTR_WIDTH+1-1:0] input_fifo_wr_ptr_reg = 0, input_fifo_wr_ptr_next;
reg [INPUT_FIFO_PTR_WIDTH+1-1:0] input_fifo_rd_ptr_reg = 0, input_fifo_rd_ptr_next;
reg [HDR_FIFO_PTR_WIDTH+1-1:0] hdr_fifo_wr_ptr_reg = 0, hdr_fifo_wr_ptr_next;
reg [HDR_FIFO_PTR_WIDTH+1-1:0] hdr_fifo_rd_ptr_reg = 0, hdr_fifo_rd_ptr_next;
reg [SEG_CNT_INT-1:0] mem_rd_data_valid_reg = 0, mem_rd_data_valid_next;
reg hdr_mem_rd_data_valid_reg = 0, hdr_mem_rd_data_valid_next;
reg [AXIS_DATA_WIDTH-1:0] int_seg_data;
reg [AXIS_SEG_CNT-1:0] int_seg_valid;
reg [SEG_CNT_INT*SEG_WIDTH-1:0] seg_mem_wr_data;
reg [SEG_CNT_INT-1:0] seg_mem_wr_valid;
reg [SEG_CNT_INT*INPUT_FIFO_ADDR_WIDTH-1:0] seg_mem_wr_addr_reg = 0, seg_mem_wr_addr_next;
reg [SEG_CNT_INT-1:0] seg_mem_wr_en;
reg [SEG_CNT_INT*SEG_IDX_WIDTH-1:0] seg_mem_wr_sel;
wire [SEG_CNT_INT*SEG_WIDTH-1:0] seg_mem_rd_data;
reg [SEG_CNT_INT*INPUT_FIFO_ADDR_WIDTH-1:0] seg_mem_rd_addr_reg = 0, seg_mem_rd_addr_next;
reg [SEG_CNT_INT-1:0] seg_mem_rd_en;
reg [HDR_LEN_WIDTH-1:0] hdr_mem_wr_len;
reg hdr_mem_wr_last;
reg [META_WIDTH-1:0] hdr_mem_wr_meta;
reg [HDR_FIFO_ADDR_WIDTH-1:0] hdr_mem_wr_addr;
reg hdr_mem_wr_en;
wire [HDR_LEN_WIDTH-1:0] hdr_mem_rd_len;
wire hdr_mem_rd_last;
wire [META_WIDTH-1:0] hdr_mem_rd_meta;
reg [HDR_FIFO_ADDR_WIDTH-1:0] hdr_mem_rd_addr_reg = 0, hdr_mem_rd_addr_next;
reg hdr_mem_rd_en;
reg input_fifo_full_reg = 1'b0;
reg input_fifo_half_full_reg = 1'b0;
reg input_fifo_empty_reg = 1'b1;
reg [INPUT_FIFO_PTR_WIDTH+1-1:0] input_fifo_count_reg = 0;
reg hdr_fifo_full_reg = 1'b0;
reg hdr_fifo_half_full_reg = 1'b0;
reg hdr_fifo_empty_reg = 1'b1;
reg [HDR_FIFO_PTR_WIDTH+1-1:0] hdr_fifo_count_reg = 0;
reg [SEG_CNT*SEG_WIDTH-1:0] output_data_reg = 0, output_data_next;
reg [SEG_CNT-1:0] output_valid_reg = 0, output_valid_next;
assign s_axis_tready = !input_fifo_full_reg && !hdr_fifo_full_reg && !fifo_rst_in;
assign output_data = output_data_reg;
assign output_valid = output_valid_reg;
generate
genvar n;
for (n = 0; n < SEG_CNT_INT; n = n + 1) begin : seg_ram
(* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *)
reg [SEG_WIDTH-1:0] seg_mem_data[2**INPUT_FIFO_ADDR_WIDTH-1:0];
wire wr_en = seg_mem_wr_en[n];
wire [INPUT_FIFO_ADDR_WIDTH-1:0] wr_addr = seg_mem_wr_addr_reg[n*INPUT_FIFO_ADDR_WIDTH +: INPUT_FIFO_ADDR_WIDTH];
wire [SEG_WIDTH-1:0] wr_data = seg_mem_wr_data[n*SEG_WIDTH +: SEG_WIDTH];
wire rd_en = seg_mem_rd_en[n];
wire [INPUT_FIFO_ADDR_WIDTH-1:0] rd_addr = seg_mem_rd_addr_reg[n*INPUT_FIFO_ADDR_WIDTH +: INPUT_FIFO_ADDR_WIDTH];
reg [SEG_WIDTH-1:0] rd_data_reg = 0;
assign seg_mem_rd_data[n*SEG_WIDTH +: SEG_WIDTH] = rd_data_reg;
always @(posedge clk) begin
if (wr_en) begin
seg_mem_data[wr_addr] <= wr_data;
end
end
always @(posedge clk) begin
if (rd_en) begin
rd_data_reg <= seg_mem_data[rd_addr];
end
end
end
endgenerate
(* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *)
reg [HDR_LEN_WIDTH-1:0] hdr_mem_len[2**HDR_FIFO_ADDR_WIDTH-1:0];
(* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *)
reg hdr_mem_last[2**HDR_FIFO_ADDR_WIDTH-1:0];
(* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *)
reg [META_WIDTH-1:0] hdr_mem_meta[2**HDR_FIFO_ADDR_WIDTH-1:0];
reg [HDR_LEN_WIDTH-1:0] hdr_mem_rd_len_reg = 0;
reg hdr_mem_rd_last_reg = 1'b0;
reg [META_WIDTH-1:0] hdr_mem_rd_meta_reg = 0;
assign hdr_mem_rd_len = hdr_mem_rd_len_reg;
assign hdr_mem_rd_last = hdr_mem_rd_last_reg;
assign hdr_mem_rd_meta = hdr_mem_rd_meta_reg;
always @(posedge clk) begin
if (hdr_mem_wr_en) begin
hdr_mem_len[hdr_mem_wr_addr] <= hdr_mem_wr_len;
hdr_mem_last[hdr_mem_wr_addr] <= hdr_mem_wr_last;
hdr_mem_meta[hdr_mem_wr_addr] <= hdr_mem_wr_meta;
end
end
always @(posedge clk) begin
if (hdr_mem_rd_en) begin
hdr_mem_rd_len_reg <= hdr_mem_len[hdr_mem_rd_addr_reg];
hdr_mem_rd_last_reg <= hdr_mem_last[hdr_mem_rd_addr_reg];
hdr_mem_rd_meta_reg <= hdr_mem_meta[hdr_mem_rd_addr_reg];
end
end
// limits
always @(posedge clk) begin
input_fifo_full_reg <= $unsigned(input_fifo_wr_ptr_reg - input_fifo_rd_ptr_reg) >= (2**INPUT_FIFO_ADDR_WIDTH*SEG_CNT_INT)-SEG_CNT_INT*2;
input_fifo_half_full_reg <= $unsigned(input_fifo_wr_ptr_reg - input_fifo_rd_ptr_reg) >= (2**INPUT_FIFO_ADDR_WIDTH*SEG_CNT_INT)/2;
hdr_fifo_full_reg <= $unsigned(hdr_fifo_wr_ptr_reg - hdr_fifo_rd_ptr_reg) >= 2**HDR_FIFO_ADDR_WIDTH-4;
hdr_fifo_half_full_reg <= $unsigned(hdr_fifo_wr_ptr_reg - hdr_fifo_rd_ptr_reg) >= 2**HDR_FIFO_ADDR_WIDTH/2;
if (rst) begin
input_fifo_full_reg <= 1'b0;
input_fifo_half_full_reg <= 1'b0;
hdr_fifo_full_reg <= 1'b0;
hdr_fifo_half_full_reg <= 1'b0;
end
end
// Split input segments
integer si;
always @* begin
int_seg_data = s_axis_tdata;
int_seg_valid = 0;
if (s_axis_tvalid) begin
if (s_axis_tlast) begin
for (si = 0; si < AXIS_SEG_CNT; si = si + 1) begin
int_seg_valid[si] = s_axis_tkeep[SEG_BYTE_LANES*si +: SEG_BYTE_LANES] != 0;
end
end else begin
int_seg_valid = {AXIS_SEG_CNT{1'b1}};
end
end else begin
int_seg_valid = 0;
end
end
// Write logic
integer seg, k;
reg [SEG_IDX_WIDTH+1-1:0] seg_count;
reg [SEG_IDX_WIDTH-1:0] cur_seg;
reg frame_reg = 1'b0, frame_next;
reg [HDR_LEN_WIDTH-1:0] len_reg = 0, len_next;
reg cycle_valid_reg = 1'b0, cycle_valid_next;
reg cycle_last_reg = 1'b0, cycle_last_next;
reg [CL_AXIS_BYTE_LANES+1-1:0] cycle_len_reg = 0, cycle_len_next;
reg [META_WIDTH-1:0] cycle_meta_reg = 0, cycle_meta_next;
reg [CL_AXIS_BYTE_LANES+1-1:0] cycle_len;
reg [HDR_LEN_WIDTH-1:0] hdr_len_reg = 0, hdr_len_next;
reg [META_WIDTH-1:0] hdr_meta_reg = 0, hdr_meta_next;
reg hdr_last_reg = 0, hdr_last_next;
reg hdr_commit_reg = 0, hdr_commit_next;
reg hdr_commit_prev_reg = 0, hdr_commit_prev_next;
reg hdr_valid_reg = 0, hdr_valid_next;
wire [META_WIDTH-1:0] s_axis_meta;
generate
if (AXIS_ID_ENABLE) assign s_axis_meta[META_ID_OFFSET +: AXIS_ID_WIDTH] = s_axis_tid;
if (AXIS_DEST_ENABLE) assign s_axis_meta[META_DEST_OFFSET +: AXIS_DEST_WIDTH] = s_axis_tdest;
if (AXIS_USER_ENABLE) assign s_axis_meta[META_USER_OFFSET +: AXIS_USER_WIDTH] = s_axis_tuser;
endgenerate
always @* begin
input_fifo_wr_ptr_next = input_fifo_wr_ptr_reg;
hdr_fifo_wr_ptr_next = hdr_fifo_wr_ptr_reg;
if (AXIS_KEEP_ENABLE) begin
cycle_len = 0;
for (k = 0; k < AXIS_BYTE_LANES; k = k + 1) begin
cycle_len = cycle_len + s_axis_tkeep[k];
end
end else begin
cycle_len = AXIS_BYTE_LANES;
end
// pack segments
seg_mem_wr_valid = 0;
seg_mem_wr_sel = 0;
cur_seg = input_fifo_wr_ptr_reg[SEG_IDX_WIDTH-1:0];
seg_count = 0;
for (seg = 0; seg < AXIS_SEG_CNT; seg = seg + 1) begin
if (int_seg_valid[seg]) begin
seg_mem_wr_valid[cur_seg +: 1] = 1'b1;
seg_mem_wr_sel[cur_seg*SEG_IDX_WIDTH +: SEG_IDX_WIDTH] = seg;
cur_seg = cur_seg + 1;
seg_count = seg_count + 1;
end
end
for (seg = 0; seg < SEG_CNT_INT; seg = seg + 1) begin
seg_mem_wr_data[seg*SEG_WIDTH +: SEG_WIDTH] = int_seg_data[seg_mem_wr_sel[seg*SEG_IDX_WIDTH +: SEG_IDX_WIDTH]*SEG_WIDTH +: SEG_WIDTH];
end
seg_mem_wr_addr_next = seg_mem_wr_addr_reg;
seg_mem_wr_en = 0;
hdr_mem_wr_len = hdr_len_reg;
hdr_mem_wr_last = hdr_last_reg;
hdr_mem_wr_meta = hdr_meta_reg;
hdr_mem_wr_addr = hdr_fifo_wr_ptr_reg;
hdr_mem_wr_en = 1'b0;
frame_next = frame_reg;
len_next = len_reg;
cycle_valid_next = 1'b0;
cycle_last_next = cycle_last_reg;
cycle_len_next = cycle_len_reg;
cycle_meta_next = cycle_meta_reg;
hdr_len_next = len_reg;
hdr_meta_next = cycle_meta_reg;
hdr_last_next = cycle_last_reg;
hdr_commit_next = 1'b0;
hdr_commit_prev_next = 1'b0;
hdr_valid_next = 1'b0;
if (s_axis_tvalid && s_axis_tready) begin
// transfer data
seg_mem_wr_en = seg_mem_wr_valid;
input_fifo_wr_ptr_next = input_fifo_wr_ptr_reg + seg_count;
for (seg = 0; seg < SEG_CNT_INT; seg = seg + 1) begin
seg_mem_wr_addr_next[seg*INPUT_FIFO_ADDR_WIDTH +: INPUT_FIFO_ADDR_WIDTH] = (input_fifo_wr_ptr_next + (SEG_CNT_INT-1 - seg)) >> SEG_IDX_WIDTH;
end
cycle_valid_next = 1'b1;
cycle_last_next = s_axis_tlast;
cycle_len_next = cycle_len;
cycle_meta_next = s_axis_meta;
end
if (cycle_valid_reg) begin
// process packets
if (!frame_reg) begin
frame_next = 1'b1;
if (cycle_last_reg) begin
len_next = cycle_len_reg;
end else begin
len_next = AXIS_BYTE_LANES;
end
hdr_len_next = len_next-1;
hdr_meta_next = cycle_meta_reg;
hdr_last_next = cycle_last_reg;
hdr_valid_next = 1'b1;
if (cycle_last_reg) begin
// end of frame
hdr_commit_next = 1'b1;
frame_next = 1'b0;
end
end else begin
if (cycle_meta_reg != hdr_meta_reg) begin
if (cycle_last_reg) begin
len_next = cycle_len_reg;
end else begin
len_next = AXIS_BYTE_LANES;
end
end else begin
if (cycle_last_reg) begin
len_next = len_reg + cycle_len_reg;
end else begin
len_next = len_reg + AXIS_BYTE_LANES;
end
end
hdr_len_next = len_next-1;
hdr_meta_next = cycle_meta_reg;
hdr_last_next = cycle_last_reg;
hdr_valid_next = 1'b1;
if (cycle_meta_reg != hdr_meta_reg) begin
// meta changed
hdr_commit_prev_next = 1'b1;
if (cycle_last_reg) begin
hdr_commit_next = 1'b1;
frame_next = 1'b0;
end
end else if (cycle_last_reg || len_next >= MAX_BLOCK_LEN) begin
// end of frame or block is full
hdr_commit_next = 1'b1;
frame_next = 1'b0;
end
end
end
if (hdr_valid_reg) begin
hdr_mem_wr_len = hdr_len_reg;
hdr_mem_wr_last = hdr_last_reg;
hdr_mem_wr_meta = hdr_meta_reg;
hdr_mem_wr_addr = hdr_fifo_wr_ptr_reg;
hdr_mem_wr_en = 1'b1;
if (hdr_commit_prev_reg) begin
if (hdr_commit_reg) begin
hdr_fifo_wr_ptr_next = hdr_fifo_wr_ptr_reg + 2;
hdr_mem_wr_addr = hdr_fifo_wr_ptr_reg + 1;
end else begin
hdr_fifo_wr_ptr_next = hdr_fifo_wr_ptr_reg + 1;
hdr_mem_wr_addr = hdr_fifo_wr_ptr_reg + 1;
end
end else begin
if (hdr_commit_reg) begin
hdr_fifo_wr_ptr_next = hdr_fifo_wr_ptr_reg + 1;
hdr_mem_wr_addr = hdr_fifo_wr_ptr_reg;
end
end
end
end
always @(posedge clk) begin
input_fifo_wr_ptr_reg <= input_fifo_wr_ptr_next;
hdr_fifo_wr_ptr_reg <= hdr_fifo_wr_ptr_next;
seg_mem_wr_addr_reg <= seg_mem_wr_addr_next;
frame_reg <= frame_next;
len_reg <= len_next;
cycle_valid_reg <= cycle_valid_next;
cycle_last_reg <= cycle_last_next;
cycle_len_reg <= cycle_len_next;
cycle_meta_reg <= cycle_meta_next;
hdr_len_reg <= hdr_len_next;
hdr_meta_reg <= hdr_meta_next;
hdr_last_reg <= hdr_last_next;
hdr_commit_reg <= hdr_commit_next;
hdr_commit_prev_reg <= hdr_commit_prev_next;
hdr_valid_reg <= hdr_valid_next;
if (rst || fifo_rst_in) begin
input_fifo_wr_ptr_reg <= 0;
hdr_fifo_wr_ptr_reg <= 0;
seg_mem_wr_addr_reg <= 0;
frame_reg <= 1'b0;
cycle_valid_reg <= 1'b0;
hdr_valid_reg <= 1'b0;
end
end
// Read logic
integer rd_seg;
reg [SEG_IDX_WIDTH-1:0] cur_rd_seg;
reg rd_valid;
reg out_frame_reg = 1'b0, out_frame_next;
reg [HDR_LEN_WIDTH-1:0] out_len_reg = 0, out_len_next;
reg out_split1_reg = 1'b0, out_split1_next;
reg [HDR_SEG_LEN_WIDTH-1:0] out_seg_cnt_in_reg = 0, out_seg_cnt_in_next;
reg out_seg_last_straddle_reg = 1'b0, out_seg_last_straddle_next;
reg [SEG_IDX_WIDTH-1:0] out_seg_offset_reg = 0, out_seg_offset_next;
reg [SEG_IDX_WIDTH-1:0] out_seg_fifo_offset_reg = 0, out_seg_fifo_offset_next;
reg [SEG_IDX_WIDTH+1-1:0] out_seg_count_reg = 0, out_seg_count_next;
reg [HDR_WIDTH-1:0] out_hdr_reg = 0, out_hdr_next;
reg [SEG_CNT_INT-1:0] out_ctl_seg_hdr_reg = 0, out_ctl_seg_hdr_next, out_ctl_seg_hdr_raw;
reg [SEG_CNT_INT-1:0] out_ctl_seg_split1_reg = 0, out_ctl_seg_split1_next, out_ctl_seg_split1_raw;
reg [SEG_CNT_INT-1:0] out_ctl_seg_en_reg = 0, out_ctl_seg_en_next, out_ctl_seg_en_raw;
reg [SEG_IDX_WIDTH-1:0] out_ctl_seg_idx_reg[SEG_CNT_INT-1:0], out_ctl_seg_idx_next[SEG_CNT_INT-1:0];
reg [SEG_IDX_WIDTH-1:0] out_ctl_seg_offset_reg = 0, out_ctl_seg_offset_next;
reg [HDR_WIDTH-1:0] out_shift_reg = 0, out_shift_next;
reg [7:0] block_timeout_count_reg = 0, block_timeout_count_next;
reg block_timeout_reg = 0, block_timeout_next;
always @* begin
input_fifo_rd_ptr_next = input_fifo_rd_ptr_reg;
hdr_fifo_rd_ptr_next = hdr_fifo_rd_ptr_reg;
mem_rd_data_valid_next = mem_rd_data_valid_reg;
hdr_mem_rd_data_valid_next = hdr_mem_rd_data_valid_reg;
output_data_next = output_data_reg;
output_valid_next = 0;
seg_mem_rd_addr_next = seg_mem_rd_addr_reg;
seg_mem_rd_en = 0;
hdr_mem_rd_addr_next = hdr_mem_rd_addr_reg;
hdr_mem_rd_en = 0;
out_frame_next = out_frame_reg;
out_len_next = out_len_reg;
out_split1_next = out_split1_reg;
out_seg_cnt_in_next = out_seg_cnt_in_reg;
out_seg_last_straddle_next = out_seg_last_straddle_reg;
out_seg_offset_next = out_seg_offset_reg;
out_seg_fifo_offset_next = out_seg_fifo_offset_reg;
out_hdr_next = out_hdr_reg;
out_ctl_seg_hdr_raw = 0;
out_ctl_seg_hdr_next = 0;
out_ctl_seg_split1_raw = 0;
out_ctl_seg_split1_next = 0;
out_ctl_seg_en_raw = 0;
out_ctl_seg_en_next = 0;
out_ctl_seg_offset_next = out_seg_offset_reg;
for (seg = 0; seg < SEG_CNT_INT; seg = seg + 1) begin
out_ctl_seg_idx_next[seg] = out_seg_fifo_offset_reg - out_seg_offset_reg + seg;
end
// partial block timeout handling
block_timeout_count_next = block_timeout_count_reg;
block_timeout_next = block_timeout_count_reg == 0;
if (output_valid || out_seg_offset_reg == 0) begin
block_timeout_count_next = 8'hff;
block_timeout_next = 1'b0;
end else if (block_timeout_count_reg > 0) begin
block_timeout_count_next = block_timeout_count_reg - 1;
end
// process headers and generate output commands
if (!fifo_watermark_in) begin
if (out_frame_reg) begin
if (out_seg_cnt_in_next >= SEG_CNT_INT) begin
out_frame_next = out_seg_last_straddle_next || out_seg_cnt_in_next > SEG_CNT_INT;
out_ctl_seg_en_raw = {SEG_CNT_INT{1'b1}};
out_seg_offset_next = out_seg_offset_reg + SEG_CNT_INT;
out_seg_fifo_offset_next = out_seg_fifo_offset_reg + SEG_CNT_INT;
end else begin
out_frame_next = 1'b0;
if (out_seg_last_straddle_next) begin
out_ctl_seg_split1_raw = 1 << out_seg_cnt_in_next;
if (out_seg_cnt_in_next == SEG_CNT_INT-1) begin
out_ctl_seg_en_raw = {SEG_CNT_INT{1'b1}};
end else begin
out_ctl_seg_en_raw = {SEG_CNT_INT{1'b1}} >> (SEG_CNT_INT - (out_seg_cnt_in_next+1));
end
out_seg_offset_next = out_seg_offset_reg + out_seg_cnt_in_next+1;
end else begin
out_ctl_seg_en_raw = {SEG_CNT_INT{1'b1}} >> (SEG_CNT_INT - out_seg_cnt_in_next);
out_seg_offset_next = out_seg_offset_reg + out_seg_cnt_in_next;
end
out_seg_fifo_offset_next = out_seg_fifo_offset_reg + out_seg_cnt_in_next;
end
out_seg_cnt_in_next = out_seg_cnt_in_next - SEG_CNT_INT;
end else begin
out_len_next = hdr_mem_rd_len;
out_seg_cnt_in_next = (hdr_mem_rd_len + SEG_BYTE_LANES) >> SEG_BYTE_IDX_WIDTH;
out_seg_last_straddle_next = ((hdr_mem_rd_len & (SEG_BYTE_LANES-1)) + HDR_SIZE) >> SEG_BYTE_IDX_WIDTH != 0;
out_hdr_next = 0;
out_hdr_next[0] = 1'b1;
out_hdr_next[1] = hdr_mem_rd_last;
out_hdr_next[2] = !hdr_mem_rd_last;
out_hdr_next[15:4] = hdr_mem_rd_len;
out_hdr_next[3] = ^hdr_mem_rd_len;
if (META_WIDTH > 0) begin
out_hdr_next[16 +: META_WIDTH] = hdr_mem_rd_meta;
end
out_ctl_seg_hdr_raw = 1;
if (hdr_mem_rd_data_valid_reg) begin
if (out_seg_cnt_in_next >= SEG_CNT_INT) begin
out_frame_next = out_seg_last_straddle_next || out_seg_cnt_in_next > SEG_CNT_INT;
out_ctl_seg_en_raw = {SEG_CNT_INT{1'b1}};
out_seg_offset_next = out_seg_offset_reg + SEG_CNT_INT;
out_seg_fifo_offset_next = out_seg_fifo_offset_reg + SEG_CNT_INT;
end else begin
out_frame_next = 1'b0;
if (out_seg_last_straddle_next) begin
out_ctl_seg_split1_raw = 1 << out_seg_cnt_in_next;
if (out_seg_cnt_in_next == SEG_CNT_INT-1) begin
out_ctl_seg_en_raw = {SEG_CNT_INT{1'b1}};
end else begin
out_ctl_seg_en_raw = {SEG_CNT_INT{1'b1}} >> (SEG_CNT_INT - (out_seg_cnt_in_next+1));
end
out_seg_offset_next = out_seg_offset_reg + out_seg_cnt_in_next+1;
end else begin
out_ctl_seg_en_raw = {SEG_CNT_INT{1'b1}} >> (SEG_CNT_INT - out_seg_cnt_in_next);
out_seg_offset_next = out_seg_offset_reg + out_seg_cnt_in_next;
end
out_seg_fifo_offset_next = out_seg_fifo_offset_reg + out_seg_cnt_in_next;
end
out_seg_cnt_in_next = out_seg_cnt_in_next - SEG_CNT_INT;
hdr_mem_rd_data_valid_next = 1'b0;
end else if (block_timeout_reg && out_seg_offset_reg) begin
// insert padding
out_hdr_next[15:0] = 0;
out_ctl_seg_en_raw = {SEG_CNT_INT{1'b1}} >> out_seg_offset_reg;
out_ctl_seg_hdr_raw = {SEG_CNT_INT{1'b1}};
out_ctl_seg_split1_raw = {SEG_CNT_INT{1'b1}};
out_seg_offset_next = 0;
end
end
end
out_ctl_seg_hdr_next = {2{out_ctl_seg_hdr_raw}} >> (SEG_CNT_INT - out_seg_offset_reg);
out_ctl_seg_split1_next = {2{out_ctl_seg_split1_raw}} >> (SEG_CNT_INT - out_seg_offset_reg);
out_ctl_seg_en_next = {2{out_ctl_seg_en_raw}} >> (SEG_CNT_INT - out_seg_offset_reg);
out_shift_next = out_shift_reg;
// mux segments
cur_rd_seg = out_ctl_seg_offset_reg;
for (rd_seg = 0; rd_seg < SEG_CNT_INT; rd_seg = rd_seg + 1) begin
output_data_next[cur_rd_seg*SEG_WIDTH +: SEG_WIDTH] = out_shift_next;
output_data_next[cur_rd_seg*SEG_WIDTH+HDR_WIDTH +: SEG_WIDTH-HDR_WIDTH] = seg_mem_rd_data[out_ctl_seg_idx_reg[cur_rd_seg]*SEG_WIDTH +: SEG_WIDTH-HDR_WIDTH];
if (out_ctl_seg_hdr_reg[cur_rd_seg]) begin
output_data_next[cur_rd_seg*SEG_WIDTH +: HDR_WIDTH] = out_hdr_reg;
end
output_valid_next[cur_rd_seg] = out_ctl_seg_en_reg[cur_rd_seg];
if (out_ctl_seg_en_reg[cur_rd_seg] && !out_ctl_seg_split1_reg[cur_rd_seg]) begin
mem_rd_data_valid_next[out_ctl_seg_idx_reg[cur_rd_seg]] = 1'b0;
end
if (out_ctl_seg_en_reg[cur_rd_seg]) begin
out_shift_next = seg_mem_rd_data[(out_ctl_seg_idx_reg[cur_rd_seg]+1)*SEG_WIDTH-HDR_WIDTH +: HDR_WIDTH];
end
cur_rd_seg = cur_rd_seg + 1;
end
// read segments
cur_rd_seg = input_fifo_rd_ptr_reg[SEG_IDX_WIDTH-1:0];
rd_valid = 1;
for (rd_seg = 0; rd_seg < SEG_CNT_INT; rd_seg = rd_seg + 1) begin
if (!mem_rd_data_valid_next[cur_rd_seg] && input_fifo_count_reg > rd_seg && rd_valid) begin
input_fifo_rd_ptr_next = input_fifo_rd_ptr_reg + rd_seg+1;
seg_mem_rd_en[cur_rd_seg] = 1'b1;
seg_mem_rd_addr_next[cur_rd_seg*INPUT_FIFO_ADDR_WIDTH +: INPUT_FIFO_ADDR_WIDTH] = ((input_fifo_rd_ptr_reg + rd_seg) >> SEG_IDX_WIDTH) + 1;
mem_rd_data_valid_next[cur_rd_seg] = 1'b1;
end else begin
rd_valid = 0;
end
cur_rd_seg = cur_rd_seg + 1;
end
// read header
if (!hdr_mem_rd_data_valid_next && !hdr_fifo_empty_reg) begin
hdr_fifo_rd_ptr_next = hdr_fifo_rd_ptr_reg + 1;
hdr_mem_rd_en = 1'b1;
hdr_mem_rd_addr_next = hdr_fifo_rd_ptr_next;
hdr_mem_rd_data_valid_next = 1'b1;
end
end
integer i;
always @(posedge clk) begin
input_fifo_rd_ptr_reg <= input_fifo_rd_ptr_next;
input_fifo_count_reg <= input_fifo_wr_ptr_next - input_fifo_rd_ptr_next;
input_fifo_empty_reg <= input_fifo_wr_ptr_next == input_fifo_rd_ptr_next;
hdr_fifo_rd_ptr_reg <= hdr_fifo_rd_ptr_next;
hdr_fifo_count_reg <= hdr_fifo_wr_ptr_next - hdr_fifo_rd_ptr_next;
hdr_fifo_empty_reg <= hdr_fifo_wr_ptr_next == hdr_fifo_rd_ptr_next;
seg_mem_rd_addr_reg <= seg_mem_rd_addr_next;
hdr_mem_rd_addr_reg <= hdr_mem_rd_addr_next;
mem_rd_data_valid_reg <= mem_rd_data_valid_next;
hdr_mem_rd_data_valid_reg <= hdr_mem_rd_data_valid_next;
output_data_reg <= output_data_next;
output_valid_reg <= output_valid_next;
out_frame_reg <= out_frame_next;
out_len_reg <= out_len_next;
out_split1_reg <= out_split1_next;
out_seg_cnt_in_reg <= out_seg_cnt_in_next;
out_seg_last_straddle_reg <= out_seg_last_straddle_next;
out_seg_offset_reg <= out_seg_offset_next;
out_seg_fifo_offset_reg <= out_seg_fifo_offset_next;
out_hdr_reg <= out_hdr_next;
out_ctl_seg_hdr_reg <= out_ctl_seg_hdr_next;
out_ctl_seg_split1_reg <= out_ctl_seg_split1_next;
out_ctl_seg_en_reg <= out_ctl_seg_en_next;
for (i = 0; i < SEG_CNT_INT; i = i + 1) begin
out_ctl_seg_idx_reg[i] <= out_ctl_seg_idx_next[i];
end
out_ctl_seg_offset_reg <= out_ctl_seg_offset_next;
out_shift_reg <= out_shift_next;
block_timeout_count_reg <= block_timeout_count_next;
block_timeout_reg <= block_timeout_next;
if (rst || fifo_rst_in) begin
input_fifo_rd_ptr_reg <= 0;
input_fifo_count_reg <= 0;
input_fifo_empty_reg <= 1'b1;
hdr_fifo_rd_ptr_reg <= 0;
hdr_fifo_count_reg <= 0;
hdr_fifo_empty_reg <= 1'b1;
seg_mem_rd_addr_reg <= 0;
hdr_mem_rd_addr_reg <= 0;
mem_rd_data_valid_reg <= 0;
hdr_mem_rd_data_valid_reg <= 0;
out_frame_reg <= 1'b0;
out_len_reg <= 0;
out_split1_reg <= 0;
out_seg_offset_reg <= 0;
out_seg_fifo_offset_reg <= 0;
out_seg_count_reg <= 0;
end
end
endmodule
`resetall