Skip to main content

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.py script 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.

  1. Stop the core.

    sudo python3 C0_SD_toolkit.py /dev/disk4 config core-stop
  2. Flash the binary. Pass the binary to the flash-application subcommand.

    sudo python3 C0_SD_toolkit.py /dev/disk4 flash-application program.bin

    The 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 SIZE to zero-pad the input file to a target size before writing.

warning

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.

RegionBase addressSizeContents
SPI flash, bitstream region0x000000002 MiBSignaloid-supplied FPGA bitstream
SPI flash, user data region0x0020000014 MiBYour 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-application and flash-bitstream stop 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 0x00000000 while 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.

RegisterExample valueHow to read it
COMMAND0x00000000The application-defined operation code the host wrote most recently. In the example no command is pending.
CONFIG0x00000001Bit 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.
STATUS0x00000000The application-defined status the device application wrote most recently. 0x00000000 is the conventional waiting for command value.
SD_CONFIG0x00000001The 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