Developing UxHw® Applications
The Signaloid SoC in the C0-microSD can compute directly on probability distributions using Signaloid's UxHw technology. Your application performs ordinary arithmetic on distributional values, and the hardware propagates the distributions from inputs to outputs in a single execution (see UxHw in Silicon). This guide shows you how to develop such an application, starting from the official Signaloid-Compute-Module-Application-Template repository, a minimal, working application pair (a C device application and a Python host application) that you extend with your own computation.
Compiling UxHw applications does not require a local cross-compilation toolchain. The template uses the Signaloid CLI to build your C application on the Signaloid Cloud Compute Engine, targeting the C0-microSD, and downloads the resulting binary for you to flash.
Prerequisites
- A C0-microSD and its device path on your host; see Identify Your Module.
- A Signaloid account on the Signaloid Cloud Developer Platform (SCDP) and an API key.
- A GitHub account, so that you can fork the template repository and push your changes, connected to your Signaloid account as shown in GitHub Login.
- The Signaloid CLI, installed and authenticated as shown in its installation and authentication documentation.
- Python 3.10 or later for the host application and the flashing toolkit. See Python Environment.
- The
jqcommand-line JSON processor, which the templateMakefileuses to parse Signaloid CLI responses. - Root privileges (
sudo) for raw block-device access.
Fork and clone the template
The Signaloid Cloud Compute Engine builds your application from your repository on GitHub, so any modification you make must live in a repository you can push to. Start by forking the Signaloid-Compute-Module-Application-Template repository to your own GitHub account. Make sure you have connected your GitHub account to your Signaloid account (see GitHub Login), so that the Signaloid Cloud Compute Engine can access your fork.
Then clone your fork recursively, so that you also get the
Signaloid-Compute-Module-Utilities submodule (the flashing toolkit
and the hardware abstraction layer the template builds on). Replace <your-username> with
your GitHub username.
git clone --recursive https://github.com/<your-username>/Signaloid-Compute-Module-Application-Template.git
The repository contains one application in two halves, plus the build machinery.
| Path | Contents |
|---|---|
signaloid-soc-application/main.c | The device application that runs on the Signaloid SoC and performs the UxHw computation. |
signaloid-soc-application/config.mk | The build configuration, which lists your source files, include paths, and build flags. |
python-host-application/ | The host application that sends inputs to the module and reads back results. |
Makefile | Drives the whole flow, building through the Signaloid CLI, downloading the binary, and flashing it. |
The template application
The template implements one example command, CalculateAddition, and it is the easiest
entry point for understanding the whole flow. The host application sends four floats, the
bounds of two uniform distributions X and Y. The device application constructs the two
distributions, adds them, and returns the mean and variance of the resulting distribution
for the host to print.
The core of main.c is a loop that waits for a command from the host, dispatches on it, and
reports status, following the polled handshake documented in
Host Interface and Protocol.
The kCalculateAddition case is where the UxHw computation happens.
case kCalculateAddition:
/*
* Create distributional values from inputs
*/
inputDistributionA = UxHwFloatUniformDist(kC0HALInputBufferFloat[0], kC0HALInputBufferFloat[1]);
inputDistributionB = UxHwFloatUniformDist(kC0HALInputBufferFloat[2], kC0HALInputBufferFloat[3]);
/*
* Calculate
*/
result = inputDistributionA + inputDistributionB;
/*
* Compute the mean (first moment) and variance
* (second centralised moment) of the result.
*/
mean = UxHwFloatNthMoment(result, 1);
variance = UxHwFloatNthMoment(result, 2);
/*
* Return the mean and variance as two floats by writing them
* to the output buffer.
*/
kC0HALOutputBufferFloat[0] = mean;
kC0HALOutputBufferFloat[1] = variance;
Three details are worth noting.
- UxHw values are ordinary C floats.
UxHwFloatUniformDist()returns afloatthat carries a distribution, andinputDistributionA + inputDistributionBis plain C addition, and the hardware carries the distributions through it. You query the result with functions such asUxHwFloatNthMoment(). The full API surface is in the UxHw API documentation. - The hardware abstraction layer hides the MMIO details. The
C0HAL*functions and thekC0HALInputBufferFloat/kC0HALOutputBufferFloatbuffers map onto thecommandandstatusregisters and theMOSIandMISObuffers of the MMIO interfacing model. - Commands are a shared contract. The
SignaloidSoCCommandenum inmain.cand theCommandsenum inpython-host-application/host_application.pymust stay in sync; each new computation you add is a new entry in both.
Build with the Signaloid CLI
The Signaloid CLI is the command-line interface to the Signaloid Cloud Compute Engine. It
connects a GitHub repository to your Signaloid account, starts builds of that repository on
a Signaloid core, and downloads the build products (see the
Signaloid CLI introduction). Building on the Signaloid
Cloud Compute Engine is what gives your application its UxHw capabilities, because the
platform provides the compiler and the uxhw.h API, matched to the C0 core you build for.
You do not need to invoke the CLI by hand, because the template's Makefile drives it.
First, configure three variables at the top of the Makefile.
- Set
DEVICEto the device path of your C0-microSD, for example/dev/disk4. - Set
DEVICE_TYPEtoSIGNALOID_C0_MICROSD. - Set
CORE_IDto the C0 core to build for. The core determines the distributional representation precision and whether the build tracks correlations, and theMakefilelists the core IDs of the available C0-microSD core variants. See the execution cores guide for how cores differ.
Then start the build.
make
On the first run, the Makefile uses the CLI to look up (or connect) the repository and
branch on the Signaloid Cloud Compute Engine, pointing it at the
signaloid-soc-application/ directory and your selected core. It then starts a build,
streams the build status until completion, and downloads the resulting binary as
signaloid-soc-application/<build-id>.main.bin. The build inputs (source files, include
paths, and build flags) are defined in signaloid-soc-application/config.mk.
The Signaloid Cloud Compute Engine builds the repository as it exists on GitHub, so commit
and push your changes to your fork before running make.
Flash and run
-
Flash the downloaded binary into the SPI flash user data region of the module.
make flashThe
Makefilechecks that the binary fits in the 128 KiB the Signaloid SoC loads at reset, and flashes it with theC0_microSD_toolkit.pyscript from the Signaloid-Compute-Module-Utilities submodule. Flashing happens in Bootloader mode, and the toolkit handles the mode switching for you. If the module is in Signaloid SoC mode, the toolkit first switches the boot mode and asks you to power cycle the device (eject and re-insert it). After writing and verifying the binary, the toolkit switches the module back to Signaloid SoC mode and asks for one more power cycle. The module has finished flashing when the green LED lights solid. For the mode model itself, see Switch Between Operation Modes. -
Set up and run the Python host application from the repository root.
python3 -m venv .venvsource .venv/bin/activatepip install -r python-host-application/requirements.txtcd python-host-applicationsudo ../.venv/bin/python3 host_application.py --device-path /dev/disk4 --variant C0-microSD CalculateAddition 5.0 6.0 4.0 7.5Replace
/dev/disk4with your device path. The command adds a uniform distribution over [5.0, 6.0] to a uniform distribution over [4.0, 7.5] on the module, and the host prints the mean and variance of the result. You invoke the interpreter of the virtual environment directly, because a plainsudo python3may resolve to a different interpreter that does not have the packages you installed in the virtual environment.
Make it your own
Use the following workflow to turn the template into your own application. The modifications in these steps must reach GitHub for the Signaloid Cloud Compute Engine to build them, so make them in your fork of the template.
- Develop and test your computation in the Code Playground of the Signaloid Cloud Developer Platform, where you can iterate on UxHw code without flashing hardware.
- Copy the computation into
signaloid-soc-application/main.cas a new command. Add an entry to theSignaloidSoCCommandenum and a matchingcasethat reads inputs from the input buffer and writes results to the output buffer. - Mirror the new command in the host application. Extend the
Commandsenum and the argument parsing inpython-host-application/host_application.py, and adapt the input packing and output parsing helpers inpython-host-application/app_helpers.py. - If you split your code into more
.cfiles, add them to theSOURCESvariable insignaloid-soc-application/config.mk. - Commit and push your changes to your fork, then build, flash, and run as shown above.
For complete, working applications built this way, see Examples and Demos.
Next steps
- Host Interface and Protocol, the register and buffer contract your host application writes against.
- Flash the C0-microSD, the full toolkit reference behind the flashing step above.
- Developing RISC-V Applications, the alternative path for applications that do not use UxHw arithmetic.
- Analyze
Uxoutput data on the host with signaloid-python and the Ux Data Tools.