x393_sensor.py 127 KB
Newer Older
1 2 3 4 5
from __future__ import division
from __future__ import print_function

'''
# Copyright (C) 2015, Elphel.inc.
6
# Class to control 10393 sensor-to-memory channel (including histograms)
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
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http:#www.gnu.org/licenses/>.

@author:     Andrey Filippov
@copyright:  2015 Elphel, Inc.
@license:    GPLv3.0+
@contact:    andrey@elphel.coml
@deffield    updated: Updated
'''
__author__ = "Andrey Filippov"
__copyright__ = "Copyright 2015, Elphel, Inc."
__license__ = "GPL"
__version__ = "3.0+"
__maintainer__ = "Andrey Filippov"
__email__ = "andrey@elphel.com"
__status__ = "Development"
#import sys
#import pickle
35 36
import struct

37 38 39 40 41
from x393_mem                import X393Mem
import x393_axi_control_status

import x393_utils

42
import time
43
import vrlg
44
import x393_mcntrl
45 46 47

import subprocess

48 49 50
#import x393_sens_cmprs
SENSOR_INTERFACE_PARALLEL = "PAR12"
SENSOR_INTERFACE_HISPI =    "HISPI"
51
SENSOR_INTERFACE_VOSPI =    "VOSPI"
52

53 54 55 56 57 58
class X393Sensor(object):
    DRY_MODE= True # True
    DEBUG_MODE=1
    x393_mem=None
    x393_axi_tasks=None #x393X393AxiControlStatus
    x393_utils=None
59

60 61 62 63 64 65 66
    verbose=1
    def __init__(self, debug_mode=1,dry_mode=True, saveFileName=None):
        self.DEBUG_MODE=  debug_mode
        self.DRY_MODE=    dry_mode
        self.x393_mem=            X393Mem(debug_mode,dry_mode)
        self.x393_axi_tasks=      x393_axi_control_status.X393AxiControlStatus(debug_mode,dry_mode)
        self.x393_utils=          x393_utils.X393Utils(debug_mode,dry_mode, saveFileName) # should not overwrite save file path
67

68 69 70 71
        try:
            self.verbose=vrlg.VERBOSE
        except:
            pass
72 73 74 75 76
    def getSensorInterfaceType(self):
        """
        Get sensor interface type by reading status register 0xfe that is set to 0 for parallel and 1 for HiSPi
        @return "PAR12" or "HISPI"
        """
77
        if  self.DRY_MODE is True:
78
            print ("===== Running in dry mode, using parallel sensor======")
79
            return SENSOR_INTERFACE_PARALLEL
80
        sens_type = (SENSOR_INTERFACE_PARALLEL, SENSOR_INTERFACE_HISPI,SENSOR_INTERFACE_VOSPI)[self.x393_axi_tasks.read_status(address=0xfe)] # "PAR12" , "HISPI"
81 82
        print ("===== Sensor type read from FPGA = >>> %s <<< ======"%(sens_type))
        return sens_type
83

84 85 86 87 88 89
    def program_status_sensor_i2c( self,
                                   num_sensor,
                                   mode,     # input [1:0] mode;
                                   seq_num): # input [5:0] seq_num;
        """
        Set status generation mode for selected sensor port i2c control
90
        @param num_sensor - number of the sensor port (0..3) or all
91 92 93 94
        @param mode -       status generation mode:
                                  0: disable status generation,
                                  1: single status request,
                                  2: auto status, keep specified seq number,
95
                                  3: auto, inc sequence number
96 97
        @param seq_number - 6-bit sequence number of the status message to be sent
        """
98 99 100 101 102 103 104 105 106
        try:
            if (num_sensor == all) or (num_sensor[0].upper() == "A"): #all is a built-in function
                for num_sensor in range(4):
                    self.program_status_sensor_i2c (num_sensor = num_sensor,
                                                    mode =       mode,
                                                    seq_num =    seq_num)
                return
        except:
            pass
107

108
        self.x393_axi_tasks.program_status (vrlg.SENSOR_GROUP_ADDR  + num_sensor * vrlg.SENSOR_BASE_INC + vrlg.SENSI2C_CTRL_RADDR,
109 110 111 112 113 114 115 116 117 118
                             vrlg.SENSI2C_STATUS,
                             mode,
                             seq_num)# //MCONTR_PHY_STATUS_REG_ADDR=          'h0,

    def program_status_sensor_io( self,
                                  num_sensor,
                                  mode,     # input [1:0] mode;
                                  seq_num): # input [5:0] seq_num;
        """
        Set status generation mode for selected sensor port io subsystem
119
        @param num_sensor - number of the sensor port (0..3) or all
120 121 122 123
        @param mode -       status generation mode:
                                  0: disable status generation,
                                  1: single status request,
                                  2: auto status, keep specified seq number,
124
                                  3: auto, inc sequence number
125 126
        @param seq_number - 6-bit sequence number of the status message to be sent
        """
127 128 129 130 131 132 133 134 135
        try:
            if (num_sensor == all) or (num_sensor[0].upper() == "A"): #all is a built-in function
                for num_sensor in range(4):
                    self.program_status_sensor_io (num_sensor = num_sensor,
                                                   mode =       mode,
                                                   seq_num =    seq_num)
                return
        except:
            pass
136 137 138 139 140 141

        self.x393_axi_tasks.program_status (
                             vrlg.SENSOR_GROUP_ADDR  + num_sensor * vrlg.SENSOR_BASE_INC + vrlg.SENSIO_RADDR,
                             vrlg.SENSIO_STATUS,
                             mode,
                             seq_num)# //MCONTR_PHY_STATUS_REG_ADDR=          'h0,
142

143
    def get_status_sensor_io ( self,
144
                              num_sensor="All"):
145 146 147
        """
        Read sensor_io status word (no sync)
        @param num_sensor - number of the sensor port (0..3)
148
        @return sensor_io status
149
        """
150 151 152 153
        try:
            if (num_sensor == all) or (num_sensor[0].upper() == "A"): #all is a built-in function
                rslt = []
                for num_sensor in range(4):
154
                    rslt.append(self.get_status_sensor_io (num_sensor = num_sensor))
155 156 157
                return rslt
        except:
            pass
158
        return self.x393_axi_tasks.read_status(
159
                    address=(vrlg.SENSI2C_STATUS_REG_BASE + num_sensor * vrlg.SENSI2C_STATUS_REG_INC + vrlg.SENSIO_STATUS_REG_REL))
160 161

    def print_status_sensor_io (self,
162
                                num_sensor="All", sensorType = SENSOR_INTERFACE_PARALLEL):
163 164 165 166
        """
        Print sensor_io status word (no sync)
        @param num_sensor - number of the sensor port (0..3)
        """
167 168 169 170
        try:
            if (num_sensor == all) or (num_sensor[0].upper() == "A"): #all is a built-in function
                for num_sensor in range(4):
                    print ("\n ==== Sensor %d"%(num_sensor))
171
                    self.print_status_sensor_io (num_sensor = num_sensor, sensorType = sensorType)
172 173 174
                return
        except:
            pass
175 176
        status= self.get_status_sensor_io(num_sensor)
        print ("print_status_sensor_io(%d):"%(num_sensor))
177
        
178
        if (sensorType == SENSOR_INTERFACE_VOSPI):
179 180 181 182 183 184 185 186 187
            print ("   segment_id =             %d"%((status>> 0) & 0x0f))
            print ("   gpio_in =                %d"%((status>> 4) & 0x0f))
            print ("   in_busy =                %d"%((status>> 8) & 1))
            print ("   out_busy =               %d"%((status>> 9) & 1))
            print ("   crc_err =                %d"%((status>>10) & 1))
            print ("   fake_in =                %d"%((status>>11) & 1))
            print ("   senspgmin =              %d"%((status>>24) & 1))
            print ("   busy =                   %d"%((status>>25) & 1))
            print ("   seq =                    %d"%((status>>26) & 0x3f))
188
        else:    
189
#last_in_line_1cyc_mclk, dout_valid_1cyc_mclk
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
            """
            print ("   last_in_line_1cyc_mclk = %d"%((status>>23) & 1))
            print ("   dout_valid_1cyc_mclk =   %d"%((status>>22) & 1))
            print ("   alive_hist0_gr =         %d"%((status>>21) & 1))
            print ("   alive_hist0_rq =         %d"%((status>>20) & 1))
            print ("   sof_out_mclk =           %d"%((status>>19) & 1))
            print ("   eof_mclk =               %d"%((status>>18) & 1))
            print ("   sof_mclk =               %d"%((status>>17) & 1))
            print ("   sol_mclk =               %d"%((status>>16) & 1))
            """
            """
            #Folowing 5 bits may be just temporarily available
            print ("   irst =                   %d"%((status>>20) & 1))
            print ("async_prst_with_sens_mrst = %d"%((status>>19) & 1))
            print ("   imrst =                  %d"%((status>>18) & 1))
            print ("   rst_mmcm =               %d"%((status>>17) & 1))
            print ("   pxd_out_pre[1] =         %d"%((status>>16) & 1))
            """
    
            print ("   shifted TDO              %d"%((status>>16) & 0xff))
    
            print ("   vact_alive =             %d"%((status>>15) & 1))
            print ("   hact_ext_alive =         %d"%((status>>14) & 1))
    #        print ("   hact_alive =             %d"%((status>>13) & 1))
            print ("   hact_run =               %d"%((status>>13) & 1))
            print ("   locked_pxd_mmcm =        %d"%((status>>12) & 1))
            print ("   clkin_pxd_stopped_mmcm = %d"%((status>>11) & 1))
            print ("   clkfb_pxd_stopped_mmcm = %d"%((status>>10) & 1))
            print ("   xfpgadone =              %d"%((status>> 9) & 1))
            print ("   ps_rdy =                 %d"%((status>> 8) & 1))
            print ("   ps_out =                 %d"%((status>> 0)  & 0xff))
            print ("   xfpgatdo =               %d"%((status>>25) & 1))
            print ("   senspgmin =              %d"%((status>>24) & 1))
            print ("   seq =                    %d"%((status>>26) & 0x3f))
