diff --git a/rtl/pcie_tag_manager.v b/rtl/pcie_tag_manager.v new file mode 100644 index 0000000..e7d0eda --- /dev/null +++ b/rtl/pcie_tag_manager.v @@ -0,0 +1,168 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * PCIe tag manager + */ +module pcie_tag_manager # +( + parameter PCIE_TAG_WIDTH = 8, + parameter PCIE_TAG_COUNT = 256, + parameter PCIE_EXT_TAG_ENABLE = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXIS tag output + */ + output wire [PCIE_TAG_WIDTH-1:0] m_axis_tag, + output wire m_axis_tag_valid, + input wire m_axis_tag_ready, + + /* + * AXIS tag output + */ + input wire [PCIE_TAG_WIDTH-1:0] s_axis_tag, + input wire s_axis_tag_valid, + + /* + * Configuration + */ + input wire ext_tag_enable, + + /* + * Status + */ + output wire [PCIE_TAG_COUNT-1:0] active_tags +); + +// parameter assertions +initial begin + if (PCIE_TAG_WIDTH < $clog2(PCIE_TAG_COUNT)) begin + $error("Error: PCIe tag width insufficient for requested tag count"); + $finish; + end + + if (PCIE_TAG_COUNT < 1 || PCIE_TAG_COUNT > 256) begin + $error("Error: PCIe tag count must be between 1 and 256"); + $finish; + end + + if (PCIE_TAG_COUNT > 32 && !PCIE_EXT_TAG_ENABLE) begin + $warning("Warning: PCIe tag count set larger than 32, but extended tag support is disabled"); + end + + if (PCIE_TAG_COUNT <= 32 && PCIE_EXT_TAG_ENABLE) begin + $warning("Warning: PCIe tag count set to 32 or less, but extended tag support is enabled"); + end +end + +reg [PCIE_TAG_COUNT-1:0] tag_active_reg = {PCIE_TAG_COUNT{1'b0}}, tag_active_next; +reg [PCIE_TAG_COUNT-1:0] tag_mask_reg = {PCIE_TAG_COUNT{1'b0}}, tag_mask_next; + +reg [PCIE_TAG_WIDTH-1:0] m_axis_tag_reg = {PCIE_TAG_WIDTH{1'b0}}, m_axis_tag_next; +reg m_axis_tag_valid_reg = 1'b0, m_axis_tag_valid_next; + +assign m_axis_tag = m_axis_tag_reg; +assign m_axis_tag_valid = m_axis_tag_valid_reg; + +assign active_tags = tag_active_reg; + +wire tag_valid; +wire [PCIE_TAG_WIDTH-1:0] tag_index; + +priority_encoder #( + .WIDTH(PCIE_TAG_COUNT), + .LSB_PRIORITY("HIGH") +) +priority_encoder_inst ( + .input_unencoded(~tag_active_reg & (ext_tag_enable && PCIE_EXT_TAG_ENABLE ? {256{1'b1}} : {32{1'b1}})), + .output_valid(tag_valid), + .output_encoded(tag_index), + .output_unencoded() +); + +wire masked_tag_valid; +wire [PCIE_TAG_WIDTH-1:0] masked_tag_index; + +priority_encoder #( + .WIDTH(PCIE_TAG_COUNT), + .LSB_PRIORITY("HIGH") +) +priority_encoder_masked ( + .input_unencoded(~tag_active_reg & tag_mask_reg & (ext_tag_enable && PCIE_EXT_TAG_ENABLE ? {256{1'b1}} : {32{1'b1}})), + .output_valid(masked_tag_valid), + .output_encoded(masked_tag_index), + .output_unencoded() +); + +always @* begin + tag_active_next = tag_active_reg; + tag_mask_next = tag_mask_reg; + + m_axis_tag_next = m_axis_tag_reg; + m_axis_tag_valid_next = m_axis_tag_valid_reg && !m_axis_tag_ready; + + if (s_axis_tag_valid) begin + tag_active_next[s_axis_tag] = 1'b0; + end + + if (!m_axis_tag_valid || m_axis_tag_ready) begin + if (tag_valid) begin + if (masked_tag_valid) begin + m_axis_tag_next = masked_tag_index; + m_axis_tag_valid_next = 1'b1; + tag_active_next[masked_tag_index] = 1'b1; + tag_mask_next = {PCIE_TAG_COUNT{1'b1}} << (masked_tag_index + 1); + end else begin + m_axis_tag_next = tag_index; + m_axis_tag_valid_next = 1'b1; + tag_active_next[tag_index] = 1'b1; + tag_mask_next = {PCIE_TAG_COUNT{1'b1}} << (tag_index + 1); + end + end + end +end + +always @(posedge clk) begin + if (rst) begin + tag_active_reg <= {PCIE_TAG_COUNT{1'b0}}; + tag_mask_reg <= {PCIE_TAG_COUNT{1'b0}}; + m_axis_tag_valid_reg <= 1'b0; + end else begin + tag_active_reg <= tag_active_next; + tag_mask_reg <= tag_mask_next; + m_axis_tag_valid_reg <= m_axis_tag_valid_next; + end + + m_axis_tag_reg <= m_axis_tag_next; +end + +endmodule diff --git a/tb/test_pcie_tag_manager.py b/tb/test_pcie_tag_manager.py new file mode 100755 index 0000000..144163d --- /dev/null +++ b/tb/test_pcie_tag_manager.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from myhdl import * +import os + +import axis_ep + +module = 'pcie_tag_manager' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + PCIE_TAG_WIDTH = 8 + PCIE_TAG_COUNT = 256 + PCIE_EXT_TAG_ENABLE = 1 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + m_axis_tag_ready = Signal(bool(0)) + s_axis_tag = Signal(intbv(0)[PCIE_TAG_WIDTH:]) + s_axis_tag_valid = Signal(bool(0)) + ext_tag_enable = Signal(bool(0)) + + # Outputs + m_axis_tag = Signal(intbv(0)[PCIE_TAG_WIDTH:]) + m_axis_tag_valid = Signal(bool(0)) + active_tags = Signal(intbv(0)[PCIE_TAG_COUNT:]) + + # sources and sinks + tag_sink_pause = Signal(bool(1)) + + tag_source = axis_ep.AXIStreamSource() + + tag_source_logic = tag_source.create_logic( + clk, + rst, + tdata=s_axis_tag, + tvalid=s_axis_tag_valid, + name='tag_source' + ) + + tag_sink = axis_ep.AXIStreamSink() + + tag_sink_logic = tag_sink.create_logic( + clk, + rst, + tdata=m_axis_tag, + tvalid=m_axis_tag_valid, + tready=m_axis_tag_ready, + pause=tag_sink_pause, + name='tag_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + m_axis_tag=m_axis_tag, + m_axis_tag_valid=m_axis_tag_valid, + m_axis_tag_ready=m_axis_tag_ready, + s_axis_tag=s_axis_tag, + s_axis_tag_valid=s_axis_tag_valid, + ext_tag_enable=ext_tag_enable, + active_tags=active_tags + ) + + @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 + + # testbench stimulus + + ext_tag_enable.next = 0 + + yield clk.posedge + print("test 1: activate all tags") + current_test.next = 1 + + tag_sink_pause.next = 0 + + yield delay(300) + + tag_sink_pause.next = 1 + + for k in range(32): + assert tag_sink.recv().data[0] == k + + yield delay(100) + + yield clk.posedge + print("test 2: return and reissue some tags") + current_test.next = 2 + + for k in [2, 4, 6, 8]: + tag_source.send([k]) + + tag_sink_pause.next = 0 + + yield delay(100) + + tag_sink_pause.next = 1 + + for k in [2, 4, 6, 8]: + assert tag_sink.recv().data[0] == k + + yield delay(100) + + yield clk.posedge + print("test 3: activate all extended tags") + current_test.next = 3 + + rst.next = 1 + ext_tag_enable.next = 1 + yield clk.posedge + rst.next = 0 + + tag_sink_pause.next = 0 + + yield delay(2100) + + tag_sink_pause.next = 1 + + for k in range(256): + assert tag_sink.recv().data[0] == k + + yield delay(100) + + yield clk.posedge + print("test 4: return and reissue some tags") + current_test.next = 4 + + for k in [10, 20, 30, 40, 50, 60]: + tag_source.send([k]) + + tag_sink_pause.next = 0 + + yield delay(100) + + tag_sink_pause.next = 1 + + for k in [10, 20, 30, 40, 50, 60]: + assert tag_sink.recv().data[0] == k + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_pcie_tag_manager.v b/tb/test_pcie_tag_manager.v new file mode 100644 index 0000000..df9352e --- /dev/null +++ b/tb/test_pcie_tag_manager.v @@ -0,0 +1,93 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Testbench for pcie_tag_manager + */ +module test_pcie_tag_manager; + +// Parameters +parameter PCIE_TAG_WIDTH = 8; +parameter PCIE_TAG_COUNT = 256; +parameter PCIE_EXT_TAG_ENABLE = 1; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg m_axis_tag_ready = 0; +reg [PCIE_TAG_WIDTH-1:0] s_axis_tag = 0; +reg s_axis_tag_valid = 0; +reg ext_tag_enable = 0; + +// Outputs +wire [PCIE_TAG_WIDTH-1:0] m_axis_tag; +wire m_axis_tag_valid; +wire [PCIE_TAG_COUNT-1:0] active_tags; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + m_axis_tag_ready, + s_axis_tag, + s_axis_tag_valid, + ext_tag_enable + ); + $to_myhdl( + m_axis_tag, + m_axis_tag_valid, + active_tags + ); + + // dump file + $dumpfile("test_pcie_tag_manager.lxt"); + $dumpvars(0, test_pcie_tag_manager); +end + +pcie_tag_manager #( + .PCIE_TAG_WIDTH(PCIE_TAG_WIDTH), + .PCIE_TAG_COUNT(PCIE_TAG_COUNT), + .PCIE_EXT_TAG_ENABLE(PCIE_EXT_TAG_ENABLE) +) +UUT ( + .clk(clk), + .rst(rst), + .m_axis_tag(m_axis_tag), + .m_axis_tag_valid(m_axis_tag_valid), + .m_axis_tag_ready(m_axis_tag_ready), + .s_axis_tag(s_axis_tag), + .s_axis_tag_valid(s_axis_tag_valid), + .ext_tag_enable(ext_tag_enable), + .active_tags(active_tags) +); + +endmodule