Compare commits

...

15 Commits

Author SHA1 Message Date
a21293b1ad add filesystem modes instructions
Signed-off-by: Florian Agbuya <fa@m-labs.ph>
2025-04-10 13:49:18 +08:00
53c2da6df2 mount SD read-only with overlay FS and add fs-mode command
Signed-off-by: Florian Agbuya <fa@m-labs.ph>
2025-04-10 13:44:32 +08:00
4aa96b9ac9 Update README 2025-04-09 11:50:14 +08:00
87bb551088 gui: rm redpitaya mentions 2025-04-09 11:27:28 +08:00
52d7be43d5 gateware: Change dac csr register default value
- if the Si5340 is preprogrammed to output the correct frequency, this
    change removes undefined behavior on Dac before PL initialization.
2025-04-08 12:51:39 +08:00
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
7a0c9f1a2a gui: update optimization panel to hardware limit
- Vpp max 2.0 to 1.0
- Modulation Freq Decimal Places 2 -> 4
2025-04-08 12:45:30 +08:00
09e2fc39d2 fix typo 2025-04-07 13:16:15 +08:00
6d31e72515 gui: Scale the adc input correctly to the gain
- Combined error signal was showing the error_1
    signal instead of combined error signal.
    This commit corrects this behavior.
2025-04-01 20:20:05 +08:00
1b76caef2c gui: Enable opengl but do not draw signal strength
- OpenGL is needed for the GUI to render complex waveform in reasonable
    time
- this commit removes the rendering of ths signal strength of error
    signals which cause the Painter path exceeds +/-32767 pixels problem
- that is due to an limitations in Pyqt5.
2025-04-01 20:20:05 +08:00
5eb7dce067 Update sweep speed and Vpp Definition 2025-04-01 20:20:05 +08:00
25e1744a63 Update README 2025-04-01 20:20:05 +08:00
1b129ae1f2 flake: update dependencies 2025-04-01 18:59:43 +08:00
0e43393aa5 PL init: Add delay to fix fetch io error
- Fetch IO error happens randomly at the beginning of Adc Init
    only at startup
- Adding some delays seems to fix it
2025-03-25 13:09:01 +08:00
d615e47ae8 PL init: Do not start linien-server if init fails
- Also, turn Error(RED) LEDs on if PL init fails
2025-03-25 12:57:25 +08:00
18 changed files with 528 additions and 92 deletions

View File

@ -8,14 +8,14 @@
## Building
### Reproducible build with Nix
1. Run `nix build .#packages.armv7l-linux.fast-servo-sd-image` to build the sd card image.
2. Run `nix build .#packages.armv7l-linux.fast-servo-gui` to build the GUI
2. Run `nix build .#packages.armv7l-linux.fast-servo-gui` to build the GUI.
3. Run `nix develop` to build a dev shell having access the GUI.
### Flashing
1. Plug in your SD card to your computer and run `lsblk` to locate SD card
1. Plug in your SD card to your computer and run `lsblk` to locate SD card.
2. If there are any partitions on the SD card, run `umount <mount point>` all the related mount points.
3. Run `sudo dd if=<path to the SD Card Image> of=/dev/<SD Card Device Name in lsblk> bs=4M status=progress oflag=sync`
4. Eject the SD Card before removal
3. Run `sudo dd if=<path to the SD Card Image> of=/dev/<SD Card Device Name in lsblk> bs=4M status=progress oflag=sync`.
4. Eject the SD Card before removal.
## Configuration
You can modify the followings by altering the files directly on the SD card. You can do that by inserting the SD card to a computer in Linux Operating system.
@ -30,23 +30,33 @@ You can modify the followings by altering the files directly on the SD card. You
You should append the ssh public key of your PC here with hostname removed.
- Instructions:
1. Run `ssh-keygen`
1. Run `ssh-keygen`.
2. Type a filename for key to be saved. Let's say `foo`.
3. Type `Return(Enter)` twice when it asks for your passphrase. Fast-servo's ssh authentication is passwordless.
4. Open the generated public key file `foo.pub`.
5. Append the public key to the file `/etc/ssh/authorized_keys.d/root` with `@hostname` removed.
Sample ssh public key: `ssh-ed25519 xxx...xxx username@hostname`
5. Append the public key to the file `/etc/ssh/authorized_keys.d/root` with `@hostname` removed(if any). For example, if sample ssh public key is `ssh-ed25519 xxx...xxx username@hostname`, you should append `ssh-ed25519 xxx...xxx username` to that file.
## Usage
1. Make sure the onboard DIP Switch is in the following condition.
1. Make sure the onboard DIP Switch is in the following positions.
- EN: OFF
- MODE: ON
2. Install the SD Card, power up the board via the power jack or PoE and plug in the RJ45 Ethernet cable.
3. Wait for all the front panel LEDs except the termination status LEDs to turn off. It can take a minutes or two for first boot. If it does not boot up, try to flash the SD Card again.
4. By default, linien-server starts up automatically. In case linien-server crashes, it will restart itself. Logs are stored in `/root/linien-server-log`.
3. It can take a minutes or two for first boot. If it does not boot up, try to flash the SD Card again. The following table describes PL initialization status with the status of the front panel LEDs L0, L1, L2, L3.
| L0 | L1 | L2 | L3 | Meaning |
|-----|----|------|-----|------------------------|
| On | On | On | On | Si5340 Clock Generator fails to initialize |
| On | On | X | X | LTC2195 Adc fails to initialize |
| X | X | On | On | AD9117-2 Dac fails to initialize |
| Off | Off | Off | Off | Fast-Servo PL initialization is successful |
Note: X = Don't Care
For debugging, you can hook up a USB-to-UART adapter and power cycle to the board to check the boot log. Or if able, ssh into the board and run `python -m pyfastservo.initialize` to check its log.
4. By default, linien-server starts up automatically if PL Init is successful. In case linien-server crashes, it will restart itself. Logs are stored in `/root/linien-server-log`.
ssh access is not required for linien client if linien-server is started. If not, then linien client will ssh into the fast-servo device to start the linien-server service.
Here are some commands to interact with the linien-server service once you `ssh <fast servo ip address> -p 3030` into fast-servo.
Here are some commands for interacting with the linien-server service once you `ssh <fast servo ip address> -p 3030` into fast-servo.
| Description | Command |
|------------------------------------------------------|-----------------------------------------|
@ -56,5 +66,15 @@ ssh access is not required for linien client if linien-server is started. If not
| Set the linien-server service to start at bootup | `linien-server enable` |
| Set the linien-server service not to start at bootup | `linien-server disable` |
4. In the dev shell, run `linien` to launch the GUI. Add new device. Username is `root`.
4. In the dev shell, run `linien` to launch the GUI. Add new device. Fill in the hostname/ip address of the device and leave the other fields with default value unchanged.
5. Select the newly added device and click connect in the GUI to connect and start the GUI.
6. To save the parameters to be loaded at start up, you would need to shutdown the linien-server service by clicking the corresponding button on the GUI, will ssh into the fast-servo and stop the linien-server service. Removing the power input on Fast-Servo will not save any parameter. Alternatively, you can save and load settings to/from a file on your PC via the GUI.
## Filesystem Modes
By default, the SD card is mounted in read-only mode with RAM overlay to prevent data corruption when power is unexpectedly removed.
To change the filesystem mode:
1. Set read-only mode: `fs-mode ro`
2. Set read-write mode: `fs-mode rw`
The system will automatically reboot after changing modes.

