##_______________________________________________________________________________________________
##
##                                      CONV-TTL-RS485 PTS
##
##                                         CERN,BE/CO-HT
##_______________________________________________________________________________________________
##
##-----------------------------------------------------------------------------------------------
##
##                            CONV-TTL-RS485 TTL pulse repetition test
##
##-----------------------------------------------------------------------------------------------
##
## Description  This module implements the TTL pulse repetition test. It tests TTL input Schmitt
##              triggers IC4 (TTL) and IC9(INV-TTL), along with bus buffers IC2, IC3 and enable
##              NAND gate IC8.
##
##              There are ten TTL pulse channels this test checks. Six (CH1-6) are TTL
##              channels and four (CH6-10) are INV-TTL channels, but the FPGA gateware sends
##              TTL pulses on all channels, since the PCB circuits for TTL and INV-TTL channels
##              are the same.
##
##              Counters are implemented in the gateware to count the number of pulses sent and
##              received on a channel. There are two counters per channel, one for input and
##              one for output; the counters increment on the rising edge of a pulse signal. The
##              current value of each counter can be read via a special Wishbone-mapped slave,
##              accessible at base address 0xC00. Below is the full address map for the TTL
##              pulse counters:
##
##                 WB addr    Counter
##                  0xC00     TTL_CH1_OUT
##                  0xC04     TTL_CH1_IN
##                  0xC08     TTL_CH2_OUT
##                  0xC0C     TTL_CH2_IN
##                  0xC10     TTL_CH3_OUT
##                  0xC14     TTL_CH3_IN
##                  0xC18     TTL_CH4_OUT
##                  0xC1C     TTL_CH4_IN
##                  0xC20     TTL_CH5_OUT
##                  0xC24     TTL_CH5_IN
##                  0xC28     TTL_CH6_OUT
##                  0xC2C     TTL_CH6_IN
##                  0xC30     TTL_CH7_OUT
##                  0xC34     TTL_CH7_IN
##                  0xC38     TTL_CH8_OUT
##                  0xC3C     TTL_CH8_IN
##                  0xC40     TTL_CH9_OUT
##                  0xC44     TTL_CH9_IN
##                  0xC48     TTL_CH10_OUT
##                  0xC4C     TTL_CH10_IN
##
##              At hardware level, the channels are daisy-chained, CH1 output to CH2 input,
##              CH2 output to CH3 input and so forth, until the last channel, CH10, which is
##              connected back to the CH1 input. CH1 generates pulses twice a second which
##              get sent from one channel to the other in the daisy chain.
##
##              This module contains a pulse counter class which implements methods to decode
##              channel pulse counter numbers into their respective address. In the main
##              function of the module, a pulse counter object is created and pulses are
##              generated from CH10 for 1 second. These pulses are replicated through the
##              daisy chain and the counters are updated on each pulse. At the end of the
##              test, at least two pulses should be replicated through the daisy chain. The test
##              program waits for 1s, stops pulse generation on CH10 and reads the pulse
##              counter values. If the number of pulses received on ALL channels matches
##              the expected number of pulse, the test passes, otherwise the circuit that
##              might be the fault is indicated.
##
##              After the TTL lines are tested, the switch lines are read for correct values.
##              The switches should in this purpose be set in their default position:
##
##                     SW1        SW2
##              ON  | | | | |  | | | |o|
##              OFF |o|o|o|o|  |o|o|o| |
##                   1 2 3 4    1 2 3 4
##
##              The test checks these lines and since an ON switch connects the FPGA input to
##              GND, the above switch setup should yield the value 0xFF. If this value gets
##              read back from the switches, the switch test is successful.
##
## Authors      Theodor-Adrian Stana (t.stana@cern.ch)
## Website      http://www.ohwr.org/projects/conv-ttl-rs485
## Date         31/10/2013
##-----------------------------------------------------------------------------------------------
##
##------------------------------------------------------------------------------------------------
##                               GNU LESSER GENERAL PUBLIC LICENSE
##                              ------------------------------------
## This source file is free software; you can redistribute it and/or modify it under the terms of
## the GNU Lesser General Public License as published by the Free Software Foundation; either
## version 2.1 of the License, or (at your option) any later version.
## This source 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 Lesser General Public License for more details.
## You should have received a copy of the GNU Lesser General Public License along with this
## source; if not, download it from http://www.gnu.org/licenses/lgpl-2.1.html
##-------------------------------------------------------------------------------------------------

