#!   /usr/bin/env   python

# Copyright Creotech SA, 2016
# Author: Marek Guminski <marek.guminski@creotech.pl>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org


import spec
import fmcmasterfip as fmc
import utilities as util
import time
import ptsexcept
from time import sleep
from spec_identification import dut_bus
import sys


PLOT=True


def wait_transmission_termination( dut, ret = 1000, val = 2 ):
    
    util.info_msg("Waiting for termination of transmission (inactive CD_N)")
    
    for i in xrange( ret ):
        if dut.fippts.read_regname('fd_rxcdn_cnt') > val-1:
#             util.dbg_msg("Finished waiting for cdn after %d tries" % i)
            break
        
        else:
            time.sleep(0.001)
#         
    for i in xrange( ret ):
        if dut.fippts.read_regname('fd.cd_n') == 1: 
            util.info_msg("CD_N line did go high after transmission termination after %d tries." %i)
            return True
        else:
            time.sleep(1)
    
    util.info_msg("CD_N line did not go high after transmission termination")
    return False

def verify_rx_status( stat ):
    
    # assume test failure
    test_result = {
        'Number of bytes'   : 0,
        'CRC'               : 0,
        'Frame format'      : 0,
        'Preamble'          : 0 
    }
    
    if util.bitvector(stat, 8, 16) != 7:
        util.info_msg("Wrong number of bytes received")
    else:
        test_result['Number of bytes'] = 1
        
    if ( util.bitvector(stat, 2, 2) == 1 ):
        util.info_msg("CRC error")
    else :
        test_result['CRC'] = 1
    
    if util.bitvector(stat, 1, 1) == 0 :
        util.info_msg("Not complete frame received")
    else :
        test_result['Frame format'] = 1
    
    if util.bitvector(stat, 0, 0) == 0 :
        util.info_msg("Preamble not detected")
    else :
        test_result['Preamble'] = 1
    
    
    
    return test_result
    
    
def  verify_presence( carrier ):
    util.info_msg("Bus transmission: checking presence")
    
    # Send ID_DAT for presence variable (0x14)
    # nanoFIPdiag address (common to all such modules in CERN): 7F
    carrier.send_id_dat(0x7F14) 
    
    # wait for response
    time.sleep(1)
    
    # Read RP_DAT reply from nanoFIPdiag
    [stat, ctrl, data ] = carrier.read_rp_dat()
    
    test_result = verify_rx_status( stat )

    util.info_msg("Control field in bus response: 0x%x" % ctrl)
    
    # replay should have ctrl value equal to 2
    if ctrl == 2:
        test_result['Data control field'] = 1
    else :
        test_result['Data control field'] = 0
        
    # presence variable data is always the same
    if util.bitvector(data, 0, 8*6) ==  0x00f00003800550 :
        util.info_msg ("Presence variable OK!")
        test_result['Data field'] = 1
    
    elif (data == 0x0):
        util.info_msg("No WorldFIP traffic")
        test_result['Data field'] = 0
        
    else:
        util.info_msg("Data errors")
        test_result['Data field'] = 0
    
    return test_result



def test_cdn( dut, maxtries = 50 ):
    last = 0
    det = 0
    
    test_result = { 'CD_N during transmission' : 0 }
    
#     time.sleep(0.5)
    # no transmission
    # cdn should be high
    for i in xrange(10):
        if dut.fippts.read_regname('fd.cd_n') == 0 :
            if i == 9:
                test_result['CD_N during idle'] = 0
                util.err_msg("CD_N doesn't change value to inactive")
            else:
                time.sleep(0.5)
        else:
            test_result['CD_N during idle'] = 1
            util.info_msg("CD_N found inactive during bus idle. Took %d tries" % i)
            break
            
#     resets error counter
#     rxcdn_cnt counts rising edges of CD_N line
#     after each request response will arrive
#     counter should increase by 2
#     dut.rst_core()
    before = dut.fippts.read_regname('fd_rxcdn_cnt')
    util.info_msg ("CD_N counter value before transmission: %d" % before )
    
    dut.send_id_dat(0x7F14)
    
    time.sleep(1)
    det = dut.fippts.read_regname('fd_rxcdn_cnt')
    util.info_msg ("CD_N counter value after transmission: %d" % before )

    det = det - before 
    
    if det == 2:
        test_result[ 'CD_N during transmission' ] = 1;
        util.info_msg("Active CD_N detected during transmission (%d frames)." % det )

    else:
        test_result[ 'CD_N during transmission' ] = 0;  
        util.err_msg( "CD_N NOT detected during transmission" )
    
    wait_transmission_termination( dut )
    
    return test_result
    

