Closed frohro closed 4 months ago
I think there's a misunderstanding here. The available
method works like a Stream.available()
or Serial.available()
and returns the number of words that can be read, not the free space. So starting w/rp2040.available() == 0
is definitely the expected case. Just like in the Serial
case, the idea is that you can see if there's data there before doing a possibly blocking read.
Thanks @earlephilhower , For newbies like me, it might help if the brief docs were made a tad more descriptive. For example, what you wrote above would be a nice addition to the docs. Here are some other things that I wonder about. 1) Is it right that there is a FIFO that Core 0 sends messages to Core 1, and another FIFO that Core 1 sends messages to Core 0? It seems that way from my testing and even seems implied, but not explicit in the docs. 2) What is the number of uint32_t that can be used, or what limits it? 3) I am assuming that I can pass pointers back and forth between cores, which can each access the memory pointed to, as long as the other core does not try to at the same time. I hope I'm right about this. 4) An example of 3 above would be nice.
There's no reason you can't implement your own FIFO of any size and type, if desired. And for multiprocessor comms there are many other things you can use, like shared volatile globals, single write/single reader queues, etc. instead of the FIFO. I implemented it this way to replicate the HW FIFO which needs to be used by the core and SDK to let one core interrupt the other core (there's no other way than the HW FIFO to send an IRQ from Core0->Core1/Core1->Core0, strangely enough!).
@earlephilhower , I'll work on a pull request for the docs on the multicore communication. Thanks for the answers!
I couldn't figure out the pull-request procedure. Here is the multicore.rst I modified though. You would need to read it. I am not 100% sure about it, but it reflects my experience.
Multicore Processing
====================
The RP2040 chip has 2 cores that can run independently of each other, sharing
peripherals and memory with each other. Arduino code will normally execute
only on core 0, with the 2nd core sitting idle in a low power state.
By adding a ``setup1()`` and ``loop1()`` function to your sketch you can make
use of the second core. Anything called from within the ``setup1()`` or
``loop1()`` routines will execute on the second core.
``setup()`` and ``setup1()`` will be called at the same time, and the ``loop()``
or ``loop1()`` will be started as soon as the core's ``setup()`` completes (i.e.
not necessarily simultaneously!).
See the ``Multicore.ino`` example in the ``rp2040`` example directory for a
quick introduction.
Stack Sizes
-----------
When the Pico is running in single core mode, core 0 has the full 8KB of stack
space available to it. When using multicore ``setup1``/``loop1`` the 8KB is split
into two 4K stacks, one per core. It is possible for core 0's stack to overwrite
core 1's stack in this case, if you go beyond the 4K limitation.
To allocate a separate 8K stack for core 1, resulting in 8K stacks being available
for both cores, simply define the following variable in your sketch and set it
to ``true``:
.. code:: cpp
bool core1_separate_stack = true;
Pausing Cores
-------------
Sometimes an application needs to pause the other core on chip (i.e. it is
writing to flash or needs to stop processing while some other event occurs).
void rp2040.idleOtherCore()
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sends a message to stop the other core (i.e. when called from core 0 it
pauses core 1, and vice versa). Waits for the other core to acknowledge
before returning.
The other core will have its interrupts disabled and be busy-waiting in
an RAM-based routine, so flash and other peripherals can be accessed.
**NOTE** If you idle core 0 too long, then the USB port can become frozen.
This is because core 0 manages the USB and needs to service IRQs in a
timely manner (which it can't do when idled). In most cases, you should
try not to idle a core if you can get by using a mutex. You can use the
mutex functions from available in the Raspberry Pi Pico SDK to protect
shared memory.
void rp2040.resumeOtherCore()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Resumes processing in the other core, where it left off.
void rp2040.restartCore1()
~~~~~~~~~~~~~~~~~~~~~~~~~~
Hard resets Core1 from Core 0 and restarts its operation from ``setup1()``.
Communicating Between Cores
---------------------------
The RP2040 provides a hardware FIFO for communicating between cores, but it
is used exclusively for the idle/resume calls described above. Instead, please
use the following functions to access a software-managed, multicore safe
FIFO. There is a separate FIFO for each core. One core writes to it, the other
reads from it.
It is important to understand that you can share memory between the two
cores, but you must ensure no core writes while the other is reading or writing.
Both cores can read from shared memory at the same time, but only one core can
write at a time. You can use mutexes or other synchronization primitives to
ensure this.
void rp2040.fifo.push(uint32_t)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Pushes a value to the other core. Will block if the FIFO is full.
bool rp2040.fifo.push_nb(uint32_t)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Pushes a value to the other core. If the FIFO is full, returns ``false``
immediately and doesn't block. If the push is successful, returns ``true``.
uint32_t rp2040.fifo.pop()
~~~~~~~~~~~~~~~~~~~~~~~~~~
Reads a value from this core's FIFO. Blocks until one is available.
bool rp2040.fifo.pop_nb(uint32_t \*dest)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Reads a value from this core's FIFO and places it in dest. Will return
``true`` if successful, or ``false`` if the pop would block.
int rp2040.fifo.available()
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Returns the number of values available for this core to read from its FIFO.
'''
The docs say this function tells you the number of uint32_t left in the buffer. It seems to give the number you have used instead. Also, it is ambiguous which core it is giving you the number for, because "this core's fifo" does not tell you if the core that writes the FIFO or the core that reads the FIFO owns it. :-). I am assuming it is a way you can see if the opposite core has read the data you gave it. Here is my messy test code showing this behavior.
Thanks for all your good work on the Arduino-Pico tools.