Skip to content
Snippets Groups Projects
fpga_comp.py 18.41 KiB
#!/usr/bin/python
"""
   fpga_comp.py -- module to query and set registers managed by the fpga

   Note:
      This code will be running on FC
   Author
      PYD 07/12/2017
      JPC 25/10/2018 : added fpga version read

"""
import time
import json
from lli import mem_multi as mem


class Arria10(object):
    """ Class to manage registers of the arria10.

    The mapping of port registers per type of PLL is given as a class dictionnary
    of lists of registers addresses.

    Args:
        dev (int):

    """
    regs = {"SI5344_U54": {
                0:0x000,
                1: 0x160},
            "SI5345_U23": {
                0: 0x30,
                1: 0x40,
                2: 0x50,
                3: 0x60},
            "SI5345_U48": {
                0: 0x70,
                1: 0x80,
                3: 0x90,
                4: 0xA0,
                8: 0x130},
            "SI53344": {
                0: 0xB0,
                1: 0xC0,
                2: 0xD0,
                3: 0xE0,
                4: 0xF0,
                5: 0x100,
                6: 0x110,
                7: 0x120,
                8: 0x150},
            "SI5340": {
                0: 0x140}
           }

    clk_meas = 0x01000 # offset on avalon bus
    io_pll   = 0x40000
    list_adr_pll = [0x00088000, 0x00098000, 0x000A8000, 0x000B8000, 0x000C8000, 0x000D8000, 0x000E8000, 0x000F8000, 0x00070000]

    def __init__(self, dev):
        """
        Args:
          dev (int): board identifier
          type_pll forced to fPLL
        """
        self.dev = dev
        self.type_pll = 0

    def dump_pll_frequencies(self, source):
        """dump the frequency measure for all ports of the given PLL.

        Args:
            source (str):
                name of the PLL. Possible values are SI5344_U54,
                SI5345_U23, SI5345_U48, SI53344, SI53340.

        Raises:
            ValueError:
                for invalid name.

        """

        if source not in Arria10.regs:
            raise ValueError("unknown source")

        fmt = "{}_{} register 0x{:x} status {} value 0x{:x} [{}]"
        for port, reg in Arria10.regs[source].iteritems():
            val = mem.read(self.dev, Arria10.clk_meas + reg)
            print fmt.format(source, port, reg, val[0], val[1], val[1])

    def read_pll_port_frequency(self, source, port):
        """Measure the frequency on the given ports of the PLL.

        Args:
            source (str):
                name of the PLL. Possible values are  SI5344_U54,
                SI5345_U23, SI5345_U48, SI53344, SI53340.

            port (int)
                a number ranging from 0 to 8.
                Available port depends on the PLL.

        Return
            tuple:
                status and value

        Raises:
            ValueError:
                for invalid name of port

        """
        if source not in Arria10.regs:
            raise ValueError("unknown source {}".format(source))

        ports = Arria10.regs[source]
        if port not in ports:
            raise ValueError("unkown port {}".format(port))

        return mem.read(self.dev, Arria10.clk_meas + ports[port])

    def temperature(self):
        """ Return the internal temperature of the FPGA.

        Returns
            tuple:
                status, value

        """
        add = Arria10.clk_meas + 0x150
        return mem.read(self.dev, add)

    def clear_reset_gbt(self):
        """ Sets the system_status_register to "00000001"
            in order to clear the reset signal feeding the
            serial transceivers.

        Returns
            status

        """
        value = 0x1
        add = 0x30120
        # print "..... GBT active"
        return mem.write(self.dev, add, value)

    def init_reset_gbt(self):
        """ Sets the system_status_register to "00000000"
            in order to clear the reset signal feeding the
            serial transceivers.

        Returns
            status

        """
        value = 0x0
        add = 0x30120
        print "..... GBT in reset"
        return mem.write(self.dev, add, value)

    def reconfig_pll(self, num_pll):
        """reconfigure the given PLL.

        Args:
            PLL number

        Raises:
            ValueError:
                for invalid name.

        """
        if num_pll > 8:
        #print "NUM PLL FORBIDDEN"
            return False
        pll_add = self.list_adr_pll[num_pll]
        timeout = time.time() + 2  # timeout = 2 second

        #### patch PLL direct mode ON

        #print ".patch PLL direct mode ON "
        status, val = mem.read(self.dev, pll_add + 0x126*4)
        #print val
        new_val = val|0x01
#        print new_val
        mem.write(self.dev, pll_add + 0x126*4, new_val)


#        print "********* Start calibration PLL" + str(num_pll) + " *********"

#### 1
    #print ". request user acces "
        mem.write(self.dev, pll_add+0x000*4, 0x2)           # write 0x2


