forked from M-Labs/nix-servo
Compare commits
26 Commits
master
...
adc_dac_ne
Author | SHA1 | Date | |
---|---|---|---|
55aaf00a74 | |||
af3812e258 | |||
e22237e347 | |||
d799fd0d08 | |||
ba07d6a7de | |||
0f628aecc7 | |||
9e34cd9dff | |||
125611f51e | |||
25524856b9 | |||
79315d3d1b | |||
8b20379427 | |||
15a66c01fa | |||
2d75b4112e | |||
ed816517cf | |||
f5729d3bdb | |||
e3b1525125 | |||
b92d401f2b | |||
eed43e3fe3 | |||
b2dedd77ad | |||
693e301ccf | |||
6fca060256 | |||
85af23e547 | |||
c9d34348bc | |||
db88e5db59 | |||
7d5fbdd281 | |||
fc71fe0ab3 |
141
fast-servo/linien-pyrp3-monitor.patch
Normal file
141
fast-servo/linien-pyrp3-monitor.patch
Normal file
@ -0,0 +1,141 @@
|
||||
# Fix for bus error issues when compiling cpython extensions in pyrp3 v1.2.0+
|
||||
# Patch sourced from: https://github.com/linien-org/pyrp3/tree/e6688acf8bd79d2dbe1d192d09c1a1baf1f6c67b (setup.py & monitor/Makefile)
|
||||
# Reference: https://github.com/elhep/Fast-Servo-Firmware/blob/master/OS/scripts/linien_install_requirements.sh#L28
|
||||
|
||||
diff --git a/monitor/Makefile b/monitor/Makefile
|
||||
new file mode 100644
|
||||
index 0000000..044d88e
|
||||
--- /dev/null
|
||||
+++ b/monitor/Makefile
|
||||
@@ -0,0 +1,31 @@
|
||||
+# Makefile for libmonitor
|
||||
+
|
||||
+OBJS = monitor.o
|
||||
+SRCS = $(subst .o,.c, $(OBJS))
|
||||
+OSOBJS = monitor.os
|
||||
+TARGETLIB=libmonitor.so
|
||||
+CFLAGS=-g -std=gnu99 -Wall -Werror
|
||||
+LIBS=-lm -lpthread
|
||||
+
|
||||
+# Use CROSS_COMPILE=arm-linux-gnueabi-
|
||||
+CC=$(CROSS_COMPILE)gcc
|
||||
+INSTALL_DIR ?= .
|
||||
+
|
||||
+
|
||||
+all: $(TARGETLIB)
|
||||
+lib: $(TARGETLIB)
|
||||
+
|
||||
+%.os: %.c
|
||||
+ $(CC) -c -fPIC $(CFLAGS) $< -o $@
|
||||
+
|
||||
+$(TARGETLIB): $(OSOBJS)
|
||||
+ $(CC) -o $@ -shared $^ $(CFLAGS) $(LIBS)
|
||||
+
|
||||
+clean:
|
||||
+ rm -f $(TARGETLIB) *.o *.os
|
||||
+
|
||||
+# Install target - creates 'lib/' sub-directory in $(INSTALL_DIR) and copies all
|
||||
+# executables to that location.
|
||||
+install:
|
||||
+ mkdir -p $(INSTALL_DIR)/lib
|
||||
+ cp $(TARGETLIB) $(INSTALL_DIR)/lib
|
||||
\ No newline at end of file
|
||||
diff --git a/pyrp3/raw_memory.py b/pyrp3/raw_memory.py
|
||||
index ce1b28e..233b82a 100644
|
||||
--- a/pyrp3/raw_memory.py
|
||||
+++ b/pyrp3/raw_memory.py
|
||||
@@ -1,12 +1,9 @@
|
||||
from ctypes import POINTER, c_uint32, cast, cdll, create_string_buffer, sizeof
|
||||
-from importlib.machinery import EXTENSION_SUFFIXES
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
-libmonitor_file = str(
|
||||
- Path(__file__).parent / ".." / "monitor{}".format(EXTENSION_SUFFIXES[0])
|
||||
-)
|
||||
+libmonitor_file = 'libmonitor.so'
|
||||
|
||||
libmonitor = cdll.LoadLibrary(libmonitor_file)
|
||||
libmonitor.read_value.restype = c_uint32
|
||||
diff --git a/setup.py b/setup.py
|
||||
index 98bdaee..b0a8af4 100644
|
||||
--- a/setup.py
|
||||
+++ b/setup.py
|
||||
@@ -1,5 +1,10 @@
|
||||
import re
|
||||
-from distutils.core import Extension, setup
|
||||
+import os
|
||||
+
|
||||
+from distutils.core import setup
|
||||
+from distutils.command.build import build
|
||||
+from distutils.command.install import install
|
||||
+
|
||||
from pathlib import Path
|
||||
|
||||
# from https://stackoverflow.com/a/7071358/2750945
|
||||
@@ -11,9 +16,50 @@ if mo:
|
||||
verstr = mo.group(1)
|
||||
else:
|
||||
raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,))
|
||||
+
|
||||
+# Patch from https://github.com/linien-org/pyrp3/blob/e6688acf8bd79d2dbe1d192d09c1a1baf1f6c67b/setup.py#L16-L55
|
||||
+build_dir = "monitor/"
|
||||
+
|
||||
+def compile_libmonitor():
|
||||
+ cwd = os.getcwd() # get current directory
|
||||
+ try:
|
||||
+ os.chdir(build_dir)
|
||||
+ os.system("make clean")
|
||||
+ os.system("make all")
|
||||
+ finally:
|
||||
+ os.chdir(cwd)
|
||||
+
|
||||
+
|
||||
+def install_libmonitor(prefix=""):
|
||||
+ cwd = os.getcwd() # get current directory
|
||||
+ try:
|
||||
+ os.chdir(build_dir)
|
||||
+ os.system("make install INSTALL_DIR={prefix}".format(prefix=prefix))
|
||||
+ finally:
|
||||
+ os.chdir(cwd)
|
||||
+
|
||||
+
|
||||
+class lib_build(build):
|
||||
+ def run(self):
|
||||
+ compile_libmonitor()
|
||||
+ build.run(self)
|
||||
+
|
||||
+
|
||||
+class lib_install(install):
|
||||
+ def run(self):
|
||||
+ compile_libmonitor()
|
||||
+ install_libmonitor(self.prefix)
|
||||
+ # install.run(self)
|
||||
+
|
||||
+# Will use nix to install libmonitor
|
||||
+cmdclass = {
|
||||
+ "build": lib_build
|
||||
+}
|
||||
+
|
||||
this_directory = Path(__file__).parent
|
||||
long_description = (this_directory / "README.rst").read_text()
|
||||
|
||||
+
|
||||
setup(
|
||||
name="pyrp3",
|
||||
version=verstr,
|
||||
@@ -32,6 +78,7 @@ setup(
|
||||
"cached_property>=1.5.2",
|
||||
"numpy>=1.11.0",
|
||||
],
|
||||
+ cmdclass=cmdclass,
|
||||
classifiers=[
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: Education",
|
||||
@@ -45,5 +92,4 @@ setup(
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
],
|
||||
keywords=["redpitaya", "FPGA", "zynq"],
|
||||
- ext_modules=[Extension("monitor", ["monitor/monitor.c"])],
|
||||
)
|
@ -54,7 +54,7 @@ def main_adc_config(test_pattern):
|
||||
spi.open(MAIN_ADC_BUS, MAIN_ADC_DEVICE)
|
||||
spi.max_speed_hz = 50000
|
||||
spi.mode = 0b00 # CPOL = 0 CPHA = 0
|
||||
spi.cshigh = True
|
||||
spi.cshigh = False
|
||||
# spi.read0 = False
|
||||
|
||||
spi_buffer = [0x00, 0x80] # reset
|
||||
@ -121,7 +121,7 @@ def main_adc_test_mode(enable):
|
||||
spi.open(MAIN_ADC_BUS, MAIN_ADC_DEVICE)
|
||||
spi.max_speed_hz = 50000
|
||||
spi.mode = 0b00 # CPOL = 0 CPHA = 0
|
||||
spi.cshigh = True
|
||||
spi.cshigh = False
|
||||
# spi.read0 = True
|
||||
|
||||
reg_contents = (
|
||||
@ -189,66 +189,58 @@ def write_to_memory(address, value):
|
||||
|
||||
|
||||
def word_align():
|
||||
|
||||
value = 0
|
||||
edge_detected = False
|
||||
transition = False
|
||||
edge_start = None
|
||||
edge_end = None
|
||||
tap_delay = 0
|
||||
|
||||
# Perform initial bitslip if necessary
|
||||
for i in range(4):
|
||||
current_frame = read_from_memory(ADC_FRAME_ADDR, 1)[0]
|
||||
if current_frame != 0x0C:
|
||||
print(
|
||||
f"Performing bitslip (bitslip iteration: {i}). Reason: current_frame is 0x{current_frame:02x} instead of 0x0C"
|
||||
)
|
||||
print(f"Performing bitslip (iteration: {i}). Current frame: 0x{current_frame:02x}")
|
||||
write_to_memory(ADC_BITSLIP_ADDR, 1)
|
||||
else:
|
||||
print(f"No bitslip required; Currernt frame = 0x{current_frame:02x}")
|
||||
print(f"No bitslip required; Current frame = 0x{current_frame:02x}")
|
||||
break
|
||||
|
||||
current_frame = read_from_memory(ADC_FRAME_ADDR, 1)[0]
|
||||
prev_frame = current_frame
|
||||
|
||||
for i in range(32):
|
||||
# Sweep through all possible delay values
|
||||
for tap_delay in range(32):
|
||||
write_to_memory(ADC_DELAY_ADDR, tap_delay)
|
||||
if edge_detected == 1:
|
||||
break
|
||||
current_frame = read_from_memory(ADC_FRAME_ADDR, 1)[0]
|
||||
|
||||
print(f"Tap delay: {tap_delay}, Current frame: 0x{current_frame:02x}")
|
||||
|
||||
print(f"Tap delay: {tap_delay}")
|
||||
print(f"Current frame: 0x{current_frame:02x}")
|
||||
if current_frame == 0x0C:
|
||||
if edge_start is None:
|
||||
edge_start = tap_delay
|
||||
elif edge_start is not None and edge_end is None:
|
||||
edge_end = tap_delay - 1
|
||||
break
|
||||
|
||||
if current_frame == prev_frame:
|
||||
tap_delay += 1
|
||||
elif not transition:
|
||||
tap_delay += 1
|
||||
transition = True
|
||||
elif transition:
|
||||
tap_delay = i // 2
|
||||
edge_detected = True
|
||||
# Analyze the sweep results
|
||||
if edge_start is None:
|
||||
print("No stable region found. Using default delay.")
|
||||
optimal_delay = 11 # Default value
|
||||
elif edge_end is None:
|
||||
print("Stable region extends to the end. Using middle of detected stable region.")
|
||||
optimal_delay = (edge_start + 31) // 2
|
||||
else:
|
||||
print(f"Stable region detected from {edge_start} to {edge_end}")
|
||||
optimal_delay = (edge_start + edge_end) // 2
|
||||
|
||||
prev_frame = current_frame
|
||||
# Set the optimal delay
|
||||
write_to_memory(ADC_DELAY_ADDR, optimal_delay)
|
||||
print(f"Setting optimal delay to: {optimal_delay}")
|
||||
|
||||
if not edge_detected:
|
||||
tap_delay = 11 # empirically tested to work best
|
||||
write_to_memory(ADC_DELAY_ADDR, tap_delay)
|
||||
print(f"No edge detected; setting iDelay to: {tap_delay}")
|
||||
if edge_detected:
|
||||
write_to_memory(ADC_DELAY_ADDR, tap_delay + 2)
|
||||
print(f"Edge detected; setting iDelay to (tap_delay + 2): {tap_delay} + 2")
|
||||
|
||||
adc_ch0 = read_from_memory(ADC_CH0_HIGH_ADDR, 4)
|
||||
print(f"ADC_CH0: 0x{adc_ch0}")
|
||||
|
||||
adc_ch0 = (read_from_memory(ADC_CH0_HIGH_ADDR, 1)[0] << 8) | read_from_memory(
|
||||
ADC_CH0_LOW_ADDR, 1
|
||||
)[0]
|
||||
adc_ch1 = (read_from_memory(ADC_CH1_HIGH_ADDR, 1)[0] << 8) | read_from_memory(
|
||||
ADC_CH1_LOW_ADDR, 1
|
||||
)[0]
|
||||
# Verify final setup
|
||||
adc_ch0 = (read_from_memory(ADC_CH0_HIGH_ADDR, 1)[0] << 8) | read_from_memory(ADC_CH0_LOW_ADDR, 1)[0]
|
||||
adc_ch1 = (read_from_memory(ADC_CH1_HIGH_ADDR, 1)[0] << 8) | read_from_memory(ADC_CH1_LOW_ADDR, 1)[0]
|
||||
print(f"Final ADC_CH0: 0x{adc_ch0:04x}")
|
||||
print(f"Final ADC_CH1: 0x{adc_ch1:04x}")
|
||||
|
||||
return optimal_delay
|
||||
|
||||
|
||||
def modify_bit(original_value, position, bit_value):
|
||||
mask = 1 << position
|
||||
@ -292,7 +284,7 @@ def adc_aux_read(port, type, pin):
|
||||
spi.open(1, 3) # AUX ADC 1?
|
||||
spi.max_speed_hz = 5000
|
||||
spi.mode = 0b00
|
||||
spi.cshigh = True
|
||||
spi.cshigh = False
|
||||
|
||||
read_buffer = spi.xfer2(write_buffer)
|
||||
mu_voltage = read_buffer[0] << 8 | read_buffer[1] >> 2
|
||||
@ -307,13 +299,15 @@ def adc_aux_read(port, type, pin):
|
||||
|
||||
def main():
|
||||
main_adc_config(0x811F)
|
||||
word_align()
|
||||
optimal_delay = word_align()
|
||||
|
||||
main_adc_test_mode(False)
|
||||
|
||||
write_to_memory(ADC_AFE_CTRL_ADDR, 0b1100) # {-, -, ch2_X10, ch1_X10}
|
||||
print(read_from_memory(ADC_AFE_CTRL_ADDR, 1)[0])
|
||||
|
||||
print(f"Optimal delay used: {optimal_delay}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -22,8 +22,8 @@ MAP_SIZE = 0x1000
|
||||
MAP_MASK = 0xFFF
|
||||
PAGESIZE = 0x1000
|
||||
|
||||
# LINIEN_OFFSET = 0x0
|
||||
LINIEN_OFFSET = 0x300000
|
||||
LINIEN_OFFSET = 0x0
|
||||
# LINIEN_OFFSET = 0x300000
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# FRONT PANEL LEDS REGISTER ADDRESSES
|
||||
|
@ -1,24 +1,10 @@
|
||||
# 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 mmap
|
||||
import os
|
||||
import logging
|
||||
import time
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
import spidev
|
||||
from pyfastservo.common import (
|
||||
@ -38,54 +24,153 @@ MAIN_DAC_DEVICE = 0
|
||||
DAC_VERSION = 0x0A
|
||||
|
||||
|
||||
def main_dac_init():
|
||||
spi = spidev.SpiDev()
|
||||
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 hard_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)
|
||||
if version == DAC_VERSION:
|
||||
print(f"Verified DAC version: 0x{version:02X}")
|
||||
else:
|
||||
print(f"Unrecognized device version: 0x{version:02X}")
|
||||
|
||||
def set_data_control_and_output_mode(spi, mode='differential'):
|
||||
try:
|
||||
spi.open(MAIN_DAC_BUS, MAIN_DAC_DEVICE)
|
||||
spi.max_speed_hz = 5000
|
||||
spi.mode = 0b00 # CPOL = 0 CPHA = 0
|
||||
spi.cshigh = True
|
||||
current_reg = spi_read(spi, 0x02)
|
||||
logger.info(f"Current data control register value: 0x{current_reg:02X}")
|
||||
|
||||
spi_buffer = [0x00, 0x10] # software reset
|
||||
spi.xfer2(spi_buffer)
|
||||
new_reg = 0xB4
|
||||
|
||||
spi_buffer = [0x00, 0x00] # release software reset
|
||||
spi.xfer2(spi_buffer)
|
||||
if mode == 'differential':
|
||||
new_reg &= ~0x03
|
||||
elif mode == 'single_ended':
|
||||
new_reg = (new_reg & ~0x01) | 0x02
|
||||
else:
|
||||
logger.error(f"Invalid output mode: {mode}")
|
||||
return
|
||||
|
||||
spi_buffer = [
|
||||
0x80,
|
||||
0x00,
|
||||
] # for some reason it is needed to read the reset address for reset to actually reset
|
||||
rx_buffer = spi.xfer2(spi_buffer)
|
||||
spi_write(spi, 0x02, new_reg)
|
||||
|
||||
spi_buffer = [0x9F, 0x00] # hardware version
|
||||
rx_buffer = spi.xfer2(spi_buffer)
|
||||
if rx_buffer[1] != DAC_VERSION:
|
||||
print(f"Unrecognized device: 0x{rx_buffer[1]:02X}")
|
||||
verify_reg = spi_read(spi, 0x02)
|
||||
if verify_reg == new_reg:
|
||||
logger.info(f"Data control set and output mode successfully set to {mode}")
|
||||
logger.info(f"New data control register value: 0x{verify_reg:02X}")
|
||||
else:
|
||||
logger.error(f"Failed to set data control and output mode. Expected: 0x{new_reg:02X}, Got: 0x{verify_reg:02X}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting data control and output mode: {e}")
|
||||
|
||||
print("=== Contents of spi buffer after DAC VERSION read back: ===")
|
||||
print(f"0x{rx_buffer[0]:02X}{rx_buffer[1]:02X}")
|
||||
|
||||
spi_buffer = [0x82, 00]
|
||||
rx_buffer = spi.xfer2(spi_buffer)
|
||||
print(f"0x{rx_buffer[0]:02X}{rx_buffer[1]:02X}")
|
||||
def wait_for_clock_sync(spi):
|
||||
max_attempts = 10
|
||||
for attempt in range(max_attempts):
|
||||
clkmode = spi_read(spi, 0x14)
|
||||
if not clkmode & 0x10:
|
||||
print(f"Clock synchronized after {attempt + 1} attempts")
|
||||
break
|
||||
if attempt == max_attempts - 1:
|
||||
print("Warning: Clock synchronization not achieved")
|
||||
time.sleep(0.001)
|
||||
|
||||
# set to 2's complement and I to be first of pair on data input pads
|
||||
spi_buffer = [0x02, 0xB4]
|
||||
rx_buffer = spi.xfer2(spi_buffer)
|
||||
spi_buffer = [0x82, 00]
|
||||
rx_buffer = spi.xfer2(spi_buffer)
|
||||
print(f"0x{rx_buffer[0]:02X}{rx_buffer[1]:02X}")
|
||||
|
||||
for i in range(10):
|
||||
spi_buffer = [0x94, 0x00]
|
||||
rx_buffer = spi.xfer2(spi_buffer)
|
||||
print(f"0x{rx_buffer[0]:02X}{rx_buffer[1]:02X}")
|
||||
def enable_dac_outputs(spi):
|
||||
try:
|
||||
# Read current power-down register value
|
||||
power_down_reg = spi_read(spi, 0x01)
|
||||
logger.info(f"Current power-down register value: 0x{power_down_reg:02X}")
|
||||
|
||||
finally:
|
||||
spi.close()
|
||||
if not (power_down_reg & ((1 << 4) | (1 << 3))):
|
||||
logger.info("DAC outputs are already enabled")
|
||||
return
|
||||
|
||||
new_power_down_reg = power_down_reg & ~((1 << 4) | (1 << 3))
|
||||
spi_write(spi, 0x01, new_power_down_reg)
|
||||
|
||||
# Verify the write operation
|
||||
verify_power_down_reg = spi_read(spi, 0x01)
|
||||
if verify_power_down_reg == new_power_down_reg:
|
||||
logger.info("DAC outputs successfully enabled")
|
||||
else:
|
||||
logger.error(f"Failed to enable DAC outputs. Expected: 0x{new_power_down_reg:02X}, Got: 0x{verify_power_down_reg:02X}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error enabling DAC outputs: {e}")
|
||||
|
||||
|
||||
def set_dac_constant_output(value=0xFFFF):
|
||||
try:
|
||||
max_value = 0x3FFF # 14-bit maximum (2^14 - 1)
|
||||
value = min(value, max_value)
|
||||
|
||||
# Split the value into high and low words
|
||||
low_word_value = value & 0xFF
|
||||
high_word_value = (value >> 8) & 0x3F
|
||||
|
||||
# Write to Channel 0
|
||||
write_to_memory(CH0_HIGH_WORD_ADDR, high_word_value)
|
||||
write_to_memory(CH0_LOW_WORD_ADDR, low_word_value)
|
||||
|
||||
# Write to Channel 1
|
||||
write_to_memory(CH1_HIGH_WORD_ADDR, high_word_value)
|
||||
write_to_memory(CH1_LOW_WORD_ADDR, low_word_value)
|
||||
|
||||
logger.info(f"DAC outputs set to constant value: 0x{value:04X}")
|
||||
logger.info("Please verify the output using an oscilloscope")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting DAC constant output: {e}")
|
||||
|
||||
|
||||
def configure_dac_for_hardware(spi):
|
||||
try:
|
||||
# Enable internal reference
|
||||
power_down_reg = spi_read(spi, 0x01)
|
||||
power_down_reg &= ~(1 << 0) # Clear EXTREF bit for internal reference
|
||||
spi_write(spi, 0x01, power_down_reg)
|
||||
logger.info("Internal reference enabled")
|
||||
|
||||
# Set RREF to default 10 kΩ for 1.0V reference
|
||||
spi_write(spi, 0x0D, 0x00)
|
||||
logger.info("RREF set to 10 kΩ for 1.0V reference")
|
||||
|
||||
# Enable on-chip IRSET and QRSET
|
||||
irset_value = 0xA0 # 10100000
|
||||
spi_write(spi, 0x04, irset_value) # IRSET
|
||||
spi_write(spi, 0x07, irset_value) # QRSET
|
||||
|
||||
# Disable internal termination resistors
|
||||
ircml_value = 0x00
|
||||
spi_write(spi, 0x05, ircml_value) # IRCML
|
||||
spi_write(spi, 0x08, ircml_value) # QRCML
|
||||
|
||||
spi_write(spi, 0x02, 0xB4)
|
||||
|
||||
# Verify settings
|
||||
irset_read = spi_read(spi, 0x04)
|
||||
qrset_read = spi_read(spi, 0x07)
|
||||
ircml_read = spi_read(spi, 0x05)
|
||||
qrcml_read = spi_read(spi, 0x08)
|
||||
data_control_read = spi_read(spi, 0x02)
|
||||
|
||||
if (irset_read == irset_value and qrset_read == irset_value and
|
||||
ircml_read == ircml_value and qrcml_read == ircml_value and
|
||||
data_control_read == 0xB4):
|
||||
logger.info("DAC configured for 20mA output with external termination")
|
||||
else:
|
||||
logger.error(f"DAC configuration failed. IRSET: 0x{irset_read:02X}, QRSET: 0x{qrset_read:02X}, "
|
||||
f"IRCML: 0x{ircml_read:02X}, QRCML: 0x{qrcml_read:02X}, "
|
||||
f"Data Control: 0x{data_control_read:02X}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error configuring DAC: {e}")
|
||||
|
||||
def read_from_memory(address, n_bytes):
|
||||
assert n_bytes <= 4
|
||||
@ -152,31 +237,49 @@ def power_down(channel, power_down=True):
|
||||
reg_contents = read_from_memory(CTRL_ADDR, 1)[0]
|
||||
print(f"REG contents: 0b{reg_contents:03b}")
|
||||
|
||||
def main_dac_init():
|
||||
spi = spidev.SpiDev()
|
||||
|
||||
def write_sample(channel, sample):
|
||||
assert channel in (0, 1)
|
||||
if channel == 0:
|
||||
addresses = [CH0_HIGH_WORD_ADDR, CH0_LOW_WORD_ADDR]
|
||||
else:
|
||||
addresses = [CH1_HIGH_WORD_ADDR, CH1_LOW_WORD_ADDR]
|
||||
try:
|
||||
spi.open(MAIN_DAC_BUS, MAIN_DAC_DEVICE)
|
||||
spi.max_speed_hz = 5000
|
||||
spi.mode = 0b00 # CPOL = 0 CPHA = 0
|
||||
spi.cshigh = False
|
||||
|
||||
low_word_value = sample & 0xFF
|
||||
high_word_value = (sample >> 8) & 0x3F
|
||||
values = [high_word_value, low_word_value]
|
||||
for addr, value in zip(addresses, values):
|
||||
write_to_memory(addr, value)
|
||||
hard_reset(spi)
|
||||
check_version(spi)
|
||||
|
||||
configure_dac_for_hardware(spi)
|
||||
set_data_control_and_output_mode(spi, 'differential')
|
||||
wait_for_clock_sync(spi)
|
||||
enable_dac_outputs(spi)
|
||||
|
||||
def write_ramp():
|
||||
signal = [i for i in range(16384)]
|
||||
power_down(0, False)
|
||||
power_down(1, False)
|
||||
|
||||
manual_override(True)
|
||||
|
||||
set_dac_constant_output(0x1FFF)
|
||||
|
||||
# Verify control register
|
||||
reg_contents = read_from_memory(CTRL_ADDR, 1)[0]
|
||||
logger.info(f"Control register contents after setting: 0b{reg_contents:03b}")
|
||||
|
||||
# Verify channel settings
|
||||
ch0_high = read_from_memory(CH0_HIGH_WORD_ADDR, 1)[0]
|
||||
ch0_low = read_from_memory(CH0_LOW_WORD_ADDR, 1)[0]
|
||||
ch1_high = read_from_memory(CH1_HIGH_WORD_ADDR, 1)[0]
|
||||
ch1_low = read_from_memory(CH1_LOW_WORD_ADDR, 1)[0]
|
||||
logger.info(f"Channel 0: 0x{ch0_high:02X}{ch0_low:02X}, Channel 1: 0x{ch1_high:02X}{ch1_low:02X}")
|
||||
except Exception as e:
|
||||
print(f"Error initializing DAC: {e}")
|
||||
|
||||
finally:
|
||||
spi.close()
|
||||
|
||||
for value in signal:
|
||||
write_sample(0, value)
|
||||
|
||||
def main():
|
||||
main_dac_init()
|
||||
power_down(0, False)
|
||||
power_down(1, False)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
48
fast-servo/pyfastservo/loopback.py
Normal file
48
fast-servo/pyfastservo/loopback.py
Normal file
@ -0,0 +1,48 @@
|
||||
# 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
|
||||
|
||||
from pyfastservo.dac import (
|
||||
CH0_HIGH_WORD_ADDR,
|
||||
CH0_LOW_WORD_ADDR,
|
||||
CH1_HIGH_WORD_ADDR,
|
||||
CH1_LOW_WORD_ADDR,
|
||||
write_sample,
|
||||
manual_override,
|
||||
read_from_memory as dac_read_from_memory
|
||||
)
|
||||
from pyfastservo.adc import (
|
||||
ADC_CH0_HIGH_ADDR,
|
||||
ADC_CH0_LOW_ADDR,
|
||||
ADC_CH1_HIGH_ADDR,
|
||||
ADC_CH1_LOW_ADDR,
|
||||
read_from_memory as adc_read_from_memory
|
||||
)
|
||||
|
||||
def main():
|
||||
# Apply manual override for DAC
|
||||
manual_override(True)
|
||||
|
||||
# Write constant value to DAC channels
|
||||
constant_value = 0x1FFF
|
||||
write_sample(0, constant_value)
|
||||
write_sample(1, constant_value)
|
||||
|
||||
# Verify DAC register values
|
||||
dac_ch0 = (dac_read_from_memory(CH0_HIGH_WORD_ADDR, 1)[0] << 8) | dac_read_from_memory(CH0_LOW_WORD_ADDR, 1)[0]
|
||||
dac_ch1 = (dac_read_from_memory(CH1_HIGH_WORD_ADDR, 1)[0] << 8) | dac_read_from_memory(CH1_LOW_WORD_ADDR, 1)[0]
|
||||
|
||||
print(f"DAC CH0: 0x{dac_ch0:04X}")
|
||||
print(f"DAC CH1: 0x{dac_ch1:04X}")
|
||||
|
||||
# Read ADC values
|
||||
adc_ch0 = (adc_read_from_memory(ADC_CH0_HIGH_ADDR, 1)[0] << 8) | adc_read_from_memory(ADC_CH0_LOW_ADDR, 1)[0]
|
||||
adc_ch1 = (adc_read_from_memory(ADC_CH1_HIGH_ADDR, 1)[0] << 8) | adc_read_from_memory(ADC_CH1_LOW_ADDR, 1)[0]
|
||||
|
||||
print(f"ADC CH0: 0x{adc_ch0:04X}")
|
||||
print(f"ADC CH1: 0x{adc_ch1:04X}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -17,102 +17,264 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Additional Reference:
|
||||
# https://github.com/torvalds/linux/blob/master/drivers/clk/clk-si5341.c
|
||||
|
||||
import time
|
||||
from smbus2 import SMBus
|
||||
|
||||
BUS_NO = 0
|
||||
IC_ADDR = 0x74
|
||||
|
||||
PAGE_ADDR = 0x1
|
||||
DEVICE_READY = 0x00FE
|
||||
PLL_M_DEN = 0x023B
|
||||
STATUS = 0x000C
|
||||
STATUS_STICKY = 0x0011
|
||||
|
||||
OUT0_MUX_SEL_ADDR = 0x15
|
||||
OUT1_MUX_SEL_ADDR = 0x1A
|
||||
OUT2_MUX_SEL_ADDR = 0x29
|
||||
OUT3_MUX_SEL_ADDR = 0x2E
|
||||
|
||||
OUT2_AMPL_ADDR = 0x28
|
||||
|
||||
OUT3_PDN_ADDR = 0x2B
|
||||
OUT3_FORMAT_ADDR = 0x2C
|
||||
OUT3_AMPL_ADDR = 0x2D
|
||||
STATUS_LOSREF = 0x04
|
||||
STATUS_LOL = 0x08
|
||||
|
||||
|
||||
N1_DIVIDER_UPDATE_ADDR = 0x17
|
||||
def write_register(bus, address, value):
|
||||
page = address >> 8
|
||||
register = address & 0xFF
|
||||
bus.write_byte_data(IC_ADDR, 0x01, page)
|
||||
try:
|
||||
bus.write_byte_data(IC_ADDR, register, value)
|
||||
except Exception as e:
|
||||
raise Exception(f"Write failed 0x{value:02X} at 0x{address:04X}: {e}")
|
||||
|
||||
data_to_write = 0
|
||||
clk_out_addr = [
|
||||
OUT0_MUX_SEL_ADDR,
|
||||
OUT1_MUX_SEL_ADDR,
|
||||
OUT2_MUX_SEL_ADDR,
|
||||
OUT3_MUX_SEL_ADDR,
|
||||
]
|
||||
def write_preamble(bus):
|
||||
preamble = [
|
||||
(0x0B24, 0xC0),
|
||||
(0x0B25, 0x00),
|
||||
(0x0502, 0x01),
|
||||
(0x0505, 0x03),
|
||||
(0x0957, 0x17),
|
||||
(0x0B4E, 0x1A),
|
||||
]
|
||||
for address, value in preamble:
|
||||
write_register(bus, address, value)
|
||||
|
||||
def write_postamble(bus):
|
||||
postamble = [
|
||||
(0x001C, 0x01), # Soft reset
|
||||
(0x0B24, 0xC3),
|
||||
(0x0B25, 0x02),
|
||||
]
|
||||
for address, value in postamble:
|
||||
write_register(bus, address, value)
|
||||
|
||||
def wait_device_ready(bus):
|
||||
for _ in range(15):
|
||||
if bus.read_byte_data(IC_ADDR, DEVICE_READY) == 0x0F:
|
||||
return True
|
||||
time.sleep(0.02)
|
||||
return False
|
||||
|
||||
def wait_for_lock(bus):
|
||||
for _ in range(10):
|
||||
status = bus.read_byte_data(IC_ADDR, STATUS)
|
||||
if not (status & (STATUS_LOSREF | STATUS_LOL)):
|
||||
return True
|
||||
time.sleep(0.01)
|
||||
return False
|
||||
|
||||
def check_pll_status(bus):
|
||||
pll_status = bus.read_byte_data(IC_ADDR, 0x0C)
|
||||
pll_locked = not (pll_status & STATUS_LOL)
|
||||
print(f"PLL {'locked' if pll_locked else 'unlocked'}")
|
||||
return pll_locked
|
||||
|
||||
def check_los_status(bus):
|
||||
los_status = bus.read_byte_data(IC_ADDR, 0x0D)
|
||||
xaxb_los = (los_status & 0x10) != 0
|
||||
print(f"XA/XB LOS {'asserted' if xaxb_los else 'deasserted'}")
|
||||
return not xaxb_los
|
||||
|
||||
def configure_si5340():
|
||||
with SMBus(BUS_NO) as bus:
|
||||
if not wait_device_ready(bus):
|
||||
print("Device not ready. Aborting.")
|
||||
return
|
||||
|
||||
bus.write_byte_data(IC_ADDR, PAGE_ADDR, 0x0) # setting page to page 0
|
||||
# Programming sequence from ClockBuilder Pro, default settings
|
||||
# to initialize system using XTAL input
|
||||
main_config = [
|
||||
(0x0006, 0x00), # TOOL_VERSION
|
||||
(0x0007, 0x00), # Not in datasheet
|
||||
(0x0008, 0x00), # Not in datasheet
|
||||
(0x000B, 0x74), # I2C_ADDR
|
||||
(0x0017, 0xD0), # INT mask (disable interrupts)
|
||||
(0x0018, 0xFF), # INT mask
|
||||
(0x0021, 0x0F), # Select XTAL as input
|
||||
(0x0022, 0x00), # Not in datasheet
|
||||
(0x002B, 0x02), # SPI config
|
||||
(0x002C, 0x20), # LOS enable for XTAL
|
||||
(0x002D, 0x00), # LOS timing
|
||||
(0x002E, 0x00), # LOS trigger (thresholds)
|
||||
(0x002F, 0x00),
|
||||
(0x0030, 0x00),
|
||||
(0x0031, 0x00),
|
||||
(0x0032, 0x00),
|
||||
(0x0033, 0x00),
|
||||
(0x0034, 0x00),
|
||||
(0x0035, 0x00), # LOS trigger (thresholds) end
|
||||
(0x0036, 0x00), # LOS clear (thresholds)
|
||||
(0x0037, 0x00),
|
||||
(0x0038, 0x00),
|
||||
(0x0039, 0x00),
|
||||
(0x003A, 0x00),
|
||||
(0x003B, 0x00),
|
||||
(0x003C, 0x00),
|
||||
(0x003D, 0x00), # LOS clear (thresholds) end
|
||||
(0x0041, 0x00), # LOS0_DIV_SEL
|
||||
(0x0042, 0x00), # LOS1_DIV_SEL
|
||||
(0x0043, 0x00), # LOS2_DIV_SEL
|
||||
(0x0044, 0x00), # LOS3_DIV_SEL
|
||||
(0x009E, 0x00), # LOL_SET_THR
|
||||
(0x0102, 0x01), # Enable outputs
|
||||
(0x013F, 0x00), # OUTX_ALWAYS_ON
|
||||
(0x0140, 0x00), # OUTX_ALWAYS_ON
|
||||
(0x0141, 0x40), # OUT_DIS_LOL_MSK, OUT_DIS_MSK_LOS_PFD
|
||||
(0x0202, 0x00), # XAXB_FREQ_OFFSET (=0)
|
||||
|
||||
# read device id
|
||||
low_word = bus.read_byte_data(IC_ADDR, 0x2)
|
||||
high_word = bus.read_byte_data(IC_ADDR, 0x3)
|
||||
# PLL Configuration
|
||||
(0x0235, 0x00), # M_NUM
|
||||
(0x0236, 0x00),
|
||||
(0x0237, 0x00),
|
||||
(0x0238, 0x80),
|
||||
(0x0239, 0x89),
|
||||
(0x023A, 0x00),
|
||||
(0x023B, 0x00), # M_DEN
|
||||
(0x023C, 0x00),
|
||||
(0x023D, 0x00),
|
||||
(0x023E, 0x80),
|
||||
|
||||
print(f"DEV ID: 0x{high_word:2x}{low_word:2x}")
|
||||
# Synthesizer configuration
|
||||
(0x0302, 0x00), # N0_NUM
|
||||
(0x0303, 0x00),
|
||||
(0x0304, 0x00),
|
||||
(0x0305, 0x00),
|
||||
(0x0306, 0x21),
|
||||
(0x0307, 0x00),
|
||||
(0x0308, 0x00), # N0_DEN
|
||||
(0x0309, 0x00),
|
||||
(0x030A, 0x00),
|
||||
(0x030B, 0x80),
|
||||
(0x030C, 0x01), # N0_UPDATE
|
||||
|
||||
data_to_write = 0x1
|
||||
bus.write_byte_data(
|
||||
IC_ADDR, PAGE_ADDR, data_to_write
|
||||
) # change to page 1 for output settings
|
||||
# N1 Configuration (1:1 ratio)
|
||||
(0x030D, 0x00), # N1_NUM
|
||||
(0x030E, 0x00),
|
||||
(0x030F, 0x00),
|
||||
(0x0310, 0x00),
|
||||
(0x0311, 0x00),
|
||||
(0x0312, 0x01),
|
||||
(0x0313, 0x00), # N1_DEN
|
||||
(0x0314, 0x00),
|
||||
(0x0315, 0x00),
|
||||
(0x0316, 0x01),
|
||||
(0x0317, 0x01), # N1_UPDATE
|
||||
|
||||
readback = bus.read_byte_data(IC_ADDR, PAGE_ADDR)
|
||||
if data_to_write != readback:
|
||||
raise ValueError(f"Failed to set page.")
|
||||
# N2 Configuration (1:1 ratio)
|
||||
(0x0318, 0x00), # N2_NUM
|
||||
(0x0319, 0x00),
|
||||
(0x031A, 0x00),
|
||||
(0x031B, 0x00),
|
||||
(0x031C, 0x00),
|
||||
(0x031D, 0x01),
|
||||
(0x031E, 0x00), # N2_DEN
|
||||
(0x031F, 0x00),
|
||||
(0x0320, 0x00),
|
||||
(0x0321, 0x01),
|
||||
(0x0322, 0x01), # N2_UPDATE
|
||||
|
||||
for addr in clk_out_addr:
|
||||
bus.write_byte_data(IC_ADDR, addr, 1) # set source to N1
|
||||
# N3 Configuration (1:1 ratio)
|
||||
(0x0323, 0x00), # N3_NUM
|
||||
(0x0324, 0x00),
|
||||
(0x0325, 0x00),
|
||||
(0x0326, 0x00),
|
||||
(0x0327, 0x00),
|
||||
(0x0328, 0x01),
|
||||
(0x0329, 0x00), # N3_DEN
|
||||
(0x032A, 0x00),
|
||||
(0x032B, 0x00),
|
||||
(0x032C, 0x01),
|
||||
(0x032D, 0x01), # N3_UPDATE
|
||||
|
||||
bus.write_byte_data(IC_ADDR, OUT2_AMPL_ADDR, 13)
|
||||
readback = bus.read_byte_data(IC_ADDR, OUT2_AMPL_ADDR)
|
||||
# if data_to_write != readback:
|
||||
# raise ValueError(f"Problematic read: {readback}.")
|
||||
# Output configuration
|
||||
(0x0112, 0x06), # OUT0 config
|
||||
(0x0113, 0x09), # OUT0 format
|
||||
(0x0114, 0x3B), # OUT0 CM/AMPL
|
||||
(0x0115, 0x28), # OUT0 MUX_SEL
|
||||
|
||||
bus.write_byte_data(IC_ADDR, OUT2_AMPL_ADDR, 0x6B) # setting OUT2 to LVDS25
|
||||
(0x0117, 0x06), # OUT1 config
|
||||
(0x0118, 0x09), # OUT1 format
|
||||
(0x0119, 0x3B), # OUT1 CM/AMPL
|
||||
(0x011A, 0x28), # OUT1 MUX_SEL
|
||||
|
||||
bus.write_byte_data(IC_ADDR, OUT3_FORMAT_ADDR, 0xCC) # SETTING out3 to LVCMOS 18
|
||||
# bus.write_byte_data(IC_ADDR, 0x2E, 0x09) # SETTING out3 to LVCMOS 33
|
||||
(0x0126, 0x06), # OUT2 config
|
||||
(0x0127, 0x09), # OUT2 format
|
||||
(0x0128, 0x3B), # OUT2 CM/AMPL
|
||||
(0x0129, 0x28), # OUT2 MUX_SEL
|
||||
|
||||
readback = bus.read_byte_data(IC_ADDR, OUT3_PDN_ADDR)
|
||||
print(f"Si5340 OUTx_PDN CLK3: 0x{readback}")
|
||||
(0x012B, 0x06), # OUT3 config
|
||||
(0x012C, 0xCC), # OUT3 format
|
||||
(0x012D, 0x00), # OUT3 CM/AMPL
|
||||
(0x012E, 0x58), # OUT3 MUX_SEL
|
||||
|
||||
readback = bus.read_byte_data(IC_ADDR, OUT3_FORMAT_ADDR)
|
||||
print(f"Si5340 OUTx_FORMAT CLK3: 0x{readback}")
|
||||
# Miscellaneous configuration
|
||||
(0x090E, 0x02), # XAXB_EXTCLK_EN=0 XAXB_PDNB=1 (use XTAL)
|
||||
(0x091C, 0x04), # ZDM_EN=4 (Normal mode)
|
||||
(0x0943, 0x00), # IO_VDD_SEL
|
||||
(0x0949, 0x00), # IN_EN (disable input clocks)
|
||||
(0x094A, 0x00), # INx_TO_PFD_EN (disabled)
|
||||
(0x094E, 0x49), # REFCLK_HYS_SEL (set by CBPro)
|
||||
(0x094F, 0x02), # Not in datasheet
|
||||
(0x095E, 0x00), # M_INTEGER (set by CBPro)
|
||||
(0x0A02, 0x00), # N_ADD_0P5 (set by CBPro)
|
||||
(0x0A03, 0x01), # N_CLK_TO_OUTX_EN
|
||||
(0x0A04, 0x01), # N_PIBYP
|
||||
(0x0A05, 0x01), # N_PDNB
|
||||
(0x0A14, 0x00), # N0_HIGH_FREQ (set by CBPro)
|
||||
(0x0A1A, 0x00), # N1_HIGH_FREQ (set by CBPro)
|
||||
(0x0A20, 0x00), # N2_HIGH_FREQ (set by CBPro)
|
||||
(0x0A26, 0x00), # N3_HIGH_FREQ (set by CBPro)
|
||||
(0x0B44, 0x0F), # PDIV_ENB (set by CBPro)
|
||||
(0x0B4A, 0x0E), # N_CLK_DIS
|
||||
(0x0B57, 0x0E), # VCO_RESET_CALCODE (set by CBPro)
|
||||
(0x0B58, 0x01), # VCO_RESET_CALCODE (set by CBPro)
|
||||
]
|
||||
|
||||
readback = bus.read_byte_data(IC_ADDR, OUT3_AMPL_ADDR)
|
||||
print(f"Si5340 OUTx_AMPL CLK3: 0x{readback}")
|
||||
write_preamble(bus)
|
||||
|
||||
readback = bus.read_byte_data(IC_ADDR, OUT3_MUX_SEL_ADDR)
|
||||
print(f"Si5340 OUTx_CM CLK3: 0x{readback}")
|
||||
time.sleep(0.3)
|
||||
|
||||
bus.write_byte_data(
|
||||
IC_ADDR, PAGE_ADDR, 0x3
|
||||
) # setting page to 3 to change dividers values
|
||||
print("Writing main configuration...")
|
||||
for address, value in main_config:
|
||||
write_register(bus, address, value)
|
||||
print("Main configuration written")
|
||||
|
||||
n1_numerator = [0x0, 0x0, 0x0, 0x60, 0x22, 0x0]
|
||||
n1_numerator_10M = [0x0, 0x0, 0x0, 0xC0, 0x57, 0x1]
|
||||
n1_num_addr = [0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12]
|
||||
n1_denom_addr = [0x13, 0x14, 0x15, 0x16]
|
||||
for addr, value in zip(n1_num_addr, n1_numerator):
|
||||
bus.write_byte_data(IC_ADDR, addr, value)
|
||||
write_postamble(bus)
|
||||
|
||||
bus.write_byte_data(IC_ADDR, N1_DIVIDER_UPDATE_ADDR, 1)
|
||||
if not wait_for_lock(bus):
|
||||
print("Error waiting for input clock or PLL lock")
|
||||
else:
|
||||
print("Input clock present and PLL locked")
|
||||
|
||||
for addr in n1_num_addr:
|
||||
readback = bus.read_byte_data(IC_ADDR, addr)
|
||||
print(f"Numerator buffer: 0x{readback:02x}")
|
||||
bus.write_byte_data(IC_ADDR, STATUS_STICKY, 0)
|
||||
|
||||
for addr in n1_denom_addr:
|
||||
readback = bus.read_byte_data(IC_ADDR, addr)
|
||||
print(f"Denominator buffer: 0x{readback:02x}")
|
||||
# Final status check
|
||||
pll_locked = check_pll_status(bus)
|
||||
xaxb_signal_present = check_los_status(bus)
|
||||
|
||||
bus.write_byte_data(IC_ADDR, PAGE_ADDR, 0x0) # setting page to page 0
|
||||
if not pll_locked:
|
||||
print("Error: PLL is not locked")
|
||||
elif not xaxb_signal_present:
|
||||
print("Error: XA/XB signal is lost")
|
||||
else:
|
||||
print("Si5340 configuration completed successfully")
|
||||
|
||||
if __name__ == "__main__":
|
||||
configure_si5340()
|
22
flake.lock
generated
22
flake.lock
generated
@ -18,16 +18,16 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1709237383,
|
||||
"narHash": "sha256-cy6ArO4k5qTx+l5o+0mL9f5fa86tYUX3ozE1S+Txlds=",
|
||||
"lastModified": 1723938990,
|
||||
"narHash": "sha256-9tUadhnZQbWIiYVXH8ncfGXGvkNq3Hag4RCBEMUk7MI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1536926ef5621b09bba54035ae2bb6d806d72ac8",
|
||||
"rev": "c42fcfbdfeae23e68fc520f9182dde9f38ad1890",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"ref": "nixos-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
@ -64,11 +64,11 @@
|
||||
"src-migen": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1702942348,
|
||||
"narHash": "sha256-gKIfHZxsv+jcgDFRW9mPqmwqbZXuRvXefkZcSFjOGHw=",
|
||||
"lastModified": 1721561053,
|
||||
"narHash": "sha256-z3LRhNmKZrjr6rFD0yxtccSa/SWvFIYmb+G/D5d2Jd8=",
|
||||
"owner": "m-labs",
|
||||
"repo": "migen",
|
||||
"rev": "50934ad10a87ade47219b796535978b9bdf24023",
|
||||
"rev": "9279e8623f8433bc4f23ac51e5e2331bfe544417",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -80,11 +80,11 @@
|
||||
"src-misoc": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1699352904,
|
||||
"narHash": "sha256-SglyTmXOPv8jJOjwAjJrj/WhAkItQfUbvKfUqrynwRg=",
|
||||
"lastModified": 1715647536,
|
||||
"narHash": "sha256-q+USDcaKHABwW56Jzq8u94iGPWlyLXMyVt0j/Gyg+IE=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "a53859f2167c31ab5225b6c09f30cf05527b94f4",
|
||||
"revCount": 2452,
|
||||
"rev": "fea9de558c730bc394a5936094ae95bb9d6fa726",
|
||||
"revCount": 2455,
|
||||
"submodules": true,
|
||||
"type": "git",
|
||||
"url": "https://github.com/m-labs/misoc.git"
|
||||
|
20
flake.nix
20
flake.nix
@ -1,7 +1,7 @@
|
||||
{
|
||||
description = "Firmware for Sinara Fast-Servo based on Not-OS and Linien";
|
||||
|
||||
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-unstable;
|
||||
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-24.05;
|
||||
inputs.not-os.url = github:cleverca22/not-os;
|
||||
inputs.not-os.inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
||||
@ -110,13 +110,23 @@
|
||||
rev = "v${version}";
|
||||
hash = "sha256-43TTlpJ5SMAjQM71bNVvrWQyciRXM3zpuA/Dw41AEgU=";
|
||||
};
|
||||
nativeBuildInputs = with pkgs-armv7l.python3Packages; [ setuptools wheel setuptools-scm ];
|
||||
patches = ./fast-servo/linien-pyrp3-monitor.patch;
|
||||
nativeBuildInputs = with pkgs-armv7l.python3Packages; [
|
||||
setuptools wheel setuptools-scm
|
||||
] ++ (with pkgs-armv7l; [ gcc gnumake ]);
|
||||
propagatedBuildInputs = with pkgs-armv7l.python3Packages; [
|
||||
myhdl
|
||||
rpyc4
|
||||
cached-property
|
||||
numpy
|
||||
];
|
||||
postInstall = ''
|
||||
cp monitor/libmonitor.so $out/lib
|
||||
'';
|
||||
postFixup = ''
|
||||
substituteInPlace $out/${pkgs.python3.sitePackages}/pyrp3/raw_memory.py \
|
||||
--replace "libmonitor.so" "$out/lib/libmonitor.so"
|
||||
'';
|
||||
};
|
||||
|
||||
linien-server = pkgs-armv7l.python3Packages.buildPythonPackage rec {
|
||||
@ -125,6 +135,7 @@
|
||||
inherit (pkgs.python3Packages.linien-common) src version;
|
||||
sourceRoot = "source/linien-server";
|
||||
postPatch = ''
|
||||
cp ${fast-servo-gateware}/csrmap.py linien_server/csrmap.py
|
||||
substituteInPlace linien_server/acquisition.py \
|
||||
--replace " start_nginx()" "" \
|
||||
--replace " stop_nginx()" "" \
|
||||
@ -175,6 +186,7 @@
|
||||
mkdir -p $out $out/nix-support
|
||||
cp gateware/build/top.bit $out
|
||||
cp linien-server/linien_server/gateware.bin $out
|
||||
cp linien-server/linien_server/csrmap.py $out
|
||||
echo file binary-dist $out/top.bit >> $out/nix-support/hydra-build-products
|
||||
echo file binary-dist $out/gateware.bin >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
@ -239,7 +251,7 @@
|
||||
|
||||
# Program the FPGA
|
||||
set +x
|
||||
echo "Flashing bitstream..."
|
||||
echo "Loading bitstream into SRAM..."
|
||||
echo 0 > /sys/class/fpga_manager/fpga0/flags
|
||||
mkdir -p /lib/firmware
|
||||
cp ${fast-servo-gateware}/gateware.bin /lib/firmware/
|
||||
@ -469,7 +481,7 @@
|
||||
inherit migen misoc vivado;
|
||||
};
|
||||
packages.armv7l-linux = {
|
||||
inherit fast-servo-gateware linien-server;
|
||||
inherit fast-servo-gateware linien-server pyfastservo;
|
||||
} //
|
||||
(board-package-set { board = "zc706"; }) //
|
||||
(board-package-set { board = "fast-servo"; });
|
||||
|
@ -1,5 +1,5 @@
|
||||
diff --git a/base.nix b/base.nix
|
||||
index 7eaee32..3a2a0a9 100644
|
||||
index 7eaee32..9aa338e 100644
|
||||
--- a/base.nix
|
||||
+++ b/base.nix
|
||||
@@ -27,6 +27,11 @@ with lib;
|
||||
@ -14,9 +14,12 @@ index 7eaee32..3a2a0a9 100644
|
||||
not-os.simpleStaticIp = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
@@ -86,15 +91,25 @@ with lib;
|
||||
@@ -84,17 +89,25 @@ with lib;
|
||||
};
|
||||
environment.etc = {
|
||||
"nix/nix.conf".source = pkgs.runCommand "nix.conf" {} ''
|
||||
extraPaths=$(for i in $(cat ${pkgs.writeReferencesToFile pkgs.runtimeShell}); do if test -d $i; then echo $i; fi; done)
|
||||
- extraPaths=$(for i in $(cat ${pkgs.writeReferencesToFile pkgs.runtimeShell}); do if test -d $i; then echo $i; fi; done)
|
||||
+ extraPaths=$(for i in $(cat ${pkgs.writeClosure [ pkgs.bash ]}); do if test -d $i; then echo $i; fi; done)
|
||||
cat > $out << EOF
|
||||
- build-use-sandbox = true
|
||||
+ auto-optimise-store = true
|
||||
@ -28,8 +31,6 @@ index 7eaee32..3a2a0a9 100644
|
||||
+ extra-sandbox-paths = /bin/sh=${pkgs.runtimeShell} $(echo $extraPaths)
|
||||
+ max-jobs = auto
|
||||
+ sandbox = true
|
||||
+ substituters = https://cache.armv7l.xyz
|
||||
+ trusted-public-keys = cache.armv7l.xyz-1:kBY/eGnBAYiqYfg0fy0inWhshUo+pGFM3Pj7kIkmlBk=
|
||||
+ trusted-users = root
|
||||
EOF
|
||||
'';
|
||||
@ -151,7 +152,7 @@ index c61f9d6..fbdf0fd 100644
|
||||
};
|
||||
}
|
||||
diff --git a/zynq_image.nix b/zynq_image.nix
|
||||
index 3fa23ab..9d1621e 100644
|
||||
index 3fa23ab..d5c5eda 100644
|
||||
--- a/zynq_image.nix
|
||||
+++ b/zynq_image.nix
|
||||
@@ -1,66 +1,89 @@
|
||||
@ -163,7 +164,7 @@ index 3fa23ab..9d1621e 100644
|
||||
- # dont use overlays for the qemu, it causes a lot of wasted time on recompiles
|
||||
- x86pkgs = import pkgs.path { system = "x86_64-linux"; };
|
||||
- customKernel = pkgs.linux.override {
|
||||
+ customKernel = (pkgs.linux.override {
|
||||
+ customKernel = (pkgs.linux_6_6.override {
|
||||
extraConfig = ''
|
||||
OVERLAY_FS y
|
||||
+ MEDIA_SUPPORT n
|
||||
|
@ -394,10 +394,10 @@ index 0000000..53c5349
|
||||
\ No newline at end of file
|
||||
diff --git a/xilinx-fpga-manager.patch b/xilinx-fpga-manager.patch
|
||||
new file mode 100644
|
||||
index 0000000..59aa585
|
||||
index 0000000..33daffe
|
||||
--- /dev/null
|
||||
+++ b/xilinx-fpga-manager.patch
|
||||
@@ -0,0 +1,663 @@
|
||||
@@ -0,0 +1,676 @@
|
||||
+# Enable user-space interface for PL programming via Linux FPGA manager
|
||||
+# diff cherry-picked from Xilinx/linux-xilinx/tree/xlnx_rebase_v6.6_LTS
|
||||
+# commit IDs: e61c0a9, 0a38712, dc67651, 89a24e3, 8d224b1, 2a9c05f, 4e94580
|
||||
@ -425,7 +425,7 @@ index 0000000..59aa585
|
||||
+ tristate "Altera SOCFPGA FPGA Manager"
|
||||
+ depends on ARCH_INTEL_SOCFPGA || COMPILE_TEST
|
||||
+diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
|
||||
+index 06651389c592..6e8c45974f28 100644
|
||||
+index 0f4035b089a2..3aa9f5f041f6 100644
|
||||
+--- a/drivers/fpga/fpga-mgr.c
|
||||
++++ b/drivers/fpga/fpga-mgr.c
|
||||
+@@ -8,6 +8,9 @@
|
||||
@ -673,7 +673,7 @@ index 0000000..59aa585
|
||||
+ NULL,
|
||||
+ };
|
||||
+ ATTRIBUTE_GROUPS(fpga_mgr);
|
||||
+@@ -732,6 +864,106 @@ void fpga_mgr_put(struct fpga_manager *mgr)
|
||||
+@@ -739,6 +871,106 @@ void fpga_mgr_put(struct fpga_manager *mgr)
|
||||
+ }
|
||||
+ EXPORT_SYMBOL_GPL(fpga_mgr_put);
|
||||
+
|
||||
@ -780,8 +780,8 @@ index 0000000..59aa585
|
||||
+ /**
|
||||
+ * fpga_mgr_lock - Lock FPGA manager for exclusive use
|
||||
+ * @mgr: fpga manager
|
||||
+@@ -779,6 +1011,9 @@ struct fpga_manager *
|
||||
+ fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info)
|
||||
+@@ -788,6 +1020,9 @@ __fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *
|
||||
+ struct module *owner)
|
||||
+ {
|
||||
+ const struct fpga_manager_ops *mops = info->mops;
|
||||
++#ifdef CONFIG_FPGA_MGR_DEBUG_FS
|
||||
@ -790,7 +790,7 @@ index 0000000..59aa585
|
||||
+ struct fpga_manager *mgr;
|
||||
+ int id, ret;
|
||||
+
|
||||
+@@ -815,10 +1050,28 @@ fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *in
|
||||
+@@ -826,10 +1061,28 @@ __fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *
|
||||
+ mgr->dev.of_node = parent->of_node;
|
||||
+ mgr->dev.id = id;
|
||||
+
|
||||
@ -819,7 +819,7 @@ index 0000000..59aa585
|
||||
+ /*
|
||||
+ * Initialize framework state by requesting low level driver read state
|
||||
+ * from device. FPGA may be in reset mode or may have been programmed
|
||||
+@@ -832,6 +1085,28 @@ fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *in
|
||||
+@@ -843,6 +1096,28 @@ __fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *
|
||||
+ return ERR_PTR(ret);
|
||||
+ }
|
||||
+
|
||||
@ -848,7 +848,7 @@ index 0000000..59aa585
|
||||
+ return mgr;
|
||||
+
|
||||
+ error_device:
|
||||
+@@ -882,6 +1157,10 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
|
||||
+@@ -894,6 +1169,10 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
|
||||
+ {
|
||||
+ dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);
|
||||
+
|
||||
@ -881,10 +881,10 @@ index 0000000..59aa585
|
||||
+ &firmware_name)) {
|
||||
+ info->firmware_name = devm_kstrdup(dev, firmware_name,
|
||||
+diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
|
||||
+index f3434e2c487b..db923746cac5 100644
|
||||
+index f3434e2c487b..d2434ed85eff 100644
|
||||
+--- a/drivers/fpga/zynqmp-fpga.c
|
||||
++++ b/drivers/fpga/zynqmp-fpga.c
|
||||
+@@ -43,25 +43,42 @@ static int zynqmp_fpga_ops_write(struct fpga_manager *mgr,
|
||||
+@@ -43,25 +43,47 @@ static int zynqmp_fpga_ops_write(struct fpga_manager *mgr,
|
||||
+ struct zynqmp_fpga_priv *priv;
|
||||
+ dma_addr_t dma_addr;
|
||||
+ u32 eemi_flags = 0;
|
||||
@ -915,6 +915,11 @@ index 0000000..59aa585
|
||||
++
|
||||
+ wmb(); /* ensure all writes are done before initiate FW call */
|
||||
+
|
||||
++ if (priv->flags & FPGA_MGR_DDR_MEM_AUTH_BITSTREAM)
|
||||
++ eemi_flags |= XILINX_ZYNQMP_PM_FPGA_AUTHENTICATION_DDR;
|
||||
++ else if (priv->flags & FPGA_MGR_SECURE_MEM_AUTH_BITSTREAM)
|
||||
++ eemi_flags |= XILINX_ZYNQMP_PM_FPGA_AUTHENTICATION_OCM;
|
||||
++
|
||||
+ if (priv->flags & FPGA_MGR_PARTIAL_RECONFIG)
|
||||
+ eemi_flags |= XILINX_ZYNQMP_PM_FPGA_PARTIAL;
|
||||
+
|
||||
@ -931,20 +936,22 @@ index 0000000..59aa585
|
||||
+ return ret;
|
||||
+ }
|
||||
+diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
|
||||
+index 9dda7d9898ff..edaf77160746 100644
|
||||
+index e8b12ec8b060..ffd2dfdb6abd 100644
|
||||
+--- a/include/linux/firmware/xlnx-zynqmp.h
|
||||
++++ b/include/linux/firmware/xlnx-zynqmp.h
|
||||
+@@ -70,6 +70,8 @@
|
||||
+@@ -83,6 +83,10 @@
|
||||
+ */
|
||||
+ #define XILINX_ZYNQMP_PM_FPGA_FULL 0x0U
|
||||
+ #define XILINX_ZYNQMP_PM_FPGA_PARTIAL BIT(0)
|
||||
++#define XILINX_ZYNQMP_PM_FPGA_AUTHENTICATION_DDR BIT(1)
|
||||
++#define XILINX_ZYNQMP_PM_FPGA_AUTHENTICATION_OCM BIT(2)
|
||||
++#define XILINX_ZYNQMP_PM_FPGA_ENCRYPTION_USERKEY BIT(3)
|
||||
++#define XILINX_ZYNQMP_PM_FPGA_ENCRYPTION_DEVKEY BIT(4)
|
||||
+
|
||||
+ /* FPGA Status Reg */
|
||||
+ #define XILINX_ZYNQMP_PM_FPGA_CONFIG_STAT_OFFSET 7U
|
||||
+diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
|
||||
+index 54f63459efd6..c96a4405f909 100644
|
||||
+index 0d4fe068f3d8..f884d268c974 100644
|
||||
+--- a/include/linux/fpga/fpga-mgr.h
|
||||
++++ b/include/linux/fpga/fpga-mgr.h
|
||||
+@@ -9,8 +9,11 @@
|
||||
@ -959,7 +966,7 @@ index 0000000..59aa585
|
||||
+ struct fpga_manager;
|
||||
+ struct sg_table;
|
||||
+
|
||||
+@@ -66,17 +69,25 @@ enum fpga_mgr_states {
|
||||
+@@ -66,17 +69,29 @@ enum fpga_mgr_states {
|
||||
+ *
|
||||
+ * %FPGA_MGR_EXTERNAL_CONFIG: FPGA has been configured prior to Linux booting
|
||||
+ *
|
||||
@ -973,6 +980,10 @@ index 0000000..59aa585
|
||||
++ *
|
||||
++ * %FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM: indicates bitstream is encrypted with
|
||||
++ * user key
|
||||
++ * %FPGA_MGR_DDR_MEM_AUTH_BITSTREAM: do bitstream authentication using DDR
|
||||
++ * memory if supported
|
||||
++ * %FPGA_MGR_SECURE_MEM_AUTH_BITSTREAM: do bitstream authentication using secure
|
||||
++ * memory if supported
|
||||
+ */
|
||||
+ #define FPGA_MGR_PARTIAL_RECONFIG BIT(0)
|
||||
+ #define FPGA_MGR_EXTERNAL_CONFIG BIT(1)
|
||||
@ -986,7 +997,7 @@ index 0000000..59aa585
|
||||
+
|
||||
+ /**
|
||||
+ * struct fpga_image_info - information specific to an FPGA image
|
||||
+@@ -86,6 +97,7 @@ enum fpga_mgr_states {
|
||||
+@@ -86,6 +101,7 @@ enum fpga_mgr_states {
|
||||
+ * @config_complete_timeout_us: maximum time for FPGA to switch to operating
|
||||
+ * status in the write_complete op.
|
||||
+ * @firmware_name: name of FPGA image firmware file
|
||||
@ -994,7 +1005,7 @@ index 0000000..59aa585
|
||||
+ * @sgt: scatter/gather table containing FPGA image
|
||||
+ * @buf: contiguous buffer containing FPGA image
|
||||
+ * @count: size of buf
|
||||
+@@ -102,6 +114,7 @@ struct fpga_image_info {
|
||||
+@@ -102,6 +118,7 @@ struct fpga_image_info {
|
||||
+ u32 disable_timeout_us;
|
||||
+ u32 config_complete_timeout_us;
|
||||
+ char *firmware_name;
|
||||
@ -1002,7 +1013,7 @@ index 0000000..59aa585
|
||||
+ struct sg_table *sgt;
|
||||
+ const char *buf;
|
||||
+ size_t count;
|
||||
+@@ -160,6 +173,7 @@ struct fpga_manager_info {
|
||||
+@@ -160,6 +177,7 @@ struct fpga_manager_info {
|
||||
+ * @write: write count bytes of configuration data to the FPGA
|
||||
+ * @write_sg: write the scatter list of configuration data to the FPGA
|
||||
+ * @write_complete: set FPGA to operating state after writing is done
|
||||
@ -1010,7 +1021,7 @@ index 0000000..59aa585
|
||||
+ * @fpga_remove: optional: Set FPGA into a specific state during driver remove
|
||||
+ * @groups: optional attribute groups.
|
||||
+ *
|
||||
+@@ -182,6 +196,7 @@ struct fpga_manager_ops {
|
||||
+@@ -182,6 +200,7 @@ struct fpga_manager_ops {
|
||||
+ int (*write_sg)(struct fpga_manager *mgr, struct sg_table *sgt);
|
||||
+ int (*write_complete)(struct fpga_manager *mgr,
|
||||
+ struct fpga_image_info *info);
|
||||
@ -1018,7 +1029,7 @@ index 0000000..59aa585
|
||||
+ void (*fpga_remove)(struct fpga_manager *mgr);
|
||||
+ const struct attribute_group **groups;
|
||||
+ };
|
||||
+@@ -196,21 +211,35 @@ struct fpga_manager_ops {
|
||||
+@@ -196,23 +215,37 @@ struct fpga_manager_ops {
|
||||
+ /**
|
||||
+ * struct fpga_manager - fpga manager structure
|
||||
+ * @name: name of low level fpga manager
|
||||
@ -1031,6 +1042,7 @@ index 0000000..59aa585
|
||||
+ * @state: state of fpga manager
|
||||
+ * @compat_id: FPGA manager id for compatibility check.
|
||||
+ * @mops: pointer to struct of fpga manager ops
|
||||
+ * @mops_owner: module containing the mops
|
||||
+ * @priv: low level driver private date
|
||||
++ * @err: low level driver error code
|
||||
++ * @dir: debugfs image directory
|
||||
@ -1046,6 +1058,7 @@ index 0000000..59aa585
|
||||
+ enum fpga_mgr_states state;
|
||||
+ struct fpga_compat_id *compat_id;
|
||||
+ const struct fpga_manager_ops *mops;
|
||||
+ struct module *mops_owner;
|
||||
+ void *priv;
|
||||
++ int err;
|
||||
++#ifdef CONFIG_FPGA_MGR_DEBUG_FS
|
||||
@ -1054,21 +1067,21 @@ index 0000000..59aa585
|
||||
+ };
|
||||
+
|
||||
+ #define to_fpga_manager(d) container_of(d, struct fpga_manager, dev)
|
||||
+@@ -244,4 +273,6 @@ struct fpga_manager *
|
||||
+ devm_fpga_mgr_register(struct device *parent, const char *name,
|
||||
+ const struct fpga_manager_ops *mops, void *priv);
|
||||
+@@ -258,4 +291,6 @@ __devm_fpga_mgr_register(struct device *parent, const char *name,
|
||||
+ const struct fpga_manager_ops *mops, void *priv,
|
||||
+ struct module *owner);
|
||||
+
|
||||
++#define FPGA_IOCTL_LOAD_DMA_BUFF _IOWR('R', 1, __u32)
|
||||
++
|
||||
+ #endif /*_LINUX_FPGA_MGR_H */
|
||||
diff --git a/zynq_image.nix b/zynq_image.nix
|
||||
index 9d1621e..012e50c 100644
|
||||
index d5c5eda..7ede584 100644
|
||||
--- a/zynq_image.nix
|
||||
+++ b/zynq_image.nix
|
||||
@@ -3,6 +3,16 @@
|
||||
with lib;
|
||||
let
|
||||
customKernel = (pkgs.linux.override {
|
||||
customKernel = (pkgs.linux_6_6.override {
|
||||
+ kernelPatches = [
|
||||
+ ({
|
||||
+ name = "xilinx-configfs-overlays";
|
||||
|
Loading…
Reference in New Issue
Block a user