224 225
#vact_alive, hact_ext_alive, hact_alive
    def get_status_sensor_i2c ( self,
226
                              num_sensor="All"):
227 228 229 230 231
        """
        Read sensor_i2c status word (no sync)
        @param num_sensor - number of the sensor port (0..3)
        @return sesnor_io status
        """
232 233 234 235 236 237 238 239
        try:
            if (num_sensor == all) or (num_sensor[0].upper() == "A"): #all is a built-in function
                rslt = []
                for num_sensor in range(4):
                    rslt.append(self.get_status_sensor_i2c (num_sensor = num_sensor))
                return rslt
        except:
            pass
240
        return self.x393_axi_tasks.read_status(
241
                    address=(vrlg.SENSI2C_STATUS_REG_BASE + num_sensor * vrlg.SENSI2C_STATUS_REG_INC + vrlg.SENSI2C_STATUS_REG_REL))
242 243

    def print_status_sensor_i2c (self,
244
                                num_sensor="All"):
245 246 247 248
        """
        Print sensor_i2c status word (no sync)
        @param num_sensor - number of the sensor port (0..3)
        """
249 250 251 252 253 254 255 256
        try:
            if (num_sensor == all) or (num_sensor[0].upper() == "A"): #all is a built-in function
                for num_sensor in range(4):
                    print ("\n ==== Sensor %d"%(num_sensor))
                    self.print_status_sensor_i2c (num_sensor = num_sensor)
                return
        except:
            pass
257 258
        status= self.get_status_sensor_i2c(num_sensor)
        print ("print_status_sensor_i2c(%d):"%(num_sensor))
259 260 261
        print ("   reset_on =               %d"%((status>>17) & 1))
        print ("   req_clr =                %d"%((status>>16) & 1))
        print ("   frame_num =              %d"%((status>>12) & 0xf))
262
        print ("   wr_full =                %d"%((status>>11) & 1))
263 264 265 266
        print ("   busy =                   %d"%((status>>10) & 1))
        print ("   i2c_fifo_lsb =           %d"%((status>> 9) & 1))
        print ("   i2c_fifo_nempty =        %d"%((status>> 8) & 1))
        print ("   i2c_fifo_dout =          %d"%((status>> 0) & 0xff))
267

268 269 270
        print ("   sda_in =                 %d"%((status>>25) & 1))
        print ("   scl_in =                 %d"%((status>>24) & 1))
        print ("   seq =                    %d"%((status>>26) & 0x3f))
271 272 273

# Functions used by sensor-related tasks
    def func_sensor_mode (self,
274
                          hist_en =   None,
275 276
                          hist_nrst = None,
                          chn_en =    None,
277
                          bits16 =    None):
278 279 280 281
        """
        Combine parameters into sensor mode control word
        @param hist_en -   bit mask to enable histogram sub-modules, when 0 - disable after processing
                           the started frame
282 283
        @param hist_nrst - bit mask to immediately reset histogram sub-module (if 0)
        @param chn_en    - enable sensor channel (False - reset)
284 285 286 287 288
        @param bits16)   - True - 16 bpp mode, false - 8 bpp mode (bypass gamma). Gamma-processed data
                           is still used for histograms
        @return: sensor mode control word
        """
        rslt = 0;
289
        if (not hist_en is None) and (not hist_nrst is None):
290 291 292
            rslt |= (hist_en & 0xf) <<   vrlg.SENSOR_HIST_EN_BITS
            rslt |= (hist_nrst & 0xf) << vrlg.SENSOR_HIST_NRST_BITS
            rslt |= 1 << vrlg.SENSOR_HIST_BITS_SET;
293
        if not chn_en is None:
294 295
            rslt |= ((0,1)[chn_en]) <<   vrlg.SENSOR_CHN_EN_BIT
            rslt |= 1 <<                 vrlg.SENSOR_CHN_EN_BIT_SET
296
        if not bits16 is None:
297 298
            rslt |= ((0,1)[bits16]) <<   vrlg.SENSOR_16BIT_BIT
            rslt |= 1 <<                 vrlg.SENSOR_16BIT_BIT_SET
299
        return rslt
300

301 302 303
    def func_sensor_i2c_command (self,
                                 rst_cmd =   False,
                                 run_cmd =   None,
304
                                 active_sda = None,
305 306
                                 early_release_0 = None,
                                 advance_FIFO = None,
307 308
                                 sda = None,
                                 scl = None,
309
                                 use_eof = None,
310
                                 verbose = 1):
311 312 313
        """
        @param rst_cmd - reset all FIFO (takes 16 clock pulses), also - stops i2c until run command
        @param run_cmd - True - run i2c, False - stop i2c (needed before software i2c), None - no change
314
        @param active_sda - pull-up SDA line during second half of SCL=0, when needed and possible
315 316
        @param early_release_0 -  release SDA=0 immediately after the end of SCL=1 (SDA hold will be provided by week pullup)
        @param advance_FIFO - advance i2c read FIFO
317 318
        @param sda - control SDA line (stopped mode only): I<nput>, L<ow> or 0, High or 1
        @param scl - control SCL line (stopped mode only): I<nput>, L<ow> or 0, High or 1
319
        @param use_eof - advance sequencer at EOF, not at SOF
320 321 322
        @param verbose -          verbose level
        @return combined command word.
        active_sda and early_release_0 should be defined both to take effect (any of the None skips setting these parameters)
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
        """
        def parse_sda_scl(val):
            if val is None:
                return 0
            elif isinstance(val, (unicode,str)):
                if not val:
                    return 0
                if val[0] in "lL0":
                    return 1
                elif val[0] in "hH1":
                    return 2
                elif val[0] in "iI":
                    return 3
                else:
                    print("Unrecognized value for SDA/SCL: %s, should be in lL0hH1iI (or None/ empty string)"%(val))
                    return 0
            else:
                if val == 0:
                    return 1
                elif val == 1:
                    return 2
                else:
                    return 3
346

Andrey Filippov's avatar
Andrey Filippov committed
347
        if verbose>1:
348 349
            print ("func_sensor_i2c_command(): rst_cmd= ",rst_cmd,", run_cmd=",run_cmd,", active_sda = ",active_sda,", early_release_0 = ",early_release_0,
                   ", sda=",sda,", scl=",scl)
350

351 352 353 354 355
        rslt = 0
        rslt |= (0,1)[rst_cmd] << vrlg.SENSI2C_CMD_RESET
        if not run_cmd is None:
            rslt |= 1 <<                 vrlg.SENSI2C_CMD_RUN
            rslt |= (0,1)[run_cmd] <<    (vrlg.SENSI2C_CMD_RUN - vrlg.SENSI2C_CMD_RUN_PBITS)
356 357 358 359 360 361
        if (not active_sda is None) and (not early_release_0 is None):
            rslt |= (0,1)[early_release_0] << vrlg.SENSI2C_CMD_ACIVE_EARLY0
            rslt |= (0,1)[active_sda] << vrlg.SENSI2C_CMD_ACIVE_SDA
            rslt |= 1 <<                 vrlg.SENSI2C_CMD_ACIVE
        if advance_FIFO:
            rslt |= 1 << vrlg.SENSI2C_CMD_FIFO_RD
362 363 364
        if not use_eof is None:
            rslt |= 1 <<                (vrlg.SENSI2C_CMD_USE_EOF + 1)
            rslt |= (0,1)[use_eof] <<   (vrlg.SENSI2C_CMD_USE_EOF)
365 366 367

        rslt |= parse_sda_scl(sda) <<  vrlg.SENSI2C_CMD_SOFT_SDA
        rslt |= parse_sda_scl(scl) <<  vrlg.SENSI2C_CMD_SOFT_SCL
368 369
        if verbose>0:
            print (" => 0x%x"%(rslt))
370

371
        return rslt
372 373 374 375

    def func_sensor_i2c_table_reg_wr (self,
                                 slave_addr,
                                 rah,
376
                                 num_bytes,
377 378 379 380 381
                                 bit_delay,
                                 verbose = 1):
        """
        @param slave_addr - 7-bit i2c slave address
        @param rah -        register address high byte (bits [15:8]) optionally used for register write commands
382
        @param num_bytes -  number of bytes to send (including register address bytes) 1..10
383 384 385
        @param bit_delay -  number of mclk clock cycle in 1/4 of the SCL period
        @param verbose -    verbose level
        @return combined table data word.
386
        """
387
        if verbose>1:
388 389 390 391 392 393
            print ("func_sensor_i2c_table_reg_wr(): slave_addr= ",slave_addr,", rah=",rah,", num_bytes = ",num_bytes,", bit_delay = ",bit_delay)
        rslt = 0
        rslt |= (slave_addr & ((1 << vrlg.SENSI2C_TBL_SA_BITS)   - 1)) << vrlg.SENSI2C_TBL_SA
        rslt |= (rah &        ((1 << vrlg.SENSI2C_TBL_RAH_BITS)  - 1)) << vrlg.SENSI2C_TBL_RAH
        rslt |= (num_bytes &  ((1 << vrlg.SENSI2C_TBL_NBWR_BITS) - 1)) << vrlg.SENSI2C_TBL_NBWR
        rslt |= (bit_delay &  ((1 << vrlg.SENSI2C_TBL_DLY_BITS)  - 1)) << vrlg.SENSI2C_TBL_DLY
394
        return rslt
395 396 397 398 399 400 401 402 403 404 405 406

    def func_sensor_i2c_table_reg_rd (self,
                                 two_byte_addr,
                                 num_bytes_rd,
                                 bit_delay,
                                 verbose = 1):
        """
        @param two_byte_addr - Use a 2-byte register address for read command (False - single byte)
        @param num_bytes_rd -  Number of bytes to read (1..8)
        @param bit_delay -     number of mclk clock cycle in 1/4 of the SCL period
        @param verbose -       verbose level
        @return combined table data word.
407
        """