def test_txerr( dut, box, maxtries = 50 ):
    
    
    test_result = { 
       'TX_ERR during normal transmission'          : 0, 
       'TX_ERR during corrupted transmission'       : 0, 
       'TX_ERR during idle'                         : 0 
       }
    
    dut.fippts.write_regname('tx_corrupt',0)

########################################################################
    util.section_msg("Checking if TX_ERR is not stack on active value")

    for i in xrange(10):
        # check pin value during bus idle
        if dut.fippts.read_regname('fd.txer') == 0 :
            test_result['TX_ERR during idle'] = 1
            util.info_msg("TX_ERR is inactive during bus idle")
            break
        else:
            if i == 9:
                test_result['TX_ERR during idle'] = 0
                util.err_msg("TX_ERR doesn't go inactive during bus idle")
            else:
                time.sleep(0.5)
    
########################################################################
    util.section_msg("Checking if TX_ERR is not going active during normal transmission")
    
    dut.rst_core()
    util.info_msg("TX_ERR counter before transmission: %d" % dut.fippts.read_regname('fd_txerr_cnt') )
    
    dut.send_id_dat(0x7F14)
    
    time.sleep(1)
    [stat, ctrl, data ] = dut.read_rp_dat()
    tmp = verify_rx_status( stat )
    test_result = util.merge_dictionaries_prefix(test_result, tmp, "", "Transmission verification during TX_ERR test. " )
   
    time.sleep(0.5)
    det = dut.fippts.read_regname('fd_txerr_cnt')
    
    
    if det == 0:
        test_result[ 'TX_ERR during normal transmission' ] = 1;
        util.info_msg("TXERR NOT detected during transmission")
        
    else:
        test_result[ 'TX_ERR during normal transmission' ] = 0;  
        util.err_msg( "TX_ERR counter after transmission: %d" % det )
    
########################################################################
    util.section_msg("Checking if TX_ERR IS going active during corrupted transmission (serial output is constant 1)")
    
    dut.rst_core()
    util.info_msg("TX_ERR counter before transmission: %d" % dut.fippts.read_regname('fd_txerr_cnt') )
    
    # corrupting serial output!!!!!
    dut.fippts.write_regname('tx_corrupt',1)
    
    dut.fipcore.write_regname('tx_ctrl', 1)
    dut.fipcore.write_regname('tx_ctrl', 0)
    dut.fipcore.write_regname('tx_payld_ctrl', 0x3)
    dut.fipcore.write_regname('tx_payld_reg1', 0x7F14)
    dut.fipcore.write_regname('tx_ctrl', 0x202)
    
    time.sleep(0.5)
    det = dut.fippts.read_regname('fd_txerr_cnt')
    
    # since worldfip cable is disconnected
    # transmission errors should be detected
    if det > 0:
        test_result[ 'TX_ERR during corrupted transmission' ] = 1;
        util.info_msg( "TX_ERR counter after corrupted transmission: %d" % det )
        
    else:
        test_result[ 'TX_ERR during corrupted transmission' ] = 0;  
        util.err_msg( "TXERR NOT detected during corrupted transmission" )

    
    # fixing serial output!!!!!
    dut.fippts.write_regname('tx_corrupt',0)
    wait_transmission_termination( dut )
    
    return test_result
        
