From 5220a5a2f715f9194f85281fdf0e3b414d91a193 Mon Sep 17 00:00:00 2001
From: Erik Kooistra <kooistra@astron.nl>
Date: Wed, 13 Jun 2018 14:31:43 +0000
Subject: [PATCH] Initial version to capture and verify the visibility packet
 header.

---
 .../tests/verify_correlator_db_output.py      | 255 ++++++++++++++++++
 1 file changed, 255 insertions(+)
 create mode 100644 applications/apertif/commissioning/tests/verify_correlator_db_output.py

diff --git a/applications/apertif/commissioning/tests/verify_correlator_db_output.py b/applications/apertif/commissioning/tests/verify_correlator_db_output.py
new file mode 100644
index 0000000000..cb7c1f54ae
--- /dev/null
+++ b/applications/apertif/commissioning/tests/verify_correlator_db_output.py
@@ -0,0 +1,255 @@
+###############################################################################
+#
+# Copyright (C) 2018
+# ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+###############################################################################
+
+# Author:
+# E. Kooistra        12 Mar 2018  Created
+
+"""Test correlator visibility packet output via DB
+   
+   These test on hardware is equivalent to test bench tb_node_apertif_unb1_correlator_processing_output.vhd
+   and tb_apertif_unb1_correlator_nodes in simulation. In node_apertif_unb1_correlator_output.vhd the test uses:
+   
+   * REG_DIAG_DATA_BUFFER_OUTPUT to set the data capture at the start of a packet
+   * RAM_DIAG_DATA_BUFFER_OUTPUT to read the packet data
+   
+   The test verifies the read data and runs independently per PN.
+   
+   Usage:
+   # -v 5 or 6 for debugging
+   # -v 0, 1, 2 or 3 for regression test
+   
+   # At central correlator verify expected result via DB
+   > python $RADIOHDL/applications/apertif/commissioning/tests/verify_correlator_db_output.py -v 3 --tel a --unb 0 --fn 0:3 --bn 0:3
+"""
+
+###############################################################################
+# System imports
+import sys
+import common as cm
+import test_case
+import node_io
+import pi_diag_data_buffer
+
+N_dish         = 12
+N_pol          = 2
+N_tp           = N_pol * N_dish  # = 24
+N_vis          = N_tp * (N_tp + 1) / 2
+N_band         = 16
+N_clk          = 256
+N_blk          = 176      # 8b mode
+Q_interleave   = 2
+M_blk          = N_blk / Q_interleave   # = 88
+N_int_x        = 800000
+N_chan_x       = 64
+N_chan_x_w     = cm.ceil_log2(N_chan_x)
+nof_bn         = 4        # Number of back node FPGAs (BN) per UniBoard
+nof_fn         = 4        # Number of front node FPGAs (FN) per UniBoard
+nof_pn         = 8        # Number of processing node FPGAs per UniBoard, = nof_fn + nof_bn
+nof_10g        = 3        # Number of external 10G links per PN on UniBoard
+c_dbSize       = 1024
+c_dbPattern    = [13]*c_dbSize
+
+c_expDstMac  = 0x123456789ABC
+c_baseSrcMac = 0x002286080000
+
+wsrtTelescopes = ['2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd']
+
+###############################################################################
+# Classes, functions
+
+# Use class ApertifUnb1CorrelatorPacketInfo in Python as record. Similar as t_apertif_unb1_correlator_packet_info
+# VHDL record in apertif_unb1_correlator_pkg.vhd.
+class ApertifUnb1CorrelatorPacketInfo:
+    def __init__(self, nof_visibilities):
+        """Define parameters for correlator packet info
+           . nof_visibilities determines the packet payload size
+           . size in number of cm.c_word_w = 32b words
+        """
+        self.vis_header_size       = 21  # (pad(2) + eth(14) + ip(20) + udp(8) + app_id(16) + app_flags(24)) / 4 = 84 bytes / 4 = 21 words
+        self.vis_payload_size      = nof_visibilities * cm.c_nof_complex
+        self.vis_payload_nof_bits  = self.vis_payload_size * cm.c_word_w
+        self.vis_packet_size       = self.vis_header_size + self.vis_payload_size
+        # account for ethernet overhead
+        self.eth_tail_size         = 1   # for the eth CRC word
+        self.eth_gap_size          = 3   # 3 words for c_network_eth_gap_len of 12 bytes idle time between packets
+        self.eth_packet_size       = self.vis_packet_size + self.eth_tail_size + self.eth_gap_size
+        # express ethernet packet time in effective number of bits on the link
+        self.eth_packet_nof_bits   = self.eth_packet_size * cm.c_word_w
+        # Calculate ethernet packet overhead compared to visibility payload due to header, tail and interpacket gap
+        self.eth_packet_overhead   = 1.0 * self.eth_packet_nof_bits / self.vis_payload_nof_bits
+
+# Declaring an object of this class is equivalent to func_apertif_unb1_correlator_packet_info() in VHDL.
+corrPacketInfo = ApertifUnb1CorrelatorPacketInfo(N_vis)
+
+###############################################################################
+# Instantiations
+tc = test_case.Testcase('VERIFY_BF_CORRELATOR_LINK - ', '')
+tc.set_result('PASSED')
+
+# One or more beamlet and channel indices to select the visibility packets
+beamlets = tc.beamlets
+channels = tc.channels
+
+tc.append_log(3, '>>>')
+tc.append_log(1, '>>> Title : Test case to verify visibility packet header in correlator DB OUTPUT for %s' % tc.unb_nodes_string())
+tc.append_log(3, '>>>         . beamlet(s) : %s (from range(M_blk) = [0:%d])' % (beamlets, M_blk-1))
+tc.append_log(3, '>>>         . channel(s) : %s (from range(N_chan_x) = [0:%d])' % (channels, N_chan_x-1))
+tc.append_log(3, '>>>')
+tc.append_log(3, '')
+
+io = node_io.NodeIO(tc.nodeImages, tc.base_ip)
+
+# Create one DB object for all PN
+db_output = pi_diag_data_buffer.PiDiagDataBuffer(tc, io, instanceName='OUTPUT', nofStreams=1, ramSizePerStream=c_dbSize, version=0)
+
+
+for bui in beamlets:
+    for ch in channels:
+        ###############################################################################
+        # Stimuli 
+        
+        # Overwrite DB data to be able to recognize new data
+        db_output.overwrite_all_data_buffers(data=c_dbPattern, vLevel=9)
+
+        # Use start of packet address for DB sync delay
+        sopAddr = ((bui * N_chan_x) + ch) * corrPacketInfo.vis_packet_size          
+        dbSyncDelay = sopAddr
+        db_output.write_sync_delay(data=dbSyncDelay)
+        db_output.read_sync_delay()
+        # when dbSyncDelay=0 then the DB is written every sync
+        # else need to arm the DB using write access strobe to capture once at next sync + dbSyncDelay + latency(14)
+        if dbSyncDelay>0:
+            # Wait unit DB is ready to arm
+            tc.sleep(2)
+            db_output.write_arm_reg(data=1)
+        # Wait unit DB have been filled        
+        tc.sleep(2)
+        
+        # Read DB on all PN
+        dbOutputReadData = db_output.read_data_buffer(nofColumns=tc.nofCol)
+
+        ###############################################################################
+        # Verification per PN
+        for ni,nr in enumerate(tc.nodeNrs):  # loop PN
+            # Beamlet mapping as specified in SP-062 table 6 (SC1), 9 (SC4):
+            #
+            #   u  bi  dest        bu_i
+            #   0   0    0    FN0    0    2  ...
+            #   1   0    1    FN1  256  258  ...
+            #   2   0    2    FN2  512  514  ...
+            #   3   0    3    FN3  768  770  ...
+            #   0   1    4    BN0    1    3  ...
+            #   1   1    5    BN1  257  259  ...
+            #   2   1    6    BN2  513  515  ...
+            #   3   1    7    BN3  769  771  ...
+            #
+            # Beamlet mapping as implemented in Apertif X due to reordered_sosi_arr wiring in node_apertif_unb1_correlator_mesh.vhd:
+            #
+            #   u  bi  dest        bu_i
+            #   0   0    0    FN0    0    2  ...
+            #   1   0    1    FN1    1    3  ...
+            #   2   0    2    FN2  256  258  ...
+            #   3   0    3    FN3  257  259  ...
+            #   0   1    4    BN0  512  514  ...
+            #   1   1    5    BN1  513  515  ...
+            #   2   1    6    BN2  768  770  ...
+            #   3   1    7    BN3  769  771  ...
+            unb = nr / nof_pn       # = range(N_band) = 0:15 = [0:127] / 8
+            pn  = nr % nof_pn       # = range(nof_pn) = 0:7 = [0:127] % 8
+            
+            expBeamlet = N_slot * unb                                                    # offset for UniBoard band
+            expBeamlet = expBeamlet + N_clk * (pn / Q_interleave) + (pn % Q_interleave)  # offset for Local PN
+            expBeamlet = expBeamlet + bui * Q_interleave                                 # offset serial beamlets
+            expChannel = cm.reverse_bits(ch, N_chan_x_w)
+            expChannel = cm.invert_msbit(expChannel, N_chan_x_w)
+
+            # Verify header fields
+            #    0 gap(16) + ETH dst mac hi(16)
+            #    1 ETH dst mac lo
+            #    2 ETH src mac hi
+            #    3 ETH src mac lo(16) + type(16)
+            #    4 IP
+            #    5 IP
+            #    6 IP
+            #    7 IP src addr
+            #    8 IP dst addr
+            #    9 UDP src port(16) + dst port(16)
+            #   10 UDP total len(16) + checksum(16)
+            #   11 ID marker(8) + version(8) + beamlet(16)
+            #   12 ID channel(16) + rsvd(16)
+            #   13 ID timestamp hi
+            #   14 ID timestamp lo
+            #   15 FLAG
+            #   16 FLAG
+            #   17 FLAG
+            #   18 FLAG
+            #   19 FLAG
+            #   20 FLAG
+            
+            rdData = dbOutputReadData[ni]
+            reportStr = 'unb = %2d, pn = %d, bui = %3d, ch = %2d : ' % (unb, pn, bui, ch)
+            
+            # . dst mac
+            rdDstMac  = to_unsigned(rdData[0], 16) << 32
+            rdDstMac += to_unsigned(rdData[1], 32)
+            if rdDstMac == expDstMac:
+                tc.append_log(tc.V_INFO_DETAILS, reportStr + 'destination MAC = 0x%012X is OK' % rdDstMac)
+            else:
+                tc.append_log(tc.V_ERRORS, reportStr + 'read destination MAC is wrong (read 0x%012X != 0x%012X expected)' % (rdDstMac, expDstMac))
+                tc.set_result('FAILED')
+
+            # . src mac
+            expSrcMac = baseSrcMac + unb * 256 + pn
+            rdSrcMac  = to_unsigned(rdData[2], 32) << 16
+            rdSrcMac += to_unsigned(rdData[3], 32) >> 16
+            if rdSrcMac == expSrcMac:
+                tc.append_log(tc.V_INFO_DETAILS, reportStr + 'source MAC = 0x%012X is OK' % rdSrcMac)
+            else:
+                tc.append_log(tc.V_ERRORS, reportStr + 'read source MAC is wrong (read 0x%012X != 0x%012X expected)' % (rdSrcMac, expSrcMac))
+                tc.set_result('FAILED')
+
+            # . beamlet
+            rdBeamlet = to_unsigned(rdData[11], 16)
+            if rdBeamlet == expBeamlet:
+                tc.append_log(tc.V_INFO_DETAILS, reportStr + 'beamlet = %d is OK' % rdBeamlet)
+            else:
+                tc.append_log(tc.V_ERRORS, reportStr + 'read beamlet is wrong (read %d != %d expected)' % (rdBeamlet, expBeamlet))
+                tc.set_result('FAILED')
+              
+            # . channel
+            rdChannel = to_unsigned(rdData[12], 32) >> 16
+            if rdChannel == expChannel:
+                tc.append_log(tc.V_INFO_DETAILS, reportStr + 'channel = %d is OK' % rdChannel)
+            else:
+                tc.append_log(tc.V_ERRORS, reportStr + 'read channel is wrong (read %d != %d expected)' % (rdChannel, expChannel))
+                tc.set_result('FAILED')
+    
+            # . timestamp (= bsn)
+            rdBsn  = to_unsigned(rdData[13], 32) << 32
+            rdBsn += to_unsigned(rdData[14], 32)
+            tc.append_log(tc.V_INFO_DETAILS, reportStr + 'read BSN = %d' % rdBsn)
+
+                                
+###############################################################################
+# End
+tc.set_section_id('')
+tc.append_log(tc.V_RESULT, '>>> Test result: %s' % tc.get_result())
+
-- 
GitLab