From 918ef8f76caa46b60cc5d92e5eca5ab553e625d5 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Sat, 8 Nov 2014 00:23:23 -0800 Subject: [PATCH] Add AXI async FIFO and testbench --- rtl/axis_async_fifo.v | 149 ++++++++++++ rtl/axis_async_fifo_64.v | 152 ++++++++++++ tb/test_axis_async_fifo.py | 412 +++++++++++++++++++++++++++++++++ tb/test_axis_async_fifo.v | 97 ++++++++ tb/test_axis_async_fifo_64.py | 422 ++++++++++++++++++++++++++++++++++ tb/test_axis_async_fifo_64.v | 103 +++++++++ 6 files changed, 1335 insertions(+) create mode 100644 rtl/axis_async_fifo.v create mode 100644 rtl/axis_async_fifo_64.v create mode 100755 tb/test_axis_async_fifo.py create mode 100644 tb/test_axis_async_fifo.v create mode 100755 tb/test_axis_async_fifo_64.py create mode 100644 tb/test_axis_async_fifo_64.v diff --git a/rtl/axis_async_fifo.v b/rtl/axis_async_fifo.v new file mode 100644 index 000000000..f52e04121 --- /dev/null +++ b/rtl/axis_async_fifo.v @@ -0,0 +1,149 @@ +/* + +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_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, + output wire output_axis_tuser +); + +reg [ADDR_WIDTH:0] wr_ptr = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next; +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, 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_tuser, 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; + +wire write = input_axis_tvalid & ~full; +wire read = (output_axis_tready | ~output_axis_tvalid_reg) & ~empty; + +assign {output_axis_tlast, output_axis_tuser, 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 + mem[wr_ptr[ADDR_WIDTH-1:0]] <= data_in; + wr_ptr_next = wr_ptr + 1; + wr_ptr <= wr_ptr_next; + wr_ptr_gray <= wr_ptr_next ^ (wr_ptr_next >> 1); + 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_fifo_64.v b/rtl/axis_async_fifo_64.v new file mode 100644 index 000000000..baf3bf406 --- /dev/null +++ b/rtl/axis_async_fifo_64.v @@ -0,0 +1,152 @@ +/* + +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_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, + output wire output_axis_tuser +); + +reg [ADDR_WIDTH:0] wr_ptr = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next; +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, 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_tuser, 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; + +wire write = input_axis_tvalid & ~full; +wire read = (output_axis_tready | ~output_axis_tvalid_reg) & ~empty; + +assign {output_axis_tlast, output_axis_tuser, 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 + mem[wr_ptr[ADDR_WIDTH-1:0]] <= data_in; + wr_ptr_next = wr_ptr + 1; + wr_ptr <= wr_ptr_next; + wr_ptr_gray <= wr_ptr_next ^ (wr_ptr_next >> 1); + 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_fifo.py b/tb/test_axis_async_fifo.py new file mode 100755 index 000000000..e6eb8a3c5 --- /dev/null +++ b/tb/test_axis_async_fifo.py @@ -0,0 +1,412 @@ +#!/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_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_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, + output_axis_tuser): + + 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, + output_axis_tuser=output_axis_tuser) + +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)) + 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(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, + tuser=output_axis_tuser, + fifo=sink_queue, + pause=sink_pause, + name='sink') + + # DUT + dut = dut_axis_async_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, + output_axis_tuser) + + @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 + + 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 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 + assert rx_frame.user[-1] + + 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_fifo.v b/tb/test_axis_async_fifo.v new file mode 100644 index 000000000..dbfbdd494 --- /dev/null +++ b/tb/test_axis_async_fifo.v @@ -0,0 +1,97 @@ +/* + +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_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; +wire output_axis_tuser; + +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, + output_axis_tuser); + + // dump file + $dumpfile("test_axis_async_fifo.lxt"); + $dumpvars(0, test_axis_async_fifo); +end + +axis_async_fifo #( + .ADDR_WIDTH(2), + .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), + .output_axis_tuser(output_axis_tuser) +); + +endmodule diff --git a/tb/test_axis_async_fifo_64.py b/tb/test_axis_async_fifo_64.py new file mode 100755 index 000000000..6b9eabeef --- /dev/null +++ b/tb/test_axis_async_fifo_64.py @@ -0,0 +1,422 @@ +#!/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_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_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, + output_axis_tuser): + + 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, + output_axis_tuser=output_axis_tuser) + +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)) + 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(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, + tuser=output_axis_tuser, + fifo=sink_queue, + pause=sink_pause, + name='sink') + + # DUT + dut = dut_axis_async_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, + output_axis_tuser) + + @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 + + 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 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 + assert rx_frame.user[-1] + + 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_fifo_64.v b/tb/test_axis_async_fifo_64.v new file mode 100644 index 000000000..6741debeb --- /dev/null +++ b/tb/test_axis_async_fifo_64.v @@ -0,0 +1,103 @@ +/* + +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_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; +wire output_axis_tuser; + +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, + output_axis_tuser); + + // dump file + $dumpfile("test_axis_async_fifo_64.lxt"); + $dumpvars(0, test_axis_async_fifo_64); +end + +axis_async_fifo_64 #( + .ADDR_WIDTH(2), + .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), + .output_axis_tuser(output_axis_tuser) +); + +endmodule