FIFO
The BIO has 4 FIFOs mapped to the BIO processors as x16-x19 r/w registers (blocking on read until new data is pushed), and in the host through BIO_BDMA_SFR_TXFx for writing data (PUSH) and BIO_BDMA_SFR_RXFx for reading (POP) data (or returning the last data read if nothing else is found), where x is a number 0-3 for the 4 FIFOs.
So in a BIO processor, an instruction like this:
mv t0, x16 # wait for FIFO0
This will block until there is info in FIFO0.
To unblock it, you need, from another BIO processor:
li x16, 0x10
or from the host processor:
BIO_BDMA_SFR_TXF0 = 0x10; // bio_push_fifo0(0x10); in SDK
To read in the host processor:
uint32_t data = BIO_BDMA_SFR_RXF0; // bio_pop_fifo0(); in SDK
This will always return a value and won’t block execution, so for example:
BIO_BDMA_SFR_TXF0 = 0x10; // FIFO0 has 0x10
BIO_BDMA_SFR_TXF0 = 0x20; // FIFO0 has 0x10,0x20
uint32_t data;
data = BIO_BDMA_SFR_RXF0; // data = 0x10
data = BIO_BDMA_SFR_RXF0; // data = 0x20
data = BIO_BDMA_SFR_RXF0; // data = 0x20
So the last data read/popped will always be available to the host.
There is an example here: https://github.com/danguer/embedded-boards101/tree/main/daobao-baochip/bio_multiply_fifo
To test the hardware multiplier in the BIO Processor, the following instructions are needed for BIO:
mv t0, x16 # wait for FIFO0
li t1, 2 # load multiplier
mul x16, t0, t1 # multiply and store back in fifo
So the BIO will get a value from FIFO0, multiply by 2, and store it again in FIFO0.
Because we are reusing the same FIFO, if we read the value before the BIO does the multiplication, we are going to read the original value and not the result (I know this is poor design, as another FIFO should be used, but this is to show the communication with the host).
So the host will do the following:
- add the value to FIFO0
- read the value from FIFO0
- check that the value is different from the original value
bio_start_cores(1);
bio_push_fifo0(multiplicand);
// wait until is multiplied
do {
result = bio_pop_fifo0();
} while(result == multiplicand);
bio_stop_cores(BIO_CORE);
Even though this code works, the issue is that the FIFO read register will ALWAYS return a value.
To avoid this issue, there is another register called BIO_BDMA_SFR_FLEVEL.
This register will return all the FIFO “levels” (number of entries or length), with 4 bits for each FIFO (as there is a maximum of 16 entries per FIFO). So, to know how many items exist, it can be used like this:
uint8_t fifo0_length = BIO_BDMA_SFR_FLEVEL & 0xF; // or bio_fifo_level(0); in SDK
Still, for this example, the issue is that we need to use another FIFO to avoid collisions:
mv t0, x16 # wait for FIFO0
li t1, 2 # load multiplier
mul x17, t0, t1 # multiply and store in fifo1
bio_push_fifo0(multiplicand);
// wait until fifo1 has data
while(bio_fifo_level(1) == 0);
result = bio_pop_fifo1();
Note that there is a way to “reset” the FIFO levels/length through the register BIO_BDMA_SFR_FIFO_CLR.
This is a 4-bit config where each bit corresponds to a FIFO number, and a 1 means clearing the levels (but not the contents), so for example:
BIO_BDMA_SFR_FIFO_CLR = 0xF;
This resets all the FIFO levels/length.
Events
The events register is a shared 32-bit register, where bits [23:0] are standard bits and can be changed with:
-
x28to SET in the BIO Processors (orBIO_BDMA_SFR_EVENT_SETin host,bio_event_setin SDK) -
x29to CLEAR in the BIO Processors (orBIO_BDMA_SFR_EVENT_CLRin host,bio_event_clearin SDK)
In the BIO Processor there are two more registers:
-
x27is a mask to only halt on certain events -
x30is a Halt-on-Event register: when you try to read it, it will halt until(x27 & events) != 0or until an event has happened; reading this will return all the event data, regardless of the event mask
For the host, BIO_BDMA_SFR_EVENT_STATUS is similar to x30, so it is a READ ONLY register.
So, you can wait for a signal, for example:
BIO Processor
mv t0, x16 # wait for FIFO0
mul x17, t0, t1 # multiply and store back in FIFO1
li x28, 0x4 # signal that multiply has finished
j cycle
In the host, this code can be added:
bio_push_fifo0(multiplicand); // sdk
// wait until signal happens
while((BIO_SFR_EVENT_STATUS & 0x4) == 0);
With this, the host will be waiting for a signal from the event register.
Event Thresholds
Bits [31:24] are special bits; for each FIFO there will be 2 comparators, which will set those 8 bits if they match one of three possible options — < (less than), == (equal), > (greater than) — compared against the value of the level/length of the FIFO.
So you can configure logic testing like:
IF FIFO0_LEVEL == 4 OR FIFO_LEVEL > 4
If the comparator matches, it will set the proper event bit. So, let’s say the previous example was for Thresh0/Comparator0; then, when it matches, it will set 1 in the Event[24] bit, which can be used in the host or the BIO Processor.
The comparators need to be set in the host through two variables:
-
BIO_BDMA_SFR_ELEVELfor setting the value to compare -
BIO_BDMA_SFR_ETYPEfor setting the type of comparator
For the BIO_BDMA_SFR_ELEVEL, there are 8 comparators, and each one can have a value of 4 bits (FIFO has a max of 16 entries). So let’s say we want the first comparator (FIFO0, Thresh0) to match the value 4 or 0b0100; it needs to be set like this:
BIO_BDMA_SFR_ELEVEL = 0x4;
The BIO_BDMA_SFR_ETYPE contains 3 groups of 8 bits for:
- [7:0]
LTor Less Than - [15:8]
EQor Equal - [31:16]
GTor Greater Than
As this is 8 bits long for each type, a 1 in the proper bit will set the comparator type; so if we want to check EQ for the first comparator, bit 8 (or 0x100) should be set:
BIO_SFR_ETYPE = 0x100;
In the BIO Processor you can wait for the signal like this:
li x27, 0x1000000 # mask for event[24]
wait_for_event:
mv t1, x30 # halt for event
# run here all the code for event
j wait_for_event
So what this does is the following:
- Asks to only be woken up when the event matches mask
0x1000000 - Halts on event by reading
x30 - When there are 4 items in FIFO0, it will continue
Let’s note that the Event Register contains other registers and is not documented yet, so either set the mask through x27 for a specific event, or wait for an event and check through a separate mask whether it is the event you want.
An example that adds 4 values using a signal is here: https://github.com/danguer/embedded-boards101/tree/main/daobao-baochip/bio_add_four
