Microcontroller.py#
Serial interface for MCU-mediated FPGA measurement.
Provides the Microcontroller class that communicates with a
microcontroller over a serial link to program an ICE40 FPGA and
retrieve fitness-evaluation data (pulse counts or ADC waveforms).
Note
Serial protocol details were inferred from code and may need verification against MCU firmware.
- class Hardware.Microcontroller.Microcontroller(fpga: str, logger: Logger, config: MicrocontrollerConfig)[source]#
Driver for a serial-connected MCU that programs and reads from an FPGA.
Opens a persistent serial connection on construction and provides async methods to request waveform or pulse-count measurements.
Concurrency note:#
The async methods in this class use blocking pyserial calls and do NOT yield to the event loop. As a result, asyncio.gather() over multiple Microcontroller instances currently runs them sequentially, not in parallel.
TODO: Migrate serial I/O to serial_asyncio (https://pypi.org/project/serial-asyncio/) so that serial reads genuinely suspend and allow other coroutines to run. This is the server-side component — when the architecture moves to a server-client model, this class (or its replacement) will run on the server and use serial_asyncio.
Per-Icestick exclusivity:#
Each Microcontroller instance manages exactly one Icestick via one serial port. iceprog holds exclusive USB access to the device during compile(). Concurrent calls to request_measurement() on the SAME Microcontroller instance will corrupt each other. The server must serialize calls per-instance (e.g. one asyncio.Semaphore(1) per Microcontroller, or a dedicated worker coroutine per device). Calls on DIFFERENT Microcontroller instances (different Icesticks) can run concurrently. See .claude/docs/hardware_concurrency.md.
- __init__(fpga: str, logger: Logger, config: MicrocontrollerConfig)[source]#
Initialize the serial connection to the MCU.
- Parameters:
fpga (str) – FPGA device identifier passed to
icepack/iceprog.logger (Logger) – Logger instance for event and warning messages.
config (MicrocontrollerConfig) – Serial port settings.
- get_available_FPGAs() list[str][source]#
Return the list of FPGA device identifiers managed by this MCU.
- async measure_pulses(samples: int) list[int][source]#
Collect multiple pulse-count samples from the FPGA.
- async measure_pulses_once() list[int][source]#
Perform a single pulse-count read from the MCU.
Sends command
'1'and polls the serial line for a numeric response, retrying up to 5 times on timeout. Returns-1on timeout and-2on parse failure.
- async measure_signal() list[int][source]#
Capture an ADC waveform from the FPGA via the MCU.
Sends command
'2'to initiate ADC capture, then reads lines betweenSTARTandFINISHEDdelimiters.
- async request_measurement(measurement: Measurement) Measurement[source]#
Compile the circuit and perform the requested measurement.
Dispatches to
measure_signalormeasure_pulsesbased onmeasurement.data_request. The result (or error) is stored inmeasurement.resultas aSuccessorFailure.- Parameters:
measurement (Measurement) – Measurement descriptor specifying circuit and data type.
- Returns:
The same object with
resultpopulated.- Return type: