fmc_tdc.py 11.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
#!   /usr/bin/env   python
#    coding: utf8

# Copyright CERN, 2011
# Author: EGousiou <egousiou@cern.ch>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org

# Import system modules
import sys
import time
import os
import math
import i2c
import onewire
import ds18b20
import eeprom_24aa64

# Add common modules location to path
sys.path.append('../../../')
sys.path.append('../../../gnurabbit/python/')
sys.path.append('../../../common/')

# Import common modules
import rr
from ptsexcept import *
import csr
import gn4124


"""
FMC TDC class
"""
class CFMCTDC:

    # GNUM core WISHBONE adress
    GNUM_CSR_ADDR = 0x0

    # TDC core WISHBONE adress
    TDC_CORE_ADDR = 0x20000

    # IRQ controller
    IRQ_CONTROLLER_ADDR = 0xA0000
    IRQ_CTRL_SRC = 0x4
    IRQ_CTRL_EN_MASK = 0x8

    # Mezzanine I2C
    MEZZ_I2C_ADDR = 0x60000
    MEZZ_EEPROM_ADDR = 0x50

    # Carrier One Wire
    CARR_ONEWIRE_ADDR = 0x40000

    # Mezzanine One Wire
    MEZZ_ONEWIRE_ADDR = 0x80000

    # ACAM register addresses
    ACAM_READBACK_ADDR = [0x40, 0x44, 0x48, 0x4C, 0X50, 0x54, 0x58, 0x5C, 0x60, 0x64, 0x68, 0x6C, 0x70, 0x78]

    # DMA length in bytes
    DMA_LENGTH = 4096

    # Write pointer register
    WP_MASK = 0xFFF
    WP_OVERFLOW_MASK = 0xFFFFF000

    # Timestamp meta-data
    META_CHANNEL_MASK = 0xF
    META_SLOPE_MASK = 0xF0
    META_FIFO_LF_MASK = 0xF00
    META_FIFO_EF_MASK = 0xF000
    META_SLOPE = ['falling', 'rising']


###############################################################################################


    def __init__(self, bus):
        # Objects delcaration
        self.bus = bus;
        self.tdc_regs = csr.CCSR(self.bus, self.TDC_CORE_ADDR)
        self.gnum = gn4124.CGN4124(self.bus, self.GNUM_CSR_ADDR)
        self.irq_controller = csr.CCSR(self.bus, self.IRQ_CONTROLLER_ADDR)

        # Set GNUM local bus frequency
        self.gnum.set_local_bus_freq(160)
        print "Gennum local bus clock %d MHz"%(self.gnum.get_local_bus_freq())
        time.sleep(2)

        # Reset FPGA
        print "Resetting FPGA"
        self.gnum.rstout33_cycle()
        time.sleep(3)

	    # I2C mezzanine
        self.mezz_i2c = i2c.COpenCoresI2C(self.bus, self.MEZZ_I2C_ADDR, 249)
        self.mezz_eeprom_24aa64 = eeprom_24aa64.C24AA64(self.mezz_i2c, self.MEZZ_EEPROM_ADDR)

	    # One Wire mezzanine
	    self.mezz_onewire = onewire.COpenCoresOneWire(self.bus, self.MEZZ_ONEWIRE_ADDR, 624, 124)
        self.mezz_ds18b20 = ds18b20.CDS18B20(self.mezz_onewire, 0)

	    # One Wire carrier
	    self.carr_onewire = onewire.COpenCoresOneWire(self.bus, self.CARR_ONEWIRE_ADDR, 624, 124)
        self.carr_ds18b20 = ds18b20.CDS18B20(self.carr_onewire, 0)

        # Get physical addresses of the memory pages for DMA transfer
        self.dma_pages = self.gnum.get_physical_addr()