408 409 410 411 412 413
        if verbose>0:
            print ("func_sensor_i2c_table_reg_rd(): two_byte_addr= ",two_byte_addr,", num_bytes_rd=",num_bytes_rd,", bit_delay = ",bit_delay)
        rslt = 0
        rslt |= 1 << vrlg.SENSI2C_TBL_RNWREG # this is read register command (0 - write register)
        if two_byte_addr > 1:
            two_byte_addr = 1
414
        rslt |= (0,1)[two_byte_addr]                                      << vrlg.SENSI2C_TBL_NABRD
415 416
        rslt |= (num_bytes_rd &  ((1 << vrlg.SENSI2C_TBL_NBRD_BITS) - 1)) << vrlg.SENSI2C_TBL_NBRD
        rslt |= (bit_delay &     ((1 << vrlg.SENSI2C_TBL_DLY_BITS)  - 1)) << vrlg.SENSI2C_TBL_DLY
417
        return rslt
418 419 420 421 422 423 424 425 426 427

    def func_sensor_io_ctl (self,
                            mrst = None,
                            arst = None,
                            aro  = None,
                            mmcm_rst = None,
                            clk_sel = None,
                            set_delays = False,
                            quadrants = None):
        """
428
        Combine sensor I/O control parameters into a control word
429 430 431 432 433
        @param mrst -  True - activate MRST signal (low), False - deactivate MRST (high), None - no change
        @param arst -  True - activate ARST signal (low), False - deactivate ARST (high), None - no change
        @param aro -   True - activate ARO signal (low), False - deactivate ARO (high), None - no change
        @param mmcm_rst - True - activate MMCM reset, False - deactivate MMCM reset, None - no change (needed after clock change/interruption)
        @param clk_sel - True - use pixel clock from the sensor, False - use internal clock (provided to the sensor), None - no chnage
434
        @param set_delays - (self-clearing) load all pre-programmed delays for the sensor pad inputs
435
        @param quadrants -  90-degree shifts for data [1:0], hact [3:2] and vact [5:4] [6] - extra hact delay by 1 pixel (7'h01), None - no change
436 437 438 439
        @return sensor i/o control word
        """
        rslt = 0
        if not mrst is None:
440
            rslt |= (3,2)[mrst] <<     vrlg.SENS_CTRL_MRST
441
        if not arst is None:
442
            rslt |= (3,2)[arst] <<     vrlg.SENS_CTRL_ARST
443
        if not aro is None:
444
            rslt |= (3,2)[aro]  <<     vrlg.SENS_CTRL_ARO
445 446 447 448 449 450 451 452 453 454 455
        if not mmcm_rst is None:
            rslt |= (2,3)[mmcm_rst] << vrlg.SENS_CTRL_RST_MMCM
        if not clk_sel is None:
            rslt |= (2,3)[clk_sel] <<  vrlg.SENS_CTRL_EXT_CLK
        rslt |= (0,1)[set_delays] <<   vrlg.SENS_CTRL_LD_DLY

        if not quadrants is None:
            rslt |= 1 <<  vrlg.SENS_CTRL_QUADRANTS_EN
            rslt |= (quadrants & ((1 << vrlg.SENS_CTRL_QUADRANTS_WIDTH) - 1)) <<  vrlg.SENS_CTRL_QUADRANTS
        return rslt

456
    def func_sensor_io_ctl_lwir (self,
457 458 459
                                 rst =        None,
                                 rst_seq =    None,
                                 spi_seq =    None,
460 461 462 463 464 465 466 467 468 469
                                 mclk  =      None,
                                 spi_en =     None, # 1 - reset+disable, 2 - noreset, disable, 3 - noreset, enable
                                 segm_zero =  None,
                                 out_en =     None,
                                 out_single = False,
                                 reset_crc =  False,
                                 spi_clk =    None,
                                 gpio0 =      None, 
                                 gpio1 =      None, 
                                 gpio2 =      None, 
470 471
#                                 gpio3 =      None,
                                 telemetry =  None, 
472 473
                                 vsync_use =  None,
                                 noresync =   None,
474
                                 
475
                                 dbg_src =    None):
476 477
        """
        Combine sensor I/O control parameters into a control word
478 479 480
        @param rst -        Sensor reset/power down control (0 - NOP, 1 - power down + reset, 2 - no pwdn, reset, 3 - no pwdn, no reset
        @param rst_seq      Initiate simultaneous all sensors reset, generate SOF after pause
        @param spi_seq      Initiate VOSPI reset, will generate normal SOF if successful
481 482 483 484 485 486 487 488 489 490
        @param mclk -       True - enable master clock (25MHz) to sensor, False - disable, None - no change
        @param spi_en -     True - SPI reset/enable: 0 - NOP, 1 - reset+disable, 2 - noreset, disable, 3 - noreset, enable, None - no change 
        @param segm_zero =  True - allow receiving segment ID==0 (ITAR invalid), False - disallow, None - no change,
        @param out_en =     True - enable continuously receiving data to memory, False - disable, None - no change
        @param out_single = True - acquire single frame,
        @param reset_crc =  True - reset CRC error status bit,
        @param spi_clk =    True - generate SPI clock during inactive SPI CS, False - do not generate SPI CLK when CS is inactive, None - no change
        @param gpio0 =      Output control for GPIO0: 0 - nop, 1 - set low, 2 - set high, 3 - input
        @param gpio1 =      Output control for GPIO0: 1 - nop, 1 - set low, 2 - set high, 3 - input 
        @param gpio2 =      Output control for GPIO0: 2 - nop, 1 - set low, 2 - set high, 3 - input 
491
        @param telemetry =  Enable (1) /disable (0) telemetry data lines (should be set in the sensor too, or it will hang)
492 493 494
        @param vsync_use =  Wait for VSYNC (should be enabled over i2c) before reading each segment
        @param noresync =   Disable resynchronization by discard packets
        @param dbg_src =    source of the hardware debug output: 0 - dbg_running
495
                                                                 1 - will_sync
496 497 498 499 500 501
                                                                 2 - vsync_rdy[1]
                                                                 3 - discard_segment
                                                                 4 - in_busy
                                                                 5 - out_busy
                                                                 6 - hact
                                                                 7 - sof
502
        @return VOSPI sensor i/o control word
503 504
        """
        rslt = 0
505 506 507 508 509 510
        if not rst is None:
            rslt |= (rst & 3) <<         vrlg.VOSPI_MRST
        if rst_seq:
            rslt |= 1 <<                 vrlg.VOSPI_RST_SEQ
        if spi_seq:
            rslt |= 1 <<                 vrlg.VOSPI_SPI_SEQ
511 512 513 514 515 516 517 518 519 520 521
        if not mclk is None:
            rslt |= (2,3)[mclk] <<       vrlg.VOSPI_MCLK
        if not spi_en is None:
            rslt |= (spi_en & 3) <<      vrlg.VOSPI_EN
        if not segm_zero is None:
            rslt |= (2,3)[segm_zero] <<  vrlg.VOSPI_SEGM0_OK
        if not out_en is None:
            rslt |= (2,3)[out_en] <<     vrlg.VOSPI_OUT_EN
        if out_single:
            rslt |= 1 <<                 vrlg.VOSPI_OUT_EN_SINGL
        if reset_crc:
522
            rslt |= 1 <<                 vrlg.VOSPI_RESET_ERR
523
        if not spi_clk is None:
524 525 526 527 528 529 530
            rslt |= (2,3)[spi_clk] <<    vrlg.VOSPI_SPI_CLK
        if not gpio0 is None:
            rslt |= (gpio0 & 3) <<       (vrlg.VOSPI_GPIO + 0)
        if not gpio1 is None:
            rslt |= (gpio1 & 3) <<       (vrlg.VOSPI_GPIO + 2)
        if not gpio2 is None:
            rslt |= (gpio2 & 3) <<       (vrlg.VOSPI_GPIO + 4)
531 532 533 534 535 536
            
#        if not gpio3 is None:
#            rslt |= (gpio3 & 3) <<       (vrlg.VOSPI_GPIO + 6)
        if not telemetry is None:
            rslt |= (2,3)[telemetry] <<  vrlg.VOSPI_TELEMETRY
            
537 538 539 540 541 542 543 544 545 546 547 548 549 550
        if not vsync_use is None:
            rslt |= (2,3)[vsync_use] <<  vrlg.VOSPI_VSYNC
        if not noresync is None:
            rslt |= (2,3)[noresync] <<   vrlg.VOSPI_NORESYNC

        if not dbg_src is None:
            rslt |= ((dbg_src & (( 1 << (vrlg.VOSPI_DBG_SRC_BITS - 1)) -1 )) |
                      (1 << (vrlg.VOSPI_DBG_SRC_BITS - 1))) << vrlg.VOSPI_DBG_SRC
            pass    
            
#            .VOSPI_DBG_SRC          (VOSPI_DBG_SRC), // =         26, // source of the debug output
#            .VOSPI_DBG_SRC_BITS     (VOSPI_DBG_SRC_BITS), // =     4,
            
            
551 552 553 554
        return rslt



