The Baochip is a new MCU using RISC-V which has a BIO feature. This feature contains 4 PicoRV32E processors that can run in parallel with access to memory and GPIO.
It also features 16 special registers (x16-x31) compared to the RV32E specification, which contains only 16 (x0-x15); it also supports the Zmmul extension (integer multiplication).
A brief introduction of the board is here
The official documentation is here and I will use an image from the documentation:

For the SDK, I will use the Dabao SDK, and for compiling BIO I will use the xpack riscv-none-elf-gcc 15.2.0, which is what the SDK uses — though any RISC-V gcc should work.
General Description
The BIO consists of 4 PicoRV32E processors that can run in parallel up to 720 MHz (it takes 3 cycles for each instruction without pipelining, so the real speed is around 240 MHz).
Each processor has 4 kiB of RAM, meaning it can hold up to 1024 instructions. Each processor also has the Zmmul extension, so it has a hardware multiplier.
Each BIO Processor has special registers x16-x31 to communicate with other processors and the Host Processor; some of the registers will halt CPU execution until an external event happens.
The BIO Processor can have access to GPIO, so it allows "bit banging" at higher speeds and running in parallel without main CPU slowdown; it can also have access to host memory.
The special registers are (taken from the official documentation, with more info added):
FIFO - 8-deep fifo head/tail access. Cores halt on overflow/underflow.
Each FIFO can have up to 16 items of 32 bits
- x16 r/w fifo[0]
- x17 r/w fifo[1]
- x18 r/w fifo[2]
- x19 r/w fifo[3]
Quantum - core will halt until host-configured clock pulse occurs, or an external event comes in a GPIO pin.
- x20 -/w halt to quantum
GPIO
- x26 r/w mask GPIO action outputs
- x21 r/w write: (x26 & x21) -> gpio pins; read: gpio pins -> x21
- x22 -/w (x26 & x22) -> a `1` in x22 will set corresponding pin on gpio
- x23 -/w (x26 & ~x23) -> a `0` in x23 will clear corresponding pin on gpio
- x24 -/w (x26 & x24) -> a `1` in x24 will make corresponding gpio pin an output
- x25 -/w (x26 & x25) -> a `1` in x25 will make corresponding gpio pin an input
Events
Bits [31:24] are hard-wired to FIFO level flags, configured by the host; writes to bits [31:24] are ignored.
These bits are for comparators against FIFO levels (FIFO length), there are 2 possible comparators for each FIFO, so 8 comparators in total.
- x27 -/w event mask
- x28 -/w `1` will set the corresponding event bit. Only [23:0] are wired up.
- x29 -/w `1` will clear the corresponding event bit. Only [23:0] are wired up.
- x30 r/- halt until ((x27 & events) != 0), and return unmasked `events` value
Core ID & debug:
- x31 r/- [31:30] -> core ID; [29:0] -> cpu clocks since reset
BIO Setup
In order to run the BIO, it needs the following:
- set the clock settings (SDK:
bio_init(FCLK_HZ);) - flush core instructions/fifo and set up stack pointers; this is done in
bio_init(other example) - set the code to execute in binary opcodes (SDK:
bio_load_code_words(core, *buffer, length)) - start the code (SDK:
bio_start_cores(core_number_1_to_4))
So you need to compile instructions into RISC-V opcodes for each core; here is a script to compile ASM into bytecode for each core.
The script can be used like this:
GCC_PATH="YOUR_PATH_TO/xpack-riscv-none-elf-gcc-15.2.0-1" ./compile-bio.sh source.s
And will output a block like this:
static const uint32_t bio_program_length = 15;
static const uint32_t bio_program[] = {
0x010002b7,
...
};
You can copy and paste this code into the C implementation. This tool needs xpack (in theory, it can work with any RISC-V gcc compiler) and the hexdump command.
GPIO Handling
Since the GPIO is handled through 32-bit registers, there are up to 32 GPIO pins available to use; here is more info about the internal details.
The 32 pins are:
- bits [15:0] ->
PB[15:0] - bits [31:16] ->
PC[16:0]
In order to use GPIO, you need to:
- Set the pin you want to use to
AF1(Alternate Function 1) through the proper register. For example,AFSELBLcontains the config forPort B, Pin 0-7andAFSELBHforPort B, Pin 8-15Reference - Configure
BIOSELto set the proper bit Reference
So, for example, to use pin PB2, this needs to be done:
AFSELBL = 0x10; // 0b_00_00_00_00_00_01_00_00 or PortB,Pin2=AF1
BIOSEL = 0x4; // 1 << 2 to select Pin2
In the SDK: bio_map_pin(pin_number)
I will use this as an example:
https://github.com/ArmstrongSubero/dabao-sdk/blob/main/examples/bio_blink/bio_blink.c
The name is a bit confusing since the LED won't blink, but it will actually be a PWM using BIO instead of the main CPU.
So in order to generate a PWM, a pulse clock with a certain frequency is needed. For this, x20 will halt until a pulse happens, and this can be configured using the QDIV register for the processor being used, or in the SDK: bio_set_divider(core, divider_int, divider_frac)
Note that in the SDK they use the following example:
bio_set_divider(0, 1750, 0); /* 100 kHz output (fclk / 1750 / 4) */
I haven't confirmed if this is accurate, since FCLK = 700 MHz (or 700,000) and they use 4 cycles per instruction, but according to the official documentation it is 3 cycles; but maybe this is due to rounding.
So when setting the divider, the x20 or quantum pulse will be delivered every 1/100,000s.
So in ASM, if you do:
addi x20, x0, 0
# mv x20,x0
This instruction will halt until a quantum pulse happens.
So in order to "blink", the following needs to happen:
- setup gpio
- wait for quantum pulse
- turn ON gpio
- wait for quantum pulse
- turn OFF gpio
- jump to 2.
So the following code is in the example (I'm using pseudo instructions to make it easier to read):
li x5, 0x4 # bit mask for pin 2
mv x26, x5 # GPIO mask = pin 2, for this example this is optional
mv x24, x5 # x24 configures output pins, so pin 2 = output
loop:
mv x22, x5 # x22 is SET GPIO, so pin 2=HIGH
mv x20, x0 # wait quantum pulse
mv x23, x0 # x23 is CLEAR GPIO, so pin 2=LOW
mv x20, x0 # wait quantum pulse
j loop # loop
Top comments (0)