Giter Club home page Giter Club logo

stabilizer's Introduction

QUARTIQ Matrix Chat Continuous Integration Stabilizer HITL [Nightly]

Stabilizer Firmware

Applications

Check out the Documentation for more information on usage, configuration, and development.

Hardware

Stabilizer

Pounder

stabilizer's People

Contributors

bors[bot] avatar cjbe avatar dependabot-preview[bot] avatar dependabot[bot] avatar dnadlinger avatar doronbehar avatar harrymakes avatar jordens avatar matthuszagh avatar nkrackow avatar nkuh avatar pmldrmota avatar ryan-summers avatar sbourdeauducq avatar sergachev avatar svrotter avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stabilizer's Issues

MQTT settings/status/telemetry framework

To make MQTT easy to deploy on Stabilizer (and Thermostat, Thermostat_EEM, Driver, Booster, Humpback etc.) we need to reduce it's footprint in the code by developing some kind of framework.

Status quo

For Stabilizer we have currently to route the json-over-TCP requests:

stabilizer/src/main.rs

Lines 109 to 158 in 66c917b

macro_rules! route_request {
($request:ident,
readable_attributes: [$($read_attribute:tt: $getter:tt),*],
modifiable_attributes: [$($write_attribute:tt: $TYPE:ty, $setter:tt),*]) => {
match $request.req {
server::AccessRequest::Read => {
match $request.attribute {
$(
$read_attribute => {
let value = match $getter() {
Ok(data) => data,
Err(_) => return server::Response::error($request.attribute,
"Failed to read attribute"),
};
let encoded_data: String<U256> = match serde_json_core::to_string(&value) {
Ok(data) => data,
Err(_) => return server::Response::error($request.attribute,
"Failed to encode attribute value"),
};
server::Response::success($request.attribute, &encoded_data)
},
)*
_ => server::Response::error($request.attribute, "Unknown attribute")
}
},
server::AccessRequest::Write => {
match $request.attribute {
$(
$write_attribute => {
let new_value = match serde_json_core::from_str::<$TYPE>(&$request.value) {
Ok(data) => data,
Err(_) => return server::Response::error($request.attribute,
"Failed to decode value"),
};
match $setter(new_value) {
Ok(_) => server::Response::success($request.attribute, &$request.value),
Err(_) => server::Response::error($request.attribute,
"Failed to set attribute"),
}
}
)*
_ => server::Response::error($request.attribute, "Unknown attribute")
}
}
}
}
}

For Booster there is a sizeable amount of request routing, deserialization, serialization code. Adding a setting or telemetry quantity to the stack requires multiple adjustment in several different places.

Ideas

