linuswck 90f18a2a00 pl init: Correct the initialization sequence
- Si5340 should be initialized first before calling any PL register since if
    Si5340 is not preprogrammed, there is not PL system clock driving any PL register.
- Adc Initialization causes the PL MMCM to relock and trigger a global PL reset. Thus,
    CSR registers should only be altered after Adc is initialized successfully.
2025-04-08 12:51:39 +08:00

210 lines
7.2 KiB
Python

# This file is part of Fast Servo Software Package.
#
# Copyright (C) 2023 Jakub Matyas
# Warsaw University of Technology <jakubk.m@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
#
# 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 <https://www.gnu.org/licenses/>.
import time
import spidev
from pyfastservo.common import (
CH0_HIGH_WORD_ADDR,
CH0_LOW_WORD_ADDR,
CH1_HIGH_WORD_ADDR,
CH1_LOW_WORD_ADDR,
CTRL_ADDR,
MAP_MASK,
PAGESIZE,
write_to_memory,
read_from_memory
)
# /dev/spidev2.0 <=> spidev<BUS>.<DEVICE>
MAIN_DAC_BUS = 2
MAIN_DAC_DEVICE = 0
DAC_VERSION = 0x0A
def inc_ddr_clk_phase():
curr_cfg = read_from_memory(CTRL_ADDR, 1)[0] & 0x07
write_to_memory(CTRL_ADDR, 0x10 | curr_cfg) # Set MMCM Phase Shift to be INC
write_to_memory(CTRL_ADDR, 0x18 | curr_cfg) # Assert MMCM Phase Shift EN High
write_to_memory(CTRL_ADDR, curr_cfg) # Deassert MMCM Phase Shift EN High
def dec_ddr_clk_phase():
curr_cfg = read_from_memory(CTRL_ADDR, 1)[0] & 0x07
write_to_memory(CTRL_ADDR, 0x00 | curr_cfg) # Set MMCM Phase Shift to be DEC
write_to_memory(CTRL_ADDR, 0x08 | curr_cfg) # Assert MMCM Phase Shift EN High
write_to_memory(CTRL_ADDR, curr_cfg) # Deassert MMCM Phase Shift EN High
def spi_write(spi, address, value):
spi.xfer2([address, value])
def spi_read(spi, address):
rx_buffer = spi.xfer2([0x80 | address, 0x00])
return rx_buffer[1]
def verify_registers_vals(spi, reg_to_check):
for register, expected_value in reg_to_check.items():
value = spi_read(spi, register)
if value != expected_value:
print(f"Different value read than sent in reg 0x{register:02x}. Expected: 0x{expected_value:02x} Got: 0x{value:02x}")
return False
return True
def soft_reset(spi):
spi_write(spi, 0x00, 0x10) # Software reset
spi_write(spi, 0x00, 0x00) # Release software reset
spi_read(spi, 0x00) # Read reset address (necessary for reset to take effect)
def check_version(spi):
version = spi_read(spi, 0x1F)
print(f"DAC version: 0x{version:02X}")
return version == DAC_VERSION
def configure_dac(spi):
register_settings = {
0x01: 0x40, # Clear EXTREF bit for internal reference,
0x02: 0xB4, # Enable 2's complement, IFirst: True, IRising: True, DCI_EN: Enabled
0x0D: 0x00, # Set RREF to 10 kΩ for 1.0V reference
0x04: 0xA0, # Enable on-chip IRSET (1.6 kΩ for 20mA output)
0x07: 0xA0, # Enable on-chip QRSET (1.6 kΩ for 20mA output)
0x05: 0x00, # Disable internal IRCML
0x08: 0x00, # Disable internal QRCML
}
for addr, val in register_settings.items():
spi_write(spi, addr, val)
success = verify_registers_vals(spi, register_settings)
if success:
# Set manual override output default back to 0V in 2's complement
set_dac_input_override_value(0x0000)
return success
def dac_self_calibration(spi):
spi_write(spi, 0x12, 0x00) # Reset calibration status
spi_write(spi, 0x0E, 0x08) # Enable calibration clock, default divide ratio
spi_write(spi, 0x0E, 0x38) # CALSELI = 1, CALSELQ = 1, CALCLK = 1
spi_write(spi, 0x12, 0x10) # Set CALEN bit
while True:
status = spi_read(spi, 0x0F)
if status & 0xC0 == 0xC0: # Both CALSTATI and CALSTATQ are 1
break
time.sleep(0.01)
spi_write(spi, 0x12, 0x00) # Clear calibration bits
spi_write(spi, 0x0E, 0x30) # Keep CALSELI and CALSELQ set, clear CALCLK
print("DAC self-calibration completes")
return True
def set_dac_input_override(enable=True):
# It is enabled by default after PL reset.
# Disable it during PL init to routes the DAC output from linien-server
reg_contents = read_from_memory(CTRL_ADDR, 1)[0]
to_write = reg_contents | 0b1 if enable else reg_contents & 0b110
write_to_memory(CTRL_ADDR, to_write)
print(f"Enable DAC Input Override: {enable}")
def power_down_afe(channel, power_down=True):
# It is enabled by default after PL reset.
# Disable it during PL init
assert channel in (0, 1)
bitmask = 1 << (channel + 1) & 0b111
reg_contents = read_from_memory(CTRL_ADDR, 1)[0]
value = (1 if power_down else 0) << (channel + 1)
reg_contents &= ~bitmask
to_write = reg_contents | value
write_to_memory(CTRL_ADDR, to_write)
reg_contents = read_from_memory(CTRL_ADDR, 1)[0]
print(f"Power Down DAC AFE Ch{channel}: {power_down}")
def set_dac_input_override_value(value):
value = min(value, 0x3FFF)
low_word = value & 0xFF
high_word = (value >> 8) & 0x3F
# Note: DAC HIGH word and LOW word output are not updated
# at the same time. On scope, you will see more than one step
# of value changed.
write_to_memory(CH0_HIGH_WORD_ADDR, high_word)
write_to_memory(CH0_LOW_WORD_ADDR, low_word)
write_to_memory(CH1_HIGH_WORD_ADDR, high_word)
write_to_memory(CH1_LOW_WORD_ADDR, low_word)
print(f"Set DAC Input Override Value to: 0x{value:04X}")
def check_clk_relationship(spi):
# Trigger the retimer to reacquire the clock relationship
spi_write(spi, 0x14, 0x00)
spi_write(spi, 0x14, 0x08)
spi_write(spi, 0x14, 0x00)
clkmode_reg = spi_read(spi, 0x14)
print(f"AD9117 CLKMODE reg: 0x{clkmode_reg:02X}")
if clkmode_reg & 0b00010000:
print("Ad9117 Clock relationship is established")
return False
else:
print("Ad9117 Clock relationship is found")
return True
def configure_ad9117():
print()
print("### Initializing AD9117 Dac")
success = True
spi = spidev.SpiDev()
spi.open(MAIN_DAC_BUS, MAIN_DAC_DEVICE)
spi.max_speed_hz = 5000
spi.mode = 0b00 # CPOL = 0 CPHA = 0
spi.cshigh = False
try:
soft_reset(spi)
if not check_version(spi):
print("Unrecognized DAC version")
return False
# Disable DAC Output during Initialization
spi_write(spi, 0x01, 0x46)
success &= verify_registers_vals(spi, {0x01: 0x46})
success &= configure_dac(spi)
success &= check_clk_relationship(spi)
success &= dac_self_calibration(spi)
power_down_afe(0, False)
power_down_afe(1, False)
if success:
set_dac_input_override(False)
# Re-Enable DAC Output
spi_write(spi, 0x01, 0x40)
verify_registers_vals(spi, {0x01: 0x40})
print("AD9117 Dac init completed")
else:
print("AD9117 Dac init fails")
return success
except Exception as e:
print(f"Error configuring AD9117 Dac: {e}")
return False
finally:
spi.close()
if __name__ == "__main__":
configure_ad9117()