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 > Send FIFO is breaking things (Solved)

#153684 - HyperHacker - Thu Apr 03, 2008 10:20 pm

[edit] Fixed it. It looks like you only get a FIFO Not Empty interrupt when the FIFO becomes non-empty. If your interrupt handler doesn't completely drain it the interrupt won't fire again because it's still not empty. The solution is to simply loop in the interrupt handler, reading everything:
while(!(REG_IPC_FIFO_CR & IPC_FIFO_RECV_EMPTY))

What would happen was I'd send two messages to ARM9 within a very short time, and it would only read one every time the Not Empty interrupt fired. The second message would arrive before it had read all of the first one, and thus never get read. ARM7 continues sending, the FIFO fills up with unread messages, and SendMessage() goes into an endless loop waiting for it to be not full. Since I'd call SendMessage() from the VBlank interrupt, other interrupts never got a chance to fire, hence never returning to the main loop and/or not receiving messages from ARM9. Nasty!

Also I didn't need to do any of that gross nested interrupt stuff. :-D I discovered that the FIFO interrupt will simply fire when the VBlank handler is done.

(I swear, the best way to fix a bug is to ask for help on a forum. Probably 90% of the time I fix it myself within hours of posting. :-p)


Original post follows.


This is my ARM7 code so far (obviously far from complete):
Code:
//ARM7 Code
#include "main.h"

volatile bool ARM9Init = false;

/*
Entry Point
CPU: ARM7
Inputs:
   -argc: Number of arguments
   -argv: Pointer to arguments
Returns: Program return code
*/
int main(int argc, char** argv)
{
   irqInit();
   REG_IME = IME_DISABLE;
   memset((u8*)IPC, 0, sizeof(TransferRegion)); //Blank out IPC


   //Init interrupts
   REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_IRQ | IPC_FIFO_RECV_IRQ; //Enable FIFO IRQs
   irqSet(IRQ_VBLANK, VBlankInterrupt);
   //irqSet(IRQ_TIMER3, Timer3Interrupt);
   irqSet(IRQ_FIFO_NOT_EMPTY, FifoRecvInterrupt);
   irqEnable(IRQ_VBLANK | IRQ_FIFO_NOT_EMPTY);


   SendMessage(IPC_SYS_INIT, 0);
   REG_IME = IME_ENABLE; //Enable interrupts
   swiWaitForVBlank();
   swiWaitForVBlank();
   swiWaitForVBlank();

   while(true)
   {
      if(ARM9Init)
      {
         writePowerManagement(0, PM_SOUND_AMP | PM_BACKLIGHT_TOP | PM_BACKLIGHT_BOTTOM | BIT(4));
         while(true) swiWaitForVBlank();
      }

      swiWaitForVBlank();
   }
   return 0;
}


/*
Sends a message to ARM9.
Inputs:
   -Msg: Message ID (one of IPC_*).
   -Params: # of parameters.
   -...: Parameters (32 bits).
Returns: True on success, false otherwise.
Notes:
   -Blocks until the message can be sent (i.e. FIFO is not too full).
*/
bool SendMessage(IPCMessage Msg, u16 Params, ...)
{
   u32 Word;
   va_list list;

   va_start(list, Params);
   Word = (Msg << 16) | Params;
   for(u32 i=0; i<(Params + 1); i++) //+1 for the message itself
   {
      while(REG_IPC_FIFO_CR & IPC_FIFO_SEND_FULL);
      REG_IPC_FIFO_TX = Word;
      Word = va_arg(list, u32);
   }

   va_end(list);
   return true;
}


/*
VBlank handler
CPU: ARM7
*/
void VBlankInterrupt()
{
   touchPosition tempPos;
   s32 T1, T2; //todo: what exactly are these for?
   u32 Btns;
   u16 TouchX = 0, TouchY = 0, TouchZ1 = 0, TouchZ2 = 0;

   REG_IME = IME_ENABLE; //Receive FIFO interrupts while processing VBlank

   //Read buttons
   Btns = ((~REG_KEYXY) << 10) & (KEY_X | KEY_Y);
   if(!(REG_KEYXY & BIT(3))) Btns |= KEY_DEBUG;
   Btns |= (~REG_KEYINPUT) & 0x3FF;
   if(REG_KEYXY & 0x80) Btns |= KEY_LID;

   //Read touchscreen
   if(!(REG_KEYXY & 0x40)) //touched
   {
      Btns |= KEY_TOUCH;
      tempPos = touchReadXY();
      TouchX = tempPos.px;
      TouchY = tempPos.py;
   }

   TouchZ1 = touchRead(TSC_MEASURE_Z1);
   TouchZ2 = touchRead(TSC_MEASURE_Z2);

   //todo: read temperature once per second not once per frame
   SendMessage(IPC_SYS_INPUT, 4, Btns, (TouchX << 16) | TouchY, (TouchZ1 << 16) | TouchZ2, touchReadTemperature(&T1, &T2));
}


/*
Timer3 interrupt handler
CPU: ARM7
*/
void Timer3Interrupt()
{
}


/*
FIFO Not Empty interrupt handler
CPU: ARM7
*/
void FifoRecvInterrupt()
{
   u32 Word;
   IPCMessage Msg;
   u16 Params; //# params - only used for messages with variable parameters

   Word = REG_IPC_FIFO_RX;
   Msg = Word >> 16;
   Params = Word & 0xFFFF;

   switch(Msg)
   {
      case IPC_SYS_INIT:
         ARM9Init = true;
         writePowerManagement(0, 0);
      break;

      case IPC_SYS_COMPLETE:
      case IPC_SYS_ERROR:
      case IPC_SYS_FATAL:
         //todo
      break;

      case IPC_SND_PLAY:
      break;

      //Messages that ARM7 shouldn't be receiving, defined here to shut the compiler up.
      case IPC_SYS_INPUT: break;
   }
}
I added some writePowerManagement() calls for debugging. When it receives the IPC_SYS_INIT message it turns off the backlights. When the main loop sees this message has been received (ARM9Init == true) it turns them back on and blinks the power LED. Right now they turn off and never come back on so somehow it's getting the message but ARM9Init is being set back to false?

It works if I remove this line at the end of VBlankInterrupt:
Code:
SendMessage(IPC_SYS_INPUT, 4, Btns, (TouchX << 16) | TouchY, (TouchZ1 << 16) | TouchZ2, touchReadTemperature(&T1, &T2));
Replacing the touchReadTemperature() call with a constant zero didn't help either. Somehow sending a message is breaking something? It receives the message from ARM9 but the flag isn't set or the main loop doesn't get run or something.

I had to enable nesting in the VBlank interrupt or else the FIFO interrupt would be missed and it wouldn't get the message at all. Anything wrong with that?
_________________
I'm a PSP hacker now, but I still <3 DS.