From 232464c7f7d8265c9df987916471cc989d5c2fe9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20B=2E=20Preu=C3=9Fer?= <thomas.preusser@xilinx.com>
Date: Wed, 4 May 2022 17:02:47 +0100
Subject: [PATCH] Prototype for checksumming over stream data.

---
 finn-rtllib/checksum/checksum.cpp   |  51 +++++++++++
 finn-rtllib/checksum/checksum.hpp   | 114 +++++++++++++++++++++++
 finn-rtllib/checksum/checksum_tb.sv | 136 ++++++++++++++++++++++++++++
 3 files changed, 301 insertions(+)
 create mode 100644 finn-rtllib/checksum/checksum.cpp
 create mode 100644 finn-rtllib/checksum/checksum.hpp
 create mode 100644 finn-rtllib/checksum/checksum_tb.sv

diff --git a/finn-rtllib/checksum/checksum.cpp b/finn-rtllib/checksum/checksum.cpp
new file mode 100644
index 000000000..494adee85
--- /dev/null
+++ b/finn-rtllib/checksum/checksum.cpp
@@ -0,0 +1,51 @@
+/******************************************************************************
+ *  Copyright (c) 2022, Xilinx, Inc.
+ *  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.
+ *
+ *  3.  Neither the name of the copyright holder nor the names of its
+ *      contributors may be used to endorse or promote products derived from
+ *      this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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.
+ *
+ * @brief	Validation top-level module for checksum component.
+ * @author	Thomas B. Preußer <tpreusse@amd.com>
+ *
+ *******************************************************************************/
+#include "checksum.hpp"
+
+
+using  T = ap_uint<32>;
+
+void checksum_top(
+	hls::stream<T> &src,
+	hls::stream<T> &dst,
+	ap_uint<32>    &chk
+) {
+#pragma HLS interface port=src axis
+#pragma HLS interface port=dst axis
+#pragma HLS interface port=chk ap_ovld
+#pragma HLS interface port=return ap_ctrl_none
+#pragma HLS dataflow
+	checksum<60, 4>(src, dst, chk);
+}
diff --git a/finn-rtllib/checksum/checksum.hpp b/finn-rtllib/checksum/checksum.hpp
new file mode 100644
index 000000000..ed45b5d57
--- /dev/null
+++ b/finn-rtllib/checksum/checksum.hpp
@@ -0,0 +1,114 @@
+/******************************************************************************
+ *  Copyright (c) 2022, Xilinx, Inc.
+ *  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.
+ *
+ *  3.  Neither the name of the copyright holder nor the names of its
+ *      contributors may be used to endorse or promote products derived from
+ *      this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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.
+ *
+ * @brief	Checksum over stream-carried data frames.
+ * @author	Thomas B. Preußer <tpreusse@amd.com>
+ *
+ *******************************************************************************/
+#include <hls_stream.h>
+#include <ap_int.h>
+
+
+/**
+ * Computes a checksum over a forwarded stream assumed to carry frames of
+ * N words further subdivided into K subwords.
+ *	- Subword slicing can be customized typically by using a lambda.
+ *	  The provided DefaultSubwordSlicer assumes an `ap_(u)int`-like word
+ *	  type with a member `width` and a range-based slicing operator. It
+ *	  further assumes a little-endian arrangement of subwords within words
+ *	  for the canonical subword stream order. 
+ *	- Subwords wider than 23 bits are folded using bitwise XOR across
+ *	  slices of 23 bits starting from the LSB.
+ *	- The folded subword values are weighted according to their position
+ *	  in the stream relative to the start of frame by a periodic weight
+ *	  sequence 1, 2, 3, ...
+ *	- The weighted folded subword values are reduced to a checksum by an
+ *	  accumulation module 2^24.
+ *	- A checksum is emitted for each completed frame. It is the concatenation
+ *	  of an 8-bit (modulo 256) frame counter and the 24-bit frame checksum.
+ */
+template<typename T, unsigned K> class DefaultSubwordSlicer {
+	static_assert(T::width%K == 0, "Word size must be subword multiple.");
+	static constexpr unsigned  W = T::width/K;
+public:
+	ap_uint<W> operator()(T const &x, unsigned const  j) const {
+#pragma HLS inline
+		return  x((j+1)*W-1, j*W);
+	}
+};
+
+template<
+  unsigned N,	// number of data words in a frame
+  unsigned K,	// subword count per data word
+  typename T,	// type of stream-carried data words
+  typename F = DefaultSubwordSlicer<T, K>	// f(T(), j) to extract subwords
+>
+void checksum(
+	hls::stream<T> &src,
+	hls::stream<T> &dst,
+	ap_uint<32>    &chk,
+	F&& f = F()
+) {
+	ap_uint<2>  coeff[3] = { 1, 2, 3 };
+	ap_uint<24>  s = 0;
+
+	for(unsigned  i = 0; i < N; i++) {
+#pragma HLS pipeline II=1 style=flp
+		T const  x = src.read();
+
+		// Pass-thru copy
+		dst.write(x);
+
+		// Actual checksum update
+		for(unsigned  j = 0; j < K; j++) {
+#pragma HLS unroll
+			auto const   v0 = f(x, j);
+			constexpr unsigned  W = 1 + (decltype(v0)::width-1)/23;
+			ap_uint<K*23>  v = v0;
+			ap_uint<  23>  w = 0;
+			for(unsigned  k = 0; k < W; k++) {
+				w ^= v(23*k+22, 23*k);
+			}
+			s += (coeff[j%3][1]? (w, ap_uint<1>(0)) : ap_uint<24>(0)) + (coeff[j%3][0]? w : ap_uint<23>(0));
+		}
+
+		// Re-align coefficients
+		for(unsigned  j = 0; j < 3; j++) {
+#pragma HLS unroll
+			ap_uint<3> const  cc = coeff[j] + ap_uint<3>(K%3);
+			coeff[j] = cc(1, 0) + cc[2];
+		}
+	}
+
+	// Frame counter & output
+	static ap_uint<8>  cnt = 0;
+#pragma HLS reset variable=cnt
+	chk = (cnt++, s);
+}
diff --git a/finn-rtllib/checksum/checksum_tb.sv b/finn-rtllib/checksum/checksum_tb.sv
new file mode 100644
index 000000000..cec4e1b5b
--- /dev/null
+++ b/finn-rtllib/checksum/checksum_tb.sv
@@ -0,0 +1,136 @@
+/******************************************************************************
+ *  Copyright (c) 2022, Xilinx, Inc.
+ *  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.
+ *
+ *  3.  Neither the name of the copyright holder nor the names of its
+ *      contributors may be used to endorse or promote products derived from
+ *      this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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.
+ *
+ * @brief	Testbench for checksum component.
+ * @author	Thomas B. Preußer <tpreusse@amd.com>
+ *
+ *******************************************************************************/
+module checksum_tb;
+
+	//-----------------------------------------------------------------------
+	// Global Control
+	logic  clk = 0;
+	always #5ns clk = !clk;
+	logic  rst;
+
+	//-----------------------------------------------------------------------
+	// DUT
+	localparam int unsigned  N = 60;	// words per frame
+	localparam int unsigned  K = 4;		// subwords per word
+	localparam int unsigned  W = 8;		// subword size
+
+	logic [K-1:0][W-1:0]  src_TDATA;
+	logic  src_TVALID;
+	uwire  src_TREADY;
+
+	uwire [K-1:0][W-1:0]  dst_TDATA;
+	uwire  dst_TVALID;
+	logic  dst_TREADY;
+
+	uwire [31:0]  chk;
+	uwire         chk_vld;
+
+	checksum_top dut (
+		.ap_clk(clk), .ap_rst_n(!rst),
+		.src_TDATA, .src_TVALID, .src_TREADY,
+		.dst_TDATA, .dst_TVALID, .dst_TREADY,
+		.chk, .chk_ap_vld(chk_vld),
+		.ap_local_block(), .ap_local_deadlock()
+	);
+
+	//-----------------------------------------------------------------------
+	// Stimulus
+	logic [K-1:0][W-1:0]  Bypass  [$] = {};
+	logic [31:0]          Checksum[$] = {};
+	initial begin
+		src_TDATA  = 'x;
+		src_TVALID =  0;
+
+		rst = 1;
+		repeat(9) @(posedge clk);
+		rst <= 0;
+
+		for(int unsigned  r = 0; r < 311; r++) begin
+			automatic logic [23:0]  sum = 0;
+			src_TVALID <= 1;
+			for(int unsigned  i = 0; i < N; i++) begin
+				for(int unsigned  k = 0; k < K; k++) begin
+					automatic logic [W-1:0]  v = $urandom()>>17;
+					src_TDATA[k] <= v;
+					sum += ((K*i+k)%3 + 1) * v;
+				end
+				@(posedge clk iff src_TREADY);
+				Bypass.push_back(src_TDATA);
+			end
+			src_TVALID <= 0;
+			$display("Expect: %02x:%06x", r[7:0], sum);
+			Checksum.push_back({r, sum});
+		end
+
+		repeat(8) @(posedge clk);
+		$finish;
+	end
+
+	//-----------------------------------------------------------------------
+	// Output Validation
+
+	// Drain and check pass-thru stream
+	assign	dst_TREADY = 1;
+	always_ff @(posedge clk iff dst_TVALID) begin
+		assert(Bypass.size()) begin
+			automatic logic [K-1:0][W-1:0]  exp = Bypass.pop_front();
+			assert(dst_TDATA === exp) else begin
+				$error("Unexpected output %0x instead of %0x.", dst_TDATA, exp);
+				$stop;
+			end
+		end
+		else begin
+			$error("Spurious data output.");
+			$stop;
+		end
+	end
+
+	// Validate checksum reports
+	always_ff @(posedge clk iff chk_vld) begin
+		$display("Check:  %02x:%06x", chk[31:24], chk[23:0]);
+		assert(Checksum.size()) begin
+			automatic logic [31:0]  exp = Checksum.pop_front();
+			assert(chk === exp) else begin
+				$error("Unexpected checksum %0x instead of %0x.", chk, exp);
+				$stop;
+			end
+		end
+		else begin
+			$error("Spurious checksum output.");
+			$stop;
+		end
+	end
+
+endmodule : checksum_tb
-- 
GitLab