#!/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()