#### 2
#        print ". wait for reconfig_waitrequest to be deasserted "
        bool_wait0 = True
        while  bool_wait0:
            status, val = mem.read(self.dev, pll_add+0x280*4)
            test_bit = val&0x04
            if test_bit == 0:
               bool_wait0 = False
#               print ". bus granted to user"
            else:
               if time.time() > timeout:
#                  print "********** TIMEOUT **********"
                  return False
####  3

#        print ". start calibration PLL "
        status, val = mem.read(self.dev, pll_add + 0x100*4)
        if self.type_pll == 0:
            new_val = val|0x2 # FPLL PLL
#            print ". Targetting PLL type =  FPLL"
        if self.type_pll == 1:
            new_val = val|0x1 # ATX PLL
#            print ". Targetting PLL type = ATX"

        mem.write(self.dev, pll_add + 0x100*4, new_val)


#### 4
#        print ". release bus to preSICE"
        mem.write(self.dev, pll_add +0x000*4, 0x1)

#### 5
#        print ". check reset of call_busy signal"
        bool_call_busy1 = True
        while  bool_call_busy1:
            status, val = mem.read(self.dev, pll_add +0x280*4)
            test_bit = val&0x02
            if test_bit == 0:
                bool_call_busy1 = False
                print "PLL" + str(num_pll) + " Calibration done "
            else:
                if time.time() > timeout:
                    print "PLL" + str(num_pll) + " TIMEOUT during calibration"
                    return False


### patch PLL direct mode OFF

        #print ".patch PLL direct mode OFF "
        status, val = mem.read(self.dev, pll_add + 0x126*4)
        #print val
        new_val = val&0xfe
        #print new_val
        mem.write(self.dev, pll_add + 0x126*4, new_val)

### check pll is locked
        (status, message) = self.status_pll(num_pll)
        return (status, message)



############  status_pll return False,""  return True,"" ####################

    def status_pll(self, num_pll):
        """
        return status PLL
        """
        if num_pll > 8:
        #print "NUM PLL FORBIDDEN"
            return False, ""
        pll_add = self.list_adr_pll[num_pll]
        check_lock = False
        timeout = time.time() + 2  # timeout = 2 second

####  Test PLL locked

#        print ". check PLL" + str(num_pll) + " lock status"
        bool_call_busy2 = True
        while  bool_call_busy2:
            status, val = mem.read(self.dev, pll_add + 0x280*4)#0x280
            test_bit = val&0x01
            if test_bit == 1:
                bool_call_busy2 = False
                print " PLL" + str(num_pll) + " is locked"
                check_lock = True
                #print
                #print
                return check_lock, "PLL" + str(num_pll) + " is locked" #renvoie 1 pour valider la Qprossbar + message
            else:
                if time.time() > timeout:
                    print "********** PLL" + str(num_pll) + " TIMEOUT **********"
                #print
                #print
                    return False, "PLL" + str(num_pll) + " is not locked :timeout"

