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 > libnds 20070127 fifo irq issues

#116897 - GPFerror - Tue Jan 30, 2007 7:35 pm

"The interrupt dispatcher has now returned to the older method of acknowledging before entering the user handler. The convoluted method was based on a misunderstanding of a situation where it appeared user data was lost if the interrupt flag was cleared first" This is in the latest libnds release notes, maybe its related?

My fifo/irq has stopped working in latest release and not sure how to fix it yet.

here is my arm7 code
Code:

void VblankHandler(void) {
//---------------------------------------------------------------------------------

   u32 i;
   if(REG_IF & IRQ_VBLANK)
   {
      SoundVBlankIrq();


   //sound code  :)
   TransferSound *snd = IPC->soundData;
   IPC->soundData = 0;

   if (0 != snd) {

      for (i=0; i<snd->count; i++) {
         s32 chan = getFreeSoundChannel();

         if (chan >= 0) {
            startSound(snd->data[i].rate, snd->data[i].data, snd->data[i].len, chan, snd->data[i].vol, snd->data[i].pan, snd->data[i].format);
         }
      }
   }
   }
   if(REG_IF & IRQ_FIFO_NOT_EMPTY)
   {
      SoundFifoHandler();
   }
}

and the init code


Code:
   irqInit();
   irqSet(IRQ_VBLANK, VblankHandler);
   SetYtrigger(80);
   vcount = 80;
   irqSet(IRQ_VCOUNT, VcountHandler);
   REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR | IPC_FIFO_RECV_IRQ;
   irqSet(IRQ_FIFO_NOT_EMPTY, VblankHandler);
   irqEnable(IRQ_VBLANK | IRQ_VCOUNT|IRQ_FIFO_NOT_EMPTY);


and my arm9 code
Code:
void InterruptHandler(void)
{
   u32 command,remain;
   //printdbg("InterruptHandler\n");
   if(REG_IF & IRQ_VBLANK)
   {
      framecounter++;
   }
   if(REG_IF & IRQ_FIFO_NOT_EMPTY)
   {
      if(!(REG_IPC_FIFO_CR & IPC_FIFO_RECV_EMPTY))
      {
         command = REG_IPC_FIFO_RX;
         
         switch(command)
         {
         case FIFO_NONE:
            break;
         case UPDATEON_ARM9:
            MixSound();
            SendCommandToArm7(MIXCOMPLETE_ONARM9);
            break;
         }
      }
   }
}

and its init code
Code:
   irqInit();
   irqSet(IRQ_VBLANK | IRQ_FIFO_NOT_EMPTY,&InterruptHandler);
   irqEnable(IRQ_FIFO_NOT_EMPTY);
   
   REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR | IPC_FIFO_RECV_IRQ;


Any ideas on what I need to change it to for r20?

Thanks,
Troy(GPF)
http://gpf.dcemu.co.uk

#116898 - DekuTree64 - Tue Jan 30, 2007 8:00 pm

GPFerror wrote:
"The interrupt dispatcher has now returned to the older method of acknowledging before entering the user handler. The convoluted method was based on a misunderstanding of a situation where it appeared user data was lost if the interrupt flag was cleared first" This is in the latest libnds release notes, maybe its related?

Awesome! That means FIFO interrupts can actually be 100% reliable now (see this post for details).

As for your code, you'll need to check the not empty bit in FIFO control rather than the interrupt flags. Or just give the FIFO its own interrupt handler so you don't have to check it in the first place, which is less confusing anyway.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#116905 - GPFerror - Tue Jan 30, 2007 10:39 pm

arm7 code
Code:

void VblankHandler(void) {
//---------------------------------------------------------------------------------

   u32 i;

   SoundVBlankIrq();

   //sound code  :)
   TransferSound *snd = IPC->soundData;
   IPC->soundData = 0;

   if (0 != snd) {

      for (i=0; i<snd->count; i++) {
         s32 chan = getFreeSoundChannel();

         if (chan >= 0) {
            startSound(snd->data[i].rate, snd->data[i].data, snd->data[i].len, chan, snd->data[i].vol, snd->data[i].pan, snd->data[i].format);
         }
      }
   }

}
void FiFoHandler(void)
//---------------------------------------------------------------------------------
{
   while ( !(REG_IPC_FIFO_CR & (IPC_FIFO_RECV_EMPTY)) )
   {
      SoundFifoHandler();
   }
}