555 556 557 558 559 560 561 562 563 564 565 566 567
    def func_sensor_jtag_ctl(self,
                             pgmen = None,    # <2: keep PGMEN, 2 - PGMEN low (inactive),  3 - high (active) enable JTAG control
                             prog =  None,    # <2: keep prog, 2 - prog low (active),  3 - high (inactive) ("program" pin control)
                             tck =   None,    # <2: keep TCK,  2 - set TCK low,  3 - set TCK high
                             tms =   None,    # <2: keep TMS,  2 - set TMS low,  3 - set TMS high
                             tdi =   None):   # <2: keep TDI,  2 - set TDI low,  3 - set TDI high
        """
        JTAG interface for programming external sensor multiplexer using shared signal lines on the sensor ports
        @param pgmen - False PGMEN low (inactive),  True - high (active) enable JTAG control, None - keep previous value
        @param prog -  False prog low (active),  True - high (inactive) ("program" pin control), None - keep previous value
        @param tck =   False - set TCK low,  True - set TCK high, None - keep previous value
        @param tms =   False - set TMS low,  True - set TMS high, None - keep previous value
        @param tdi =   False - set TDI low,  True - set TDI high, None - keep previous value
568
        @return combined control word
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
        """
        rslt = 0
        if not pgmen is None:
            rslt |= (2,3)[pgmen] << vrlg.SENS_JTAG_PGMEN
        if not prog is None:
            rslt |= (2,3)[prog] <<  vrlg.SENS_JTAG_PROG
        if not tck is None:
            rslt |= (2,3)[tck] <<   vrlg.SENS_JTAG_TCK
        if not tms is None:
            rslt |= (2,3)[tms] <<   vrlg.SENS_JTAG_TMS
        if not tdi is None:
            rslt |= (2,3)[tdi] <<   vrlg.SENS_JTAG_TDI
        return rslt

    def func_sensor_gamma_ctl(self,
584 585 586 587 588
                              bayer =      None,
                              table_page = None,
                              en_input =   None,
                              repet_mode = None, #  Normal mode, single trigger - just for debugging  TODO: re-assign?
                              trig =       False):
589 590 591 592 593 594 595 596 597
        """
        @param bayer - Bayer shift (0..3)
        @param table_page - Gamma table page
        @param en_input -   Enable input
        @param repet_mode - Repetitive (normal) mode. Set False for debugging, then use trig for single frame trigger
        @param trig       - single trigger (when repet_mode is False), debug feature
        @return combined control word
        """
        rslt = 0
598 599 600
        if not bayer is None:
            rslt |= (bayer & 3) <<       vrlg.SENS_GAMMA_MODE_BAYER
            rslt |=          1  <<       vrlg.SENS_GAMMA_MODE_BAYER_SET
601

602 603 604 605 606 607 608
        if not table_page is None:
            rslt |= (0,1)[table_page] << vrlg.SENS_GAMMA_MODE_PAGE
            rslt |=                1  << vrlg.SENS_GAMMA_MODE_PAGE_SET

        if not en_input is None:
            rslt |= (0,1)[en_input] <<   vrlg.SENS_GAMMA_MODE_EN
            rslt |=              1  <<   vrlg.SENS_GAMMA_MODE_EN_SET
609

610 611 612
        if not repet_mode is None:
            rslt |= (0,1)[repet_mode] << vrlg.SENS_GAMMA_MODE_REPET
            rslt |=                1  << vrlg.SENS_GAMMA_MODE_REPET_SET
613

614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
        rslt |= (0,1)[trig] <<       vrlg.SENS_GAMMA_MODE_TRIG
        return rslt

    def func_status_addr_sensor_i2c(self,
                                    num_sensor):
        """
        @param num_sensor - sensor port number (0..3)
        @return status register address for i2c for selected sensor port
        """
        return (vrlg.SENSI2C_STATUS_REG_BASE + num_sensor * vrlg.SENSI2C_STATUS_REG_INC + vrlg.SENSI2C_STATUS_REG_REL);

    def func_status_addr_sensor_io(self,
                                    num_sensor):
        """
        @param num_sensor - sensor port number (0..3)
        @return status register address for I/O for selected sensor port
        """
        return (vrlg.SENSI2C_STATUS_REG_BASE + num_sensor * vrlg.SENSI2C_STATUS_REG_INC + vrlg.SENSIO_STATUS_REG_REL);
632

633 634
    def set_sensor_mode (self,
                         num_sensor,
635
                         hist_en =   None,
636 637
                         hist_nrst = None,
                         chn_en =    None,
638
                         bits16 =    None):
639 640 641 642 643
        """
        Set sensor mode
        @param num_sensor - sensor port number (0..3)
        @param hist_en -   bit mask to enable histogram sub-modules, when 0 - disable after processing
                           the started frame
644 645
        @param hist_nrst - bit mask to immediately reset histogram sub-module (if 0)
        @param chn_en    - enable sensor channel (False - reset)
646 647 648
        @param bits16)   - True - 16 bpp mode, false - 8 bpp mode (bypass gamma). Gamma-processed data
                           is still used for histograms
        """
649 650 651 652 653
        try:
            if (num_sensor == all) or (num_sensor[0].upper() == "A"): #all is a built-in function
                for num_sensor in range(4):
                    self.set_sensor_mode (num_sensor = num_sensor,
                         hist_en = hist_en,
654 655
                         hist_nrst = hist_nrst,
                         chn_en = chn_en,
656 657 658 659
                         bits16 = bits16)
                return
        except:
            pass
660

661
        self.x393_axi_tasks.write_control_register(vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC + vrlg.SENSOR_CTRL_RADDR,
662 663 664 665 666
                                                  self.func_sensor_mode(
                                                                   hist_en =   hist_en,
                                                                   hist_nrst = hist_nrst,
                                                                   chn_en =    chn_en,
                                                                   bits16 =    bits16))
667 668 669 670




671 672 673

    def set_sensor_i2c_command (self,
                                num_sensor,
674 675
                                rst_cmd =         False,
                                run_cmd =         None,
676
                                active_sda =      None,
677
                                early_release_0 = None,
678 679 680
                                advance_FIFO =    None,
                                sda =             None,
                                scl =             None,
681
                                use_eof =         None,
682
                                verbose =         1):
683
        """
684
        @param num_sensor - sensor port number (0..3) or all
685 686
        @param rst_cmd - reset all FIFO (takes 16 clock pulses), also - stops i2c until run command
        @param run_cmd - True - run i2c, False - stop i2c (needed before software i2c), None - no change
687
        @param active_sda - pull-up SDA line during second half of SCL=0, when needed and possible
688 689
        @param early_release_0 -  release SDA=0 immediately after the end of SCL=1 (SDA hold will be provided by week pullup)
        @param advance_FIFO -     advance i2c read FIFO
690 691
        @param sda - control SDA line (stopped mode only): I<nput>, L<ow> or 0, High or 1
        @param scl - control SCL line (stopped mode only): I<nput>, L<ow> or 0, High or 1
692
        @param use_eof - advance sequencer at EOF, not at SOF
693 694 695
        @param verbose -          verbose level
        active_sda and early_release_0 should be defined both to take effect (any of the None skips setting these parameters)

696 697 698 699 700 701 702
        """
        try:
            if (num_sensor == all) or (num_sensor[0].upper() == "A"): #all is a built-in function
                for num_sensor in range(4):
                    self.set_sensor_i2c_command (num_sensor,
                                rst_cmd =         rst_cmd,
                                run_cmd =         run_cmd,
703
                                active_sda =      active_sda,
704 705 706 707
                                early_release_0 = early_release_0,
                                advance_FIFO =    advance_FIFO,
                                sda =             sda,
                                scl =             scl,
708
                                use_eof =         use_eof,
709 710 711 712 713
                                verbose =         verbose)

                return
        except:
            pass
714 715


716
        self.x393_axi_tasks.write_control_register(vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC + vrlg.SENSI2C_CTRL_RADDR,
717
                                                  self.func_sensor_i2c_command(
718 719 720 721 722
                                                       rst_cmd =         rst_cmd,
                                                       run_cmd =         run_cmd,
                                                       active_sda =      active_sda,
                                                       early_release_0 = early_release_0,
                                                       advance_FIFO =    advance_FIFO,
723 724
                                                       sda =             sda,
                                                       scl =             scl,
725
                                                       use_eof =         use_eof,
726
                                                       verbose =         verbose-1))
727 728 729 730 731 732

    def set_sensor_i2c_table_reg_wr (self,
                                     num_sensor,
                                     page,
                                     slave_addr,
                                     rah,
733
                                     num_bytes,
734 735 736 737 738 739 740 741
                                     bit_delay,
                                     verbose = 1):
        """
        Set table entry for a single index for register write
        @param num_sensor - sensor port number (0..3)
        @param page -       1 byte table index (later provided as high byte of the 32-bit command)
        @param slave_addr - 7-bit i2c slave address
        @param rah -        register address high byte (bits [15:8]) optionally used for register write commands
742
        @param num_bytes -  number of bytes to send (including register address bytes) 1..10
743 744 745 746 747 748 749
        @param bit_delay -  number of mclk clock cycle in 1/4 of the SCL period
        @param verbose -    verbose level
        """
        ta = (1 << vrlg.SENSI2C_CMD_TABLE) | (1 << vrlg.SENSI2C_CMD_TAND) | (page & 0xff)
        td = (1 << vrlg.SENSI2C_CMD_TABLE) | self.func_sensor_i2c_table_reg_wr(
                                               slave_addr = slave_addr,
                                               rah =        rah,
750
                                               num_bytes =  num_bytes,
751
                                               bit_delay =  bit_delay,
752
                                               verbose =    verbose)
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777

        self.x393_axi_tasks.write_control_register(vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC + vrlg.SENSI2C_CTRL_RADDR, ta)
        self.x393_axi_tasks.write_control_register(vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC + vrlg.SENSI2C_CTRL_RADDR, td)

    def set_sensor_i2c_table_reg_rd (self,
                                     num_sensor,
                                     page,
                                     two_byte_addr,
                                     num_bytes_rd,
                                     bit_delay,
                                     verbose = 1):
        """
        Set table entry for a single index for register write
        @param num_sensor -    sensor port number (0..3)
        @param page -          1 byte table index (later provided as high byte of the 32-bit command)
        @param two_byte_addr - Use a 2-byte register address for read command (False - single byte)
        @param num_bytes_rd -  Number of bytes to read (1..8)
        @param bit_delay -     number of mclk clock cycle in 1/4 of the SCL period
        @param verbose -       verbose level
        """
        ta = (1 << vrlg.SENSI2C_CMD_TABLE) | (1 << vrlg.SENSI2C_CMD_TAND) | (page & 0xff)
        td = (1 << vrlg.SENSI2C_CMD_TABLE) | self.func_sensor_i2c_table_reg_rd(
                                               two_byte_addr = two_byte_addr,
                                               num_bytes_rd = num_bytes_rd,
                                               bit_delay =  bit_delay,
778
                                               verbose =    verbose)
