From 6100e3ad78d574ec1c2dfd720b9e6495183ad128 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Tue, 16 Jul 2019 00:42:49 -0700 Subject: [PATCH] Add RX checksum module and testbench --- fpga/common/rtl/rx_checksum.v | 198 ++++++++++++++++++ fpga/common/tb/test_rx_checksum.py | 322 +++++++++++++++++++++++++++++ fpga/common/tb/test_rx_checksum.v | 97 +++++++++ 3 files changed, 617 insertions(+) create mode 100644 fpga/common/rtl/rx_checksum.v create mode 100755 fpga/common/tb/test_rx_checksum.py create mode 100644 fpga/common/tb/test_rx_checksum.v diff --git a/fpga/common/rtl/rx_checksum.v b/fpga/common/rtl/rx_checksum.v new file mode 100644 index 000000000..e3fef14a2 --- /dev/null +++ b/fpga/common/rtl/rx_checksum.v @@ -0,0 +1,198 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Receive checksum offload module + */ +module rx_checksum # +( + parameter DATA_WIDTH = 256, + parameter KEEP_WIDTH = (DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + input wire s_axis_tlast, + + /* + * Checksum output + */ + output wire [15:0] m_axis_csum, + output wire m_axis_csum_valid +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 256) begin + $error("Error: AXI stream interface width must be 256"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: AXI stream interface requires byte (8-bit) granularity"); + $finish; + end +end + +reg [KEEP_WIDTH-1:0] mask_reg = 32'hffffc000; +reg [DATA_WIDTH-1:0] s_axis_tdata_masked; + +reg [16:0] sum_1_1_reg = 0; +reg [16:0] sum_1_2_reg = 0; +reg [16:0] sum_1_3_reg = 0; +reg [16:0] sum_1_4_reg = 0; +reg [16:0] sum_1_5_reg = 0; +reg [16:0] sum_1_6_reg = 0; +reg [16:0] sum_1_7_reg = 0; +reg [16:0] sum_1_8_reg = 0; +reg sum_1_valid_reg = 1'b0; +reg sum_1_last_reg = 1'b0; + +reg [17:0] sum_2_1_reg = 0; +reg [17:0] sum_2_2_reg = 0; +reg [17:0] sum_2_3_reg = 0; +reg [17:0] sum_2_4_reg = 0; +reg sum_2_valid_reg = 1'b0; +reg sum_2_last_reg = 1'b0; + +reg [18:0] sum_3_1_reg = 0; +reg [18:0] sum_3_2_reg = 0; +reg sum_3_valid_reg = 1'b0; +reg sum_3_last_reg = 1'b0; + +reg [19:0] sum_4_reg = 0; +reg sum_4_valid_reg = 1'b0; +reg sum_4_last_reg = 1'b0; + +reg [20:0] sum_5_temp = 0; +reg [15:0] sum_5_reg = 0; + +reg [15:0] m_axis_csum_reg = 0; +reg m_axis_csum_valid_reg = 1'b0; + +assign m_axis_csum = m_axis_csum_reg; +assign m_axis_csum_valid = m_axis_csum_valid_reg; + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < KEEP_WIDTH; j = j + 1) begin + s_axis_tdata_masked[j*8 +: 8] = (s_axis_tkeep[j] && mask_reg[j]) ? s_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +always @(posedge clk) begin + sum_1_valid_reg <= 1'b0; + sum_2_valid_reg <= 1'b0; + sum_3_valid_reg <= 1'b0; + sum_4_valid_reg <= 1'b0; + m_axis_csum_valid_reg <= 1'b0; + + if (s_axis_tvalid) begin + sum_1_1_reg <= {s_axis_tdata_masked[ 0*8 +: 8], s_axis_tdata_masked[ 1*8 +: 8]} + {s_axis_tdata_masked[ 2*8 +: 8], s_axis_tdata_masked[ 3*8 +: 8]}; + sum_1_2_reg <= {s_axis_tdata_masked[ 4*8 +: 8], s_axis_tdata_masked[ 5*8 +: 8]} + {s_axis_tdata_masked[ 6*8 +: 8], s_axis_tdata_masked[ 7*8 +: 8]}; + sum_1_3_reg <= {s_axis_tdata_masked[ 8*8 +: 8], s_axis_tdata_masked[ 9*8 +: 8]} + {s_axis_tdata_masked[10*8 +: 8], s_axis_tdata_masked[11*8 +: 8]}; + sum_1_4_reg <= {s_axis_tdata_masked[12*8 +: 8], s_axis_tdata_masked[13*8 +: 8]} + {s_axis_tdata_masked[14*8 +: 8], s_axis_tdata_masked[15*8 +: 8]}; + sum_1_5_reg <= {s_axis_tdata_masked[16*8 +: 8], s_axis_tdata_masked[17*8 +: 8]} + {s_axis_tdata_masked[18*8 +: 8], s_axis_tdata_masked[19*8 +: 8]}; + sum_1_6_reg <= {s_axis_tdata_masked[20*8 +: 8], s_axis_tdata_masked[21*8 +: 8]} + {s_axis_tdata_masked[22*8 +: 8], s_axis_tdata_masked[23*8 +: 8]}; + sum_1_7_reg <= {s_axis_tdata_masked[24*8 +: 8], s_axis_tdata_masked[25*8 +: 8]} + {s_axis_tdata_masked[26*8 +: 8], s_axis_tdata_masked[27*8 +: 8]}; + sum_1_8_reg <= {s_axis_tdata_masked[28*8 +: 8], s_axis_tdata_masked[29*8 +: 8]} + {s_axis_tdata_masked[30*8 +: 8], s_axis_tdata_masked[31*8 +: 8]}; + sum_1_valid_reg <= 1'b1; + sum_1_last_reg <= s_axis_tlast; + + if (s_axis_tlast) begin + mask_reg <= 32'hffffc000; + end else begin + mask_reg <= {KEEP_WIDTH{1'b1}}; + end + end + + if (sum_1_valid_reg) begin + sum_2_1_reg <= sum_1_1_reg + sum_1_2_reg; + sum_2_2_reg <= sum_1_3_reg + sum_1_4_reg; + sum_2_3_reg <= sum_1_5_reg + sum_1_6_reg; + sum_2_4_reg <= sum_1_7_reg + sum_1_8_reg; + sum_2_valid_reg <= 1'b1; + sum_2_last_reg <= sum_1_last_reg; + end + + if (sum_2_valid_reg) begin + sum_3_1_reg <= sum_2_1_reg + sum_2_2_reg; + sum_3_2_reg <= sum_2_3_reg + sum_2_4_reg; + sum_3_valid_reg <= 1'b1; + sum_3_last_reg <= sum_2_last_reg; + end + + if (sum_3_valid_reg) begin + sum_4_reg <= sum_3_1_reg + sum_3_2_reg; + sum_4_valid_reg <= 1'b1; + sum_4_last_reg <= sum_3_last_reg; + end + + if (sum_4_valid_reg) begin + sum_5_temp = sum_4_reg + sum_5_reg; + sum_5_temp = sum_5_temp[15:0] + sum_5_temp[20:16]; + sum_5_temp = sum_5_temp[15:0] + sum_5_temp[16]; + + if (sum_4_last_reg) begin + m_axis_csum_reg <= sum_5_temp; + m_axis_csum_valid_reg <= 1'b1; + sum_5_reg <= 0; + end else begin + sum_5_reg <= sum_5_temp; + end + end + + if (rst) begin + mask_reg <= 32'hffffc000; + sum_1_valid_reg <= 1'b0; + sum_2_valid_reg <= 1'b0; + sum_3_valid_reg <= 1'b0; + sum_4_valid_reg <= 1'b0; + m_axis_csum_valid_reg <= 1'b0; + end +end + +endmodule diff --git a/fpga/common/tb/test_rx_checksum.py b/fpga/common/tb/test_rx_checksum.py new file mode 100755 index 000000000..96f9dcc33 --- /dev/null +++ b/fpga/common/tb/test_rx_checksum.py @@ -0,0 +1,322 @@ +#!/usr/bin/env python +""" + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep + +module = 'rx_checksum' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def frame_checksum(frame): + data = bytearray() + if isinstance(frame, eth_ep.EthFrame): + data = frame.payload.data + elif isinstance(frame, axis_ep.AXIStreamFrame): + data = frame.data[14:] + else: + return None + + csum = 0 + odd = False + + for b in data: + if odd: + csum += b + else: + csum += b << 8 + odd = not odd + + csum = (csum & 0xffff) + (csum >> 16) + csum = (csum & 0xffff) + (csum >> 16) + + return csum + +def bench(): + + # Parameters + DATA_WIDTH = 256 + KEEP_WIDTH = (DATA_WIDTH/8) + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + + # Outputs + m_axis_csum = Signal(intbv(0)[16:]) + m_axis_csum_valid = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tlast=s_axis_tlast, + pause=source_pause, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=(m_axis_csum,), + tvalid=m_axis_csum_valid, + name='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, + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tlast=s_axis_tlast, + m_axis_csum=m_axis_csum, + m_axis_csum_valid=m_axis_csum_valid + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + def wait_normal(): + while s_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid: + yield clk.posedge + yield clk.posedge + source_pause.next = False + yield clk.posedge + source_pause.next = True + yield clk.posedge + + source_pause.next = False + + @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 + + for payload_len in list(range(1, 128)) + list([1024, 1500, 9000, 9214]): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray((x%256 for x in range(payload_len))) + + axis_frame = test_frame.build_axis() + + for wait in wait_normal, wait_pause_source: + source.send(axis_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_csum = sink.recv().data[0][0] + print(hex(rx_csum)) + + csum = frame_checksum(test_frame) + print(hex(csum)) + + assert rx_csum == csum + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray((x%256 for x in range(payload_len))) + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray((~x%256 for x in range(payload_len))) + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + for wait in wait_normal, wait_pause_source: + source.send(axis_frame1) + source.send(axis_frame2) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_csum = sink.recv().data[0][0] + print(hex(rx_csum)) + + csum = frame_checksum(test_frame1) + print(hex(csum)) + + assert rx_csum == csum + + yield sink.wait() + rx_csum = sink.recv().data[0][0] + print(hex(rx_csum)) + + csum = frame_checksum(test_frame2) + print(hex(csum)) + + assert rx_csum == csum + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: overflow test") + current_test.next = 3 + + axis_frame = axis_ep.AXIStreamFrame(bytearray([0xff]*10240)) + + for wait in wait_normal, wait_pause_source: + source.send(axis_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_csum = sink.recv().data[0][0] + print(hex(rx_csum)) + + csum = frame_checksum(axis_frame) + print(hex(csum)) + + assert rx_csum == csum + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: checksum test") + current_test.next = 4 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDA0203040506 + test_frame.eth_src_mac = 0xCA0203040506 + test_frame.eth_type = 0x005a + test_frame.payload = b'\xab\xcd'+bytearray(range(20, 108)) + + axis_frame = test_frame.build_axis() + + for wait in wait_normal, wait_pause_source: + source.send(axis_frame) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink.wait() + rx_csum = sink.recv().data[0][0] + print(hex(rx_csum)) + + csum = frame_checksum(test_frame) + print(hex(csum)) + + assert csum == 0x8ad8 + assert rx_csum == csum + + assert sink.empty() + + 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/fpga/common/tb/test_rx_checksum.v b/fpga/common/tb/test_rx_checksum.v new file mode 100644 index 000000000..cfcd4128d --- /dev/null +++ b/fpga/common/tb/test_rx_checksum.v @@ -0,0 +1,97 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Testbench for rx_checksum + */ +module test_rx_checksum; + +// Parameters +parameter DATA_WIDTH = 256; +parameter KEEP_WIDTH = (DATA_WIDTH/8); + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; + +// Outputs +wire [15:0] m_axis_csum; +wire m_axis_csum_valid; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast + ); + $to_myhdl( + m_axis_csum, + m_axis_csum_valid + ); + + // dump file + $dumpfile("test_rx_checksum.lxt"); + $dumpvars(0, test_rx_checksum); +end + +rx_checksum #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tlast(s_axis_tlast), + .m_axis_csum(m_axis_csum), + .m_axis_csum_valid(m_axis_csum_valid) +); + +endmodule