Code:

   irqInit();
   irqSet(IRQ_VBLANK, VblankHandler);
   SetYtrigger(80);
   vcount = 80;
   irqSet(IRQ_VCOUNT, VcountHandler);
   REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR | IPC_FIFO_RECV_IRQ;
   irqSet(IRQ_FIFO_NOT_EMPTY, FiFoHandler);
   irqEnable(IRQ_VBLANK | IRQ_VCOUNT|IRQ_FIFO_NOT_EMPTY);


arm7 SoundFifoHandler and SendCommandToArm9

Code:

void SoundFifoHandler(void)
{
   u32 command;

   if (!(REG_IPC_FIFO_CR & IPC_FIFO_RECV_EMPTY))
   {
      command = REG_IPC_FIFO_RX;
      
      switch(command)
      {
      case FIFO_NONE:
         break;
      case MIXCOMPLETE_ONARM9:
         soundsystem->soundcursor += soundsystem->numsamples;
         if(soundsystem->format == 8)
            while (soundsystem->soundcursor > soundsystem->buffersize) soundsystem->soundcursor -= soundsystem->buffersize;
         else
            while (soundsystem->soundcursor > (soundsystem->buffersize >> 1)) soundsystem->soundcursor -= (soundsystem->buffersize >> 1);
         break;
      }
   }
}

void SendCommandToArm9(u32 command)
{
    while (REG_IPC_FIFO_CR & IPC_FIFO_SEND_FULL);
    if (REG_IPC_FIFO_CR & IPC_FIFO_ERROR)
    {
        REG_IPC_FIFO_CR |= IPC_FIFO_SEND_CLEAR;
    }
   
    REG_IPC_FIFO_TX = command;
}


arm9 code
Code:

int framecounter;
void InterruptHandler(void)
{
   framecounter++;
}
void FiFoHandler(void)
{
   u32 command,remain;
   while ( !(REG_IPC_FIFO_CR & (IPC_FIFO_RECV_EMPTY)) )
   {
      command = REG_IPC_FIFO_RX;

      switch(command)
      {
      case FIFO_NONE:
         break;
      case UPDATEON_ARM9:
         MixSound();
         SendCommandToArm7(MIXCOMPLETE_ONARM9);
         break;
      }
   }
}


Code:
   irqInit();
   irqSet(IRQ_VBLANK,&InterruptHandler);
   irqSet(IRQ_FIFO_NOT_EMPTY,&FiFoHandler);
   irqEnable(IRQ_FIFO_NOT_EMPTY);
   
   REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR | IPC_FIFO_RECV_IRQ;


arm7 SoundFifoHandler and SendCommandToArm9

Code:

void SoundFifoHandler(void)
{
   u32 command;

   if (!(REG_IPC_FIFO_CR & IPC_FIFO_RECV_EMPTY))
   {
      command = REG_IPC_FIFO_RX;
      
      switch(command)
      {
      case FIFO_NONE:
         break;
      case MIXCOMPLETE_ONARM9:
         soundsystem->soundcursor += soundsystem->numsamples;
         if(soundsystem->format == 8)
            while (soundsystem->soundcursor > soundsystem->buffersize) soundsystem->soundcursor -= soundsystem->buffersize;
         else
            while (soundsystem->soundcursor > (soundsystem->buffersize >> 1)) soundsystem->soundcursor -= (soundsystem->buffersize >> 1);
         break;
      }
   }
}

void SendCommandToArm9(u32 command)
{
    while (REG_IPC_FIFO_CR & IPC_FIFO_SEND_FULL);
    if (REG_IPC_FIFO_CR & IPC_FIFO_ERROR)
    {
        REG_IPC_FIFO_CR |= IPC_FIFO_SEND_CLEAR;
    }
   
    REG_IPC_FIFO_TX = command;
}