##-------------------------------------------------------------------------------------------------
##                                            Import
##-------------------------------------------------------------------------------------------------

# Import system modules
import sys
sys.path.append("pyts")
import time
import os, errno, re, struct
import os.path
import traceback

# Import common modules
from ctypes import *
from ptsexcept import *
from vv_pts import *
from ptsdefine import *

class CPulseCounter:

    def __init__(self, bus, base):
        self.bus  = bus
        self.base = base

    def wr_reg(self, addr, val):
        self.bus.vv_write(self.base + addr,val)

    def rd_reg(self, addr):
        return self.bus.vv_read(self.base + addr)

    def wr_out_cnt(self, chan, val):
        return self.wr_reg((chan-1)*8, val)

    def wr_in_cnt(self, chan, val):
        return self.wr_reg((chan-1)*8 + 4, val)

    def rd_out_cnt(self, chan):
        return self.rd_reg((chan-1)*8)

    def rd_in_cnt(self, chan):
        return self.rd_reg((chan-1)*8 + 4)

##-------------------------------------------------------------------------------------------------
##                                             main                                              --
##-------------------------------------------------------------------------------------------------
def main(bus, tname, inf, log):

    """
    tests : TTL pulse repetition, buffers IC2, IC3, Schmitt trigger
            inputs IC4, IC9, NAND gate IC8
    uses  : pts.bit and ttl_pulse_switch.py
    """

    pel = PTS_ERROR_LOGGER(inf, log)

    chans = ['1', '2', '3', '4', '5', '6', 'A', 'B', 'C', 'D']

    try:
        # Initialize a pulse counter object
        pc = CPulseCounter(bus, PULSE_CNT_BASE)

        # Clear pulse counters for the TTL channels
        for i in range(1, 11):
            pc.wr_out_cnt(i, 0)
            pc.wr_in_cnt(i, 0)

        # Enable pulse generation
        val = bus.vv_read(CSR)
        val |= (1 << CSR_TTLPT_OFS)
        bus.vv_write(CSR, val)

        # wait one second, then disable pulse generation
        time.sleep(1)
        val &= ~(1 << CSR_TTLPT_OFS)
        bus.vv_write(CSR, val)

        # Read the channel registers
        ic_arr = []
        oc_arr = []
        for i in range(1,11):
            ic_arr.append(pc.rd_in_cnt(i))
            oc_arr.append(pc.rd_out_cnt(i))

        # First, check for all-zeroes in counters, indicating enable NAND gate failure
        if all(ic == 0 for ic in ic_arr):
            msg = "ERROR: No pulses received - check daisy-chain, or enable NAND gate IC8"
            pel.set(msg)

        # Then, check if the number of pulses sent on the previous channel has
        # been received correctly on the current channel
        for i in range(0,10):
            if (ic_arr[i] != 0) and (ic_arr[i] == oc_arr[i-1]):
                msg = "Ch%s received %d pulses and sent %d pulses - good\n" % (chans[i], ic_arr[i], oc_arr[i])
                inf.write(msg)
            else:
                msg = "ERROR: Ch%s received %d pulses and sent %d pulses - " % (chans[i], ic_arr[i], oc_arr[i])
                if (i == 0):
                    msg += "check daisy-chain, IC4 or IC2, or IC3"
                elif (i < 6):
                    msg += "check daisy-chain, IC4 or IC2"
                else:
                    msg += "check daisy-chain, IC9 or IC3"
                pel.set(msg)

        # Switches test
        switches = (bus.vv_read(CSR) >> CSR_SWITCH_OFS) & 0xff
        if (switches != 0x80):
            msg = "ERROR: Switches readout (0x%x) incorrect - expected 0x80" % switches
            pel.set(msg)
        else:
            msg = "Switches readout as expected: 0x%x\n" % switches
            inf.write(msg)

        inf.write("\n")

        # Finally, return the number of errors that occured
        return pel.get()

    except BusException, e:
        raise PtsError("SKT Exception: %s" % e)

    except BusWarning, e:
        raise PtsError("SKT Warning: %s" % e)

