From b232a6459d082dcba624fce6b442c87fce150c73 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Sat, 8 Nov 2014 12:45:36 -0800 Subject: [PATCH 1/5] Remove counter from AXI fifo modules --- rtl/axis_fifo.v | 27 +++++++++------------------ rtl/axis_fifo_64.v | 27 +++++++++------------------ 2 files changed, 18 insertions(+), 36 deletions(-) diff --git a/rtl/axis_fifo.v b/rtl/axis_fifo.v index 8004a8311..6a291ff99 100644 --- a/rtl/axis_fifo.v +++ b/rtl/axis_fifo.v @@ -57,9 +57,8 @@ module axis_fifo # output wire output_axis_tuser ); -reg [ADDR_WIDTH-1:0] wr_ptr = {ADDR_WIDTH{1'b0}}; -reg [ADDR_WIDTH-1:0] rd_ptr = {ADDR_WIDTH{1'b0}}; -reg [ADDR_WIDTH-1:0] counter = {ADDR_WIDTH{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr = {ADDR_WIDTH+1{1'b0}}; reg [DATA_WIDTH+2-1:0] data_out_reg = {1'b0, 1'b0, {DATA_WIDTH{1'b0}}}; @@ -72,8 +71,11 @@ reg output_axis_tvalid_reg = 1'b0; wire [DATA_WIDTH+2-1:0] data_in = {input_axis_tlast, input_axis_tuser, input_axis_tdata}; -wire full = (counter == (2**ADDR_WIDTH)-1); -wire empty = (counter == 0); +// full when first MSB different but rest same +wire full = ((wr_ptr[ADDR_WIDTH] != rd_ptr[ADDR_WIDTH]) && + (wr_ptr[ADDR_WIDTH-1:0] == rd_ptr[ADDR_WIDTH-1:0])); +// empty when pointers match exactly +wire empty = wr_ptr == rd_ptr; wire write = input_axis_tvalid & ~full; wire read = (output_axis_tready | ~output_axis_tvalid_reg) & ~empty; @@ -88,7 +90,7 @@ always @(posedge clk or posedge rst) begin if (rst) begin wr_ptr <= 0; end else if (write) begin - mem[wr_ptr] <= data_in; + mem[wr_ptr[ADDR_WIDTH-1:0]] <= data_in; wr_ptr <= wr_ptr + 1; end end @@ -98,22 +100,11 @@ always @(posedge clk or posedge rst) begin if (rst) begin rd_ptr <= 0; end else if (read) begin - data_out_reg <= mem[rd_ptr]; + data_out_reg <= mem[rd_ptr[ADDR_WIDTH-1:0]]; rd_ptr <= rd_ptr + 1; end end -// counter -always @(posedge clk or posedge rst) begin - if (rst) begin - counter <= 0; - end else if (~read & write) begin - counter <= counter + 1; - end else if (read & ~write) begin - counter <= counter - 1; - end -end - // source ready output always @(posedge clk or posedge rst) begin if (rst) begin diff --git a/rtl/axis_fifo_64.v b/rtl/axis_fifo_64.v index 8d02f5c0d..0accac316 100644 --- a/rtl/axis_fifo_64.v +++ b/rtl/axis_fifo_64.v @@ -60,9 +60,8 @@ module axis_fifo_64 # output wire output_axis_tuser ); -reg [ADDR_WIDTH-1:0] wr_ptr = {ADDR_WIDTH{1'b0}}; -reg [ADDR_WIDTH-1:0] rd_ptr = {ADDR_WIDTH{1'b0}}; -reg [ADDR_WIDTH-1:0] counter = {ADDR_WIDTH{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr = {ADDR_WIDTH+1{1'b0}}; reg [DATA_WIDTH+KEEP_WIDTH+2-1:0] data_out_reg = {1'b0, 1'b0, {KEEP_WIDTH{1'b0}}, {DATA_WIDTH{1'b0}}}; @@ -75,8 +74,11 @@ reg output_axis_tvalid_reg = 1'b0; wire [DATA_WIDTH+KEEP_WIDTH+2-1:0] data_in = {input_axis_tlast, input_axis_tuser, input_axis_tkeep, input_axis_tdata}; -wire full = (counter == (2**ADDR_WIDTH)-1); -wire empty = (counter == 0); +// full when first MSB different but rest same +wire full = ((wr_ptr[ADDR_WIDTH] != rd_ptr[ADDR_WIDTH]) && + (wr_ptr[ADDR_WIDTH-1:0] == rd_ptr[ADDR_WIDTH-1:0])); +// empty when pointers match exactly +wire empty = wr_ptr == rd_ptr; wire write = input_axis_tvalid & ~full; wire read = (output_axis_tready | ~output_axis_tvalid_reg) & ~empty; @@ -91,7 +93,7 @@ always @(posedge clk or posedge rst) begin if (rst) begin wr_ptr <= 0; end else if (write) begin - mem[wr_ptr] <= data_in; + mem[wr_ptr[ADDR_WIDTH-1:0]] <= data_in; wr_ptr <= wr_ptr + 1; end end @@ -101,22 +103,11 @@ always @(posedge clk or posedge rst) begin if (rst) begin rd_ptr <= 0; end else if (read) begin - data_out_reg <= mem[rd_ptr]; + data_out_reg <= mem[rd_ptr[ADDR_WIDTH-1:0]]; rd_ptr <= rd_ptr + 1; end end -// counter -always @(posedge clk or posedge rst) begin - if (rst) begin - counter <= 0; - end else if (~read & write) begin - counter <= counter + 1; - end else if (read & ~write) begin - counter <= counter - 1; - end -end - // source ready output always @(posedge clk or posedge rst) begin if (rst) begin From 6fa46b6c57f19dacf5e856d5e147b816a7aebcd0 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Sat, 8 Nov 2014 21:07:47 -0800 Subject: [PATCH 2/5] Add AXI frame fifo and testbench --- rtl/axis_frame_fifo.v | 138 ++++++++++++ rtl/axis_frame_fifo_64.v | 141 ++++++++++++ tb/test_axis_frame_fifo.py | 401 ++++++++++++++++++++++++++++++++ tb/test_axis_frame_fifo.v | 88 ++++++++ tb/test_axis_frame_fifo_64.py | 414 ++++++++++++++++++++++++++++++++++ tb/test_axis_frame_fifo_64.v | 94 ++++++++ 6 files changed, 1276 insertions(+) create mode 100644 rtl/axis_frame_fifo.v create mode 100644 rtl/axis_frame_fifo_64.v create mode 100755 tb/test_axis_frame_fifo.py create mode 100644 tb/test_axis_frame_fifo.v create mode 100755 tb/test_axis_frame_fifo_64.py create mode 100644 tb/test_axis_frame_fifo_64.v diff --git a/rtl/axis_frame_fifo.v b/rtl/axis_frame_fifo.v new file mode 100644 index 000000000..d037adfe4 --- /dev/null +++ b/rtl/axis_frame_fifo.v @@ -0,0 +1,138 @@ +/* + +Copyright (c) 2014 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 FIFO + */ +module axis_frame_fifo # +( + parameter ADDR_WIDTH = 12, + parameter DATA_WIDTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] input_axis_tdata, + input wire input_axis_tvalid, + output wire input_axis_tready, + input wire input_axis_tlast, + input wire input_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] output_axis_tdata, + output wire output_axis_tvalid, + input wire output_axis_tready, + output wire output_axis_tlast +); + +reg [ADDR_WIDTH:0] wr_ptr = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_cur = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr = {ADDR_WIDTH+1{1'b0}}; + +reg [DATA_WIDTH+2-1:0] data_out_reg = {1'b0, {DATA_WIDTH{1'b0}}}; + +//(* RAM_STYLE="BLOCK" *) +reg [DATA_WIDTH+2-1:0] mem[(2**ADDR_WIDTH)-1:0]; + +reg output_read = 1'b0; + +reg output_axis_tvalid_reg = 1'b0; + +wire [DATA_WIDTH+2-1:0] data_in = {input_axis_tlast, input_axis_tdata}; + +// full when first MSB different but rest same +wire full = ((wr_ptr[ADDR_WIDTH] != rd_ptr[ADDR_WIDTH]) && + (wr_ptr[ADDR_WIDTH-1:0] == rd_ptr[ADDR_WIDTH-1:0])); +// empty when pointers match exactly +wire empty = wr_ptr == rd_ptr; +// overflow in single packet +wire full_cur = ((wr_ptr[ADDR_WIDTH] != wr_ptr_cur[ADDR_WIDTH]) && + (wr_ptr[ADDR_WIDTH-1:0] == wr_ptr_cur[ADDR_WIDTH-1:0])); + +wire write = input_axis_tvalid & ~full; +wire read = (output_axis_tready | ~output_axis_tvalid_reg) & ~empty; + +assign {output_axis_tlast, output_axis_tdata} = data_out_reg; + +assign input_axis_tready = ~full; +assign output_axis_tvalid = output_axis_tvalid_reg; + +// write +always @(posedge clk or posedge rst) begin + if (rst) begin + wr_ptr <= 0; + end else if (write) begin + if (full_cur) begin + // buffer full, hold current pointer, drop packet at end + if (input_axis_tlast) begin + wr_ptr_cur <= wr_ptr; + end + end else begin + mem[wr_ptr_cur[ADDR_WIDTH-1:0]] <= data_in; + wr_ptr_cur <= wr_ptr_cur + 1; + if (input_axis_tlast) begin + if (input_axis_tuser) begin + // bad packet, reset write pointer + wr_ptr_cur <= wr_ptr; + end else begin + // good packet, push new write pointer + wr_ptr <= wr_ptr_cur + 1; + end + end + end + end +end + +// read +always @(posedge clk or posedge rst) begin + if (rst) begin + rd_ptr <= 0; + end else if (read) begin + data_out_reg <= mem[rd_ptr[ADDR_WIDTH-1:0]]; + rd_ptr <= rd_ptr + 1; + end +end + +// source ready output +always @(posedge clk or posedge rst) begin + if (rst) begin + output_axis_tvalid_reg <= 1'b0; + end else if (output_axis_tready | ~output_axis_tvalid_reg) begin + output_axis_tvalid_reg <= ~empty; + end else begin + output_axis_tvalid_reg <= output_axis_tvalid_reg; + end +end + +endmodule diff --git a/rtl/axis_frame_fifo_64.v b/rtl/axis_frame_fifo_64.v new file mode 100644 index 000000000..f55dcf01a --- /dev/null +++ b/rtl/axis_frame_fifo_64.v @@ -0,0 +1,141 @@ +/* + +Copyright (c) 2014 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 FIFO (64 bit datapath) + */ +module axis_frame_fifo_64 # +( + parameter ADDR_WIDTH = 12, + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] input_axis_tdata, + input wire [KEEP_WIDTH-1:0] input_axis_tkeep, + input wire input_axis_tvalid, + output wire input_axis_tready, + input wire input_axis_tlast, + input wire input_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] output_axis_tdata, + output wire [KEEP_WIDTH-1:0] output_axis_tkeep, + output wire output_axis_tvalid, + input wire output_axis_tready, + output wire output_axis_tlast +); + +reg [ADDR_WIDTH:0] wr_ptr = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_cur = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr = {ADDR_WIDTH+1{1'b0}}; + +reg [DATA_WIDTH+KEEP_WIDTH+2-1:0] data_out_reg = {1'b0, {KEEP_WIDTH{1'b0}}, {DATA_WIDTH{1'b0}}}; + +//(* RAM_STYLE="BLOCK" *) +reg [DATA_WIDTH+KEEP_WIDTH+2-1:0] mem[(2**ADDR_WIDTH)-1:0]; + +reg output_read = 1'b0; + +reg output_axis_tvalid_reg = 1'b0; + +wire [DATA_WIDTH+KEEP_WIDTH+2-1:0] data_in = {input_axis_tlast, input_axis_tkeep, input_axis_tdata}; + +// full when first MSB different but rest same +wire full = ((wr_ptr[ADDR_WIDTH] != rd_ptr[ADDR_WIDTH]) && + (wr_ptr[ADDR_WIDTH-1:0] == rd_ptr[ADDR_WIDTH-1:0])); +// empty when pointers match exactly +wire empty = wr_ptr == rd_ptr; +// overflow in single packet +wire full_cur = ((wr_ptr[ADDR_WIDTH] != wr_ptr_cur[ADDR_WIDTH]) && + (wr_ptr[ADDR_WIDTH-1:0] == wr_ptr_cur[ADDR_WIDTH-1:0])); + +wire write = input_axis_tvalid & ~full; +wire read = (output_axis_tready | ~output_axis_tvalid_reg) & ~empty; + +assign {output_axis_tlast, output_axis_tkeep, output_axis_tdata} = data_out_reg; + +assign input_axis_tready = ~full; +assign output_axis_tvalid = output_axis_tvalid_reg; + +// write +always @(posedge clk or posedge rst) begin + if (rst) begin + wr_ptr <= 0; + end else if (write) begin + if (full_cur) begin + // buffer full, hold current pointer, drop packet at end + if (input_axis_tlast) begin + wr_ptr_cur <= wr_ptr; + end + end else begin + mem[wr_ptr_cur[ADDR_WIDTH-1:0]] <= data_in; + wr_ptr_cur <= wr_ptr_cur + 1; + if (input_axis_tlast) begin + if (input_axis_tuser) begin + // bad packet, reset write pointer + wr_ptr_cur <= wr_ptr; + end else begin + // good packet, push new write pointer + wr_ptr <= wr_ptr_cur + 1; + end + end + end + end +end + +// read +always @(posedge clk or posedge rst) begin + if (rst) begin + rd_ptr <= 0; + end else if (read) begin + data_out_reg <= mem[rd_ptr[ADDR_WIDTH-1:0]]; + rd_ptr <= rd_ptr + 1; + end +end + +// source ready output +always @(posedge clk or posedge rst) begin + if (rst) begin + output_axis_tvalid_reg <= 1'b0; + end else if (output_axis_tready | ~output_axis_tvalid_reg) begin + output_axis_tvalid_reg <= ~empty; + end else begin + output_axis_tvalid_reg <= output_axis_tvalid_reg; + end +end + +endmodule diff --git a/tb/test_axis_frame_fifo.py b/tb/test_axis_frame_fifo.py new file mode 100755 index 000000000..76a28dd08 --- /dev/null +++ b/tb/test_axis_frame_fifo.py @@ -0,0 +1,401 @@ +#!/usr/bin/env python2 +""" + +Copyright (c) 2014 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. + +""" + +from myhdl import * +import os +from Queue import Queue + +import axis_ep + +module = 'axis_frame_fifo' + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("test_%s.v" % module) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o test_%s.vvp %s" % (module, src) + +def dut_axis_frame_fifo(clk, + rst, + current_test, + + input_axis_tdata, + input_axis_tvalid, + input_axis_tready, + input_axis_tlast, + input_axis_tuser, + + output_axis_tdata, + output_axis_tvalid, + output_axis_tready, + output_axis_tlast): + + if os.system(build_cmd): + raise Exception("Error running build command") + return Cosimulation("vvp -m myhdl test_%s.vvp -lxt2" % module, + clk=clk, + rst=rst, + current_test=current_test, + + input_axis_tdata=input_axis_tdata, + input_axis_tvalid=input_axis_tvalid, + input_axis_tready=input_axis_tready, + input_axis_tlast=input_axis_tlast, + input_axis_tuser=input_axis_tuser, + + output_axis_tdata=output_axis_tdata, + output_axis_tvalid=output_axis_tvalid, + output_axis_tready=output_axis_tready, + output_axis_tlast=output_axis_tlast) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + input_axis_tdata = Signal(intbv(0)[8:]) + input_axis_tvalid = Signal(bool(0)) + input_axis_tlast = Signal(bool(0)) + input_axis_tuser = Signal(bool(0)) + output_axis_tready = Signal(bool(0)) + + # Outputs + input_axis_tready = Signal(bool(0)) + output_axis_tdata = Signal(intbv(0)[8:]) + output_axis_tvalid = Signal(bool(0)) + output_axis_tlast = Signal(bool(0)) + + # sources and sinks + source_queue = Queue() + source_pause = Signal(bool(0)) + sink_queue = Queue() + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_axis_tdata, + tvalid=input_axis_tvalid, + tready=input_axis_tready, + tlast=input_axis_tlast, + tuser=input_axis_tuser, + fifo=source_queue, + pause=source_pause, + name='source') + + sink = axis_ep.AXIStreamSink(clk, + rst, + tdata=output_axis_tdata, + tvalid=output_axis_tvalid, + tready=output_axis_tready, + tlast=output_axis_tlast, + fifo=sink_queue, + pause=sink_pause, + name='sink') + + # DUT + dut = dut_axis_frame_fifo(clk, + rst, + current_test, + + input_axis_tdata, + input_axis_tvalid, + input_axis_tready, + input_axis_tlast, + input_axis_tuser, + + output_axis_tdata, + output_axis_tvalid, + output_axis_tready, + output_axis_tlast) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame) + yield clk.posedge + + yield output_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + bytearray(range(256))) + source_queue.put(test_frame) + yield clk.posedge + + yield output_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield output_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame2 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame1) + source_queue.put(test_frame2) + yield clk.posedge + + yield output_axis_tlast.posedge + yield clk.posedge + yield output_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame1 + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame2 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame1) + source_queue.put(test_frame2) + yield clk.posedge + + while input_axis_tvalid or output_axis_tvalid: + source_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame1 + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame2 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame1) + source_queue.put(test_frame2) + yield clk.posedge + + while input_axis_tvalid or output_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame1 + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame.user = 1 + source_queue.put(test_frame) + yield clk.posedge + + yield delay(1000) + + assert sink_queue.empty() + + yield delay(100) + + yield clk.posedge + print("test 8: single packet overflow") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + bytearray(range(256))*2) + source_queue.put(test_frame) + yield clk.posedge + + yield delay(10000) + + assert sink_queue.empty() + + yield delay(100) + + raise StopSimulation + + return dut, source, sink, clkgen, check + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/tb/test_axis_frame_fifo.v b/tb/test_axis_frame_fifo.v new file mode 100644 index 000000000..69ae48514 --- /dev/null +++ b/tb/test_axis_frame_fifo.v @@ -0,0 +1,88 @@ +/* + +Copyright (c) 2014 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 1 ns / 1 ps + +module test_axis_frame_fifo; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [7:0] input_axis_tdata = 0; +reg input_axis_tvalid = 0; +reg input_axis_tlast = 0; +reg input_axis_tuser = 0; +reg output_axis_tready = 0; + +// Outputs +wire input_axis_tready; +wire [7:0] output_axis_tdata; +wire output_axis_tvalid; +wire output_axis_tlast; + +initial begin + // myhdl integration + $from_myhdl(clk, + rst, + current_test, + input_axis_tdata, + input_axis_tvalid, + input_axis_tlast, + input_axis_tuser, + output_axis_tready); + $to_myhdl(input_axis_tready, + output_axis_tdata, + output_axis_tvalid, + output_axis_tlast); + + // dump file + $dumpfile("test_axis_frame_fifo.lxt"); + $dumpvars(0, test_axis_frame_fifo); +end + +axis_frame_fifo #( + .ADDR_WIDTH(9), + .DATA_WIDTH(8) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .input_axis_tdata(input_axis_tdata), + .input_axis_tvalid(input_axis_tvalid), + .input_axis_tready(input_axis_tready), + .input_axis_tlast(input_axis_tlast), + .input_axis_tuser(input_axis_tuser), + // AXI output + .output_axis_tdata(output_axis_tdata), + .output_axis_tvalid(output_axis_tvalid), + .output_axis_tready(output_axis_tready), + .output_axis_tlast(output_axis_tlast) +); + +endmodule diff --git a/tb/test_axis_frame_fifo_64.py b/tb/test_axis_frame_fifo_64.py new file mode 100755 index 000000000..ce6f47cda --- /dev/null +++ b/tb/test_axis_frame_fifo_64.py @@ -0,0 +1,414 @@ +#!/usr/bin/env python2 +""" + +Copyright (c) 2014 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. + +""" + +from myhdl import * +import os +from Queue import Queue + +import axis_ep + +module = 'axis_frame_fifo_64' + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("test_%s.v" % module) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o test_%s.vvp %s" % (module, src) + +def dut_axis_frame_fifo_64(clk, + rst, + current_test, + + input_axis_tdata, + input_axis_tkeep, + input_axis_tvalid, + input_axis_tready, + input_axis_tlast, + input_axis_tuser, + + output_axis_tdata, + output_axis_tkeep, + output_axis_tvalid, + output_axis_tready, + output_axis_tlast, + output_axis_tuser): + + if os.system(build_cmd): + raise Exception("Error running build command") + return Cosimulation("vvp -m myhdl test_%s.vvp -lxt2" % module, + clk=clk, + rst=rst, + current_test=current_test, + + input_axis_tdata=input_axis_tdata, + input_axis_tkeep=input_axis_tkeep, + input_axis_tvalid=input_axis_tvalid, + input_axis_tready=input_axis_tready, + input_axis_tlast=input_axis_tlast, + input_axis_tuser=input_axis_tuser, + + output_axis_tdata=output_axis_tdata, + output_axis_tkeep=output_axis_tkeep, + output_axis_tvalid=output_axis_tvalid, + output_axis_tready=output_axis_tready, + output_axis_tlast=output_axis_tlast, + output_axis_tuser=output_axis_tuser) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + input_axis_tdata = Signal(intbv(0)[64:]) + input_axis_tkeep = Signal(intbv(0)[8:]) + input_axis_tvalid = Signal(bool(0)) + input_axis_tlast = Signal(bool(0)) + input_axis_tuser = Signal(bool(0)) + output_axis_tready = Signal(bool(0)) + + # Outputs + input_axis_tready = Signal(bool(0)) + output_axis_tdata = Signal(intbv(0)[64:]) + output_axis_tkeep = Signal(intbv(0)[8:]) + output_axis_tvalid = Signal(bool(0)) + output_axis_tlast = Signal(bool(0)) + output_axis_tuser = Signal(bool(0)) + + # sources and sinks + source_queue = Queue() + source_pause = Signal(bool(0)) + sink_queue = Queue() + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_axis_tdata, + tkeep=input_axis_tkeep, + tvalid=input_axis_tvalid, + tready=input_axis_tready, + tlast=input_axis_tlast, + tuser=input_axis_tuser, + fifo=source_queue, + pause=source_pause, + name='source') + + sink = axis_ep.AXIStreamSink(clk, + rst, + tdata=output_axis_tdata, + tkeep=output_axis_tkeep, + tvalid=output_axis_tvalid, + tready=output_axis_tready, + tlast=output_axis_tlast, + tuser=output_axis_tuser, + fifo=sink_queue, + pause=sink_pause, + name='sink') + + # DUT + dut = dut_axis_frame_fifo_64(clk, + rst, + current_test, + + input_axis_tdata, + input_axis_tkeep, + input_axis_tvalid, + input_axis_tready, + input_axis_tlast, + input_axis_tuser, + + output_axis_tdata, + output_axis_tkeep, + output_axis_tvalid, + output_axis_tready, + output_axis_tlast, + output_axis_tuser) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame) + yield clk.posedge + + yield output_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + bytearray(range(256))) + source_queue.put(test_frame) + yield clk.posedge + + yield output_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + bytearray(range(256))) + source_queue.put(test_frame) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_pause.next = True + yield delay(32) + yield clk.posedge + source_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield output_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame2 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame1) + source_queue.put(test_frame2) + yield clk.posedge + + yield output_axis_tlast.posedge + yield clk.posedge + yield output_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame1 + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame2 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame1) + source_queue.put(test_frame2) + yield clk.posedge + + while input_axis_tvalid or output_axis_tvalid: + source_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame1 + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame2 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame1) + source_queue.put(test_frame2) + yield clk.posedge + + while input_axis_tvalid or output_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame1 + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame.user = 1 + source_queue.put(test_frame) + yield clk.posedge + + yield delay(1000) + + assert sink_queue.empty() + + yield clk.posedge + print("test 8: single packet overflow") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + bytearray(range(256))*2) + source_queue.put(test_frame) + yield clk.posedge + + yield delay(10000) + + assert sink_queue.empty() + + yield delay(100) + + raise StopSimulation + + return dut, source, sink, clkgen, check + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/tb/test_axis_frame_fifo_64.v b/tb/test_axis_frame_fifo_64.v new file mode 100644 index 000000000..c7048e3fd --- /dev/null +++ b/tb/test_axis_frame_fifo_64.v @@ -0,0 +1,94 @@ +/* + +Copyright (c) 2014 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 1 ns / 1 ps + +module test_axis_frame_fifo_64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [63:0] input_axis_tdata = 0; +reg [7:0] input_axis_tkeep = 0; +reg input_axis_tvalid = 0; +reg input_axis_tlast = 0; +reg input_axis_tuser = 0; +reg output_axis_tready = 0; + +// Outputs +wire input_axis_tready; +wire [63:0] output_axis_tdata; +wire [7:0] output_axis_tkeep; +wire output_axis_tvalid; +wire output_axis_tlast; + +initial begin + // myhdl integration + $from_myhdl(clk, + rst, + current_test, + input_axis_tdata, + input_axis_tkeep, + input_axis_tvalid, + input_axis_tlast, + input_axis_tuser, + output_axis_tready); + $to_myhdl(input_axis_tready, + output_axis_tdata, + output_axis_tkeep, + output_axis_tvalid, + output_axis_tlast); + + // dump file + $dumpfile("test_axis_frame_fifo_64.lxt"); + $dumpvars(0, test_axis_frame_fifo_64); +end + +axis_frame_fifo_64 #( + .ADDR_WIDTH(6), + .DATA_WIDTH(64) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI input + .input_axis_tdata(input_axis_tdata), + .input_axis_tkeep(input_axis_tkeep), + .input_axis_tvalid(input_axis_tvalid), + .input_axis_tready(input_axis_tready), + .input_axis_tlast(input_axis_tlast), + .input_axis_tuser(input_axis_tuser), + // AXI output + .output_axis_tdata(output_axis_tdata), + .output_axis_tkeep(output_axis_tkeep), + .output_axis_tvalid(output_axis_tvalid), + .output_axis_tready(output_axis_tready), + .output_axis_tlast(output_axis_tlast) +); + +endmodule From 10e0d7d1bb0d6d8bc631f3817b7934b7cfa61fb9 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Sat, 8 Nov 2014 21:29:39 -0800 Subject: [PATCH 3/5] Add AXI async frame fifo and testbench --- rtl/axis_async_frame_fifo.v | 168 +++++++++++ rtl/axis_async_frame_fifo_64.v | 171 +++++++++++ tb/test_axis_async_frame_fifo.py | 421 +++++++++++++++++++++++++++ tb/test_axis_async_frame_fifo.v | 94 ++++++ tb/test_axis_async_frame_fifo_64.py | 431 ++++++++++++++++++++++++++++ tb/test_axis_async_frame_fifo_64.v | 100 +++++++ 6 files changed, 1385 insertions(+) create mode 100644 rtl/axis_async_frame_fifo.v create mode 100644 rtl/axis_async_frame_fifo_64.v create mode 100755 tb/test_axis_async_frame_fifo.py create mode 100644 tb/test_axis_async_frame_fifo.v create mode 100755 tb/test_axis_async_frame_fifo_64.py create mode 100644 tb/test_axis_async_frame_fifo_64.v diff --git a/rtl/axis_async_frame_fifo.v b/rtl/axis_async_frame_fifo.v new file mode 100644 index 000000000..16f2e17bc --- /dev/null +++ b/rtl/axis_async_frame_fifo.v @@ -0,0 +1,168 @@ +/* + +Copyright (c) 2014 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 asynchronous FIFO + */ +module axis_async_frame_fifo # +( + parameter ADDR_WIDTH = 12, + parameter DATA_WIDTH = 8 +) +( + /* + * AXI input + */ + input wire input_clk, + input wire input_rst, + input wire [DATA_WIDTH-1:0] input_axis_tdata, + input wire input_axis_tvalid, + output wire input_axis_tready, + input wire input_axis_tlast, + input wire input_axis_tuser, + + /* + * AXI output + */ + input wire output_clk, + input wire output_rst, + output wire [DATA_WIDTH-1:0] output_axis_tdata, + output wire output_axis_tvalid, + input wire output_axis_tready, + output wire output_axis_tlast +); + +reg [ADDR_WIDTH:0] wr_ptr = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next; +reg [ADDR_WIDTH:0] wr_ptr_cur = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_gray = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next; +reg [ADDR_WIDTH:0] rd_ptr_gray = {ADDR_WIDTH+1{1'b0}}; + +reg [ADDR_WIDTH:0] wr_ptr_gray_sync1 = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_gray_sync2 = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_gray_sync3 = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_gray_sync1 = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_gray_sync2 = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_gray_sync3 = {ADDR_WIDTH+1{1'b0}}; + +reg [DATA_WIDTH+2-1:0] data_out_reg = {1'b0, {DATA_WIDTH{1'b0}}}; + +//(* RAM_STYLE="BLOCK" *) +reg [DATA_WIDTH+2-1:0] mem[(2**ADDR_WIDTH)-1:0]; + +reg output_read = 1'b0; + +reg output_axis_tvalid_reg = 1'b0; + +wire [DATA_WIDTH+2-1:0] data_in = {input_axis_tlast, input_axis_tdata}; + +// full when first TWO MSBs do NOT match, but rest matches +// (gray code equivalent of first MSB different but rest same) +wire full = ((wr_ptr_gray[ADDR_WIDTH] != rd_ptr_gray_sync3[ADDR_WIDTH]) && + (wr_ptr_gray[ADDR_WIDTH-1] != rd_ptr_gray_sync3[ADDR_WIDTH-1]) && + (wr_ptr_gray[ADDR_WIDTH-2:0] == rd_ptr_gray_sync3[ADDR_WIDTH-2:0])); +// empty when pointers match exactly +wire empty = rd_ptr_gray == wr_ptr_gray_sync3; +// overflow in single packet +wire full_cur = ((wr_ptr[ADDR_WIDTH] != wr_ptr_cur[ADDR_WIDTH]) && + (wr_ptr[ADDR_WIDTH-1:0] == wr_ptr_cur[ADDR_WIDTH-1:0])); + +wire write = input_axis_tvalid & ~full; +wire read = (output_axis_tready | ~output_axis_tvalid_reg) & ~empty; + +assign {output_axis_tlast, output_axis_tdata} = data_out_reg; + +assign input_axis_tready = ~full; +assign output_axis_tvalid = output_axis_tvalid_reg; + +// write +always @(posedge input_clk or posedge input_rst) begin + if (input_rst) begin + wr_ptr <= 0; + end else if (write) begin + if (full_cur) begin + // buffer full, hold current pointer, drop packet at end + if (input_axis_tlast) begin + wr_ptr_cur <= wr_ptr; + end + end else begin + mem[wr_ptr_cur[ADDR_WIDTH-1:0]] <= data_in; + wr_ptr_cur <= wr_ptr_cur + 1; + if (input_axis_tlast) begin + if (input_axis_tuser) begin + // bad packet, reset write pointer + wr_ptr_cur <= wr_ptr; + end else begin + // good packet, push new write pointer + wr_ptr_next = wr_ptr_cur + 1; + wr_ptr <= wr_ptr_next; + wr_ptr_gray <= wr_ptr_next ^ (wr_ptr_next >> 1); + end + end + end + end +end + +// pointer synchronization in SRL16 +always @(posedge input_clk) begin + rd_ptr_gray_sync1 <= rd_ptr_gray; + rd_ptr_gray_sync2 <= rd_ptr_gray_sync1; + rd_ptr_gray_sync3 <= rd_ptr_gray_sync2; +end + +// read +always @(posedge output_clk or posedge output_rst) begin + if (output_rst) begin + rd_ptr <= 0; + end else if (read) begin + data_out_reg <= mem[rd_ptr[ADDR_WIDTH-1:0]]; + rd_ptr_next = rd_ptr + 1; + rd_ptr <= rd_ptr_next; + rd_ptr_gray <= rd_ptr_next ^ (rd_ptr_next >> 1); + end +end + +// pointer synchronization in SRL16 +always @(posedge output_clk) begin + wr_ptr_gray_sync1 <= wr_ptr_gray; + wr_ptr_gray_sync2 <= wr_ptr_gray_sync1; + wr_ptr_gray_sync3 <= wr_ptr_gray_sync2; +end + +// source ready output +always @(posedge output_clk or posedge output_rst) begin + if (output_rst) begin + output_axis_tvalid_reg <= 1'b0; + end else if (output_axis_tready | ~output_axis_tvalid_reg) begin + output_axis_tvalid_reg <= ~empty; + end else begin + output_axis_tvalid_reg <= output_axis_tvalid_reg; + end +end + +endmodule diff --git a/rtl/axis_async_frame_fifo_64.v b/rtl/axis_async_frame_fifo_64.v new file mode 100644 index 000000000..3da51f5ef --- /dev/null +++ b/rtl/axis_async_frame_fifo_64.v @@ -0,0 +1,171 @@ +/* + +Copyright (c) 2014 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 asynchronous FIFO (64 bit datapath) + */ +module axis_async_frame_fifo_64 # +( + parameter ADDR_WIDTH = 12, + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8) +) +( + /* + * AXI input + */ + input wire input_clk, + input wire input_rst, + input wire [DATA_WIDTH-1:0] input_axis_tdata, + input wire [KEEP_WIDTH-1:0] input_axis_tkeep, + input wire input_axis_tvalid, + output wire input_axis_tready, + input wire input_axis_tlast, + input wire input_axis_tuser, + + /* + * AXI output + */ + input wire output_clk, + input wire output_rst, + output wire [DATA_WIDTH-1:0] output_axis_tdata, + output wire [KEEP_WIDTH-1:0] output_axis_tkeep, + output wire output_axis_tvalid, + input wire output_axis_tready, + output wire output_axis_tlast +); + +reg [ADDR_WIDTH:0] wr_ptr = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next; +reg [ADDR_WIDTH:0] wr_ptr_cur = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_gray = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next; +reg [ADDR_WIDTH:0] rd_ptr_gray = {ADDR_WIDTH+1{1'b0}}; + +reg [ADDR_WIDTH:0] wr_ptr_gray_sync1 = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_gray_sync2 = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_gray_sync3 = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_gray_sync1 = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_gray_sync2 = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_gray_sync3 = {ADDR_WIDTH+1{1'b0}}; + +reg [DATA_WIDTH+KEEP_WIDTH+2-1:0] data_out_reg = {1'b0, {KEEP_WIDTH{1'b0}}, {DATA_WIDTH{1'b0}}}; + +//(* RAM_STYLE="BLOCK" *) +reg [DATA_WIDTH+KEEP_WIDTH+2-1:0] mem[(2**ADDR_WIDTH)-1:0]; + +reg output_read = 1'b0; + +reg output_axis_tvalid_reg = 1'b0; + +wire [DATA_WIDTH+KEEP_WIDTH+2-1:0] data_in = {input_axis_tlast, input_axis_tkeep, input_axis_tdata}; + +// full when first TWO MSBs do NOT match, but rest matches +// (gray code equivalent of first MSB different but rest same) +wire full = ((wr_ptr_gray[ADDR_WIDTH] != rd_ptr_gray_sync3[ADDR_WIDTH]) && + (wr_ptr_gray[ADDR_WIDTH-1] != rd_ptr_gray_sync3[ADDR_WIDTH-1]) && + (wr_ptr_gray[ADDR_WIDTH-2:0] == rd_ptr_gray_sync3[ADDR_WIDTH-2:0])); +// empty when pointers match exactly +wire empty = rd_ptr_gray == wr_ptr_gray_sync3; +// overflow in single packet +wire full_cur = ((wr_ptr[ADDR_WIDTH] != wr_ptr_cur[ADDR_WIDTH]) && + (wr_ptr[ADDR_WIDTH-1:0] == wr_ptr_cur[ADDR_WIDTH-1:0])); + +wire write = input_axis_tvalid & ~full; +wire read = (output_axis_tready | ~output_axis_tvalid_reg) & ~empty; + +assign {output_axis_tlast, output_axis_tkeep, output_axis_tdata} = data_out_reg; + +assign input_axis_tready = ~full; +assign output_axis_tvalid = output_axis_tvalid_reg; + +// write +always @(posedge input_clk or posedge input_rst) begin + if (input_rst) begin + wr_ptr <= 0; + end else if (write) begin + if (full_cur) begin + // buffer full, hold current pointer, drop packet at end + if (input_axis_tlast) begin + wr_ptr_cur <= wr_ptr; + end + end else begin + mem[wr_ptr_cur[ADDR_WIDTH-1:0]] <= data_in; + wr_ptr_cur <= wr_ptr_cur + 1; + if (input_axis_tlast) begin + if (input_axis_tuser) begin + // bad packet, reset write pointer + wr_ptr_cur <= wr_ptr; + end else begin + // good packet, push new write pointer + wr_ptr_next = wr_ptr_cur + 1; + wr_ptr <= wr_ptr_next; + wr_ptr_gray <= wr_ptr_next ^ (wr_ptr_next >> 1); + end + end + end + end +end + +// pointer synchronization in SRL16 +always @(posedge input_clk) begin + rd_ptr_gray_sync1 <= rd_ptr_gray; + rd_ptr_gray_sync2 <= rd_ptr_gray_sync1; + rd_ptr_gray_sync3 <= rd_ptr_gray_sync2; +end + +// read +always @(posedge output_clk or posedge output_rst) begin + if (output_rst) begin + rd_ptr <= 0; + end else if (read) begin + data_out_reg <= mem[rd_ptr[ADDR_WIDTH-1:0]]; + rd_ptr_next = rd_ptr + 1; + rd_ptr <= rd_ptr_next; + rd_ptr_gray <= rd_ptr_next ^ (rd_ptr_next >> 1); + end +end + +// pointer synchronization in SRL16 +always @(posedge output_clk) begin + wr_ptr_gray_sync1 <= wr_ptr_gray; + wr_ptr_gray_sync2 <= wr_ptr_gray_sync1; + wr_ptr_gray_sync3 <= wr_ptr_gray_sync2; +end + +// source ready output +always @(posedge output_clk or posedge output_rst) begin + if (output_rst) begin + output_axis_tvalid_reg <= 1'b0; + end else if (output_axis_tready | ~output_axis_tvalid_reg) begin + output_axis_tvalid_reg <= ~empty; + end else begin + output_axis_tvalid_reg <= output_axis_tvalid_reg; + end +end + +endmodule diff --git a/tb/test_axis_async_frame_fifo.py b/tb/test_axis_async_frame_fifo.py new file mode 100755 index 000000000..e7bd55da5 --- /dev/null +++ b/tb/test_axis_async_frame_fifo.py @@ -0,0 +1,421 @@ +#!/usr/bin/env python2 +""" + +Copyright (c) 2014 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. + +""" + +from myhdl import * +import os +from Queue import Queue + +import axis_ep + +module = 'axis_async_frame_fifo' + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("test_%s.v" % module) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o test_%s.vvp %s" % (module, src) + +def dut_axis_async_frame_fifo(input_clk, + input_rst, + output_clk, + output_rst, + current_test, + + input_axis_tdata, + input_axis_tvalid, + input_axis_tready, + input_axis_tlast, + input_axis_tuser, + + output_axis_tdata, + output_axis_tvalid, + output_axis_tready, + output_axis_tlast): + + if os.system(build_cmd): + raise Exception("Error running build command") + return Cosimulation("vvp -m myhdl test_%s.vvp -lxt2" % module, + input_clk=input_clk, + input_rst=input_rst, + output_clk=output_clk, + output_rst=output_rst, + current_test=current_test, + + input_axis_tdata=input_axis_tdata, + input_axis_tvalid=input_axis_tvalid, + input_axis_tready=input_axis_tready, + input_axis_tlast=input_axis_tlast, + input_axis_tuser=input_axis_tuser, + + output_axis_tdata=output_axis_tdata, + output_axis_tvalid=output_axis_tvalid, + output_axis_tready=output_axis_tready, + output_axis_tlast=output_axis_tlast) + +def bench(): + + # Inputs + input_clk = Signal(bool(0)) + input_rst = Signal(bool(0)) + output_clk = Signal(bool(0)) + output_rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + input_axis_tdata = Signal(intbv(0)[8:]) + input_axis_tvalid = Signal(bool(0)) + input_axis_tlast = Signal(bool(0)) + input_axis_tuser = Signal(bool(0)) + output_axis_tready = Signal(bool(0)) + + # Outputs + input_axis_tready = Signal(bool(0)) + output_axis_tdata = Signal(intbv(0)[8:]) + output_axis_tvalid = Signal(bool(0)) + output_axis_tlast = Signal(bool(0)) + + # sources and sinks + source_queue = Queue() + source_pause = Signal(bool(0)) + sink_queue = Queue() + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource(input_clk, + input_rst, + tdata=input_axis_tdata, + tvalid=input_axis_tvalid, + tready=input_axis_tready, + tlast=input_axis_tlast, + tuser=input_axis_tuser, + fifo=source_queue, + pause=source_pause, + name='source') + + sink = axis_ep.AXIStreamSink(output_clk, + output_rst, + tdata=output_axis_tdata, + tvalid=output_axis_tvalid, + tready=output_axis_tready, + tlast=output_axis_tlast, + fifo=sink_queue, + pause=sink_pause, + name='sink') + + # DUT + dut = dut_axis_async_frame_fifo(input_clk, + input_rst, + output_clk, + output_rst, + current_test, + + input_axis_tdata, + input_axis_tvalid, + input_axis_tready, + input_axis_tlast, + input_axis_tuser, + + output_axis_tdata, + output_axis_tvalid, + output_axis_tready, + output_axis_tlast) + + @always(delay(4)) + def input_clkgen(): + input_clk.next = not input_clk + + @always(delay(5)) + def output_clkgen(): + output_clk.next = not output_clk + + @instance + def check(): + yield delay(100) + yield input_clk.posedge + input_rst.next = 1 + output_rst.next = 1 + yield input_clk.posedge + yield input_clk.posedge + yield input_clk.posedge + input_rst.next = 0 + output_rst.next = 0 + yield input_clk.posedge + yield delay(100) + yield input_clk.posedge + + yield input_clk.posedge + + yield input_clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame) + yield input_clk.posedge + + yield output_axis_tlast.posedge + yield output_clk.posedge + yield output_clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame + + yield delay(100) + + yield input_clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + bytearray(range(256))) + source_queue.put(test_frame) + yield input_clk.posedge + + yield output_axis_tlast.posedge + yield output_clk.posedge + yield output_clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame + + yield input_clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame) + yield input_clk.posedge + + yield delay(64) + yield input_clk.posedge + source_pause.next = True + yield delay(32) + yield input_clk.posedge + source_pause.next = False + + yield delay(64) + yield output_clk.posedge + sink_pause.next = True + yield delay(32) + yield output_clk.posedge + sink_pause.next = False + + yield output_axis_tlast.posedge + yield output_clk.posedge + yield output_clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame + + yield delay(100) + + yield input_clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame2 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame1) + source_queue.put(test_frame2) + yield input_clk.posedge + + yield output_axis_tlast.posedge + yield output_clk.posedge + yield output_axis_tlast.posedge + yield output_clk.posedge + yield output_clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame1 + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield input_clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame2 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame1) + source_queue.put(test_frame2) + yield input_clk.posedge + + while input_axis_tvalid or output_axis_tvalid: + source_pause.next = True + yield input_clk.posedge + yield input_clk.posedge + yield input_clk.posedge + source_pause.next = False + yield input_clk.posedge + + yield output_clk.posedge + yield output_clk.posedge + if output_axis_tvalid: + yield output_axis_tlast.posedge + yield output_clk.posedge + yield output_clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame1 + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield input_clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame2 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame1) + source_queue.put(test_frame2) + yield input_clk.posedge + + while input_axis_tvalid or output_axis_tvalid: + sink_pause.next = True + yield output_clk.posedge + yield output_clk.posedge + yield output_clk.posedge + sink_pause.next = False + yield output_clk.posedge + + yield output_clk.posedge + yield output_clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame1 + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield input_clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame.user = 1 + source_queue.put(test_frame) + yield input_clk.posedge + + yield delay(1000) + + assert sink_queue.empty() + + yield delay(100) + + yield input_clk.posedge + print("test 8: single packet overflow") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + bytearray(range(256))*2) + source_queue.put(test_frame) + yield input_clk.posedge + + yield delay(10000) + + assert sink_queue.empty() + + yield delay(100) + + raise StopSimulation + + return dut, source, sink, input_clkgen, output_clkgen, check + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/tb/test_axis_async_frame_fifo.v b/tb/test_axis_async_frame_fifo.v new file mode 100644 index 000000000..916b87354 --- /dev/null +++ b/tb/test_axis_async_frame_fifo.v @@ -0,0 +1,94 @@ +/* + +Copyright (c) 2014 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 1 ns / 1 ps + +module test_axis_async_frame_fifo; + +// Inputs +reg input_clk = 0; +reg input_rst = 0; +reg output_clk = 0; +reg output_rst = 0; +reg [7:0] current_test = 0; + +reg [7:0] input_axis_tdata = 0; +reg input_axis_tvalid = 0; +reg input_axis_tlast = 0; +reg input_axis_tuser = 0; +reg output_axis_tready = 0; + +// Outputs +wire input_axis_tready; +wire [7:0] output_axis_tdata; +wire output_axis_tvalid; +wire output_axis_tlast; + +initial begin + // myhdl integration + $from_myhdl(input_clk, + input_rst, + output_clk, + output_rst, + current_test, + input_axis_tdata, + input_axis_tvalid, + input_axis_tlast, + input_axis_tuser, + output_axis_tready); + $to_myhdl(input_axis_tready, + output_axis_tdata, + output_axis_tvalid, + output_axis_tlast); + + // dump file + $dumpfile("test_axis_async_frame_fifo.lxt"); + $dumpvars(0, test_axis_async_frame_fifo); +end + +axis_async_frame_fifo #( + .ADDR_WIDTH(9), + .DATA_WIDTH(8) +) +UUT ( + // AXI input + .input_clk(input_clk), + .input_rst(input_rst), + .input_axis_tdata(input_axis_tdata), + .input_axis_tvalid(input_axis_tvalid), + .input_axis_tready(input_axis_tready), + .input_axis_tlast(input_axis_tlast), + .input_axis_tuser(input_axis_tuser), + // AXI output + .output_clk(output_clk), + .output_rst(output_rst), + .output_axis_tdata(output_axis_tdata), + .output_axis_tvalid(output_axis_tvalid), + .output_axis_tready(output_axis_tready), + .output_axis_tlast(output_axis_tlast) +); + +endmodule diff --git a/tb/test_axis_async_frame_fifo_64.py b/tb/test_axis_async_frame_fifo_64.py new file mode 100755 index 000000000..f12906653 --- /dev/null +++ b/tb/test_axis_async_frame_fifo_64.py @@ -0,0 +1,431 @@ +#!/usr/bin/env python2 +""" + +Copyright (c) 2014 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. + +""" + +from myhdl import * +import os +from Queue import Queue + +import axis_ep + +module = 'axis_async_frame_fifo_64' + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("test_%s.v" % module) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o test_%s.vvp %s" % (module, src) + +def dut_axis_async_frame_fifo_64(input_clk, + input_rst, + output_clk, + output_rst, + current_test, + + input_axis_tdata, + input_axis_tkeep, + input_axis_tvalid, + input_axis_tready, + input_axis_tlast, + input_axis_tuser, + + output_axis_tdata, + output_axis_tkeep, + output_axis_tvalid, + output_axis_tready, + output_axis_tlast): + + if os.system(build_cmd): + raise Exception("Error running build command") + return Cosimulation("vvp -m myhdl test_%s.vvp -lxt2" % module, + input_clk=input_clk, + input_rst=input_rst, + output_clk=output_clk, + output_rst=output_rst, + current_test=current_test, + + input_axis_tdata=input_axis_tdata, + input_axis_tkeep=input_axis_tkeep, + input_axis_tvalid=input_axis_tvalid, + input_axis_tready=input_axis_tready, + input_axis_tlast=input_axis_tlast, + input_axis_tuser=input_axis_tuser, + + output_axis_tdata=output_axis_tdata, + output_axis_tkeep=output_axis_tkeep, + output_axis_tvalid=output_axis_tvalid, + output_axis_tready=output_axis_tready, + output_axis_tlast=output_axis_tlast) + +def bench(): + + # Inputs + input_clk = Signal(bool(0)) + input_rst = Signal(bool(0)) + output_clk = Signal(bool(0)) + output_rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + input_axis_tdata = Signal(intbv(0)[64:]) + input_axis_tkeep = Signal(intbv(0)[8:]) + input_axis_tvalid = Signal(bool(0)) + input_axis_tlast = Signal(bool(0)) + input_axis_tuser = Signal(bool(0)) + output_axis_tready = Signal(bool(0)) + + # Outputs + input_axis_tready = Signal(bool(0)) + output_axis_tdata = Signal(intbv(0)[64:]) + output_axis_tkeep = Signal(intbv(0)[8:]) + output_axis_tvalid = Signal(bool(0)) + output_axis_tlast = Signal(bool(0)) + + # sources and sinks + source_queue = Queue() + source_pause = Signal(bool(0)) + sink_queue = Queue() + sink_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource(input_clk, + input_rst, + tdata=input_axis_tdata, + tkeep=input_axis_tkeep, + tvalid=input_axis_tvalid, + tready=input_axis_tready, + tlast=input_axis_tlast, + tuser=input_axis_tuser, + fifo=source_queue, + pause=source_pause, + name='source') + + sink = axis_ep.AXIStreamSink(output_clk, + output_rst, + tdata=output_axis_tdata, + tkeep=output_axis_tkeep, + tvalid=output_axis_tvalid, + tready=output_axis_tready, + tlast=output_axis_tlast, + fifo=sink_queue, + pause=sink_pause, + name='sink') + + # DUT + dut = dut_axis_async_frame_fifo_64(input_clk, + input_rst, + output_clk, + output_rst, + current_test, + + input_axis_tdata, + input_axis_tkeep, + input_axis_tvalid, + input_axis_tready, + input_axis_tlast, + input_axis_tuser, + + output_axis_tdata, + output_axis_tkeep, + output_axis_tvalid, + output_axis_tready, + output_axis_tlast) + + @always(delay(4)) + def input_clkgen(): + input_clk.next = not input_clk + + @always(delay(5)) + def output_clkgen(): + output_clk.next = not output_clk + + @instance + def check(): + yield delay(100) + yield input_clk.posedge + input_rst.next = 1 + output_rst.next = 1 + yield input_clk.posedge + yield input_clk.posedge + yield input_clk.posedge + input_rst.next = 0 + output_rst.next = 0 + yield input_clk.posedge + yield delay(100) + yield input_clk.posedge + + yield input_clk.posedge + + yield input_clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame) + yield input_clk.posedge + + yield output_axis_tlast.posedge + yield output_clk.posedge + yield output_clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame + + yield delay(100) + + yield input_clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + bytearray(range(256))) + source_queue.put(test_frame) + yield input_clk.posedge + + yield output_axis_tlast.posedge + yield output_clk.posedge + yield output_clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame + + yield input_clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + bytearray(range(256))) + source_queue.put(test_frame) + yield input_clk.posedge + + yield delay(64) + yield input_clk.posedge + source_pause.next = True + yield delay(32) + yield input_clk.posedge + source_pause.next = False + + yield delay(64) + yield output_clk.posedge + sink_pause.next = True + yield delay(32) + yield output_clk.posedge + sink_pause.next = False + + yield output_axis_tlast.posedge + yield output_clk.posedge + yield output_clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame + + yield delay(100) + + yield input_clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame1 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame2 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame1) + source_queue.put(test_frame2) + yield input_clk.posedge + + yield output_axis_tlast.posedge + yield output_clk.posedge + yield output_axis_tlast.posedge + yield output_clk.posedge + yield output_clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame1 + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield input_clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame1 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame2 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame1) + source_queue.put(test_frame2) + yield input_clk.posedge + + while input_axis_tvalid or output_axis_tvalid: + source_pause.next = True + yield input_clk.posedge + yield input_clk.posedge + yield input_clk.posedge + source_pause.next = False + yield input_clk.posedge + + yield output_clk.posedge + yield output_clk.posedge + if output_axis_tvalid: + yield output_axis_tlast.posedge + yield output_clk.posedge + yield output_clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame1 + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield input_clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame1 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame2 = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x02\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + source_queue.put(test_frame1) + source_queue.put(test_frame2) + yield input_clk.posedge + + while input_axis_tvalid or output_axis_tvalid: + sink_pause.next = True + yield output_clk.posedge + yield output_clk.posedge + yield output_clk.posedge + sink_pause.next = False + yield output_clk.posedge + + yield output_clk.posedge + yield output_clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame1 + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame == test_frame2 + + yield delay(100) + + yield input_clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10') + test_frame.user = 1 + source_queue.put(test_frame) + yield input_clk.posedge + + yield delay(1000) + + assert sink_queue.empty() + + yield delay(100) + + yield input_clk.posedge + print("test 8: single packet overflow") + current_test.next = 8 + + test_frame = axis_ep.AXIStreamFrame('\xDA\xD1\xD2\xD3\xD4\xD5' + + '\x5A\x51\x52\x53\x54\x55' + + '\x80\x00' + + bytearray(range(256))*2) + source_queue.put(test_frame) + yield input_clk.posedge + + yield delay(10000) + + assert sink_queue.empty() + + yield delay(100) + + raise StopSimulation + + return dut, source, sink, input_clkgen, output_clkgen, check + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/tb/test_axis_async_frame_fifo_64.v b/tb/test_axis_async_frame_fifo_64.v new file mode 100644 index 000000000..62e3ad7aa --- /dev/null +++ b/tb/test_axis_async_frame_fifo_64.v @@ -0,0 +1,100 @@ +/* + +Copyright (c) 2014 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 1 ns / 1 ps + +module test_axis_async_frame_fifo_64; + +// Inputs +reg input_clk = 0; +reg input_rst = 0; +reg output_clk = 0; +reg output_rst = 0; +reg [7:0] current_test = 0; + +reg [63:0] input_axis_tdata = 0; +reg [7:0] input_axis_tkeep = 0; +reg input_axis_tvalid = 0; +reg input_axis_tlast = 0; +reg input_axis_tuser = 0; +reg output_axis_tready = 0; + +// Outputs +wire input_axis_tready; +wire [63:0] output_axis_tdata; +wire [7:0] output_axis_tkeep; +wire output_axis_tvalid; +wire output_axis_tlast; + +initial begin + // myhdl integration + $from_myhdl(input_clk, + input_rst, + output_clk, + output_rst, + current_test, + input_axis_tdata, + input_axis_tkeep, + input_axis_tvalid, + input_axis_tlast, + input_axis_tuser, + output_axis_tready); + $to_myhdl(input_axis_tready, + output_axis_tdata, + output_axis_tkeep, + output_axis_tvalid, + output_axis_tlast); + + // dump file + $dumpfile("test_axis_async_frame_fifo_64.lxt"); + $dumpvars(0, test_axis_async_frame_fifo_64); +end + +axis_async_frame_fifo_64 #( + .ADDR_WIDTH(6), + .DATA_WIDTH(64) +) +UUT ( + // AXI input + .input_clk(input_clk), + .input_rst(input_rst), + .input_axis_tdata(input_axis_tdata), + .input_axis_tkeep(input_axis_tkeep), + .input_axis_tvalid(input_axis_tvalid), + .input_axis_tready(input_axis_tready), + .input_axis_tlast(input_axis_tlast), + .input_axis_tuser(input_axis_tuser), + // AXI output + .output_clk(output_clk), + .output_rst(output_rst), + .output_axis_tdata(output_axis_tdata), + .output_axis_tkeep(output_axis_tkeep), + .output_axis_tvalid(output_axis_tvalid), + .output_axis_tready(output_axis_tready), + .output_axis_tlast(output_axis_tlast) +); + +endmodule From 7804272b2e36a07a9ef31dd6adfa23a5435fdf2b Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Sun, 9 Nov 2014 02:13:20 -0800 Subject: [PATCH 4/5] Updated readme --- README | 3 +- README.md | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 2 deletions(-) mode change 100644 => 120000 README create mode 100644 README.md diff --git a/README b/README deleted file mode 100644 index 34d1f782c..000000000 --- a/README +++ /dev/null @@ -1,2 +0,0 @@ -Verilog AXI Stream components - diff --git a/README b/README new file mode 120000 index 000000000..42061c01a --- /dev/null +++ b/README @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..60300ac79 --- /dev/null +++ b/README.md @@ -0,0 +1,205 @@ +# Verilog AXI Stream Components Readme + +For more information and updates: http://alexforencich.com/wiki/en/verilog/axis/start + +GitHub repository: https://github.com/alexforencich/verilog-axis + +## Introduction + +Collection of AXI Stream bus components. Most components are fully +parametrizable in interface widths. Includes full MyHDL testbench with +intelligent bus cosimulation endpoints. + +## Documentation + +### axis_adapter module + +The axis_adapter module bridges AXI stream busses of differing widths. The +module is parametrizable, but there are certain restrictions. First, the bus +word widths must be identical (e.g. one 8-bit lane and eight 8-bit lanes, but +not one 16-bit lane and one 32-bit lane). Second, the bus widths must be +related by an integer multiple (e.g. 2 words and 6 words, but not 4 words +and 6 words). Wait states will be inserted on the wider bus side when +necessary. + +### axis_async_fifo module + +Basic word-based asynchronous FIFO with parametrizable data width and depth. +Supports power of two depths only. + +### axis_async_fifo_64 module + +Basic word-based asynchronous FIFO with tkeep signal and parametrizable data +width and depth. Supports power of two depths only. + +### axis_async_frame_fifo module + +Basic frame-based asynchronous FIFO with parametrizable data width and depth. +Supports power of two depths only. + +### axis_async_fifo_64 module + +Basic frame-based asynchronous FIFO with tkeep signal and parametrizable data +width and depth. Supports power of two depths only. + +### axis_fifo module + +Basic word-based synchronous FIFO with parametrizable data width and depth. +Supports power of two depths only. + +### axis_fifo_64 module + +Basic word-based synchronous FIFO with tkeep signal and parametrizable data +width and depth. Supports power of two depths only. + +### axis_frame_fifo module + +Basic frame-based synchronous FIFO with parametrizable data width and depth. +Supports power of two depths only. + +### axis_fifo_64 module + +Basic frame-based synchronous FIFO with tkeep signal and parametrizable data +width and depth. Supports power of two depths only. + +### axis_frame_join_N module + +Frame joiner with optional tag. 8 bit data path only. + +Can be generated with arbitrary port counts with axis_frame_join.py. + +### axis_ll_bringe module + +AXI stream to LocalLink bridge. + +### axis_rate_limit module + +Fractional rate limiter, supports word and frame modes. Inserts wait states +to limit data rate to specified ratio. Frame mode inserts wait states at end +of frames, word mode ignores frames and inserts wait states at any point. +Parametrizable data width. Rate and mode are configurable at run time. + +### axis_rate_limit_64 module + +Fractional rate limiter with tkeep signal, supports word and frame modes. +Inserts wait states to limit data rate to specified ratio. Frame mode inserts +wait states at end of frames, word mode ignores frames and inserts wait states +at any point. Parametrizable data width. Rate and mode are configurable at +run time. + +### axis_register module + +Datapath register. Use to improve timing for long routes. + +### axis_register_64 module + +Datapath register with tkeep signal. Use to improve timing for long routes. + +### axis_stat_counter module + +Statistics counter module. Counts bytes and frames passing through monitored +AXI stream interface. Trigger signal used to reset and dump counts out of AXI +interface, along with tag value. Use with axis_frame_join_N to form a single +monolithic frame from multiple monitored points with the same trigger. + +### ll_axis_bridge module + +LocalLink to AXI stream bridge. + +### Common signals + + tdata : Data (width generally DATA_WIDTH) + tkeep : Data word valid (width generally KEEP_WIDTH, present on _64 modules) + tvalid : Data valid + tready : Sink ready + tlast : End-of-frame + tuser : Bad frame (valid with tlast & tvalid) + +### Source Files + + rtl/axis_adapter.v : Parametrizable bus width adapter + rtl/axis_async_fifo.v : Asynchronous FIFO + rtl/axis_async_fifo_64.v : Asynchronous FIFO (64 bit) + rtl/axis_async_frame_fifo.v : Asynchronous frame FIFO + rtl/axis_async_frame_fifo_64.v : Asynchronous frame FIFO (64 bit) + rtl/axis_fifo.v : Synchronous FIFO + rtl/axis_fifo_64.v : Synchronous FIFO (64 bit) + rtl/axis_frame_fifo.v : Synchronous frame FIFO + rtl/axis_frame_fifo_64.v : Synchronous frame FIFO (64 bit) + rtl/axis_frame_join.py : Frame joiner generator + rtl/axis_frame_join_4.v : 4 port frame joiner + rtl/axis_ll_bridge.v : AXI stream to LocalLink bridge + rtl/axis_rate_limit.v : Fractional rate limiter + rtl/axis_rate_limit_64.v : Fractional rate limiter (64 bit) + rtl/axis_register.v : AXI Stream register + rtl/axis_register_64.v : AXI Stream register (64 bit) + rtl/axis_stat_counter.v : Statistics counter + rtl/ll_axis_bridge.v : LocalLink to AXI stream bridge + +### AXI Stream Interface Example + +two byte transfer with sink pause after each byte + + __ __ __ __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _________________ + tdata XXXXXXXXX_D0__X_D1______________XXXXXXXXXXXXXXXXXXXXXXXX + _____ _________________ + tkeep XXXXXXXXX_K0__X_K1______________XXXXXXXXXXXXXXXXXXXXXXXX + _______________________ + tvalid ________/ \_______________________ + ______________ _____ ___________ + tready \___________/ \___________/ + _________________ + tlast ______________/ \_______________________ + + tuser ________________________________________________________ + + +two back-to-back packets, no pauses + + __ __ __ __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _____ _____ _____ _____ _____ + tdata XXXXXXXXX_A0__X_A1__X_A2__X_B0__X_B1__X_B2__XXXXXXXXXXXX + _____ _____ _____ _____ _____ _____ + tkeep XXXXXXXXX_K0__X_K1__X_K2__X_K0__X_K1__X_K2__XXXXXXXXXXXX + ___________________________________ + tvalid ________/ \___________ + ________________________________________________________ + tready + _____ _____ + tlast ____________________/ \___________/ \___________ + + tuser ________________________________________________________ + + +bad frame + + __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _____ _____ + tdata XXXXXXXXX_A0__X_A1__X_A2__XXXXXXXXXXXX + _____ _____ _____ + tkeep XXXXXXXXX_K0__X_K1__X_K2__XXXXXXXXXXXX + _________________ + tvalid ________/ \___________ + ______________________________________ + tready + _____ + tlast ____________________/ \___________ + _____ + tuser ____________________/ \___________ + + +## Testing + +Running the included testbenches requires MyHDL and Icarus Verilog. Make sure +that myhdl.vpi is installed properly for cosimulation to work correctly. The +testbenches can be run with a Python test runner like nose or py.test, or the +individual test scripts can be run with python directly. + +### Testbench Files + + tb/axis_ep.py : MyHDL AXI Stream endpoints + tb/ll_ep.py : MyHDL LocalLink endpoints From a28a534bff820037db4fd09d3be001072f18a5e2 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Wed, 12 Nov 2014 01:54:31 -0800 Subject: [PATCH 5/5] Add AXI stream crosspoint module and testbench --- rtl/axis_crosspoint.py | 206 +++++++++++++ rtl/axis_crosspoint_4x4.v | 281 +++++++++++++++++ rtl/axis_crosspoint_64.py | 214 +++++++++++++ rtl/axis_crosspoint_64_4x4.v | 322 ++++++++++++++++++++ tb/test_axis_crosspoint_4x4.py | 448 +++++++++++++++++++++++++++ tb/test_axis_crosspoint_4x4.v | 146 +++++++++ tb/test_axis_crosspoint_64_4x4.py | 488 ++++++++++++++++++++++++++++++ tb/test_axis_crosspoint_64_4x4.v | 170 +++++++++++ 8 files changed, 2275 insertions(+) create mode 100755 rtl/axis_crosspoint.py create mode 100644 rtl/axis_crosspoint_4x4.v create mode 100755 rtl/axis_crosspoint_64.py create mode 100644 rtl/axis_crosspoint_64_4x4.v create mode 100755 tb/test_axis_crosspoint_4x4.py create mode 100644 tb/test_axis_crosspoint_4x4.v create mode 100755 tb/test_axis_crosspoint_64_4x4.py create mode 100644 tb/test_axis_crosspoint_64_4x4.v diff --git a/rtl/axis_crosspoint.py b/rtl/axis_crosspoint.py new file mode 100755 index 000000000..dc8f24149 --- /dev/null +++ b/rtl/axis_crosspoint.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python +"""axis_crosspoint + +Generates an AXI Stream crosspoint switch with a specific number of ports + +Usage: axis_crosspoint [OPTION]... + -?, --help display this help and exit + -p, --ports specify number of ports + -n, --name specify module name + -o, --output specify output file name +""" + +import io +import sys +import getopt +from math import * +from jinja2 import Template + +class Usage(Exception): + def __init__(self, msg): + self.msg = msg + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + try: + opts, args = getopt.getopt(argv[1:], "?n:p:o:", ["help", "name=", "ports=", "output="]) + except getopt.error as msg: + raise Usage(msg) + # more code, unchanged + except Usage as err: + print(err.msg, file=sys.stderr) + print("for help use --help", file=sys.stderr) + return 2 + + ports = 4 + name = None + out_name = None + + # process options + for o, a in opts: + if o in ('-?', '--help'): + print(__doc__) + sys.exit(0) + if o in ('-p', '--ports'): + ports = int(a) + if o in ('-n', '--name'): + name = a + if o in ('-o', '--output'): + out_name = a + + if name is None: + name = "axis_crosspoint_{0}x{0}".format(ports) + + if out_name is None: + out_name = name + ".v" + + print("Opening file '%s'..." % out_name) + + try: + out_file = open(out_name, 'w') + except Exception as ex: + print("Error opening \"%s\": %s" %(out_name, ex.strerror), file=sys.stderr) + exit(1) + + print("Generating {0} port AXI Stream crosspoint {1}...".format(ports, name)) + + select_width = ceil(log2(ports)) + + t = Template(u"""/* + +Copyright (c) 2014 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}}x{{n}} crosspoint + */ +module {{name}} # +( + parameter DATA_WIDTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ + {%- for p in ports %} + input wire [DATA_WIDTH-1:0] input_{{p}}_axis_tdata, + input wire input_{{p}}_axis_tvalid, + input wire input_{{p}}_axis_tlast, + {% endfor %} + /* + * AXI Stream outputs + */ + {%- for p in ports %} + output wire [DATA_WIDTH-1:0] output_{{p}}_axis_tdata, + output wire output_{{p}}_axis_tvalid, + output wire output_{{p}}_axis_tlast, + {% endfor %} + /* + * Control + */ + {%- for p in ports %} + input wire [{{w-1}}:0] output_{{p}}_select{% if not loop.last %},{% endif %} + {%- endfor %} +); +{% for p in ports %} +reg [DATA_WIDTH-1:0] input_{{p}}_axis_tdata_reg = 0; +reg input_{{p}}_axis_tvalid_reg = 0; +reg input_{{p}}_axis_tlast_reg = 0; +{% endfor %} + +{%- for p in ports %} +reg [DATA_WIDTH-1:0] output_{{p}}_axis_tdata_reg = 0; +reg output_{{p}}_axis_tvalid_reg = 0; +reg output_{{p}}_axis_tlast_reg = 0; +{% endfor %} + +{%- for p in ports %} +reg [{{w-1}}:0] output_{{p}}_select_reg = 0; +{%- endfor %} +{% for p in ports %} +assign output_{{p}}_axis_tdata = output_{{p}}_axis_tdata_reg; +assign output_{{p}}_axis_tvalid = output_{{p}}_axis_tvalid_reg; +assign output_{{p}}_axis_tlast = output_{{p}}_axis_tlast_reg; +{% endfor %} + +always @(posedge clk or posedge rst) begin + if (rst) begin + {%- for p in ports %} + output_{{p}}_select_reg <= 0; + {%- endfor %} + {% for p in ports %} + input_{{p}}_axis_tvalid_reg <= 0; + input_{{p}}_axis_tlast_reg <= 0; + {%- endfor %} + {% for p in ports %} + output_{{p}}_axis_tvalid_reg <= 0; + output_{{p}}_axis_tlast_reg <= 0; + {%- endfor %} + end else begin + {%- for p in ports %} + input_{{p}}_axis_tdata_reg <= input_{{p}}_axis_tdata; + input_{{p}}_axis_tvalid_reg <= input_{{p}}_axis_tvalid; + input_{{p}}_axis_tlast_reg <= input_{{p}}_axis_tlast; + {% endfor %} + {%- for p in ports %} + output_{{p}}_select_reg <= output_{{p}}_select; + {%- endfor %} + {%- for p in ports %} + + case (output_{{p}}_select_reg) + {%- for q in ports %} + {{w}}'d{{q}}: begin + output_{{p}}_axis_tdata_reg <= input_{{q}}_axis_tdata_reg; + output_{{p}}_axis_tvalid_reg <= input_{{q}}_axis_tvalid_reg; + output_{{p}}_axis_tlast_reg <= input_{{q}}_axis_tlast_reg; + end + {%- endfor %} + endcase + {%- endfor %} + end +end + +endmodule + +""") + + out_file.write(t.render( + n=ports, + w=select_width, + name=name, + ports=range(ports) + )) + + print("Done") + +if __name__ == "__main__": + sys.exit(main()) + diff --git a/rtl/axis_crosspoint_4x4.v b/rtl/axis_crosspoint_4x4.v new file mode 100644 index 000000000..1734f49f3 --- /dev/null +++ b/rtl/axis_crosspoint_4x4.v @@ -0,0 +1,281 @@ +/* + +Copyright (c) 2014 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 4x4 crosspoint + */ +module axis_crosspoint_4x4 # +( + parameter DATA_WIDTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ + input wire [DATA_WIDTH-1:0] input_0_axis_tdata, + input wire input_0_axis_tvalid, + input wire input_0_axis_tlast, + + input wire [DATA_WIDTH-1:0] input_1_axis_tdata, + input wire input_1_axis_tvalid, + input wire input_1_axis_tlast, + + input wire [DATA_WIDTH-1:0] input_2_axis_tdata, + input wire input_2_axis_tvalid, + input wire input_2_axis_tlast, + + input wire [DATA_WIDTH-1:0] input_3_axis_tdata, + input wire input_3_axis_tvalid, + input wire input_3_axis_tlast, + + /* + * AXI Stream outputs + */ + output wire [DATA_WIDTH-1:0] output_0_axis_tdata, + output wire output_0_axis_tvalid, + output wire output_0_axis_tlast, + + output wire [DATA_WIDTH-1:0] output_1_axis_tdata, + output wire output_1_axis_tvalid, + output wire output_1_axis_tlast, + + output wire [DATA_WIDTH-1:0] output_2_axis_tdata, + output wire output_2_axis_tvalid, + output wire output_2_axis_tlast, + + output wire [DATA_WIDTH-1:0] output_3_axis_tdata, + output wire output_3_axis_tvalid, + output wire output_3_axis_tlast, + + /* + * Control + */ + input wire [1:0] output_0_select, + input wire [1:0] output_1_select, + input wire [1:0] output_2_select, + input wire [1:0] output_3_select +); + +reg [DATA_WIDTH-1:0] input_0_axis_tdata_reg = 0; +reg input_0_axis_tvalid_reg = 0; +reg input_0_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] input_1_axis_tdata_reg = 0; +reg input_1_axis_tvalid_reg = 0; +reg input_1_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] input_2_axis_tdata_reg = 0; +reg input_2_axis_tvalid_reg = 0; +reg input_2_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] input_3_axis_tdata_reg = 0; +reg input_3_axis_tvalid_reg = 0; +reg input_3_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] output_0_axis_tdata_reg = 0; +reg output_0_axis_tvalid_reg = 0; +reg output_0_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] output_1_axis_tdata_reg = 0; +reg output_1_axis_tvalid_reg = 0; +reg output_1_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] output_2_axis_tdata_reg = 0; +reg output_2_axis_tvalid_reg = 0; +reg output_2_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] output_3_axis_tdata_reg = 0; +reg output_3_axis_tvalid_reg = 0; +reg output_3_axis_tlast_reg = 0; + +reg [1:0] output_0_select_reg = 0; +reg [1:0] output_1_select_reg = 0; +reg [1:0] output_2_select_reg = 0; +reg [1:0] output_3_select_reg = 0; + +assign output_0_axis_tdata = output_0_axis_tdata_reg; +assign output_0_axis_tvalid = output_0_axis_tvalid_reg; +assign output_0_axis_tlast = output_0_axis_tlast_reg; + +assign output_1_axis_tdata = output_1_axis_tdata_reg; +assign output_1_axis_tvalid = output_1_axis_tvalid_reg; +assign output_1_axis_tlast = output_1_axis_tlast_reg; + +assign output_2_axis_tdata = output_2_axis_tdata_reg; +assign output_2_axis_tvalid = output_2_axis_tvalid_reg; +assign output_2_axis_tlast = output_2_axis_tlast_reg; + +assign output_3_axis_tdata = output_3_axis_tdata_reg; +assign output_3_axis_tvalid = output_3_axis_tvalid_reg; +assign output_3_axis_tlast = output_3_axis_tlast_reg; + + +always @(posedge clk or posedge rst) begin + if (rst) begin + output_0_select_reg <= 0; + output_1_select_reg <= 0; + output_2_select_reg <= 0; + output_3_select_reg <= 0; + + input_0_axis_tvalid_reg <= 0; + input_0_axis_tlast_reg <= 0; + input_1_axis_tvalid_reg <= 0; + input_1_axis_tlast_reg <= 0; + input_2_axis_tvalid_reg <= 0; + input_2_axis_tlast_reg <= 0; + input_3_axis_tvalid_reg <= 0; + input_3_axis_tlast_reg <= 0; + + output_0_axis_tvalid_reg <= 0; + output_0_axis_tlast_reg <= 0; + output_1_axis_tvalid_reg <= 0; + output_1_axis_tlast_reg <= 0; + output_2_axis_tvalid_reg <= 0; + output_2_axis_tlast_reg <= 0; + output_3_axis_tvalid_reg <= 0; + output_3_axis_tlast_reg <= 0; + end else begin + input_0_axis_tdata_reg <= input_0_axis_tdata; + input_0_axis_tvalid_reg <= input_0_axis_tvalid; + input_0_axis_tlast_reg <= input_0_axis_tlast; + + input_1_axis_tdata_reg <= input_1_axis_tdata; + input_1_axis_tvalid_reg <= input_1_axis_tvalid; + input_1_axis_tlast_reg <= input_1_axis_tlast; + + input_2_axis_tdata_reg <= input_2_axis_tdata; + input_2_axis_tvalid_reg <= input_2_axis_tvalid; + input_2_axis_tlast_reg <= input_2_axis_tlast; + + input_3_axis_tdata_reg <= input_3_axis_tdata; + input_3_axis_tvalid_reg <= input_3_axis_tvalid; + input_3_axis_tlast_reg <= input_3_axis_tlast; + + output_0_select_reg <= output_0_select; + output_1_select_reg <= output_1_select; + output_2_select_reg <= output_2_select; + output_3_select_reg <= output_3_select; + + case (output_0_select_reg) + 2'd0: begin + output_0_axis_tdata_reg <= input_0_axis_tdata_reg; + output_0_axis_tvalid_reg <= input_0_axis_tvalid_reg; + output_0_axis_tlast_reg <= input_0_axis_tlast_reg; + end + 2'd1: begin + output_0_axis_tdata_reg <= input_1_axis_tdata_reg; + output_0_axis_tvalid_reg <= input_1_axis_tvalid_reg; + output_0_axis_tlast_reg <= input_1_axis_tlast_reg; + end + 2'd2: begin + output_0_axis_tdata_reg <= input_2_axis_tdata_reg; + output_0_axis_tvalid_reg <= input_2_axis_tvalid_reg; + output_0_axis_tlast_reg <= input_2_axis_tlast_reg; + end + 2'd3: begin + output_0_axis_tdata_reg <= input_3_axis_tdata_reg; + output_0_axis_tvalid_reg <= input_3_axis_tvalid_reg; + output_0_axis_tlast_reg <= input_3_axis_tlast_reg; + end + endcase + + case (output_1_select_reg) + 2'd0: begin + output_1_axis_tdata_reg <= input_0_axis_tdata_reg; + output_1_axis_tvalid_reg <= input_0_axis_tvalid_reg; + output_1_axis_tlast_reg <= input_0_axis_tlast_reg; + end + 2'd1: begin + output_1_axis_tdata_reg <= input_1_axis_tdata_reg; + output_1_axis_tvalid_reg <= input_1_axis_tvalid_reg; + output_1_axis_tlast_reg <= input_1_axis_tlast_reg; + end + 2'd2: begin + output_1_axis_tdata_reg <= input_2_axis_tdata_reg; + output_1_axis_tvalid_reg <= input_2_axis_tvalid_reg; + output_1_axis_tlast_reg <= input_2_axis_tlast_reg; + end + 2'd3: begin + output_1_axis_tdata_reg <= input_3_axis_tdata_reg; + output_1_axis_tvalid_reg <= input_3_axis_tvalid_reg; + output_1_axis_tlast_reg <= input_3_axis_tlast_reg; + end + endcase + + case (output_2_select_reg) + 2'd0: begin + output_2_axis_tdata_reg <= input_0_axis_tdata_reg; + output_2_axis_tvalid_reg <= input_0_axis_tvalid_reg; + output_2_axis_tlast_reg <= input_0_axis_tlast_reg; + end + 2'd1: begin + output_2_axis_tdata_reg <= input_1_axis_tdata_reg; + output_2_axis_tvalid_reg <= input_1_axis_tvalid_reg; + output_2_axis_tlast_reg <= input_1_axis_tlast_reg; + end + 2'd2: begin + output_2_axis_tdata_reg <= input_2_axis_tdata_reg; + output_2_axis_tvalid_reg <= input_2_axis_tvalid_reg; + output_2_axis_tlast_reg <= input_2_axis_tlast_reg; + end + 2'd3: begin + output_2_axis_tdata_reg <= input_3_axis_tdata_reg; + output_2_axis_tvalid_reg <= input_3_axis_tvalid_reg; + output_2_axis_tlast_reg <= input_3_axis_tlast_reg; + end + endcase + + case (output_3_select_reg) + 2'd0: begin + output_3_axis_tdata_reg <= input_0_axis_tdata_reg; + output_3_axis_tvalid_reg <= input_0_axis_tvalid_reg; + output_3_axis_tlast_reg <= input_0_axis_tlast_reg; + end + 2'd1: begin + output_3_axis_tdata_reg <= input_1_axis_tdata_reg; + output_3_axis_tvalid_reg <= input_1_axis_tvalid_reg; + output_3_axis_tlast_reg <= input_1_axis_tlast_reg; + end + 2'd2: begin + output_3_axis_tdata_reg <= input_2_axis_tdata_reg; + output_3_axis_tvalid_reg <= input_2_axis_tvalid_reg; + output_3_axis_tlast_reg <= input_2_axis_tlast_reg; + end + 2'd3: begin + output_3_axis_tdata_reg <= input_3_axis_tdata_reg; + output_3_axis_tvalid_reg <= input_3_axis_tvalid_reg; + output_3_axis_tlast_reg <= input_3_axis_tlast_reg; + end + endcase + end +end + +endmodule diff --git a/rtl/axis_crosspoint_64.py b/rtl/axis_crosspoint_64.py new file mode 100755 index 000000000..dea44e2ea --- /dev/null +++ b/rtl/axis_crosspoint_64.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python +"""axis_crosspoint_64_64 + +Generates an AXI Stream crosspoint switch with a specific number of ports + +Usage: axis_crosspoint_64 [OPTION]... + -?, --help display this help and exit + -p, --ports specify number of ports + -n, --name specify module name + -o, --output specify output file name +""" + +import io +import sys +import getopt +from math import * +from jinja2 import Template + +class Usage(Exception): + def __init__(self, msg): + self.msg = msg + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + try: + opts, args = getopt.getopt(argv[1:], "?n:p:o:", ["help", "name=", "ports=", "output="]) + except getopt.error as msg: + raise Usage(msg) + # more code, unchanged + except Usage as err: + print(err.msg, file=sys.stderr) + print("for help use --help", file=sys.stderr) + return 2 + + ports = 4 + name = None + out_name = None + + # process options + for o, a in opts: + if o in ('-?', '--help'): + print(__doc__) + sys.exit(0) + if o in ('-p', '--ports'): + ports = int(a) + if o in ('-n', '--name'): + name = a + if o in ('-o', '--output'): + out_name = a + + if name is None: + name = "axis_crosspoint_64_{0}x{0}".format(ports) + + if out_name is None: + out_name = name + ".v" + + print("Opening file '%s'..." % out_name) + + try: + out_file = open(out_name, 'w') + except Exception as ex: + print("Error opening \"%s\": %s" %(out_name, ex.strerror), file=sys.stderr) + exit(1) + + print("Generating {0} port AXI Stream crosspoint {1}...".format(ports, name)) + + select_width = ceil(log2(ports)) + + t = Template(u"""/* + +Copyright (c) 2014 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}}x{{n}} crosspoint (64 bit datapath) + */ +module {{name}} # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ + {%- for p in ports %} + input wire [DATA_WIDTH-1:0] input_{{p}}_axis_tdata, + input wire [KEEP_WIDTH-1:0] input_{{p}}_axis_tkeep, + input wire input_{{p}}_axis_tvalid, + input wire input_{{p}}_axis_tlast, + {% endfor %} + /* + * AXI Stream outputs + */ + {%- for p in ports %} + output wire [DATA_WIDTH-1:0] output_{{p}}_axis_tdata, + output wire [KEEP_WIDTH-1:0] output_{{p}}_axis_tkeep, + output wire output_{{p}}_axis_tvalid, + output wire output_{{p}}_axis_tlast, + {% endfor %} + /* + * Control + */ + {%- for p in ports %} + input wire [{{w-1}}:0] output_{{p}}_select{% if not loop.last %},{% endif %} + {%- endfor %} +); +{% for p in ports %} +reg [DATA_WIDTH-1:0] input_{{p}}_axis_tdata_reg = 0; +reg [KEEP_WIDTH-1:0] input_{{p}}_axis_tkeep_reg = 0; +reg input_{{p}}_axis_tvalid_reg = 0; +reg input_{{p}}_axis_tlast_reg = 0; +{% endfor %} + +{%- for p in ports %} +reg [DATA_WIDTH-1:0] output_{{p}}_axis_tdata_reg = 0; +reg [KEEP_WIDTH-1:0] output_{{p}}_axis_tkeep_reg = 0; +reg output_{{p}}_axis_tvalid_reg = 0; +reg output_{{p}}_axis_tlast_reg = 0; +{% endfor %} + +{%- for p in ports %} +reg [{{w-1}}:0] output_{{p}}_select_reg = 0; +{%- endfor %} +{% for p in ports %} +assign output_{{p}}_axis_tdata = output_{{p}}_axis_tdata_reg; +assign output_{{p}}_axis_tkeep = output_{{p}}_axis_tkeep_reg; +assign output_{{p}}_axis_tvalid = output_{{p}}_axis_tvalid_reg; +assign output_{{p}}_axis_tlast = output_{{p}}_axis_tlast_reg; +{% endfor %} + +always @(posedge clk or posedge rst) begin + if (rst) begin + {%- for p in ports %} + output_{{p}}_select_reg <= 0; + {%- endfor %} + {% for p in ports %} + input_{{p}}_axis_tvalid_reg <= 0; + input_{{p}}_axis_tlast_reg <= 0; + {%- endfor %} + {% for p in ports %} + output_{{p}}_axis_tvalid_reg <= 0; + output_{{p}}_axis_tlast_reg <= 0; + {%- endfor %} + end else begin + {%- for p in ports %} + input_{{p}}_axis_tdata_reg <= input_{{p}}_axis_tdata; + input_{{p}}_axis_tkeep_reg <= input_{{p}}_axis_tkeep; + input_{{p}}_axis_tvalid_reg <= input_{{p}}_axis_tvalid; + input_{{p}}_axis_tlast_reg <= input_{{p}}_axis_tlast; + {% endfor %} + {%- for p in ports %} + output_{{p}}_select_reg <= output_{{p}}_select; + {%- endfor %} + {%- for p in ports %} + + case (output_{{p}}_select_reg) + {%- for q in ports %} + {{w}}'d{{q}}: begin + output_{{p}}_axis_tdata_reg <= input_{{q}}_axis_tdata_reg; + output_{{p}}_axis_tkeep_reg <= input_{{q}}_axis_tkeep_reg; + output_{{p}}_axis_tvalid_reg <= input_{{q}}_axis_tvalid_reg; + output_{{p}}_axis_tlast_reg <= input_{{q}}_axis_tlast_reg; + end + {%- endfor %} + endcase + {%- endfor %} + end +end + +endmodule + +""") + + out_file.write(t.render( + n=ports, + w=select_width, + name=name, + ports=range(ports) + )) + + print("Done") + +if __name__ == "__main__": + sys.exit(main()) + diff --git a/rtl/axis_crosspoint_64_4x4.v b/rtl/axis_crosspoint_64_4x4.v new file mode 100644 index 000000000..25345c15d --- /dev/null +++ b/rtl/axis_crosspoint_64_4x4.v @@ -0,0 +1,322 @@ +/* + +Copyright (c) 2014 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 4x4 crosspoint (64 bit datapath) + */ +module axis_crosspoint_64_4x4 # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ + input wire [DATA_WIDTH-1:0] input_0_axis_tdata, + input wire [KEEP_WIDTH-1:0] input_0_axis_tkeep, + input wire input_0_axis_tvalid, + input wire input_0_axis_tlast, + + input wire [DATA_WIDTH-1:0] input_1_axis_tdata, + input wire [KEEP_WIDTH-1:0] input_1_axis_tkeep, + input wire input_1_axis_tvalid, + input wire input_1_axis_tlast, + + input wire [DATA_WIDTH-1:0] input_2_axis_tdata, + input wire [KEEP_WIDTH-1:0] input_2_axis_tkeep, + input wire input_2_axis_tvalid, + input wire input_2_axis_tlast, + + input wire [DATA_WIDTH-1:0] input_3_axis_tdata, + input wire [KEEP_WIDTH-1:0] input_3_axis_tkeep, + input wire input_3_axis_tvalid, + input wire input_3_axis_tlast, + + /* + * AXI Stream outputs + */ + output wire [DATA_WIDTH-1:0] output_0_axis_tdata, + output wire [KEEP_WIDTH-1:0] output_0_axis_tkeep, + output wire output_0_axis_tvalid, + output wire output_0_axis_tlast, + + output wire [DATA_WIDTH-1:0] output_1_axis_tdata, + output wire [KEEP_WIDTH-1:0] output_1_axis_tkeep, + output wire output_1_axis_tvalid, + output wire output_1_axis_tlast, + + output wire [DATA_WIDTH-1:0] output_2_axis_tdata, + output wire [KEEP_WIDTH-1:0] output_2_axis_tkeep, + output wire output_2_axis_tvalid, + output wire output_2_axis_tlast, + + output wire [DATA_WIDTH-1:0] output_3_axis_tdata, + output wire [KEEP_WIDTH-1:0] output_3_axis_tkeep, + output wire output_3_axis_tvalid, + output wire output_3_axis_tlast, + + /* + * Control + */ + input wire [1:0] output_0_select, + input wire [1:0] output_1_select, + input wire [1:0] output_2_select, + input wire [1:0] output_3_select +); + +reg [DATA_WIDTH-1:0] input_0_axis_tdata_reg = 0; +reg [KEEP_WIDTH-1:0] input_0_axis_tkeep_reg = 0; +reg input_0_axis_tvalid_reg = 0; +reg input_0_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] input_1_axis_tdata_reg = 0; +reg [KEEP_WIDTH-1:0] input_1_axis_tkeep_reg = 0; +reg input_1_axis_tvalid_reg = 0; +reg input_1_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] input_2_axis_tdata_reg = 0; +reg [KEEP_WIDTH-1:0] input_2_axis_tkeep_reg = 0; +reg input_2_axis_tvalid_reg = 0; +reg input_2_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] input_3_axis_tdata_reg = 0; +reg [KEEP_WIDTH-1:0] input_3_axis_tkeep_reg = 0; +reg input_3_axis_tvalid_reg = 0; +reg input_3_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] output_0_axis_tdata_reg = 0; +reg [KEEP_WIDTH-1:0] output_0_axis_tkeep_reg = 0; +reg output_0_axis_tvalid_reg = 0; +reg output_0_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] output_1_axis_tdata_reg = 0; +reg [KEEP_WIDTH-1:0] output_1_axis_tkeep_reg = 0; +reg output_1_axis_tvalid_reg = 0; +reg output_1_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] output_2_axis_tdata_reg = 0; +reg [KEEP_WIDTH-1:0] output_2_axis_tkeep_reg = 0; +reg output_2_axis_tvalid_reg = 0; +reg output_2_axis_tlast_reg = 0; + +reg [DATA_WIDTH-1:0] output_3_axis_tdata_reg = 0; +reg [KEEP_WIDTH-1:0] output_3_axis_tkeep_reg = 0; +reg output_3_axis_tvalid_reg = 0; +reg output_3_axis_tlast_reg = 0; + +reg [1:0] output_0_select_reg = 0; +reg [1:0] output_1_select_reg = 0; +reg [1:0] output_2_select_reg = 0; +reg [1:0] output_3_select_reg = 0; + +assign output_0_axis_tdata = output_0_axis_tdata_reg; +assign output_0_axis_tkeep = output_0_axis_tkeep_reg; +assign output_0_axis_tvalid = output_0_axis_tvalid_reg; +assign output_0_axis_tlast = output_0_axis_tlast_reg; + +assign output_1_axis_tdata = output_1_axis_tdata_reg; +assign output_1_axis_tkeep = output_1_axis_tkeep_reg; +assign output_1_axis_tvalid = output_1_axis_tvalid_reg; +assign output_1_axis_tlast = output_1_axis_tlast_reg; + +assign output_2_axis_tdata = output_2_axis_tdata_reg; +assign output_2_axis_tkeep = output_2_axis_tkeep_reg; +assign output_2_axis_tvalid = output_2_axis_tvalid_reg; +assign output_2_axis_tlast = output_2_axis_tlast_reg; + +assign output_3_axis_tdata = output_3_axis_tdata_reg; +assign output_3_axis_tkeep = output_3_axis_tkeep_reg; +assign output_3_axis_tvalid = output_3_axis_tvalid_reg; +assign output_3_axis_tlast = output_3_axis_tlast_reg; + + +always @(posedge clk or posedge rst) begin + if (rst) begin + output_0_select_reg <= 0; + output_1_select_reg <= 0; + output_2_select_reg <= 0; + output_3_select_reg <= 0; + + input_0_axis_tvalid_reg <= 0; + input_0_axis_tlast_reg <= 0; + input_1_axis_tvalid_reg <= 0; + input_1_axis_tlast_reg <= 0; + input_2_axis_tvalid_reg <= 0; + input_2_axis_tlast_reg <= 0; + input_3_axis_tvalid_reg <= 0; + input_3_axis_tlast_reg <= 0; + + output_0_axis_tvalid_reg <= 0; + output_0_axis_tlast_reg <= 0; + output_1_axis_tvalid_reg <= 0; + output_1_axis_tlast_reg <= 0; + output_2_axis_tvalid_reg <= 0; + output_2_axis_tlast_reg <= 0; + output_3_axis_tvalid_reg <= 0; + output_3_axis_tlast_reg <= 0; + end else begin + input_0_axis_tdata_reg <= input_0_axis_tdata; + input_0_axis_tkeep_reg <= input_0_axis_tkeep; + input_0_axis_tvalid_reg <= input_0_axis_tvalid; + input_0_axis_tlast_reg <= input_0_axis_tlast; + + input_1_axis_tdata_reg <= input_1_axis_tdata; + input_1_axis_tkeep_reg <= input_1_axis_tkeep; + input_1_axis_tvalid_reg <= input_1_axis_tvalid; + input_1_axis_tlast_reg <= input_1_axis_tlast; + + input_2_axis_tdata_reg <= input_2_axis_tdata; + input_2_axis_tkeep_reg <= input_2_axis_tkeep; + input_2_axis_tvalid_reg <= input_2_axis_tvalid; + input_2_axis_tlast_reg <= input_2_axis_tlast; + + input_3_axis_tdata_reg <= input_3_axis_tdata; + input_3_axis_tkeep_reg <= input_3_axis_tkeep; + input_3_axis_tvalid_reg <= input_3_axis_tvalid; + input_3_axis_tlast_reg <= input_3_axis_tlast; + + output_0_select_reg <= output_0_select; + output_1_select_reg <= output_1_select; + output_2_select_reg <= output_2_select; + output_3_select_reg <= output_3_select; + + case (output_0_select_reg) + 2'd0: begin + output_0_axis_tdata_reg <= input_0_axis_tdata_reg; + output_0_axis_tkeep_reg <= input_0_axis_tkeep_reg; + output_0_axis_tvalid_reg <= input_0_axis_tvalid_reg; + output_0_axis_tlast_reg <= input_0_axis_tlast_reg; + end + 2'd1: begin + output_0_axis_tdata_reg <= input_1_axis_tdata_reg; + output_0_axis_tkeep_reg <= input_1_axis_tkeep_reg; + output_0_axis_tvalid_reg <= input_1_axis_tvalid_reg; + output_0_axis_tlast_reg <= input_1_axis_tlast_reg; + end + 2'd2: begin + output_0_axis_tdata_reg <= input_2_axis_tdata_reg; + output_0_axis_tkeep_reg <= input_2_axis_tkeep_reg; + output_0_axis_tvalid_reg <= input_2_axis_tvalid_reg; + output_0_axis_tlast_reg <= input_2_axis_tlast_reg; + end + 2'd3: begin + output_0_axis_tdata_reg <= input_3_axis_tdata_reg; + output_0_axis_tkeep_reg <= input_3_axis_tkeep_reg; + output_0_axis_tvalid_reg <= input_3_axis_tvalid_reg; + output_0_axis_tlast_reg <= input_3_axis_tlast_reg; + end + endcase + + case (output_1_select_reg) + 2'd0: begin + output_1_axis_tdata_reg <= input_0_axis_tdata_reg; + output_1_axis_tkeep_reg <= input_0_axis_tkeep_reg; + output_1_axis_tvalid_reg <= input_0_axis_tvalid_reg; + output_1_axis_tlast_reg <= input_0_axis_tlast_reg; + end + 2'd1: begin + output_1_axis_tdata_reg <= input_1_axis_tdata_reg; + output_1_axis_tkeep_reg <= input_1_axis_tkeep_reg; + output_1_axis_tvalid_reg <= input_1_axis_tvalid_reg; + output_1_axis_tlast_reg <= input_1_axis_tlast_reg; + end + 2'd2: begin + output_1_axis_tdata_reg <= input_2_axis_tdata_reg; + output_1_axis_tkeep_reg <= input_2_axis_tkeep_reg; + output_1_axis_tvalid_reg <= input_2_axis_tvalid_reg; + output_1_axis_tlast_reg <= input_2_axis_tlast_reg; + end + 2'd3: begin + output_1_axis_tdata_reg <= input_3_axis_tdata_reg; + output_1_axis_tkeep_reg <= input_3_axis_tkeep_reg; + output_1_axis_tvalid_reg <= input_3_axis_tvalid_reg; + output_1_axis_tlast_reg <= input_3_axis_tlast_reg; + end + endcase + + case (output_2_select_reg) + 2'd0: begin + output_2_axis_tdata_reg <= input_0_axis_tdata_reg; + output_2_axis_tkeep_reg <= input_0_axis_tkeep_reg; + output_2_axis_tvalid_reg <= input_0_axis_tvalid_reg; + output_2_axis_tlast_reg <= input_0_axis_tlast_reg; + end + 2'd1: begin + output_2_axis_tdata_reg <= input_1_axis_tdata_reg; + output_2_axis_tkeep_reg <= input_1_axis_tkeep_reg; + output_2_axis_tvalid_reg <= input_1_axis_tvalid_reg; + output_2_axis_tlast_reg <= input_1_axis_tlast_reg; + end + 2'd2: begin + output_2_axis_tdata_reg <= input_2_axis_tdata_reg; + output_2_axis_tkeep_reg <= input_2_axis_tkeep_reg; + output_2_axis_tvalid_reg <= input_2_axis_tvalid_reg; + output_2_axis_tlast_reg <= input_2_axis_tlast_reg; + end + 2'd3: begin + output_2_axis_tdata_reg <= input_3_axis_tdata_reg; + output_2_axis_tkeep_reg <= input_3_axis_tkeep_reg; + output_2_axis_tvalid_reg <= input_3_axis_tvalid_reg; + output_2_axis_tlast_reg <= input_3_axis_tlast_reg; + end + endcase + + case (output_3_select_reg) + 2'd0: begin + output_3_axis_tdata_reg <= input_0_axis_tdata_reg; + output_3_axis_tkeep_reg <= input_0_axis_tkeep_reg; + output_3_axis_tvalid_reg <= input_0_axis_tvalid_reg; + output_3_axis_tlast_reg <= input_0_axis_tlast_reg; + end + 2'd1: begin + output_3_axis_tdata_reg <= input_1_axis_tdata_reg; + output_3_axis_tkeep_reg <= input_1_axis_tkeep_reg; + output_3_axis_tvalid_reg <= input_1_axis_tvalid_reg; + output_3_axis_tlast_reg <= input_1_axis_tlast_reg; + end + 2'd2: begin + output_3_axis_tdata_reg <= input_2_axis_tdata_reg; + output_3_axis_tkeep_reg <= input_2_axis_tkeep_reg; + output_3_axis_tvalid_reg <= input_2_axis_tvalid_reg; + output_3_axis_tlast_reg <= input_2_axis_tlast_reg; + end + 2'd3: begin + output_3_axis_tdata_reg <= input_3_axis_tdata_reg; + output_3_axis_tkeep_reg <= input_3_axis_tkeep_reg; + output_3_axis_tvalid_reg <= input_3_axis_tvalid_reg; + output_3_axis_tlast_reg <= input_3_axis_tlast_reg; + end + endcase + end +end + +endmodule diff --git a/tb/test_axis_crosspoint_4x4.py b/tb/test_axis_crosspoint_4x4.py new file mode 100755 index 000000000..44803183e --- /dev/null +++ b/tb/test_axis_crosspoint_4x4.py @@ -0,0 +1,448 @@ +#!/usr/bin/env python2 +""" + +Copyright (c) 2014 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. + +""" + +from myhdl import * +import os +from Queue import Queue + +import axis_ep + +module = 'axis_crosspoint_4x4' + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("test_%s.v" % module) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o test_%s.vvp %s" % (module, src) + +def dut_axis_crosspoint_4x4(clk, + rst, + current_test, + + input_0_axis_tdata, + input_0_axis_tvalid, + input_0_axis_tlast, + input_1_axis_tdata, + input_1_axis_tvalid, + input_1_axis_tlast, + input_2_axis_tdata, + input_2_axis_tvalid, + input_2_axis_tlast, + input_3_axis_tdata, + input_3_axis_tvalid, + input_3_axis_tlast, + + output_0_axis_tdata, + output_0_axis_tvalid, + output_0_axis_tlast, + output_1_axis_tdata, + output_1_axis_tvalid, + output_1_axis_tlast, + output_2_axis_tdata, + output_2_axis_tvalid, + output_2_axis_tlast, + output_3_axis_tdata, + output_3_axis_tvalid, + output_3_axis_tlast, + + output_0_select, + output_1_select, + output_2_select, + output_3_select): + + if os.system(build_cmd): + raise Exception("Error running build command") + return Cosimulation("vvp -m myhdl test_%s.vvp -lxt2" % module, + clk=clk, + rst=rst, + current_test=current_test, + + input_0_axis_tdata=input_0_axis_tdata, + input_0_axis_tvalid=input_0_axis_tvalid, + input_0_axis_tlast=input_0_axis_tlast, + input_1_axis_tdata=input_1_axis_tdata, + input_1_axis_tvalid=input_1_axis_tvalid, + input_1_axis_tlast=input_1_axis_tlast, + input_2_axis_tdata=input_2_axis_tdata, + input_2_axis_tvalid=input_2_axis_tvalid, + input_2_axis_tlast=input_2_axis_tlast, + input_3_axis_tdata=input_3_axis_tdata, + input_3_axis_tvalid=input_3_axis_tvalid, + input_3_axis_tlast=input_3_axis_tlast, + + output_0_axis_tdata=output_0_axis_tdata, + output_0_axis_tvalid=output_0_axis_tvalid, + output_0_axis_tlast=output_0_axis_tlast, + output_1_axis_tdata=output_1_axis_tdata, + output_1_axis_tvalid=output_1_axis_tvalid, + output_1_axis_tlast=output_1_axis_tlast, + output_2_axis_tdata=output_2_axis_tdata, + output_2_axis_tvalid=output_2_axis_tvalid, + output_2_axis_tlast=output_2_axis_tlast, + output_3_axis_tdata=output_3_axis_tdata, + output_3_axis_tvalid=output_3_axis_tvalid, + output_3_axis_tlast=output_3_axis_tlast, + + output_0_select=output_0_select, + output_1_select=output_1_select, + output_2_select=output_2_select, + output_3_select=output_3_select) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + input_0_axis_tdata = Signal(intbv(0)[8:]) + input_0_axis_tvalid = Signal(bool(0)) + input_0_axis_tlast = Signal(bool(0)) + input_1_axis_tdata = Signal(intbv(0)[8:]) + input_1_axis_tvalid = Signal(bool(0)) + input_1_axis_tlast = Signal(bool(0)) + input_2_axis_tdata = Signal(intbv(0)[8:]) + input_2_axis_tvalid = Signal(bool(0)) + input_2_axis_tlast = Signal(bool(0)) + input_3_axis_tdata = Signal(intbv(0)[8:]) + input_3_axis_tvalid = Signal(bool(0)) + input_3_axis_tlast = Signal(bool(0)) + + output_0_select = Signal(intbv(0)[2:]) + output_1_select = Signal(intbv(0)[2:]) + output_2_select = Signal(intbv(0)[2:]) + output_3_select = Signal(intbv(0)[2:]) + + # Outputs + output_0_axis_tdata = Signal(intbv(0)[8:]) + output_0_axis_tvalid = Signal(bool(0)) + output_0_axis_tlast = Signal(bool(0)) + output_1_axis_tdata = Signal(intbv(0)[8:]) + output_1_axis_tvalid = Signal(bool(0)) + output_1_axis_tlast = Signal(bool(0)) + output_2_axis_tdata = Signal(intbv(0)[8:]) + output_2_axis_tvalid = Signal(bool(0)) + output_2_axis_tlast = Signal(bool(0)) + output_3_axis_tdata = Signal(intbv(0)[8:]) + output_3_axis_tvalid = Signal(bool(0)) + output_3_axis_tlast = Signal(bool(0)) + + # sources and sinks + source_0_queue = Queue() + source_0_pause = Signal(bool(0)) + source_1_queue = Queue() + source_1_pause = Signal(bool(0)) + source_2_queue = Queue() + source_2_pause = Signal(bool(0)) + source_3_queue = Queue() + source_3_pause = Signal(bool(0)) + sink_0_queue = Queue() + sink_0_pause = Signal(bool(0)) + sink_1_queue = Queue() + sink_1_pause = Signal(bool(0)) + sink_2_queue = Queue() + sink_2_pause = Signal(bool(0)) + sink_3_queue = Queue() + sink_3_pause = Signal(bool(0)) + + source_0 = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_0_axis_tdata, + tvalid=input_0_axis_tvalid, + tlast=input_0_axis_tlast, + fifo=source_0_queue, + pause=source_0_pause, + name='source0') + source_1 = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_1_axis_tdata, + tvalid=input_1_axis_tvalid, + tlast=input_1_axis_tlast, + fifo=source_1_queue, + pause=source_1_pause, + name='source1') + source_2 = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_2_axis_tdata, + tvalid=input_2_axis_tvalid, + tlast=input_2_axis_tlast, + fifo=source_2_queue, + pause=source_2_pause, + name='source2') + source_3 = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_3_axis_tdata, + tvalid=input_3_axis_tvalid, + tlast=input_3_axis_tlast, + fifo=source_3_queue, + pause=source_3_pause, + name='source3') + + sink_0 = axis_ep.AXIStreamSink(clk, + rst, + tdata=output_0_axis_tdata, + tvalid=output_0_axis_tvalid, + tlast=output_0_axis_tlast, + fifo=sink_0_queue, + pause=sink_0_pause, + name='sink0') + sink_1 = axis_ep.AXIStreamSink(clk, + rst, + tdata=output_1_axis_tdata, + tvalid=output_1_axis_tvalid, + tlast=output_1_axis_tlast, + fifo=sink_1_queue, + pause=sink_1_pause, + name='sink1') + sink_2 = axis_ep.AXIStreamSink(clk, + rst, + tdata=output_2_axis_tdata, + tvalid=output_2_axis_tvalid, + tlast=output_2_axis_tlast, + fifo=sink_2_queue, + pause=sink_2_pause, + name='sink2') + sink_3 = axis_ep.AXIStreamSink(clk, + rst, + tdata=output_3_axis_tdata, + tvalid=output_3_axis_tvalid, + tlast=output_3_axis_tlast, + fifo=sink_3_queue, + pause=sink_3_pause, + name='sink3') + + # DUT + dut = dut_axis_crosspoint_4x4(clk, + rst, + current_test, + + input_0_axis_tdata, + input_0_axis_tvalid, + input_0_axis_tlast, + input_1_axis_tdata, + input_1_axis_tvalid, + input_1_axis_tlast, + input_2_axis_tdata, + input_2_axis_tvalid, + input_2_axis_tlast, + input_3_axis_tdata, + input_3_axis_tvalid, + input_3_axis_tlast, + + output_0_axis_tdata, + output_0_axis_tvalid, + output_0_axis_tlast, + output_1_axis_tdata, + output_1_axis_tvalid, + output_1_axis_tlast, + output_2_axis_tdata, + output_2_axis_tvalid, + output_2_axis_tlast, + output_3_axis_tdata, + output_3_axis_tvalid, + output_3_axis_tlast, + + output_0_select, + output_1_select, + output_2_select, + output_3_select) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: 0123 -> 0123") + current_test.next = 1 + + output_0_select.next = 0 + output_1_select.next = 1 + output_2_select.next = 2 + output_3_select.next = 3 + + test_frame0 = axis_ep.AXIStreamFrame('\x01\x00\x00\xFF\x01\x02\x03\x04') + test_frame1 = axis_ep.AXIStreamFrame('\x01\x01\x01\xFF\x01\x02\x03\x04') + test_frame2 = axis_ep.AXIStreamFrame('\x01\x02\x02\xFF\x01\x02\x03\x04') + test_frame3 = axis_ep.AXIStreamFrame('\x01\x03\x03\xFF\x01\x02\x03\x04') + source_0_queue.put(test_frame0) + source_1_queue.put(test_frame1) + source_2_queue.put(test_frame2) + source_3_queue.put(test_frame3) + yield clk.posedge + + while input_0_axis_tvalid or input_1_axis_tvalid or input_2_axis_tvalid or input_3_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rx_frame0 = None + if not sink_0_queue.empty(): + rx_frame0 = sink_0_queue.get() + + assert rx_frame0 == test_frame0 + + rx_frame1 = None + if not sink_1_queue.empty(): + rx_frame1 = sink_1_queue.get() + + assert rx_frame1 == test_frame1 + + rx_frame2 = None + if not sink_2_queue.empty(): + rx_frame2 = sink_2_queue.get() + + assert rx_frame2 == test_frame2 + + rx_frame3 = None + if not sink_3_queue.empty(): + rx_frame3 = sink_3_queue.get() + + assert rx_frame3 == test_frame3 + + yield delay(100) + + yield clk.posedge + print("test 2: 0123 -> 3210") + current_test.next = 2 + + output_0_select.next = 3 + output_1_select.next = 2 + output_2_select.next = 1 + output_3_select.next = 0 + + test_frame0 = axis_ep.AXIStreamFrame('\x02\x00\x03\xFF\x01\x02\x03\x04') + test_frame1 = axis_ep.AXIStreamFrame('\x02\x01\x02\xFF\x01\x02\x03\x04') + test_frame2 = axis_ep.AXIStreamFrame('\x02\x02\x01\xFF\x01\x02\x03\x04') + test_frame3 = axis_ep.AXIStreamFrame('\x02\x03\x00\xFF\x01\x02\x03\x04') + source_0_queue.put(test_frame0) + source_1_queue.put(test_frame1) + source_2_queue.put(test_frame2) + source_3_queue.put(test_frame3) + yield clk.posedge + + while input_0_axis_tvalid or input_1_axis_tvalid or input_2_axis_tvalid or input_3_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rx_frame0 = None + if not sink_0_queue.empty(): + rx_frame0 = sink_0_queue.get() + + assert rx_frame0 == test_frame3 + + rx_frame1 = None + if not sink_1_queue.empty(): + rx_frame1 = sink_1_queue.get() + + assert rx_frame1 == test_frame2 + + rx_frame2 = None + if not sink_2_queue.empty(): + rx_frame2 = sink_2_queue.get() + + assert rx_frame2 == test_frame1 + + rx_frame3 = None + if not sink_3_queue.empty(): + rx_frame3 = sink_3_queue.get() + + assert rx_frame3 == test_frame0 + + yield delay(100) + + yield clk.posedge + print("test 3: 0000 -> 0123") + current_test.next = 3 + + output_0_select.next = 0 + output_1_select.next = 0 + output_2_select.next = 0 + output_3_select.next = 0 + + test_frame0 = axis_ep.AXIStreamFrame('\x03\x00\xFF\xFF\x01\x02\x03\x04') + source_0_queue.put(test_frame0) + yield clk.posedge + + while input_0_axis_tvalid or input_1_axis_tvalid or input_2_axis_tvalid or input_3_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rx_frame0 = None + if not sink_0_queue.empty(): + rx_frame0 = sink_0_queue.get() + + assert rx_frame0 == test_frame0 + + rx_frame1 = None + if not sink_1_queue.empty(): + rx_frame1 = sink_1_queue.get() + + assert rx_frame1 == test_frame0 + + rx_frame2 = None + if not sink_2_queue.empty(): + rx_frame2 = sink_2_queue.get() + + assert rx_frame2 == test_frame0 + + rx_frame3 = None + if not sink_3_queue.empty(): + rx_frame3 = sink_3_queue.get() + + assert rx_frame3 == test_frame0 + + yield delay(100) + + raise StopSimulation + + return dut, source_0, source_1, source_2, source_3, sink_0, sink_1, sink_2, sink_3, clkgen, check + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/tb/test_axis_crosspoint_4x4.v b/tb/test_axis_crosspoint_4x4.v new file mode 100644 index 000000000..5114ae959 --- /dev/null +++ b/tb/test_axis_crosspoint_4x4.v @@ -0,0 +1,146 @@ +/* + +Copyright (c) 2014 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 1 ns / 1 ps + +module test_axis_crosspoint_4x4; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [7:0] input_0_axis_tdata = 0; +reg input_0_axis_tvalid = 0; +reg input_0_axis_tlast = 0; +reg [7:0] input_1_axis_tdata = 0; +reg input_1_axis_tvalid = 0; +reg input_1_axis_tlast = 0; +reg [7:0] input_2_axis_tdata = 0; +reg input_2_axis_tvalid = 0; +reg input_2_axis_tlast = 0; +reg [7:0] input_3_axis_tdata = 0; +reg input_3_axis_tvalid = 0; +reg input_3_axis_tlast = 0; + +reg [1:0] output_0_select = 0; +reg [1:0] output_1_select = 0; +reg [1:0] output_2_select = 0; +reg [1:0] output_3_select = 0; + +// Outputs +wire [7:0] output_0_axis_tdata; +wire output_0_axis_tvalid; +wire output_0_axis_tlast; +wire [7:0] output_1_axis_tdata; +wire output_1_axis_tvalid; +wire output_1_axis_tlast; +wire [7:0] output_2_axis_tdata; +wire output_2_axis_tvalid; +wire output_2_axis_tlast; +wire [7:0] output_3_axis_tdata; +wire output_3_axis_tvalid; +wire output_3_axis_tlast; + +initial begin + // myhdl integration + $from_myhdl(clk, + rst, + current_test, + input_0_axis_tdata, + input_0_axis_tvalid, + input_0_axis_tlast, + input_1_axis_tdata, + input_1_axis_tvalid, + input_1_axis_tlast, + input_2_axis_tdata, + input_2_axis_tvalid, + input_2_axis_tlast, + input_3_axis_tdata, + input_3_axis_tvalid, + input_3_axis_tlast, + output_0_select, + output_1_select, + output_2_select, + output_3_select); + $to_myhdl(output_0_axis_tdata, + output_0_axis_tvalid, + output_0_axis_tlast, + output_1_axis_tdata, + output_1_axis_tvalid, + output_1_axis_tlast, + output_2_axis_tdata, + output_2_axis_tvalid, + output_2_axis_tlast, + output_3_axis_tdata, + output_3_axis_tvalid, + output_3_axis_tlast); + + // dump file + $dumpfile("test_axis_crosspoint_4x4.lxt"); + $dumpvars(0, test_axis_crosspoint_4x4); +end + +axis_crosspoint_4x4 #( + .DATA_WIDTH(8) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI inputs + .input_0_axis_tdata(input_0_axis_tdata), + .input_0_axis_tvalid(input_0_axis_tvalid), + .input_0_axis_tlast(input_0_axis_tlast), + .input_1_axis_tdata(input_1_axis_tdata), + .input_1_axis_tvalid(input_1_axis_tvalid), + .input_1_axis_tlast(input_1_axis_tlast), + .input_2_axis_tdata(input_2_axis_tdata), + .input_2_axis_tvalid(input_2_axis_tvalid), + .input_2_axis_tlast(input_2_axis_tlast), + .input_3_axis_tdata(input_3_axis_tdata), + .input_3_axis_tvalid(input_3_axis_tvalid), + .input_3_axis_tlast(input_3_axis_tlast), + // AXI outputs + .output_0_axis_tdata(output_0_axis_tdata), + .output_0_axis_tvalid(output_0_axis_tvalid), + .output_0_axis_tlast(output_0_axis_tlast), + .output_1_axis_tdata(output_1_axis_tdata), + .output_1_axis_tvalid(output_1_axis_tvalid), + .output_1_axis_tlast(output_1_axis_tlast), + .output_2_axis_tdata(output_2_axis_tdata), + .output_2_axis_tvalid(output_2_axis_tvalid), + .output_2_axis_tlast(output_2_axis_tlast), + .output_3_axis_tdata(output_3_axis_tdata), + .output_3_axis_tvalid(output_3_axis_tvalid), + .output_3_axis_tlast(output_3_axis_tlast), + // Control + .output_0_select(output_0_select), + .output_1_select(output_1_select), + .output_2_select(output_2_select), + .output_3_select(output_3_select) +); + +endmodule diff --git a/tb/test_axis_crosspoint_64_4x4.py b/tb/test_axis_crosspoint_64_4x4.py new file mode 100755 index 000000000..ba91ecf6b --- /dev/null +++ b/tb/test_axis_crosspoint_64_4x4.py @@ -0,0 +1,488 @@ +#!/usr/bin/env python2 +""" + +Copyright (c) 2014 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. + +""" + +from myhdl import * +import os +from Queue import Queue + +import axis_ep + +module = 'axis_crosspoint_64_4x4' + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("test_%s.v" % module) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o test_%s.vvp %s" % (module, src) + +def dut_axis_crosspoint_64_4x4(clk, + rst, + current_test, + + input_0_axis_tdata, + input_0_axis_tkeep, + input_0_axis_tvalid, + input_0_axis_tlast, + input_1_axis_tdata, + input_1_axis_tkeep, + input_1_axis_tvalid, + input_1_axis_tlast, + input_2_axis_tdata, + input_2_axis_tkeep, + input_2_axis_tvalid, + input_2_axis_tlast, + input_3_axis_tdata, + input_3_axis_tkeep, + input_3_axis_tvalid, + input_3_axis_tlast, + + output_0_axis_tdata, + output_0_axis_tkeep, + output_0_axis_tvalid, + output_0_axis_tlast, + output_1_axis_tdata, + output_1_axis_tkeep, + output_1_axis_tvalid, + output_1_axis_tlast, + output_2_axis_tdata, + output_2_axis_tkeep, + output_2_axis_tvalid, + output_2_axis_tlast, + output_3_axis_tdata, + output_3_axis_tkeep, + output_3_axis_tvalid, + output_3_axis_tlast, + + output_0_select, + output_1_select, + output_2_select, + output_3_select): + + if os.system(build_cmd): + raise Exception("Error running build command") + return Cosimulation("vvp -m myhdl test_%s.vvp -lxt2" % module, + clk=clk, + rst=rst, + current_test=current_test, + + input_0_axis_tdata=input_0_axis_tdata, + input_0_axis_tkeep=input_0_axis_tkeep, + input_0_axis_tvalid=input_0_axis_tvalid, + input_0_axis_tlast=input_0_axis_tlast, + input_1_axis_tdata=input_1_axis_tdata, + input_1_axis_tkeep=input_1_axis_tkeep, + input_1_axis_tvalid=input_1_axis_tvalid, + input_1_axis_tlast=input_1_axis_tlast, + input_2_axis_tdata=input_2_axis_tdata, + input_2_axis_tkeep=input_2_axis_tkeep, + input_2_axis_tvalid=input_2_axis_tvalid, + input_2_axis_tlast=input_2_axis_tlast, + input_3_axis_tdata=input_3_axis_tdata, + input_3_axis_tkeep=input_3_axis_tkeep, + input_3_axis_tvalid=input_3_axis_tvalid, + input_3_axis_tlast=input_3_axis_tlast, + + output_0_axis_tdata=output_0_axis_tdata, + output_0_axis_tkeep=output_0_axis_tkeep, + output_0_axis_tvalid=output_0_axis_tvalid, + output_0_axis_tlast=output_0_axis_tlast, + output_1_axis_tdata=output_1_axis_tdata, + output_1_axis_tkeep=output_1_axis_tkeep, + output_1_axis_tvalid=output_1_axis_tvalid, + output_1_axis_tlast=output_1_axis_tlast, + output_2_axis_tdata=output_2_axis_tdata, + output_2_axis_tkeep=output_2_axis_tkeep, + output_2_axis_tvalid=output_2_axis_tvalid, + output_2_axis_tlast=output_2_axis_tlast, + output_3_axis_tdata=output_3_axis_tdata, + output_3_axis_tkeep=output_3_axis_tkeep, + output_3_axis_tvalid=output_3_axis_tvalid, + output_3_axis_tlast=output_3_axis_tlast, + + output_0_select=output_0_select, + output_1_select=output_1_select, + output_2_select=output_2_select, + output_3_select=output_3_select) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + input_0_axis_tdata = Signal(intbv(0)[64:]) + input_0_axis_tkeep = Signal(intbv(0)[8:]) + input_0_axis_tvalid = Signal(bool(0)) + input_0_axis_tlast = Signal(bool(0)) + input_1_axis_tdata = Signal(intbv(0)[64:]) + input_1_axis_tkeep = Signal(intbv(0)[8:]) + input_1_axis_tvalid = Signal(bool(0)) + input_1_axis_tlast = Signal(bool(0)) + input_2_axis_tdata = Signal(intbv(0)[64:]) + input_2_axis_tkeep = Signal(intbv(0)[8:]) + input_2_axis_tvalid = Signal(bool(0)) + input_2_axis_tlast = Signal(bool(0)) + input_3_axis_tdata = Signal(intbv(0)[64:]) + input_3_axis_tkeep = Signal(intbv(0)[8:]) + input_3_axis_tvalid = Signal(bool(0)) + input_3_axis_tlast = Signal(bool(0)) + + output_0_select = Signal(intbv(0)[2:]) + output_1_select = Signal(intbv(0)[2:]) + output_2_select = Signal(intbv(0)[2:]) + output_3_select = Signal(intbv(0)[2:]) + + # Outputs + output_0_axis_tdata = Signal(intbv(0)[64:]) + output_0_axis_tkeep = Signal(intbv(0)[8:]) + output_0_axis_tvalid = Signal(bool(0)) + output_0_axis_tlast = Signal(bool(0)) + output_1_axis_tdata = Signal(intbv(0)[64:]) + output_1_axis_tkeep = Signal(intbv(0)[8:]) + output_1_axis_tvalid = Signal(bool(0)) + output_1_axis_tlast = Signal(bool(0)) + output_2_axis_tdata = Signal(intbv(0)[64:]) + output_2_axis_tkeep = Signal(intbv(0)[8:]) + output_2_axis_tvalid = Signal(bool(0)) + output_2_axis_tlast = Signal(bool(0)) + output_3_axis_tdata = Signal(intbv(0)[64:]) + output_3_axis_tkeep = Signal(intbv(0)[8:]) + output_3_axis_tvalid = Signal(bool(0)) + output_3_axis_tlast = Signal(bool(0)) + + # sources and sinks + source_0_queue = Queue() + source_0_pause = Signal(bool(0)) + source_1_queue = Queue() + source_1_pause = Signal(bool(0)) + source_2_queue = Queue() + source_2_pause = Signal(bool(0)) + source_3_queue = Queue() + source_3_pause = Signal(bool(0)) + sink_0_queue = Queue() + sink_0_pause = Signal(bool(0)) + sink_1_queue = Queue() + sink_1_pause = Signal(bool(0)) + sink_2_queue = Queue() + sink_2_pause = Signal(bool(0)) + sink_3_queue = Queue() + sink_3_pause = Signal(bool(0)) + + source_0 = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_0_axis_tdata, + tkeep=input_0_axis_tkeep, + tvalid=input_0_axis_tvalid, + tlast=input_0_axis_tlast, + fifo=source_0_queue, + pause=source_0_pause, + name='source0') + source_1 = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_1_axis_tdata, + tkeep=input_1_axis_tkeep, + tvalid=input_1_axis_tvalid, + tlast=input_1_axis_tlast, + fifo=source_1_queue, + pause=source_1_pause, + name='source1') + source_2 = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_2_axis_tdata, + tkeep=input_2_axis_tkeep, + tvalid=input_2_axis_tvalid, + tlast=input_2_axis_tlast, + fifo=source_2_queue, + pause=source_2_pause, + name='source2') + source_3 = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_3_axis_tdata, + tkeep=input_3_axis_tkeep, + tvalid=input_3_axis_tvalid, + tlast=input_3_axis_tlast, + fifo=source_3_queue, + pause=source_3_pause, + name='source3') + + sink_0 = axis_ep.AXIStreamSink(clk, + rst, + tdata=output_0_axis_tdata, + tkeep=output_0_axis_tkeep, + tvalid=output_0_axis_tvalid, + tlast=output_0_axis_tlast, + fifo=sink_0_queue, + pause=sink_0_pause, + name='sink0') + sink_1 = axis_ep.AXIStreamSink(clk, + rst, + tdata=output_1_axis_tdata, + tkeep=output_1_axis_tkeep, + tvalid=output_1_axis_tvalid, + tlast=output_1_axis_tlast, + fifo=sink_1_queue, + pause=sink_1_pause, + name='sink1') + sink_2 = axis_ep.AXIStreamSink(clk, + rst, + tdata=output_2_axis_tdata, + tkeep=output_2_axis_tkeep, + tvalid=output_2_axis_tvalid, + tlast=output_2_axis_tlast, + fifo=sink_2_queue, + pause=sink_2_pause, + name='sink2') + sink_3 = axis_ep.AXIStreamSink(clk, + rst, + tdata=output_3_axis_tdata, + tkeep=output_3_axis_tkeep, + tvalid=output_3_axis_tvalid, + tlast=output_3_axis_tlast, + fifo=sink_3_queue, + pause=sink_3_pause, + name='sink3') + + # DUT + dut = dut_axis_crosspoint_64_4x4(clk, + rst, + current_test, + + input_0_axis_tdata, + input_0_axis_tkeep, + input_0_axis_tvalid, + input_0_axis_tlast, + input_1_axis_tdata, + input_1_axis_tkeep, + input_1_axis_tvalid, + input_1_axis_tlast, + input_2_axis_tdata, + input_2_axis_tkeep, + input_2_axis_tvalid, + input_2_axis_tlast, + input_3_axis_tdata, + input_3_axis_tkeep, + input_3_axis_tvalid, + input_3_axis_tlast, + + output_0_axis_tdata, + output_0_axis_tkeep, + output_0_axis_tvalid, + output_0_axis_tlast, + output_1_axis_tdata, + output_1_axis_tkeep, + output_1_axis_tvalid, + output_1_axis_tlast, + output_2_axis_tdata, + output_2_axis_tkeep, + output_2_axis_tvalid, + output_2_axis_tlast, + output_3_axis_tdata, + output_3_axis_tkeep, + output_3_axis_tvalid, + output_3_axis_tlast, + + output_0_select, + output_1_select, + output_2_select, + output_3_select) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + + yield clk.posedge + print("test 1: 0123 -> 0123") + current_test.next = 1 + + output_0_select.next = 0 + output_1_select.next = 1 + output_2_select.next = 2 + output_3_select.next = 3 + + test_frame0 = axis_ep.AXIStreamFrame('\x01\x00\x00\xFF\x01\x02\x03\x04') + test_frame1 = axis_ep.AXIStreamFrame('\x01\x01\x01\xFF\x01\x02\x03\x04') + test_frame2 = axis_ep.AXIStreamFrame('\x01\x02\x02\xFF\x01\x02\x03\x04') + test_frame3 = axis_ep.AXIStreamFrame('\x01\x03\x03\xFF\x01\x02\x03\x04') + source_0_queue.put(test_frame0) + source_1_queue.put(test_frame1) + source_2_queue.put(test_frame2) + source_3_queue.put(test_frame3) + yield clk.posedge + + while input_0_axis_tvalid or input_1_axis_tvalid or input_2_axis_tvalid or input_3_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rx_frame0 = None + if not sink_0_queue.empty(): + rx_frame0 = sink_0_queue.get() + + assert rx_frame0 == test_frame0 + + rx_frame1 = None + if not sink_1_queue.empty(): + rx_frame1 = sink_1_queue.get() + + assert rx_frame1 == test_frame1 + + rx_frame2 = None + if not sink_2_queue.empty(): + rx_frame2 = sink_2_queue.get() + + assert rx_frame2 == test_frame2 + + rx_frame3 = None + if not sink_3_queue.empty(): + rx_frame3 = sink_3_queue.get() + + assert rx_frame3 == test_frame3 + + yield delay(100) + + yield clk.posedge + print("test 2: 0123 -> 3210") + current_test.next = 2 + + output_0_select.next = 3 + output_1_select.next = 2 + output_2_select.next = 1 + output_3_select.next = 0 + + test_frame0 = axis_ep.AXIStreamFrame('\x02\x00\x03\xFF\x01\x02\x03\x04') + test_frame1 = axis_ep.AXIStreamFrame('\x02\x01\x02\xFF\x01\x02\x03\x04') + test_frame2 = axis_ep.AXIStreamFrame('\x02\x02\x01\xFF\x01\x02\x03\x04') + test_frame3 = axis_ep.AXIStreamFrame('\x02\x03\x00\xFF\x01\x02\x03\x04') + source_0_queue.put(test_frame0) + source_1_queue.put(test_frame1) + source_2_queue.put(test_frame2) + source_3_queue.put(test_frame3) + yield clk.posedge + + while input_0_axis_tvalid or input_1_axis_tvalid or input_2_axis_tvalid or input_3_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rx_frame0 = None + if not sink_0_queue.empty(): + rx_frame0 = sink_0_queue.get() + + assert rx_frame0 == test_frame3 + + rx_frame1 = None + if not sink_1_queue.empty(): + rx_frame1 = sink_1_queue.get() + + assert rx_frame1 == test_frame2 + + rx_frame2 = None + if not sink_2_queue.empty(): + rx_frame2 = sink_2_queue.get() + + assert rx_frame2 == test_frame1 + + rx_frame3 = None + if not sink_3_queue.empty(): + rx_frame3 = sink_3_queue.get() + + assert rx_frame3 == test_frame0 + + yield delay(100) + + yield clk.posedge + print("test 3: 0000 -> 0123") + current_test.next = 3 + + output_0_select.next = 0 + output_1_select.next = 0 + output_2_select.next = 0 + output_3_select.next = 0 + + test_frame0 = axis_ep.AXIStreamFrame('\x03\x00\xFF\xFF\x01\x02\x03\x04') + source_0_queue.put(test_frame0) + yield clk.posedge + + while input_0_axis_tvalid or input_1_axis_tvalid or input_2_axis_tvalid or input_3_axis_tvalid: + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rx_frame0 = None + if not sink_0_queue.empty(): + rx_frame0 = sink_0_queue.get() + + assert rx_frame0 == test_frame0 + + rx_frame1 = None + if not sink_1_queue.empty(): + rx_frame1 = sink_1_queue.get() + + assert rx_frame1 == test_frame0 + + rx_frame2 = None + if not sink_2_queue.empty(): + rx_frame2 = sink_2_queue.get() + + assert rx_frame2 == test_frame0 + + rx_frame3 = None + if not sink_3_queue.empty(): + rx_frame3 = sink_3_queue.get() + + assert rx_frame3 == test_frame0 + + yield delay(100) + + raise StopSimulation + + return dut, source_0, source_1, source_2, source_3, sink_0, sink_1, sink_2, sink_3, clkgen, check + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/tb/test_axis_crosspoint_64_4x4.v b/tb/test_axis_crosspoint_64_4x4.v new file mode 100644 index 000000000..a302ceef8 --- /dev/null +++ b/tb/test_axis_crosspoint_64_4x4.v @@ -0,0 +1,170 @@ +/* + +Copyright (c) 2014 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 1 ns / 1 ps + +module test_axis_crosspoint_64_4x4; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [63:0] input_0_axis_tdata = 0; +reg [7:0] input_0_axis_tkeep = 0; +reg input_0_axis_tvalid = 0; +reg input_0_axis_tlast = 0; +reg [63:0] input_1_axis_tdata = 0; +reg [7:0] input_1_axis_tkeep = 0; +reg input_1_axis_tvalid = 0; +reg input_1_axis_tlast = 0; +reg [63:0] input_2_axis_tdata = 0; +reg [7:0] input_2_axis_tkeep = 0; +reg input_2_axis_tvalid = 0; +reg input_2_axis_tlast = 0; +reg [63:0] input_3_axis_tdata = 0; +reg [7:0] input_3_axis_tkeep = 0; +reg input_3_axis_tvalid = 0; +reg input_3_axis_tlast = 0; + +reg [1:0] output_0_select = 0; +reg [1:0] output_1_select = 0; +reg [1:0] output_2_select = 0; +reg [1:0] output_3_select = 0; + +// Outputs +wire [63:0] output_0_axis_tdata; +wire [7:0] output_0_axis_tkeep; +wire output_0_axis_tvalid; +wire output_0_axis_tlast; +wire [63:0] output_1_axis_tdata; +wire [7:0] output_1_axis_tkeep; +wire output_1_axis_tvalid; +wire output_1_axis_tlast; +wire [63:0] output_2_axis_tdata; +wire [7:0] output_2_axis_tkeep; +wire output_2_axis_tvalid; +wire output_2_axis_tlast; +wire [63:0] output_3_axis_tdata; +wire [7:0] output_3_axis_tkeep; +wire output_3_axis_tvalid; +wire output_3_axis_tlast; + +initial begin + // myhdl integration + $from_myhdl(clk, + rst, + current_test, + input_0_axis_tdata, + input_0_axis_tkeep, + input_0_axis_tvalid, + input_0_axis_tlast, + input_1_axis_tdata, + input_1_axis_tkeep, + input_1_axis_tvalid, + input_1_axis_tlast, + input_2_axis_tdata, + input_2_axis_tkeep, + input_2_axis_tvalid, + input_2_axis_tlast, + input_3_axis_tdata, + input_3_axis_tkeep, + input_3_axis_tvalid, + input_3_axis_tlast, + output_0_select, + output_1_select, + output_2_select, + output_3_select); + $to_myhdl(output_0_axis_tdata, + output_0_axis_tkeep, + output_0_axis_tvalid, + output_0_axis_tlast, + output_1_axis_tdata, + output_1_axis_tkeep, + output_1_axis_tvalid, + output_1_axis_tlast, + output_2_axis_tdata, + output_2_axis_tkeep, + output_2_axis_tvalid, + output_2_axis_tlast, + output_3_axis_tdata, + output_3_axis_tkeep, + output_3_axis_tvalid, + output_3_axis_tlast); + + // dump file + $dumpfile("test_axis_crosspoint_64_4x4.lxt"); + $dumpvars(0, test_axis_crosspoint_64_4x4); +end + +axis_crosspoint_64_4x4 #( + .DATA_WIDTH(64) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI inputs + .input_0_axis_tdata(input_0_axis_tdata), + .input_0_axis_tkeep(input_0_axis_tkeep), + .input_0_axis_tvalid(input_0_axis_tvalid), + .input_0_axis_tlast(input_0_axis_tlast), + .input_1_axis_tdata(input_1_axis_tdata), + .input_1_axis_tkeep(input_1_axis_tkeep), + .input_1_axis_tvalid(input_1_axis_tvalid), + .input_1_axis_tlast(input_1_axis_tlast), + .input_2_axis_tdata(input_2_axis_tdata), + .input_2_axis_tkeep(input_2_axis_tkeep), + .input_2_axis_tvalid(input_2_axis_tvalid), + .input_2_axis_tlast(input_2_axis_tlast), + .input_3_axis_tdata(input_3_axis_tdata), + .input_3_axis_tkeep(input_3_axis_tkeep), + .input_3_axis_tvalid(input_3_axis_tvalid), + .input_3_axis_tlast(input_3_axis_tlast), + // AXI outputs + .output_0_axis_tdata(output_0_axis_tdata), + .output_0_axis_tkeep(output_0_axis_tkeep), + .output_0_axis_tvalid(output_0_axis_tvalid), + .output_0_axis_tlast(output_0_axis_tlast), + .output_1_axis_tdata(output_1_axis_tdata), + .output_1_axis_tkeep(output_1_axis_tkeep), + .output_1_axis_tvalid(output_1_axis_tvalid), + .output_1_axis_tlast(output_1_axis_tlast), + .output_2_axis_tdata(output_2_axis_tdata), + .output_2_axis_tkeep(output_2_axis_tkeep), + .output_2_axis_tvalid(output_2_axis_tvalid), + .output_2_axis_tlast(output_2_axis_tlast), + .output_3_axis_tdata(output_3_axis_tdata), + .output_3_axis_tkeep(output_3_axis_tkeep), + .output_3_axis_tvalid(output_3_axis_tvalid), + .output_3_axis_tlast(output_3_axis_tlast), + // Control + .output_0_select(output_0_select), + .output_1_select(output_1_select), + .output_2_select(output_2_select), + .output_3_select(output_3_select) +); + +endmodule