pw_stream_shmem_mcuxpresso#
pw_stream_shmem_mcuxpresso
implements the pw_stream
interface for
reading and writing between two different processor cores via shared memory
using the NXP MCUXpresso SDK. It uses the messaging unit module (MU) to signal
data readiness between cores.
Setup#
This module requires a little setup:
Use
pw_build_mcuxpresso
to create apw_source_set
for an MCUXpresso SDK.Include the debug console component in this SDK definition.
Specify the
pw_third_party_mcuxpresso_SDK
GN global variable to specify the name of this source set.
The name of the SDK source set must be set in the “pw_third_party_mcuxpresso_SDK” GN arg
Usage#
ShmemMcuxpressoStream
blocks on both reads and writes, as only one
outstanding buffer can be transferred at a time in each direction. This means a
dedicated thread should be used for both reading and writing. A typical use case
for this class would be as the underlying transport for a pw_rpc network between
cores. Use with the pw::rpc::StreamRpcFrameSender
and
pw::rpc::StreamRpcDispatcher
classes.
Interrupt handlers and shared buffers on both cores must be setup before using this stream. The shared buffer must be mapped as uncacheable on both sides.
As an example on the RT595, we connect the M33 core to the FusionF1 DSP. On the FusionF1 side, the MU interrupt must be explicitly routed.
Initialization for the M33 Core:
// `kSharedBuffer` is a pointer to memory that is shared between the M33 and
// F1 cores, and it is at least `2 * kSharedBufferSize` in size.
ByteSpan read_buffer = ByteSpan{kSharedBuffer, kSharedBufferSize};
ByteSpan write_buffer = ByteSpan{kSharedBuffer + kSharedBufferSize, kSharedBufferSize};
ShmemMcuxpressoStream stream{MUA, read_buffer, write_buffer};
PW_EXTERN_C void MU_A_DriverIRQHandler() {
stream.HandleInterrupt();
}
void Init() {
return stream.Enable();
}
Initialization for the FusionF1 Core:
ByteSpan write_buffer = ByteSpan{kSharedBuffer, kSharedBufferSize};
ByteSpan read_buffer = ByteSpan{kSharedBuffer + kSharedBufferSize, kSharedBufferSize};
ShmemMcuxpressoStream stream{MUB, read_buffer, write_buffer};
PW_EXTERN_C void MU_B_IrqHandler(void*) {
stream.HandleInterrupt();
}
void Init() {
// Enables the clock for the Input Mux
INPUTMUX_Init(INPUTMUX);
// MUB interrupt signal is selected for DSP interrupt input 1
INPUTMUX_AttachSignal(INPUTMUX, 1U, kINPUTMUX_MuBToDspInterrupt);
// Disables the clock for the Input Mux to save power
INPUTMUX_Deinit(INPUTMUX);
xt_set_interrupt_handler(kMuBIrqNum, MU_B_IrqHandler, NULL);
xt_interrupt_enable(kMuBIrqNum);
stream.Enable();
}
Read/Write example where each core has threads for reading and writing.
Core 0:
constexpr std::byte kCore0Value = std::byte{0xab};
constexpr std::byte kCore1Value = std::byte{0xcd};
void ReadThread() {
while(true) {
std::array<std::byte, 1> read = {};
auto status = stream.Read(read);
if (!status.ok() || status.size() != 1 || read[0] != kCore1Value) {
PW_LOG_WARN("Incorrect value read from core1");
}
}
}
void WriteThread() {
std::array<std::byte, 1> write = {kCore0Value};
while(true) {
stream.Write(write);
}
}
Core 1:
void ReadThread() {
while(true) {
std::array<std::byte, 1> read = {};
auto status = stream.Read(read);
if (!status.ok() || status.size() != 1 || read[0] != kCore0Value) {
PW_LOG_WARN("Incorrect value read from core0");
}
}
}
void WriteThread() {
std::array<std::byte, 1> write = {kCore1Value};
while(true) {
stream.Write(write);
}
}