Files
b210-k7-fpga/lib/axi4s_sv/AxiStreamIf.sv
T
Wade Fife 287659ddeb fpga: Fix first arg in calls to $fatal()
This fixes warnings regarding the first argument to $fatal(), which is
supposed to be a number indicating what diagnostics to display. 1
corresponds to "Prints simulation time and location".


Original-commit: 58763bdfdaa276155ec6b79a3e5420de17172c7a
2022-06-28 10:34:18 -05:00

595 lines
17 KiB
Systemverilog

//
// 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(1, "Can't get trailing if TUSER doesn't exist");
assert (USER_WIDTH >= TRAILING_WIDTH) else
$fatal(1, "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(1, "Can't set trailing if TUSER doesn't exist");
assert (USER_WIDTH >= TRAILING_WIDTH) else
$fatal(1, "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