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 > lickfifo (r4) - an automatic FIFO-interrupt-handler

#115440 - Lick - Tue Jan 16, 2007 3:42 am

Download
#Scroll down for r4

Why
I wrote a small, simple and automatic FIFO-interrupt-handler to eliminate the chaotic FIFO IRQ spaghetti that was present in my previous projects.

What
I call it lickfifo, and it will enable, disable the FIFO IRQ with 2 simple functions, and it also has a functionpointer-system, where you can add your own FIFO IRQ messagehandlers to an automatic process. It works on both ARM7 and ARM9!
The example project should illustrate how it works, and why it makes your code much cleaner. It sends (triggered by keyinput) a message from ARM9 to the ARM7 (automatically) back to the ARM9 that (automatically) swaps the LCDs.

How
#Scroll down for r4

Suggestions are welcome,
- Lick
_________________
http://licklick.wordpress.com


Last edited by Lick on Fri Jan 19, 2007 2:54 pm; edited 10 times in total

#115445 - Lick - Tue Jan 16, 2007 3:56 am

Currently, lickfifoAddHandler(functionpointer) returns an index that can be used to remove the handler at a later stage with lickfifoRemoveHandler(index). I could probably replace the index-system by simply checking the handler-array against the passed functionpointer. Like: lickfifoRemoveHandler(functionpointer).
Dunno if that's necessary though. It's probably slower as well.

- Lick
_________________
http://licklick.wordpress.com

#115480 - 0xtob - Tue Jan 16, 2007 11:52 am

Great idea Lick!

I would really like to see this in libnds. Then, the IPC transfer region struct could be replaced by something more flexible and it would be much easier to let the CPUs talk to each other.

Some suggestions:

I would personally think it's cleaner to add handlers for specific message types instead of checking for the message inside the handler, i.e.
Code:

lickfifoAddHandler(FIFO_CMD_PLAY_SAMPLE, handlePlaySample);

Of course, handlers like handleBelowTen would not be possible like this, but I don't see the point in that anyway.

Also, it would be cool to have an easy way of sending commands and data over the FIFO, something like
Code:

sendFifoCommand(FIFO_CMD_PLAY_SAMPLE, &smpinfo, sizeof(smpinfo));


What do you think?
_________________
http://blog.dev-scene.com/0xtob | http://nitrotracker.tobw.net | http://dsmi.tobw.net

#115484 - Lick - Tue Jan 16, 2007 3:34 pm

I was thinking about it myself too. It seems much easier to let the automatic process recognize the message, instead of doing it in each and every handler.
Sending messages is indeed something that should be included as well, hehe.

I will work on it later today!
- Lick
_________________
http://licklick.wordpress.com

#115541 - Lick - Wed Jan 17, 2007 12:34 am

Download
lickfifo_r2.zip (17kB) (Example included, press A/B)

What's added in r2
Message handlers are now assigned to id's and you can send data along with the message. Thanks 0xtob for the suggestions.

Notes
- Data can be sent per byte (1), per short (2) or per long (4).

How
Processor A
Code:
void SampleMessageHandler1(u32 bytes, u32 transfertype) {
  // message handler that doesn't receive data
}

void SampleMessageHandler2(u32 bytes, u32 transfertype) {
  // message handler that receives data
   u8 data[256];
   lickfifoRecv(&data, bytes, transfertype);
}

..
lickfifoAddHandler(201, SampleMessageHandler1);
lickfifoAddHandler(202, SampleMessageHandler2);
lickfifoEnable();
..

Processor B
Code:

char data[256];
strcpy(data, "Example data");
..
lickfifoEnable();
lickfifoSend(201, 0, 0, 0);
lickfifoSend(202, data, 256, LICKFIFO_TRANSFER_LONG);
..

_________________
http://licklick.wordpress.com

#115544 - 0xtob - Wed Jan 17, 2007 1:09 am

Great work, Lick!

With libcartreset, this will be the second Lick-lib in NitroTracker. One question though: What are the different transfer types for? Does it make a difference (except for a speed gain perhaps) if the data is transferred as bytes, shorts or longs?
_________________
http://blog.dev-scene.com/0xtob | http://nitrotracker.tobw.net | http://dsmi.tobw.net

#115546 - Lick - Wed Jan 17, 2007 1:22 am

The transfertype is only set when you send, after that it's all just 'pass along'. And I -think- that 4byte transfers will be the fastest. [edit] Also make sure your data is the correct size.