###############################################################################################

    # Returns mezzanine unique ID
    def mezz_get_unique_id(self):
        return self.mezz_ds18b20.read_serial_number()


    # Returns mezzanine temperature
    def mezz_get_temp(self):
        serial_number = self.mezz_ds18b20.read_serial_number()
        if(serial_number == -1):
            return -1
        else:
            return self.mezz_ds18b20.read_temp(serial_number)


    # Returns carrier unique ID
    def carr_get_unique_id(self):
        return self.carr_ds18b20.read_serial_number()


    # Returns carrier temperature
    def carr_get_temp(self):
        serial_number = self.carr_ds18b20.read_serial_number()
        if(serial_number == -1):
            return -1
        else:
            return self.carr_ds18b20.read_temp(serial_number)


    # scan FMC i2c bus
    def mezz_i2c_scan(self):
        print 'Scanning I2C bus...',
        return self.mezz_i2c.scan()

    # write to EEPROM on system i2c bus
    def mezz_i2c_eeprom_write(self, addr, data):
        return self.mezz_eeprom_24aa64.wr_data(addr, data)

    # read from  EEPROM on system i2c bus
    def mezz_i2c_eeprom_read(self, addr, size):
        return self.mezz_eeprom_24aa64.rd_data(addr, size)


    # Configures ACAM    
    def config_acam(self):
        print "Loading ACAM and TDC core registers"
        time.sleep(1)
        self.tdc_regs.wr_reg(0x00, 0x1F0FC81)
        self.tdc_regs.wr_reg(0x04, 0x0)
        self.tdc_regs.wr_reg(0x08, 0xE02)
        time.sleep(1)
        self.tdc_regs.wr_reg(0x0C, 0x0)
        self.tdc_regs.wr_reg(0x10, 0x200000F)
        self.tdc_regs.wr_reg(0x14, 0x7D0)
        self.tdc_regs.wr_reg(0x18, 0x3)
        self.tdc_regs.wr_reg(0x1C, 0x1FEA)
        self.tdc_regs.wr_reg(0x2C, 0xFF0000)
        self.tdc_regs.wr_reg(0x30, 0x4000000)
        self.tdc_regs.wr_reg(0x38, 0x0)
        self.tdc_regs.wr_reg(0xFC, 0x4)
        time.sleep(1)

    def load_acam_config(self):
        print "Loading ACAM configuration"
        self.tdc_regs.wr_reg(0xFC, 0x4)
        time.sleep(1)

    def reset_acam(self):
        print "Reseting ACAM"
        self.tdc_regs.wr_reg(0xFC, 0x100)
        time.sleep(1)

   # Configures DAC and PLL
    def configure_mezz_dac(self, dac_word):
        print "Configuring mezzanine DAC"
        self.tdc_regs.wr_reg(0x98, dac_word)
        self.tdc_regs.wr_reg(0xFC, 0x800)
        time.sleep(2)


    def readback_acam_config(self):
        self.tdc_regs.wr_reg(0xFC, 0x8)
        time.sleep(1)
        for i in self.ACAM_READBACK_ADDR:
            print "reg 0x%02X = 0x%08X"%(i, self.tdc_regs.rd_reg(i))

    # Read ACAM status register
    def read_acam_status(self):
        self.tdc_regs.wr_reg(0xFC, 0x10)
        return self.tdc_regs.rd_reg(0x70)

    def enable_channels(self):
        print "Enabling channels"
        self.tdc_regs.wr_bit(0x84, 7, 1)

    def channel_term(self, channel, enable):
        self.tdc_regs.wr_bit(0x84, channel-1, enable)
        #print "0x%08X"%self.tdc_regs.rd_reg(0x84)

    def set_utc(self, utc):
        self.tdc_regs.wr_reg(0x80, utc)
        self.tdc_regs.wr_reg(0xFC, 0x200)

    def get_utc(self):
        return self.tdc_regs.rd_reg(0xA0)

    def start_acq(self):
        self.tdc_regs.wr_reg(0xFC, 0x1)
        print "Aquisition started!"

    def set_irq_tstamp_thresh(self,tstamp_thresh):
        print "Setting IRQ timestamps threshold"
        self.tdc_regs.wr_reg(0x90, tstamp_thresh) # irq tstaps threshold; only 9 LSbits are significant

    def set_irq_time_thresh(self, time_thresh):
        print "Setting IRQ time threshold"
        self.tdc_regs.wr_reg(0x94, time_thresh) # irq time (sec) threshold

    def stop_acq(self):
        self.tdc_regs.wr_reg(0xFC, 0x2)
        print "Aquisition stopped."

    def get_pointer(self):
        return (self.WP_MASK & self.tdc_regs.rd_reg(0xA8))

    def get_overflow_counter(self):
        return ((self.WP_OVERFLOW_MASK & self.tdc_regs.rd_reg(0xA8)) >> 12)

    # Set IRQ enable mask
    def set_irq_en_mask(self, mask):
        self.irq_controller.wr_reg(self.IRQ_CTRL_EN_MASK, mask)
        return self.irq_controller.rd_reg(self.IRQ_CTRL_EN_MASK)

    # Get IRQ source
    def get_irq_src(self):
        return self.irq_controller.rd_reg(self.IRQ_CTRL_SRC)

    # Clear IRQ source
    def clear_irq_src(self, src):
        self.irq_controller.wr_bit(self.IRQ_CTRL_SRC, src, 1)

    # Check for IRQs
    def check_irq(self, verbose=0):
        self.gnum.irq_en()
        self.gnum.wait_irq()
        if(verbose):
            print('#################!!!!GN4124 interrupt occured!!!!#################')


    def get_timestamps(self, verbose=0):
        # Read the number of bytes to be read from the board
        dma_length_tmp = self.get_pointer()
        if dma_length_tmp == 0: # add overflow> 0 
            dma_length = 4096
        else:
            dma_length = dma_length_tmp
        carrier_addr = 0x0

        # Calculate the number of DMA item required to get the data
        items_required = int(math.ceil(dma_length/float(self.DMA_LENGTH)))
        if(128 < items_required):
            print('Required items: %d')%items_required
            raise Exception('Current gn4124 class only supports up to 128 items.')
        if(verbose):
            print('Required items: %d')%items_required

        # Configure DMA
        for num in range(items_required):
            if(items_required == num+1):
                next_item = 0
                item_length = (dma_length-(num*self.DMA_LENGTH))
            else:
                next_item = 1
                item_length = self.DMA_LENGTH
            if(0 == num):
                item_start_addr = carrier_addr
            else:
                item_start_addr = carrier_addr + (num*self.DMA_LENGTH)
            if(verbose):
                print("item nb:%d item_carrier_addr:0x%.8X item_host_addr:0x%.8X item_length:%d next_item:%d)")%(num,item_start_addr,self.dma_pages[num+1],item_length,next_item)
            self.gnum.add_dma_item(item_start_addr, self.dma_pages[num+1], item_length, 0, next_item)
        if(verbose):
            if(items_required > 1):
                items = self.gnum.get_memory_page(0)
                print('DMA items:')
                for i in range(items_required*8):
                    print('%.4X: %.8X')%(i*4,items[i])

        # Start DMA
        dma_finished = 0
        if(verbose):
            print "Start DMA"
        self.gnum.start_dma()

        """
        # Poll on DMA status
        print "Wait for DMA done"
        while('Done' == self.gnum.get_dma_status()):
            time.sleep(0.01)
        print "DMA done!"
        """

        # Wait for end of DMA interrupt
        if(verbose):
            print('Wait GN4124 interrupt')
        self.gnum.wait_irq()
        if(verbose):
            print('GN4124 interrupt occured')
        """
        print('irq mask:%.4X')%self.get_irq_en_mask() if(verbose)
        while(0 == dma_finished):
            irq_src = self.get_irq_source()
            if(verbose):
                print('IRQ source : %.4X')%irq_src
                print('DMA status: %s')%self.gnum.get_dma_status()
            if(irq_src & self.IRQ_SRC_DMA_END):
                print('IRQ source : %.4X')%irq_src if(verbose)
                self.clear_irq_source(self.IRQ_SRC_DMA_END)
                print('IRQ source : %.4X')%self.get_irq_source() if(verbose)
                dma_finished = 1
            time.sleep(0.005)
        print('DMA finished!') if(verbose)
        """
        # Retrieve data from host memory
        data = []
        for i in range(items_required):
            data += self.gnum.get_memory_page(i+1)
        if(verbose):
            print('data length:%d')%(len(data)*4)
            for i in range(len(data)):
                print data[i]

        # Format timestamps
        if(verbose):
            print "Timestamps retreived:"
        raw_timestamps = []
        timestamps = []
        for i in range(0,dma_length/4,4):
            # timestamp value
            timestamp = data[i+2] * 1E12 + data[i+1] * 8E3 + data[i] * 81.03
            # timestamp metadata
            channel = self.META_CHANNEL_MASK & data[i+3]
            slope = ((self.META_SLOPE_MASK & data[i+3]) >> 4)
            fifo_lf = ((self.META_FIFO_LF_MASK & data[i+3]) >> 8)
            fifo_ef = ((self.META_FIFO_EF_MASK & data[i+3]) >> 12)
            # putting everything together
            raw_timestamps.append((data[i+3]<<96)+(data[i+2]<<64)+(data[i+1]<<32)+data[i])
            timestamps.append([timestamp, channel, slope, fifo_lf, fifo_ef])
            #if(verbose):
            #    print "[%03d] sec:%20.3f channel:%d slope:%7s fifo_lf:%d fifo_ef:%d"%(i/4,timestamp, channel, self.META_SLOPE[slope], fifo_lf, fifo_ef)


        if(verbose):
            print "Number of timestamps = %d" % (len(timestamps))
            for i in range(len(timestamps)):
                print timestamps[i]

        return timestamps, data


if __name__ == '__main__' :
    main()