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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
|
//
// Copyright 2020 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Interface: AxiStreamIf
// Description:
// AXI4-Stream is an ARM standard for representing streams or packets in
// a design. For more information on the spec see:
// https://static.docs.arm.com/ihi0051/a/IHI0051A_amba4_axi4_stream_v1_0_protocol_spec.pdf
//
// The interface contains methods for
// (1) Monitoring progress of a packet
// (2) Extracting fields from a packet
// (3) Overwriting fields in a packet
//
// Parameters:
// - DATA_WIDTH - Width of tdata on AXI4S bus
// - USER_WIDTH - Width of tuser on AXI4S bus
// - MAX_PACKET_BYTES - Maximum number of bytes between tlast. If 0, no
// word_count resources are added.
// - TDATA - use tData if 1
// - TUSER - use tUser if 1
// - TKEEP - use tKeep if 1
// - TLAST - use tLast if 1
//
// Some Historic USRP code uses tuser without tkeep frequently.
// The usage of tuser varies, here are some examples:
// (1) Instead of using tkeep, user can encode number of valid bytes in a word
// (2) The MSB can indicate an FCS failure from the MAC
// (3) Header information is passed from xport to chdr system
//
//-----------------------------------------------------------------------------
// Unidirectional AXI4-Stream interface
//-----------------------------------------------------------------------------
// Minimal - Interface Capable of continuous assignment
interface AxiStreamIf #(
int DATA_WIDTH = 64,
int USER_WIDTH = 1,
int MAX_PACKET_BYTES = 0,
bit TDATA = 1,
bit TUSER = 1,
bit TKEEP = 1,
bit TLAST = 1
) (
input logic clk,
input logic rst = 1'b0
);
// We don't want to check this, but this is an example of how to
// check an input parameter
// initial begin
// assert (DATA_WIDTH % 8 == 0) else begin
// $display("DATA_WIDTH == %1d",DATA_WIDTH);
// $fatal(1,"Invalid data width on AxiStreamIf");
// end
// end
localparam BYTES_PER_WORD = DATA_WIDTH/8;
localparam DWIDTH = TDATA ? DATA_WIDTH : 0;
localparam UWIDTH = TUSER ? USER_WIDTH : 0;
localparam KWIDTH = TKEEP ? DATA_WIDTH/8 : 0;
localparam LWIDTH = TLAST ? 1 : 0;
localparam PACKED_WIDTH = DWIDTH + UWIDTH + KWIDTH + LWIDTH ;
// local type defs
typedef logic [DATA_WIDTH-1:0] data_t;
typedef logic [USER_WIDTH-1:0] user_t;
typedef logic [DATA_WIDTH/8-1:0] keep_t;
// Signals that make up a unidirectional AXI-Stream interface
logic tlast;
logic tvalid;
logic tready;
data_t tdata;
user_t tuser;
keep_t tkeep;
//---------------------------------------
// Trailing Bytes/Keep functions
// Trailing byte of 0 means full word else trailing bytes is the
// number of bytes in the last word.
//---------------------------------------
// bits needed to represent trailing bytes
localparam TRAILING_WIDTH = $clog2(DATA_WIDTH/8);
typedef logic [TRAILING_WIDTH-1:0] trailing_bytes_t;
// translate between bytes and tkeep
function automatic trailing_bytes_t keep2trailing(keep_t keep);
trailing_bytes_t bytes = '0;
if (tlast == 1) begin
// mux between values based on the high bit = 1
for(int b = 0; b < DATA_WIDTH/8 ; b++) begin
if (keep[b]) bytes = b+1;
end
end
return bytes;
endfunction : keep2trailing
function automatic keep_t trailing2keep(trailing_bytes_t bytes);
keep_t keep = '1;
if (tlast == 1 && bytes != 0) begin
foreach(keep[b]) begin
keep[b] = bytes > b;
end
end
return keep;
endfunction : trailing2keep
// View from the master side
modport master (
input clk, rst,
output tvalid, tdata, tuser, tkeep, tlast,
input tready,
import keep2trailing,
import trailing2keep
);
// View from the slave side
modport slave (
input clk, rst,
input tvalid, tdata, tuser, tkeep, tlast,
output tready,
import keep2trailing,
import trailing2keep
);
endinterface : AxiStreamIf
// Full featured for packet modification. Must use procedural assignment.
interface AxiStreamPacketIf #(
int DATA_WIDTH = 64,
int USER_WIDTH = 1,
int MAX_PACKET_BYTES = 0,
bit TDATA = 1,
bit TUSER = 1,
bit TKEEP = 1,
bit TLAST = 1
) (
input logic clk,
input logic rst = 1'b0
);
// We don't want to check this, but this is an example of how to
// check an input parameter:
// initial begin
// assert (DATA_WIDTH % 8 == 0) else begin
// $display("DATA_WIDTH == %1d",DATA_WIDTH);
// $fatal(1,"Invalid data width on AxiStreamIf");
// end
// end
localparam BYTES_PER_WORD = DATA_WIDTH/8;
localparam MAX_PACKET_WORDS = MAX_PACKET_BYTES/BYTES_PER_WORD;
// local type defs
typedef logic [DATA_WIDTH-1:0] data_t;
typedef logic [USER_WIDTH-1:0] user_t;
typedef logic [DATA_WIDTH/8-1:0] keep_t;
typedef logic [$clog2(MAX_PACKET_WORDS)-1:0] word_count_t;
// Signals that make up a unidirectional AXI-Stream interface
logic tlast;
logic tvalid;
logic tready;
data_t tdata;
user_t tuser;
keep_t tkeep;
word_count_t word_count = 0;
if (MAX_PACKET_BYTES>0) begin
always_ff @(posedge clk) begin
if (rst) begin
word_count <= 0;
end else begin
// reset at last
if (tlast && tvalid && tready) begin
word_count <= 0;
// increment when valid and ready
end else if (tvalid && tready)begin
word_count <= word_count+1;
end
end
end
end
//---------------------------------------
// Packing functions
//---------------------------------------
localparam DWIDTH = TDATA ? DATA_WIDTH : 0;
localparam UWIDTH = TUSER ? USER_WIDTH : 0;
localparam KWIDTH = TKEEP ? DATA_WIDTH/8 : 0;
localparam LWIDTH = TLAST ? 1 : 0;
localparam PACKED_WIDTH = DWIDTH + UWIDTH + KWIDTH + LWIDTH ;
typedef logic [PACKED_WIDTH-1:0] packed_t;
function automatic packed_t pack(INC_DATA=1,INC_USER=1,INC_KEEP=1,INC_LAST=1);
logic [PACKED_WIDTH-1:0] data = 'X;
int USTART;
int KSTART;
int LSTART;
USTART = INC_DATA ? DWIDTH :0;
KSTART = INC_USER ? USTART+UWIDTH :USTART;
LSTART = INC_KEEP ? KSTART+KWIDTH :KSTART;
if (TDATA && INC_DATA) begin
// in the LSB
data[0+:DATA_WIDTH] = tdata;
end
if (TUSER && INC_USER) begin
// in the 1st MIDDLE
data[USTART+:USER_WIDTH] = tuser;
end
if (TKEEP && INC_KEEP) begin
// in the 2nd MIDDLE
data[KSTART+:DATA_WIDTH/8] = tkeep;
end
if (TLAST && INC_LAST) begin
// in the MSB
data[LSTART] = tlast;
end
return data;
endfunction : pack
task automatic unpack (packed_t data,INC_DATA=1,INC_USER=1,INC_KEEP=1,INC_LAST=1);
int USTART;
int KSTART;
int LSTART;
USTART = INC_DATA ? DWIDTH :0;
KSTART = INC_USER ? USTART+UWIDTH :USTART;
LSTART = INC_KEEP ? KSTART+KWIDTH :KSTART;
if (TDATA && INC_DATA) begin
// in the LSB
tdata = data[0+:DATA_WIDTH];
end
if (TUSER && INC_USER) begin
// in the 1st MIDDLE
tuser = data[USTART+:USER_WIDTH];
end
if (TKEEP && INC_KEEP) begin
// in the 2nd MIDDLE
tkeep = data[KSTART+:DATA_WIDTH/8];
end
if (TLAST && INC_LAST) begin
// in the MSB
tlast = data[LSTART];
end
endtask : unpack
//---------------------------------------
// Trailing Bytes/Keep functions
// Trailing byte of 0 means full word else trailing bytes is the
// number of bytes in the last word.
//---------------------------------------
// bits needed to represent trailing bytes
localparam TRAILING_WIDTH = $clog2(DATA_WIDTH/8);
typedef logic [TRAILING_WIDTH-1:0] trailing_bytes_t;
// translate between bytes and tkeep
function automatic trailing_bytes_t keep2trailing(keep_t keep);
trailing_bytes_t bytes = '0;
if (tlast == 1) begin
// mux between values based on the high bit = 1
for(int b = 0; b < DATA_WIDTH/8 ; b++) begin
if (keep[b]) bytes = b+1;
end
end
return bytes;
endfunction : keep2trailing
function automatic keep_t trailing2keep(trailing_bytes_t bytes);
keep_t keep = '1;
if (tlast == 1 && bytes != 0) begin
foreach(keep[b]) begin
keep[b] = bytes > b;
end
end
return keep;
endfunction : trailing2keep
// do the translation directly to/from the ifc
function automatic keep_t get_trailing_bytes();
localparam USER_TRAILING_WIDTH =
USER_WIDTH >= TRAILING_WIDTH ? TRAILING_WIDTH : USER_WIDTH;
assert (TUSER) else
$fatal("Can't get trailing if TUSER doesn't exist");
assert (USER_WIDTH >= TRAILING_WIDTH) else
$fatal("USER_WIDTH is to narrow to contain trailing");
return trailing2keep(tuser[USER_TRAILING_WIDTH-1:0]);
endfunction : get_trailing_bytes
task automatic set_trailing_bytes(keep_t keep);
localparam USER_TRAILING_WIDTH =
USER_WIDTH >= TRAILING_WIDTH ? TRAILING_WIDTH : USER_WIDTH;
assert (TUSER) else
$fatal("Can't set trailing if TUSER doesn't exist");
assert (USER_WIDTH >= TRAILING_WIDTH) else
$fatal("USER_WIDTH is to narrow to set trailing");
tuser[USER_TRAILING_WIDTH-1:0] = keep2trailing(keep);
endtask : set_trailing_bytes
//---------------------------------------
// Packet PROGRESS functions
//---------------------------------------
// Notify when a byte is reached(VALID)
function automatic bit reached_packet_byte(
input int OFFSET
);
// constant because they only depend on offset
int WORD_OFFSET;
assert (MAX_PACKET_BYTES > 0) else
$fatal(1,"checking packet on a non packet bus");
// Constant
WORD_OFFSET = OFFSET / (DATA_WIDTH/8);
return (word_count == WORD_OFFSET);
endfunction : reached_packet_byte
// Notify when a byte is being transmitted(VALID && READY)
function automatic bit xfering_packet_byte(
input int OFFSET
);
return reached_packet_byte(OFFSET) && tvalid && tready;
endfunction : xfering_packet_byte
// drive functions (These are not particularly useful
// but they guarantee the modules using the package don't
// drive the interface with a continuous assignment
task automatic drive_tlast(input logic last);
tlast = last;
endtask
task automatic drive_tvalid(input logic valid);
tvalid = valid;
endtask
task automatic drive_tready(input logic ready);
tready = ready;
endtask
task automatic drive_tdata(input logic data);
tdata = data;
endtask
task automatic drive_tuser(input logic user);
tuser = user;
endtask
task automatic drive_tkeep(input logic keep);
tkeep = keep;
endtask
function automatic word_count_t get_word_count();
return word_count;
endfunction
//---------------------------------------
// Packet FIELD READ functions
//
// USAGE:
//
// // get the fields - don't use assign. assign will not activate with changes to in1.
// always_comb begin : get_fields
// eth_dst_addr_new = in1.get_packet_field48(eth_dst_addr_old,DST_MAC_BYTE,.NETWORK_ORDER(1));
// reached_end_of_udp = in1.reached_packet_byte(DST_PORT_BYTE+3);// we have enough to decide
// end
//
// // use the fields
// always_comb begin : use_fields
// if (reached_end_of_udp) begin // Wait until you know the field is fully captured before using it!
// ** DO SOMETHING INTERESTING WITH est_dst_addr_new
// end
//---------------------------------------
// Grab a byte from a passing stream
function automatic logic [7:0] get_packet_byte(
input logic [7:0] old_value,
input int OFFSET
);
// constant because they only depend on offset
int BYTE_OFFSET;
logic [7:0] new_value;
// Constant
BYTE_OFFSET = OFFSET % (DATA_WIDTH/8);
// default : Stay the same
new_value = old_value;
if (reached_packet_byte(OFFSET)) begin
new_value = tdata[BYTE_OFFSET*8+:8];
end
return new_value;
endfunction : get_packet_byte
// Grab a 16 bit field from a passing stream
function automatic logic [15:0] get_packet_field16(
input logic [15:0] old_value,
input int OFFSET,
input int NETWORK_ORDER=0
);
logic [15:0] new_value;
localparam BYTES=$size(new_value)/8;
for (int i=0;i < BYTES; i++) begin
if (NETWORK_ORDER==1) begin
new_value[i*8+:8] = get_packet_byte(old_value[i*8+:8],OFFSET+BYTES-1-i);
end else begin
new_value[i*8+:8] = get_packet_byte(old_value[i*8+:8],OFFSET+i);
end
end
return new_value;
endfunction : get_packet_field16
// Grab a 32 bit field from a passing stream
function automatic logic [31:0] get_packet_field32(
input logic [31:0] old_value,
input int OFFSET,
input int NETWORK_ORDER=0
);
logic [31:0] new_value;
localparam BYTES=$size(new_value)/8;
for (int i=0;i < BYTES; i++) begin
if (NETWORK_ORDER==1) begin
new_value[i*8+:8] = get_packet_byte(old_value[i*8+:8],OFFSET+BYTES-1-i);
end else begin
new_value[i*8+:8] = get_packet_byte(old_value[i*8+:8],OFFSET+i);
end
end
return new_value;
endfunction : get_packet_field32
// Grab a 48 bit field from a passing stream
function automatic logic [47:0] get_packet_field48(
input logic [47:0] old_value,
input int OFFSET,
input int NETWORK_ORDER=0
);
logic [47:0] new_value;
localparam BYTES=$size(new_value)/8;
for (int i=0;i < BYTES; i++) begin
if (NETWORK_ORDER==1) begin
new_value[i*8+:8] = get_packet_byte(old_value[i*8+:8],OFFSET+BYTES-1-i);
end else begin
new_value[i*8+:8] = get_packet_byte(old_value[i*8+:8],OFFSET+i);
end
end
return new_value;
endfunction : get_packet_field48
//---------------------------------------
// Packet FIELD OVERWRITE functions
//
// USAGE:
//
// // Each call muxes data as it goes by. This chains together muxing which synthesis will simplify
// always_comb begin : set_header_fields
// v2e5.tdata = v2e4.tdata;
// v2e5.put_packet_field48(mac_dst,DST_MAC_BYTE,.NETWORK_ORDER(1));
// end
//---------------------------------------
// Overwrite a byte in a passing stream
task automatic put_packet_byte(
input logic [7:0] new_value,
input int OFFSET
);
// constant because they only depend on offset
int BYTE_OFFSET;
// Constant
BYTE_OFFSET = OFFSET % (DATA_WIDTH/8);
if (reached_packet_byte(OFFSET)) begin
tdata[BYTE_OFFSET*8+:8] = new_value;
end
endtask : put_packet_byte
// Overwrite a 16 bit field in a passing stream
task automatic put_packet_field16(
input logic [15:0] new_value,
input int OFFSET,
input int NETWORK_ORDER=0
);
localparam BYTES=$size(new_value)/8;
for (int i=0;i < BYTES; i++) begin
if (NETWORK_ORDER==1) begin
put_packet_byte(new_value[i*8+:8],OFFSET+BYTES-1-i);
end else begin
put_packet_byte(new_value[i*8+:8],OFFSET+i);
end
end
endtask : put_packet_field16
// Overwrite a 32 bit field in a passing stream
task automatic put_packet_field32(
input logic [31:0] new_value,
input int OFFSET,
input int NETWORK_ORDER=0
);
localparam BYTES=$size(new_value)/8;
for (int i=0;i < BYTES; i++) begin
if (NETWORK_ORDER==1) begin
put_packet_byte(new_value[i*8+:8],OFFSET+BYTES-1-i);
end else begin
put_packet_byte(new_value[i*8+:8],OFFSET+i);
end
end
endtask : put_packet_field32
// Overwrite a 48 bit field in a passing stream
task automatic put_packet_field48(
input logic [47:0] new_value,
input int OFFSET,
input int NETWORK_ORDER=0
);
localparam BYTES=$size(new_value)/8;
for (int i=0;i < BYTES; i++) begin
if (NETWORK_ORDER==1) begin
put_packet_byte(new_value[i*8+:8],OFFSET+BYTES-1-i);
end else begin
put_packet_byte(new_value[i*8+:8],OFFSET+i);
end
end
endtask : put_packet_field48
// View from the master side
modport master (
input clk, rst,
output tvalid, tdata, tuser, tkeep, tlast,
input tready,
//import methods
import pack,
import unpack,
import keep2trailing,
import trailing2keep,
import get_trailing_bytes,
import set_trailing_bytes,
import get_word_count,
import reached_packet_byte,
import xfering_packet_byte,
import get_packet_byte,
import get_packet_field16,
import get_packet_field32,
import get_packet_field48,
import put_packet_byte,
import put_packet_field16,
import put_packet_field32,
import put_packet_field48
);
// View from the slave side
modport slave (
input clk, rst,
input tvalid, tdata, tuser, tkeep, tlast,
output tready,
//import methods
import pack,
import unpack,
import keep2trailing,
import trailing2keep,
import get_trailing_bytes,
import set_trailing_bytes,
import get_word_count,
import reached_packet_byte,
import xfering_packet_byte,
import get_packet_byte,
import get_packet_field16,
import get_packet_field32,
import get_packet_field48,
import put_packet_byte,
import put_packet_field16,
import put_packet_field32,
import put_packet_field48
);
endinterface : AxiStreamPacketIf
|