779 780
        self.x393_axi_tasks.write_control_register(vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC + vrlg.SENSI2C_CTRL_RADDR, ta)
        self.x393_axi_tasks.write_control_register(vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC + vrlg.SENSI2C_CTRL_RADDR, td)
781 782
        if verbose > 1:
            print ("ta= 0x%x, td = 0x%x"%(ta,td))
783

784 785 786 787 788 789 790
    def write_sensor_reg16(self,
                           num_sensor,
                           reg_addr16,
                           reg_data16):
        """
        Write i2c register in immediate mode
        @param num_sensor - sensor port number (0..3), or "all" - same to all sensors
791 792
        @param reg_addr16 - 16-bit register address (page+low byte, for MT9P006 high byte is an 8-bit slave address = 0x90)
        @param reg_data16 - 16-bit data to write to sensor register
793 794 795 796 797 798
        """
        self.write_sensor_i2c (num_sensor = num_sensor,
                               rel_addr = True,
                               addr = 0,
                               data = ((reg_addr16 & 0xffff) << 16) | (reg_data16 & 0xffff) )

799 800 801 802 803 804 805
    def write_sensor_i2c (self,
                          num_sensor,
                          rel_addr,
                          addr,
                          data):
        """
        Write i2c command to the i2c command sequencer
806
        @param num_sensor - sensor port number (0..3), or "all" - same to all sensors
807 808
        @param rel_addr - True - relative frame address, False - absolute frame address
        @param addr - frame address (0..15)
809 810 811 812 813
        @param data - depends on context:
                      1 - register write: index page, 3 payload bytes. Payload bytes are used according to table and sent
                          after the slave address and optional high address byte other bytes are sent in descending order (LSB- last).
                          If less than 4 bytes are programmed in the table the high bytes (starting with the one from the table) are
                          skipped.
814
                          If more than 4 bytes are programmed in the table for the page (high byte), one or two next 32-bit words
815 816 817 818
                          bypass the index table and all 4 bytes are considered payload ones. If less than 4 extra bytes are to be
                          sent for such extra word, only the lower bytes are sent.
                      2 - register read: index page, slave address (8-bit, with lower bit 0) and one or 2 address bytes (as programmed
                          in the table. Slave address is always in byte 2 (bits 23:16), byte1 (high register address) is skipped if
819
                          read address in the table is programmed to be a single-byte one
820
        """
821 822 823 824 825 826 827 828 829 830
        try:
            if (num_sensor == all) or (num_sensor[0].upper() == "A"): #all is a built-in function
                for num_sensor in range(4):
                    self.write_sensor_i2c (num_sensor = num_sensor,
                                           rel_addr =   rel_addr,
                                           addr =       addr,
                                           data =       data)
                return
        except:
            pass
831 832 833
        reg_addr =  (vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC)
        reg_addr += ((vrlg.SENSI2C_ABS_RADDR,vrlg.SENSI2C_REL_RADDR)[rel_addr] )
        reg_addr += (addr & ~vrlg.SENSI2C_ADDR_MASK);
834
        self.x393_axi_tasks.write_control_register(reg_addr, data)
835 836 837

    def read_sensor_i2c (self,
                         num_sensor,
838 839
                         num_bytes = None,
                         verbose = 0):
840 841 842 843
        """
        Read sequence of bytes available
        @param num_sensor - sensor port number (0..3), or "all" - same to all sensors
        @param num_bytes - number of bytes to read (None - all in FIFO)
844
        @verbose - verbose level
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
        @return list of read bytes
        """
        ODDEVEN="ODDEVEN"
        DAV = "DAV"
        DATA = "DATA"
        def read_i2c_data(num_sensor):
            addr = vrlg.SENSI2C_STATUS_REG_BASE + num_sensor * vrlg.SENSI2C_STATUS_REG_INC + vrlg.SENSI2C_STATUS_REG_REL
            d = self.x393_axi_tasks.read_status(addr)
            return {ODDEVEN : (d >> 9) & 1, DAV : (d >> 8) & 1, DATA : d & 0xff}

        timeout = 1.0 # sec
        end_time = time.time() + timeout
        rslt = []
        while True:
            d = read_i2c_data(num_sensor)
            if not d[DAV]:
                if num_bytes is None:
                    break # no data available in FIFO and number of bytes is not specified
                while (time.time() < end_time) and (not d[DAV]): # wait for data available
                    d = read_i2c_data(num_sensor)
                if not d[DAV]:
                    break # no data available - timeout
            rslt.append(d[DATA])
            # advance to the next data byte
            oddeven = d[ODDEVEN]
            self. set_sensor_i2c_command (
                                num_sensor =   num_sensor,
                                advance_FIFO = True,
873
                                verbose =      verbose)
874 875 876 877 878 879
            # wait until odd/even bit reverses (no timeout here)
            while d[ODDEVEN] == oddeven:
                d = read_i2c_data(num_sensor)
            if len(rslt) == num_bytes:
                break # read all that was requested (num_bytes == None will not get here)
        return  rslt
880

881 882 883 884 885
    def print_sensor_i2c (self,
                          num_sensor,
                          reg_addr,
                          indx =  1,
                          sa7   = 0x48,
886
                          verbose = 1):
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
        """
        Read sequence of bytes available and print the result as a single hex number
        @param num_sensor - sensor port number (0..3), or "all" - same to all sensors
        @param reg_addr - register to read address 1/2 bytes (defined by previously set format)
        @param indx - i2c command index in 1 256-entry table (defines here i2c delay, number of address bytes and number of data bytes)
        @param sa7 - 7-bit i2c slave address
        @param verbose - verbose level
        """
        #clean up FIFO
        dl = self.read_sensor_i2c (num_sensor = num_sensor,
                                   num_bytes = None,
                                   verbose = verbose)
        if len(dl):
            d = 0
            for b in dl:
                d = (d << 8) | (b & 0xff)
            fmt="FIFO contained %d bytes i2c data = 0x%%0%dx"%(len(dl),len(dl*2))
904
            print (fmt%(d))
905 906 907 908 909 910 911 912 913
        #create and send i2c command in ASAP mode:
        i2c_cmd = ((indx & 0xff) << 24) | (sa7 <<17) | (reg_addr & 0xffff)
        #write_sensor_i2c  0 1 0 0x91900004
        self.write_sensor_i2c(num_sensor = num_sensor,
                              rel_addr = 1,
                              addr = 0,
                              data = i2c_cmd)
        time.sleep(0.05) # We do not know how many bytes are expected, so just wait long enough and hope all bytes are in fifo already

914 915


916 917 918 919 920 921 922
        dl = self.read_sensor_i2c (num_sensor = num_sensor,
                                   num_bytes = None,
                                   verbose = verbose)
        if len(dl):
            d = 0
            for b in dl:
                d = (d << 8) | (b & 0xff)
923
            if verbose > 0:
924
                fmt="i2c data[0x%02x:0x%x] = 0x%%0%dx"%(sa7,reg_addr,len(dl)*2)
925
                print (fmt%(d))
926
        return d
927

928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944
    def set_sensor_flipXY(self,
                                  num_sensor,
                                  flip_x =  False,
                                  flip_y =  False,
                                  verbose = 1):
        """
        Set sensor horizontal and vertical mirror (flip)
        @param num_sensor - sensor number or "all"
        @param flip_x -  mirror image around vertical axis
        @param flip_y -  mirror image around horizontal axis
        @param verbose - verbose level
        """
        sensorType = self.getSensorInterfaceType()
        if flip_x is None:
            flip_x = False
        if flip_y is None:
            flip_y = False
945

946
        if sensorType == "PAR12":
947
            data = (0,0x8000)[flip_y] | (0,0x4000)[flip_x]
948 949 950 951
            self.write_sensor_reg16 (num_sensor = num_sensor,
                                     reg_addr16 = 0x9020,
                                     reg_data16 = data)
        elif sensorType == "HISPI":
952
            data = (0,0x8000)[flip_y] | (0,0x4000)[flip_x] | 0x41
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981
            self.write_sensor_reg16 (num_sensor = num_sensor,
                                     reg_addr16 = 0x3040,
                                     reg_data16 = data)
        else:
            raise ("Unknown sensor type: %s"%(sensorType))

    def set_sensor_gains_exposure(self,
                                  num_sensor,
                                  gain_r =   None,
                                  gain_gr =  None,
                                  gain_gb =  None,
                                  gain_b =   None,
                                  exposure = None,
                                  verbose =  1):
        """
        Set sensor analog gains (raw register values) and
        exposure (in scan lines)
        @param num_sensor - sensor number or "all"
        @param gain_r -   RED gain
        @param gain_gr -  GREEN in red row gain
        @param gain_gb -  GREEN in blue row gain
        @param gain_b -   BLUE gain
        @param exposure - exposure time in scan lines
        @param verbose -  verbose level
        """
        sensorType = self.getSensorInterfaceType()
        if sensorType == "PAR12":
            if not gain_r is None:
                self.write_sensor_reg16 (num_sensor = num_sensor,
982
                                         reg_addr16 = 0x902d,
983 984 985 986 987 988 989 990 991 992 993
                                         reg_data16 = gain_r)
            if not gain_gr is None:
                self.write_sensor_reg16 (num_sensor = num_sensor,
                                         reg_addr16 = 0x902b,
                                         reg_data16 = gain_gr)
            if not gain_gb is None:
                self.write_sensor_reg16 (num_sensor = num_sensor,
                                         reg_addr16 = 0x902e,
                                         reg_data16 = gain_gb)
            if not gain_b is None:
                self.write_sensor_reg16 (num_sensor = num_sensor,
994
                                         reg_addr16 = 0x902c,
995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
                                         reg_data16 = gain_b)
            if not exposure is None:
                self.write_sensor_reg16 (num_sensor = num_sensor,
                                         reg_addr16 = 0x9009,
                                         reg_data16 = exposure)
        elif sensorType == "HISPI":
            if not gain_r is None:
                self.write_sensor_reg16 (num_sensor = num_sensor,
                                         reg_addr16 = 0x208,
                                         reg_data16 = gain_r)
            if not gain_gr is None:
                self.write_sensor_reg16 (num_sensor = num_sensor,
                                         reg_addr16 = 0x206, # SMIA register
                                         reg_data16 = gain_gr)
            if not gain_gb is None:
                self.write_sensor_reg16 (num_sensor = num_sensor,
                                         reg_addr16 = 0x20c, # SMIA register
                                         reg_data16 = gain_gb)
            if not gain_b is None:
                self.write_sensor_reg16 (num_sensor = num_sensor,
                                         reg_addr16 = 0x20a, # SMIA register
                                         reg_data16 = gain_b)
            if not exposure is None:
                self.write_sensor_reg16 (num_sensor = num_sensor,
                                         reg_addr16 = 0x202, # SMIA register
                                         reg_data16 = exposure)
        else:
            raise ("Unknown sensor type: %s"%(sensorType))
