DEV Community

Daniel Guerrero
Daniel Guerrero

Posted on

BIO Baochip/Dabao - FIFO and Events

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
Enter fullscreen mode Exit fullscreen mode

This will block until there is info in FIFO0.
To unblock it, you need, from another BIO processor:

li x16, 0x10
Enter fullscreen mode Exit fullscreen mode

or from the host processor:

BIO_BDMA_SFR_TXF0 = 0x10; // bio_push_fifo0(0x10); in SDK
Enter fullscreen mode Exit fullscreen mode

To read in the host processor:

uint32_t data = BIO_BDMA_SFR_RXF0; // bio_pop_fifo0(); in SDK
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

  1. add the value to FIFO0
  2. read the value from FIFO0
  3. 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);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
bio_push_fifo0(multiplicand);
// wait until fifo1 has data
while(bio_fifo_level(1) == 0);
result = bio_pop_fifo1();
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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:

  • x28 to SET in the BIO Processors (or BIO_BDMA_SFR_EVENT_SET in host, bio_event_set in SDK)
  • x29 to CLEAR in the BIO Processors (or BIO_BDMA_SFR_EVENT_CLR in host, bio_event_clear in SDK)

In the BIO Processor there are two more registers:

  • x27 is a mask to only halt on certain events
  • x30 is a Halt-on-Event register: when you try to read it, it will halt until (x27 & events) != 0 or 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
Enter fullscreen mode Exit fullscreen mode

In the host, this code can be added:

bio_push_fifo0(multiplicand); // sdk

// wait until signal happens
while((BIO_SFR_EVENT_STATUS & 0x4) == 0);
Enter fullscreen mode Exit fullscreen mode

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:

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;
Enter fullscreen mode Exit fullscreen mode

The BIO_BDMA_SFR_ETYPE contains 3 groups of 8 bits for:

  • [7:0] LT or Less Than
  • [15:8] EQ or Equal
  • [31:16] GT or 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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

So what this does is the following:

  1. Asks to only be woken up when the event matches mask 0x1000000
  2. Halts on event by reading x30
  3. 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

Top comments (0)