View File

@ -1,15 +1,17 @@
diff --git a/linien-common/linien_common/common.py b/linien-common/linien_common/common.py
index 854d776..a310dbe 100644
index 854d776..98de41f 100644
--- a/linien-common/linien_common/common.py
+++ b/linien-common/linien_common/common.py
@@ -25,8 +25,8 @@ from typing import Dict, Iterable, List, Tuple, Union
@@ -25,8 +25,10 @@ from typing import Dict, Iterable, List, Tuple, Union
import numpy as np
from scipy.signal import correlate, resample
-MHz = 0x10000000 / 8
-Vpp = ((1 << 14) - 1) / 4
+MHz = 0x10000000 / 8
+Vpp = (1 << 14 - 1) / 0.355 * 0.85
+CORDIC_GAIN = 1.6467602570986222
+
+MHz = (2 ** 32) / 125.0
+Vpp = (2 ** 14 - 1) / CORDIC_GAIN
# conversion of bits to V
ANALOG_OUT_V = 1.8 / ((2**15) - 1)

View File

@ -30,9 +30,9 @@ class DAC(Module, AutoCSR):
phase_shift_en, phase_shift_dir = phase_shift_ctrl
self.dac_ctrl = CSRStorage(5)
self.output_value_ch0 = CSRStorage(14)
self.output_value_ch1 = CSRStorage(14)
self.dac_ctrl = CSRStorage(5, reset=0b00111)
self.output_value_ch0 = CSRStorage(14, reset=0x1FFF)
self.output_value_ch1 = CSRStorage(14, reset=0x1FFF)
manual_override = Signal()
ch0_pd = Signal()

View File

@ -89,7 +89,7 @@ class CRG(Module):
p_CLKOUT1_DIVIDE=8,
p_CLKOUT1_PHASE=0.0,
p_CLKOUT1_DUTY_CYCLE=0.5,
o_CLKOUT1=clk_sys, # 1000MHz / 8 -> 120MHz
o_CLKOUT1=clk_sys, # 1000MHz / 8 -> 125MHz
p_CLKOUT2_DIVIDE=4,
p_CLKOUT2_PHASE=0.0,
@ -290,7 +290,7 @@ class BaseSoC(PS7, AutoCSR):
class LED(Module, AutoCSR):
def __init__(self, led):
self.led_out = CSRStorage(1)
self.led_out = CSRStorage(1, reset=1)
self.comb += led.eq(self.led_out.storage)

View File

