What is this?
A novel approach to inter-processor communications and related resource management for the Nintendo DS. Based on the concepts of simplicity, ease of use, modularity, and run-time resource allocation.
In addition to the usual FIFO functionality, functions for managing the 4K shared memory space (swrmalloc/swrfree), and rudimentary inter-processor mutual exclusion functions are also provided.
Inter-processor systems provided:
- Shared Work Ram allocation (swrmalloc/swrfree)
- Shared Work Ram mutual exclusion locks
- FIFO for sending data and alerting other CPU of events
- Ring buffers for queuing data recieved by fifo (seperate, in the rngLib library)
Other libraries using thfifo include:
- 2009/03/30 - added check for fifo full before transmitting to prevent possible data loss. See ToDo.
Where to get it
Currently the sources for this are part of LibThds. The source can be downloaded via SVN (see libthds page) or directly from the svn httpd:
(.h and .c files should be placed in both arm7 and arm9 directories plz :D)
Functions detailed here are for managing the 4K shared work ram (swr) segment. This segment is uncached on the arm9 side and therefore slower so should only be used for variables and structures that routinely need to be immediately available to both CPUs.
Traditionally this area was managed at compile time using complex #define statements requiring careful planning to prevent two libraries reusing the same region twice. To avoid these potential pitfalls a simple dynamic runtime allocation system is provided so that libraries may request swr space with minimal concern.
These functions may be called from either CPU, and you should be able to malloc on one side and free on the other! (however this is discouraged).
Initialize the swr functionality, called on both CPUs before using. Calling this function is unnecessary if using the FIFO because its called already in fifoInit().
Allocate a region of swr. Only argument is the size requested. Returns a pointer to the newly allocated region or null if fail.
void *swrmalloc(int size);
Frees up a previously allocated region of swr. Only argument is the previously allocated swr pointer. returns true if successful.
Do not see this being used very often if at all, but it is provided for completeness.
bool swrfree(void *loc);
Inter-processor Mutual Exclusion
Two functions are available to restrict access during critical read/writes to swr. For this the well documented Peterson algorithm is used.
These functions were originally conceived to prevent corruption during swrmalloc but may be useful to developers as well. Still debating if the FIFO section itself shouldnt provide per channel locks... @_@
While use of these functions is provided it should generally be avoided since it slows down the other CPU, and essentially wastes time while waiting.
This function is caled before accessing critical data.
This function is caled after accessing critical data.
getGlobalSWRLock(); //get exclusive access to SWR for this cpu, and/or wait until other CPU is finished with it myIpcData->variable=newvalue; myIpcData->othervar=anothervalue; giveGlobalSWRLock(); //allow other CPU access to SWR
The design philosophy here is based on simplicity and modularity. It is essentially a channelized version of the hardware rather than an attempt to provide facilities for every possible need. Channel numbers are 0 thru 15.
The channels are named with strings to ease locating on the other CPU. In this way the channel number to use does not need deciding beforehand.
In addition the the ASCII name, an optional pointer to user data to be shared between CPUs (most likely allocated with swrmalloc :P).
Each CPU expecting to receiving data is required to assign a handler function. To receive data must set a handler function using fifoChanSetFunc(). Additionally fifoChanRecvChk() can be used to determine if a channel has a receive function set on the other CPU or not. In this way it may double as a mechanism to sync user's initialization functions. (need example!) If no receive function is set, data received at buffer is discarded.
Up to 24 bits of data may be transfered at a time, the format of this data is left up to the developer.
Should be called on both cpu before using the FIFO. Will sync and setup fifo for use.
Create a new channel.
- name - Zero terminated name to use for this channel.
- udata - pointer to user data structure, usually allocated with swrmalloc.
Returns a channel number.
thfifo_t fifoChanCreate(const char *name, void *udata);
Sets a function to be called whenever this channel receives data from other CPU.
- chan - which channel to use
- func - pointer to a function of type pHFifoFunc
Returns true if channel was valid.
bool fifoChanSetFunc(thfifo_t chan, _pHFifoFunc func);
The _pHFifoFunc callback function should accecpt the following arguments:
- chan - channel number 0-15
- data - up to 24 bits of data sent from other side
- udata- pointer to shared IPC structure (prolly redundant as user will create a global for this... remove?)
//callback function type typedef void(*_pHFifoFunc)(thfifo_t chan, u32 data, void *udata); //callback
Sends up to 24 bits of data to other CPU's receive handler.
- chan - channel to send data on.
- data - data to send, up to 24 bits. MSByte (top 8 bits) are discarded, so if data = 0xffffffff then 0x00ffffff will be sent.
void fifoSendChan24(thfifo_t chan, u32 data);
Sends up to 32 bits of data to other CPU's receive handler. Note this takes 2 fifo sends where as fifoSendChan24 uses only one. If possible use 24 bit function to save. ^^
- chan - channel to send data on.
- data - data to send, up to 32 bits. so if data = 0xffffffff then 0xffffffff will be sent.
void fifoSendChan32(thfifo_t chan, u32 data);
Searches for a channel by name. ^^
- name - zero terminated name of the channel your looking for.
Returns channel if found or -1 if not.
thfifo_t fifoFindChan(const char *name);
Determins if a receive function has been set on the other CPU. chan - channel to check. Returns true if function has been set, false otherwise.
bool fifoChanRecvChk(thfifo_t chan);
fifoGetUData Returns the user data pointer for a specified channel.
- chan - channel
Returns the user data pointer for a specified channel.
void *fifoGetUData(thfifo_t chan);
While this is essentially complete and fully functional some things may be improved:
fifo32Buf[FIFOMAXCHANS] should be moved into the fifo structActually putting this here because at first glance it seems this may be useful, however having it in its own array on each cpu is faster because it does not need to write/read from SWR.
- in the fifo receive function chan&0xf and chan&0xf0 are seen multiple times, would probably be best to create a local variable containing these values already masked off.
Check if FIFO is full before transmitting data.. This Really should be implemented otherwise data may overflow and become lost. Possibly create a macro that loops while the buffer full flag is set.(added 2009/03/30) It may also be nice to add a feature to create ring buffer to queue up data in the event fifo becomes full and utilize the xmit interrupt to transparently send the queued data in the background. Integrate rng32Lib into thFifo?
- possibly provide a channelized swrLock ?