diff options
Diffstat (limited to 'mpm/python')
| -rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/eiscat.py | 219 | 
1 files changed, 133 insertions, 86 deletions
| diff --git a/mpm/python/usrp_mpm/dboard_manager/eiscat.py b/mpm/python/usrp_mpm/dboard_manager/eiscat.py index 4534e124d..55caa79f9 100644 --- a/mpm/python/usrp_mpm/dboard_manager/eiscat.py +++ b/mpm/python/usrp_mpm/dboard_manager/eiscat.py @@ -30,7 +30,7 @@ N_CHANS = 8 # Chans per dboard  # Power enable pins  POWER_ENB = 0x200C # Address of the power enable register -PWR_CHAN_EN_2V5 = [ (1<<x) for x in xrange(8) ] +PWR_CHAN_EN_2V5 = [(1<<x) for x in xrange(8)]  PWR2_5V_DC_CTRL_ENB = 1<<8  PWR2_5V_DC_PWR_EN = 1<<9  PWR2_5V_LNA_CTRL_EN = 1<<10 @@ -38,6 +38,8 @@ PWR2_5V_LMK_SPI_EN = 1<<11  PWR2_5V_ADC0_SPI_EN = 1<<12  PWR2_5V_ADC1_SPI_EN = 1<<13 +ADC_RESET = 0x2008 +  class ADS54J56(object):      """      Controls for ADS54J56 ADC @@ -66,44 +68,59 @@ class ADS54J56(object):          self.regs.poke8(0x000053, 0x80) # Set clk divider to div-2          self.regs.poke8(0x000039, 0xC0) # ALWAYS WRITE 1 to this bit          self.regs.poke8(0x000059, 0x20) # ALWAYS WRITE 1 to this bit +        readback_test_addr = 0x11 +        readback_test_val = self.regs.peek8(readback_test_addr) +        self.log.trace("ADC readback reg 0x{:x} post-init: 0x{:x}".format( +            readback_test_addr, +            readback_test_val, +        )) -    # def setup(self): -# 11	80	0	0	Select Master page in Analog Bank -# 53	80	0	0	Set clk divider to div-2 -# 39	C0	0	0	ALWAYS WRITE 1 to this bit -# 59	20	0	0	ALWAYS WRITE 1 to this bit -# 4004	68	0	0	 -# 4003	0	0	0	 -# 6000	1	0	0	Reset interleaving engine for Ch A-B -# 6000	0	0	0	 -# 7000	1	0	0	Reset interleaving engine for Ch C-D -# 7000	0	0	0	 -# 4004	61	0	0	Select decimation filter page of JESD bank. -# 4003	41	0	0	 -# 6000	E4	0	0	DDC Mode 4 for A-B and E = CH A/B N value -# 7000	E4	0	0	DDC Mode 4 for A-B and E = CH A/B N value -# 6001	4	0	0	ALWAYS WRITE 1 to this bit -# 7001	4	0	0	ALWAYS WRITE 1 to this bit -# 6002	0E	0	0	Ch A/D N value -# 7002	0E	0	0	Ch A/D N value -# 4003	0	0	0	Select analog page in JESD Bank -# 4004	6A	0	0	 -# 6016	2	0	0	PLL mode 40x for A-B -# 7016	2	0	0	PLL mode 40x for C-D -# 4003	0	0	0	Select digital page in JESD Bank -# 4004	69	0	0	 -# 6000	40	0	0	Enable JESD Mode control for A-B -# 6001	2	0	0	Set JESD Mode to 40x for LMFS=2441 -# 7000	40	0	0	Enable JESD Mode control for C-D -# 7001	2	0	0	Set JESD Mode to 40x for LMFS=2441 -# 6000	80	0	0	Set CTRL K for A-B -# 6006	0F	0	0	Set K to 16 -# 7000	80	0	0	Set CTRL K for C-D -# 7006	0F	0	0	Set K to 16 -# 4005	1	0	0	Disable broadcast mode -# 7001	20	0	0	SyncbAB to issue a SYNC request for all 4 channels -# 4005	0	0	0	Enable broadcast mode +    def setup(self): +        """ +        Enable the ADC for streaming +        """ +        self.regs.poke8(0x0011, 0x80) # Select Master page in Analog Bank +        self.regs.poke8(0x0053, 0x80) # Set clk divider to div-2 +        self.regs.poke8(0x0039, 0xC0) # ALWAYS WRITE 1 to this bit +        self.regs.poke8(0x0059, 0x20) # ALWAYS WRITE 1 to this bit +        self.regs.poke8(0x4004, 0x68) # +        self.regs.poke8(0x4003, 0x00) # +        self.regs.poke8(0x6000, 0x01) # Reset interleaving engine for Ch A-B +        self.regs.poke8(0x6000, 0x00) # +        self.regs.poke8(0x7000, 0x01) # Reset interleaving engine for Ch C-D +        self.regs.poke8(0x7000, 0x00) # +        self.regs.poke8(0x4004, 0x61) # Select decimation filter page of JESD bank. +        self.regs.poke8(0x4003, 0x41) # +        self.regs.poke8(0x6000, 0xE4) # DDC Mode 4 for A-B and E = CH A/B N value +        self.regs.poke8(0x7000, 0xE4) # DDC Mode 4 for A-B and E = CH A/B N value +        self.regs.poke8(0x6001, 0x04) # ALWAYS WRITE 1 to this bit +        self.regs.poke8(0x7001, 0x04) # ALWAYS WRITE 1 to this bit +        self.regs.poke8(0x6002, 0x0E) # Ch A/D N value +        self.regs.poke8(0x7002, 0x0E) # Ch A/D N value +        self.regs.poke8(0x4003, 0x00) # Select analog page in JESD Bank +        self.regs.poke8(0x4004, 0x6A) # +        self.regs.poke8(0x6016, 0x02) # PLL mode 40x for A-B +        self.regs.poke8(0x7016, 0x02) # PLL mode 40x for C-D +        self.regs.poke8(0x4003, 0x00) # Select digital page in JESD Bank +        self.regs.poke8(0x4004, 0x69) # +        self.regs.poke8(0x6000, 0x40) # Enable JESD Mode control for A-B +        self.regs.poke8(0x6001, 0x02) # Set JESD Mode to 40x for LMFS=2441 +        self.regs.poke8(0x7000, 0x40) # Enable JESD Mode control for C-D +        self.regs.poke8(0x7001, 0x02) # Set JESD Mode to 40x for LMFS=2441 +        self.regs.poke8(0x6000, 0x80) # Set CTRL K for A-B +        self.regs.poke8(0x6006, 0x0F) # Set K to 16 +        self.regs.poke8(0x7000, 0x80) # Set CTRL K for C-D +        self.regs.poke8(0x7006, 0x0F) # Set K to 16 +        self.regs.poke8(0x4005, 0x01) # Disable broadcast mode +        self.regs.poke8(0x7001, 0x20) # SyncbAB to issue a SYNC request for all 4 channels +        self.regs.poke8(0x4005, 0x00) # Enable broadcast mode +        readback_test_addr = 0x11 +        readback_test_val = self.regs.peek8(readback_test_addr) +        self.log.trace("ADC readback reg 0x{:x} post-init: 0x{:x}".format( +            readback_test_addr, +            readback_test_val, +        ))  class MMCM(object): @@ -152,32 +169,14 @@ class MMCM(object):          return True  class JesdCoreEiscat(object): +    """ +    Wrapper for the JESD core. Note this core is specifically adapted for +    EISCAT, it is not general-purpose. +    """      CORE_ID_BASE = 0x4A455344      ADDR_BASE = 0x0000      ADDR_OFFSET = 0x1000 -# Check Core	100	4a455344	1	FFFFFFFF	check signature -	# 104		1		date should match the export number of the core. format: yymmddhh -					 -# GT PLL Power Control	C	FFFC0000	0	0	Power down unused CPLLs and QPLLs -					 -# GT PLL Lock Control	4	11111111	0	0	Reset CPLLs -	# 4	11111100	0	0	Unreset the ones we're using -	# wait 2 ms - alternatively, poll on the locked bit below				 -	# 10	10000	0	0	Clear all CPLL sticky bits -	# 4	22	1	FF	Read CPLL locked and no unlocked stickies. Error: GT PLL failed to lock. -					 -# GT RX Reset Routine	24	10	0	0	Place the RX MGTs in reset -	# 24	20	0	0	Unreset and Enable -	# 24	F0000	1	FFFF0000	Poll for reset complete for 20 ms. b1111 = number of GTs we use. Error: TX MGTs failed to reset -					 -# Init FPGA JESD204B Deframer (RX)	40	2	0	0	Force assertion of ADC SYNC -	# 50	0	0	0	Data = 0 = Scrambler enabled. Data = 1 = disabled. Must match ADC settings. Set to 1 for now -	# GT RX Reset Routine (full sequence)				 -	# 50	0	0	0	Stop forcing assertion of ADC SYNC -					 -# Check Deframer Status (RX)	40	3000001C	1	FFFFFFFF	 -      def __init__(self, regs, slot, core_idx, log):          self.log = log          self.regs = regs @@ -214,12 +213,38 @@ class JesdCoreEiscat(object):      def init(self):          """          Run initialization sequence on JESD core. + +        Returns None, but will throw if there's a problem.          """          self._gt_pll_power_control()          self._gt_rx_reset(True)          if not self._gt_pll_lock_control():              raise RuntimeError("JESD CORE {} PLLs not locked!".format(self.core_idx)) +    def init_deframer(self): +        """ +        Init FPGA JESD204B Deframer (RX) + +        Returns nothing, but throws on error. +        """ +        self.poke32(0x40, 0x02) # Force assertion of ADC SYNC +        self.poke32(0x50, 0x01) # Data = 0 = Scrambler enabled. Data = 1 = disabled. Must match ADC settings. +        if not self._gt_rx_reset(reset_only=False): +            raise RuntimeError("JESD Core did not come out of reset properly!") +        self.poke32(0x50, 0x00) # Stop forcing assertion of ADC SYNC + +    def check_deframer_status(self): +        """ +        Check deframer status (who would have thought) + +        Returns True if deframer status is good. +        """ +        deframer_status = self.peek32(0x40) & 0xFFFFFFFF +        if deframer_status != 0x3000001C: +            self.log.error("Unexpected JESD Core Deframer Status: {:x}".format(deframer_status)) +            return False +        return True +      def _gt_pll_power_control(self):          """          Power down unused CPLLs and QPLLs @@ -228,7 +253,10 @@ class JesdCoreEiscat(object):      def _gt_rx_reset(self, reset_only=True):          """ -        RX Reset +        RX Reset. Either only puts it into reset, or also pulls it out of reset +        and makes sure lock status is correct. + +        Returns True on success.          """          self.poke32(0x024, 0x10) # Place the RX MGTs in reset          if not reset_only: @@ -238,8 +266,10 @@ class JesdCoreEiscat(object):              lock_status = self.peek32(0x024) & 0xFFFF0000              if lock_status != 0xF0000:                  self.log.error( -                    "Error: TX MGTs failed to reset! Status: 0x{:x}".format(lock_status) +                    "JESD Core {}: TX MGTs failed to reset! Status: 0x{:x}".format(self.core_idx, lock_status)                  ) +                return False +        return True      def _gt_pll_lock_control(self):          """ @@ -269,6 +299,8 @@ class JesdCoreEiscat(object):              ))          self.poke32(0x80, reg_val) + +  class EISCAT(DboardManagerBase):      """      EISCAT Daughterboard @@ -313,47 +345,56 @@ class EISCAT(DboardManagerBase):          self.log.debug("Loaded C++ drivers.")          self.log.debug("Getting uio...")          self.radio_regs = UIO(label="jesd204b-regs", read_only=False) -        jesd_id = self.radio_regs.peek32(0x100) -        self.log.trace("Reading JESD core ID: {:x}".format(jesd_id)) -        if jesd_id != 0x4A455344: -            self.log.error("Cannot identify JESD core 0! Read ID: {:x}".format(jesd_id)) -        jesd_id = self.radio_regs.peek32(0x1100) -        self.log.trace("Reading JESD core ID: {:x}".format(jesd_id)) -        if jesd_id != 0x4A455345: -            self.log.error("Cannot identify JESD core 1! Read ID: {:x}".format(jesd_id)) -            self.log.error("Cannot identify JESD core! Read ID: {:x}".format(jesd_id)) -        jesd_id = self.radio_regs.peek32(0x1100) -        self.log.trace("Reading JESD core ID: {:x}".format(jesd_id)) -        if jesd_id != 0x4A455345: -            self.log.error("Cannot identify JESD core! Read ID: {:x}".format(jesd_id)) +        # Create JESD cores. They will also test the UIO regs on initialization. +        self.jesd_cores = [ +            JesdCoreEiscat( +                self.radio_regs, +                "A", # TODO fix hard-coded slot number +                x, +                self.log +            ) for x in xrange(2) +        ]          self.log.info("Radio-register UIO object successfully generated!") + +        self.radio_regs.poke32(ADC_RESET, 0x0000) # TODO put this somewhere else + +        # Initialize Clocking          self.mmcm = MMCM(self.radio_regs, self.log) -        self.init_power(self.radio_regs) +        self._init_power(self.radio_regs)          self.mmcm.reset()          self.lmk = LMK04828EISCAT(self.lmk_regs, self.spi_lock, "A") # Initializes LMK          if not self.mmcm.enable():              self.log.error("Could not re-enable MMCM!")              raise RuntimeError("Could not re-enable MMCM!")          self.log.info("MMCM enabled!") +        # Initialize ADCs and JESD cores +        for i in xrange(2): +            self.jesd_cores[i].init()          self.adc0 = ADS54J56(self.adc0_regs, self.log)          self.adc1 = ADS54J56(self.adc1_regs, self.log)          self.adc0.reset()          self.adc1.reset()          self.log.info("ADCs resetted!") -# Send SYSREF		Pulse the "f2SendSysRefToAdcA" and "f2SendSysRefToAdcB" signals in the JESD nelist for 1 FpgaClk2x cycle. This will eventually be a timed command from the Radio. -# Initialize ADC -A		These are all SPI transactions. -# Initialize ADC -B		See the "ADC Setup" tab. -# Init FPGA JESD204B Deframer (RX) -A		See function call in "JESD204b FPGA Core Setup" tab. -# Init FPGA JESD204B Deframer (RX) -B		Same as -A. -# Send SYSREF		Same as the first Send SYSREF call. -# Check Deframer Status (RX) -A		See function call in "JESD204b FPGA Core Setup" tab. -# Check Deframer Status (RX) -B		Same as -A. - -    def init_power(self, regs): +        return +        # Send SYSREF FIXME +        self.adc0.setup() +        self.adc1.setup() +        self.log.info("ADCs set up!") +        for i in xrange(2): +            self.jesd_cores[i].init_deframer() +        # Send SYSREF FIXME +        for i in xrange(2): +            if not self.jesd_cores[i].check_deframer_status(): +                raise RuntimeError("JESD Core {}: Deframer status not lookin' so good!".format(i)) +        ## END OF THE JEPSON SEQUENCE ## + + +    def _init_power(self, regs):          """          Turn on power to the dboard. -        After this function, we should never touch this register again. +        After this function, we should never touch this register again (other +        than turning it off again).          """          reg_val = PWR2_5V_DC_CTRL_ENB          self.log.trace("Asserting power ctrl enable ({})...".format(bin(reg_val))) @@ -372,4 +413,10 @@ class EISCAT(DboardManagerBase):          self.log.trace("Asserting power enable for all the channels ({})...".format(bin(reg_val)))          regs.poke32(POWER_ENB, reg_val) +    def _deinit_power(self, regs): +        """ +        Turn off power to the dboard. +        """ +        self.log.trace("Disabling power to the daughterboard...") +        regs.poke32(POWER_ENB, 0x0000) | 
