gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > Fixed Memory Address for a Non-blocking inter-processor FIFO

#106448 - Goosey - Thu Oct 19, 2006 6:20 am

Greets,

I have been trying to implement a Non-Blocking FIFO Queue to transfer log messages from the ARM7 to the ARM9. Initially I implemented this to transfer a single u32 and it worked fine, but when I extended it to use my LogMessage structure it failed to work on hardware (still runs fine in Dualis).

The basic strategy I used was to define the FIFOQueue class, take the memory address at (IPC+1) and cast it to a pointer of the class as such:
Code:

class FIFOQueue
{
/// Stuff here...
};
FIFOQueue* ARM7_TO_ARM9_FIFOQUEUE = (FIFOQueue*)(IPC+1);


I worried that this might have some issues since I am not really allocating the memory for it, I am simply putting it at a constant address (which seems to be the same pattern the IPC structure follows).

I think the reason it would work with a u32 queue but fails on the LogMessage queue is that the memory size of the queue is quite larger and it is 'poking' into memory that is being used for the stack. That's just my guess though.

My questions are then:
- Anyone with experience in defining an arbitrary memory address for shared access know of a 'safe' way to do this.. IE: to be able to 'allocate' the space so that the head manager and the stack will not tread into it... IE: How does IPC get away with this?
- Is this heading in a foolish direction? I am aware of the hardware FIFO, however this was a simpler solution then writing a wrapper to seamlessly translate log messages into 32bit chunks the hardware FIFO can pass... It is starting to look like a general purpose FIFO 'stream' class is going to be needed now.

Any advice is welcome. I can produce the code if it would help, although the implementation details didn't seem relevant (more wondering if I am heading in the right design direction on using the fixed memory address for this stuff)..

Right now it is just for logging messages between the procs, however in the future I planned to use the FIFO pattern for other means of ipc data transfer (such as sound).

#106454 - DekuTree64 - Thu Oct 19, 2006 8:07 am

Is your queue structure larger than about 3.5KB? The way the IPC works is, the linker scripts for ARM9 and ARM7 specify the size of main RAM as 4MB - 4KB, so no code or data will go in the last 4KB of it. Then the IPC struct is defined as address 0x27ff000 (4KB before the end of the non-cached mirror of main RAM). The IPC struct itself is pretty small, but if your structure is large enough to run over, it will anger the protection unit and lock up.

But for simple log messages I'd kind of doubt that it would be more than a few hundred bytes at most... can't think of anything else offhand that would cause it to die on hardware but not dualis though.

Anyway, that sounds like an ok way of transferring log messages to me. I do something similar to pass assert info from ARM7, so I'll know what happened if it runs into a problem.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#106463 - josath - Thu Oct 19, 2006 10:13 am

To safely allocate memory which can be shared by both processors, simply allocate the RAM on the arm9 using 'new' or 'malloc', then pass the address to the arm7 (by using a single u32 in IPC, or by using the hardware FIFO, etc)

#106476 - Goosey - Thu Oct 19, 2006 3:39 pm

@DekuTree64:
Ah thanks for that explanation! Indeed my structure was extending past that barrier (32-long queue of (128char + 32bit priority number) messages).

I suppose a way I could fix this (probably would have been a better initial implementation) is to use a static string table and pass just an ID number identifying the string.

I find it interesting that the last 4KB is 'reserved' for IPC. Is this a configurable option anywhere? Is there any methodology to ensure your within the 4KB barrier other then hand calculation?

Thanks for clearing that up

@josath:
Thanks for that suggestion. I wasn't sure if malloc'd memory was always from main RAM accessible to both processors. This seems like a bit of a safer way..

#106488 - tepples - Thu Oct 19, 2006 5:36 pm

Goosey wrote:
Is there any methodology to ensure your within the 4KB barrier other then hand calculation?

sizeof
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#106491 - Goosey - Thu Oct 19, 2006 6:04 pm

tepples wrote:
Goosey wrote:
Is there any methodology to ensure your within the 4KB barrier other then hand calculation?

sizeof


Thanks, but I mean more of a compiletime check. I suppose that putting all the 'ipc structs' as members of a 'master ipc struct' and doing a sizeof (printing an immediate error fail message if over 4k) is the best i can do?

#106492 - tepples - Thu Oct 19, 2006 6:25 pm

So you want automated unit testing at compile time. You can have a second, smaller DS program write the size of the struct to a file or to GBA SRAM, run it in an emulator that supports the GBAMP CF or GBA SRAM, and then have another PC program interpret the value and return a failure result if the size is out of bounds.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#106507 - Goosey - Thu Oct 19, 2006 9:18 pm

Ah figured a way to check for 4k limit at compiletime. Might as well share knowledge for anyone that will find it useful:

Code:

struct IPCMapping
{
   sTransferRegion m_ndsLibTransferRegion;
   //Add additional IPC stuff here
};

#define IPC_MAPPING   ((IPCMapping volatile *)(IPC));

// Make sure that the IPCMapping is not above 4KB
StaticAssert( sizeof(IPCMapping) < 4*1024 );


#defines for StaticAssert..

For C Code this works (thanks devmaster.net), although it is not very descriptive error message:
Code:

 // StaticAssert() can be used to perform many compile-time assertions:
 //            type sizes, field offsets, etc.
 #define StaticAssert(e) typedef char __C_ASSERT__[(e)?1:-1]



For C++ Code this works better (more descriptive error message, if no assertion no space is used up) (Stripped down version of BOOST_STATIC_ASSERT):
Code:

//Borrow BOOST_JOIN from Boost
#define BOOST_JOIN(X,Y) X##Y

template <bool x> struct STATIC_ASSERTION_FAILURE;
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
template<int x> struct static_assert_test{};

#undef   STATIC_ASSERT
#define STATIC_ASSERT( B ) \
   enum { BOOST_JOIN(boost_static_assert_enum_, __LINE__) \
   = sizeof(STATIC_ASSERTION_FAILURE< (bool)( B ) >) }
#define StaticAssert( B ) STATIC_ASSERT( B )


Happy Codings