(ripped from #147): Stabilizer is somewhat similar to redpid (https://github.com/quartiq/redpid). Redpid has a pretty convenient solution to the settings/status problem. For migen/misoc we have a very generic way of configuration-status-registers. They are instantiated in code with a single line (e.g. https://github.com/quartiq/redpid/blob/master/gateware/limit.py#L55) and then end up on a bus with mapping generated automatically (https://github.com/quartiq/redpid/blob/master/test/csrmap.py#L109).
This is very different from Stabilizer in terms of code (HDL versus machine) and transport/protocol (HDL bus CSRs over other buses and serialization versus MQTT) but the user-facing concept is the same: lots of knobs and displays on a big switchboard. The knobs being connected to settings and the displays being connected to telemetry.
Maybe for Stabilizer we want something similar (a named map of settings and status values).
The settings aspect should be double-buffered/paged/ping-pong to allow atomic updates of multiple settings synchronously without having to transfer the entire settings space.
And we need to think a bit about the status telemetry: There might be a case for configurable rates and suppression of some telemetry updates.

Context

Networking experimental devices: MQTT

This issue serves to extend on #54 and #24. Quartiq devices will be managed/interconnected over a network, how should that be implemented?

@jordens suggested MQTT as a suitable protocol. I’ve investigated that option a bit and will compare it with what we currently have. I’ll try to summarize the approaches, and distill features/requirements/use-cases served by aspects of those approaches. Please fill in whatever I miss.

Status-quo

Yeet JSON to a TCP port. This is what stabilizer supports currently.

  • I can telnet/netcat to the port, type in my JSON and that’ll work.
  • I can vendor smoltcp and a ubiquitous JSON encoder/decoder crate and call it a day
  • I can probably read it in Wireshark
  • Can be described with JSON schemas, flexible extensible
  • Question: is JSON is a desirable wire format? Debatable.

MQTT

MQTT is a message bus protocol that facilitates N to N connectivity (hub and spoke). It acts as a transport substrate on top of TCP and implements support for application level features such as QoS (unreliable, at least once / only-once delivery), N-to-N broadcasting, and some more. Classic request-response patterns are re-implemented on top of more generic MQTT constructs.

I’ve implemented a minimal MQTT client that can publish/subscribe (QoS=0) to a topic in ~200LoC. Running ejabberd as a broker was fairly straightforward.

  • What MQTT would give us is a well defined way to control/orchestrate N devices on a network—instead of having one concurrent connection per device we’d have all devices connect to the broker, and one or more controller/orchestrator clients that connects to the broker to perform the experiment.
  • (We could go for a zero-conf setup and use e.g. DHCP/mDNS to have our devices automatically connect to local broker on startup, and implement a scheme to have automatic discovery of active devices)
  • The devices could also interact with each other without establishing connections (meshing) with each other
  • We could control auxiliary protocols like high-bandwidth sample streaming via MQTT (E.g., sending a MQTT message dump:10.10.0.7:4567 to stabilizer_XYZ could trigger it to attempt to open a TCP socket to 10.10.0.7:4567 and stream samples over that socket)
  • We’d have a clear interface (the broker) to extract low-bandwidth time series data for historic analysis
  • Caveat: you need a MQTT broker to talk to your devices (us implementing a client subset seems like reasonable effort, maintaining an MQTT broker would possibly be overkill—we’d rely on a third party providing a working broker)
  • MQTT doesn’t define a wire format for payloads (and as mentioned request/response patterns are implemented ad-hoc), we’d do our status-quo or an alternative protocol over MQTT instead of plain TCP. I.e., MQTT does not supersede any functionality that is currently implemented.
  • I can maybe still read our protocol on top of MQTT in Wireshark

Cc @ryan-summers @jordens @cjbe

Cycle counter not counting when firmware flashed (vs run via openocd)

After flashing the firmware via the DFU interface no data is streamed out from port 1234. This seems to be due to the cycle counter not counting, and hence the block that periodically sends the IIR state never running.

Uncommenting this line solves the problem.

In retrospect I have also seen this problem occasionally even when loading the program via openocd.

Migrate custom HAL development to the official STM32 HAL

During development of the HAL for Pounder support, a number of new modules were added to the existing stm32h7xx-hal repository, including:

  • Custom SPi configuration support
  • QSPI indirect read/write operation
  • Timer DMA Support
  • DMA configuration

These modules should be contributed back to the HAL if possible.

`server::json_reply()` raises error after receiving packets on the port

When I send a packet to the port (1235 by default), the firmware will panic at the socket.write_str() line of the json_reply() function in src/server.rs. The most observable symptom is that Pinging will stop to work after I send the packet. Using GDB, whenever I just send a packet to the TCP socket, the firmware seems to panic. Here is the backtrace:

^C
Program received signal SIGINT, Interrupt.
0x08024b28 in rust_begin_unwind (_info=<optimized out>)
(gdb) backtrace
#0  0x08024b28 in rust_begin_unwind (_info=<optimized out>)
#1  0x0801b3ca in core::panicking::panic_fmt () at library/core/src/panicking.rs:85
#2  0x08022f7c in core::option::expect_none_failed () at library/core/src/option.rs:1221
#3  0x0800ec38 in stabilizer::server::json_reply (socket=0x2001ac00, msg=<optimized out>)
    at /rustc/5099914a16a215794ad243df0cc7a05d91d168e0/library/core/src/result.rs:973
#4  0x08011aa4 in stabilizer::idle (c=...) at src/server.rs:127
#5  0x0801b32c in stabilizer::APP::main () at src/main.rs:160

I'd like to stress that this issue was not happening for the first few times I started using my Stabilizer v1.1 board. So far, I can't tell how to replicate this same issue on another Stabilizer.

Suggestion: Better instructions for flashing/debugging with JTAG

Update: the error is produced because of a wrong sequence of supplying power to the board - see this reply. However, the need to use the OpenOCD command gdb_memory_map disable might still be needed in the latest unstable of OpenOCD.


When testing the JTAG programming option with the latest commit of OpenOCD (openocd-org/openocd@1457a1a), running openocd -f stabilizer.cfg would always produce the following error:

$ openocd -f stabilizer.cfg 
Open On-Chip Debugger 0.10.0+dev-snapshot (2020-09-08-07:03)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
WARNING: interface/stlink-v2-1.cfg is deprecated, please switch to interface/stlink.cfg
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 1800 kHz
Info : STLINK V2J29S7 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.380611
Info : stm32h7x.cpu0: hardware has 8 breakpoints, 4 watchpoints
Error: mem2array: Read @ 0x5c001004, w=4, cnt=1, failed
Error executing event examine-end on target stm32h7x.cpu0:
/nix/store/ihj6fj3rw65j6kaxxnnhpm4l3qh1nsqj-openocd-0.10.0-dev/bin/../share/openocd/scripts/target/stm32h7x.cfg:199: Error: 
in procedure 'stm32h7x_dbgmcu_mmw' called at file "/nix/store/ihj6fj3rw65j6kaxxnnhpm4l3qh1nsqj-openocd-0.10.0-dev/bin/../share/openocd/scripts/target/stm32h7x.cfg", line 149
in procedure 'stm32h7x_mmw' called at file "/nix/store/ihj6fj3rw65j6kaxxnnhpm4l3qh1nsqj-openocd-0.10.0-dev/bin/../share/openocd/scripts/target/stm32h7x.cfg", line 224
in procedure 'stm32h7x_mrw' called at file "/nix/store/ihj6fj3rw65j6kaxxnnhpm4l3qh1nsqj-openocd-0.10.0-dev/bin/../share/openocd/scripts/target/stm32h7x.cfg", line 205
at file "/nix/store/ihj6fj3rw65j6kaxxnnhpm4l3qh1nsqj-openocd-0.10.0-dev/bin/../share/openocd/scripts/target/stm32h7x.cfg", line 199
Info : starting gdb server for stm32h7x.cpu0 on 3333
Info : Listening on port 3333 for gdb connections

Then, running cargo run would produce another error:

$ cargo run --release
    Finished release [optimized + debuginfo] target(s) in 0.02s
     Running `gdb -q -x openocd.gdb target/thumbv7em-none-eabihf/release/stabilizer`
Reading symbols from target/thumbv7em-none-eabihf/release/stabilizer...
Info : accepting 'gdb' connection on tcp/3333
Info : Halt timed out, wake up GDB.
Error: timed out while waiting for target halted
Error executing event gdb-attach on target stm32h7x.cpu0:

Warn : negative reply, retrying
Warn : negative reply, retrying
...

After Google search, the error lines mem2array: Read @ 0x5c001004, w=4, cnt=1, failed and timed out while waiting for target halted might be related to a missing gdb_memory_map disable OpenOCD command. Thus, there is a solution online that would solve this issue: https://codelv.com/blog/2020/1/attaching-to-stm32-with-openocd-during-sleep . Simply put, the steps to properly use OpenOCD/GDB on the board are:

  1. Kill all related instances of OpenOCD and GDB.
  2. Run openocd -f stabilizer.cfg -c "gdb_memory_map disable" & to run it in background in the same shell.
    3. Run gdb and use target remote :3333 to verify the connection.
    4. Kill the OpenOCD and GDB again.
    5. Run openocd -f stabilizer.cfg &.
  3. Run cargo run --release.

I am not sure how to simplify these steps yet, but above is what I would suggest to be added to the README for the time being.

Implementation of 2nd biquad

Currently Stabilizer uses one biquad (IIR) per channel to realize the controller transfer function (e. g. PI). Using two biquads per channel enables us to set more complex transfer functions.

This basic implementation uses two additive biquads. For example a transfer function consisting of gain limited I-, P- and D-part is possible: bode plot.

DAC SPI support

  • DMA or triggered with timer
  • SPI support, spi configuration, timing
  • Infinite transfer

lock-in

  • Should work well to >= 100 kHz modulation frequency
  • Modulation styles with varying complexity:
    • external modulation and a soft PLL
    • the digital input and timed ADC DMA sampling or time tagging of samples/blocks
    • internal modulation on a separate DAC channel
    • internal modulation on the PI-controlled DAC channel
    • internal modulation on a digital output
  • 1f/2f, adjustable phase
  • re-use existing IIR filter

Old prototype code:
https://github.com/quartiq/queenmod

image

[meta] use case analysis

With Stabilizer we have a powerfull CPU sitting between ADCs, DACs and DDS channels. Since this is extremely generic it allows dozens of different use cases to be implemented. Implementing all or even many of them is tricky as there are lots of interdependencies, constraints and corner cases. The interactions can also lead to bottle-necks since they compete for the same resources (CPU time, latency). We need to come up with a clear set of high-value use cases and their requirement in terms of features to be implemented. Then we can decide whether features can be made available at compile-time or run-time and how that affects usability, testing and deployment. I'll try to start with the use cases here and then once the features become clearer, fork them into their own issues.

Use cases

  1. IIR control: D-part, I^2 part, notches, LP, HP
  2. WMS: Lock-in, Modulation
  3. MTS: Same as WMS from Stabilizer perspective
  4. FMS/PDH: Demodulation with (Pounder) and then it's either DC (nothing new) or superhet-to-DC (then MTS/WMS)
  5. Phase lock: Lock-in R/phi, phase unwrap
  6. Complex MIMO lock: Some beat input, demodulate superhet with Pounder, feedback amplitude error on AOM amplitude and phase error on AOM FM/PM and slow frequency error on laser piezo/current/temperature DAC output.

I/O Architecture

  • The acquisition and transmission of ADC/DAC/DDS samples should be pushed into the peripherals as much as possible
  • The ADC should run at max rate (allowed by peripherals and hardware), the processing interrupt can run at a submultiple.
  • ADC samples should end up in a CPU-accessible buffer (timer+DMA+SPI) at least as long as the relative ADC sample/processing rate ratio.
  • DAC samples should be take from a memory buffer (timer+DMA+SPI) at least as long as the relative DAC sample/processing rate ratio.
  • DAC sample emission should be triggered by the processing routine or by fixed timer.
  • DDS should transfer a memory block of register settings (one entire profile consisting of frequency, phase, and amplitude on all four channels) to the DDS chip via timer+DMA+QSPI at max throughput. Triggered by processing routine or timer.
  • Two-level modulation of the DDS would also be useful but is somewhat restricted by our design (P0,P1 on non-timer pins)
  • Digital I/O should also be handled by timer+DMA. This applies to the DDS SYNC_CLK PA0 (ETR and "reverse" timestamping of the free CPU clock), the digital front panel inputs (flags, timestamping).

Processing blocks

With the ADC, DAC and DDS data available in memory buffers for the CPU to consume/produce without delay and overhead, the processing should be routines called at configurable rate. The partitioning of the processing to the routines may reflect the graph partitioning (a low-latency single-biquad at high rate between one ADC-DAC pair and a slower demodulating multi-biquad at lower rate between another ADC-DAC pair). How each routine handles the ADC (one to multiple) samples and generates the DAC/DDS data (one to multiple) can be configured by linking up the processing blocks and configuring them.
The data path flexibility may require compile-time configuration in some cases. In general there will be processing blocks that can be inserted and wired up in many different ways in the datapaths.

There may be some kind of configuration language involved here to describe the processing graph(s) and the settings.

TODO: boil all these down into a common DSP language

  • External modulation reference signal input for lock-in (really needed?, PLL or "edge timestamp wide-band"?) #60
  • Modulator with analytic waveform from ext-ref PLL or internal ref #60
  • Arbitrary (sample-based) waveform #60
  • DAC Modulator #60
  • FM/PM modulation through Pounder #60
  • Lock in detection (demodulation and filtering, phase choice, harmonic) #60 #80
  • FIR (internal, fixed F demodulation) fixed demodulation #60 #80
  • Dynamic demodulation with analytic waveform from PLL #80
  • Multiple biquad IIRs (typically in series, for anti-windup) #71
  • A big signal crossbar (IIR ins/outs, offsets, ADCs, DACs, modulations ...) to address muxing and routing, like redpid
  • Timestamping/counting digital inputs for #80, sinara-hw/Pounder#76
  • A flag crossbar (hold #70 , rail, scan/relock/reset, digial i/o) like redpid
  • Scanning #86
  • Transfer function measurement: log sine modulator sweep, data streaming, and host processing #296
  • Relock (Algorithms from nist-digital-servo or redpid appear robust)
  • Data streaming (TBD: configure a UDP target via MQTT and open a fire hose of UDP data, constraints?) #150

Settings/Telemetry

  • tracking #149
  • Monitoring outputs (ADC, error, scan time base, R from lockin, signals and flags on the crossbar)
  • CLI network API as reference implementation
  • GUI

DHCP support

DHCP support would be very useful - I am happy to implement this.

For the time being I would implement switching between an built-in IP address and DHCP via a compile-time option, but in the future it would be useful to have this configurable via the CPU USB interface (if we end up implementing this) or the debug UART.

[v1.1] QSPI signal contention during register read

During analysis of the QSPI interface to the Pounder DDS, it was found that the STM32 appears to be incorrectly driving the QSPI datalines during a clock cycle that the DDS should be transmitting. This results in the QSPI interface potentially reading an invalid value.

We are protected in hardware from this by series 50 Ohm resistors and a software workaround has been put in place to sample QSPI on the falling edge as opposed to the rising edge.

DS4_QuickPrint7

In the above image, IO3 (MSB of QSPI) and SCK are probed while the CSR register is read. The register contents are 0xF6, which means the IO3 should be fully asserted at the third clock cycle.

Stabilizer python script outdated

With the updates to the Stabilizer firmware, the stabilizer.py python script is oudated. It should be updated to utilize the new ethernet interface. A demonstration of the new ethernet interface is provided in pounder_test.py

Server won't acknowledge until a while after the client disconnects

Whenever a client connects to the port and send some valid messages to the board, the server doesn't seem to ACK before the client disconnects. It also would take a few seconds between the moment of client disconnection and server acknowledgement.

I suspect this has to do with the WFI behaviour on these lines. When I remove the WFI function entirely and replace those lines with the following code, the server will acknowledge immediately. Together with my temporary "fix" for Ethernet breakage by removing json_reply(), after I send a valid IIR request the DAC will be reprogrammed immediately.

/* comment out these lines
let sleep = match c.resources.net_interface.poll(
    &mut sockets,
    net::time::Instant::from_millis(time as i64),
) {
    Ok(changed) => changed == false,
    Err(net::Error::Unrecognized) => true,
    Err(e) => {
        info!("iface poll error: {:?}", e);
        true
    }
};

if sleep {
    cortex_m::asm::wfi();
}*/
match c.resources.net_interface.poll(
    &mut sockets,
    net::time::Instant::from_millis(time as i64),
) {
    Ok(_) => { },
    Err(_) => { }
};

Ch1 IIR state diagnostics not updating

Using the current master, the IIR state diagnostics for ch1 (broadcast on port 1234) is not updating, instead it has a fixed constant (plausible) value. The diagnostics for ch0 are correctly updating.

I see this both running via a debug session, and after booting from flash, with both the nightly and stable compiler.
(c.f. #32)

Ping requests only responded to after two requests have arrived

Running the current master firmware, Stabilizer seems to only respond to ping requests after two requests have arrived. Here the default ping period is 1s, so the first ping request is only responded to after the second has been sent, etc.

$ ping 10.255.6.56
PING 10.255.6.56 (10.255.6.56) 56(84) bytes of data.
64 bytes from 10.255.6.56: icmp_seq=1 ttl=64 time=1026 
64 bytes from 10.255.6.56: icmp_seq=2 ttl=64 time=0.303 
64 bytes from 10.255.6.56: icmp_seq=3 ttl=64 time=1015 
64 bytes from 10.255.6.56: icmp_seq=4 ttl=64 time=0.296 
64 bytes from 10.255.6.56: icmp_seq=5 ttl=64 time=1000 
64 bytes from 10.255.6.56: icmp_seq=6 ttl=64 time=0.411 ms

This can be clearly seen in this packet dump.

This is not problem in itself, but suggests that something in the ethernet interface is not quite right.

make pounder optional

Currently this firmware will not boot without Pounder.
Pounder should be made optional. Either and preferrably at runtime (if AD9959 or I2C init fail, remove the pounder endpoints from the API) or by a #[cfg] option.

streaming

  • Minimum overhead, timestamped raw binary sample burst packets
  • Containing a configurable subset of DAC/ADC/DDS high bandwidth data or anything from the signal crossbar
  • Maybe UDP
  • Maybe target controlled and configured via MQTT
  • Best effort task, should not harm the main processing task performance.

forked from #54
details in #99 and #147

Investigate accelerating Pounder profile configuration

@jordens has indicated that there is a desire to configure Pounder asynchronously to avoid CPU overhead. Ideally, pounder could be updated at the 500KHz sampling rate of Stabilizer.

This task involves investigative work into asynchronously sending Pounder configurations over the QSPI interface.

Specifically, the interest is to send up to two channel configurations (phase, amplitude, and frequency) at a rate of 500KHz. The CPU overhead for each configuration should be minimal.

Current investigations:

  • It is possible to configure multiple pounder registers in a single SPI transaction, so once the transaction has been set up, it can be written to the QSPI FIFO or DMA can be used.
  • Measure the amount of time required to configure a transfer in the QSPI peripheral
  • Measure the amount of time required to configure a transfer using DMA
  • Determine if QSPI can operate in "endless transaction" mode

Store configuration in EEPROM

It would be useful to have a way to store user changeable configuration (e.g. PID settings) in the EEPROM (of which 128 bytes are available).

Values to store:

  • IP address (if not using DHCP) = 4 bytes
  • MAC address (if overriding built-in address) = 6 bytes
  • IIR coefficients (4 bytes x 5 words x 2 channels) = 40 bytes
  • channel configuration (4 bytes x {min, max, offset} x 2 channels) = 24 bytes
  • options bitfield:
    • use stored IP address or use DHCP
    • use stored MAC address or use default MAC address
  • structure version number = 1 byte
  • checksum = 1 byte

This totals 77 bytes, so plenty of space remaining.

We should have an explicit command to save the current settings to EEPROM. This way users can mess with settings knowing that the original settings can be easily restored by a restart.

backport rewrite onto v1.0 hardware

  • enables backporting of most fixes and improvements in master (hal/ethernet refactor, cleaned up interrupt and timing, network interface, future developments etc.)
  • requirement for sustained maintenance of a v1.0 code base
  • can't support pounder
  • will likely need to remain a fork
  • partially done but unpublished

Measurements: Bode plot

Bode plots of Stabilizer's transfer function

Default setup:

  • Network Analyzer: Omicron Bode 100
  • Stabilizer HW v1.1
  • Stabilizer Ch0
  • ADC0 50 Ohm term.

index

expose derivative control

The current biquad IIR filter can represent a derivative part. It's just not exposed in the PID representation.

  • Derive the equations
  • Check whether adding a D part to the math has an impact on dynamic range
  • Expose, implement, map to network interface

Pounder attenuators are interfaced directly to Stabilizer SPI

The Pounder attenuators are powered by 5V rails and are directly connected to the STM32 SPI interface, which is powered by 3.3V rails. The voltage levels appear to work properly and communication completes successfully, but we should investigate using level shifting here.

Scan mode

It would be good to be able to repeatedly scan an output and then observe the response on the corresponding input (eg. a cavity resonance or an atomic feature) in real time on a PC. This sounds fairly trivial to implement on the Stabilizer side, however a good interface to the PC may need some thought and I mention it now as it's relevant to #54.

integrator hold

During process(), if a digital input is high, do not update the IIRs.

  • Strictly speaking the correct implementation is to update the y (output) delay line with the unchanged previous output, but keep updating the x delay line with new input. This seems to be semantically best implemented by updating with a "unity-gain-feedback-iir" instead of the usual "active" one. As a first approximation to this, just not calling iir.update(..) is fine.
  • Would be nice to have the pin functionality configurable or at least not conflicting. I.e. not have code conflict between the use of DIN0 as ref input for timestamping and the use as a GPIO (to be read explicitly). I.e. be able to use either functionality for DIN0 in different binaries. Conveniently, the DI0/1 signals each are available on two pins. This is easy then.
  • This integrator hold flag should be and-ed with a flag integrator_hold_chA_dinB_en and or-ed with a flag integrator_hold_chA from the network-accessible settings.

Note: DINs should have pulldowns. sinara-hw/Stabilizer#91

Ethernet starts breaking after no response to inbound packets on the port

Prior to the emergence of this Ethernet issue, I was able to correctly flash various tagged versions of the firmware to the board. For those cases, Pinging is reliably successful and proper responses can be received from the board. For example, considering that stabilizer.py hasn't been updated since v0.3.0, using this frontend would get an ok on v0.3.0, or a parse error on v0.4.0 / 0.4.1.

However, some time after numerous reflashing the board, the Ethernet begins to break.

  1. Sometimes, if RJ45 has been plugged in before powering the board (with 12V), Pinging would fail.
  2. After Pinging has been guaranteed to work properly, when I use stabilizer.py to send something on the TCP port (1235), the script would hang at trying to poll for the response.
  3. Afterwards, if I try Pinging again, the board would stop replying to my computer. Edit: Pinging would fail often but not always after using stabilizer.py; but this could sometimes be reproduced by killing stabilizer.py and re-running it.

Below is the tshark dump while these issues happen (I hardcoded the board IP as 192.168.1.79, and following lines with ### are my annotations):

$ tshark -f "host 192.168.1.79"
Capturing on 'eno1'
### Start Pinging the board that has been powered, I get replies.
    1 0.000000000 Micro-St_26:b8:26 → Broadcast    ARP 42 Who has 192.168.1.79? Tell 192.168.1.116
    2 0.000176686 Microchi_d2:89:a1 → Micro-St_26:b8:26 ARP 60 192.168.1.79 is at 04:91:62:d2:89:a1
    3 0.000180615 192.168.1.116 → 192.168.1.79 ICMP 98 Echo (ping) request  id=0x0024, seq=1/256, ttl=64
    4 0.000420384 192.168.1.79 → 192.168.1.116 ICMP 98 Echo (ping) reply    id=0x0024, seq=1/256, ttl=64 (request in 3)
    5 1.047154568 192.168.1.116 → 192.168.1.79 ICMP 98 Echo (ping) request  id=0x0024, seq=2/512, ttl=64
    6 1.047364219 192.168.1.79 → 192.168.1.116 ICMP 98 Echo (ping) reply    id=0x0024, seq=2/512, ttl=64 (request in 5)
    7 2.071126156 192.168.1.116 → 192.168.1.79 ICMP 98 Echo (ping) request  id=0x0024, seq=3/768, ttl=64
    8 2.071347588 192.168.1.79 → 192.168.1.116 ICMP 98 Echo (ping) reply    id=0x0024, seq=3/768, ttl=64 (request in 7)
    9 3.095137617 192.168.1.116 → 192.168.1.79 ICMP 98 Echo (ping) request  id=0x0024, seq=4/1024, ttl=64
   10 3.095361057 192.168.1.79 → 192.168.1.116 ICMP 98 Echo (ping) reply    id=0x0024, seq=4/1024, ttl=64 (request in 9)
### Start using `stabilizer.py`, e.g. `python -m stabilizer -c 0 -p 1.0`.
   11 6.562644070 192.168.1.116 → 192.168.1.79 TCP 74 59600 → 1235 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=335328363 TSecr=0 WS=128
### There seem to be intermittent outgoing packets from the board...
### However, `stabilizer.py` keeps polling for responses while there has been none.
   12 6.562989099 192.168.1.79 → 192.168.1.116 TCP 66 1235 → 59600 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 WS=1 SACK_PERM=1
   13 6.563003995 192.168.1.116 → 192.168.1.79 TCP 54 59600 → 1235 [ACK] Seq=1 Ack=1 Win=64256 Len=0
   14 6.563287760 192.168.1.116 → 192.168.1.79 TCP 151 59600 → 1235 [PSH, ACK] Seq=1 Ack=1 Win=64256 Len=97
### TCP retransmissions from my computer happen.
   15 6.767079080 192.168.1.116 → 192.168.1.79 TCP 151 [TCP Retransmission] 59600 → 1235 [PSH, ACK] Seq=1 Ack=1 Win=64256 Len=97
   16 6.975075893 192.168.1.116 → 192.168.1.79 TCP 151 [TCP Retransmission] 59600 → 1235 [PSH, ACK] Seq=1 Ack=1 Win=64256 Len=97
   17 7.383108497 192.168.1.116 → 192.168.1.79 TCP 151 [TCP Retransmission] 59600 → 1235 [PSH, ACK] Seq=1 Ack=1 Win=64256 Len=97
   18 8.215082051 192.168.1.116 → 192.168.1.79 TCP 151 [TCP Retransmission] 59600 → 1235 [PSH, ACK] Seq=1 Ack=1 Win=64256 Len=97
   19 9.879078601 192.168.1.116 → 192.168.1.79 TCP 151 [TCP Retransmission] 59600 → 1235 [PSH, ACK] Seq=1 Ack=1 Win=64256 Len=97
   20 12.066799297 192.168.1.116 → 192.168.1.79 TCP 54 59600 → 1235 [FIN, ACK] Seq=98 Ack=1 Win=64256 Len=0
   21 13.010963441 192.168.1.116 → 192.168.1.79 TCP 74 59602 → 1235 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=335334812 TSecr=0 WS=128
   22 13.011233708 192.168.1.79 → 192.168.1.116 TCP 60 1235 → 59602 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0
   23 13.143117939 192.168.1.116 → 192.168.1.79 TCP 151 [TCP Retransmission] 59600 → 1235 [FIN, PSH, ACK] Seq=1 Ack=1 Win=64256 Len=97
   24 13.143390122 192.168.1.79 → 192.168.1.116 TCP 60 1235 → 59600 [ACK] Seq=1 Ack=99 Win=5840 Len=0
   25 13.143444053 192.168.1.79 → 192.168.1.116 TCP 60 1235 → 59600 [FIN, ACK] Seq=1 Ack=99 Win=5840 Len=0
   26 13.143468326 192.168.1.116 → 192.168.1.79 TCP 54 59600 → 1235 [ACK] Seq=99 Ack=2 Win=64256 Len=0
### Halting `stabilizer.py`. Now try to Ping again, but I get no more replies.
   27 15.512267842 192.168.1.116 → 192.168.1.79 ICMP 98 Echo (ping) request  id=0x0025, seq=1/256, ttl=64
   28 16.536139917 192.168.1.116 → 192.168.1.79 ICMP 98 Echo (ping) request  id=0x0025, seq=2/512, ttl=64
   29 17.559131304 192.168.1.116 → 192.168.1.79 ICMP 98 Echo (ping) request  id=0x0025, seq=3/768, ttl=64
   30 18.583151300 192.168.1.116 → 192.168.1.79 ICMP 98 Echo (ping) request  id=0x0025, seq=4/1024, ttl=64
   31 19.607132085 192.168.1.116 → 192.168.1.79 ICMP 98 Echo (ping) request  id=0x0025, seq=5/1280, ttl=64
   32 20.631157921 192.168.1.116 → 192.168.1.79 ICMP 98 Echo (ping) request  id=0x0025, seq=6/1536, ttl=64
   33 21.655130647 192.168.1.116 → 192.168.1.79 ICMP 98 Echo (ping) request  id=0x0025, seq=7/1792, ttl=64
   34 22.679144554 192.168.1.116 → 192.168.1.79 ICMP 98 Echo (ping) request  id=0x0025, seq=8/2048, ttl=64

[v1.1 hw] spikes on specific voltage levels

Using the latest version of branch feature/stabilizer/v1.1 without any changes I saw some spikes on the output signal. They occur at specific voltage levels. A sine of ~1 Vpp @ ~1 kHz was on ADC0 input.
scope_5
scope_4
scope_3

Eth PHY reset time might not be long enough

When powering the board with 12V, the Ethernet PHY LAN8742A won't have enough time to reset sometimes. If the reset is done and long enough, the two LEDs on the RJ45 plug should blink briefly upon power-cycling the board - this can't be observed when the PHY hasn't been reset properly.

A solution is to lengthen the deassertion of the nRST signal to 25ms by replacing this line in src/main.rs. According to the PHY's datasheet, this should be sufficient for the duration of "external power supplies at operational level to nRST deassertion".

Read MAC address from EEPROM

The MAC address should be read from the EEPROM, which has built in EUI48.

I need this and am happy to implement it.
My initial plan would be to print the MAC address found on startup via semihosting - this requires the user have an STLink. Another option would be to dump the MAC address over the debug UART, which comes out to a header.

@jordens any preferences on how to get the MAC address out?

Testing

Some preliminary characterization results of firmware and hardware

image
image

Ethernet sporadically does not connect

When working with Stabilizer, I've noticed that the ethernet connection appears to be inconsistent.

When the ethernet cable is plugged in to the RJ45 jack, the following conditions may occur:

  • ethernet connects and both LEDs illuminate consistently (ethernet then works consistently afterwards)
  • Only the orange LED illuminates (and no network traffic occurs)
  • Both LEDs will blink 4-5 times with a ~1.5s delay between blinks, then drop to one of the other states
  • No LEDs will illuminate

@jordens reports similar behavior as well - it is unknown if his experience was with the same board as I am currently using.

It is unknown if this is a hardware or firmware issue at the moment.

Stabilizer board serial number: TS 52/20 0004

Invalid DFU suffix signature

Hi,

I am trying to use the stabilizer by following the "Using USB-DFU" documentation. I followed each step in the instructions, all except the last step succeeded. The final step, dfu-util -a 0 -s 0x08000000:leave -D stabilizer.bin, returned the following error

dfu-util 0.9

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2016 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

dfu-util: Invalid DFU suffix signature
dfu-util: A valid DFU suffix will be required in a future dfu-util release!!!
Opening DFU capable USB device...
ID 0483:df11
Run-time device DFU version 011a
Claiming USB DFU Interface...
Setting Alternate Setting #0 ...
Determining device status: state = dfuIDLE, status = 0
dfuIDLE, continuing
DFU mode device DFU version 011a
Device returned transfer size 1024
DfuSe interface name: "Internal Flash   "
Downloading to address = 0x08000000, size = 120016
Download	[=========================] 100%       120016 bytes
Download done.
File downloaded successfully
dfu-util: Error during download get_status

I have tried power cycling the USB connector and the main power supply but it still returns the same error.

ADC SPI support

  • SPI core setup, timing limitations, spi core configuration
  • Handle cnv as nss with mssi delay
  • continuous sampling or DMA
  • Trigger via timer and irq or dma

super heterodyne

With Pounder, or any other phase-like signal (PDH, fiber length, phase lock) for that matter, it would be adequate and beneficial to not convert to DC before the ADCs but to a high IF (higher than the loop bandwidth) and then perform the rest of the processing in the CPU. As the title says, this is nothing but a version of the > 100 year old super heterodyne combined with some modern SDR.

This gives:

  • free phase gain calibration
  • free amplitude monitoring and potentially power stabilization
  • free rejection of LO harmonic aliases (even when not caught by the analog LP or when introduced after it)
  • option for easy and proper AM crosstalk suppression
  • no 1/f noise and low-f interference
  • quadrature sensing
  • operation at some power independent phase offset
  • proper and infinite phase wrap tracking
  • thus arbitrary capture range into the right fringe (nice for "integrator hold" use cases)

Needs:

  • check dynamic range (required for quadrature sampling) vs noise (AFE gain can be high when operating at zero and DC): looks fine for all relevant locks at PTB at least
  • check aliasing constraints (w.r.t sampling rate and analog bandwidth)
  • check useful IF: in the range of loop bandwidth up to the analog bandwidth minus loop bandwidth, don't get the SMPS spurs into the bandwidth
  • check DDS ref clock/LO vs ADC sample clock synchronization and requirements for ADC sample phase tracking (to e.g. DDS SYNC), use sync with timer capture to tag adc samples, fix sinara-hw/Pounder#76

Context:

Command interface

As we add more features to the firmware, we will need quite a few more commands, e.g. to commit settings to flash storage (#29), to configure high-bandwidth streaming, etc.

We could add more and more TCP ports that just directly parse lines into a given serde'd JSON type, but that seems like it would quickly become unwieldy. Another simple option would be to stick with a JSON object per request, but have a set command type/payload structure.

Any set ideas/preferences?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.