Using the C0_SD_toolkit
The C0_SD_toolkit.py script from the
Signaloid-Compute-Module-Utilities repository is how you drive a
C0-microSD+ from a host computer. It flashes a compiled device application into the
SPI flash of the module, starts and stops the RISC-V core of the Signaloid SoC, updates
the FPGA bitstream, and reports what the module and the device application are doing.
The toolkit works over ordinary SD block reads and writes, so you need neither a driver nor external programming hardware. Everything on this page happens while the module stays in its slot.
Before you begin
- Install Python 3.10 or later. The
C0_SD_toolkit.pyscript uses nothing outside the Python standard library. See Python Environment. - Get the toolkit, which ships in the Signaloid-Compute-Module-Utilities repository. Cloning the Signaloid-Compute-Module-Application-Template repository recursively also brings the toolkit in as a submodule.
- Insert the C0-microSD+ into a microSD slot or a card reader. It enumerates as an unformatted block-storage device of 268.4 MB on macOS and 256 MiB on Linux and Windows. If your operating system offers to format it, do not format the device and dismiss the prompt.
- Identify the device path of the module. On macOS the path looks like
/dev/disk4, and on Linux it looks like/dev/sdb. See Identify your module for the exact commands. The examples on this page assume/dev/disk4, so substitute your own path. - Run every command with
sudo, because raw block-device access requires root privileges.
The toolkit takes the device path as a positional argument before the subcommand, and
config is an alias for the configure subcommand.
Command reference
Running C0_SD_toolkit.py --help prints every subcommand, together with the configuration
actions of every supported variant. Pass --variant to narrow that action list to a single
module. The listing below is the output for the C0-microSD+.
usage: C0_SD_toolkit.py [-h] [--variant {C0-microSD+,C0-SD}]
[--regmap-path REGMAP_PATH]
target_device <command> ...
Signaloid C0-SD toolkit. Version 2.4
positional arguments:
target_device Target device path
<command>
info Print target device info and bitstream metadata.
status Print verbose status (COMMAND, CONFIG, STATUS, and
SD_CONFIG on C0-SD).
flash-application Flash an application binary
flash-bitstream Flash a bitstream file
configure (config) Apply a configuration action (per-variant; see the
action list in the main --help).
options:
-h, --help show this help message and exit
--variant {C0-microSD+,C0-SD}
Hardware variant. Default: auto-detect from the
device's bitstream; required if it cannot be
identified.
--regmap-path REGMAP_PATH
Path to the regmap package directory for the selected
--variant (defaults to the built-in regmaps).
commands:
info
Decode and print bitstream metadata, and verify the bitstream CRC.
status
Print the COMMAND, CONFIG and STATUS registers (plus SD_CONFIG where
present).
flash-application <app_path> [-p SIZE]
Flash an application binary to the device's user-data flash region
(optionally zero-padded to SIZE).
flash-bitstream <bs_path> [-p SIZE]
Stop the SoC core, unlock the bitstream region, flash a bitstream,
then re-lock it (optionally zero-padded to SIZE).
configure / config <action>
Apply a single configuration action (see 'configure actions' below).
configure actions (use as: configure <action>):
C0-microSD+:
blue-led-off
blue-led-on
core-start
core-stop
debug-pin-0-off
debug-pin-0-on
debug-pin-1-off
debug-pin-1-on
debug-pin-2-off
debug-pin-2-on
green-led-off
green-led-on
lock-bitstream
red-led-off
red-led-on
sw-led-off
sw-led-on
unlock-bitstream (prompts for confirmation)
write-crc-force-ok-disable
write-crc-force-ok-enable
write-crc-force-write-disable
write-crc-force-write-enable
write-crc-irq-clear
write-crc-irq-connect
write-crc-irq-disconnect
The --variant option is optional. The toolkit decodes the JSON metadata prefix embedded in
the on-device bitstream and reads its compute_module_type field to work out which module
it is talking to. Pass --variant=C0-microSD+ to force the choice, which is useful when the
metadata cannot be read.
Flash an application
Flash an application in two steps. Stop the core first, so that the application currently on the module cannot access the SPI flash while the toolkit writes to it.
-
Stop the core.
sudo python3 C0_SD_toolkit.py /dev/disk4 config core-stop -
Flash the binary. Pass the binary to the
flash-applicationsubcommand.sudo python3 C0_SD_toolkit.py /dev/disk4 flash-application program.binThe toolkit writes the binary into the SPI flash user data region, reads the same range back, and compares the two. It makes up to five attempts and reports success once the readback matches. Add
-p SIZEto zero-pad the input file to a target size before writing.
MicroSD-to-SD card adapters usually carry a small write-protect switch on one side. When that switch sits in the locked position, the host refuses to write to the module and the toolkit fails with a permission error. Slide the switch to the unlocked position and run the command again.
Where the binary is written
The flash-application subcommand writes to the SPI flash user data region, which
starts at address 0x00200000 and runs to the top of the 16 MiB flash.
| Region | Base address | Size | Contents |
|---|---|---|---|
| SPI flash, bitstream region | 0x00000000 | 2 MiB | Signaloid-supplied FPGA bitstream |
| SPI flash, user data region | 0x00200000 | 14 MiB | Your application binary and its data |
Addresses are the same for the host and for the Signaloid SoC, so the region your host writes over the SD interface is the region the core reads. For the whole map, see the unified address map.
The bitstream region is always readable over the SD interface, and only writes into it are
gated: a write is accepted only while the BITSTREAM_UNLOCK register holds the unlock key.
Flashing an application writes to the user data region, so it cannot damage the bitstream. See
The bitstream region lock.
Start and stop the core
The core is held in reset by bit 0 of the CONFIG register at 0x08204000. That bit is
named rstn and is active low, so writing 1 releases the core from reset and lets it
run, and writing 0 holds the core in reset. Starting, stopping, and resetting the core all
come down to that one bit.
Start the core
sudo python3 C0_SD_toolkit.py /dev/disk4 config core-start
This sets bit 0 of CONFIG to 1 and releases the core, which begins executing the
application you flashed into the user data region of the SPI flash at 0x00200000. The core
keeps running until you stop it, until the application halts itself, or until the module
loses power.
Starting a core that is already running is harmless. The toolkit reads CONFIG, sets bit 0,
and writes the whole register back, so a second core-start leaves a running core running.
Because the toolkit always performs a read, a modify, and a write, the LED, debug pin, and
and debug pin bits of CONFIG keep their current values. If you drive CONFIG directly
from your own host application rather than through the toolkit, read the register first and
preserve the other bits. Writing a bare bit mask clears rstn and stops the core.
Stop the core
sudo python3 C0_SD_toolkit.py /dev/disk4 config core-stop
This clears bit 0 of CONFIG and holds the core in reset. The FPGA bitstream keeps running,
so the module stays enumerated as a block-storage device and every register and buffer stays
readable and writable from the host while the core is stopped. Only the RISC-V core stops.
A device application can also halt itself from inside the SoC by calling C0HALStopCore(),
which suits a one-shot workload that should not keep spinning after it has produced its
result. Restarting such an application from the host is a core-start away.
Stop the core in the following situations.
- Before you flash, if you flash from your own host code. Flashing writes into the same
SPI flash that the core boots from, so a core left running during a flash operation can end
up executing a partially written binary.
flash-applicationandflash-bitstreamstop the core for you, so you only need to do this yourself when you drive the flash directly. - Before you unlock the bitstream region. The hardware forces the unlock key to
0x00000000while the core runs, so an unlock only takes effect once the core is stopped. - When a workload is complete and you want the module idle in its slot rather than spinning in the polling loop of your application.
- When a device application has stopped responding and you want to inspect its registers without the core continuing to write to them.
Reset the core
There is no separate reset action. A reset is a stop followed by a start.
sudo python3 C0_SD_toolkit.py /dev/disk4 config core-stop
sudo python3 C0_SD_toolkit.py /dev/disk4 config core-start
Leave about one second between the two commands so that the core settles in reset before you
release it. The reset_core() helper in the signaloid-utilities Python package does
exactly this, stopping the core, waiting one second, starting the core, and waiting one
second more.
A reset restarts your device application from its entry point. It does not re-flash the application and it does not touch the FPGA bitstream, so the core comes back running the same binary it was running before.
Check the state of the core
The status subcommand reports the registers that describe what the core and the device
application are doing.
% sudo python3 C0_SD_toolkit.py /dev/disk4 status
Detected compute module: C0-microSD+
COMMAND: 0x00000000
CONFIG: 0x00000001
STATUS: 0x00000000
SD_CONFIG: 0x00000001
Read the report as follows.
| Register | Example value | How to read it |
|---|---|---|
COMMAND | 0x00000000 | The application-defined operation code the host wrote most recently. In the example no command is pending. |
CONFIG | 0x00000001 | Bit 0 (rstn) is set, so the core is running. A value with bit 0 clear, for example 0x00000000, means the core is held in reset. |
STATUS | 0x00000000 | The application-defined status the device application wrote most recently. 0x00000000 is the conventional waiting for command value. |
SD_CONFIG | 0x00000001 | The SD block-write CRC handling configuration. Leave the default in place unless you are bringing up an SD host controller you are debugging. |
The example above shows a module whose core is already running, so the CONFIG value is
not the power-up default. A module that has just powered up reports CONFIG as
0x00000000, with the core held in reset. For the reset
value of every register, see
MMIO Memory Map and Registers.
The fastest check of whether the core is running is bit 0 of CONFIG. A CONFIG value
whose low bit is 1 means the core has been released, and a value whose low bit is 0 means
the core is held in reset. For the meaning of every other bit, see
Host Interface and Protocol.
Print the module information and the decoded bitstream metadata with the info subcommand.
sudo python3 C0_SD_toolkit.py /dev/disk4 info
LED behavior
The config subcommand also exposes actions that drive the on-board LEDs directly, namely
sw-led-on, sw-led-off, red-led-on, red-led-off, green-led-on, green-led-off,
blue-led-on, and blue-led-off. From inside a device application, C0HALSetLed() drives
the green LED.
Next steps
- Developing UxHw Applications, building the binary that this page flashes.
- Host Interface and Protocol, the full register and buffer reference behind the values reported above.
- Operational Model, where flashing and running sit in the coprocessor lifecycle.
- Troubleshooting and FAQ, if a flash attempt fails or a device application does not respond.