@ -1,3 +1,25 @@
diff --git a/linien-common/linien_common/common.py b/linien-common/linien_common/common.py
index 8f671c2..8b8bb30 100644
--- a/linien-common/linien_common/common.py
+++ b/linien-common/linien_common/common.py
@@ -285,6 +285,8 @@ def combine_error_signal(
channel_mixing: int,
combined_offset: int,
chain_factor_width: int = 8,
+ gain_a: int = 1,
+ gain_b: int = 1,
) -> np.ndarray:
if not dual_channel:
signal = error_signals[0]
@@ -292,7 +294,7 @@ def combine_error_signal(
a_factor, b_factor = convert_channel_mixing_value(channel_mixing)
signal = [
- (a_factor * a + b_factor * b) >> chain_factor_width
+ int(((a_factor * a) / gain_a + (b_factor * b) / gain_b) / 2 ** chain_factor_width)
for a, b in zip(*error_signals)
]
diff --git a/linien-gui/linien_gui/ui/general_panel.py b/linien-gui/linien_gui/ui/general_panel.py
index cad2d91..499146d 100644
--- a/linien-gui/linien_gui/ui/general_panel.py
@ -130,3 +152,112 @@ index 6c2bd45..79f4580 100644
<item>
<widget class="QGroupBox" name="dualChannelMixingGroupBox">
<property name="title">
diff --git a/linien-gui/linien_gui/ui/plot_widget.py b/linien-gui/linien_gui/ui/plot_widget.py
index ef03de1..7412549 100644
--- a/linien-gui/linien_gui/ui/plot_widget.py
+++ b/linien-gui/linien_gui/ui/plot_widget.py
@@ -112,6 +112,8 @@ class PlotWidget(pg.PlotWidget):
)
self.app = get_linien_app_instance()
self.app.connection_established.connect(self.on_connection_established)
+ self.adc1_afe_gain = 1
+ self.adc2_afe_gain = 1
self.getAxis("bottom").enableAutoSIPrefix(False)
self.showGrid(x=True, y=True)
@@ -226,6 +228,12 @@ class PlotWidget(pg.PlotWidget):
self.cached_plot_data = []
self._should_reposition_reset_view_button = False
+ def on_adc_afe_gain_1_changed(self, value):
+ self.adc1_afe_gain = 10 if value else 1
+
+ def on_adc_afe_gain_2_changed(self, value):
+ self.adc2_afe_gain = 10 if value else 1
+
def on_connection_established(self):
self.parameters = self.app.parameters
self.control = self.app.control
@@ -249,6 +257,8 @@ class PlotWidget(pg.PlotWidget):
)
self.parameters.automatic_mode.add_callback(self.on_automatic_mode_changed)
self.parameters.lock.add_callback(self.on_lock_changed)
+ self.parameters.adc_afe_10x_gain_1.add_callback(self.on_adc_afe_gain_1_changed)
+ self.parameters.adc_afe_10x_gain_2.add_callback(self.on_adc_afe_gain_2_changed)
def _to_data_coords(self, event):
pos = self.plotItem.vb.mapSceneToView(event.pos())
@@ -504,7 +514,7 @@ class PlotWidget(pg.PlotWidget):
scale_history_times(
self.monitor_signal_history_data["times"], timescale
),
- np.array(self.monitor_signal_history_data["values"]) / V,
+ np.array(self.monitor_signal_history_data["values"]) / V / self.adc2_afe_gain,
)
self.plot_autolock_target_line(None)
else:
@@ -521,6 +531,8 @@ class PlotWidget(pg.PlotWidget):
dual_channel,
self.parameters.channel_mixing.value,
self.parameters.combined_offset.value if dual_channel else 0,
+ gain_a = self.adc1_afe_gain,
+ gain_b = self.adc2_afe_gain,
)
if self.plot_paused:
@@ -539,26 +551,31 @@ class PlotWidget(pg.PlotWidget):
self.monitorSignalHistory.setVisible(False)
self.combinedErrorSignal.setVisible(True)
- self.combinedErrorSignal.setData(
- list(range(len(error_signal_1))), error_signal_1 / V
- )
+ if dual_channel:
+ self.combinedErrorSignal.setData(
+ list(range(len(combined_error_signal))), combined_error_signal / V
+ )
+ else:
+ self.combinedErrorSignal.setData(
+ list(range(len(error_signal_1))), error_signal_1 / V / self.adc1_afe_gain
+ )
self.errorSignal1.setVisible(dual_channel)
if error_signal_1 is not None:
self.errorSignal1.setData(
- list(range(len(error_signal_1))), error_signal_1 / V
+ list(range(len(error_signal_1))), error_signal_1 / V / self.adc1_afe_gain
)
self.errorSignal2.setVisible(dual_channel)
if error_signal_2 is not None:
self.errorSignal2.setData(
- list(range(len(error_signal_2))), error_signal_2 / V
+ list(range(len(error_signal_2))), error_signal_2 / V / self.adc2_afe_gain
)
self.monitorSignal.setVisible(not dual_channel)
if monitor_signal is not None:
self.monitorSignal.setData(
- list(range(len(monitor_signal))), monitor_signal / V
+ list(range(len(monitor_signal))), monitor_signal / V / self.adc2_afe_gain
)
self.plot_autolock_target_line(combined_error_signal)
@@ -593,7 +610,7 @@ class PlotWidget(pg.PlotWidget):
self.parameters.offset_a.value,
getattr(self.app.settings, f"plot_color_{color}").value,
)
- / V
+ / V / self.adc1_afe_gain
)
self.signal_power1.emit(
@@ -616,7 +633,7 @@ class PlotWidget(pg.PlotWidget):
f"plot_color_{Color.ERROR2.value}",
).value,
)
- / V
+ / V / self.adc2_afe_gain
)
self.signal_power2.emit(

View File

@ -0,0 +1,17 @@
diff --git a/linien-gui/linien_gui/ui/plot_widget.py b/linien-gui/linien_gui/ui/plot_widget.py
index fe20e66..f5d03c3 100644
--- a/linien-gui/linien_gui/ui/plot_widget.py
+++ b/linien-gui/linien_gui/ui/plot_widget.py
@@ -680,8 +680,10 @@ class PlotWidget(pg.PlotWidget):
fill.setBrush(brush)
invisible_pen = pg.mkPen("k", width=0.00001)
- signal.setData(x, upper, pen=invisible_pen)
- neg_signal.setData(x, lower, pen=invisible_pen)
+ # Painter path exceeds +/-32767 pixels. Error when OpenGL is enabled
+ # signal.setData(x, upper, pen=invisible_pen)
+ # neg_signal.setData(x, lower, pen=invisible_pen)
+
return np.max([np.max(upper), -1 * np.min(lower)]) * V
def plot_autolock_target_line(self, combined_error_signal):

View File

@ -1,13 +0,0 @@
diff --git a/linien-gui/linien_gui/ui/plot_widget.py b/linien-gui/linien_gui/ui/plot_widget.py
index f3b81ce..7d865a3 100644
--- a/linien-gui/linien_gui/ui/plot_widget.py
+++ b/linien-gui/linien_gui/ui/plot_widget.py
@@ -40,7 +40,7 @@ from pyqtgraph.Qt import QtCore
# NOTE: this is required for using a pen_width > 1. There is a bug though that causes
# the plot to be way too small. Therefore, we call PlotWidget.resize() after a while
pg.setConfigOptions(
- useOpenGL=True,
+ useOpenGL=False,
# by default, pyqtgraph tries to clean some things up using atexit. This causes
# problems with rpyc objects as their connection is already closed. Therefore, we
# disable this cleanup.

View File

@ -65,11 +65,11 @@ index 3d8b8bf..72f6159 100644
</property>
<property name="minimum">
- <double>-1.000000000000000</double>
+ <double>-0.250000000000000</double>
+ <double>-0.50000000000000</double>
</property>
<property name="maximum">
- <double>1.000000000000000</double>
+ <double>0.250000000000000</double>
+ <double>0.50000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
@ -78,7 +78,7 @@ index 3d8b8bf..72f6159 100644
</property>
<property name="maximum">
- <double>1.000000000000000</double>
+ <double>0.250000000000000</double>
+ <double>0.50000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
@ -122,10 +122,88 @@ index 6d8af14..29c8a63 100644
</property>
<property name="maximum">
- <double>2.000000000000000</double>
+ <double>0.500000000000000</double>
+ <double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
diff --git a/linien-gui/linien_gui/ui/optimization_panel.ui b/linien-gui/linien_gui/ui/optimization_panel.ui
index 3bf165f..6694112 100644
--- a/linien-gui/linien_gui/ui/optimization_panel.ui
+++ b/linien-gui/linien_gui/ui/optimization_panel.ui
@@ -61,7 +61,11 @@
<number>30</number>
</property>
<item>
- <widget class="CustomDoubleSpinBoxNoSign" name="optimization_mod_freq_min"/>
+ <widget class="CustomDoubleSpinBoxNoSign" name="optimization_mod_freq_min">
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ </widget>
</item>
<item>
<widget class="QLabel" name="label_24">
@@ -84,6 +88,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="decimals">
+ <number>4</number>
+ </property>
<property name="value">
<double>10.000000000000000</double>
</property>
@@ -115,7 +122,10 @@
<item>
<widget class="CustomDoubleSpinBoxNoSign" name="optimization_mod_amp_min">
<property name="maximum">
- <double>2.000000000000000</double>
+ <double>1.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.100000000000000</double>
</property>
</widget>
</item>
@@ -135,10 +145,13 @@
<item>
<widget class="CustomDoubleSpinBoxNoSign" name="optimization_mod_amp_max">
<property name="maximum">
- <double>2.000000000000000</double>
+ <double>1.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.100000000000000</double>
</property>
<property name="value">
- <double>2.000000000000000</double>
+ <double>1.000000000000000</double>
</property>
</widget>
</item>
diff --git a/linien-gui/linien_gui/ui/new_device_dialog.ui b/linien-gui/linien_gui/ui/new_device_dialog.ui
index 38c3513..e543744 100644
--- a/linien-gui/linien_gui/ui/new_device_dialog.ui
+++ b/linien-gui/linien_gui/ui/new_device_dialog.ui
@@ -52,7 +52,7 @@
<item>
<widget class="QLineEdit" name="host">
<property name="text">
- <string>rp-xxxxxx.local</string>
+ <string>192.168.1.129</string>
</property>
</widget>
</item>
@@ -67,6 +67,9 @@
<property name="wordWrap">
<bool>true</bool>
</property>
+ <property name="visible">
+ <bool>false</bool>
+ </property>
</widget>
</item>
</layout>
diff --git a/linien-gui/linien_gui/ui/sweep_control.py b/linien-gui/linien_gui/ui/sweep_control.py
index 7d2bb2d..e98c169 100644
--- a/linien-gui/linien_gui/ui/sweep_control.py
@ -136,6 +214,32 @@ index 7d2bb2d..e98c169 100644
# set control boundaries
- self.setMinimum(-1.0)
- self.setMaximum(1.0)
+ self.setMinimum(-0.25)
+ self.setMaximum(0.25)
+ self.setMinimum(-0.5)
+ self.setMaximum(0.5)
self.setSingleStep(0.001)
diff --git a/linien-client/linien_client/device.py b/linien-client/linien_client/device.py
index 727fbba..2f0f704 100644
--- a/linien-client/linien_client/device.py
+++ b/linien-client/linien_client/device.py
@@ -48,7 +48,7 @@ class Device:
def __post_init__(self):
if self.host == "":
- self.host = "rp-xxxxxx.local"
+ self.host = "192.168.1.129"
if self.username == "":
self.username = "root"
if self.password == "":
diff --git a/linien-gui/linien_gui/ui/device_manager.py b/linien-gui/linien_gui/ui/device_manager.py
index 16994ad..85abcf2 100644
--- a/linien-gui/linien_gui/ui/device_manager.py
+++ b/linien-gui/linien_gui/ui/device_manager.py
@@ -151,7 +151,7 @@ class DeviceManager(QtWidgets.QMainWindow):
]
) -> None:
question = (
- "Linien on RedPitaya is running with different parameters than the "
+ "Linien on Fast-Servo is running with different parameters than the "
"ones saved locally on this machine. Do you want to upload the local "
"parameters or keep the remote ones? Note that remote parameters are "
"only saved if Linien server was shut down properly, not when "

View File

@ -29,7 +29,7 @@ index fe482d7..04a3c24 100644
# ------------------- SWEEP PARAMETERS -----------------------------------------
- self.sweep_amplitude = Parameter(min_=0.001, max_=1, start=1, loggable=True)
+ self.sweep_amplitude = Parameter(min_=0.001, max_=0.25, start=0.25, loggable=True)
+ self.sweep_amplitude = Parameter(min_=0.001, max_=0.5, start=0.5, loggable=True)
"""
Amplitude of the sweep in units of 0.5 * Vpp of the output (2 V for fast outputs
(range +/- 1 V) and 0.9 V for slow outputs (range 0 V to 1.8 V)). That means an
@ -37,7 +37,7 @@ index fe482d7..04a3c24 100644
"""
- self.sweep_center = Parameter(min_=-1, max_=1, start=0, loggable=True)
+ self.sweep_center = Parameter(min_=-0.25, max_=0.25, start=0, loggable=True)
+ self.sweep_center = Parameter(min_=-0.5, max_=0.5, start=0, loggable=True)
"""
The center position of the sweep. If a fast output is used for the sweep this is
the sweep center position in volts. If the slow output is used the interval
@ -50,14 +50,14 @@ index 2b8f697..65e9732 100644
logic_sweep_step=int(
DEFAULT_SWEEP_SPEED
- * self.parameters.sweep_amplitude.value
+ * self.parameters.sweep_amplitude.value * 4
+ * self.parameters.sweep_amplitude.value
/ (2**self.parameters.sweep_speed.value)
),
# NOTE: Sweep center is set by `logic_out_offset`.
- logic_sweep_min=-1 * max_(self.parameters.sweep_amplitude.value * 8191),
- logic_sweep_max=max_(self.parameters.sweep_amplitude.value * 8191),
+ logic_sweep_min=-1 * max_(self.parameters.sweep_amplitude.value * 32767),
+ logic_sweep_max=max_(self.parameters.sweep_amplitude.value * 32767),
+ logic_sweep_min=-1 * max_(self.parameters.sweep_amplitude.value * 16384),
+ logic_sweep_max=max_(self.parameters.sweep_amplitude.value * 16384),
logic_mod_freq=(
self.parameters.modulation_frequency.value
if not self.parameters.pid_only_mode.value
@ -66,7 +66,20 @@ index 2b8f697..65e9732 100644
int(self.parameters.offset_b.value), 14
),
- logic_out_offset=int(self.parameters.sweep_center.value * 8191),
+ logic_out_offset=int(self.parameters.sweep_center.value * 32767),
+ logic_out_offset=int(self.parameters.sweep_center.value * 16384),
logic_combined_offset=twos_complement(
self.parameters.combined_offset.value, 14
),
diff --git a/linien-common/linien_common/config.py b/linien-common/linien_common/config.py
index 6ba92e6..8d8c554 100644
--- a/linien-common/linien_common/config.py
+++ b/linien-common/linien_common/config.py
@@ -24,7 +24,7 @@ logger.setLevel(logging.DEBUG)
ACQUISITION_PORT = 19321
SERVER_PORT = 18862
-DEFAULT_SWEEP_SPEED = (125 * 2048) << 6
+DEFAULT_SWEEP_SPEED = 2 ** 40 / (125 * 10 ** 6 ) * 3800
USER_DATA_PATH = Path(AppDirs("linien").user_data_dir)
USER_DATA_PATH.mkdir(parents=True, exist_ok=True)

View File

@ -146,6 +146,7 @@ def print_adc_channel(ch):
print(f"Final ADC_CH1: 0x{adc_ch1:04x}")
def configure_ltc2195():
# Calling this fn stops the PL input clock momentarily, triggers PL reset.
print()
print("### Initializing LTC2195 Adc")
spi = spidev.SpiDev()

View File

@ -110,12 +110,17 @@ def dac_self_calibration(spi):
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

View File

@ -20,6 +20,7 @@
import mmap
import os
import time
import argparse
from pyfastservo.common import (
LED0_BASE_ADDR,
@ -34,7 +35,29 @@ ON = 1
OFF = 0
def main():
def turn_on_led(led, on):
f = os.open("/dev/mem", os.O_SYNC | os.O_RDWR)
addrs = [LED0_BASE_ADDR, LED1_BASE_ADDR, LED2_BASE_ADDR, LED3_BASE_ADDR]
addr = addrs[led]
with mmap.mmap(
f,
PAGESIZE,
mmap.MAP_SHARED,
mmap.PROT_READ | mmap.PROT_WRITE,
offset=addr & ~MAP_MASK,
) as mem:
start_addr = addr & MAP_MASK
stop_addr = start_addr + 4
if on:
mem[start_addr:stop_addr] = ON.to_bytes(4, "little")
else:
mem[start_addr:stop_addr] = OFF.to_bytes(4, "little")
contents = mem[start_addr:stop_addr]
os.close(f)
def turn_off_all_leds():
addrs = [LED0_BASE_ADDR, LED1_BASE_ADDR, LED2_BASE_ADDR, LED3_BASE_ADDR]
f = os.open("/dev/mem", os.O_SYNC | os.O_RDWR)
@ -46,21 +69,18 @@ def main():
mmap.PROT_READ | mmap.PROT_WRITE,
offset=addr & ~MAP_MASK,
) as mem:
for i in range(6):
start_addr = addr & MAP_MASK
stop_addr = start_addr + 4
print(
f"addr: 0x{addr:x}\tstart_addr: 0x{start_addr}\tstop_addr: 0x{stop_addr}"
)
mem[start_addr:stop_addr] = ON.to_bytes(4, "little")
contents = mem[start_addr:stop_addr]
time.sleep(0.5)
mem[start_addr:stop_addr] = OFF.to_bytes(4, "little")
contents = mem[start_addr:stop_addr]
time.sleep(0.5)
start_addr = addr & MAP_MASK
stop_addr = start_addr + 4
mem[start_addr:stop_addr] = OFF.to_bytes(4, "little")
os.close(f)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("led", type=int)
parser.add_argument("state", type=int)
args = parser.parse_args()
turn_on_led(args.led, bool(args.state))
if __name__ == "__main__":
main()

View File

@ -17,21 +17,32 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from pyfastservo import adc, si5340, dac
from pyfastservo import adc, si5340, dac, fp_leds
from time import sleep
def main():
print()
print("### PL Initialization")
print()
# Set output voltage to be 0V after AD9117-2 soft reset
# DAC starts up in unsigned Data Format. Set DAC to output 0V before Clock is provided to it
dac.set_dac_input_override_value(0x1FFF)
dac.set_dac_input_override(True)
si5340_ok, adc_ok, dac_ok = False, False, False
si5340_ok = si5340.configure_si5340()
adc_ok = adc.configure_ltc2195()
dac_ok = dac.configure_ad9117()
sleep(0.1)
# Do not touch any CSR Register(s) if PL Clock cannot be init-ed.
# Otherwise, the OS will hang up.
if si5340_ok:
# Configuring the LTC2195 would stop PL input clock momentarily and trigger PL reset.
adc_ok = adc.configure_ltc2195()
sleep(0.1)
if adc_ok:
fp_leds.turn_on_led(0, False)
fp_leds.turn_on_led(1, False)
dac_ok = dac.configure_ad9117()
sleep(0.1)
if dac_ok:
fp_leds.turn_on_led(2, False)
fp_leds.turn_on_led(3, False)
if si5340_ok and adc_ok and dac_ok:
print()
@ -44,6 +55,7 @@ def main():
print(f"LTC2195 Adc Init Status: {"Success" if adc_ok else "Failed"}")
print(f"Ad9117 Dac Init Status: {"Success" if dac_ok else "Failed"}")
print()
exit(1)
if __name__ == "__main__":
main()

View File

@ -93,6 +93,9 @@ def check_los_status(bus):
return not xaxb_los
def configure_si5340():
# After PL Reset, this fn needs to be called before accessing any CSR registers.
# Otherwise, the system may hang up.
print()
print("### Initializing Si5340 Clock Generator")
with SMBus(BUS_NO) as bus:

20
flake.lock generated
View File

@ -18,11 +18,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1736867362,
"narHash": "sha256-i/UJ5I7HoqmFMwZEH6vAvBxOrjjOJNU739lnZnhUln8=",
"lastModified": 1743367904,
"narHash": "sha256-sOos1jZGKmT6xxPvxGQyPTApOunXvScV4lNjBCXd/CI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9c6b49aeac36e2ed73a8c472f1546f6d9cf1addc",
"rev": "7ffe0edc685f14b8c635e3d6591b0bbb97365e6c",
"type": "github"
},
"original": {
@ -64,11 +64,11 @@
"src-migen": {
"flake": false,
"locked": {
"lastModified": 1735131698,
"narHash": "sha256-P4vaF+9iVekRAC2/mc9G7IwI6baBpPAxiDQ8uye4sAs=",
"lastModified": 1738906518,
"narHash": "sha256-GproDJowtcgbccsT+I0mObzFhE483shcS8MSszKXwlc=",
"owner": "m-labs",
"repo": "migen",
"rev": "4c2ae8dfeea37f235b52acb8166f12acaaae4f7c",
"rev": "2828df54594673653a641ab551caf6c6b1bfeee5",
"type": "github"
},
"original": {
@ -80,11 +80,11 @@
"src-misoc": {
"flake": false,
"locked": {
"lastModified": 1736416570,
"narHash": "sha256-tbcN/fzejZIaYbTbwk8Ir1glYevESqMinMeDB3z8oxg=",
"lastModified": 1741935500,
"narHash": "sha256-rbh4++/M1FQw57fUKow1MNXpmFfguUH5LlxfklDvUjs=",
"ref": "refs/heads/master",
"rev": "1f5318e9edc1085ac77e9b85b8f5e03371dba54c",
"revCount": 2464,
"rev": "ebeff99217e6e23eb410d7c208d1f17443a57322",
"revCount": 2496,
"submodules": true,
"type": "git",
"url": "https://github.com/m-labs/misoc.git"

View File

@ -32,7 +32,7 @@
./fast-servo/linien-server-fast-servo-hardware-specific.patch
./fast-servo/linien-gui-fast-servo-hardware-specific.patch
./fast-servo/linien-gui-add-afe_gain-combo-boxes.patch
./fast-servo/linien-gui-do-not-use-opengl.patch
./fast-servo/linien-gui-do-not-draw-signal-strength.patch
./fast-servo/linien-gui-hide-password-box.patch
./fast-servo/linien-gui-do-not-check-for-update.patch
./fast-servo/linien-client-ssh-port-change.patch
@ -64,6 +64,7 @@
./not-os-patches/pr-34.patch
./not-os-patches/iproute2.patch
./not-os-patches/user_defined_ip_settings.patch
./not-os-patches/init_script_service.patch
];
};
@ -411,6 +412,25 @@
hardeningDisable = [ "fortify" ];
};
fs-mode = pkgs-armv7l.writeScriptBin "fs-mode" ''
if [ "$1" = "ro" ]; then
mount -o remount,rw /boot
rm -f /boot/fs_mode_rw
echo "System will boot in read-only mode after reboot."
sync
reboot
elif [ "$1" = "rw" ]; then
mount -o remount,rw /boot
touch /boot/fs_mode_rw
echo "System will boot in read-write mode after reboot."
sync
reboot
else
echo "Usage: fs-mode [ro|rw]"
exit 1
fi
'';
board-package-set = { board }: let
not-os-configured = (import patched-not-os {
inherit nixpkgs;
@ -420,6 +440,7 @@
({ config, pkgs, lib, ... }: {
environment.systemPackages = [
(pkgs.python3.withPackages(ps: with ps; [ pyfastservo linien-server]))
fs-mode
];
boot.postBootCommands = lib.mkAfter ''
@ -431,8 +452,6 @@
cp ${fast-servo-gateware}/gateware.bin /lib/firmware/
echo gateware.bin > /sys/class/fpga_manager/fpga0/firmware
# Initialize All PL Peripherals
python3 -m pyfastservo.initialize
'';
})];
system = "x86_64-linux";

View File

@ -0,0 +1,55 @@
diff --git a/runit.nix b/runit.nix
index 982823d..906fa24 100644
--- a/runit.nix
+++ b/runit.nix
@@ -28,6 +28,23 @@ in
environment.systemPackages = [ compat ];
environment.etc = lib.mkMerge [
{
+ "pl_init".source = pkgs.writeScript "pl_init" ''
+ #!/bin/sh
+ exec 2>&1
+
+ python -m pyfastservo.initialize
+ EXIT_CODE=$?
+
+ if [ $EXIT_CODE -eq 0 ]; then
+ # Add Symlink linien service to /etc/service
+ ln -sf /etc/linien-server /etc/service/linien-server
+ else
+ echo "PL Initialization failed. Linien Server Service is not Started."
+ fi
+
+ exit 0
+ '';
+
"runit/1".source = pkgs.writeScript "1" ''
#!${pkgs.runtimeShell}
${lib.optionalString config.not-os.simpleStaticIp ''
@@ -53,6 +70,8 @@ in
touch /etc/runit/stopit
chmod 0 /etc/runit/stopit
${if true then "" else "${pkgs.dhcpcd}/sbin/dhcpcd"}
+
+ /etc/pl_init
'';
"runit/2".source = pkgs.writeScript "2" ''
#!${pkgs.runtimeShell}
diff --git a/zynq_image.nix b/zynq_image.nix
index 0dad456..fce8797 100644
--- a/zynq_image.nix
+++ b/zynq_image.nix
@@ -51,11 +51,11 @@ in {
hostname ${config.networking.hostName}
exec setsid agetty ttyPS0 115200
'';
- "service/linien-server/run".source = pkgs.writeShellScript "linien-server" ''
+ "linien-server/run".source = pkgs.writeShellScript "linien-server" ''
exec 2>&1
exec setsid linien-server run
'';
- "service/linien-server/log/run".source = pkgs.writeShellScript "linien-server-logger" ''
+ "linien-server/log/run".source = pkgs.writeShellScript "linien-server-logger" ''
exec 2>&1
mkdir -p /root/linien-server-log
exec svlogd -tt /root/linien-server-log

View File

@ -59,7 +59,7 @@ index d7b0bf3..70353a1 100644
'';
}
diff --git a/stage-1.nix b/stage-1.nix
index 331fecd..aa5148e 100644
index 331fecd..f1f73cb 100644
--- a/stage-1.nix
+++ b/stage-1.nix
@@ -117,11 +117,6 @@ let
@ -74,18 +74,63 @@ index 331fecd..aa5148e 100644
root=/dev/vda
realroot=tmpfs
for o in $(cat /proc/cmdline); do
@@ -164,7 +159,9 @@ let
@@ -163,20 +158,41 @@ let
chmod 755 /mnt/
mkdir -p /mnt/nix/store/
-
- ${if config.not-os.nix then ''
- # make the store writeable
- mkdir -p /mnt/nix/.ro-store /mnt/nix/.overlay-store /mnt/nix/store
- mount $root /mnt/nix/.ro-store -t squashfs
- if [ $realroot = $1 ]; then
- mount tmpfs -t tmpfs /mnt/nix/.overlay-store -o size=1G
- fi
- mkdir -pv /mnt/nix/.overlay-store/work /mnt/nix/.overlay-store/rw
- modprobe overlay
- mount -t overlay overlay -o lowerdir=/mnt/nix/.ro-store,upperdir=/mnt/nix/.overlay-store/rw,workdir=/mnt/nix/.overlay-store/work /mnt/nix/store
- '' else ''
- # readonly store
- mount $root /mnt/nix/store/ -t squashfs
+ ${if config.not-os.sd && config.not-os.nix then ''
+ mount $root /mnt
+ '' else if config.not-os.nix then ''
# make the store writeable
mkdir -p /mnt/nix/.ro-store /mnt/nix/.overlay-store /mnt/nix/store
mount $root /mnt/nix/.ro-store -t squashfs
@@ -190,6 +187,11 @@ let
+ mkdir -p /boot
+ mount -t vfat -o ro /dev/mmcblk0p1 /boot
+ USE_READONLY=1
+ if [ -e /boot/rw_mode ]; then
+ echo "Found rw_mode flag, enabling read-write mode"
+ USE_READONLY=0
+ fi
+ if [ "$USE_READONLY" = "1" ]; then
+ mkdir -p /mnt.ro /mnt.overlay
+ mount -o ro $root /mnt.ro
+ mount -t tmpfs -o size=1G tmpfs /mnt.overlay
+ mkdir -p /mnt.overlay/upper /mnt.overlay/work
+ mount -t overlay overlay -o lowerdir=/mnt.ro,upperdir=/mnt.overlay/upper,workdir=/mnt.overlay/work /mnt
+ mkdir -p /mnt/boot
+ else
+ mount $root /mnt
+ mkdir -p /mnt/boot
+ fi
+ mount --move /boot /mnt/boot
+ ''
+ else if config.not-os.nix then ''
+ # make the store writeable
+ mkdir -p /mnt/nix/.ro-store /mnt/nix/.overlay-store /mnt/nix/store
+ mount $root /mnt/nix/.ro-store -t squashfs
+ if [ $realroot = $1 ]; then
+ mount tmpfs -t tmpfs /mnt/nix/.overlay-store -o size=1G
+ fi
+ mkdir -pv /mnt/nix/.overlay-store/work /mnt/nix/.overlay-store/rw
+ modprobe overlay
+ mount -t overlay overlay -o lowerdir=/mnt/nix/.ro-store,upperdir=/mnt/nix/.overlay-store/rw,workdir=/mnt/nix/.overlay-store/work /mnt/nix/store
+ ''
+ else ''
+ # readonly store
+ mount $root /mnt/nix/store/ -t squashfs
''}
${lib.optionalString enablePlymouth ''
@@ -190,6 +206,11 @@ let
initialRamdisk = pkgs.makeInitrd {
contents = [ { object = bootStage1; symlink = "/init"; } ];
};
@ -97,7 +142,7 @@ index 331fecd..aa5148e 100644
in
{
options = {
@@ -205,6 +207,7 @@ in
@@ -205,6 +226,7 @@ in
config = {
system.build.bootStage1 = bootStage1;
system.build.initialRamdisk = initialRamdisk;
@ -152,10 +197,10 @@ index c61f9d6..fbdf0fd 100644
};
}
diff --git a/zynq_image.nix b/zynq_image.nix
index 3fa23ab..069fe89 100644
index 3fa23ab..1790801 100644
--- a/zynq_image.nix
+++ b/zynq_image.nix
@@ -1,66 +1,89 @@
@@ -1,66 +1,91 @@
-{ config, pkgs, ... }:
+{ lib, config, pkgs, ... }:
@ -167,6 +212,8 @@ index 3fa23ab..069fe89 100644
+ customKernel = (pkgs.linux_6_6.override {
extraConfig = ''
OVERLAY_FS y
+ NLS_CODEPAGE_437 y
+ NLS_ISO8859_1 y
+ MEDIA_SUPPORT n
+ FB n
+ DRM n