1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
|
//
// Copyright 2018 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axis_width_conv
// Description:
// An AXI-Stream width conversion module that can convert from
// an arbitrary input width to an arbitrary output width. The
// module also supports an optional clock crossing. Data bits
// are grouped into words which will be rearranged by this module.
// The contents of a word are not rearranged.
// Example (WORD_W=4, IN_WORDS=4, OUT_WORDS=6):
// Input : 3_1_2_0, x_6_5_4 (comma-delimited packets)
// Output : 5_4_3_2_1_0, x_x_x_x_x_6 (comma-delimited packets)
// NOTE: The use of tkeep in this module is a slight deviation from
// the AXI standard where the bits are "byte qualifiers". In
// this module, tkeep is a "word qualifier" where the width
// of a word can be arbitrary. If WORD_W = 8, the behavior
// of this module is identical to an AXI width converter.
//
// Parameters:
// - WORD_W: Bitwidth of a word
// - IN_WORDS: Number of words in the input stream
// - OUT_WORDS: Number of words in the output stream
// - SYNC_CLKS: Are s_axis_aclk and m_axis_aclk synchronous to each other?
// - PIPELINE: Which ports to pipeline? {NONE, IN, OUT, INOUT}
//
// Signals:
// - s_axis_* : Input sample stream (AXI-Stream)
// - m_axis_* : Output sample stream (AXI-Stream)
module axis_width_conv #(
parameter WORD_W = 8,
parameter IN_WORDS = 4,
parameter OUT_WORDS = 6,
parameter SYNC_CLKS = 0,
parameter PIPELINE = "NONE"
)(
// Data In (AXI-Stream)
input wire s_axis_aclk, // Input stream Clock
input wire s_axis_rst, // Input stream Reset
input wire [(IN_WORDS*WORD_W)-1:0] s_axis_tdata, // Input stream tdata
input wire [IN_WORDS-1:0] s_axis_tkeep, // Input stream tkeep
input wire s_axis_tlast, // Input stream tlast
input wire s_axis_tvalid, // Input stream tvalid
output wire s_axis_tready, // Input stream tready
// Data Out (AXI-Stream)
input wire m_axis_aclk, // Output stream Clock
input wire m_axis_rst, // Output stream Reset
output wire [(OUT_WORDS*WORD_W)-1:0] m_axis_tdata, // Output stream tdata
output wire [OUT_WORDS-1:0] m_axis_tkeep, // Output stream tkeep
output wire m_axis_tlast, // Output stream tlast
output wire m_axis_tvalid, // Output stream tvalid
input wire m_axis_tready // Output stream tready
);
//----------------------------------------------
// Pipeline Logic
//----------------------------------------------
// Add optional input and output pipeline stages
wire [(IN_WORDS*WORD_W)-1:0] i_tdata;
wire [IN_WORDS-1:0] i_tkeep;
wire i_tlast, i_tvalid, i_tready;
wire [(OUT_WORDS*WORD_W)-1:0] o_tdata;
wire [OUT_WORDS-1:0] o_tkeep;
wire o_tlast, o_tvalid, o_tready;
generate
if (PIPELINE == "IN" || PIPELINE == "INOUT") begin
axi_fifo_flop2 #(.WIDTH((IN_WORDS*(WORD_W+1))+1)) in_pipe_i (
.clk(s_axis_aclk), .reset(s_axis_rst), .clear(1'b0),
.i_tdata({s_axis_tlast, s_axis_tkeep, s_axis_tdata}),
.i_tvalid(s_axis_tvalid), .i_tready(s_axis_tready),
.o_tdata({i_tlast, i_tkeep, i_tdata}),
.o_tvalid(i_tvalid), .o_tready(i_tready),
.space(), .occupied()
);
end else begin
assign {i_tlast, i_tkeep, i_tdata} = {s_axis_tlast, s_axis_tkeep, s_axis_tdata};
assign i_tvalid = s_axis_tvalid;
assign s_axis_tready = i_tready;
end
if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin
axi_fifo_flop2 #(.WIDTH((OUT_WORDS*(WORD_W+1))+1)) out_pipe_i (
.clk(m_axis_aclk), .reset(m_axis_rst), .clear(1'b0),
.i_tdata({o_tlast, o_tkeep, o_tdata}),
.i_tvalid(o_tvalid), .i_tready(o_tready),
.o_tdata({m_axis_tlast, m_axis_tkeep, m_axis_tdata}),
.o_tvalid(m_axis_tvalid), .o_tready(m_axis_tready),
.space(), .occupied()
);
end else begin
assign {m_axis_tlast, m_axis_tkeep, m_axis_tdata} = {o_tlast, o_tkeep, o_tdata};
assign m_axis_tvalid = o_tvalid;
assign o_tready = m_axis_tready;
end
endgenerate
//----------------------------------------------
// Intermediate Data Bus
//----------------------------------------------
// To perform an M to N width conversion, we first
// convert from M to LCM(M, N), then to N
// Function to compute the least common multiple
// of two numbers (parameters or localparams only)
function integer lcm;
input integer a;
input integer b;
integer x, y, swap;
reg done;
begin
done = 1'b0;
x = a;
y = b;
while (!done) begin
if (x < y) begin
swap = x;
x = y;
y = swap;
end else if (y != 0) begin
x = x - y;
end else begin
done = 1'b1;
end
end
// x is the greatest common divisor
// LCM = (a*b)/GCD
lcm = (a*b)/x;
end
endfunction
// Intermediate bus parameters
localparam integer INT_KEEP_W = lcm(IN_WORDS, OUT_WORDS);
localparam integer INT_DATA_W = INT_KEEP_W * WORD_W;
localparam integer UPSIZE_RATIO = INT_KEEP_W / IN_WORDS;
localparam integer DOWNSIZE_RATIO = INT_KEEP_W / OUT_WORDS;
wire [INT_DATA_W-1:0] fifo_i_tdata, fifo_o_tdata;
wire [INT_KEEP_W-1:0] fifo_i_tkeep, fifo_o_tkeep;
wire fifo_i_tlast, fifo_i_tvalid, fifo_i_tready;
wire fifo_o_tlast, fifo_o_tvalid, fifo_o_tready;
// Skip the intermediate FIFO if
// - The input and output clocks are the same
// - The upsizer is effectively a passthrough and input registering is requested
// - The downsizer is effectively a passthrough and output registering is requested
localparam [0:0] SKIP_FIFO = (SYNC_CLKS == 1) && (
((PIPELINE == "IN" || PIPELINE == "INOUT") && (UPSIZE_RATIO == 1)) ||
((PIPELINE == "OUT" || PIPELINE == "INOUT") && (DOWNSIZE_RATIO == 1))
);
localparam FIFO_SIZE = 1;
//----------------------------------------------
// In => Upsizer => FIFO => Downsizer => Out
//----------------------------------------------
wire [INT_KEEP_W-1:0] up_keep_flat;
wire [UPSIZE_RATIO-1:0] up_keep_keep;
wire [DOWNSIZE_RATIO-1:0] down_keep_keep;
axis_upsizer #(
.IN_DATA_W(IN_WORDS*WORD_W), .IN_USER_W(IN_WORDS),
.RATIO(UPSIZE_RATIO)
) upsizer_i (
.clk(s_axis_aclk), .reset(s_axis_rst),
.s_axis_tdata(i_tdata), .s_axis_tuser(i_tkeep),
.s_axis_tlast(i_tlast), .s_axis_tvalid(i_tvalid), .s_axis_tready(i_tready),
.m_axis_tdata(fifo_i_tdata), .m_axis_tuser(up_keep_flat), .m_axis_tkeep(up_keep_keep),
.m_axis_tlast(fifo_i_tlast), .m_axis_tvalid(fifo_i_tvalid), .m_axis_tready(fifo_i_tready)
);
// tkeep unmasking logic after upsizer
genvar i;
generate for (i = 0; i < INT_KEEP_W; i = i + 1) begin
// tkeep is assumed to be valid only when tlast is asserted
// otherwise it is 1
assign fifo_i_tkeep[i] = ~fifo_i_tlast |
(up_keep_keep[i/IN_WORDS] ? up_keep_flat[i] : 1'b0);
end endgenerate
generate
if (SKIP_FIFO) begin
assign fifo_o_tdata = fifo_i_tdata;
assign fifo_o_tkeep = fifo_i_tkeep;
assign fifo_o_tlast = fifo_i_tlast;
assign fifo_o_tvalid = fifo_i_tvalid;
assign fifo_i_tready = fifo_o_tready;
end else begin
if (SYNC_CLKS) begin
axi_fifo #(.WIDTH(INT_DATA_W+INT_KEEP_W+1), .SIZE(FIFO_SIZE)) fifo_i (
.clk(s_axis_aclk), .reset(s_axis_rst), .clear(1'b0),
.i_tdata({fifo_i_tlast, fifo_i_tkeep, fifo_i_tdata}),
.i_tvalid(fifo_i_tvalid), .i_tready(fifo_i_tready),
.o_tdata({fifo_o_tlast, fifo_o_tkeep, fifo_o_tdata}),
.o_tvalid(fifo_o_tvalid), .o_tready(fifo_o_tready),
.space(), .occupied()
);
end else begin
axi_fifo_2clk #(.WIDTH(INT_DATA_W+INT_KEEP_W+1), .SIZE(FIFO_SIZE)) fifo_i (
.reset(s_axis_rst),
.i_aclk(s_axis_aclk),
.i_tdata({fifo_i_tlast, fifo_i_tkeep, fifo_i_tdata}),
.i_tvalid(fifo_i_tvalid), .i_tready(fifo_i_tready),
.o_aclk(m_axis_aclk),
.o_tdata({fifo_o_tlast, fifo_o_tkeep, fifo_o_tdata}),
.o_tvalid(fifo_o_tvalid), .o_tready(fifo_o_tready)
);
end
end
endgenerate
// tkeep masking logic after downsizer
generate for (i = 0; i < DOWNSIZE_RATIO; i = i + 1) begin
assign down_keep_keep[i] = |fifo_o_tkeep[i*OUT_WORDS+:OUT_WORDS];
end endgenerate
axis_downsizer #(
.OUT_DATA_W(OUT_WORDS*WORD_W), .OUT_USER_W(OUT_WORDS),
.RATIO(DOWNSIZE_RATIO)
) downsizer_i (
.clk(m_axis_aclk), .reset(m_axis_rst),
.s_axis_tdata(fifo_o_tdata), .s_axis_tuser(fifo_o_tkeep), .s_axis_tkeep(down_keep_keep),
.s_axis_tlast(fifo_o_tlast), .s_axis_tvalid(fifo_o_tvalid), .s_axis_tready(fifo_o_tready),
.m_axis_tdata(o_tdata), .m_axis_tuser(o_tkeep),
.m_axis_tlast(o_tlast), .m_axis_tvalid(o_tvalid), .m_axis_tready(o_tready)
);
endmodule // axis_width_conv
|