Linux C++ drivers
CFG=.../config.mk, memory.yml, Linux FPGA Manager and device-tree overlays. It is not backward-compatible with 0.x instruments.
Goal
Write the server-side API for an instrument. A C++ driver is compiled into the instrument server. Its public member functions become commands that can be called from Python or TypeScript clients.
A driver usually does three things:
- Access generated memory regions from
memory.hpp. - Read and write FPGA registers.
- Expose a clean command API to clients.
Add a driver to an instrument
List driver files in config.mk:
DRIVERS += $(PROJECT_PATH)/fft.hpp
DRIVERS += $(PROJECT_PATH)/fft.cpp
Board-level drivers can be included from a board make fragment:
include $(SDK_PATH)/boards/alpha250/drivers/drivers.mk
Build only the server while developing C++ code:
make -j CFG=examples/alpha250/fft/config.mk server
Include runtime and memory headers
A driver can include the runtime driver manager and generated memory access headers:
#include "server/runtime/driver_manager.hpp"
#include "server/hardware/memory_manager.hpp"
Board drivers may be included as needed:
#include "boards/alpha250/drivers/clock-generator.hpp"
#include "boards/alpha250/drivers/power-monitor.hpp"
Minimal driver skeleton
#include "server/runtime/driver_manager.hpp"
#include "server/hardware/memory_manager.hpp"
#include <cstdint>
class MyInstrument
{
public:
MyInstrument()
: ctl(hw::get_memory<mem::control>())
, sts(hw::get_memory<mem::status>())
{}
void set_output(uint32_t value) {
ctl.write<reg::digital_outputs>(value);
}
uint32_t get_input() {
return sts.read<reg::digital_inputs>();
}
private:
hw::Memory<mem::control>& ctl;
hw::Memory<mem::status>& sts;
};
The exact mem::... and reg::... identifiers come from memory.yml.
Memory access flow
A memory region declared in memory.yml:
memory:
- name: control
offset: 0x4000_0000
range: 4K
registers:
- digital_outputs
generates C++ identifiers such as mem::control and reg::digital_outputs.
Use the generated hw::Memory object to read and write registers.
Reuse another driver
Drivers can access other drivers through the runtime driver manager:
auto& power = rt::get_driver<PowerMonitor>();
auto supplies = power.get_supplies_ui();
This lets instrument drivers reuse board drivers for clocks, ADCs, DACs, power monitors and sensors.
Command API design
Public driver methods are exported in generated driver metadata. During the server build, the SDK generates C++ dispatch headers under tmp/<project>/server/, builds a metadata dumper, and writes tmp/<project>/server/drivers.json; the instrument ZIP packages that file as drivers.json.
Clients use this metadata to map class and function names to command IDs.
Examples of exported methods:
void set_input_channel(uint32_t channel);
uint32_t get_fft_size() const;
std::array<int32_t, prm::n_adc> get_adc_raw_data(uint32_t n_avg);
auto get_control_parameters();
Use fixed-width integer types such as uint32_t where possible to avoid ambiguity in the client protocol.
Return types
Clients support common scalar types such as integers, floating-point values and booleans. They also support arrays, vectors, tuples and strings when the C++ return type is exposed in the generated command metadata.
Full archive build
Build the complete instrument archive with:
make -j CFG=examples/alpha250/fft/config.mk