1023

1024 1025
    def set_sensor_io_ctl (self,
                           num_sensor,
1026 1027 1028 1029 1030
                           mrst =       None,
                           arst =       None,
                           aro  =       None,
                           mmcm_rst =   None,
                           clk_sel =    None,
1031
                           set_delays = False,
1032
                           quadrants =  None):
1033
        """
1034
        Set sensor I/O controls, including I/O signals
1035 1036 1037 1038 1039 1040
        @param num_sensor - sensor port number (0..3)
        @param mrst -  True - activate MRST signal (low), False - deactivate MRST (high), None - no change
        @param arst -  True - activate ARST signal (low), False - deactivate ARST (high), None - no change
        @param aro -   True - activate ARO signal (low), False - deactivate ARO (high), None - no change
        @param mmcm_rst - True - activate MMCM reset, False - deactivate MMCM reset, None - no change (needed after clock change/interruption)
        @param clk_sel - True - use pixel clock from the sensor, False - use internal clock (provided to the sensor), None - no chnage
1041
        @param set_delays - (self-clearing) load all pre-programmed delays for the sensor pad inputs
1042 1043
        @param quadrants -  90-degree shifts for data [1:0], hact [3:2] and vact [5:4] (6'h01), None - no change
        """
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
        try:
            if (num_sensor == all) or (num_sensor[0].upper() == "A"): #all is a built-in function
                for num_sensor in range(4):
                    self.set_sensor_io_ctl (num_sensor,
                           mrst =       mrst,
                           arst =       arst,
                           aro  =       aro,
                           mmcm_rst =   mmcm_rst,
                           clk_sel =    clk_sel,
                           set_delays = set_delays,
                           quadrants =  quadrants)
                return
        except:
            pass

1059

1060 1061 1062 1063 1064 1065 1066 1067
        data = self.func_sensor_io_ctl (
                    mrst =       mrst,
                    arst =       arst,
                    aro =        aro,
                    mmcm_rst =   mmcm_rst,
                    clk_sel =    clk_sel,
                    set_delays = set_delays,
                    quadrants =  quadrants)
1068

1069
        reg_addr = (vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC) + vrlg.SENSIO_RADDR + vrlg.SENSIO_CTRL;
1070
        self.x393_axi_tasks.write_control_register(reg_addr, data)
1071
# TODO: Make one for HiSPi (it is different)
1072 1073

    def set_sensor_io_ctl_lwir (self,
1074
                                 num_sensor,
1075 1076 1077
                                 rst =        None,
                                 rst_seq =    None,
                                 spi_seq =    None,
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
                                 mclk  =      None,
                                 spi_en =     None, # 1 - reset+disable, 2 - noreset, disable, 3 - noreset, enable
                                 segm_zero =  None,
                                 out_en =     None,
                                 out_single = False,
                                 reset_crc =  False,
                                 spi_clk =    None,
                                 gpio0 =      None, 
                                 gpio1 =      None, 
                                 gpio2 =      None, 
1088 1089
#                                 gpio3 =      None,
                                 telemetry =  None, 
1090 1091 1092
                                 vsync_use =  None,
                                 noresync =   None,
                                 dbg_src =    None):
1093 1094
        """
        Combine sensor I/O control parameters into a control word
1095 1096 1097
        @param rst -        Sensor reset/power down control (0 - NOP, 1 - power down + reset, 2 - no pwdn, reset, 3 - no pwdn, no reset
        @param rst_seq      Initiate simultaneous all sensors reset, generate SOF after pause
        @param spi_seq      Initiate VOSPI reset, will generate normal SOF if successful
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107
        @param mclk -       True - enable master clock (25MHz) to sensor, False - disable, None - no change
        @param spi_en -     True - SPI reset/enable: 0 - NOP, 1 - reset+disable, 2 - noreset, disable, 3 - noreset, enable, None - no change 
        @param segm_zero =  True - allow receiving segment ID==0 (ITAR invalid), False - disallow, None - no change,
        @param out_en =     True - enable continuously receiving data to memory, False - disable, None - no change
        @param out_single = True - acquire single frame,
        @param reset_crc =  True - reset CRC error status bit,
        @param spi_clk =    True - generate SPI clock during inactive SPI CS, False - do not generate SPI CLK when CS is inactive, None - no change
        @param gpio0 =      Output control for GPIO0: 0 - nop, 1 - set low, 2 - set high, 3 - input
        @param gpio1 =      Output control for GPIO0: 1 - nop, 1 - set low, 2 - set high, 3 - input 
        @param gpio2 =      Output control for GPIO0: 2 - nop, 1 - set low, 2 - set high, 3 - input 
1108
        @param telemetry =  Enable (1) /disable (0) telemetry data lines (should be set in the sensor too, or it will hang)
1109 1110 1111
        @param vsync_use =  Wait for VSYNC (should be enabled over i2c) before reading each segment
        @param noresync =   Disable resynchronization by discard packets
        @param dbg_src =    source of the hardware debug output: 0 - dbg_running
1112
                                                                 1 - will_sync
1113 1114 1115 1116 1117 1118
                                                                 2 - vsync_rdy[1]
                                                                 3 - discard_segment
                                                                 4 - in_busy
                                                                 5 - out_busy
                                                                 6 - hact
                                                                 7 - sof
1119 1120 1121 1122 1123
        """
        try:
            if (num_sensor == all) or (num_sensor[0].upper() == "A"): #all is a built-in function
                for num_sensor in range(4):
                    self.set_sensor_io_ctl_lwir (num_sensor,
1124 1125 1126
                           rst =        rst,
                           rst_seq =    rst_seq,
                           spi_seq =    spi_seq,
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
                           mclk  =      mclk,
                           spi_en =     spi_en,
                           segm_zero =  segm_zero,
                           out_en =     out_en,
                           out_single = out_single,
                           reset_crc =  reset_crc,
                           spi_clk =    spi_clk,
                           gpio0 =      gpio0, 
                           gpio1 =      gpio1, 
                           gpio2 =      gpio2, 
1137 1138
#                           gpio3 =      gpio3, 
                           telemetry =  telemetry, 
1139 1140 1141
                           vsync_use =  vsync_use,
                           noresync =   noresync,
                           dbg_src =    dbg_src)
1142 1143 1144 1145
                return
        except:
            pass
        data = self.func_sensor_io_ctl_lwir (
1146 1147 1148
                           rst =        rst,
                           rst_seq =    rst_seq,
                           spi_seq =    spi_seq,
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
                           mclk  =      mclk,
                           spi_en =     spi_en,
                           segm_zero =  segm_zero,
                           out_en =     out_en,
                           out_single = out_single,
                           reset_crc =  reset_crc,
                           spi_clk =    spi_clk,
                           gpio0 =      gpio0, 
                           gpio1 =      gpio1, 
                           gpio2 =      gpio2, 
1159 1160
#                           gpio3 =      gpio3, 
                           telemetry =  telemetry, 
1161 1162 1163
                           vsync_use =  vsync_use,
                           noresync =   noresync,
                           dbg_src =    dbg_src)
1164 1165 1166 1167

        reg_addr = (vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC) + vrlg.SENSIO_RADDR + vrlg.SENSIO_CTRL;
        self.x393_axi_tasks.write_control_register(reg_addr, data)

1168 1169 1170 1171 1172 1173 1174
    def set_sensor_io_dly_parallel (self,
                                    num_sensor,
                                    mmcm_phase,
                                    iclk_dly,
                                    vact_dly,
                                    hact_dly,
                                    pxd_dly):
1175 1176 1177 1178 1179 1180 1181
        """
        Set sensor port input delays and mmcm phase
        @param num_sensor - sensor port number (0..3)
        @param mmcm_phase - MMCM clock phase
        @param iclk_dly - delay in the input clock line (3 LSB are not used)
        @param vact_dly - delay in the VACT line (3 LSB are not used)
        @param hact_dly - delay in the HACT line (3 LSB are not used)
1182
        @param pxd_dly - list of data line delays (12 elements, 3 LSB are not used)
1183 1184 1185 1186
        """
        dlys=((pxd_dly[0] & 0xff) | ((pxd_dly[1] & 0xff) << 8) | ((pxd_dly[ 2] & 0xff) << 16) | ((pxd_dly[ 3] & 0xff) << 24),
              (pxd_dly[4] & 0xff) | ((pxd_dly[5] & 0xff) << 8) | ((pxd_dly[ 6] & 0xff) << 16) | ((pxd_dly[ 7] & 0xff) << 24),
              (pxd_dly[8] & 0xff) | ((pxd_dly[9] & 0xff) << 8) | ((pxd_dly[10] & 0xff) << 16) | ((pxd_dly[11] & 0xff) << 24),
1187
              (hact_dly & 0xff) |   ((vact_dly & 0xff) <<   8) | ((iclk_dly & 0xff)    << 16) | ((mmcm_phase & 0xff) <<  24))