#       return True, ""

    def dump_pll_registers(self):
        """dump the io_pll registers

        Args:
            source ():
                no name

        Raises:
            ValueError:
                for invalid name.

        """
        reg_address = {
            "m": 0x90,
            "n": 0xa0,
            "c0": 0xc0,
            "c1": 0xc1,
            "c2": 0xc2,
            "c3": 0xc3,
            "c4": 0xc4,
            "c5": 0xc5,
            "c6": 0xc6,
            "c7": 0xc7,
            "c8": 0xc8
        }

        print
        print "== Reading iopll registers =="
        print "Input frequency = 100 MHz"
        status, val = mem.read(self.dev, Arria10.io_pll + 4 * reg_address["m"])
        if val & 0x10000 == 0:
            msb = val & 0xFF
            lsb = val >> 8  & 0xFF
            m = msb + lsb
        else:
            m = 1
        print  "multiplier m  = ", m

        status, val = mem.read(self.dev, Arria10.io_pll + 4 * reg_address["n"])
        if val & 0x10000 == 0:
            msb = val & 0xFF
            lsb = val >> 8  & 0xFF
            n = msb + lsb
        else:
            n = 1
        print "divider    n  =  ", n

        #for reg in ("c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7"):
    #    status, val = mem.read(self.dev, Arria10.io_pll + 4 * reg_address[reg])
        #        if val & 0x10000 == 0:
        #            msb = val & 0xFF
    #        lsb = val >> 8  & 0xFF
        #        divider = msb + lsb
        #        else:
        #            divider = 1
        #        print "divider   ", reg, "= ", divider, ", frequency = ", 100*m/n/divider, " MHz"

        reg = "c0"
        status, val = mem.read(self.dev, Arria10.io_pll + 4 * reg_address[reg])
        if val & 0x10000 == 0:
            msb = val & 0xFF
            lsb = val >> 8  & 0xFF
            divider = msb + lsb
        else:
            divider = 1
        print "divider   ", reg, "= ", divider, ", frequency = ", 100*m/n/divider, " MHz"


    def change_pll_ratio(self, frequency):

        reg_address = {
            "m": 0x90,
            "n": 0xa0,
            "c0": 0xc0,
            "c1": 0xc1,
            "c2": 0xc2,
            "c3": 0xc3,
            "c4": 0xc4,
            "c5": 0xc5,
            "c6": 0xc6,
            "c7": 0xc7,
            "c8": 0xc8
        }

        print
        print "== Changing frequency on c0"
        m = 0x1818
        n = 0x0203
        if   frequency ==  10 : div = 0x3030
        elif frequency ==  20 : div = 0x1818
        elif frequency ==  40 : div = 0x0C0C
        elif frequency ==  80 : div = 0x0606
        elif frequency == 160 : div = 0x0303
        elif frequency == 240 : div = 0x0202
        elif frequency == 480 : div = 0x0101
        elif frequency == 560 :
            m = 0x1C1C
            div = 0x0101
        elif frequency == 600 :
            m = 0x0606
            div = 0x0101
            n = 0x10000
        elif frequency == 620 :
            m = 0x1F1F
            div = 0x0101
        elif frequency == 630 :
            m = 0x201F
            div = 0x0101
        elif frequency == 635 :
            m = 0x403F
            div = 0x0101
            n = 0x0505
        elif frequency == 640 :
            m = 0x1010
            div = 0x10000
        else                 :
            div = 0x3030
            print "Wrong value: autorized values are 10, 20, 40, 80, 160, 240, 480, 560, 600, 620, 630, 635, 640"
            print "Returning to 10 MHz"

        mem.write(self.dev, Arria10.io_pll + 4 * reg_address["m"], m)
        mem.write(self.dev, Arria10.io_pll + 4 * reg_address["n"], n)
        mem.write(self.dev, Arria10.io_pll + 4 * reg_address["c0"], div)

        print "empty FIFO"
        mem.write(self.dev, Arria10.io_pll + 4 * 0, 0)    # write anything in 0 to empty the FIFO
        print

    def set_tx_dis(self):
        mem.write(self.dev, 0x20040, 0x3)
        status, val = mem.read(self.dev,0x20040)
        return val

    def reset_tx_dis(self):
        mem.write(self.dev, 0x20040, 0x0)
        status, val = mem.read(self.dev,0x20040)
        return val

    def write_pattern_ctrl_reg(self, pattern):
        mem.write (self.dev, 0x30010, pattern)
        status, val = mem.read (self.dev, 0x30010)
        return hex(val)

    def clear_gbt_rx_ready_lost_flag(self):
        status, val = mem.read (self.dev, 0x30010)
        val = val | 0x08            # set bit
        mem.write (self.dev, 0x30010, val)
        val = val & 0xF7            # clear bit
        mem.write (self.dev, 0x30010, val)

    def clear_error_seen_flag(self):
        status, val = mem.read (self.dev, 0x30010)
        val = val | 0x04  # set bit
        mem.write (self.dev, 0x30010, val)
        val = val & 0xFB  # clear bit
        mem.write (self.dev, 0x30010, val)

    def read_ready_lost_flag(self, num_phy, channel):
        """
        Read rx_ready lost flag

            Args:
                num_phy    : phy number
                channel    : channel number

            Return:
                read_value : 1 for ready lost, else 0

            """
        mask = 1 << (16 + channel)
        return self.read_mask(0x30020 + 0x10 * num_phy, mask)


    def read_error_seen_flag(self, num_phy, channel):
        """
        Read error seen flag

            Args:
                num_phy    : phy number
                channel    : channel number

            Return:
                read_value : 1 for ready lost, else 0

            """
        mask = 1 << (0 + channel)
        return self.read_mask (0x30020 + 0x10 * num_phy, mask)


    def read_widebus_error_seen_flag(self, num_phy, channel):
        """
        Read widebus error seen flag

            Args:
                num_phy    : phy number
                channel    : channel number

            Return:
                read_value : 1 for ready lost, else 0

            """
        mask = 1 << (8 + channel)
        return self.read_mask (0x30020 + 0x10 * num_phy, mask)


    def read_rx_ready(self, num_phy, channel):
        """
        Read rx_ready flag

            Args:
                num_phy    : phy number
                channel    : channel number

            Return:
                read_value : 1 for ready, 0 for not ready

            """
        mask = 1 << (22 + channel)
        return self.read_mask(0x30020 + 0x10 * num_phy, mask)

    def read_mask(self, reg, mask):
        """
        Read a register with a mask

            Args:
                reg     : register address
                mask    : each bit set to 1 is read, other are ignored

            Return:
                read_value : read value with mask and align right

            """
        status, val = mem.read(self.dev, reg)
        masked_data = val & mask
        while mask & 1 == 0 :
            masked_data = masked_data >> 1
            mask = mask >> 1
        return hex(masked_data)

    def read_version(self):
        """
        Read fpga version


            Return:
                read_value : read value with mask and align right

            """
        base_address = 0x0000
        word = 0
        char = ""
        string = ""
        status, val = mem.read(self.dev, base_address + 0)
        if chr(val) == "{":
            while char != "}":
                status, val = mem.read(self.dev, base_address + 4 * word)
                word += 1
                char = chr(val)
                string += char
        else:
            string = "{\"design_version\" : \"not implemented\"}"

        return json.loads(string)

    def runNumber(self): 
        status, val = mem.read( self.dev , 0x00050020 )
        return ( val & 0xFF00 ) >> 8 | ( val & 0x00FF ) << 8        

    def triggerTag(self): 
        status, val = mem.read( self.dev , 0x00050040 )
        return ( val & 0xFF00 ) >> 8 | ( val & 0x00FF ) << 8  

    def clockUp(self):
        status, val = mem.read( self.dev , 0x00050000 )
        return ( val & 0x800 ) >> 11  

    def ttdUp(self):
        status, val = mem.read( self.dev , 0x00050000 )
        return ( val & 0x1000 ) >> 12  

    def triggerType(self):
        status, val = mem.read( self.dev , 0x00050000 )
        return ( val & 0x1E000 ) >> 13  

    def triggerCounter(self):
        status, val = mem.read( self.dev , 0x00050000 )
        return ( val & 0x1FFE0000 ) >> 17  
 
    def rxReady(self):
        status, val = mem.read( self.dev , 0x00050000 )
        return ( val & 0x1 ) >> 0 

    def rxReadyIndividual(self, i):
        status, val = mem.read( self.dev , 0x00050060 )
        return ( val & ( 0x1 << i ) ) >> i 

    def txReady(self):
        status, val = mem.read( self.dev , 0x00050000 )
        return ( val & 0x2 ) >> 1 

    def txReadyIndividual(self, i):
        status, val = mem.read( self.dev , 0x00050060 )
        return ( val & ( 0x10 << i ) ) >> ( i + 4 )          

    def b2linkReady(self):
        status, val = mem.read( self.dev , 0x00050000 )
        return ( val & 0x4 ) >> 2 

    def b2linkReadyIndividual(self, i):
        status, val1 = mem.read( self.dev , 0x000500C0 )
        status, val2 = mem.read( self.dev , 0x000500E0 )
        return ( ( ( val1 & ( 0x1 << i ) ) >> i ) & ( ( val2 & ( 0x1 << i ) ) >> i ) ) 
        
    def resynchronizeLink(self, i ):
        mem.write( self.dev , 0x00050240 , 0x0 )
        mem.write( self.dev , 0x00050240 , 0x1 << i )
        mem.write( self.dev , 0x00050240 , 0x0 )

    def resetTriggerCounter(self):
        mem.write( self.dev , 0x00050100 , 0x0 )
        mem.write( self.dev , 0x00050100 , 0x2 )
        mem.write( self.dev , 0x00050100 , 0x0 )

