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
|