diff --git a/applications/apertif/commissioning/tests/verify_correlator_db_output.py b/applications/apertif/commissioning/tests/verify_correlator_db_output.py index 71e9f272c0ed2d7456aef4833e069355cab527c6..798830803cfda79fda65673daefe5a79b09fa0ab 100644 --- a/applications/apertif/commissioning/tests/verify_correlator_db_output.py +++ b/applications/apertif/commissioning/tests/verify_correlator_db_output.py @@ -32,16 +32,20 @@ The test verifies the read data and runs independently per PN on the correlator. The test does not access the telescopes, but some --tel must be specified because --tel and --pol are used to derive the active - telescope paths (TP) which will result in non-zero visibility data. The assumption is that the telescopes - are receiving (any noise) and outputting beamlets. + telescope paths (TP) which will result in non-zero visibility data. The assumption is that the selected + --tel telescopes are receiving (sky) noise and are outputting beamlets. Usage: - # -v 5 or 6 for debugging - # -v 0, 1, 2 or 3 for regression test + # use verbosity level -v 5 or 6 for debugging, or -v 0, 1, 2 or 3 for regression test - # At central correlator verify expected result via DB + # First start the dishes and central boards > python $RADIOHDL/applications/apertif/commissioning/main.py --app apertif-dev --tel a --pol 0,1 --unb 0,1 - > python $RADIOHDL/applications/apertif/commissioning/tests/verify_correlator_db_output.py -v 3 --tel a --unb 0 --fn 0:3 --bn 0:3 --beamlets 0 --channels 0 + + # Set dish BF weights to have one SP per CB for all beamlets and rely on the measured sky noise to provide ADC input + > ssh lcu-rta "python $UPE/peripherals/pi_apertif_system.py -v 3 --unb 0:7 --fn 0:3 --cmd 62 --cbeams 0:36 --pol 0,1 --subbands 0:511 --globalsp 56 --weight 32767,0" + + # At central correlator verify expected visibility packet result via DB output + > python $RADIOHDL/applications/apertif/commissioning/tests/verify_correlator_db_output.py -v 3 --tel a --unb 0,1 --fn 0:3 --bn 0:3 --beamlets 0 --channels 0 """ ############################################################################### @@ -53,39 +57,11 @@ import test_case import node_io import pi_diag_data_buffer import unb_apertif as apr +import pi_apertif_system as pi_apr -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_slot = 1024 -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_baseSrcMac = 0x002286080000 - -c_wcudata = 1 # is 1 if Data Write is 'wcudata1', else is 2 if DataWriter is 'wcudata2', as defined in central_commands.sh -if c_wcudata==1: - c_baseDstMacEth01 = 0xe41d2de42690 - c_baseDstMacEth23 = 0xe41d2dbc3cd0 -else: - c_baseDstMacEth01 = 0xe41d2dbc3dc0 - c_baseDstMacEth23 = 0xe41d2de40d30 - -c_expIdMarker = 65 -c_expIdVersion = 2 +N_vis = pi_apr.N_vis # = 300 +M_blk = pi_apr.M_blk # = 88 or 120 +N_chan_x = pi_apr.N_chan_x # = 64 ############################################################################### # Instantiations @@ -97,219 +73,81 @@ activeDishes = apr.get_active_dishes(tc.telStrList) activePolarizations = tc.polNrs activeTp = apr.get_active_telescope_paths(activeDishes, activePolarizations) activeVis = apr.get_active_visibilities(activeTp) +activeAutoVis = apr.get_auto_visibilities(activeTp) # Declaring an object of this class is equivalent to func_apertif_unb1_correlator_packet_info() in VHDL. corrPacketInfo = apr.CorrelatorPacketInfo(N_vis) -# One or more beamlet and channel indices to select the visibility packets -beamlets = tc.beamlets -if not cm.exist_all_elements_from_a_in_b(beamlets, range(M_blk)): - tc.append_log(tc.V_ERRORS, 'Specified --beamlets %s must fit in range(%d)' % (tc.beamlets, M_blk)) +# One or more beamlet indices to select the channel visibility packets +beamletIndices = tc.beamlets +if not cm.exist_all_elements_from_a_in_b(beamletIndices, range(M_blk)): + tc.append_log(tc.V_ERRORS, 'Specified --beamlets %s must fit in range(%d)' % (beamletIndices, M_blk)) tc.set_result('FAILED') sys.exit() -channels = tc.channels -if not cm.exist_all_elements_from_a_in_b(channels, range(N_chan_x)): - tc.append_log(tc.V_ERRORS, 'Specified --channels %s must fit in range(%d)' % (tc.channels, N_chan_x)) +channelIndices = tc.channels +if not cm.exist_all_elements_from_a_in_b(channelIndices, range(N_chan_x)): + tc.append_log(tc.V_ERRORS, 'Specified --channels %s must fit in range(%d)' % (channelIndices, N_chan_x)) tc.set_result('FAILED') sys.exit() tc.append_log(3, '>>>') tc.append_log(1, '>>> Title : Test case to verify visibility packet header and zero/non-zero payload of 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, '>>> . beamlet(s) : %s (from index range(M_blk) = [0:%d])' % (beamletIndices, M_blk-1)) +tc.append_log(3, '>>> . channel(s) : %s (from index range(N_chan_x) = [0:%d])' % (channelIndices, N_chan_x-1)) +tc.append_log(3, '') +tc.append_log(3, '>>> . activeDishes : %s is %s from %s' % (activeDishes, tc.telStrList, apr.wsrtTelescopes)) +tc.append_log(3, '>>> . activePolarizations : %s is %s' % (activePolarizations, tc.to_pol_xy(tc.polNrs))) +tc.append_log(3, '>>> . activeTp : %s' % activeTp) +tc.append_log(3, '>>> . activeVis : %s' % activeVis) +tc.append_log(3, '>>> . activeAutoVis : %s' % activeAutoVis) 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) +# Create correlator output object +rsub = pi_apr.declare_reorder_subbands(tc, io) +rbeam = pi_apr.declare_reorder_beamlets(tc, io) +corDb = pi_apr.CorrelatorOutputDataBuffer(tc, io, rsub, rbeam, dbSize=1024) # assume minimum DB size that can fit 1 visibility packet +############################################################################### +# Stimuli -for bui in beamlets: - for ch in channels: - ############################################################################### - # Stimuli +# Setup and read DB output to capture the visibility packets +for bui in beamletIndices: + for chi in channelIndices: + corDb.read_correlator_db_output(bui, chi) - # 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) +# Verify visibility packet headers +corDb.verify_correlator_db_output_headers(beamletIndices, channelIndices) - # 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 - # Get read packet data - rdData = dbOutputReadData[ni] - - # Get packet info - unb = nr / nof_pn # = range(N_band) = 0:15 = [0:127] / 8 - pn = nr % nof_pn # = range(nof_pn) = 0:7 = [0:127] % 8 - reportStr = 'unb = %2d, pn = %d, bui = %3d, ch = %2d : ' % (unb, pn, bui, ch) - - ################################################################### - # Verify header fields - - # Beamlet mapping as specified in SP-062 table 6 (SC1), 9 (SC4): - # - # u bi dest bui - # 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 bui - # 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 ... - 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) - - # 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 - - # . dst mac as defined in central_commands.sh - if unb< 4: expDstMac = c_baseDstMacEth01 - elif unb< 8: expDstMac = c_baseDstMacEth01 + 1 - elif unb<12: expDstMac = c_baseDstMacEth23 - elif unb<16: expDstMac = c_baseDstMacEth23 + 1 - - rdDstMac = cm.to_unsigned(rdData[0], 16) << 32 - rdDstMac += cm.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 = c_baseSrcMac + unb * 256 + pn - rdSrcMac = cm.to_unsigned(rdData[2], 32) << 16 - rdSrcMac += cm.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') - # . ID marker - rdIdMarker = cm.to_unsigned(rdData[11], 32) >> 24 - if rdIdMarker == c_expIdMarker: - tc.append_log(tc.V_INFO_DETAILS, reportStr + 'ID marker = %d is OK' % rdIdMarker) - else: - tc.append_log(tc.V_ERRORS, reportStr + 'read ID marker is wrong (read %d != %d expected)' % (rdIdMarker, c_expIdMarker)) - tc.set_result('FAILED') - - # . ID version - rdIdVersion = cm.to_unsigned(rdData[11], 24) >> 16 - if rdIdVersion == c_expIdVersion: - tc.append_log(tc.V_INFO_DETAILS, reportStr + 'ID version = %d is OK' % rdIdVersion) - else: - tc.append_log(tc.V_ERRORS, reportStr + 'read ID version is wrong (read %d != %d expected)' % (rdIdVersion, c_expIdVersion)) - tc.set_result('FAILED') - - # . beamlet - rdBeamlet = cm.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 = cm.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 = cm.to_unsigned(rdData[13], 32) << 32 - rdBsn += cm.to_unsigned(rdData[14], 32) - tc.append_log(tc.V_INFO_DETAILS, reportStr + 'read BSN = %d' % rdBsn) +# Verify visibility payload data +# +# A visibility packet contains all N_vis = 300 complex visibilities for one (beamlet, channel): +# - The visibilities for which at least one TP is inactive must be zero, because for those TP the +# firmware forces the data to zero. +# - If both TP in a visibility are active, then the visibility can be non-zero. The visibility +# can still be zero if the BF weights for the corresponding beamlet are zero. +# +# Visibility (col, row) indices for +# 0 vis (0,0) +# 1 vis (0,1) 24 vis (1,1) +# 2 vis (0,2) 25 vis (1,2) 47 vis (2,2) +# 3 vis (0,3) 26 vis (1,3) 48 vis (2,3) +# ... ... ... +# 23 vis (0,23) 46 vis (1,23) 68 vis (2,23) ... 299 vis (23,23) +# +# 24 + 23 + 22 ... + 1 = 300 visibilities +# +for gn in tc.nodeNrs: # loop active PN + unb, pn = apr.correlator_gn_to_unb_pn(gn) + for bui in beamletIndices: # loop serial beamlets + for chi in channelIndices: # loop serial channels + reportStr = 'unb = %2d, pn = %d, bui = %3d, chi = %2d : ' % (unb, pn, bui, chi) - ################################################################### - # Verify payload data - # - # A visibility packet contains all N_vis = 300 visibilities for one (beamlet, channel): - # - The visibilities for which at least one TP is inactive must be zero, because for those TP the - # firmware forces the data to zero. - # - If both TP in a visibility are active, then the visibility can be non-zero. The visibility - # can still be zero if the BF weights for the corresponding beamlet are zero. - # - # Visibility (col, row) indices for - # 0 vis (0,0) - # 1 vis (0,1) 24 vis (1,1) - # 2 vis (0,2) 25 vis (1,2) 47 vis (2,2) - # 3 vis (0,3) 26 vis (1,3) 48 vis (2,3) - # ... ... ... - # 23 vis (0,23) 46 vis (1,23) 68 vis (2,23) ... 299 vis (23,23) - # - # 24 + 23 + 22 ... + 1 = 300 visibilities - # - # The visibilities are stored in two 32b words: real, imag, so in total 600 words payload. - rdPayload = rdData[corrPacketInfo.vis_header_size:corrPacketInfo.vis_header_size+N_vis*cm.c_nof_complex] - rdVisibilities = [] - even = True - for data in rdPayload: - dataNtoh = cm.reverse_word(data) # change endianess - if even: - visReal = cm.to_signed(dataNtoh, 32) - else: - visImag = cm.to_signed(dataNtoh, 32) - rdVisibilities.append(complex(visReal, visImag)) - even = not even + rdVisibilities = corDb.dbOutputVisibilities[gn][bui][chi] # Find the TP for all read visibilities that are non-zero nonZeroVisibilities = cm.find_indices_where_ne(rdVisibilities, 0) @@ -319,9 +157,12 @@ for bui in beamlets: nonZeroPolarizations = apr.tp_to_pol_indices(nonZeroTp) nonZeroAutoVisibilities = apr.get_auto_visibilities(nonZeroTp) - # Verify that the active TP yield non-zero visibilities + # Verify that only the active TP yield non-zero visibilities # . Assume the BF weights have been set to select at least one ADC input per CB then the input (sky) noise at the ADC # will contribute to all channels, so the visibilities for the TP pairs of active TP will then be none zero. + for vi in nonZeroVisibilities: + tc.append_log(tc.V_INFO_DETAILS, reportStr + 'Non zero correlation[%d] = %s' % (vi, rdVisibilities[vi])) + if activeTp == nonZeroTp: tc.append_log(tc.V_INFO_DETAILS, reportStr + 'Non zero TP = %s : polarizations = %s and dishes = %s are OK' % (nonZeroTp, nonZeroPolarizations, nonZeroDishes)) else: @@ -333,7 +174,10 @@ for bui in beamlets: for ai in nonZeroAutoVisibilities: atp2 = apr.visibilities_to_tp_pairs(ai) atp = atp2[0][0] # get tp from list with one tuple(tp, tp), both tp in tuple are the same index for auto correlation visibility - if not (rdVisibilities[ai].real>0 and rdVisibilities[ai].imag==0): + if rdVisibilities[ai].real>0 and rdVisibilities[ai].imag==0: + tc.append_log(tc.V_INFO_DETAILS, reportStr + 'Auto correlation[%d] = %s' % (ai, rdVisibilities[ai])) + else: + tc.append_log(tc.V_ERRORS, reportStr + 'Auto correlation[%d] = %s is wrong' % (ai, rdVisibilities[ai])) atpFail.append(atp) #print ai, atp2, atp, rdVisibilities[ai] if len(atpFail)==0: @@ -348,3 +192,4 @@ for bui in beamlets: tc.set_section_id('') tc.append_log(tc.V_RESULT, '>>> Test result: %s' % tc.get_result()) +