Commit 738c1fef authored by Antoine Kaufmann's avatar Antoine Kaufmann
Browse files

add corundum and verilator build files

parent 7f925101
#!/usr/bin/env python
"""
Generates an AXI Stream frame joiner wrapper with the specified number of ports
"""
from __future__ import print_function
import argparse
import math
from jinja2 import Template
def main():
parser = argparse.ArgumentParser(description=__doc__.strip())
parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports")
parser.add_argument('-n', '--name', type=str, help="module name")
parser.add_argument('-o', '--output', type=str, help="output file name")
args = parser.parse_args()
try:
generate(**args.__dict__)
except IOError as ex:
print(ex)
exit(1)
def generate(ports=4, name=None, output=None):
n = ports
if name is None:
name = "axis_frame_join_wrap_{0}".format(n)
if output is None:
output = name + ".v"
print("Opening file '{0}'...".format(output))
output_file = open(output, 'w')
print("Generating {0} port AXI stream frame joiner wrapper {1}...".format(n, name))
cn = int(math.ceil(math.log(n, 2)))
t = Template(u"""/*
Copyright (c) 2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream {{n}} port frame joiner (wrapper)
*/
module {{name}} #
(
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 8,
// Prepend data with tag
parameter TAG_ENABLE = 1,
// Tag field width
parameter TAG_WIDTH = 16
)
(
input wire clk,
input wire rst,
/*
* AXI Stream inputs
*/
{%- for p in range(n) %}
input wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axis_tdata,
input wire s{{'%02d'%p}}_axis_tvalid,
output wire s{{'%02d'%p}}_axis_tready,
input wire s{{'%02d'%p}}_axis_tlast,
input wire s{{'%02d'%p}}_axis_tuser,
{% endfor %}
/*
* AXI Stream output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire m_axis_tuser,
/*
* Configuration
*/
input wire [TAG_WIDTH-1:0] tag,
/*
* Status signals
*/
output wire busy
);
axis_frame_join #(
.S_COUNT({{n}}),
.DATA_WIDTH(DATA_WIDTH),
.TAG_ENABLE(TAG_ENABLE),
.TAG_WIDTH(TAG_WIDTH)
)
axis_frame_join_inst (
.clk(clk),
.rst(rst),
// AXI inputs
.s_axis_tdata({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tvalid({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tready({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tlast({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tuser({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }),
// AXI output
.m_axis_tdata(m_axis_tdata),
.m_axis_tvalid(m_axis_tvalid),
.m_axis_tready(m_axis_tready),
.m_axis_tlast(m_axis_tlast),
.m_axis_tuser(m_axis_tuser),
// Configuration
.tag(tag),
// Status
.busy(busy)
);
endmodule
""")
output_file.write(t.render(
n=n,
cn=cn,
name=name
))
print("Done")
if __name__ == "__main__":
main()
/*
Copyright (c) 2019 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
`timescale 1ns / 1ps
/*
* AXI4-Stream frame length measurement
*/
module axis_frame_len #
(
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 64,
// Propagate tkeep signal
// If disabled, tkeep assumed to be 1'b1
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Width of length counter
parameter LEN_WIDTH = 16
)
(
input wire clk,
input wire rst,
/*
* AXI monitor
*/
input wire [KEEP_WIDTH-1:0] monitor_axis_tkeep,
input wire monitor_axis_tvalid,
input wire monitor_axis_tready,
input wire monitor_axis_tlast,
/*
* Status
*/
output wire [LEN_WIDTH-1:0] frame_len,
output wire frame_len_valid
);
reg [LEN_WIDTH-1:0] frame_len_reg = 0, frame_len_next;
reg frame_len_valid_reg = 1'b0, frame_len_valid_next;
reg frame_reg = 1'b0, frame_next;
assign frame_len = frame_len_reg;
assign frame_len_valid = frame_len_valid_reg;
integer offset, i, bit_cnt;
always @* begin
frame_len_next = frame_len_reg;
frame_len_valid_next = 1'b0;
frame_next = frame_reg;
if (monitor_axis_tready && monitor_axis_tvalid) begin
// valid transfer cycle
if (monitor_axis_tlast) begin
// end of frame
frame_len_valid_next = 1'b1;
frame_next = 1'b0;
end else if (!frame_reg) begin
// first word after end of frame
frame_len_next = 0;
frame_next = 1'b1;
end
// increment frame length by number of words transferred
if (KEEP_ENABLE) begin
bit_cnt = 0;
for (i = 0; i <= KEEP_WIDTH; i = i + 1) begin
if (monitor_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) bit_cnt = i;
end
frame_len_next = frame_len_next + bit_cnt;
end else begin
frame_len_next = frame_len_next + 1;
end
end
end
always @(posedge clk) begin
if (rst) begin
frame_len_reg <= 0;
frame_len_valid_reg <= 0;
frame_reg <= 1'b0;
end else begin
frame_len_reg <= frame_len_next;
frame_len_valid_reg <= frame_len_valid_next;
frame_reg <= frame_next;
end
end
endmodule
/*
Copyright (c) 2015-2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream frame length adjuster
*/
module axis_frame_length_adjust #
(
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 8,
// Propagate tkeep signal
// If disabled, tkeep assumed to be 1'b1
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// Propagate tdest signal
parameter DEST_ENABLE = 0,
// tdest signal width
parameter DEST_WIDTH = 8,
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1
)
(
input wire clk,
input wire rst,
/*
* AXI input
*/
input wire [DATA_WIDTH-1:0] s_axis_tdata,
input wire [KEEP_WIDTH-1:0] s_axis_tkeep,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire [ID_WIDTH-1:0] s_axis_tid,
input wire [DEST_WIDTH-1:0] s_axis_tdest,
input wire [USER_WIDTH-1:0] s_axis_tuser,
/*
* AXI output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [ID_WIDTH-1:0] m_axis_tid,
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser,
/*
* Status
*/
output wire status_valid,
input wire status_ready,
output wire status_frame_pad,
output wire status_frame_truncate,
output wire [15:0] status_frame_length,
output wire [15:0] status_frame_original_length,
/*
* Configuration
*/
input wire [15:0] length_min,
input wire [15:0] length_max
);
// bus word width
localparam DATA_WORD_WIDTH = DATA_WIDTH / KEEP_WIDTH;
// bus width assertions
initial begin
if (DATA_WORD_WIDTH * KEEP_WIDTH != DATA_WIDTH) begin
$error("Error: data width not evenly divisble (instance %m)");
$finish;
end
end
// state register
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_TRANSFER = 3'd1,
STATE_PAD = 3'd2,
STATE_TRUNCATE = 3'd3;
reg [2:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
reg store_last_word;
reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next;
reg [DATA_WIDTH-1:0] s_axis_tdata_masked;
// frame length counters
reg [15:0] short_counter_reg = 16'd0, short_counter_next = 16'd0;
reg [15:0] long_counter_reg = 16'd0, long_counter_next = 16'd0;
reg [DATA_WIDTH-1:0] last_word_data_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] last_word_keep_reg = {KEEP_WIDTH{1'b0}};
reg [ID_WIDTH-1:0] last_word_id_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] last_word_dest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] last_word_user_reg = {USER_WIDTH{1'b0}};
reg status_valid_reg = 1'b0, status_valid_next;
reg status_frame_pad_reg = 1'b0, status_frame_pad_next;
reg status_frame_truncate_reg = 1'b0, status_frame_truncate_next;
reg [15:0] status_frame_length_reg = 16'd0, status_frame_length_next;
reg [15:0] status_frame_original_length_reg = 16'd0, status_frame_original_length_next;
// internal datapath
reg [DATA_WIDTH-1:0] m_axis_tdata_int;
reg [KEEP_WIDTH-1:0] m_axis_tkeep_int;
reg m_axis_tvalid_int;
reg m_axis_tready_int_reg = 1'b0;
reg m_axis_tlast_int;
reg [ID_WIDTH-1:0] m_axis_tid_int;
reg [DEST_WIDTH-1:0] m_axis_tdest_int;
reg [USER_WIDTH-1:0] m_axis_tuser_int;
wire m_axis_tready_int_early;
reg s_axis_tready_reg = 1'b0, s_axis_tready_next;
assign s_axis_tready = s_axis_tready_reg;
assign status_valid = status_valid_reg;
assign status_frame_pad = status_frame_pad_reg;
assign status_frame_truncate = status_frame_truncate_reg;
assign status_frame_length = status_frame_length_reg;
assign status_frame_original_length = status_frame_original_length_reg;
integer i, word_cnt;
always @* begin
state_next = STATE_IDLE;
store_last_word = 1'b0;
frame_ptr_next = frame_ptr_reg;
short_counter_next = short_counter_reg;
long_counter_next = long_counter_reg;
m_axis_tdata_int = {DATA_WIDTH{1'b0}};
m_axis_tkeep_int = {KEEP_WIDTH{1'b0}};
m_axis_tvalid_int = 1'b0;
m_axis_tlast_int = 1'b0;
m_axis_tid_int = {ID_WIDTH{1'b0}};
m_axis_tdest_int = {DEST_WIDTH{1'b0}};
m_axis_tuser_int = {USER_WIDTH{1'b0}};
s_axis_tready_next = 1'b0;
status_valid_next = status_valid_reg && !status_ready;
status_frame_pad_next = status_frame_pad_reg;
status_frame_truncate_next = status_frame_truncate_reg;
status_frame_length_next = status_frame_length_reg;
status_frame_original_length_next = status_frame_original_length_reg;
if (KEEP_ENABLE) begin
for (i = 0; i < KEEP_WIDTH; i = i + 1) begin
s_axis_tdata_masked[i*DATA_WORD_WIDTH +: DATA_WORD_WIDTH] = s_axis_tkeep[i] ? s_axis_tdata[i*DATA_WORD_WIDTH +: DATA_WORD_WIDTH] : {DATA_WORD_WIDTH{1'b0}};
end
end else begin
s_axis_tdata_masked = s_axis_tdata;
end
case (state_reg)
STATE_IDLE: begin
// idle state
// accept data next cycle if output register ready next cycle
s_axis_tready_next = m_axis_tready_int_early && (!status_valid_reg || status_ready);
m_axis_tdata_int = s_axis_tdata_masked;
m_axis_tkeep_int = s_axis_tkeep;
m_axis_tvalid_int = s_axis_tvalid;
m_axis_tlast_int = s_axis_tlast;
m_axis_tid_int = s_axis_tid;
m_axis_tdest_int = s_axis_tdest;
m_axis_tuser_int = s_axis_tuser;
short_counter_next = length_min;
long_counter_next = length_max;
if (s_axis_tready && s_axis_tvalid) begin
// transfer through
word_cnt = 0;
for (i = 0; i <= KEEP_WIDTH; i = i + 1) begin
//bit_cnt = bit_cnt + monitor_axis_tkeep[i];
if (s_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) word_cnt = i;
end
frame_ptr_next = frame_ptr_reg+KEEP_WIDTH;
if (short_counter_reg > KEEP_WIDTH) begin
short_counter_next = short_counter_reg - KEEP_WIDTH;
end else begin
short_counter_next = 16'd0;
end
if (long_counter_reg > KEEP_WIDTH) begin
long_counter_next = long_counter_reg - KEEP_WIDTH;
end else begin
long_counter_next = 16'd0;
end
if (long_counter_reg <= word_cnt) begin
m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-long_counter_reg);
if (s_axis_tlast) begin
status_valid_next = 1'b1;
status_frame_pad_next = 1'b0;
status_frame_truncate_next = word_cnt > long_counter_reg;
status_frame_length_next = length_max;
status_frame_original_length_next = frame_ptr_reg+word_cnt;
s_axis_tready_next = m_axis_tready_int_early && status_ready;
frame_ptr_next = 16'd0;
short_counter_next = length_min;
long_counter_next = length_max;
state_next = STATE_IDLE;
end else begin
m_axis_tvalid_int = 1'b0;
store_last_word = 1'b1;
state_next = STATE_TRUNCATE;
end
end else begin
if (s_axis_tlast) begin
status_frame_original_length_next = frame_ptr_reg+word_cnt;
if (short_counter_reg > word_cnt) begin
if (short_counter_reg > KEEP_WIDTH) begin
frame_ptr_next = frame_ptr_reg + KEEP_WIDTH;
s_axis_tready_next = 1'b0;
m_axis_tkeep_int = {KEEP_WIDTH{1'b1}};
m_axis_tlast_int = 1'b0;
store_last_word = 1'b1;
state_next = STATE_PAD;
end else begin
status_valid_next = 1'b1;
status_frame_pad_next = 1'b1;
status_frame_truncate_next = 1'b0;
status_frame_length_next = length_min;
s_axis_tready_next = m_axis_tready_int_early && status_ready;
m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-(length_min - frame_ptr_reg));
frame_ptr_next = 16'd0;
short_counter_next = length_min;
long_counter_next = length_max;
state_next = STATE_IDLE;
end
end else begin
status_valid_next = 1'b1;
status_frame_pad_next = 1'b0;
status_frame_truncate_next = 1'b0;
status_frame_length_next = frame_ptr_reg+word_cnt;
status_frame_original_length_next = frame_ptr_reg+word_cnt;
s_axis_tready_next = m_axis_tready_int_early && status_ready;
frame_ptr_next = 16'd0;
short_counter_next = length_min;
long_counter_next = length_max;
state_next = STATE_IDLE;
end
end else begin
state_next = STATE_TRANSFER;
end
end
end else begin
state_next = STATE_IDLE;
end
end
STATE_TRANSFER: begin
// transfer data
// accept data next cycle if output register ready next cycle
s_axis_tready_next = m_axis_tready_int_early;
m_axis_tdata_int = s_axis_tdata_masked;
m_axis_tkeep_int = s_axis_tkeep;
m_axis_tvalid_int = s_axis_tvalid;
m_axis_tlast_int = s_axis_tlast;
m_axis_tid_int = s_axis_tid;
m_axis_tdest_int = s_axis_tdest;
m_axis_tuser_int = s_axis_tuser;
if (s_axis_tready && s_axis_tvalid) begin
// transfer through
word_cnt = 1;
for (i = 1; i <= KEEP_WIDTH; i = i + 1) begin
//bit_cnt = bit_cnt + monitor_axis_tkeep[i];
if (s_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) word_cnt = i;
end
frame_ptr_next = frame_ptr_reg+KEEP_WIDTH;
if (short_counter_reg > KEEP_WIDTH) begin
short_counter_next = short_counter_reg - KEEP_WIDTH;
end else begin
short_counter_next = 16'd0;
end
if (long_counter_reg > KEEP_WIDTH) begin
long_counter_next = long_counter_reg - KEEP_WIDTH;
end else begin
long_counter_next = 16'd0;
end
if (long_counter_reg <= word_cnt) begin
m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-long_counter_reg);
if (s_axis_tlast) begin
status_valid_next = 1'b1;
status_frame_pad_next = 1'b0;
status_frame_truncate_next = word_cnt > long_counter_reg;
status_frame_length_next = length_max;
status_frame_original_length_next = frame_ptr_reg+word_cnt;
s_axis_tready_next = m_axis_tready_int_early && status_ready;
frame_ptr_next = 16'd0;
short_counter_next = length_min;
long_counter_next = length_max;
state_next = STATE_IDLE;
end else begin
m_axis_tvalid_int = 1'b0;
store_last_word = 1'b1;
state_next = STATE_TRUNCATE;
end
end else begin
if (s_axis_tlast) begin
status_frame_original_length_next = frame_ptr_reg+word_cnt;
if (short_counter_reg > word_cnt) begin
if (short_counter_reg > KEEP_WIDTH) begin
frame_ptr_next = frame_ptr_reg + KEEP_WIDTH;
s_axis_tready_next = 1'b0;
m_axis_tkeep_int = {KEEP_WIDTH{1'b1}};
m_axis_tlast_int = 1'b0;
store_last_word = 1'b1;
state_next = STATE_PAD;
end else begin
status_valid_next = 1'b1;
status_frame_pad_next = 1'b1;
status_frame_truncate_next = 1'b0;
status_frame_length_next = length_min;
s_axis_tready_next = m_axis_tready_int_early && status_ready;
m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-short_counter_reg);
frame_ptr_next = 16'd0;
short_counter_next = length_min;
long_counter_next = length_max;
state_next = STATE_IDLE;
end
end else begin
status_valid_next = 1'b1;
status_frame_pad_next = 1'b0;
status_frame_truncate_next = 1'b0;
status_frame_length_next = frame_ptr_reg+word_cnt;
status_frame_original_length_next = frame_ptr_reg+word_cnt;
s_axis_tready_next = m_axis_tready_int_early && status_ready;
frame_ptr_next = 16'd0;
short_counter_next = length_min;
long_counter_next = length_max;
state_next = STATE_IDLE;
end
end else begin
state_next = STATE_TRANSFER;
end
end
end else begin
state_next = STATE_TRANSFER;
end
end
STATE_PAD: begin
// pad to minimum length
s_axis_tready_next = 1'b0;
m_axis_tdata_int = {DATA_WIDTH{1'b0}};
m_axis_tkeep_int = {KEEP_WIDTH{1'b1}};
m_axis_tvalid_int = 1'b1;
m_axis_tlast_int = 1'b0;
m_axis_tid_int = last_word_id_reg;
m_axis_tdest_int = last_word_dest_reg;
m_axis_tuser_int = last_word_user_reg;
if (m_axis_tready_int_reg) begin
frame_ptr_next = frame_ptr_reg + KEEP_WIDTH;
if (short_counter_reg > KEEP_WIDTH) begin
short_counter_next = short_counter_reg - KEEP_WIDTH;
end else begin
short_counter_next = 16'd0;
end
if (long_counter_reg > KEEP_WIDTH) begin
long_counter_next = long_counter_reg - KEEP_WIDTH;
end else begin
long_counter_next = 16'd0;
end
if (short_counter_reg <= KEEP_WIDTH) begin
status_valid_next = 1'b1;
status_frame_pad_next = 1'b1;
status_frame_truncate_next = 1'b0;
status_frame_length_next = length_min;
s_axis_tready_next = m_axis_tready_int_early && status_ready;
m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-short_counter_reg);
m_axis_tlast_int = 1'b1;
frame_ptr_next = 16'd0;
short_counter_next = length_min;
long_counter_next = length_max;
state_next = STATE_IDLE;
end else begin
state_next = STATE_PAD;
end
end else begin
state_next = STATE_PAD;
end
end
STATE_TRUNCATE: begin
// drop after maximum length
s_axis_tready_next = m_axis_tready_int_early;
m_axis_tdata_int = last_word_data_reg;
m_axis_tkeep_int = last_word_keep_reg;
m_axis_tvalid_int = s_axis_tvalid && s_axis_tlast;
m_axis_tlast_int = s_axis_tlast;
m_axis_tid_int = last_word_id_reg;
m_axis_tdest_int = last_word_dest_reg;
m_axis_tuser_int = s_axis_tuser;
if (s_axis_tready && s_axis_tvalid) begin
word_cnt = 0;
for (i = 0; i <= KEEP_WIDTH; i = i + 1) begin
//bit_cnt = bit_cnt + monitor_axis_tkeep[i];
if (s_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) word_cnt = i;
end
frame_ptr_next = frame_ptr_reg+KEEP_WIDTH;
if (s_axis_tlast) begin
status_valid_next = 1'b1;
status_frame_pad_next = 1'b0;
status_frame_truncate_next = 1'b1;
status_frame_length_next = length_max;
status_frame_original_length_next = frame_ptr_reg+word_cnt;
s_axis_tready_next = m_axis_tready_int_early && status_ready;
frame_ptr_next = 16'd0;
short_counter_next = length_min;
long_counter_next = length_max;
state_next = STATE_IDLE;
end else begin
state_next = STATE_TRUNCATE;
end
end else begin
state_next = STATE_TRUNCATE;
end
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
frame_ptr_reg <= 16'd0;
short_counter_reg <= 16'd0;
long_counter_reg <= 16'd0;
s_axis_tready_reg <= 1'b0;
status_valid_reg <= 1'b0;
end else begin
state_reg <= state_next;
frame_ptr_reg <= frame_ptr_next;
short_counter_reg <= short_counter_next;
long_counter_reg <= long_counter_next;
s_axis_tready_reg <= s_axis_tready_next;
status_valid_reg <= status_valid_next;
end
status_frame_pad_reg <= status_frame_pad_next;
status_frame_truncate_reg <= status_frame_truncate_next;
status_frame_length_reg <= status_frame_length_next;
status_frame_original_length_reg <= status_frame_original_length_next;
if (store_last_word) begin
last_word_data_reg <= m_axis_tdata_int;
last_word_keep_reg <= m_axis_tkeep_int;
last_word_id_reg <= m_axis_tid_int;
last_word_dest_reg <= m_axis_tdest_int;
last_word_user_reg <= m_axis_tuser_int;
end
end
// output datapath logic
reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
reg m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}};
reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next;
reg temp_m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}};
// datapath control
reg store_axis_int_to_output;
reg store_axis_int_to_temp;
reg store_axis_temp_to_output;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = m_axis_tlast_reg;
assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_axis_tvalid_next = m_axis_tvalid_reg;
temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg;
store_axis_int_to_output = 1'b0;
store_axis_int_to_temp = 1'b0;
store_axis_temp_to_output = 1'b0;
if (m_axis_tready_int_reg) begin
// input is ready
if (m_axis_tready || !m_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_temp = 1'b1;
end
end else if (m_axis_tready) begin
// input is not ready, but output is ready
m_axis_tvalid_next = temp_m_axis_tvalid_reg;
temp_m_axis_tvalid_next = 1'b0;
store_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_axis_tvalid_reg <= 1'b0;
m_axis_tready_int_reg <= 1'b0;
temp_m_axis_tvalid_reg <= 1'b0;
end else begin
m_axis_tvalid_reg <= m_axis_tvalid_next;
m_axis_tready_int_reg <= m_axis_tready_int_early;
temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next;
end
// datapath
if (store_axis_int_to_output) begin
m_axis_tdata_reg <= m_axis_tdata_int;
m_axis_tkeep_reg <= m_axis_tkeep_int;
m_axis_tlast_reg <= m_axis_tlast_int;
m_axis_tid_reg <= m_axis_tid_int;
m_axis_tdest_reg <= m_axis_tdest_int;
m_axis_tuser_reg <= m_axis_tuser_int;
end else if (store_axis_temp_to_output) begin
m_axis_tdata_reg <= temp_m_axis_tdata_reg;
m_axis_tkeep_reg <= temp_m_axis_tkeep_reg;
m_axis_tlast_reg <= temp_m_axis_tlast_reg;
m_axis_tid_reg <= temp_m_axis_tid_reg;
m_axis_tdest_reg <= temp_m_axis_tdest_reg;
m_axis_tuser_reg <= temp_m_axis_tuser_reg;
end
if (store_axis_int_to_temp) begin
temp_m_axis_tdata_reg <= m_axis_tdata_int;
temp_m_axis_tkeep_reg <= m_axis_tkeep_int;
temp_m_axis_tlast_reg <= m_axis_tlast_int;
temp_m_axis_tid_reg <= m_axis_tid_int;
temp_m_axis_tdest_reg <= m_axis_tdest_int;
temp_m_axis_tuser_reg <= m_axis_tuser_int;
end
end
endmodule
/*
Copyright (c) 2015-2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream frame length adjuster with FIFO
*/
module axis_frame_length_adjust_fifo #
(
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 8,
// Propagate tkeep signal
// If disabled, tkeep assumed to be 1'b1
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// Propagate tdest signal
parameter DEST_ENABLE = 0,
// tdest signal width
parameter DEST_WIDTH = 8,
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1,
// Depth of data FIFO in words
parameter FRAME_FIFO_DEPTH = 4096,
// Depth of header FIFO
parameter HEADER_FIFO_DEPTH = 8
)
(
input wire clk,
input wire rst,
/*
* AXI input
*/
input wire [DATA_WIDTH-1:0] s_axis_tdata,
input wire [KEEP_WIDTH-1:0] s_axis_tkeep,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire [ID_WIDTH-1:0] s_axis_tid,
input wire [DEST_WIDTH-1:0] s_axis_tdest,
input wire [USER_WIDTH-1:0] s_axis_tuser,
/*
* AXI output
*/
output wire m_axis_hdr_valid,
input wire m_axis_hdr_ready,
output wire m_axis_hdr_pad,
output wire m_axis_hdr_truncate,
output wire [15:0] m_axis_hdr_length,
output wire [15:0] m_axis_hdr_original_length,
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [ID_WIDTH-1:0] m_axis_tid,
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser,
/*
* Configuration
*/
input wire [15:0] length_min,
input wire [15:0] length_max
);
wire [DATA_WIDTH-1:0] fifo_axis_tdata;
wire [KEEP_WIDTH-1:0] fifo_axis_tkeep;
wire fifo_axis_tvalid;
wire fifo_axis_tready;
wire fifo_axis_tlast;
wire [ID_WIDTH-1:0] fifo_axis_tid;
wire [DEST_WIDTH-1:0] fifo_axis_tdest;
wire [USER_WIDTH-1:0] fifo_axis_tuser;
wire status_valid;
wire status_ready;
wire status_frame_pad;
wire status_frame_truncate;
wire [15:0] status_frame_length;
wire [15:0] status_frame_original_length;
axis_frame_length_adjust #(
.DATA_WIDTH(DATA_WIDTH),
.KEEP_ENABLE(KEEP_ENABLE),
.KEEP_WIDTH(KEEP_WIDTH),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(DEST_ENABLE),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH)
)
axis_frame_length_adjust_inst (
.clk(clk),
.rst(rst),
// AXI input
.s_axis_tdata(s_axis_tdata),
.s_axis_tkeep(s_axis_tkeep),
.s_axis_tvalid(s_axis_tvalid),
.s_axis_tready(s_axis_tready),
.s_axis_tlast(s_axis_tlast),
.s_axis_tid(s_axis_tid),
.s_axis_tdest(s_axis_tdest),
.s_axis_tuser(s_axis_tuser),
// AXI output
.m_axis_tdata(fifo_axis_tdata),
.m_axis_tkeep(fifo_axis_tkeep),
.m_axis_tvalid(fifo_axis_tvalid),
.m_axis_tready(fifo_axis_tready),
.m_axis_tlast(fifo_axis_tlast),
.m_axis_tid(fifo_axis_tid),
.m_axis_tdest(fifo_axis_tdest),
.m_axis_tuser(fifo_axis_tuser),
// Status
.status_valid(status_valid),
.status_ready(status_ready),
.status_frame_pad(status_frame_pad),
.status_frame_truncate(status_frame_truncate),
.status_frame_length(status_frame_length),
.status_frame_original_length(status_frame_original_length),
// Configuration
.length_min(length_min),
.length_max(length_max)
);
axis_fifo #(
.DEPTH(FRAME_FIFO_DEPTH),
.DATA_WIDTH(DATA_WIDTH),
.KEEP_ENABLE(KEEP_ENABLE),
.KEEP_WIDTH(KEEP_WIDTH),
.LAST_ENABLE(1),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(DEST_ENABLE),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH),
.FRAME_FIFO(0)
)
frame_fifo_inst (
.clk(clk),
.rst(rst),
// AXI input
.s_axis_tdata(fifo_axis_tdata),
.s_axis_tkeep(fifo_axis_tkeep),
.s_axis_tvalid(fifo_axis_tvalid),
.s_axis_tready(fifo_axis_tready),
.s_axis_tlast(fifo_axis_tlast),
.s_axis_tid(fifo_axis_tid),
.s_axis_tdest(fifo_axis_tdest),
.s_axis_tuser(fifo_axis_tuser),
// AXI output
.m_axis_tdata(m_axis_tdata),
.m_axis_tkeep(m_axis_tkeep),
.m_axis_tvalid(m_axis_tvalid),
.m_axis_tready(m_axis_tready),
.m_axis_tlast(m_axis_tlast),
.m_axis_tid(m_axis_tid),
.m_axis_tdest(m_axis_tdest),
.m_axis_tuser(m_axis_tuser),
// Status
.status_overflow(),
.status_bad_frame(),
.status_good_frame()
);
axis_fifo #(
.DEPTH(HEADER_FIFO_DEPTH),
.DATA_WIDTH(1+1+16+16),
.KEEP_ENABLE(0),
.LAST_ENABLE(0),
.ID_ENABLE(0),
.DEST_ENABLE(0),
.USER_ENABLE(0),
.FRAME_FIFO(0)
)
header_fifo_inst (
.clk(clk),
.rst(rst),
// AXI input
.s_axis_tdata({status_frame_pad, status_frame_truncate, status_frame_length, status_frame_original_length}),
.s_axis_tkeep(0),
.s_axis_tvalid(status_valid),
.s_axis_tready(status_ready),
.s_axis_tlast(0),
.s_axis_tid(0),
.s_axis_tdest(0),
.s_axis_tuser(0),
// AXI output
.m_axis_tdata({m_axis_hdr_pad, m_axis_hdr_truncate, m_axis_hdr_length, m_axis_hdr_original_length}),
.m_axis_tkeep(),
.m_axis_tvalid(m_axis_hdr_valid),
.m_axis_tready(m_axis_hdr_ready),
.m_axis_tlast(),
.m_axis_tid(),
.m_axis_tdest(),
.m_axis_tuser(),
// Status
.status_overflow(),
.status_bad_frame(),
.status_good_frame()
);
endmodule
/*
Copyright (c) 2014-2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream to LocalLink bridge
*/
module axis_ll_bridge #
(
parameter DATA_WIDTH = 8
)
(
input wire clk,
input wire rst,
/*
* AXI input
*/
input wire [DATA_WIDTH-1:0] s_axis_tdata,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
/*
* LocalLink output
*/
output wire [DATA_WIDTH-1:0] ll_data_out,
output wire ll_sof_out_n,
output wire ll_eof_out_n,
output wire ll_src_rdy_out_n,
input wire ll_dst_rdy_in_n
);
reg last_tlast = 1'b1;
always @(posedge clk) begin
if (rst) begin
last_tlast = 1'b1;
end else begin
if (s_axis_tvalid && s_axis_tready) last_tlast = s_axis_tlast;
end
end
// high for packet length 1 -> cannot set SOF and EOF in same cycle
// invalid packets are discarded
wire invalid = s_axis_tvalid && s_axis_tlast && last_tlast;
assign s_axis_tready = !ll_dst_rdy_in_n;
assign ll_data_out = s_axis_tdata;
assign ll_sof_out_n = !(last_tlast && s_axis_tvalid && !invalid);
assign ll_eof_out_n = !(s_axis_tlast && !invalid);
assign ll_src_rdy_out_n = !(s_axis_tvalid && !invalid);
endmodule
/*
Copyright (c) 2014-2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream multiplexer
*/
module axis_mux #
(
// Number of AXI stream inputs
parameter S_COUNT = 4,
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 8,
// Propagate tkeep signal
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// Propagate tdest signal
parameter DEST_ENABLE = 0,
// tdest signal width
parameter DEST_WIDTH = 8,
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1
)
(
input wire clk,
input wire rst,
/*
* AXI inputs
*/
input wire [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata,
input wire [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep,
input wire [S_COUNT-1:0] s_axis_tvalid,
output wire [S_COUNT-1:0] s_axis_tready,
input wire [S_COUNT-1:0] s_axis_tlast,
input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid,
input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest,
input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser,
/*
* AXI output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [ID_WIDTH-1:0] m_axis_tid,
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser,
/*
* Control
*/
input wire enable,
input wire [$clog2(S_COUNT)-1:0] select
);
parameter CL_S_COUNT = $clog2(S_COUNT);
reg [CL_S_COUNT-1:0] select_reg = 2'd0, select_next;
reg frame_reg = 1'b0, frame_next;
reg [S_COUNT-1:0] s_axis_tready_reg = 0, s_axis_tready_next;
// internal datapath
reg [DATA_WIDTH-1:0] m_axis_tdata_int;
reg [KEEP_WIDTH-1:0] m_axis_tkeep_int;
reg m_axis_tvalid_int;
reg m_axis_tready_int_reg = 1'b0;
reg m_axis_tlast_int;
reg [ID_WIDTH-1:0] m_axis_tid_int;
reg [DEST_WIDTH-1:0] m_axis_tdest_int;
reg [USER_WIDTH-1:0] m_axis_tuser_int;
wire m_axis_tready_int_early;
assign s_axis_tready = s_axis_tready_reg;
// mux for incoming packet
wire [DATA_WIDTH-1:0] current_s_tdata = s_axis_tdata[select_reg*DATA_WIDTH +: DATA_WIDTH];
wire [KEEP_WIDTH-1:0] current_s_tkeep = s_axis_tkeep[select_reg*KEEP_WIDTH +: KEEP_WIDTH];
wire current_s_tvalid = s_axis_tvalid[select_reg];
wire current_s_tready = s_axis_tready[select_reg];
wire current_s_tlast = s_axis_tlast[select_reg];
wire [ID_WIDTH-1:0] current_s_tid = s_axis_tid[select_reg*ID_WIDTH +: ID_WIDTH];
wire [DEST_WIDTH-1:0] current_s_tdest = s_axis_tdest[select_reg*DEST_WIDTH +: DEST_WIDTH];
wire [USER_WIDTH-1:0] current_s_tuser = s_axis_tuser[select_reg*USER_WIDTH +: USER_WIDTH];
always @* begin
select_next = select_reg;
frame_next = frame_reg;
s_axis_tready_next = 0;
if (current_s_tvalid & current_s_tready) begin
// end of frame detection
if (current_s_tlast) begin
frame_next = 1'b0;
end
end
if (!frame_reg && enable && (s_axis_tvalid & (1 << select))) begin
// start of frame, grab select value
frame_next = 1'b1;
select_next = select;
end
// generate ready signal on selected port
s_axis_tready_next = (m_axis_tready_int_early && frame_next) << select_next;
// pass through selected packet data
m_axis_tdata_int = current_s_tdata;
m_axis_tkeep_int = current_s_tkeep;
m_axis_tvalid_int = current_s_tvalid && current_s_tready && frame_reg;
m_axis_tlast_int = current_s_tlast;
m_axis_tid_int = current_s_tid;
m_axis_tdest_int = current_s_tdest;
m_axis_tuser_int = current_s_tuser;
end
always @(posedge clk) begin
if (rst) begin
select_reg <= 0;
frame_reg <= 1'b0;
s_axis_tready_reg <= 0;
end else begin
select_reg <= select_next;
frame_reg <= frame_next;
s_axis_tready_reg <= s_axis_tready_next;
end
end
// output datapath logic
reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
reg m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}};
reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next;
reg temp_m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}};
// datapath control
reg store_axis_int_to_output;
reg store_axis_int_to_temp;
reg store_axis_temp_to_output;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = m_axis_tlast_reg;
assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_axis_tvalid_next = m_axis_tvalid_reg;
temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg;
store_axis_int_to_output = 1'b0;
store_axis_int_to_temp = 1'b0;
store_axis_temp_to_output = 1'b0;
if (m_axis_tready_int_reg) begin
// input is ready
if (m_axis_tready || !m_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_temp = 1'b1;
end
end else if (m_axis_tready) begin
// input is not ready, but output is ready
m_axis_tvalid_next = temp_m_axis_tvalid_reg;
temp_m_axis_tvalid_next = 1'b0;
store_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_axis_tvalid_reg <= 1'b0;
m_axis_tready_int_reg <= 1'b0;
temp_m_axis_tvalid_reg <= 1'b0;
end else begin
m_axis_tvalid_reg <= m_axis_tvalid_next;
m_axis_tready_int_reg <= m_axis_tready_int_early;
temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next;
end
// datapath
if (store_axis_int_to_output) begin
m_axis_tdata_reg <= m_axis_tdata_int;
m_axis_tkeep_reg <= m_axis_tkeep_int;
m_axis_tlast_reg <= m_axis_tlast_int;
m_axis_tid_reg <= m_axis_tid_int;
m_axis_tdest_reg <= m_axis_tdest_int;
m_axis_tuser_reg <= m_axis_tuser_int;
end else if (store_axis_temp_to_output) begin
m_axis_tdata_reg <= temp_m_axis_tdata_reg;
m_axis_tkeep_reg <= temp_m_axis_tkeep_reg;
m_axis_tlast_reg <= temp_m_axis_tlast_reg;
m_axis_tid_reg <= temp_m_axis_tid_reg;
m_axis_tdest_reg <= temp_m_axis_tdest_reg;
m_axis_tuser_reg <= temp_m_axis_tuser_reg;
end
if (store_axis_int_to_temp) begin
temp_m_axis_tdata_reg <= m_axis_tdata_int;
temp_m_axis_tkeep_reg <= m_axis_tkeep_int;
temp_m_axis_tlast_reg <= m_axis_tlast_int;
temp_m_axis_tid_reg <= m_axis_tid_int;
temp_m_axis_tdest_reg <= m_axis_tdest_int;
temp_m_axis_tuser_reg <= m_axis_tuser_int;
end
end
endmodule
#!/usr/bin/env python
"""
Generates an AXI Stream mux wrapper with the specified number of ports
"""
from __future__ import print_function
import argparse
import math
from jinja2 import Template
def main():
parser = argparse.ArgumentParser(description=__doc__.strip())
parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports")
parser.add_argument('-n', '--name', type=str, help="module name")
parser.add_argument('-o', '--output', type=str, help="output file name")
args = parser.parse_args()
try:
generate(**args.__dict__)
except IOError as ex:
print(ex)
exit(1)
def generate(ports=4, name=None, output=None):
n = ports
if name is None:
name = "axis_mux_wrap_{0}".format(n)
if output is None:
output = name + ".v"
print("Opening file '{0}'...".format(output))
output_file = open(output, 'w')
print("Generating {0} port AXI stream mux wrapper {1}...".format(n, name))
cn = int(math.ceil(math.log(n, 2)))
t = Template(u"""/*
Copyright (c) 2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream {{n}} port mux (wrapper)
*/
module {{name}} #
(
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 8,
// Propagate tkeep signal
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// Propagate tdest signal
parameter DEST_ENABLE = 0,
// tdest signal width
parameter DEST_WIDTH = 8,
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1
)
(
input wire clk,
input wire rst,
/*
* AXI Stream inputs
*/
{%- for p in range(n) %}
input wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axis_tdata,
input wire [KEEP_WIDTH-1:0] s{{'%02d'%p}}_axis_tkeep,
input wire s{{'%02d'%p}}_axis_tvalid,
output wire s{{'%02d'%p}}_axis_tready,
input wire s{{'%02d'%p}}_axis_tlast,
input wire [ID_WIDTH-1:0] s{{'%02d'%p}}_axis_tid,
input wire [DEST_WIDTH-1:0] s{{'%02d'%p}}_axis_tdest,
input wire [USER_WIDTH-1:0] s{{'%02d'%p}}_axis_tuser,
{% endfor %}
/*
* AXI Stream output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [ID_WIDTH-1:0] m_axis_tid,
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser,
/*
* Control
*/
input wire enable,
input wire [{{cn-1}}:0] select
);
axis_mux #(
.S_COUNT({{n}}),
.DATA_WIDTH(DATA_WIDTH),
.KEEP_ENABLE(KEEP_ENABLE),
.KEEP_WIDTH(KEEP_WIDTH),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(DEST_ENABLE),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH)
)
axis_mux_inst (
.clk(clk),
.rst(rst),
// AXI inputs
.s_axis_tdata({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tkeep({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tvalid({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tready({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tlast({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tid({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tdest({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tuser({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }),
// AXI output
.m_axis_tdata(m_axis_tdata),
.m_axis_tkeep(m_axis_tkeep),
.m_axis_tvalid(m_axis_tvalid),
.m_axis_tready(m_axis_tready),
.m_axis_tlast(m_axis_tlast),
.m_axis_tid(m_axis_tid),
.m_axis_tdest(m_axis_tdest),
.m_axis_tuser(m_axis_tuser),
// Control
.enable(enable),
.select(select)
);
endmodule
""")
output_file.write(t.render(
n=n,
cn=cn,
name=name
))
print("Done")
if __name__ == "__main__":
main()
/*
Copyright (c) 2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream pipeline register
*/
module axis_pipeline_register #
(
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 8,
// Propagate tkeep signal
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Propagate tlast signal
parameter LAST_ENABLE = 1,
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// Propagate tdest signal
parameter DEST_ENABLE = 0,
// tdest signal width
parameter DEST_WIDTH = 8,
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1,
// Register type
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
parameter REG_TYPE = 2,
// Number of registers in pipeline
parameter LENGTH = 2
)
(
input wire clk,
input wire rst,
/*
* AXI input
*/
input wire [DATA_WIDTH-1:0] s_axis_tdata,
input wire [KEEP_WIDTH-1:0] s_axis_tkeep,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire [ID_WIDTH-1:0] s_axis_tid,
input wire [DEST_WIDTH-1:0] s_axis_tdest,
input wire [USER_WIDTH-1:0] s_axis_tuser,
/*
* AXI output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [ID_WIDTH-1:0] m_axis_tid,
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser
);
wire [DATA_WIDTH-1:0] axis_tdata[0:LENGTH];
wire [KEEP_WIDTH-1:0] axis_tkeep[0:LENGTH];
wire axis_tvalid[0:LENGTH];
wire axis_tready[0:LENGTH];
wire axis_tlast[0:LENGTH];
wire [ID_WIDTH-1:0] axis_tid[0:LENGTH];
wire [DEST_WIDTH-1:0] axis_tdest[0:LENGTH];
wire [USER_WIDTH-1:0] axis_tuser[0:LENGTH];
assign axis_tdata[0] = s_axis_tdata;
assign axis_tkeep[0] = s_axis_tkeep;
assign axis_tvalid[0] = s_axis_tvalid;
assign s_axis_tready = axis_tready[0];
assign axis_tlast[0] = s_axis_tlast;
assign axis_tid[0] = s_axis_tid;
assign axis_tdest[0] = s_axis_tdest;
assign axis_tuser[0] = s_axis_tuser;
assign m_axis_tdata = axis_tdata[LENGTH];
assign m_axis_tkeep = axis_tkeep[LENGTH];
assign m_axis_tvalid = axis_tvalid[LENGTH];
assign axis_tready[LENGTH] = m_axis_tready;
assign m_axis_tlast = axis_tlast[LENGTH];
assign m_axis_tid = axis_tid[LENGTH];
assign m_axis_tdest = axis_tdest[LENGTH];
assign m_axis_tuser = axis_tuser[LENGTH];
generate
genvar i;
for (i = 0; i < LENGTH; i = i + 1) begin : pipe_reg
axis_register #(
.DATA_WIDTH(DATA_WIDTH),
.KEEP_ENABLE(KEEP_ENABLE),
.KEEP_WIDTH(KEEP_WIDTH),
.LAST_ENABLE(LAST_ENABLE),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(DEST_ENABLE),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH),
.REG_TYPE(REG_TYPE)
)
reg_inst (
.clk(clk),
.rst(rst),
// AXI input
.s_axis_tdata(axis_tdata[i]),
.s_axis_tkeep(axis_tkeep[i]),
.s_axis_tvalid(axis_tvalid[i]),
.s_axis_tready(axis_tready[i]),
.s_axis_tlast(axis_tlast[i]),
.s_axis_tid(axis_tid[i]),
.s_axis_tdest(axis_tdest[i]),
.s_axis_tuser(axis_tuser[i]),
// AXI output
.m_axis_tdata(axis_tdata[i+1]),
.m_axis_tkeep(axis_tkeep[i+1]),
.m_axis_tvalid(axis_tvalid[i+1]),
.m_axis_tready(axis_tready[i+1]),
.m_axis_tlast(axis_tlast[i+1]),
.m_axis_tid(axis_tid[i+1]),
.m_axis_tdest(axis_tdest[i+1]),
.m_axis_tuser(axis_tuser[i+1])
);
end
endgenerate
endmodule
/*
Copyright (c) 2020 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
`timescale 1ns / 1ps
/*
* AXI4-Stream RAM switch
*/
module axis_ram_switch #
(
// FIFO depth in words (each virtual FIFO)
// KEEP_WIDTH words per cycle if KEEP_ENABLE set
// Rounded up to nearest power of 2 cycles
parameter FIFO_DEPTH = 4096,
// Command FIFO depth (each virtual FIFO)
// Rounded up to nearest power of 2
parameter CMD_FIFO_DEPTH = 32,
// Speedup factor (internal data width scaling factor)
// Speedup of 0 scales internal width to provide maximum bandwidth
parameter SPEEDUP = 0,
// Number of AXI stream inputs
parameter S_COUNT = 4,
// Number of AXI stream outputs
parameter M_COUNT = 4,
// Width of input AXI stream interfaces in bits
parameter S_DATA_WIDTH = 8,
// Propagate tkeep signal
parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8),
// Width of output AXI stream interfaces in bits
parameter M_DATA_WIDTH = 8,
// Propagate tkeep signal
parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8),
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// tdest signal width
// must be wide enough to uniquely address outputs
parameter DEST_WIDTH = $clog2(M_COUNT),
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1,
// tuser value for bad frame marker
parameter USER_BAD_FRAME_VALUE = 1'b1,
// tuser mask for bad frame marker
parameter USER_BAD_FRAME_MASK = 1'b1,
// Drop frames marked bad
parameter DROP_BAD_FRAME = 0,
// Drop incoming frames when full
// When set, s_axis_tready is always asserted
parameter DROP_WHEN_FULL = 0,
// Output interface routing base tdest selection
// Concatenate M_COUNT DEST_WIDTH sized constants
// Port selected if M_BASE <= tdest <= M_TOP
// set to zero for default routing with tdest MSBs as port index
parameter M_BASE = 0,
// Output interface routing top tdest selection
// Concatenate M_COUNT DEST_WIDTH sized constants
// Port selected if M_BASE <= tdest <= M_TOP
// set to zero to inherit from M_BASE
parameter M_TOP = 0,
// Interface connection control
// M_COUNT concatenated fields of S_COUNT bits
parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}},
// arbitration type: "PRIORITY" or "ROUND_ROBIN"
parameter ARB_TYPE = "ROUND_ROBIN",
// LSB priority: "LOW", "HIGH"
parameter LSB_PRIORITY = "HIGH"
)
(
input wire clk,
input wire rst,
/*
* AXI Stream inputs
*/
input wire [S_COUNT*S_DATA_WIDTH-1:0] s_axis_tdata,
input wire [S_COUNT*S_KEEP_WIDTH-1:0] s_axis_tkeep,
input wire [S_COUNT-1:0] s_axis_tvalid,
output wire [S_COUNT-1:0] s_axis_tready,
input wire [S_COUNT-1:0] s_axis_tlast,
input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid,
input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest,
input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser,
/*
* AXI Stream outputs
*/
output wire [M_COUNT*M_DATA_WIDTH-1:0] m_axis_tdata,
output wire [M_COUNT*M_KEEP_WIDTH-1:0] m_axis_tkeep,
output wire [M_COUNT-1:0] m_axis_tvalid,
input wire [M_COUNT-1:0] m_axis_tready,
output wire [M_COUNT-1:0] m_axis_tlast,
output wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid,
output wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest,
output wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser,
/*
* Status
*/
output wire [S_COUNT-1:0] status_overflow,
output wire [S_COUNT-1:0] status_bad_frame,
output wire [S_COUNT-1:0] status_good_frame
);
parameter CL_S_COUNT = $clog2(S_COUNT);
parameter CL_M_COUNT = $clog2(M_COUNT);
// force keep width to 1 when disabled
parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1;
parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1;
// bus word sizes (must be identical)
parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT;
parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT;
// total data and keep widths
parameter MIN_DATA_WIDTH = (M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT ? M_DATA_WIDTH : S_DATA_WIDTH);
parameter MIN_KEEP_WIDTH = (M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT);
// speedup factor
parameter M_TOTAL_DATA_WIDTH = M_DATA_WIDTH*M_COUNT;
parameter S_TOTAL_DATA_WIDTH = S_DATA_WIDTH*S_COUNT;
parameter SPEEDUP_INT = SPEEDUP > 0 ? SPEEDUP : (M_TOTAL_DATA_WIDTH > S_TOTAL_DATA_WIDTH ? (M_TOTAL_DATA_WIDTH / MIN_DATA_WIDTH) : (S_TOTAL_DATA_WIDTH / MIN_DATA_WIDTH));
parameter DATA_WIDTH = MIN_DATA_WIDTH*SPEEDUP_INT;
parameter KEEP_WIDTH = MIN_KEEP_WIDTH*SPEEDUP_INT;
parameter ADDR_WIDTH = $clog2(FIFO_DEPTH/KEEP_WIDTH);
parameter RAM_ADDR_WIDTH = $clog2(S_COUNT*FIFO_DEPTH/KEEP_WIDTH);
parameter CMD_ADDR_WIDTH = $clog2(CMD_FIFO_DEPTH);
integer i, j;
// check configuration
initial begin
if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin
$error("Error: input data width not evenly divisble (instance %m)");
$finish;
end
if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin
$error("Error: output data width not evenly divisble (instance %m)");
$finish;
end
if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin
$error("Error: word size mismatch (instance %m)");
$finish;
end
if (DEST_WIDTH < CL_M_COUNT) begin
$error("Error: DEST_WIDTH too small for port count (instance %m)");
$finish;
end
if (M_BASE == 0) begin
// M_BASE is zero, route with tdest as port index
end else if (M_TOP == 0) begin
// M_TOP is zero, assume equal to M_BASE
for (i = 0; i < M_COUNT; i = i + 1) begin
for (j = i+1; j < M_COUNT; j = j + 1) begin
if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] == M_BASE[j*DEST_WIDTH +: DEST_WIDTH]) begin
$display("%d: %08x", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH]);
$display("%d: %08x", j, M_BASE[j*DEST_WIDTH +: DEST_WIDTH]);
$error("Error: ranges overlap (instance %m)");
$finish;
end
end
end
end else begin
for (i = 0; i < M_COUNT; i = i + 1) begin
if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] > M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin
$error("Error: invalid range (instance %m)");
$finish;
end
end
for (i = 0; i < M_COUNT; i = i + 1) begin
for (j = i+1; j < M_COUNT; j = j + 1) begin
if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[j*DEST_WIDTH +: DEST_WIDTH] && M_BASE[j*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin
$display("%d: %08x-%08x", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH], M_TOP[i*DEST_WIDTH +: DEST_WIDTH]);
$display("%d: %08x-%08x", j, M_BASE[j*DEST_WIDTH +: DEST_WIDTH], M_TOP[j*DEST_WIDTH +: DEST_WIDTH]);
$error("Error: ranges overlap (instance %m)");
$finish;
end
end
end
end
end
// Shared RAM
reg [DATA_WIDTH-1:0] mem[(2**RAM_ADDR_WIDTH)-1:0];
reg [DATA_WIDTH-1:0] mem_read_data_reg;
reg [M_COUNT-1:0] mem_read_data_valid_reg;
wire [S_COUNT*DATA_WIDTH-1:0] port_ram_wr_data;
wire [S_COUNT*RAM_ADDR_WIDTH-1:0] port_ram_wr_addr;
wire [S_COUNT-1:0] port_ram_wr_en;
wire [S_COUNT-1:0] port_ram_wr_ack;
wire [M_COUNT*RAM_ADDR_WIDTH-1:0] port_ram_rd_addr;
wire [M_COUNT-1:0] port_ram_rd_en;
wire [M_COUNT-1:0] port_ram_rd_ack;
wire [M_COUNT*DATA_WIDTH-1:0] port_ram_rd_data;
wire [M_COUNT-1:0] port_ram_rd_data_valid;
assign port_ram_rd_data = {M_COUNT{mem_read_data_reg}};
assign port_ram_rd_data_valid = mem_read_data_valid_reg;
wire [CL_S_COUNT-1:0] ram_wr_sel;
wire ram_wr_en;
wire [CL_M_COUNT-1:0] ram_rd_sel;
wire ram_rd_en;
generate
if (S_COUNT > 1) begin
arbiter #(
.PORTS(S_COUNT),
.TYPE("ROUND_ROBIN"),
.BLOCK("NONE"),
.LSB_PRIORITY("HIGH")
)
ram_write_arb_inst (
.clk(clk),
.rst(rst),
.request(port_ram_wr_en & ~port_ram_wr_ack),
.acknowledge({S_COUNT{1'b0}}),
.grant(port_ram_wr_ack),
.grant_valid(ram_wr_en),
.grant_encoded(ram_wr_sel)
);
end else begin
assign ram_wr_en = port_ram_wr_en;
assign port_ram_wr_ack = port_ram_wr_en;
assign ram_wr_sel = 0;
end
endgenerate
always @(posedge clk) begin
if (ram_wr_en) begin
mem[port_ram_wr_addr[ram_wr_sel*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]] <= port_ram_wr_data[ram_wr_sel*DATA_WIDTH +: DATA_WIDTH];
end
end
generate
if (M_COUNT > 1) begin
arbiter #(
.PORTS(M_COUNT),
.TYPE("ROUND_ROBIN"),
.BLOCK("NONE"),
.LSB_PRIORITY("HIGH")
)
ram_read_arb_inst (
.clk(clk),
.rst(rst),
.request(port_ram_rd_en & ~port_ram_rd_ack),
.acknowledge({M_COUNT{1'b0}}),
.grant(port_ram_rd_ack),
.grant_valid(ram_rd_en),
.grant_encoded(ram_rd_sel)
);
end else begin
assign ram_rd_en = port_ram_rd_en;
assign port_ram_rd_ack = port_ram_rd_en;
assign ram_rd_sel = 0;
end
endgenerate
always @(posedge clk) begin
mem_read_data_valid_reg <= 0;
if (ram_rd_en) begin
mem_read_data_reg <= mem[port_ram_rd_addr[ram_rd_sel*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]];
mem_read_data_valid_reg <= 1 << ram_rd_sel;
end
if (rst) begin
mem_read_data_valid_reg <= 0;
end
end
// Interconnect
wire [S_COUNT*RAM_ADDR_WIDTH-1:0] int_cmd_addr;
wire [S_COUNT*ADDR_WIDTH-1:0] int_cmd_len;
wire [S_COUNT*CMD_ADDR_WIDTH-1:0] int_cmd_id;
wire [S_COUNT*KEEP_WIDTH-1:0] int_cmd_tkeep;
wire [S_COUNT*ID_WIDTH-1:0] int_cmd_tid;
wire [S_COUNT*DEST_WIDTH-1:0] int_cmd_tdest;
wire [S_COUNT*USER_WIDTH-1:0] int_cmd_tuser;
wire [S_COUNT*M_COUNT-1:0] int_cmd_valid;
wire [M_COUNT*S_COUNT-1:0] int_cmd_ready;
wire [M_COUNT*CMD_ADDR_WIDTH-1:0] int_cmd_status_id;
wire [M_COUNT*S_COUNT-1:0] int_cmd_status_valid;
wire [S_COUNT*M_COUNT-1:0] int_cmd_status_ready;
generate
genvar m, n;
for (m = 0; m < S_COUNT; m = m + 1) begin : s_ifaces
wire [DATA_WIDTH-1:0] port_axis_tdata;
wire [KEEP_WIDTH-1:0] port_axis_tkeep;
wire port_axis_tvalid;
wire port_axis_tready;
wire port_axis_tlast;
wire [ID_WIDTH-1:0] port_axis_tid;
wire [DEST_WIDTH-1:0] port_axis_tdest;
wire [USER_WIDTH-1:0] port_axis_tuser;
axis_adapter #(
.S_DATA_WIDTH(S_DATA_WIDTH),
.S_KEEP_ENABLE(S_KEEP_ENABLE),
.S_KEEP_WIDTH(S_KEEP_WIDTH),
.M_DATA_WIDTH(DATA_WIDTH),
.M_KEEP_ENABLE(1),
.M_KEEP_WIDTH(KEEP_WIDTH),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(1),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH)
)
adapter_inst (
.clk(clk),
.rst(rst),
// AXI input
.s_axis_tdata(s_axis_tdata[S_DATA_WIDTH*m +: S_DATA_WIDTH]),
.s_axis_tkeep(s_axis_tkeep[S_KEEP_WIDTH*m +: S_KEEP_WIDTH]),
.s_axis_tvalid(s_axis_tvalid[m]),
.s_axis_tready(s_axis_tready[m]),
.s_axis_tlast(s_axis_tlast[m]),
.s_axis_tid(s_axis_tid[ID_WIDTH*m +: ID_WIDTH]),
.s_axis_tdest(s_axis_tdest[DEST_WIDTH*m +: DEST_WIDTH]),
.s_axis_tuser(s_axis_tuser[USER_WIDTH*m +: USER_WIDTH]),
// AXI output
.m_axis_tdata(port_axis_tdata),
.m_axis_tkeep(port_axis_tkeep),
.m_axis_tvalid(port_axis_tvalid),
.m_axis_tready(port_axis_tready),
.m_axis_tlast(port_axis_tlast),
.m_axis_tid(port_axis_tid),
.m_axis_tdest(port_axis_tdest),
.m_axis_tuser(port_axis_tuser)
);
// decoding
reg [CL_M_COUNT-1:0] select_reg = 0, select_next;
reg drop_reg = 1'b0, drop_next;
reg select_valid_reg = 1'b0, select_valid_next;
integer k;
always @* begin
select_next = select_reg;
drop_next = drop_reg && !(port_axis_tvalid && port_axis_tready && port_axis_tlast);
select_valid_next = select_valid_reg && !(port_axis_tvalid && port_axis_tready && port_axis_tlast);
if (port_axis_tvalid && !select_valid_reg && !drop_reg) begin
select_next = 1'b0;
select_valid_next = 1'b0;
drop_next = 1'b1;
for (k = 0; k < M_COUNT; k = k + 1) begin
if (M_BASE == 0) begin
// M_BASE is zero, route with $clog2(M_COUNT) MSBs of tdest as port index
if (port_axis_tdest[DEST_WIDTH-CL_M_COUNT +: CL_M_COUNT] == k && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin
select_next = k;
select_valid_next = 1'b1;
drop_next = 1'b0;
end
end else if (M_TOP == 0) begin
// M_TOP is zero, assume equal to M_BASE
if (port_axis_tdest == M_BASE[k*DEST_WIDTH +: DEST_WIDTH] && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin
select_next = k;
select_valid_next = 1'b1;
drop_next = 1'b0;
end
end else begin
if (port_axis_tdest >= M_BASE[k*DEST_WIDTH +: DEST_WIDTH] && port_axis_tdest <= M_TOP[k*DEST_WIDTH +: DEST_WIDTH] && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin
select_next = k;
select_valid_next = 1'b1;
drop_next = 1'b0;
end
end
end
end
end
always @(posedge clk) begin
select_reg <= select_next;
drop_reg <= drop_next;
select_valid_reg <= select_valid_next;
if (rst) begin
select_valid_reg <= 1'b0;
end
end
// status arbitration
wire [M_COUNT-1:0] request;
wire [M_COUNT-1:0] acknowledge;
wire [M_COUNT-1:0] grant;
wire grant_valid;
wire [CL_M_COUNT-1:0] grant_encoded;
arbiter #(
.PORTS(M_COUNT),
.TYPE(ARB_TYPE),
.BLOCK("ACKNOWLEDGE"),
.LSB_PRIORITY(LSB_PRIORITY)
)
cmd_status_arb_inst (
.clk(clk),
.rst(rst),
.request(request),
.acknowledge(acknowledge),
.grant(grant),
.grant_valid(grant_valid),
.grant_encoded(grant_encoded)
);
// mux
wire [CMD_ADDR_WIDTH-1:0] cmd_status_id_mux = int_cmd_status_id[grant_encoded*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH];
wire cmd_status_valid_mux = int_cmd_status_valid[grant_encoded*S_COUNT+m] && grant_valid;
wire cmd_status_ready_mux;
assign int_cmd_status_ready[m*M_COUNT +: M_COUNT] = (grant_valid && cmd_status_ready_mux) << grant_encoded;
for (n = 0; n < M_COUNT; n = n + 1) begin
assign request[n] = int_cmd_status_valid[m+n*S_COUNT] && !grant[n];
assign acknowledge[n] = grant[n] && int_cmd_status_valid[m+n*S_COUNT] && cmd_status_ready_mux;
end
reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next;
reg [ADDR_WIDTH:0] wr_ptr_cur_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_next;
reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next;
reg [ADDR_WIDTH-1:0] len_reg = {ADDR_WIDTH{1'b0}}, len_next;
// full when first MSB different but rest same
wire full = wr_ptr_cur_reg == (rd_ptr_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}});
// empty when pointers match exactly
wire empty = wr_ptr_reg == rd_ptr_reg;
// overflow within packet
wire full_wr = wr_ptr_cur_reg == (wr_ptr_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}});
reg drop_frame_reg = 1'b0, drop_frame_next;
reg overflow_reg = 1'b0, overflow_next;
reg bad_frame_reg = 1'b0, bad_frame_next;
reg good_frame_reg = 1'b0, good_frame_next;
reg [DATA_WIDTH-1:0] ram_wr_data_reg = {DATA_WIDTH{1'b0}}, ram_wr_data_next;
reg [ADDR_WIDTH-1:0] ram_wr_addr_reg = {ADDR_WIDTH{1'b0}}, ram_wr_addr_next;
reg ram_wr_en_reg = 1'b0, ram_wr_en_next;
wire ram_wr_ack;
reg [2**CMD_ADDR_WIDTH-1:0] cmd_table_active = 0;
reg [2**CMD_ADDR_WIDTH-1:0] cmd_table_commit = 0;
reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_addr_start[2**CMD_ADDR_WIDTH-1:0];
reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_addr_end[2**CMD_ADDR_WIDTH-1:0];
reg [ADDR_WIDTH-1:0] cmd_table_len[2**CMD_ADDR_WIDTH-1:0];
reg [CL_M_COUNT-1:0] cmd_table_select[2**CMD_ADDR_WIDTH-1:0];
reg [KEEP_WIDTH-1:0] cmd_table_tkeep[2**CMD_ADDR_WIDTH-1:0];
reg [ID_WIDTH-1:0] cmd_table_tid[2**CMD_ADDR_WIDTH-1:0];
reg [DEST_WIDTH-1:0] cmd_table_tdest[2**CMD_ADDR_WIDTH-1:0];
reg [USER_WIDTH-1:0] cmd_table_tuser[2**CMD_ADDR_WIDTH-1:0];
reg [CMD_ADDR_WIDTH+1-1:0] cmd_table_start_ptr_reg = 0;
reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_start_addr_start;
reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_start_addr_end;
reg [ADDR_WIDTH-1:0] cmd_table_start_len;
reg [CL_M_COUNT-1:0] cmd_table_start_select;
reg [KEEP_WIDTH-1:0] cmd_table_start_tkeep;
reg [ID_WIDTH-1:0] cmd_table_start_tid;
reg [DEST_WIDTH-1:0] cmd_table_start_tdest;
reg [USER_WIDTH-1:0] cmd_table_start_tuser;
reg cmd_table_start_en;
reg [CMD_ADDR_WIDTH+1-1:0] cmd_table_read_ptr_reg = 0;
reg cmd_table_read_en;
reg [CMD_ADDR_WIDTH-1:0] cmd_table_commit_ptr;
reg cmd_table_commit_en;
reg [CMD_ADDR_WIDTH+1-1:0] cmd_table_finish_ptr_reg = 0;
reg cmd_table_finish_en;
reg [RAM_ADDR_WIDTH-1:0] cmd_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, cmd_addr_next;
reg [ADDR_WIDTH-1:0] cmd_len_reg = {ADDR_WIDTH{1'b0}}, cmd_len_next;
reg [CMD_ADDR_WIDTH-1:0] cmd_id_reg = {CMD_ADDR_WIDTH{1'b0}}, cmd_id_next;
reg [KEEP_WIDTH-1:0] cmd_tkeep_reg = {KEEP_WIDTH{1'b0}}, cmd_tkeep_next;
reg [ID_WIDTH-1:0] cmd_tid_reg = {ID_WIDTH{1'b0}}, cmd_tid_next;
reg [DEST_WIDTH-1:0] cmd_tdest_reg = {DEST_WIDTH{1'b0}}, cmd_tdest_next;
reg [USER_WIDTH-1:0] cmd_tuser_reg = {USER_WIDTH{1'b0}}, cmd_tuser_next;
reg [M_COUNT-1:0] cmd_valid_reg = 0, cmd_valid_next;
reg cmd_status_ready_reg = 1'b0, cmd_status_ready_next;
wire [M_COUNT-1:0] port_cmd_ready;
for (n = 0; n < M_COUNT; n = n + 1) begin
assign port_cmd_ready[n] = int_cmd_ready[m+n*S_COUNT];
end
assign port_axis_tready = (select_valid_reg && (!ram_wr_en_reg || ram_wr_ack) && (!full || full_wr || DROP_WHEN_FULL) && ($unsigned(cmd_table_start_ptr_reg - cmd_table_finish_ptr_reg) < 2**CMD_ADDR_WIDTH)) || drop_reg;
assign port_ram_wr_data[m*DATA_WIDTH +: DATA_WIDTH] = ram_wr_data_reg;
assign port_ram_wr_addr[m*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH] = ram_wr_addr_reg[ADDR_WIDTH-1:0] | (m << ADDR_WIDTH);
assign port_ram_wr_en[m] = ram_wr_en_reg;
assign ram_wr_ack = port_ram_wr_ack[m];
assign int_cmd_addr[m*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH] = cmd_addr_reg[ADDR_WIDTH-1:0] | (m << ADDR_WIDTH);
assign int_cmd_len[m*ADDR_WIDTH +: ADDR_WIDTH] = cmd_len_reg;
assign int_cmd_id[m*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH] = cmd_id_reg;
assign int_cmd_tkeep[m*KEEP_WIDTH +: KEEP_WIDTH] = cmd_tkeep_reg;
assign int_cmd_tid[m*ID_WIDTH +: ID_WIDTH] = cmd_tid_reg;
assign int_cmd_tdest[m*DEST_WIDTH +: DEST_WIDTH] = cmd_tdest_reg;
assign int_cmd_tuser[m*USER_WIDTH +: USER_WIDTH] = cmd_tuser_reg;
assign int_cmd_valid[m*M_COUNT +: M_COUNT] = cmd_valid_reg;
assign cmd_status_ready_mux = cmd_status_ready_reg;
assign status_overflow[m] = overflow_reg;
assign status_bad_frame[m] = bad_frame_reg;
assign status_good_frame[m] = good_frame_reg;
always @* begin
wr_ptr_next = wr_ptr_reg;
wr_ptr_cur_next = wr_ptr_cur_reg;
rd_ptr_next = rd_ptr_reg;
len_next = len_reg;
drop_frame_next = drop_frame_reg;
overflow_next = 1'b0;
bad_frame_next = 1'b0;
good_frame_next = 1'b0;
ram_wr_data_next = ram_wr_data_reg;
ram_wr_addr_next = ram_wr_addr_reg;
ram_wr_en_next = ram_wr_en_reg && !ram_wr_ack;
cmd_table_start_addr_start = wr_ptr_reg;
cmd_table_start_addr_end = wr_ptr_cur_reg + 1;
cmd_table_start_len = len_reg;
cmd_table_start_select = select_reg;
cmd_table_start_tkeep = S_KEEP_ENABLE ? port_axis_tkeep : 1'b1;
cmd_table_start_tid = port_axis_tid;
cmd_table_start_tdest = port_axis_tdest;
cmd_table_start_tuser = port_axis_tuser;
cmd_table_start_en = 1'b0;
cmd_table_read_en = 1'b0;
cmd_table_commit_ptr = 0;
cmd_table_commit_en = 1'b0;
cmd_table_finish_en = 1'b0;
cmd_addr_next = cmd_addr_reg;
cmd_len_next = cmd_len_reg;
cmd_id_next = cmd_id_reg;
cmd_tkeep_next = cmd_tkeep_reg;
cmd_tid_next = cmd_tid_reg;
cmd_tdest_next = cmd_tdest_reg;
cmd_tuser_next = cmd_tuser_reg;
cmd_valid_next = cmd_valid_reg;
cmd_status_ready_next = 1'b0;
// issue memory writes and commands
if (port_axis_tready && port_axis_tvalid && select_valid_reg && !drop_reg) begin
if (full || full_wr || drop_frame_reg) begin
// full, packet overflow, or currently dropping frame
// drop frame
drop_frame_next = 1'b1;
if (port_axis_tlast) begin
// end of frame, reset write pointer
wr_ptr_cur_next = wr_ptr_reg;
drop_frame_next = 1'b0;
overflow_next = 1'b1;
end
end else begin
wr_ptr_cur_next = wr_ptr_cur_reg + 1;
len_next = len_reg + 1;
// issue write operation
ram_wr_data_next = port_axis_tdata;
ram_wr_addr_next = wr_ptr_cur_reg;
ram_wr_en_next = 1'b1;
if (port_axis_tlast) begin
// end of frame
len_next = 0;
if (DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(port_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin
// bad packet, reset write pointer
wr_ptr_cur_next = wr_ptr_reg;
bad_frame_next = 1'b1;
end else begin
// good packet, update write pointer
wr_ptr_next = wr_ptr_cur_reg + 1;
good_frame_next = 1'b1;
cmd_table_start_addr_start = wr_ptr_reg;
cmd_table_start_addr_end = wr_ptr_cur_reg + 1;
cmd_table_start_len = len_reg;
cmd_table_start_select = select_reg;
cmd_table_start_tkeep = S_KEEP_ENABLE ? port_axis_tkeep : 1'b1;
cmd_table_start_tid = port_axis_tid;
cmd_table_start_tdest = port_axis_tdest;
cmd_table_start_tuser = port_axis_tuser;
cmd_table_start_en = 1'b1;
end
end
end
end
// read
cmd_valid_next = cmd_valid_reg & ~port_cmd_ready;
if (!cmd_valid_reg && cmd_table_active[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_read_ptr_reg != cmd_table_start_ptr_reg) begin
cmd_table_read_en = 1'b1;
cmd_addr_next = cmd_table_addr_start[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_len_next = cmd_table_len[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_id_next = cmd_table_read_ptr_reg;
cmd_tkeep_next = cmd_table_tkeep[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_tid_next = cmd_table_tid[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_tdest_next = cmd_table_tdest[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_tuser_next = cmd_table_tuser[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_valid_next = 1 << cmd_table_select[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]];
end
// commit
if (cmd_status_valid_mux) begin
cmd_status_ready_next = 1'b1;
cmd_table_commit_ptr = cmd_status_id_mux;
cmd_table_commit_en = 1'b1;
end
// clean-up
if (cmd_table_active[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_commit[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_finish_ptr_reg != cmd_table_start_ptr_reg) begin
// update read pointer
rd_ptr_next = cmd_table_addr_end[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]];
cmd_table_finish_en = 1'b1;
end
end
always @(posedge clk) begin
wr_ptr_reg <= wr_ptr_next;
wr_ptr_cur_reg <= wr_ptr_cur_next;
rd_ptr_reg <= rd_ptr_next;
len_reg <= len_next;
drop_frame_reg <= drop_frame_next;
overflow_reg <= overflow_next;
bad_frame_reg <= bad_frame_next;
good_frame_reg <= good_frame_next;
ram_wr_data_reg <= ram_wr_data_next;
ram_wr_addr_reg <= ram_wr_addr_next;
ram_wr_en_reg <= ram_wr_en_next;
cmd_addr_reg <= cmd_addr_next;
cmd_len_reg <= cmd_len_next;
cmd_id_reg <= cmd_id_next;
cmd_tkeep_reg <= cmd_tkeep_next;
cmd_tid_reg <= cmd_tid_next;
cmd_tdest_reg <= cmd_tdest_next;
cmd_tuser_reg <= cmd_tuser_next;
cmd_valid_reg <= cmd_valid_next;
cmd_status_ready_reg <= cmd_status_ready_next;
if (cmd_table_start_en) begin
cmd_table_start_ptr_reg <= cmd_table_start_ptr_reg + 1;
cmd_table_active[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b1;
cmd_table_commit[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b0;
cmd_table_addr_start[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_addr_start;
cmd_table_addr_end[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_addr_end;
cmd_table_len[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_len;
cmd_table_select[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_select;
cmd_table_tkeep[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tkeep;
cmd_table_tid[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tid;
cmd_table_tdest[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tdest;
cmd_table_tuser[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tuser;
end
if (cmd_table_read_en) begin
cmd_table_read_ptr_reg <= cmd_table_read_ptr_reg + 1;
end
if (cmd_table_commit_en) begin
cmd_table_commit[cmd_table_commit_ptr] <= 1'b1;
end
if (cmd_table_finish_en) begin
cmd_table_finish_ptr_reg <= cmd_table_finish_ptr_reg + 1;
cmd_table_active[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b1;
end
if (rst) begin
wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}};
rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
len_reg <= {ADDR_WIDTH{1'b0}};
drop_frame_reg <= 1'b0;
overflow_reg <= 1'b0;
bad_frame_reg <= 1'b0;
good_frame_reg <= 1'b0;
ram_wr_en_reg <= 1'b0;
cmd_valid_reg <= 1'b0;
cmd_status_ready_reg <= 1'b0;
cmd_table_start_ptr_reg <= 0;
cmd_table_read_ptr_reg <= 0;
cmd_table_finish_ptr_reg <= 0;
end
end
end // s_ifaces
for (n = 0; n < M_COUNT; n = n + 1) begin : m_ifaces
// command arbitration
wire [S_COUNT-1:0] request;
wire [S_COUNT-1:0] acknowledge;
wire [S_COUNT-1:0] grant;
wire grant_valid;
wire [CL_S_COUNT-1:0] grant_encoded;
arbiter #(
.PORTS(S_COUNT),
.TYPE(ARB_TYPE),
.BLOCK("ACKNOWLEDGE"),
.LSB_PRIORITY(LSB_PRIORITY)
)
cmd_arb_inst (
.clk(clk),
.rst(rst),
.request(request),
.acknowledge(acknowledge),
.grant(grant),
.grant_valid(grant_valid),
.grant_encoded(grant_encoded)
);
// mux
wire [RAM_ADDR_WIDTH-1:0] cmd_addr_mux = int_cmd_addr[grant_encoded*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH];
wire [ADDR_WIDTH-1:0] cmd_len_mux = int_cmd_len[grant_encoded*ADDR_WIDTH +: ADDR_WIDTH];
wire [CMD_ADDR_WIDTH-1:0] cmd_id_mux = int_cmd_id[grant_encoded*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH];
wire [KEEP_WIDTH-1:0] cmd_tkeep_mux = int_cmd_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH];
wire [ID_WIDTH-1:0] cmd_tid_mux = int_cmd_tid[grant_encoded*ID_WIDTH +: ID_WIDTH];
wire [DEST_WIDTH-1:0] cmd_tdest_mux = int_cmd_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH];
wire [USER_WIDTH-1:0] cmd_tuser_mux = int_cmd_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH];
wire cmd_valid_mux = int_cmd_valid[grant_encoded*M_COUNT+n] && grant_valid;
wire cmd_ready_mux;
assign int_cmd_ready[n*S_COUNT +: S_COUNT] = (grant_valid && cmd_ready_mux) << grant_encoded;
for (m = 0; m < S_COUNT; m = m + 1) begin
assign request[m] = int_cmd_valid[m*M_COUNT+n] && !grant[m];
assign acknowledge[m] = grant[m] && int_cmd_valid[m*M_COUNT+n] && cmd_ready_mux;
end
reg [RAM_ADDR_WIDTH-1:0] rd_ptr_reg = {RAM_ADDR_WIDTH-1{1'b0}}, rd_ptr_next;
reg [ADDR_WIDTH-1:0] len_reg = {ADDR_WIDTH{1'b0}}, len_next;
reg [CL_S_COUNT-1:0] src_reg = 0, src_next;
reg [CMD_ADDR_WIDTH-1:0] id_reg = 0, id_next;
reg [KEEP_WIDTH-1:0] last_cycle_tkeep_reg = {KEEP_WIDTH{1'b0}}, last_cycle_tkeep_next;
reg [ID_WIDTH-1:0] tid_reg = {ID_WIDTH{1'b0}}, tid_next;
reg [DEST_WIDTH-1:0] tdest_reg = {DEST_WIDTH{1'b0}}, tdest_next;
reg [USER_WIDTH-1:0] tuser_reg = {USER_WIDTH{1'b0}}, tuser_next;
reg [DATA_WIDTH-1:0] out_axis_tdata_reg = {DATA_WIDTH{1'b0}}, out_axis_tdata_next;
reg [KEEP_WIDTH-1:0] out_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}, out_axis_tkeep_next;
reg out_axis_tvalid_reg = 1'b0, out_axis_tvalid_next;
wire out_axis_tready;
reg out_axis_tlast_reg = 1'b0, out_axis_tlast_next;
reg [ID_WIDTH-1:0] out_axis_tid_reg = {ID_WIDTH{1'b0}}, out_axis_tid_next;
reg [DEST_WIDTH-1:0] out_axis_tdest_reg = {DEST_WIDTH{1'b0}}, out_axis_tdest_next;
reg [USER_WIDTH-1:0] out_axis_tuser_reg = {USER_WIDTH{1'b0}}, out_axis_tuser_next;
reg [RAM_ADDR_WIDTH-1:0] ram_rd_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, ram_rd_addr_next;
reg ram_rd_en_reg = 1'b0, ram_rd_en_next;
wire ram_rd_ack;
wire [DATA_WIDTH-1:0] ram_rd_data;
wire ram_rd_data_valid;
reg cmd_valid_reg = 1'b0, cmd_valid_next;
reg [CMD_ADDR_WIDTH-1:0] cmd_status_id_reg = {CMD_ADDR_WIDTH{1'b0}}, cmd_status_id_next;
reg [S_COUNT-1:0] cmd_status_valid_reg = 0, cmd_status_valid_next;
wire [S_COUNT-1:0] port_cmd_status_ready;
for (m = 0; m < S_COUNT; m = m + 1) begin
assign port_cmd_status_ready[m] = int_cmd_status_ready[m*M_COUNT+n];
end
reg [DATA_WIDTH-1:0] out_fifo_tdata[31:0];
reg [KEEP_WIDTH-1:0] out_fifo_tkeep[31:0];
reg out_fifo_tlast[31:0];
reg [ID_WIDTH-1:0] out_fifo_tid[31:0];
reg [DEST_WIDTH-1:0] out_fifo_tdest[31:0];
reg [USER_WIDTH-1:0] out_fifo_tuser[31:0];
reg [5:0] out_fifo_data_wr_ptr_reg = 0;
reg [DATA_WIDTH-1:0] out_fifo_data_wr_tdata;
reg out_fifo_data_wr_en;
reg [5:0] out_fifo_ctrl_wr_ptr_reg = 0;
reg [KEEP_WIDTH-1:0] out_fifo_ctrl_wr_tkeep;
reg out_fifo_ctrl_wr_tlast;
reg [ID_WIDTH-1:0] out_fifo_ctrl_wr_tid;
reg [DEST_WIDTH-1:0] out_fifo_ctrl_wr_tdest;
reg [USER_WIDTH-1:0] out_fifo_ctrl_wr_tuser;
reg out_fifo_ctrl_wr_en;
reg [5:0] out_fifo_rd_ptr_reg = 0;
reg out_fifo_rd_en;
assign port_ram_rd_addr[n*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH] = ram_rd_addr_reg;
assign port_ram_rd_en[n] = ram_rd_en_reg;
assign ram_rd_ack = port_ram_rd_ack[n];
assign ram_rd_data = port_ram_rd_data[n*DATA_WIDTH +: DATA_WIDTH];
assign ram_rd_data_valid = port_ram_rd_data_valid[n];
assign cmd_ready_mux = !cmd_valid_reg;
assign int_cmd_status_id[n*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH] = cmd_status_id_reg;
assign int_cmd_status_valid[n*S_COUNT +: S_COUNT] = cmd_status_valid_reg;
always @* begin
rd_ptr_next = rd_ptr_reg;
len_next = len_reg;
src_next = src_reg;
id_next = id_reg;
last_cycle_tkeep_next = last_cycle_tkeep_reg;
tid_next = tid_reg;
tdest_next = tdest_reg;
tuser_next = tuser_reg;
out_axis_tdata_next = out_axis_tdata_reg;
out_axis_tkeep_next = out_axis_tkeep_reg;
out_axis_tvalid_next = out_axis_tvalid_reg && !out_axis_tready;
out_axis_tlast_next = out_axis_tlast_reg;
out_axis_tid_next = out_axis_tid_reg;
out_axis_tdest_next = out_axis_tdest_reg;
out_axis_tuser_next = out_axis_tuser_reg;
ram_rd_addr_next = ram_rd_addr_reg;
ram_rd_en_next = ram_rd_en_reg && !ram_rd_ack;
cmd_valid_next = cmd_valid_reg;
cmd_status_id_next = cmd_status_id_reg;
cmd_status_valid_next = cmd_status_valid_reg & ~port_cmd_status_ready;
out_fifo_data_wr_tdata = ram_rd_data;
out_fifo_data_wr_en = 1'b0;
out_fifo_ctrl_wr_tkeep = len_reg == 0 ? last_cycle_tkeep_reg : {KEEP_WIDTH{1'b1}};
out_fifo_ctrl_wr_tlast = len_reg == 0;
out_fifo_ctrl_wr_tid = tid_reg;
out_fifo_ctrl_wr_tdest = tdest_reg;
out_fifo_ctrl_wr_tuser = tuser_reg;
out_fifo_ctrl_wr_en = 1'b0;
out_fifo_rd_en = 1'b0;
// receive commands
if (!cmd_valid_reg && cmd_valid_mux) begin
cmd_valid_next = 1'b1;
rd_ptr_next = cmd_addr_mux;
len_next = cmd_len_mux;
last_cycle_tkeep_next = cmd_tkeep_mux;
id_next = cmd_id_mux;
tid_next = cmd_tid_mux;
tdest_next = cmd_tdest_mux;
tuser_next = cmd_tuser_mux;
src_next = grant_encoded;
end
// process commands and issue memory reads
if (cmd_valid_reg && (!ram_rd_en_reg || ram_rd_ack) && ($unsigned(out_fifo_ctrl_wr_ptr_reg - out_fifo_rd_ptr_reg) < 32)) begin
// update counters
rd_ptr_next[ADDR_WIDTH-1:0] = rd_ptr_reg[ADDR_WIDTH-1:0] + 1;
len_next = len_reg - 1;
// issue memory read
ram_rd_addr_next = rd_ptr_reg;
ram_rd_en_next = 1'b1;
// write output control FIFO
out_fifo_ctrl_wr_tkeep = len_reg == 0 ? last_cycle_tkeep_reg : {KEEP_WIDTH{1'b1}};
out_fifo_ctrl_wr_tlast = len_reg == 0;
out_fifo_ctrl_wr_tid = tid_reg;
out_fifo_ctrl_wr_tdest = tdest_reg;
out_fifo_ctrl_wr_tuser = tuser_reg;
out_fifo_ctrl_wr_en = 1'b1;
if (len_reg == 0) begin
// indicate operation complete
cmd_status_id_next = id_reg;
cmd_status_valid_next = 1 << src_reg;
cmd_valid_next = 1'b0;
end
end
// write RAM read data to output data FIFO
if (ram_rd_data_valid) begin
out_fifo_data_wr_tdata = ram_rd_data;
out_fifo_data_wr_en = 1'b1;
end
// generate output AXI stream data from control and data FIFOs
if ((out_axis_tready || !out_axis_tvalid_reg) && (out_fifo_rd_ptr_reg != out_fifo_ctrl_wr_ptr_reg) && (out_fifo_rd_ptr_reg != out_fifo_data_wr_ptr_reg)) begin
out_fifo_rd_en = 1'b1;
out_axis_tdata_next = out_fifo_tdata[out_fifo_rd_ptr_reg[4:0]];
out_axis_tkeep_next = out_fifo_tkeep[out_fifo_rd_ptr_reg[4:0]];
out_axis_tvalid_next = 1'b1;
out_axis_tlast_next = out_fifo_tlast[out_fifo_rd_ptr_reg[4:0]];
out_axis_tid_next = out_fifo_tid[out_fifo_rd_ptr_reg[4:0]];
out_axis_tdest_next = out_fifo_tdest[out_fifo_rd_ptr_reg[4:0]];
out_axis_tuser_next = out_fifo_tuser[out_fifo_rd_ptr_reg[4:0]];
end
end
always @(posedge clk) begin
rd_ptr_reg <= rd_ptr_next;
len_reg <= len_next;
src_reg <= src_next;
id_reg <= id_next;
last_cycle_tkeep_reg <= last_cycle_tkeep_next;
tid_reg <= tid_next;
tdest_reg <= tdest_next;
tuser_reg <= tuser_next;
out_axis_tdata_reg <= out_axis_tdata_next;
out_axis_tkeep_reg <= out_axis_tkeep_next;
out_axis_tvalid_reg <= out_axis_tvalid_next;
out_axis_tlast_reg <= out_axis_tlast_next;
out_axis_tid_reg <= out_axis_tid_next;
out_axis_tdest_reg <= out_axis_tdest_next;
out_axis_tuser_reg <= out_axis_tuser_next;
ram_rd_addr_reg <= ram_rd_addr_next;
ram_rd_en_reg <= ram_rd_en_next;
cmd_valid_reg <= cmd_valid_next;
cmd_status_id_reg <= cmd_status_id_next;
cmd_status_valid_reg <= cmd_status_valid_next;
if (out_fifo_data_wr_en) begin
out_fifo_data_wr_ptr_reg <= out_fifo_data_wr_ptr_reg + 1;
out_fifo_tdata[out_fifo_data_wr_ptr_reg[4:0]] <= out_fifo_data_wr_tdata;
end
if (out_fifo_ctrl_wr_en) begin
out_fifo_ctrl_wr_ptr_reg <= out_fifo_ctrl_wr_ptr_reg + 1;
out_fifo_tkeep[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tkeep;
out_fifo_tlast[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tlast;
out_fifo_tid[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tid;
out_fifo_tdest[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tdest;
out_fifo_tuser[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tuser;
end
if (out_fifo_rd_en) begin
out_fifo_rd_ptr_reg <= out_fifo_rd_ptr_reg + 1;
end
if (rst) begin
len_reg <= 0;
out_axis_tvalid_reg <= 1'b0;
ram_rd_en_reg <= 1'b0;
cmd_valid_reg <= 1'b0;
cmd_status_valid_reg <= 0;
out_fifo_data_wr_ptr_reg <= 0;
out_fifo_ctrl_wr_ptr_reg <= 0;
out_fifo_rd_ptr_reg <= 0;
end
end
axis_adapter #(
.S_DATA_WIDTH(DATA_WIDTH),
.S_KEEP_ENABLE(1),
.S_KEEP_WIDTH(KEEP_WIDTH),
.M_DATA_WIDTH(M_DATA_WIDTH),
.M_KEEP_ENABLE(M_KEEP_ENABLE),
.M_KEEP_WIDTH(M_KEEP_WIDTH),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(1),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH)
)
adapter_inst (
.clk(clk),
.rst(rst),
// AXI input
.s_axis_tdata(out_axis_tdata_reg),
.s_axis_tkeep(out_axis_tkeep_reg),
.s_axis_tvalid(out_axis_tvalid_reg),
.s_axis_tready(out_axis_tready),
.s_axis_tlast(out_axis_tlast_reg),
.s_axis_tid(out_axis_tid_reg),
.s_axis_tdest(out_axis_tdest_reg),
.s_axis_tuser(out_axis_tuser_reg),
// AXI output
.m_axis_tdata(m_axis_tdata[M_DATA_WIDTH*n +: M_DATA_WIDTH]),
.m_axis_tkeep(m_axis_tkeep[M_KEEP_WIDTH*n +: M_KEEP_WIDTH]),
.m_axis_tvalid(m_axis_tvalid[n]),
.m_axis_tready(m_axis_tready[n]),
.m_axis_tlast(m_axis_tlast[n]),
.m_axis_tid(m_axis_tid[ID_WIDTH*n +: ID_WIDTH]),
.m_axis_tdest(m_axis_tdest[DEST_WIDTH*n +: DEST_WIDTH]),
.m_axis_tuser(m_axis_tuser[USER_WIDTH*n +: USER_WIDTH])
);
end // m_ifaces
endgenerate
endmodule
/*
Copyright (c) 2014-2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream rate limiter
*/
module axis_rate_limit #
(
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 8,
// Propagate tkeep signal
// If disabled, tkeep assumed to be 1'b1
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Propagate tlast signal
parameter LAST_ENABLE = 1,
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// Propagate tdest signal
parameter DEST_ENABLE = 0,
// tdest signal width
parameter DEST_WIDTH = 8,
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1
)
(
input wire clk,
input wire rst,
/*
* AXI input
*/
input wire [DATA_WIDTH-1:0] s_axis_tdata,
input wire [KEEP_WIDTH-1:0] s_axis_tkeep,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire [ID_WIDTH-1:0] s_axis_tid,
input wire [DEST_WIDTH-1:0] s_axis_tdest,
input wire [USER_WIDTH-1:0] s_axis_tuser,
/*
* AXI output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [ID_WIDTH-1:0] m_axis_tid,
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser,
/*
* Configuration
*/
input wire [7:0] rate_num,
input wire [7:0] rate_denom,
input wire rate_by_frame
);
// internal datapath
reg [DATA_WIDTH-1:0] m_axis_tdata_int;
reg [KEEP_WIDTH-1:0] m_axis_tkeep_int;
reg m_axis_tvalid_int;
reg m_axis_tready_int_reg = 1'b0;
reg m_axis_tlast_int;
reg [ID_WIDTH-1:0] m_axis_tid_int;
reg [DEST_WIDTH-1:0] m_axis_tdest_int;
reg [USER_WIDTH-1:0] m_axis_tuser_int;
wire m_axis_tready_int_early;
reg [23:0] acc_reg = 24'd0, acc_next;
reg pause;
reg frame_reg = 1'b0, frame_next;
reg s_axis_tready_reg = 1'b0, s_axis_tready_next;
assign s_axis_tready = s_axis_tready_reg;
always @* begin
acc_next = acc_reg;
pause = 1'b0;
frame_next = frame_reg;
if (acc_reg >= rate_num) begin
acc_next = acc_reg - rate_num;
end
if (s_axis_tready && s_axis_tvalid) begin
// read input
frame_next = !s_axis_tlast;
acc_next = acc_reg + (rate_denom - rate_num);
end
if (acc_next >= rate_num) begin
if (LAST_ENABLE && rate_by_frame) begin
pause = !frame_next;
end else begin
pause = 1'b1;
end
end
s_axis_tready_next = m_axis_tready_int_early && !pause;
m_axis_tdata_int = s_axis_tdata;
m_axis_tkeep_int = s_axis_tkeep;
m_axis_tvalid_int = s_axis_tvalid && s_axis_tready;
m_axis_tlast_int = s_axis_tlast;
m_axis_tid_int = s_axis_tid;
m_axis_tdest_int = s_axis_tdest;
m_axis_tuser_int = s_axis_tuser;
end
always @(posedge clk) begin
if (rst) begin
acc_reg <= 24'd0;
frame_reg <= 1'b0;
s_axis_tready_reg <= 1'b0;
end else begin
acc_reg <= acc_next;
frame_reg <= frame_next;
s_axis_tready_reg <= s_axis_tready_next;
end
end
// output datapath logic
reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
reg m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}};
reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next;
reg temp_m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}};
// datapath control
reg store_axis_int_to_output;
reg store_axis_int_to_temp;
reg store_axis_temp_to_output;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : 1'b1;
assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_axis_tvalid_next = m_axis_tvalid_reg;
temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg;
store_axis_int_to_output = 1'b0;
store_axis_int_to_temp = 1'b0;
store_axis_temp_to_output = 1'b0;
if (m_axis_tready_int_reg) begin
// input is ready
if (m_axis_tready || !m_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_temp = 1'b1;
end
end else if (m_axis_tready) begin
// input is not ready, but output is ready
m_axis_tvalid_next = temp_m_axis_tvalid_reg;
temp_m_axis_tvalid_next = 1'b0;
store_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_axis_tvalid_reg <= 1'b0;
m_axis_tready_int_reg <= 1'b0;
temp_m_axis_tvalid_reg <= 1'b0;
end else begin
m_axis_tvalid_reg <= m_axis_tvalid_next;
m_axis_tready_int_reg <= m_axis_tready_int_early;
temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next;
end
// datapath
if (store_axis_int_to_output) begin
m_axis_tdata_reg <= m_axis_tdata_int;
m_axis_tkeep_reg <= m_axis_tkeep_int;
m_axis_tlast_reg <= m_axis_tlast_int;
m_axis_tid_reg <= m_axis_tid_int;
m_axis_tdest_reg <= m_axis_tdest_int;
m_axis_tuser_reg <= m_axis_tuser_int;
end else if (store_axis_temp_to_output) begin
m_axis_tdata_reg <= temp_m_axis_tdata_reg;
m_axis_tkeep_reg <= temp_m_axis_tkeep_reg;
m_axis_tlast_reg <= temp_m_axis_tlast_reg;
m_axis_tid_reg <= temp_m_axis_tid_reg;
m_axis_tdest_reg <= temp_m_axis_tdest_reg;
m_axis_tuser_reg <= temp_m_axis_tuser_reg;
end
if (store_axis_int_to_temp) begin
temp_m_axis_tdata_reg <= m_axis_tdata_int;
temp_m_axis_tkeep_reg <= m_axis_tkeep_int;
temp_m_axis_tlast_reg <= m_axis_tlast_int;
temp_m_axis_tid_reg <= m_axis_tid_int;
temp_m_axis_tdest_reg <= m_axis_tdest_int;
temp_m_axis_tuser_reg <= m_axis_tuser_int;
end
end
endmodule
/*
Copyright (c) 2014-2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream register
*/
module axis_register #
(
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 8,
// Propagate tkeep signal
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Propagate tlast signal
parameter LAST_ENABLE = 1,
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// Propagate tdest signal
parameter DEST_ENABLE = 0,
// tdest signal width
parameter DEST_WIDTH = 8,
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1,
// Register type
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
parameter REG_TYPE = 2
)
(
input wire clk,
input wire rst,
/*
* AXI Stream input
*/
input wire [DATA_WIDTH-1:0] s_axis_tdata,
input wire [KEEP_WIDTH-1:0] s_axis_tkeep,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire [ID_WIDTH-1:0] s_axis_tid,
input wire [DEST_WIDTH-1:0] s_axis_tdest,
input wire [USER_WIDTH-1:0] s_axis_tuser,
/*
* AXI Stream output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [ID_WIDTH-1:0] m_axis_tid,
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser
);
generate
if (REG_TYPE > 1) begin
// skid buffer, no bubble cycles
// datapath registers
reg s_axis_tready_reg = 1'b0;
reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
reg m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}};
reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next;
reg temp_m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}};
// datapath control
reg store_axis_input_to_output;
reg store_axis_input_to_temp;
reg store_axis_temp_to_output;
assign s_axis_tready = s_axis_tready_reg;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : 1'b1;
assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
wire s_axis_tready_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !s_axis_tvalid));
always @* begin
// transfer sink ready state to source
m_axis_tvalid_next = m_axis_tvalid_reg;
temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg;
store_axis_input_to_output = 1'b0;
store_axis_input_to_temp = 1'b0;
store_axis_temp_to_output = 1'b0;
if (s_axis_tready_reg) begin
// input is ready
if (m_axis_tready || !m_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_axis_tvalid_next = s_axis_tvalid;
store_axis_input_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_axis_tvalid_next = s_axis_tvalid;
store_axis_input_to_temp = 1'b1;
end
end else if (m_axis_tready) begin
// input is not ready, but output is ready
m_axis_tvalid_next = temp_m_axis_tvalid_reg;
temp_m_axis_tvalid_next = 1'b0;
store_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
s_axis_tready_reg <= 1'b0;
m_axis_tvalid_reg <= 1'b0;
temp_m_axis_tvalid_reg <= 1'b0;
end else begin
s_axis_tready_reg <= s_axis_tready_early;
m_axis_tvalid_reg <= m_axis_tvalid_next;
temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next;
end
// datapath
if (store_axis_input_to_output) begin
m_axis_tdata_reg <= s_axis_tdata;
m_axis_tkeep_reg <= s_axis_tkeep;
m_axis_tlast_reg <= s_axis_tlast;
m_axis_tid_reg <= s_axis_tid;
m_axis_tdest_reg <= s_axis_tdest;
m_axis_tuser_reg <= s_axis_tuser;
end else if (store_axis_temp_to_output) begin
m_axis_tdata_reg <= temp_m_axis_tdata_reg;
m_axis_tkeep_reg <= temp_m_axis_tkeep_reg;
m_axis_tlast_reg <= temp_m_axis_tlast_reg;
m_axis_tid_reg <= temp_m_axis_tid_reg;
m_axis_tdest_reg <= temp_m_axis_tdest_reg;
m_axis_tuser_reg <= temp_m_axis_tuser_reg;
end
if (store_axis_input_to_temp) begin
temp_m_axis_tdata_reg <= s_axis_tdata;
temp_m_axis_tkeep_reg <= s_axis_tkeep;
temp_m_axis_tlast_reg <= s_axis_tlast;
temp_m_axis_tid_reg <= s_axis_tid;
temp_m_axis_tdest_reg <= s_axis_tdest;
temp_m_axis_tuser_reg <= s_axis_tuser;
end
end
end else if (REG_TYPE == 1) begin
// simple register, inserts bubble cycles
// datapath registers
reg s_axis_tready_reg = 1'b0;
reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
reg m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}};
// datapath control
reg store_axis_input_to_output;
assign s_axis_tready = s_axis_tready_reg;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : 1'b1;
assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
// enable ready input next cycle if output buffer will be empty
wire s_axis_tready_early = !m_axis_tvalid_next;
always @* begin
// transfer sink ready state to source
m_axis_tvalid_next = m_axis_tvalid_reg;
store_axis_input_to_output = 1'b0;
if (s_axis_tready_reg) begin
m_axis_tvalid_next = s_axis_tvalid;
store_axis_input_to_output = 1'b1;
end else if (m_axis_tready) begin
m_axis_tvalid_next = 1'b0;
end
end
always @(posedge clk) begin
if (rst) begin
s_axis_tready_reg <= 1'b0;
m_axis_tvalid_reg <= 1'b0;
end else begin
s_axis_tready_reg <= s_axis_tready_early;
m_axis_tvalid_reg <= m_axis_tvalid_next;
end
// datapath
if (store_axis_input_to_output) begin
m_axis_tdata_reg <= s_axis_tdata;
m_axis_tkeep_reg <= s_axis_tkeep;
m_axis_tlast_reg <= s_axis_tlast;
m_axis_tid_reg <= s_axis_tid;
m_axis_tdest_reg <= s_axis_tdest;
m_axis_tuser_reg <= s_axis_tuser;
end
end
end else begin
// bypass
assign m_axis_tdata = s_axis_tdata;
assign m_axis_tkeep = KEEP_ENABLE ? s_axis_tkeep : {KEEP_WIDTH{1'b1}};
assign m_axis_tvalid = s_axis_tvalid;
assign m_axis_tlast = LAST_ENABLE ? s_axis_tlast : 1'b1;
assign m_axis_tid = ID_ENABLE ? s_axis_tid : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? s_axis_tdest : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? s_axis_tuser : {USER_WIDTH{1'b0}};
assign s_axis_tready = m_axis_tready;
end
endgenerate
endmodule
/*
Copyright (c) 2014-2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream SRL-based FIFO
*/
module axis_srl_fifo #
(
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 8,
// Propagate tkeep signal
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Propagate tlast signal
parameter LAST_ENABLE = 1,
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// Propagate tdest signal
parameter DEST_ENABLE = 0,
// tdest signal width
parameter DEST_WIDTH = 8,
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1,
// FIFO depth in cycles
parameter DEPTH = 16
)
(
input wire clk,
input wire rst,
/*
* AXI input
*/
input wire [DATA_WIDTH-1:0] s_axis_tdata,
input wire [KEEP_WIDTH-1:0] s_axis_tkeep,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire [ID_WIDTH-1:0] s_axis_tid,
input wire [DEST_WIDTH-1:0] s_axis_tdest,
input wire [USER_WIDTH-1:0] s_axis_tuser,
/*
* AXI output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [ID_WIDTH-1:0] m_axis_tid,
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser,
/*
* Status
*/
output wire [$clog2(DEPTH+1)-1:0] count
);
localparam KEEP_OFFSET = DATA_WIDTH;
localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0);
localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0);
localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0);
localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0);
localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0);
reg [WIDTH-1:0] data_reg[DEPTH-1:0];
reg [$clog2(DEPTH+1)-1:0] ptr_reg = 0;
reg full_reg = 1'b0, full_next;
reg empty_reg = 1'b1, empty_next;
wire [WIDTH-1:0] s_axis;
wire [WIDTH-1:0] m_axis = data_reg[ptr_reg-1];
assign s_axis_tready = !full_reg;
generate
assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata;
if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep;
if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast;
if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid;
if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest;
if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser;
endgenerate
assign m_axis_tvalid = !empty_reg;
assign m_axis_tdata = m_axis[DATA_WIDTH-1:0];
assign m_axis_tkeep = KEEP_ENABLE ? m_axis[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}};
assign m_axis_tlast = LAST_ENABLE ? m_axis[LAST_OFFSET] : 1'b1;
assign m_axis_tid = ID_ENABLE ? m_axis[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}};
assign count = ptr_reg;
wire ptr_empty = ptr_reg == 0;
wire ptr_empty1 = ptr_reg == 1;
wire ptr_full = ptr_reg == DEPTH;
wire ptr_full1 = ptr_reg == DEPTH-1;
reg shift;
reg inc;
reg dec;
integer i;
initial begin
for (i = 0; i < DEPTH; i = i + 1) begin
data_reg[i] <= 0;
end
end
always @* begin
shift = 1'b0;
inc = 1'b0;
dec = 1'b0;
full_next = full_reg;
empty_next = empty_reg;
if (m_axis_tready && s_axis_tvalid && s_axis_tready) begin
shift = 1'b1;
inc = ptr_empty;
empty_next = 1'b0;
end else if (m_axis_tready && m_axis_tvalid) begin
dec = 1'b1;
full_next = 1'b0;
empty_next = ptr_empty1;
end else if (s_axis_tvalid && s_axis_tready) begin
shift = 1'b1;
inc = 1'b1;
full_next = ptr_full1;
empty_next = 1'b0;
end
end
always @(posedge clk) begin
if (rst) begin
ptr_reg <= 0;
full_reg <= 1'b0;
empty_reg <= 1'b1;
end else begin
if (inc) begin
ptr_reg <= ptr_reg + 1;
end else if (dec) begin
ptr_reg <= ptr_reg - 1;
end else begin
ptr_reg <= ptr_reg;
end
full_reg <= full_next;
empty_reg <= empty_next;
end
if (shift) begin
data_reg[0] <= s_axis;
for (i = 0; i < DEPTH-1; i = i + 1) begin
data_reg[i+1] <= data_reg[i];
end
end
end
endmodule
/*
Copyright (c) 2014-2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream SRL-based FIFO register
*/
module axis_srl_register #
(
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 8,
// Propagate tkeep signal
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Propagate tlast signal
parameter LAST_ENABLE = 1,
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// Propagate tdest signal
parameter DEST_ENABLE = 0,
// tdest signal width
parameter DEST_WIDTH = 8,
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1
)
(
input wire clk,
input wire rst,
/*
* AXI input
*/
input wire [DATA_WIDTH-1:0] s_axis_tdata,
input wire [KEEP_WIDTH-1:0] s_axis_tkeep,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire [ID_WIDTH-1:0] s_axis_tid,
input wire [DEST_WIDTH-1:0] s_axis_tdest,
input wire [USER_WIDTH-1:0] s_axis_tuser,
/*
* AXI output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [ID_WIDTH-1:0] m_axis_tid,
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser
);
localparam KEEP_OFFSET = DATA_WIDTH;
localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0);
localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0);
localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0);
localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0);
localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0);
reg [WIDTH-1:0] data_reg[1:0];
reg valid_reg[1:0];
reg ptr_reg = 0;
reg full_reg = 0;
wire [WIDTH-1:0] s_axis;
wire [WIDTH-1:0] m_axis = data_reg[ptr_reg];
assign s_axis_tready = !full_reg;
generate
assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata;
if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep;
if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast;
if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid;
if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest;
if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser;
endgenerate
assign m_axis_tvalid = valid_reg[ptr_reg];
assign m_axis_tdata = m_axis[DATA_WIDTH-1:0];
assign m_axis_tkeep = KEEP_ENABLE ? m_axis[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}};
assign m_axis_tlast = LAST_ENABLE ? m_axis[LAST_OFFSET] : 1'b1;
assign m_axis_tid = ID_ENABLE ? m_axis[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}};
integer i;
initial begin
for (i = 0; i < 2; i = i + 1) begin
data_reg[i] <= 0;
valid_reg[i] <= 0;
end
end
always @(posedge clk) begin
if (rst) begin
ptr_reg <= 0;
full_reg <= 0;
end else begin
// transfer empty to full
full_reg <= !m_axis_tready && m_axis_tvalid;
// transfer in if not full
if (s_axis_tready) begin
data_reg[0] <= s_axis;
valid_reg[0] <= s_axis_tvalid;
for (i = 0; i < 1; i = i + 1) begin
data_reg[i+1] <= data_reg[i];
valid_reg[i+1] <= valid_reg[i];
end
ptr_reg <= valid_reg[0];
end
if (m_axis_tready) begin
ptr_reg <= 0;
end
end
end
endmodule
/*
Copyright (c) 2014-2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream statistics counter
*/
module axis_stat_counter #
(
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 64,
// Propagate tkeep signal
// If disabled, tkeep assumed to be 1'b1
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Prepend data with tag
parameter TAG_ENABLE = 1,
// Tag field width
parameter TAG_WIDTH = 16,
// Count cycles
parameter TICK_COUNT_ENABLE = 1,
// Cycle counter width
parameter TICK_COUNT_WIDTH = 32,
// Count bytes
parameter BYTE_COUNT_ENABLE = 1,
// Byte counter width
parameter BYTE_COUNT_WIDTH = 32,
// Count frames
parameter FRAME_COUNT_ENABLE = 1,
// Frame counter width
parameter FRAME_COUNT_WIDTH = 32
)
(
input wire clk,
input wire rst,
/*
* AXI monitor
*/
input wire [KEEP_WIDTH-1:0] monitor_axis_tkeep,
input wire monitor_axis_tvalid,
input wire monitor_axis_tready,
input wire monitor_axis_tlast,
/*
* AXI status data output
*/
output wire [7:0] m_axis_tdata,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire m_axis_tuser,
/*
* Configuration
*/
input wire [TAG_WIDTH-1:0] tag,
input wire trigger,
/*
* Status
*/
output wire busy
);
localparam TAG_BYTE_WIDTH = (TAG_WIDTH + 7) / 8;
localparam TICK_COUNT_BYTE_WIDTH = (TICK_COUNT_WIDTH + 7) / 8;
localparam BYTE_COUNT_BYTE_WIDTH = (BYTE_COUNT_WIDTH + 7) / 8;
localparam FRAME_COUNT_BYTE_WIDTH = (FRAME_COUNT_WIDTH + 7) / 8;
localparam TOTAL_LENGTH = TAG_BYTE_WIDTH + TICK_COUNT_BYTE_WIDTH + BYTE_COUNT_BYTE_WIDTH + FRAME_COUNT_BYTE_WIDTH;
// state register
localparam [1:0]
STATE_IDLE = 2'd0,
STATE_OUTPUT_DATA = 2'd1;
reg [1:0] state_reg = STATE_IDLE, state_next;
reg [TICK_COUNT_WIDTH-1:0] tick_count_reg = 0, tick_count_next;
reg [BYTE_COUNT_WIDTH-1:0] byte_count_reg = 0, byte_count_next;
reg [FRAME_COUNT_WIDTH-1:0] frame_count_reg = 0, frame_count_next;
reg frame_reg = 1'b0, frame_next;
reg store_output;
reg [$clog2(TOTAL_LENGTH)-1:0] frame_ptr_reg = 0, frame_ptr_next;
reg [TICK_COUNT_WIDTH-1:0] tick_count_output_reg = 0;
reg [BYTE_COUNT_WIDTH-1:0] byte_count_output_reg = 0;
reg [FRAME_COUNT_WIDTH-1:0] frame_count_output_reg = 0;
reg busy_reg = 1'b0;
// internal datapath
reg [7:0] m_axis_tdata_int;
reg m_axis_tvalid_int;
reg m_axis_tready_int_reg = 1'b0;
reg m_axis_tlast_int;
reg m_axis_tuser_int;
wire m_axis_tready_int_early;
assign busy = busy_reg;
integer offset, i, bit_cnt;
always @* begin
state_next = STATE_IDLE;
tick_count_next = tick_count_reg;
byte_count_next = byte_count_reg;
frame_count_next = frame_count_reg;
frame_next = frame_reg;
m_axis_tdata_int = 8'd0;
m_axis_tvalid_int = 1'b0;
m_axis_tlast_int = 1'b0;
m_axis_tuser_int = 1'b0;
store_output = 1'b0;
frame_ptr_next = frame_ptr_reg;
// data readout
case (state_reg)
STATE_IDLE: begin
if (trigger) begin
store_output = 1'b1;
tick_count_next = 0;
byte_count_next = 0;
frame_count_next = 0;
frame_ptr_next = 0;
if (m_axis_tready_int_reg) begin
frame_ptr_next = 1;
if (TAG_ENABLE) begin
m_axis_tdata_int = tag[(TAG_BYTE_WIDTH-1)*8 +: 8];
end else if (TICK_COUNT_ENABLE) begin
m_axis_tdata_int = tick_count_reg[(TICK_COUNT_BYTE_WIDTH-1)*8 +: 8];
end else if (BYTE_COUNT_ENABLE) begin
m_axis_tdata_int = byte_count_reg[(BYTE_COUNT_BYTE_WIDTH-1)*8 +: 8];
end else if (FRAME_COUNT_ENABLE) begin
m_axis_tdata_int = frame_count_reg[(FRAME_COUNT_BYTE_WIDTH-1)*8 +: 8];
end
m_axis_tvalid_int = 1'b1;
end
state_next = STATE_OUTPUT_DATA;
end else begin
state_next = STATE_IDLE;
end
end
STATE_OUTPUT_DATA: begin
if (m_axis_tready_int_reg) begin
state_next = STATE_OUTPUT_DATA;
frame_ptr_next = frame_ptr_reg + 1;
m_axis_tvalid_int = 1'b1;
offset = 0;
if (TAG_ENABLE) begin
for (i = TAG_BYTE_WIDTH-1; i >= 0; i = i - 1) begin
if (frame_ptr_reg == offset) begin
m_axis_tdata_int = tag[i*8 +: 8];
end
offset = offset + 1;
end
end
if (TICK_COUNT_ENABLE) begin
for (i = TICK_COUNT_BYTE_WIDTH-1; i >= 0; i = i - 1) begin
if (frame_ptr_reg == offset) begin
m_axis_tdata_int = tick_count_output_reg[i*8 +: 8];
end
offset = offset + 1;
end
end
if (BYTE_COUNT_ENABLE) begin
for (i = BYTE_COUNT_BYTE_WIDTH-1; i >= 0; i = i - 1) begin
if (frame_ptr_reg == offset) begin
m_axis_tdata_int = byte_count_output_reg[i*8 +: 8];
end
offset = offset + 1;
end
end
if (FRAME_COUNT_ENABLE) begin
for (i = FRAME_COUNT_BYTE_WIDTH-1; i >= 0; i = i - 1) begin
if (frame_ptr_reg == offset) begin
m_axis_tdata_int = frame_count_output_reg[i*8 +: 8];
end
offset = offset + 1;
end
end
if (frame_ptr_reg == offset-1) begin
m_axis_tlast_int = 1'b1;
state_next = STATE_IDLE;
end
end else begin
state_next = STATE_OUTPUT_DATA;
end
end
endcase
// stats collection
// increment tick count by number of words that can be transferred per cycle
tick_count_next = tick_count_next + (KEEP_ENABLE ? KEEP_WIDTH : 1);
if (monitor_axis_tready && monitor_axis_tvalid) begin
// valid transfer cycle
// increment byte count by number of words transferred
if (KEEP_ENABLE) begin
bit_cnt = 0;
for (i = 0; i <= KEEP_WIDTH; i = i + 1) begin
//bit_cnt = bit_cnt + monitor_axis_tkeep[i];
if (monitor_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) bit_cnt = i;
end
byte_count_next = byte_count_next + bit_cnt;
end else begin
byte_count_next = byte_count_next + 1;
end
// count frames
if (monitor_axis_tlast) begin
// end of frame
frame_next = 1'b0;
end else if (!frame_reg) begin
// first word after end of frame
frame_count_next = frame_count_next + 1;
frame_next = 1'b1;
end
end
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
tick_count_reg <= 0;
byte_count_reg <= 0;
frame_count_reg <= 0;
frame_reg <= 1'b0;
frame_ptr_reg <= 0;
busy_reg <= 1'b0;
end else begin
state_reg <= state_next;
tick_count_reg <= tick_count_next;
byte_count_reg <= byte_count_next;
frame_count_reg <= frame_count_next;
frame_reg <= frame_next;
frame_ptr_reg <= frame_ptr_next;
busy_reg <= state_next != STATE_IDLE;
end
if (store_output) begin
tick_count_output_reg <= tick_count_reg;
byte_count_output_reg <= byte_count_reg;
frame_count_output_reg <= frame_count_reg;
end
end
// output datapath logic
reg [7:0] m_axis_tdata_reg = 8'd0;
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
reg m_axis_tlast_reg = 1'b0;
reg m_axis_tuser_reg = 1'b0;
reg [7:0] temp_m_axis_tdata_reg = 8'd0;
reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next;
reg temp_m_axis_tlast_reg = 1'b0;
reg temp_m_axis_tuser_reg = 1'b0;
// datapath control
reg store_axis_int_to_output;
reg store_axis_int_to_temp;
reg store_axis_temp_to_output;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = m_axis_tlast_reg;
assign m_axis_tuser = m_axis_tuser_reg;
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_axis_tvalid_next = m_axis_tvalid_reg;
temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg;
store_axis_int_to_output = 1'b0;
store_axis_int_to_temp = 1'b0;
store_axis_temp_to_output = 1'b0;
if (m_axis_tready_int_reg) begin
// input is ready
if (m_axis_tready || !m_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_temp = 1'b1;
end
end else if (m_axis_tready) begin
// input is not ready, but output is ready
m_axis_tvalid_next = temp_m_axis_tvalid_reg;
temp_m_axis_tvalid_next = 1'b0;
store_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_axis_tvalid_reg <= 1'b0;
m_axis_tready_int_reg <= 1'b0;
temp_m_axis_tvalid_reg <= 1'b0;
end else begin
m_axis_tvalid_reg <= m_axis_tvalid_next;
m_axis_tready_int_reg <= m_axis_tready_int_early;
temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next;
end
// datapath
if (store_axis_int_to_output) begin
m_axis_tdata_reg <= m_axis_tdata_int;
m_axis_tlast_reg <= m_axis_tlast_int;
m_axis_tuser_reg <= m_axis_tuser_int;
end else if (store_axis_temp_to_output) begin
m_axis_tdata_reg <= temp_m_axis_tdata_reg;
m_axis_tlast_reg <= temp_m_axis_tlast_reg;
m_axis_tuser_reg <= temp_m_axis_tuser_reg;
end
if (store_axis_int_to_temp) begin
temp_m_axis_tdata_reg <= m_axis_tdata_int;
temp_m_axis_tlast_reg <= m_axis_tlast_int;
temp_m_axis_tuser_reg <= m_axis_tuser_int;
end
end
endmodule
/*
Copyright (c) 2016-2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream switch
*/
module axis_switch #
(
// Number of AXI stream inputs
parameter S_COUNT = 4,
// Number of AXI stream outputs
parameter M_COUNT = 4,
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 8,
// Propagate tkeep signal
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// tdest signal width
// must be wide enough to uniquely address outputs
parameter DEST_WIDTH = $clog2(M_COUNT),
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1,
// Output interface routing base tdest selection
// Concatenate M_COUNT DEST_WIDTH sized constants
// Port selected if M_BASE <= tdest <= M_TOP
// set to zero for default routing with tdest MSBs as port index
parameter M_BASE = 0,
// Output interface routing top tdest selection
// Concatenate M_COUNT DEST_WIDTH sized constants
// Port selected if M_BASE <= tdest <= M_TOP
// set to zero to inherit from M_BASE
parameter M_TOP = 0,
// Interface connection control
// M_COUNT concatenated fields of S_COUNT bits
parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}},
// Input interface register type
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
parameter S_REG_TYPE = 0,
// Output interface register type
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
parameter M_REG_TYPE = 2,
// arbitration type: "PRIORITY" or "ROUND_ROBIN"
parameter ARB_TYPE = "ROUND_ROBIN",
// LSB priority: "LOW", "HIGH"
parameter LSB_PRIORITY = "HIGH"
)
(
input wire clk,
input wire rst,
/*
* AXI Stream inputs
*/
input wire [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata,
input wire [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep,
input wire [S_COUNT-1:0] s_axis_tvalid,
output wire [S_COUNT-1:0] s_axis_tready,
input wire [S_COUNT-1:0] s_axis_tlast,
input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid,
input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest,
input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser,
/*
* AXI Stream outputs
*/
output wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata,
output wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep,
output wire [M_COUNT-1:0] m_axis_tvalid,
input wire [M_COUNT-1:0] m_axis_tready,
output wire [M_COUNT-1:0] m_axis_tlast,
output wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid,
output wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest,
output wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser
);
parameter CL_S_COUNT = $clog2(S_COUNT);
parameter CL_M_COUNT = $clog2(M_COUNT);
integer i, j;
// check configuration
initial begin
if (DEST_WIDTH < CL_M_COUNT) begin
$error("Error: DEST_WIDTH too small for port count (instance %m)");
$finish;
end
if (M_BASE == 0) begin
// M_BASE is zero, route with tdest as port index
end else if (M_TOP == 0) begin
// M_TOP is zero, assume equal to M_BASE
for (i = 0; i < M_COUNT; i = i + 1) begin
for (j = i+1; j < M_COUNT; j = j + 1) begin
if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] == M_BASE[j*DEST_WIDTH +: DEST_WIDTH]) begin
$display("%d: %08x", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH]);
$display("%d: %08x", j, M_BASE[j*DEST_WIDTH +: DEST_WIDTH]);
$error("Error: ranges overlap (instance %m)");
$finish;
end
end
end
end else begin
for (i = 0; i < M_COUNT; i = i + 1) begin
if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] > M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin
$error("Error: invalid range (instance %m)");
$finish;
end
end
for (i = 0; i < M_COUNT; i = i + 1) begin
for (j = i+1; j < M_COUNT; j = j + 1) begin
if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[j*DEST_WIDTH +: DEST_WIDTH] && M_BASE[j*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin
$display("%d: %08x-%08x", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH], M_TOP[i*DEST_WIDTH +: DEST_WIDTH]);
$display("%d: %08x-%08x", j, M_BASE[j*DEST_WIDTH +: DEST_WIDTH], M_TOP[j*DEST_WIDTH +: DEST_WIDTH]);
$error("Error: ranges overlap (instance %m)");
$finish;
end
end
end
end
end
wire [S_COUNT*DATA_WIDTH-1:0] int_s_axis_tdata;
wire [S_COUNT*KEEP_WIDTH-1:0] int_s_axis_tkeep;
wire [S_COUNT-1:0] int_s_axis_tvalid;
wire [S_COUNT-1:0] int_s_axis_tready;
wire [S_COUNT-1:0] int_s_axis_tlast;
wire [S_COUNT*ID_WIDTH-1:0] int_s_axis_tid;
wire [S_COUNT*DEST_WIDTH-1:0] int_s_axis_tdest;
wire [S_COUNT*USER_WIDTH-1:0] int_s_axis_tuser;
wire [S_COUNT*M_COUNT-1:0] int_axis_tvalid;
wire [M_COUNT*S_COUNT-1:0] int_axis_tready;
generate
genvar m, n;
for (m = 0; m < S_COUNT; m = m + 1) begin : s_ifaces
// decoding
reg [CL_M_COUNT-1:0] select_reg = 0, select_next;
reg drop_reg = 1'b0, drop_next;
reg select_valid_reg = 1'b0, select_valid_next;
integer k;
always @* begin
select_next = select_reg;
drop_next = drop_reg && !(int_s_axis_tvalid[m] && int_s_axis_tready[m] && int_s_axis_tlast[m]);
select_valid_next = select_valid_reg && !(int_s_axis_tvalid[m] && int_s_axis_tready[m] && int_s_axis_tlast[m]);
if (int_s_axis_tvalid[m] && !select_valid_reg) begin
select_next = 1'b0;
select_valid_next = 1'b0;
drop_next = 1'b1;
for (k = 0; k < M_COUNT; k = k + 1) begin
if (M_BASE == 0) begin
// M_BASE is zero, route with $clog2(M_COUNT) MSBs of tdest as port index
if (int_s_axis_tdest[m*DEST_WIDTH+(DEST_WIDTH-CL_M_COUNT) +: CL_M_COUNT] == k && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin
select_next = k;
select_valid_next = 1'b1;
drop_next = 1'b0;
end
end else if (M_TOP == 0) begin
// M_TOP is zero, assume equal to M_BASE
if (int_s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH] == M_BASE[k*DEST_WIDTH +: DEST_WIDTH] && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin
select_next = k;
select_valid_next = 1'b1;
drop_next = 1'b0;
end
end else begin
if (int_s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH] >= M_BASE[k*DEST_WIDTH +: DEST_WIDTH] && int_s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[k*DEST_WIDTH +: DEST_WIDTH] && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin
select_next = k;
select_valid_next = 1'b1;
drop_next = 1'b0;
end
end
end
end
end
always @(posedge clk) begin
if (rst) begin
select_valid_reg <= 1'b0;
end else begin
select_valid_reg <= select_valid_next;
end
select_reg <= select_next;
drop_reg <= drop_next;
end
// forwarding
assign int_axis_tvalid[m*M_COUNT +: M_COUNT] = (int_s_axis_tvalid[m] && select_valid_reg && !drop_reg) << select_reg;
assign int_s_axis_tready[m] = int_axis_tready[select_reg*S_COUNT+m] || drop_reg;
// S side register
axis_register #(
.DATA_WIDTH(DATA_WIDTH),
.KEEP_ENABLE(KEEP_ENABLE),
.KEEP_WIDTH(KEEP_WIDTH),
.LAST_ENABLE(1),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(1),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH),
.REG_TYPE(S_REG_TYPE)
)
reg_inst (
.clk(clk),
.rst(rst),
// AXI input
.s_axis_tdata(s_axis_tdata[m*DATA_WIDTH +: DATA_WIDTH]),
.s_axis_tkeep(s_axis_tkeep[m*KEEP_WIDTH +: KEEP_WIDTH]),
.s_axis_tvalid(s_axis_tvalid[m]),
.s_axis_tready(s_axis_tready[m]),
.s_axis_tlast(s_axis_tlast[m]),
.s_axis_tid(s_axis_tid[m*ID_WIDTH +: ID_WIDTH]),
.s_axis_tdest(s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH]),
.s_axis_tuser(s_axis_tuser[m*USER_WIDTH +: USER_WIDTH]),
// AXI output
.m_axis_tdata(int_s_axis_tdata[m*DATA_WIDTH +: DATA_WIDTH]),
.m_axis_tkeep(int_s_axis_tkeep[m*KEEP_WIDTH +: KEEP_WIDTH]),
.m_axis_tvalid(int_s_axis_tvalid[m]),
.m_axis_tready(int_s_axis_tready[m]),
.m_axis_tlast(int_s_axis_tlast[m]),
.m_axis_tid(int_s_axis_tid[m*ID_WIDTH +: ID_WIDTH]),
.m_axis_tdest(int_s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH]),
.m_axis_tuser(int_s_axis_tuser[m*USER_WIDTH +: USER_WIDTH])
);
end // s_ifaces
for (n = 0; n < M_COUNT; n = n + 1) begin : m_ifaces
// arbitration
wire [S_COUNT-1:0] request;
wire [S_COUNT-1:0] acknowledge;
wire [S_COUNT-1:0] grant;
wire grant_valid;
wire [CL_S_COUNT-1:0] grant_encoded;
arbiter #(
.PORTS(S_COUNT),
.TYPE(ARB_TYPE),
.BLOCK("ACKNOWLEDGE"),
.LSB_PRIORITY(LSB_PRIORITY)
)
arb_inst (
.clk(clk),
.rst(rst),
.request(request),
.acknowledge(acknowledge),
.grant(grant),
.grant_valid(grant_valid),
.grant_encoded(grant_encoded)
);
// mux
wire [DATA_WIDTH-1:0] s_axis_tdata_mux = int_s_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH];
wire [KEEP_WIDTH-1:0] s_axis_tkeep_mux = int_s_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH];
wire s_axis_tvalid_mux = int_axis_tvalid[grant_encoded*M_COUNT+n] && grant_valid;
wire s_axis_tready_mux;
wire s_axis_tlast_mux = int_s_axis_tlast[grant_encoded];
wire [ID_WIDTH-1:0] s_axis_tid_mux = int_s_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH];
wire [DEST_WIDTH-1:0] s_axis_tdest_mux = int_s_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH];
wire [USER_WIDTH-1:0] s_axis_tuser_mux = int_s_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH];
assign int_axis_tready[n*S_COUNT +: S_COUNT] = (grant_valid && s_axis_tready_mux) << grant_encoded;
for (m = 0; m < S_COUNT; m = m + 1) begin
assign request[m] = int_axis_tvalid[m*M_COUNT+n] && !grant[m];
assign acknowledge[m] = grant[m] && int_axis_tvalid[m*M_COUNT+n] && s_axis_tlast_mux && s_axis_tready_mux;
end
// M side register
axis_register #(
.DATA_WIDTH(DATA_WIDTH),
.KEEP_ENABLE(KEEP_ENABLE),
.KEEP_WIDTH(KEEP_WIDTH),
.LAST_ENABLE(1),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(1),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH),
.REG_TYPE(M_REG_TYPE)
)
reg_inst (
.clk(clk),
.rst(rst),
// AXI input
.s_axis_tdata(s_axis_tdata_mux),
.s_axis_tkeep(s_axis_tkeep_mux),
.s_axis_tvalid(s_axis_tvalid_mux),
.s_axis_tready(s_axis_tready_mux),
.s_axis_tlast(s_axis_tlast_mux),
.s_axis_tid(s_axis_tid_mux),
.s_axis_tdest(s_axis_tdest_mux),
.s_axis_tuser(s_axis_tuser_mux),
// AXI output
.m_axis_tdata(m_axis_tdata[n*DATA_WIDTH +: DATA_WIDTH]),
.m_axis_tkeep(m_axis_tkeep[n*KEEP_WIDTH +: KEEP_WIDTH]),
.m_axis_tvalid(m_axis_tvalid[n]),
.m_axis_tready(m_axis_tready[n]),
.m_axis_tlast(m_axis_tlast[n]),
.m_axis_tid(m_axis_tid[n*ID_WIDTH +: ID_WIDTH]),
.m_axis_tdest(m_axis_tdest[n*DEST_WIDTH +: DEST_WIDTH]),
.m_axis_tuser(m_axis_tuser[n*USER_WIDTH +: USER_WIDTH])
);
end // m_ifaces
endgenerate
endmodule
#!/usr/bin/env python
"""
Generates an AXI Stream switch wrapper with the specified number of ports
"""
from __future__ import print_function
import argparse
import math
from jinja2 import Template
def main():
parser = argparse.ArgumentParser(description=__doc__.strip())
parser.add_argument('-p', '--ports', type=int, default=[4], nargs='+', help="number of ports")
parser.add_argument('-n', '--name', type=str, help="module name")
parser.add_argument('-o', '--output', type=str, help="output file name")
args = parser.parse_args()
try:
generate(**args.__dict__)
except IOError as ex:
print(ex)
exit(1)
def generate(ports=4, name=None, output=None):
if type(ports) is int:
m = n = ports
elif len(ports) == 1:
m = n = ports[0]
else:
m, n = ports
if name is None:
name = "axis_switch_wrap_{0}x{1}".format(m, n)
if output is None:
output = name + ".v"
print("Opening file '{0}'...".format(output))
output_file = open(output, 'w')
print("Generating {0}x{1} port AXI stream switch wrapper {2}...".format(m, n, name))
cm = int(math.ceil(math.log(m, 2)))
cn = int(math.ceil(math.log(n, 2)))
t = Template(u"""/*
Copyright (c) 2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream {{m}}x{{n}} switch (wrapper)
*/
module {{name}} #
(
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 8,
// Propagate tkeep signal
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// tdest signal width
// must be wide enough to uniquely address outputs
parameter DEST_WIDTH = {{cm}},
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1,
{%- for p in range(n) %}
// Output interface routing base tdest selection
// Port selected if M_BASE <= tdest <= M_TOP
parameter M{{'%02d'%p}}_BASE = {{p}},
// Output interface routing top tdest selection
// Port selected if M_BASE <= tdest <= M_TOP
parameter M{{'%02d'%p}}_TOP = {{p}},
// Interface connection control
parameter M{{'%02d'%p}}_CONNECT = {{m}}'b{% for p in range(m) %}1{% endfor %},
{%- endfor %}
// Input interface register type
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
parameter S_REG_TYPE = 0,
// Output interface register type
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
parameter M_REG_TYPE = 2,
// arbitration type: "PRIORITY" or "ROUND_ROBIN"
parameter ARB_TYPE = "ROUND_ROBIN",
// LSB priority: "LOW", "HIGH"
parameter LSB_PRIORITY = "HIGH"
)
(
input wire clk,
input wire rst,
/*
* AXI Stream inputs
*/
{%- for p in range(m) %}
input wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axis_tdata,
input wire [KEEP_WIDTH-1:0] s{{'%02d'%p}}_axis_tkeep,
input wire s{{'%02d'%p}}_axis_tvalid,
output wire s{{'%02d'%p}}_axis_tready,
input wire s{{'%02d'%p}}_axis_tlast,
input wire [ID_WIDTH-1:0] s{{'%02d'%p}}_axis_tid,
input wire [DEST_WIDTH-1:0] s{{'%02d'%p}}_axis_tdest,
input wire [USER_WIDTH-1:0] s{{'%02d'%p}}_axis_tuser,
{% endfor %}
/*
* AXI Stream outputs
*/
{%- for p in range(n) %}
output wire [DATA_WIDTH-1:0] m{{'%02d'%p}}_axis_tdata,
output wire [KEEP_WIDTH-1:0] m{{'%02d'%p}}_axis_tkeep,
output wire m{{'%02d'%p}}_axis_tvalid,
input wire m{{'%02d'%p}}_axis_tready,
output wire m{{'%02d'%p}}_axis_tlast,
output wire [ID_WIDTH-1:0] m{{'%02d'%p}}_axis_tid,
output wire [DEST_WIDTH-1:0] m{{'%02d'%p}}_axis_tdest,
output wire [USER_WIDTH-1:0] m{{'%02d'%p}}_axis_tuser{% if not loop.last %},{% endif %}
{% endfor -%}
);
// parameter sizing helpers
function [31:0] w_32(input [31:0] val);
w_32 = val;
endfunction
function [S_COUNT-1:0] w_s(input [S_COUNT-1:0] val);
w_s = val;
endfunction
axis_switch #(
.S_COUNT({{m}}),
.M_COUNT({{n}}),
.DATA_WIDTH(DATA_WIDTH),
.KEEP_ENABLE(KEEP_ENABLE),
.KEEP_WIDTH(KEEP_WIDTH),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH),
.M_BASE({ {% for p in range(n-1,-1,-1) %}w_32(M{{'%02d'%p}}_BASE){% if not loop.last %}, {% endif %}{% endfor %} }),
.M_TOP({ {% for p in range(n-1,-1,-1) %}w_32(M{{'%02d'%p}}_TOP){% if not loop.last %}, {% endif %}{% endfor %} }),
.M_CONNECT({ {% for p in range(n-1,-1,-1) %}w_s(M{{'%02d'%p}}_CONNECT){% if not loop.last %}, {% endif %}{% endfor %} }),
.S_REG_TYPE(S_REG_TYPE),
.M_REG_TYPE(M_REG_TYPE),
.ARB_TYPE(ARB_TYPE),
.LSB_PRIORITY(LSB_PRIORITY)
)
axis_switch_inst (
.clk(clk),
.rst(rst),
// AXI inputs
.s_axis_tdata({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tkeep({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tvalid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tready({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tlast({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tdest({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }),
.s_axis_tuser({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }),
// AXI outputs
.m_axis_tdata({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }),
.m_axis_tkeep({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }),
.m_axis_tvalid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }),
.m_axis_tready({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }),
.m_axis_tlast({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }),
.m_axis_tid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }),
.m_axis_tdest({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }),
.m_axis_tuser({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} })
);
endmodule
""")
output_file.write(t.render(
m=m,
n=n,
cm=cm,
cn=cn,
name=name
))
print("Done")
if __name__ == "__main__":
main()
/*
Copyright (c) 2015-2018 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
`timescale 1ns / 1ps
/*
* AXI4-Stream tap
*/
module axis_tap #
(
// Width of AXI stream interfaces in bits
parameter DATA_WIDTH = 8,
// Propagate tkeep signal
parameter KEEP_ENABLE = (DATA_WIDTH>8),
// tkeep signal width (words per cycle)
parameter KEEP_WIDTH = (DATA_WIDTH/8),
// Propagate tid signal
parameter ID_ENABLE = 0,
// tid signal width
parameter ID_WIDTH = 8,
// Propagate tdest signal
parameter DEST_ENABLE = 0,
// tdest signal width
parameter DEST_WIDTH = 8,
// Propagate tuser signal
parameter USER_ENABLE = 1,
// tuser signal width
parameter USER_WIDTH = 1,
// tuser value for bad frame marker
parameter USER_BAD_FRAME_VALUE = 1'b1,
// tuser mask for bad frame marker
parameter USER_BAD_FRAME_MASK = 1'b1
)
(
input wire clk,
input wire rst,
/*
* AXI tap
*/
input wire [DATA_WIDTH-1:0] tap_axis_tdata,
input wire [KEEP_WIDTH-1:0] tap_axis_tkeep,
input wire tap_axis_tvalid,
input wire tap_axis_tready,
input wire tap_axis_tlast,
input wire [ID_WIDTH-1:0] tap_axis_tid,
input wire [DEST_WIDTH-1:0] tap_axis_tdest,
input wire [USER_WIDTH-1:0] tap_axis_tuser,
/*
* AXI output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [ID_WIDTH-1:0] m_axis_tid,
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser
);
// datapath control signals
reg store_last_word;
reg [ID_WIDTH-1:0] last_word_id_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] last_word_dest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] last_word_user_reg = {USER_WIDTH{1'b0}};
// internal datapath
reg [DATA_WIDTH-1:0] m_axis_tdata_int;
reg [KEEP_WIDTH-1:0] m_axis_tkeep_int;
reg m_axis_tvalid_int;
reg m_axis_tready_int_reg = 1'b0;
reg m_axis_tlast_int;
reg [ID_WIDTH-1:0] m_axis_tid_int;
reg [DEST_WIDTH-1:0] m_axis_tdest_int;
reg [USER_WIDTH-1:0] m_axis_tuser_int;
wire m_axis_tready_int_early;
localparam [1:0]
STATE_IDLE = 2'd0,
STATE_TRANSFER = 2'd1,
STATE_TRUNCATE = 2'd2,
STATE_WAIT = 2'd3;
reg [1:0] state_reg = STATE_IDLE, state_next;
reg frame_reg = 1'b0, frame_next;
always @* begin
state_next = STATE_IDLE;
store_last_word = 1'b0;
frame_next = frame_reg;
m_axis_tdata_int = {DATA_WIDTH{1'b0}};
m_axis_tkeep_int = {KEEP_WIDTH{1'b0}};
m_axis_tvalid_int = 1'b0;
m_axis_tlast_int = 1'b0;
m_axis_tid_int = {ID_WIDTH{1'b0}};
m_axis_tdest_int = {DEST_WIDTH{1'b0}};
m_axis_tuser_int = {USER_WIDTH{1'b0}};
if (tap_axis_tready && tap_axis_tvalid) begin
frame_next = !tap_axis_tlast;
end
case (state_reg)
STATE_IDLE: begin
if (tap_axis_tready && tap_axis_tvalid) begin
// start of frame
if (m_axis_tready_int_reg) begin
m_axis_tdata_int = tap_axis_tdata;
m_axis_tkeep_int = tap_axis_tkeep;
m_axis_tvalid_int = tap_axis_tvalid && tap_axis_tready;
m_axis_tlast_int = tap_axis_tlast;
m_axis_tid_int = tap_axis_tid;
m_axis_tdest_int = tap_axis_tdest;
m_axis_tuser_int = tap_axis_tuser;
if (tap_axis_tlast) begin
state_next = STATE_IDLE;
end else begin
state_next = STATE_TRANSFER;
end
end else begin
state_next = STATE_WAIT;
end
end else begin
state_next = STATE_IDLE;
end
end
STATE_TRANSFER: begin
if (tap_axis_tready && tap_axis_tvalid) begin
// transfer data
if (m_axis_tready_int_reg) begin
m_axis_tdata_int = tap_axis_tdata;
m_axis_tkeep_int = tap_axis_tkeep;
m_axis_tvalid_int = tap_axis_tvalid && tap_axis_tready;
m_axis_tlast_int = tap_axis_tlast;
m_axis_tid_int = tap_axis_tid;
m_axis_tdest_int = tap_axis_tdest;
m_axis_tuser_int = tap_axis_tuser;
if (tap_axis_tlast) begin
state_next = STATE_IDLE;
end else begin
state_next = STATE_TRANSFER;
end
end else begin
store_last_word = 1'b1;
state_next = STATE_TRUNCATE;
end
end else begin
state_next = STATE_TRANSFER;
end
end
STATE_TRUNCATE: begin
if (m_axis_tready_int_reg) begin
m_axis_tdata_int = {DATA_WIDTH{1'b0}};
m_axis_tkeep_int = {{KEEP_WIDTH-1{1'b0}}, 1'b1};
m_axis_tvalid_int = 1'b1;
m_axis_tlast_int = 1'b1;
m_axis_tid_int = last_word_id_reg;
m_axis_tdest_int = last_word_dest_reg;
m_axis_tuser_int = (last_word_user_reg & ~USER_BAD_FRAME_MASK) | (USER_BAD_FRAME_VALUE & USER_BAD_FRAME_MASK);
if (frame_next) begin
state_next = STATE_WAIT;
end else begin
state_next = STATE_IDLE;
end
end else begin
state_next = STATE_TRUNCATE;
end
end
STATE_WAIT: begin
if (tap_axis_tready && tap_axis_tvalid) begin
if (tap_axis_tlast) begin
state_next = STATE_IDLE;
end else begin
state_next = STATE_WAIT;
end
end else begin
state_next = STATE_WAIT;
end
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
frame_reg <= 1'b0;
end else begin
state_reg <= state_next;
frame_reg <= frame_next;
end
if (store_last_word) begin
last_word_id_reg <= tap_axis_tid;
last_word_dest_reg <= tap_axis_tdest;
last_word_user_reg <= tap_axis_tuser;
end
end
// output datapath logic
reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
reg m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}};
reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next;
reg temp_m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}};
// datapath control
reg store_axis_int_to_output;
reg store_axis_int_to_temp;
reg store_axis_temp_to_output;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = m_axis_tlast_reg;
assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_axis_tvalid_next = m_axis_tvalid_reg;
temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg;
store_axis_int_to_output = 1'b0;
store_axis_int_to_temp = 1'b0;
store_axis_temp_to_output = 1'b0;
if (m_axis_tready_int_reg) begin
// input is ready
if (m_axis_tready || !m_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_temp = 1'b1;
end
end else if (m_axis_tready) begin
// input is not ready, but output is ready
m_axis_tvalid_next = temp_m_axis_tvalid_reg;
temp_m_axis_tvalid_next = 1'b0;
store_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_axis_tvalid_reg <= 1'b0;
m_axis_tready_int_reg <= 1'b0;
temp_m_axis_tvalid_reg <= 1'b0;
end else begin
m_axis_tvalid_reg <= m_axis_tvalid_next;
m_axis_tready_int_reg <= m_axis_tready_int_early;
temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next;
end
// datapath
if (store_axis_int_to_output) begin
m_axis_tdata_reg <= m_axis_tdata_int;
m_axis_tkeep_reg <= m_axis_tkeep_int;
m_axis_tlast_reg <= m_axis_tlast_int;
m_axis_tid_reg <= m_axis_tid_int;
m_axis_tdest_reg <= m_axis_tdest_int;
m_axis_tuser_reg <= m_axis_tuser_int;
end else if (store_axis_temp_to_output) begin
m_axis_tdata_reg <= temp_m_axis_tdata_reg;
m_axis_tkeep_reg <= temp_m_axis_tkeep_reg;
m_axis_tlast_reg <= temp_m_axis_tlast_reg;
m_axis_tid_reg <= temp_m_axis_tid_reg;
m_axis_tdest_reg <= temp_m_axis_tdest_reg;
m_axis_tuser_reg <= temp_m_axis_tuser_reg;
end
if (store_axis_int_to_temp) begin
temp_m_axis_tdata_reg <= m_axis_tdata_int;
temp_m_axis_tkeep_reg <= m_axis_tkeep_int;
temp_m_axis_tlast_reg <= m_axis_tlast_int;
temp_m_axis_tid_reg <= m_axis_tid_int;
temp_m_axis_tdest_reg <= m_axis_tdest_int;
temp_m_axis_tuser_reg <= m_axis_tuser_int;
end
end
endmodule
/*
Copyright (c) 2014-2018 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
`timescale 1ns / 1ps
/*
* LocalLink to AXI4-Stream bridge
*/
module ll_axis_bridge #
(
parameter DATA_WIDTH = 8
)
(
input wire clk,
input wire rst,
/*
* LocalLink input
*/
input wire [DATA_WIDTH-1:0] ll_data_in,
input wire ll_sof_in_n,
input wire ll_eof_in_n,
input wire ll_src_rdy_in_n,
output wire ll_dst_rdy_out_n,
/*
* AXI output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast
);
assign m_axis_tdata = ll_data_in;
assign m_axis_tvalid = !ll_src_rdy_in_n;
assign m_axis_tlast = !ll_eof_in_n;
assign ll_dst_rdy_out_n = !m_axis_tready;
endmodule
/*
Copyright (c) 2014-2018 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
`timescale 1ns / 1ps
/*
* Priority encoder module
*/
module priority_encoder #
(
parameter WIDTH = 4,
// LSB priority: "LOW", "HIGH"
parameter LSB_PRIORITY = "LOW"
)
(
input wire [WIDTH-1:0] input_unencoded,
output wire output_valid,
output wire [$clog2(WIDTH)-1:0] output_encoded,
output wire [WIDTH-1:0] output_unencoded
);
// power-of-two width
parameter W1 = 2**$clog2(WIDTH);
parameter W2 = W1/2;
generate
if (WIDTH == 1) begin
// one input
assign output_valid = input_unencoded;
assign output_encoded = 0;
end else if (WIDTH == 2) begin
// two inputs - just an OR gate
assign output_valid = |input_unencoded;
if (LSB_PRIORITY == "LOW") begin
assign output_encoded = input_unencoded[1];
end else begin
assign output_encoded = ~input_unencoded[0];
end
end else begin
// more than two inputs - split into two parts and recurse
// also pad input to correct power-of-two width
wire [$clog2(W2)-1:0] out1, out2;
wire valid1, valid2;
priority_encoder #(
.WIDTH(W2),
.LSB_PRIORITY(LSB_PRIORITY)
)
priority_encoder_inst1 (
.input_unencoded(input_unencoded[W2-1:0]),
.output_valid(valid1),
.output_encoded(out1)
);
priority_encoder #(
.WIDTH(W2),
.LSB_PRIORITY(LSB_PRIORITY)
)
priority_encoder_inst2 (
.input_unencoded({{W1-WIDTH{1'b0}}, input_unencoded[WIDTH-1:W2]}),
.output_valid(valid2),
.output_encoded(out2)
);
// multiplexer to select part
assign output_valid = valid1 | valid2;
if (LSB_PRIORITY == "LOW") begin
assign output_encoded = valid2 ? {1'b1, out2} : {1'b0, out1};
end else begin
assign output_encoded = valid1 ? {1'b0, out1} : {1'b1, out2};
end
end
endgenerate
// unencoded output
assign output_unencoded = 1 << output_encoded;
endmodule
/*
Copyright (c) 2014-2020 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
`timescale 1ns / 1ps
/*
* Synchronizes an active-high asynchronous reset signal to a given clock by
* using a pipeline of N registers.
*/
module sync_reset #
(
// depth of synchronizer
parameter N = 2
)
(
input wire clk,
input wire rst,
output wire out
);
(* srl_style = "register" *)
reg [N-1:0] sync_reg = {N{1'b1}};
assign out = sync_reg[N-1];
always @(posedge clk or posedge rst) begin
if (rst) begin
sync_reg <= {N{1'b1}};
end else begin
sync_reg <= {sync_reg[N-2:0], 1'b0};
end
end
endmodule
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment