aboutsummaryrefslogtreecommitdiffstats
path: root/mpm
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2017-05-01 13:48:57 -0700
committerMartin Braun <martin.braun@ettus.com>2017-12-22 15:03:52 -0800
commit2906db7528850040bb6f6fdd057b3488898c23aa (patch)
tree2115f2cd21bc71bdde0f86db520aa7297123b8d4 /mpm
parent0e7fe25f42105de0d01fc568cc717f9f04d825b7 (diff)
downloaduhd-2906db7528850040bb6f6fdd057b3488898c23aa.tar.gz
uhd-2906db7528850040bb6f6fdd057b3488898c23aa.tar.bz2
uhd-2906db7528850040bb6f6fdd057b3488898c23aa.zip
mpm: Added more to the EISCAT bringup sequence
Diffstat (limited to 'mpm')
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/eiscat.py219
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)