#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):
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:
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.
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; } } |
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)); |
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.