Oh and if you haven't noticed (you should have!) the max handlers is currently set to 32. You can change that yourself by changing a define, according to your own needs (speed vs maximum).

I kinda dislike libcartreset, at least, the current form. I should update it -fast-. Hehe..
- Lick
_________________
http://licklick.wordpress.com

#115621 - Lick - Wed Jan 17, 2007 11:55 pm

Download
go to this page

lickfifo r3
- Fixed the non-working "lickfifoRemoveHandler()" in r2. It works now, and it accepts the id of the handler that will be removed.
- Renamed some types like the functionpointer-type, it's now called a "LICKFIFO_CALLBACK".
- The callbacks do not require parameters anymore, because..
- The data-receive function will now track the amount of bytes transferred. It's simply "lickfifoRecv(&myData);" now.

This should be the final release, until new features are suggested! Thanks for using lickfifo!
- Lick
_________________
http://licklick.wordpress.com

#115624 - Mighty Max - Thu Jan 18, 2007 12:29 am

In your send rountine:

You are waiting for the fifo to becoem empty before sending the next byte. Instad you should wait until the fifo is not full.

If you are allways waiting for a empty fifo, you are risking to cause an irq on the other side for every single byte as the other side might leave the irq because the fifo is empty before it was filled with the next byte. This will be really slow. If you instead fill the next byte as soon as there is place available, you can handle the stream in one irq, and with the full speed the fifo can do.