def main():
    """ Main function
    instanciation of Class Si534x()
    execute hard test for all PLLs
    """
    fpga = Arria10(0)
    #init LLI reg
    fpga.init_reset_gbt()

    plls = ["SI5344_U54", "SI5345_U23", "SI5345_U48", "SI53344", "SI5340"]
    for pll in plls:
        fpga.dump_pll_frequencies(pll)
    fpga.temperature()

    fpga.read_pll_port_frequency("SI5344_U54", 0)
    fpga.read_pll_port_frequency("SI5344_U54", 1)
    fpga.read_pll_port_frequency("SI5340", 0)
    #
    #
    #
    # print "PLL calibration"
    # for i in range (0, 8):
    #     fpga.reconfig_pll(i)
    #
    #
    # fpga.dump_pll_registers()
    # fpga.change_pll_ratio(10)
    # fpga.dump_pll_registers()
    # status, temperature =  fpga.temperature()
    # print "Temperature = ", temperature """
    # print "Clearing reset for transceivers"
    # fpga.clear_reset_gbt()

    print fpga.read_rx_ready (0, 0)
    print fpga.read_rx_ready (0, 1)
    print fpga.read_rx_ready (0, 2)
    print fpga.read_rx_ready (0, 3)
    print fpga.read_rx_ready (0, 4)
    print fpga.read_rx_ready (0, 5)

if __name__ == "__main__":
    main()