def test_wdgn( dut ):
   
    ###################### test ##################################################
    
    ########################################################################
    util.section_msg("Checking if WDG_N is not stack on active value")
    
    result = { }
    for i in xrange(10):
        tmp = dut.fippts.read_regname('fd.wdgn')
        if tmp :
            util.info_msg("WDG_N value during bus idle: %x" % tmp )
            result['WDG_N during idle'] = 1
            break
        else:
            if i == 9:
                result['WDG_N during idle'] = 0
                util.err_msg("WDG_N value is did not go inactive" )
            else:
                time.sleep(0.5)
    
    ###################### test ##################################################
    
    ########################################################################
    util.section_msg("Checking if WDG_N becomes active during normal transmission")
    dut.rst_core()
    
    dut.fipcore.write_regname('tx_ctrl', 1)
    dut.fipcore.write_regname('tx_ctrl', 0)
    dut.fipcore.write_regname('tx_payld_ctrl', 0x3)
    dut.fipcore.write_regname('tx_payld_reg1', 0x7F14)
    dut.fipcore.write_regname('tx_ctrl', 0x202)
    wait_transmission_termination(dut, 100, 2 )
    
    tmp = dut.fippts.read_regname('fd.wdgn_latch')
    if tmp > 0:
        util.info_msg( "WDG_N was inactive after normal transmission" )
    else:
        util.err_msg( "WDG_N was active after normal transmission" )

    result['WDG_N during correct transmission'] = tmp
    
    ###################### test ##################################################
    util.section_msg("Checking if WDG_N becomes active during very long transmission")
    dut.rst_core()

    dut.fipcore.write_regname('tx_ctrl', 1)
    dut.fipcore.write_regname('tx_ctrl', 0)
    dut.fipcore.write_regname('tx_payld_ctrl', 0x3)
    dut.fipcore.write_regname('tx_payld_reg1', 0x7F14)
    dut.fipcore.write_regname('tx_ctrl', 0xFFF02)
    
    
    wait_transmission_termination(dut, 100, 1 )

    tmp = dut.fippts.read_regname('fd.wdgn_latch')
    if tmp == 1:
        util.err_msg( "WDG_N did not go active during corrupted transmission\nCan't continue with FieldDrive RST_N test" )
        result['WDG_N during long transmission'] = 0
        result['RST_N'] = 0
        return result
    else:
        util.info_msg( "WDG_N went active during corrupted transmission" )
        result['WDG_N during long transmission'] = 1
        
    
    tmp = dut.fippts.read_regname('fd.wdgn')
    if tmp == 1:
        util.err_msg( "WDG_N was inactive after corrupted transmission finished\nCan't continue with FieldDrive RST_N test" )
        result['WDG_N latch in FieldDrive'] = 0
        result['RST_N'] = 0
        return result
    else:
        util.info_msg( "WDG_N was active after corrupted transmission finished" )
        result['WDG_N latch in FieldDrive'] = 1
    
    util.section_msg("Checking FieldDrive RST_N line")
    util.info_msg("Reseting FieldDrive chip")
    
    dut.rst_fd()
    
    tmp = dut.fippts.read_regname('fd.wdgn')
    if tmp == 0:
        util.err_msg( "WDG_N did not go inactive after reset." )
        result['RST_N'] = 0
    else:
        util.info_msg( "WDG_N went inactive after reset." )
        result['RST_N'] = 1
        
    
    return result
        
def main (card=None, default_directory='.',suite=None,serial=""):
    
    testname= "Test02: WorldFIP communication"
    util.header_msg( testname, [ "Simple communication", 
                                "FieldDrive CD_N line", 
                                "FieldDrive WDG_N line", 
                                "FieldDrive RESET line",
                                "FieldDrive TX_ERR line"] )
    
    box = util.calibration_box_init()
    
    ###############################################################################
    ############################ initialization ###################################
    ###############################################################################
    util.section_msg("Hardware initialization")
    abspath = util.find_prj_topdir(util.TOPDIRNAME);
    carrier = spec.spec( abspath );
        
    # FPGA bitstream and FMC presence are verified during initialization
    dut = fmc.fmcmasterfip(carrier, abspath, util.FIRMWARE_PATH)
    
    
    ###############################################################################
    ############################ actual test ######################################
    ###############################################################################
    
    ############################### test ##########################################
    util.section_msg("Testing WordFIP transmission" )
    test_results = verify_presence( dut );
    
    ##############################################################################
    
    ############################### test ##########################################
    util.section_msg("Testing FD CD_N line" )
    dut.rst_core()
    tmp = test_cdn( dut )
    test_results = dict( test_results.items() + tmp.items() )
    del tmp
    util.info_msg("Testing FD CD_N line finished" )
    ##############################################################################
    
    
    ############################### test ##########################################
    # transmit long frame
    # pool CSR looking for TXERR during normal operation
    # and with disconnected WorldFIP cable (should give error)
    util.section_msg("Testing FD TX_ERR line" )
    dut.rst_core()
    wait_transmission_termination( dut )
    tmp = test_txerr( dut, box )
    test_results = dict( test_results.items() + tmp.items() )
    del tmp
    util.info_msg("Testing FD TX_ERR line finished" )
    ##############################################################################
    
    ############################### test ##########################################
    # transmit long frame
    # pool CSR looking for TXERR during normal operation
    # and with disconnected WorldFIP cable (should give error)
    util.section_msg("Testing FD WDG_N line" )
    dut.rst_core()
    wait_transmission_termination( dut )
    tmp = test_wdgn( dut )
    test_results = dict( test_results.items() + tmp.items() )
    del tmp
    util.info_msg("Testing FD WDG_N line finished" )
    ##############################################################################
    

    ##############################################################################
    dut.rst_core()
    wait_transmission_termination( dut )
    
    ###############################################################################
    ########################## result processing ##################################
    ###############################################################################
    
    errors = util.summarise_test_results( testname, test_results)

    
    sys.stdout.flush()
    return 0
        
if __name__ == '__main__' :
    main()