The Recv function does not check against buffer overrun, and there is no indication whether the data was transfered. It is not possible to wait for an transfer (even if i'd implement a way check if the transfer was successfull) in an irq handler. I.e. when you try to request another chunk of sounddata from within a timerirq, without hassle with reentrant irq calls or leaving the timer.

But that all can be improved, so keep up the work!
_________________
GBAMP Multiboot

#115637 - Lick - Thu Jan 18, 2007 1:31 am

Mighty Max wrote:
The Recv function does not check against buffer overrun, and there is no indication whether the data was transfered. It is not possible to wait for an transfer (even if i'd implement a way check if the transfer was successfull) in an irq handler. I.e. when you try to request another chunk of sounddata from within a timerirq, without hassle with reentrant irq calls or leaving the timer.


Hey Mighty Max, thanks for your comments, it sure seems like there is much that still needs to be done hehe.

I'm not sure if I should make the system bug-prove though. It's designed to make things easier, not safer. At this point, the programmer should know what he's doing when calling Send and Recv.
IMO, it's pretty obvious because each id is paired with 1 callback, so there's always 1 send and 1 (optional) receive. It might get buggy if an id is accidentally paired with two callbacks, so I need to prevent that from happening. (Will do in r4)

As for the latter part, about waiting for a transfer.. I'm not sure I get what you're trying to say here. You mean like a Recv function that works outside of a FIFO callback? Perhaps I can make a SendAndRecv function, so it will trigger the other side to send data, and after that, receive the data in the same function. Good idea?


Thanks for your interesting comments,
- Lick
_________________
http://licklick.wordpress.com

#115841 - Lick - Fri Jan 19, 2007 2:51 pm

Download
Downloadpage r4

lickfifo r4
- All "lick" and "LICK" prefixes removed. (The library is still called "lickfifo" though. ;)
- fifoSend and fifoRecv modified to only transfer data.
- fifoTrigger added. This will do the triggering from now on.
- fifoSend routine now waits until the "fifo is not full" instead of "fifo is empty".

How
Code:
.. first processor
fifoAddHandler(id, callback);
fifoEnable();

.. other processor
fifoTrigger(id); // this invokes callback on the other processor
fifoSend(data, sizeof(data), FIFO_TRANSFER_BYTE); // optional
fifoRecv(data, sizeof(data), FIFO_TRANSFER_BYTE); // optional


Keep the suggestions coming!
- Lick
_________________
http://licklick.wordpress.com

#115860 - Lick - Fri Jan 19, 2007 8:24 pm

Well, to show how easy it is to add to this automatic system, I went ahead and implemented lickfifoX. It's basically a "useful set of handlers" for lickfifo.

Download page

It enables you to do the following:
- ARM9: see if hardware is DS Lite.
- ARM9: control the DS Lite brightness.
- ARM9: control the backlights.
- ARM9: read the firmware.

Call fifoXAddHandlers() to add the handlers to the lickfifo-system.

Enjoy!
- Lick
_________________
http://licklick.wordpress.com

#115890 - Lick - Fri Jan 19, 2007 11:40 pm

It suddenly strook me: should this actually be called an IPC system instead of FIFO system?

w00t..
_________________
http://licklick.wordpress.com

#115907 - HyperHacker - Sat Jan 20, 2007 2:41 am

Perhaps, but it could be confused with the "IPC" struct in the libnds header files, which is simply shared RAM.
_________________
I'm a PSP hacker now, but I still <3 DS.

#115932 - Lick - Sat Jan 20, 2007 1:02 pm

I got a tip from someone that this code seriously has problems and might cause breakage and bugs.
It's recommended NOT to use it.

We'll just forget about its existence..
_________________
http://licklick.wordpress.com

#115958 - tepples - Sat Jan 20, 2007 7:38 pm

Have you abandoned the project entirely, or will you fix these major problems in an r5?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#115965 - Lick - Sat Jan 20, 2007 7:50 pm

Well, the code works, but the whole design of it (a callback-mechanism inside an interrupt) can cause a lot of bugs.

To be honest, I am STILL not really sure what issues might occur, but I am convinced that whatever Wintermute advices is probably correct. He is the one who wrote the interruptDispatcher, after all.
_________________
http://licklick.wordpress.com

#116000 - DekuTree64 - Sun Jan 21, 2007 12:51 am

Lick wrote:
Well, the code works, but the whole design of it (a callback-mechanism inside an interrupt) can cause a lot of bugs.

Huh? That's basically what the interrupt dispatcher itself does... Sure you have to be careful not to mess with things that could break the code that was interrupted, but that's just standard procedure when working with interrupts.

I'd like to hear what the specific problems were, because it looks fine to me, and I've been using a similar system that I wrote for a long time with no problems.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#116016 - Lick - Sun Jan 21, 2007 4:46 am

I updated the r4 download. The .c sourcefile is now much easier to read. No other changes (except for the loop in the FIFO IRQ-handler).

DekuTree64, I think it has to do with reentrancy, or something like that.
_________________
http://licklick.wordpress.com

#116032 - DekuTree64 - Sun Jan 21, 2007 6:53 am

Lick wrote:
DekuTree64, I think it has to do with reentrancy, or something like that.

Ah, nested interrupts. That could cause chaos if you don't check for it, but it doesn't make the design of the system inherently bad.

Should be all you need is in __fifoIRQ(), disable the FIFO receive interrupt in REG_IE, then read stuff from the FIFO and do callbacks, then reenable at the end. That way, if another FIFO transfer happens in the middle of the handler, it won't actually be registered until after the callback has finished processing.


EDIT: Nevermind that, I just looked at the default dispatcher in the libnds source and found this little bit of code:
Code:
@---------------------------------------------------------------------------------
got_handler:
@---------------------------------------------------------------------------------

   mrs   r2, cpsr
   bic   r2, r2, #0xdf      @ \__
   orr   r2, r2, #0x1f      @ /  --> Enable IRQ & FIQ. Set CPU mode to System.
   msr   cpsr,r2

   ldr   r2, [r3,#0x210]      @ REG_IE
   stmfd   sp!, {r0,r2, r3,lr}   @ irq mask, IE, REG_IE, lr
   bic   r2, r2, r0      @ disable interrupt about to be serviced
   str   r2, [r3,#0x210]

   adr   lr, IntrRet
   bx   r1

Which already disables the interrupt it's about to jump to the handler for. So there should be no re-entering __fifoIRQ() at all. So I have no idea what the danger is.


EDIT #2: Oh wait, there is still potential for missed messages due to an annoying thing in the dispatcher which I complained about before. I guess you could do a blocking system to get around it, where when one CPU send a message, it waits until the other has finished running the callback and reported back before moving on. Slower, but guarantees safety.

But I still hold that clearing the REG_IF bit before jumping to the user handler is the only safe way to handle interrupts.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#116052 - Lick - Sun Jan 21, 2007 1:24 pm

If it's a matter of time and how long the interrupt handler takes (if it takes too long then messages will be lost), then I think there could be critical and non-critical messages. The critical messages will be handled immediately, and non-critical messages will be put into a queue and are then handled in the mainloop, or vblank.
It's not often that these interrupts are critical, so in practice it would come down to 2 or 3 critical and the rest non-critical.

But there's more to it, I'm sure there is, but no one can explain it thoroughly.
_________________
http://licklick.wordpress.com