arm9 SendCommandToArm7
Code:

void SendCommandToArm7(u32 command)
{
    while (REG_IPC_FIFO_CR & IPC_FIFO_SEND_FULL);
    if (REG_IPC_FIFO_CR & IPC_FIFO_ERROR)
    {
        REG_IPC_FIFO_CR |= IPC_FIFO_SEND_CLEAR;
    }
   
    REG_IPC_FIFO_TX = command;
}




Yeah this seems to work correctly now, not sure if its the correct way or not, seems to be broken in Dualis and no$gba, and partially works in Desmume 5, but works fine on hardware :)

Troy(GPF)
http://gpf.dcemu.co.uk

#116935 - wintermute - Wed Jan 31, 2007 4:21 am

This looks fine for basic FIFO goodness but might be a little hard to extend for extra commands.

Something to be aware of here is that the libnds dispatcher defaults to a single interrupt mode where other interrupts cannot be processed until the last one completes. If you have a sound mixer on the ARM9 which might take some time at arbitrary intervals then you should probably enable nesting before jumping into the mixer. This is achieved by simply enabling the master interrupt - REG_IME = 1. The dispatcher already takes care of switching to system stack before user handlers are called so blowing the irq stack isnt an issue.

When you enable nesting in this way you need to take care that your FIFO functions are reentrant or can't be called again until they complete. Another poster suggested disabling the FIFO interrupt temporarily in a scenario like this which is one potential solution. This would mean that you could only process one FIFO command at a time though.

I'm intending to put together some FIFO related functions and build a command system to put in the default arm7 if I ever get the time to finish it.

What I'd like to do is create a command packet system which includes the size of the packet in the first word which the base FIFO handler would use to receive an entire packet before handing off the packet to a subsystem function determined from part of the command ID. This way I could have some commands in a single word which might be used for power management related functions and other complex multi word commands for playing sounds.

Right now I'm not sure about how to handle return values from FIFO related functions. It would be nice to know which channel a sound was played on if we retain the play on free channel system in the current, crude system. This could be done by placing a value in the shared memory somewhere but still leaves the issue of knowing when that value is valid. Perhaps a callback to pick up the return value via FIFO might be useful in this case - as I've said many times I believe the IPC structure should be deprecated in favour of something more reliable.

Any thoughts and suggestions regarding such a system would be appreciated.
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog

#117008 - DekuTree64 - Wed Jan 31, 2007 7:35 pm

wintermute wrote:
Any thoughts and suggestions regarding such a system would be appreciated.

I've been thinking about doing a packet system like that too. Probably could just have the receive handler loop until it gets the whole thing. Not much point in buffering them since the sender will be doing it all in one go anyway.

My current FIFO system only handles single word messages, and uses the upper 4 bits as the message type, and lower 28 as the data. That way you get a reasonable number of types, and enough data to send pointers.

You can either send and wait for processing to complete (uses some counters in shared memory to coordinate), or send and return immediately. The send function returns a handle to the command sent (actually the value of the shared memory counter), which you can pass to another function to check if processing has completed on it.

The packet system could mostly be built on top of that system, using one of the 16 types as "packet", and the 28 data bits to specify the type of packet, and number of words.

A "finished processing" callback for commands (single-word and packets) would probably the best way to send return values. Could do it using the FIFO, but may be easier to use the IPC sync register and shared memory to avoid conflicts.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#117032 - HyperHacker - Wed Jan 31, 2007 9:58 pm

I've been using a simple system that seems to work well. Each command is in the format ccpp0000; cc=command ID, pp=# of parameters. After that comes the parameters, one word each. Currently the system allows for only 4 parameters but it wouldn't be hard to allow more; it waits until space is free before sending each, and the other CPU waits to receive them all before processing them. I haven't yet figured how to implement return values though.
_________________
I'm a PSP hacker now, but I still <3 DS.