Skip to content

Commit

Permalink
Merge pull request #312 from linien-org/feature/slow-integrator-fast-…
Browse files Browse the repository at this point in the history
…dacs

Add ability to output slow PID on fast DACs
  • Loading branch information
bleykauf authored Mar 21, 2023
2 parents 76e0d9c + a358fe7 commit 2794e04
Show file tree
Hide file tree
Showing 35 changed files with 606 additions and 588 deletions.
10 changes: 5 additions & 5 deletions .flake8
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[flake8]
max-line-length = 88
per-file-ignores =
extend-ignore = E203, E402, E731, W503
tests/*: D103, D100
[flake8]
max-line-length = 88
per-file-ignores =
extend-ignore = E203, E402, E731, W503
tests/*: D103, D100
1 change: 1 addition & 0 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Run PyTest

on:
workflow_dispatch:
push:
paths:
- gateware/**
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Features
- **Logging**: Use
[linien-influxdb](https://github.com/linien-org/linien-influxdb)
to log the lock status to influxdb.
- **Second integrator** on (slow) analog output 0
- **Second integrator** for slow control of piezo in an ECDL
- **Additional analog outputs** may be used using the GUI or python client (ANALOG_OUT 1, 2 and 3)
- **16 GPIO outputs** may be programmed (e.g. for controlling other devices)

Expand Down Expand Up @@ -439,9 +439,10 @@ License
-------
Linien ‒ User-friendly locking of lasers using RedPitaya (STEMlab 125-14) that just works.

Copyright © 2014-2015 Robert Jördens
Copyright © 2018-2022 Benjamin Wiegand
Copyright © 2021-2023 Bastian Leykauf
Copyright © 2014-2015 Robert Jördens\
Copyright © 2018-2022 Benjamin Wiegand\
Copyright © 2021-2023 Bastian Leykauf\
Copyright © 2022 Christian Freier

Linien 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.

Expand Down
2 changes: 1 addition & 1 deletion gateware/bit2bin.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def bit2bin(bit, bin, flip=False):
bitfile = open(bit, "rb")

(l,) = struct.unpack(">H", bitfile.read(2))
if l != 9:
if l != 9: # noqa E741
raise ValueError("Missing <0009> header, not a bit file")

bitfile.read(l)
Expand Down
185 changes: 93 additions & 92 deletions gateware/linien_module.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright 2014-2015 Robert Jördens <[email protected]>
# Copyright 2018-2022 Benjamin Wiegand <[email protected]>
# Copyright 2021-2022 Bastian Leykauf <[email protected]>
# Copyright 2021-2023 Bastian Leykauf <[email protected]>
# Copyright 2022 Christian Freier <[email protected]>
#
# This file is part of Linien and based on redpid.
#
Expand Down Expand Up @@ -52,43 +53,37 @@

class LinienLogic(Module, AutoCSR):
def __init__(self, width=14, signal_width=25, chain_factor_width=8, coeff_width=25):
self.init_csr(width, signal_width, chain_factor_width)
self.init_csr(width, chain_factor_width)
self.init_submodules(width, signal_width)
self.connect_pid()
self.connect_everything(width, signal_width, coeff_width)

def init_csr(self, width, signal_width, chain_factor_width):
factor_reset = 1 << (chain_factor_width - 1)
# we use chain_factor_width + 1 for the single channel mode
def init_csr(self, width, chain_factor_width):
self.dual_channel = CSRStorage(1)
self.mod_channel = CSRStorage(1)
self.control_channel = CSRStorage(1)
self.sweep_channel = CSRStorage(2)
self.control_slow_channel = CSRStorage(2)
self.fast_mode = CSRStorage(1)

# we use chain_factor_width + 1 for the single channel mode
factor_reset = 1 << (chain_factor_width - 1)
self.chain_a_factor = CSRStorage(chain_factor_width + 1, reset=factor_reset)
self.chain_b_factor = CSRStorage(chain_factor_width + 1, reset=factor_reset)

self.chain_a_offset = CSRStorage(width)
self.chain_b_offset = CSRStorage(width)
self.chain_a_offset_signed = Signal((width, True))
self.chain_b_offset_signed = Signal((width, True))
self.combined_offset = CSRStorage(width)
self.combined_offset_signed = Signal((width, True))
self.out_offset = CSRStorage(width)
self.out_offset_signed = Signal((width, True))

self.mod_channel = CSRStorage(1)
self.control_channel = CSRStorage(1)
self.sweep_channel = CSRStorage(2)

self.fast_mode = CSRStorage(1)
self.slow_decimation = CSRStorage(bits_for(16))
for i in range(1, 4):
setattr(self, f"analog_out_{i}", CSRStorage(15, name=f"analog_out_{i}"))

self.slow_value = CSRStatus(width)

max_decimation = 16
self.slow_decimation = CSRStorage(bits_for(max_decimation))

for i in range(4):
if i == 0:
continue
name = "analog_out_%d" % i
setattr(self, name, CSRStorage(15, name=name))
self.chain_a_offset_signed = Signal((width, True))
self.chain_b_offset_signed = Signal((width, True))
self.out_offset_signed = Signal((width, True))

def init_submodules(self, width, signal_width):
self.submodules.mod = Modulate(width=width)
Expand All @@ -97,7 +92,6 @@ def init_submodules(self, width, signal_width):
self.submodules.limit_fast1 = LimitCSR(width=width, guard=5)
self.submodules.limit_fast2 = LimitCSR(width=width, guard=5)
self.submodules.pid = PID(width=signal_width)

self.submodules.autolock = FPGAAutolock(width=width, max_delay=8191)

def connect_pid(self):
Expand All @@ -115,13 +109,11 @@ def connect_pid(self):
]

def connect_everything(self, width, signal_width, coeff_width):
s = signal_width - width

combined_error_signal = Signal((signal_width, True))
self.control_signal = Signal((signal_width, True))

# additional IIR filter that prevents aliasing effects when recording
# PSD of error signal
# additional IIR filter that prevents aliasing effects when recording PSD of
# error signal
self.submodules.raw_acquisition_iir = Iir(
width=signal_width,
coeff_width=coeff_width,
Expand Down Expand Up @@ -158,15 +150,16 @@ def connect_everything(self, width, signal_width, coeff_width):
Array([self.limit_fast1.y, self.limit_fast2.y])[
self.control_channel.storage
]
<< s
<< signal_width - width
),
]


class LinienModule(Module, AutoCSR):
def __init__(self, platform):
width = 14
signal_width, coeff_width = 25, 25
signal_width = 25
coeff_width = 25
chain_factor_bits = 8

self.init_submodules(
Expand All @@ -191,7 +184,7 @@ def init_submodules(
pwm = platform.request("pwm", i)
ds = sys_double(DeltaSigma(width=15))
self.comb += pwm.eq(ds.out)
setattr(self.submodules, "ds%i" % i, ds)
setattr(self.submodules, f"ds{i}", ds)

exp = platform.request("exp")
self.submodules.gpio_n = Gpio(exp.n)
Expand Down Expand Up @@ -223,7 +216,7 @@ def init_submodules(
self.submodules.decimate = sys_double(Decimate(max_decimation))
self.clock_domains.cd_decimated_clock = ClockDomain()
decimated_clock = ClockDomainsRenamer("decimated_clock")
self.submodules.slow = decimated_clock(SlowChain())
self.submodules.slow_chain = decimated_clock(SlowChain())

self.submodules.scopegen = ScopeGen(signal_width)

Expand All @@ -232,7 +225,7 @@ def init_submodules(
[
("fast_a", self.fast_a),
("fast_b", self.fast_b),
("slow", self.slow),
("slow_chain", self.slow_chain),
("scopegen", self.scopegen),
("logic", self.logic),
("robust", self.logic.autolock.robust),
Expand All @@ -246,7 +239,7 @@ def init_submodules(
"gpio_p": 31,
"fast_a": 0,
"fast_b": 1,
"slow": 2,
"slow_chain": 2,
"scopegen": 6,
"noise": 7,
"logic": 8,
Expand Down Expand Up @@ -299,6 +292,7 @@ def connect_everything(self, width, signal_width, coeff_width, chain_factor_bits
mixed_limited.eq(self.logic.limit_error_signal.y),
]

# FAST PID ---------------------------------------------------------------------
pid_out = Signal((width, True))
self.comb += [
If(
Expand All @@ -310,72 +304,84 @@ def connect_everything(self, width, signal_width, coeff_width, chain_factor_bits
pid_out.eq(self.logic.pid.pid_out >> s),
]

fast_outs = list(Signal((width + 4, True)) for channel in (0, 1))
# SLOW PID ---------------------------------------------------------------------
self.comb += [
self.slow_chain.pid.running.eq(self.logic.autolock.lock_running.status),
self.slow_chain.input.eq(self.logic.control_signal >> s),
self.decimate.decimation.eq(self.logic.slow_decimation.storage),
self.cd_decimated_clock.clk.eq(self.decimate.output),
self.logic.slow_value.status.eq(self.slow_chain.output),
]

for channel, fast_out in enumerate(fast_outs):
# FAST OUTPUTS -----------------------------------------------------------------
fast_outs = [Signal((width + 4, True)), Signal((width + 4, True))]
for n_channel, fast_out in enumerate(fast_outs):
self.comb += fast_out.eq(
Mux(self.logic.control_channel.storage == channel, pid_out, 0)
+ Mux(self.logic.mod_channel.storage == channel, self.logic.mod.y, 0)
Mux(
self.logic.control_channel.storage == n_channel,
pid_out,
0,
)
+ Mux(
self.logic.sweep_channel.storage == channel, self.logic.sweep.y, 0
self.logic.mod_channel.storage == n_channel,
self.logic.mod.y,
0,
)
+ Mux(
self.logic.sweep_channel.storage == n_channel,
self.logic.sweep.y,
0,
)
+ Mux(
self.logic.sweep_channel.storage == channel,
self.logic.sweep_channel.storage == n_channel,
self.logic.out_offset_signed,
0,
)
+ Mux(
self.logic.control_slow_channel.storage == n_channel,
self.slow_chain.output,
0,
)
)

for analog_idx in range(4):
if analog_idx == 0:
# first analog out gets a special treatment bc it may contain signal of
# slow pid or sweep
self.comb += self.slow.pid.running.eq(
self.logic.autolock.lock_running.status
# ANALOG OUTPUTS ---------------------------------------------------------------
# ANALOG OUT 0 gets a special treatment because it may contain signal of slow
# pid or sweep
analog_out = Signal((width + 3, True))
self.comb += [
analog_out.eq(
Mux(
self.logic.sweep_channel.storage == ANALOG_OUT0,
self.logic.sweep.y,
0,
)

slow_pid_out = Signal((width, True))
self.comb += slow_pid_out.eq(self.slow.output)

slow_out = Signal((width + 3, True))
self.comb += [
slow_out.eq(
slow_pid_out
+ Mux(
self.logic.sweep_channel.storage == ANALOG_OUT0,
self.logic.sweep.y,
0,
)
+ Mux(
self.logic.sweep_channel.storage == ANALOG_OUT0,
self.logic.out_offset_signed,
0,
)
),
self.slow.limit.x.eq(slow_out),
]

slow_out_shifted = Signal(15)
self.sync += slow_out_shifted.eq(
# ds0 apparently has 16 bit, but only allowing positive
# values --> "15 bit"?
(self.slow.limit.y << 1)
+ (1 << 14)
+ Mux(
self.logic.sweep_channel.storage == ANALOG_OUT0,
self.logic.out_offset_signed,
0,
)
+ Mux(
self.logic.control_slow_channel.storage == ANALOG_OUT0,
self.slow_chain.output,
0,
)
),
]
# NOTE: not sure why limit is used
self.comb += self.slow_chain.limit.x.eq(analog_out)
# ds0 apparently has 16 bit, but only allowing positive values --> "15 bit"?
slow_out_shifted = Signal(15)
self.sync += slow_out_shifted.eq((self.slow_chain.limit.y << 1) + (1 << 14))
self.comb += self.ds0.data.eq(slow_out_shifted)

# connect other analog outputs
self.comb += [
self.ds1.data.eq(self.logic.analog_out_1.storage),
self.ds2.data.eq(self.logic.analog_out_2.storage),
self.ds3.data.eq(self.logic.analog_out_3.storage),
]

analog_out = slow_out_shifted
else:
# 15 bit
dc_source = [
None,
self.logic.analog_out_1.storage,
self.logic.analog_out_2.storage,
self.logic.analog_out_3.storage,
][analog_idx]
analog_out = dc_source

dss = [self.ds0, self.ds1, self.ds2, self.ds3]
self.comb += dss[analog_idx].data.eq(analog_out)
# ------------------------------------------------------------------------------

self.sync += [
self.logic.autolock.robust.input.eq(self.scopegen.scope_written_data),
Expand All @@ -399,11 +405,6 @@ def connect_everything(self, width, signal_width, coeff_width, chain_factor_bits
),
self.analog.dac_a.eq(self.logic.limit_fast1.y),
self.analog.dac_b.eq(self.logic.limit_fast2.y),
# SLOW OUT
self.slow.input.eq(self.logic.control_signal >> s),
self.decimate.decimation.eq(self.logic.slow_decimation.storage),
self.cd_decimated_clock.clk.eq(self.decimate.output),
self.logic.slow_value.status.eq(self.slow.limit.y),
]

# Having this in a comb statement caused errors. See PR #251.
Expand Down
Loading

0 comments on commit 2794e04

Please sign in to comment.