1188
        reg_addr = (vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC) + vrlg.SENSIO_RADDR + vrlg.SENSIO_DELAYS;
1189 1190 1191 1192
        self.x393_axi_tasks.write_control_register(reg_addr + 0, dlys[0]) # {pxd3,       pxd2,  pxd1, pxd0}
        self.x393_axi_tasks.write_control_register(reg_addr + 1, dlys[1]) # {pxd7,       pxd6,  pxd5, pxd4}
        self.x393_axi_tasks.write_control_register(reg_addr + 2, dlys[2]) # {pxd11,      pxd10, pxd9, pxd8}
        self.x393_axi_tasks.write_control_register(reg_addr + 3, dlys[3]) # {mmcm_phase, bpf,   vact, hact}
1193 1194
        self.set_sensor_io_ctl (num_sensor = num_sensor,
                                set_delays = True)
1195 1196 1197

    def set_sensor_io_dly_hispi (self,
                                    num_sensor,
1198
                                    mmcm_phase = None, #24 steps in 3ns period
1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209
                                    lane0_dly =  None,
                                    lane1_dly =  None,
                                    lane2_dly =  None,
                                    lane3_dly =  None):
        """
        Set sensor port input delays and mmcm phase
        @param num_sensor - sensor port number (0..3) or all, 'A'
        @param mmcm_phase - MMCM clock phase
        @param lane0_dly - delay in the lane0 (3 LSB are not used) // All 4 lane delays should be set simultaneously
        @param lane1_dly - delay in the lane1 (3 LSB are not used)
        @param lane2_dly - delay in the lane2 (3 LSB are not used)
1210
        @param lane3_dly - delay in the lane3 (3 LSB are not used))
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228
        """
        try:
            if (num_sensor == all) or (num_sensor[0].upper() == "A"): #all is a built-in function
                for num_sensor in range(4):
                    self.set_sensor_io_dly_hispi (num_sensor = num_sensor,
                                                  mmcm_phase = mmcm_phase,
                                                  lane0_dly =  lane0_dly,
                                                  lane1_dly =  lane1_dly,
                                                  lane2_dly =  lane2_dly,
                                                  lane3_dly =  lane3_dly)
                return
        except:
            pass
        reg_addr = (vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC) + vrlg.SENSIO_RADDR + vrlg.SENSIO_DELAYS
        try: # if any delay is None - do not set
            dlys=(lane0_dly & 0xff) | ((lane1_dly & 0xff) << 8) | ((lane2_dly & 0xff) << 16) | ((lane3_dly & 0xff) << 24)
            self.x393_axi_tasks.write_control_register(reg_addr + 2, dlys)
        except:
1229
            pass
1230 1231
        if not mmcm_phase is None:
            self.x393_axi_tasks.write_control_register(reg_addr + 3, mmcm_phase & 0xff)
1232

1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
    def set_sensor_hispi_lanes(self,
                               num_sensor,
                               lane0 = 0,
                               lane1 = 1,
                               lane2 = 2,
                               lane3 = 3):
        """
        Set HiSPi sensor lane map (physical lane for each logical lane)
        @param num_sensor - sensor port number (0..3)
        @param lane0 - physical (input) lane number for logical (internal) lane 0
        @param lane1 - physical (input) lane number for logical (internal) lane 1
        @param lane2 - physical (input) lane number for logical (internal) lane 2
        @param lane3 - physical (input) lane number for logical (internal) lane 3
        """
        data = ((lane0 & 3) << 0 ) | ((lane1 & 3) << 2 ) | ((lane2 & 3) << 4 ) | ((lane3 & 3) << 6 )
        reg_addr = (vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC) + vrlg.SENSIO_RADDR + vrlg.SENSIO_DELAYS;
        self.x393_axi_tasks.write_control_register(reg_addr + 1, data)
1250

1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261
    def set_sensor_fifo_lag(self,
                            num_sensor,
                            fifo_lag = 7):
        """
        Set HiSPi sensor FIFO lag (when to start line output, ~= 1/2 FIFO size)
        @param num_sensor - sensor port number (0..3)
        @param fifo_lag - number of pixels to write to FIFO before starting output
        """
        reg_addr = (vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC) + vrlg.SENSIO_RADDR + vrlg.SENSIO_DELAYS;
        self.x393_axi_tasks.write_control_register(reg_addr + 0, fifo_lag)

1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
    def set_sensor_io_jtag (self,
                            num_sensor,
                            pgmen = None,    # <2: keep PGMEN, 2 - PGMEN low (inactive),  3 - high (active) enable JTAG control
                            prog =  None,    # <2: keep prog, 2 - prog low (active),  3 - high (inactive) ("program" pin control)
                            tck =   None,    # <2: keep TCK,  2 - set TCK low,  3 - set TCK high
                            tms =   None,    # <2: keep TMS,  2 - set TMS low,  3 - set TMS high
                            tdi =   None):   # <2: keep TDI,  2 - set TDI low,  3 - set TDI high
        """
        JTAG interface for programming external sensor multiplexer using shared signal lines on the sensor ports
        @param num_sensor - sensor port number (0..3)
        @param pgmen - False PGMEN low (inactive),  True - high (active) enable JTAG control, None - keep previous value
        @param prog -  False prog low (active),  True - high (inactive) ("program" pin control), None - keep previous value
        @param tck =   False - set TCK low,  True - set TCK high, None - keep previous value
        @param tms =   False - set TMS low,  True - set TMS high, None - keep previous value
        @param tdi =   False - set TDI low,  True - set TDI high, None - keep previous value
        """
        reg_addr = (vrlg.SENSOR_GROUP_ADDR + num_sensor * vrlg.SENSOR_BASE_INC) + vrlg.SENSIO_RADDR + vrlg.SENSIO_JTAG;
        data = self.func_sensor_jtag_ctl (
                            pgmen = pgmen,
                            prog =  prog,
                            tck =   tck,
                            tms =   tms,
                            tdi =   tdi)
1285
        self.x393_axi_tasks.write_control_register(reg_addr, data)
1286 1287 1288 1289 1290 1291 1292

#    def jtag_prep_status(self, chn):
#        seq_num = ((self.get_status_sensor_io(num_sensor = chn) >> 26) + 1) & 0x3f
#        self.program_status_sensor_io(num_sensor = num_sensor,
#                                      mode = 1,     # input [1:0] mode;
#                                      seq_num = seq_num) # input [5:0] seq_num;
#        return seq_num
1293

1294 1295 1296 1297 1298
    def jtag_get_tdo(self, chn):
        seq_num = ((self.get_status_sensor_io(num_sensor = chn) >> 26) + 1) & 0x3f
        self.program_status_sensor_io(num_sensor = chn,
                                      mode = 1,     # input [1:0] mode;
                                      seq_num = seq_num) # input [5:0] seq_num;
1299

1300 1301 1302
        for _ in range(10):
            stat = self.get_status_sensor_io(num_sensor = chn)
            if seq_num == ((stat >> 26) & 0x3f):
1303
                break
1304 1305
        else:
            print ("wait_sensio_status(): Failed to get seq_num== 0x%x, current is 0x%x"%(seq_num, (stat >> 26) & 0x3f))
1306 1307
        return (stat >> 25) & 1

1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319


    def jtag_send(self, chn, tms, ln, d):
        i = ln & 7
        if (i == 0):
            i = 8
        d &= 0xff;
        r = 0
        while i > 0:
            self.set_sensor_io_jtag (num_sensor = chn,
                            pgmen = None,
                            prog =  None,
1320
                            tck =   0,
1321 1322 1323 1324 1325 1326 1327
                            tms =   tms,
                            tdi =   ((d << 1) >> 8) & 1)
            d <<= 1
            r = (r << 1) + self.jtag_get_tdo(chn)
            self.set_sensor_io_jtag (num_sensor = chn,
                            pgmen = None,
                            prog =  None,
1328
                            tck =   1,
1329 1330 1331 1332 1333
                            tms =   None,
                            tdi =   None)
            self.set_sensor_io_jtag (num_sensor = chn,
                            pgmen = None,
                            prog =  None,
1334
                            tck =   0,
1335 1336 1337 1338
                            tms =   None,
                            tdi =   None)
            i -= 1
        return r
1339

1340 1341 1342 1343 1344 1345
    def jtag_write_bits (self,
                         chn,
                         buf,    # data to write
                         ln,     # number of bits to write
#                         check,  # compare readback data with previously written, abort on mismatch
                         last):   # output last bit with TMS=1
1346
#                         prev = None): # if null - don't use
1347 1348 1349 1350 1351 1352 1353 1354 1355
        rbuf = []
        r = 0
        for d0 in buf:
            d=d0
            for _ in range(8):
                if ln >0:
                    self.set_sensor_io_jtag (num_sensor = chn,
                                    pgmen = None,
                                    prog =  None,
1356
                                    tck =   0,
1357 1358 1359 1360 1361 1362 1363
                                    tms =   (0,1)[(ln == 1) and last],
                                    tdi =   ((d << 1) >> 8) & 1)
                    d <<= 1
                    r = (r << 1) + self.jtag_get_tdo(chn)
                    self.set_sensor_io_jtag (num_sensor = chn,
                                    pgmen = None,
                                    prog =  None,
1364
                                    tck =   1,
1365 1366 1367 1368 1369
                                    tms =   None,
                                    tdi =   None)
                    self.set_sensor_io_jtag (num_sensor = chn,
                                    pgmen = None,
                                    prog =  None,
1370
                                    tck =   0,
1371 1372 1373 1374
                                    tms =   None,
                                    tdi =   None)
                else:
                    r <<= 1
1375
                ln -= 1
1376
            rbuf.append(r & 0xff)
1377

1378
        return rbuf
1379

1380 1381 1382 1383
    def jtag_set_pgm_mode(self,chn,en):
        self.set_sensor_io_jtag (num_sensor = chn,
                        pgmen = en,
                        prog =  None,
1384
                        tck =   0,
1385 1386 1387 1388 1389 1390 1391
                        tms =   None,
                        tdi =   None)

    def jtag_set_pgm(self,chn,en):
        self.set_sensor_io_jtag (num_sensor = chn,
                        pgmen = None,
                        prog =  en,
1392
                        tck =   0,
1393 1394
                        tms =   None,
                        tdi =   None)
1395 1396


1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420
    def JTAG_openChannel (self, chn):
        self.jtag_set_pgm_mode (chn, 1);
        self.jtag_set_pgm      (chn, 1)
        self.jtag_set_pgm      (chn, 0)
        time.sleep        (0.01)
        self.jtag_send    (chn, 1, 5, 0 ) # set Test-Logic-Reset state
        self.jtag_send    (chn, 0, 1, 0 ) # set Run-Test-Idle state

    def JTAG_EXTEST     (self,  chn, buf, ln):
#        self.jtag_send(chn, 1, 5, 0   ) # step 1 - set Test-Logic-Reset state
#        self.jtag_send(chn, 0, 1, 0   ) # step 2 - set Run-Test-Idle state
        self.jtag_send(chn, 1, 2, 0   ) # step 3 - set SELECT-IR state
        self.jtag_send(chn, 0, 2, 0   ) # step 4 - set SHIFT-IR state
        self.jtag_send(chn, 0, 5, 0xf0) # step 5 - start of EXTEST
        self.jtag_send(chn, 1, 1, 0   ) # step 6 - finish EXTEST
        self.jtag_send(chn, 1, 2, 0   ) # step 7 - set SELECT-DR state
        self.jtag_send(chn, 0, 2, 0   ) # step 8 - set CAPTURE-DR state

        rbuf = self.jtag_write_bits (chn = chn,
                                     buf = buf,    # data to write
                                     ln =  ln,     # number of bytes to write
                                     last = 1)
        self.jtag_send(chn, 1, 1, 0   ) #step 9 - set UPDATE-DR state
        return rbuf
1421 1422


1423

1424 1425 1426 1427 1428 1429 1430
# /dev/sfpgabscan0
    def readbscan(self, filename):
        ffs=struct.pack("B",0xff)*97
        with open(filename,'r+') as jtag:
            jtag.write(ffs)
            jtag.seek (0,0)
            boundary= jtag.read(97)
1431 1432
        return boundary

1433 1434 1435 1436 1437 1438 1439 1440 1441
    def checkSclSda(self, chn, verbose = 1):
        '''
        Check which board is connected to the sensor board
        @param chn - sensor port number (0..3)
        @param verbose - if >0, print debug output
        @return - name of the FPGA-based board detected, "sensor" (grounded pad 7) or "" if none detected
        '''
        def print_i2c(chn):
            self.program_status_sensor_i2c(num_sensor = chn, mode = 1, seq_num = 0)
1442
            status= self.get_status_sensor_i2c(num_sensor = chn)
1443 1444 1445
            sda_in =(status>>25) & 1
            scl_in =(status>>24) & 1
            print ("chn = %d, scl = %d, sda = %d"%(chn,scl_in, sda_in))
1446 1447

        def print_bv(chn, boundary, value, key):
1448
            self.program_status_sensor_i2c(num_sensor = chn, mode = 1, seq_num = 0)
1449
            status= self.get_status_sensor_i2c(num_sensor = chn)
1450 1451 1452 1453 1454
            sda_in =(status>>25) & 1
            scl_in =(status>>24) & 1
            print ("%d: sda = %d, bit number SDA = %d, pin value SDA = %d"%(key, sda_in, value['sda'], (((ord(boundary[value['sda'] >> 3]) >> (7 -(value['sda'] & 7))) &1)) ))
            print ("%d: scl = %d, bit number SCL = %d, pin value SCL = %d"%(key, scl_in, value['scl'], (((ord(boundary[value['scl'] >> 3]) >> (7 -(value['scl'] & 7))) &1)) ))

1455

1456 1457 1458 1459 1460 1461 1462 1463 1464
        boards = [{'model':'10347', 'scl': 241,'sda': 199},  #// E4, C1
                  {'model':'10359', 'scl': 280,'sda': 296}]  #// H6, J5
        bscan_path=('/dev/sfpgabscan%d'%(chn))
        self. program_status_sensor_io(num_sensor = chn, mode = 1, seq_num = 0)
        status = self.get_status_sensor_io(num_sensor=chn)
        senspgmin = (status >> 24) & 1
        if not senspgmin:
            print ("Some sensor board is connected to port # %d, not FPGA"%(chn))
            return "sensor"
1465

1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508
        test = [1]*len(boards)
        #Stop hardware i2c controller
        self.set_sensor_i2c_command(num_sensor = chn,    run_cmd = False)
        #Set SCL=0, SDA=0 and read values:
        self.set_sensor_i2c_command(num_sensor = chn,    sda = 0,  scl = 0)
        if verbose > 0:
            print_i2c(chn = chn)
        boundary = self.readbscan(bscan_path)
        for key, value in enumerate(boards):
            test[key] &= ((((ord(boundary[value['sda'] >> 3]) >> (7 -(value['sda'] & 7))) &1) == 0) and
                          (((ord(boundary[value['scl'] >> 3]) >> (7 -(value['scl'] & 7))) &1) == 0))
            if verbose >0:
                print_bv(chn=chn, boundary = boundary, value = value, key=key)
        #Set SCL=1, SDA=0 and read values:
        self.set_sensor_i2c_command(num_sensor = chn,    sda = 0,  scl = 1)
        boundary = self.readbscan(bscan_path)
        for key, value in enumerate(boards):
            test[key] &= ((((ord(boundary[value['sda'] >> 3]) >> (7 -(value['sda'] & 7))) &1) == 0) and
                          (((ord(boundary[value['scl'] >> 3]) >> (7 -(value['scl'] & 7))) &1) == 1))
            if verbose >0:
                print_bv(chn=chn, boundary = boundary, value = value, key=key)
        #Set SCL=0, SDA=1 and read values:
        self.set_sensor_i2c_command(num_sensor = chn,    sda = 1,  scl = 0)
        boundary = self.readbscan(bscan_path)
        for key, value in enumerate(boards):
            test[key] &= ((((ord(boundary[value['sda'] >> 3]) >> (7 -(value['sda'] & 7))) &1) == 1) and
                          (((ord(boundary[value['scl'] >> 3]) >> (7 -(value['scl'] & 7))) &1) == 0))
            if verbose >0:
                print_bv(chn=chn, boundary = boundary, value = value, key=key)
        #Set SCL=1, SDA=1 and read values:
        self.set_sensor_i2c_command(num_sensor = chn,    sda = 1,  scl = 1)
        boundary = self.readbscan(bscan_path)
        for key, value in enumerate(boards):
            test[key] &= ((((ord(boundary[value['sda'] >> 3]) >> (7 -(value['sda'] & 7))) &1) == 1) and
                          (((ord(boundary[value['scl'] >> 3]) >> (7 -(value['scl'] & 7))) &1) == 1))
            if verbose >0:
                print_bv(chn=chn, boundary = boundary, value = value, key=key)
        for key, value in enumerate(boards):
            if test[key]:
                if verbose >0:
                    print ("Detected FPGA-based board :%s"%(value['model']))
                return value['model']
        return ""
1509 1510


1511
    """
1512 1513 1514 1515
   def set_sensor_i2c_command (self,
                                num_sensor,
                                rst_cmd =         False,
                                run_cmd =         None,
1516
                                active_sda =      None,
1517 1518 1519 1520 1521 1522 1523 1524
                                early_release_0 = None,
                                advance_FIFO =    None,
                                sda =             None,
                                scl =             None,
                                verbose =         1):
        @param num_sensor - sensor port number (0..3)
        @param rst_cmd - reset all FIFO (takes 16 clock pulses), also - stops i2c until run command
        @param run_cmd - True - run i2c, False - stop i2c (needed before software i2c), None - no change
1525
        @param active_sda - pull-up SDA line during second half of SCL=0, when needed and possible
1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552
        @param early_release_0 -  release SDA=0 immediately after the end of SCL=1 (SDA hold will be provided by week pullup)
        @param advance_FIFO -     advance i2c read FIFO
        @param sda - control SDA line (stopped mode only): I<nput>, L<ow> or 0, High or 1
        @param scl - control SCL line (stopped mode only): I<nput>, L<ow> or 0, High or 1
        @param verbose -          verbose level
        active_sda and early_release_0 should be defined both to take effect (any of the None skips setting these parameters)
    def program_status_sensor_i2c( self,
                                   num_sensor,
                                   mode,     # input [1:0] mode;
                                   seq_num): # input [5:0] seq_num;

    def print_status_sensor_i2c (self,
                                num_sensor="All"):
        Print sensor_i2c status word (no sync)
        @param num_sensor - number of the sensor port (0..3)
        try:
            if (num_sensor == all) or (num_sensor[0].upper() == "A"): #all is a built-in function
                for num_sensor in range(4):
                    print ("\n ==== Sensor %d"%(num_sensor))
                    self.print_status_sensor_i2c (num_sensor = num_sensor)
                return
        except:
            pass
        status= self.get_status_sensor_i2c(num_sensor)
        print ("print_status_sensor_i2c(%d):"%(num_sensor))
        print ("   reset_on =               %d"%((status>> 7) & 1))
        print ("   req_clr =                %d"%((status>> 6) & 1))
1553
        print ("   wr_full =               %d"%((status>> 5) & 1))
1554