#58951 - Mighty Max - Thu Oct 27, 2005 3:28 pm
Hey there,
i have been working the last two days of letting my arm9 do two different functions at the same time.
Currently i just have 2 Threads running max with a simple alternate sheduling (Each IRQ, Threads are switched). Before i move on, i wanted to ask some questions.
Is someone else already working for a thread sheduler for the nds libs ?
If not, would it be possible to get this included when it runs smooth enough?
What would be better on the NDS / for your applications: FIFO/LIFO or SJF/SRPT ?
Any other suggestion?
Sourcecode
_________________
GBAMP Multiboot
Last edited by Mighty Max on Thu Oct 27, 2005 11:37 pm; edited 2 times in total
#58971 - funkaster - Thu Oct 27, 2005 6:51 pm
Mighty Max wrote: |
Is someone else already working for a thread sheduler for the nds libs ?
If not, would it be possible to get this included when it runs smooth enough?
|
I'm porting a scheduler that has many features:
- semaphores
- messages
- mutex
- multicasting
etc... I hope to have it ready soon. Right now I don't have the time to finish it, but I think I'll try to finish it the next week.
It's based on a mini scheduler we used in my Operating Systems course, it's almost ansi-c, except for the context swaping, which is totally machine dependant.
#58973 - GPFerror - Thu Oct 27, 2005 7:17 pm
i attempted to port a threading example from gba to DS, not much luck though :(
here is the link to the post
http://forum.gbadev.org/viewtopic.php?t=6448&highlight=threading
#58978 - Mighty Max - Thu Oct 27, 2005 8:32 pm
Ok, then i'll let it basic, and only for my own SysPro course, which just started last week :D
whoever is interested in it can find the sources on http://mightymax.org/multithreading.html
It is now supporting
- simple cyclic sheduling
- dynamic amount of threads
- stacking CriticalSections
The example shows the creation of 2 subthreads from the main thread.
Each thread counts a var up in its own stack, and displays it on the screen.
PS: I'm still deving on r13 so it will have to be updated on other DevKitARM releases
_________________
GBAMP Multiboot
#58989 - GPFerror - Thu Oct 27, 2005 9:41 pm
very cool, need to add this support to libnds or a simple function calls that can be used by us mere mortals :)
great job
#58996 - sidral - Thu Oct 27, 2005 11:00 pm
some time ago i've read of this protothreads library as something that could be useful for ds programming.
it's not real threads but it's light, with embedded systems in mind and fully implemented in ansi c.
#59002 - Dwedit - Thu Oct 27, 2005 11:23 pm
Protothreads makes me vomit.
_________________
"We are merely sprites that dance at the beck and call of our button pressing overlord."
#59013 - sidral - Fri Oct 28, 2005 12:27 am
so you should look for a paper bag...
#71650 - knight0fdragon - Mon Feb 13, 2006 9:48 pm
what do u do with LocalStorage?? what type is it and where should I place it in threading.c
_________________
http://www.myspace.com/knight0fdragonds
MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206
#72469 - knight0fdragon - Sat Feb 18, 2006 8:07 pm
hmmm for some reason, it cannot find the stuff that is in threading_s.s, i tried adding it to the libnds library, but that doesnt seem to work either
_________________
http://www.myspace.com/knight0fdragonds
MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206
#72478 - GPFerror - Sat Feb 18, 2006 9:20 pm
http://gpf.dcemu.co.uk/MMmultithread.rar source and binaries.
I updated it for the latest NDS, have not tested it on hardware though.
Troy(GPF)
#72480 - sajiimori - Sat Feb 18, 2006 10:02 pm
Dwedit,
What makes me nauseous is code that is uglier than it needs to be because the author was unwilling to use a technique that he considered distasteful. Try making this function look as good without allocating a separate stack:
Code: |
// AI routine that moves a character around slightly until the collision
// code can find a point on the floor underneath it. Useful when a
// character is standing in an awkward spot, like between two planks.
// Returns true for success, false for failure, and indeterminate if
// the operation hasn't completed yet.
tribool WanderUntilFloorFound::operator()()
{
begin();
// 'me' is a pointer to the character that is being controlled.
if(!me)
terminate(false); // Fail if there's nobody to control.
// 4 tries before failure.
for(i = 0; i < 4; ++i)
{
// Pick which direction to go and how long.
dir = randomAngle();
time = randomInRange(8, 30);
while(time--)
{
if(canFindFloorBeneath(me))
terminate(true); // Success.
// Take a single step and wait a tick.
me->walk(dir);
yield();
}
}
// If the routine hasn't finished by now, it automatically
// terminates with 'false'.
end();
}
|
The above code uses a kind of protothreads. With dozens of AI characters in a level, indiscriminate allocation of stacks is not an option.
#72499 - knight0fdragon - Sun Feb 19, 2006 3:54 am
has anyonebeen able to get this to work on hardware, it locks up for me when it hits createthread
_________________
http://www.myspace.com/knight0fdragonds
MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206
#72520 - Durandle - Sun Feb 19, 2006 2:40 pm
Hmm threading support = SDL threading = porting 100% easier :P
#72523 - TheChuckster - Sun Feb 19, 2006 3:00 pm
There still needs to be a context switcher that is specific to the ARM processor.
#72537 - GPFerror - Sun Feb 19, 2006 5:51 pm
TheChuckster wrote: |
There still needs to be a context switcher that is specific to the ARM processor. |
I believe that the code in Mighty Max threading coding has a context switcher, Im not that familiar with arm asm so Im just guess thats what the code does in the threading_s.s file.
Troy(GPF)
#72551 - Mighty Max - Sun Feb 19, 2006 8:47 pm
Heya,
the context saving & restoring is indeed done in the threading_s.s to a buffer located within the code (so it can be referenced via cp without changing&invalidating any register)
The actual switching is then done in the c handler for the interrupt that is called immediately after the buffer is filled. Once this handler returned, the same buffer is used again to build the cpu context for returning from the interrupt state.
A new thread is created as a copy of the current context (again in the interrupt handler), so that the next interrupt will return twice to the same address. Counting this event, one jumps to the new thread address and assigns a new stack and the other returns along the existing lr & stack.
I'm not exactly familar anymore with the details how i implemented this, since it's been some time and my job & studies soaked up my time the last months. (Still having devkit R13 on the disc ;) )
_________________
GBAMP Multiboot
#72651 - GPFerror - Mon Feb 20, 2006 9:17 pm
Durandle wrote: |
Hmm threading support = SDL threading = porting 100% easier :P |
yeah also sound support is currently impossible without threads as well.
Troy(GPF)
#72663 - DekuTree64 - Mon Feb 20, 2006 10:08 pm
Why does sound require threading? Shouldn't it just be a matter of setting up a timer on ARM7 to count samples played, and every x samples, generate an IPC interrupt to call the SDL sound handler on ARM9?
Or for less sporadic timing, just accumulate samples and mix however many are needed at the end of your VBlank handler.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku
#72671 - GPFerror - Mon Feb 20, 2006 10:39 pm
its more I would have to completly rewrite SDL, currently they way its written is sound is a seperate thread. that sits around and waits for a signal that the previous sound buffer has completed then it sends the next one. thats the cross platform part of the code lol
#72672 - tepples - Mon Feb 20, 2006 10:49 pm
If you could refactor SDL's sound thread to look like this, then you could probably port it:
Code: |
while(!quit) {
doSomething();
waitForPlayedSignal();
}
|
You'd port it by setting doSomething as the ISR.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#72686 - GPFerror - Tue Feb 21, 2006 12:43 am
Yeah Im looking into a rewrite.
I still need a magic function that I could call that would tell me when my previous played buffer was completed. If I start the sound at a certain point shouldnt there be some kind of math calculation that would be approximetly tell me how many ms it would take to play the buffer? Then I could just check the number of ms(timer based) that have occured and compare it to this calculation?
Unfortunetly rewriting the audio thread doesnt help the SDL_threading/mutex/semaphore/condition api :)
Troy(GPF)
#72764 - GPFerror - Tue Feb 21, 2006 3:39 pm
oops kind of went of topic with sound :)
knight0fdragon wrote: |
has anyonebeen able to get this to work on hardware, it locks up for me when it hits createthread |
Yeah that is where it seems to lock up in all the emulators as well.
Troy(GPF)
#73333 - Mighty Max - Fri Feb 24, 2006 9:13 pm
I don't know if i had updated the web version with the last worked on version,
i had a problem with mixing thumb and arm32 instructions on create thread that were fixed short before i had to leave.
I'll take a look and upload if it is that problem tomorrow.
_________________
GBAMP Multiboot
#73387 - GPFerror - Sat Feb 25, 2006 6:08 am
great looking forward to it, hopefully it can be updated for latest libnds and interupt based thread scheduler is just what im looking for :)
Troy(GPF)
#73405 - Mighty Max - Sat Feb 25, 2006 11:25 am
Yeah, it is indeed that problem:
When CreateThread(...) is compiled as thumb it will crash the multithreader.
I can't find my backup discs with the multithreading sources atm, but the build in threader of the multiboot has had the same changes, so ill just copy & paste them here, so you can make the changes:
In the CreateThread replace
Code: |
while (1) {
if (!subCalled && (activeThread == subThread)) {
// the subthread is now running, as a copy of the creator
// so set what makes this thread individual
// Stack & InstructionPointer
subCalled = activeThread ;
asm("mov r13,%0\n"\
"mov r14,%1\n"\
"BX r14 \n"
::"r" (stack),"r" (target)) ;
} ;
|
with
Code: |
while (1) {
if (!subCalled && (activeThread == subThread)) {
// the subthread is now running, as a copy of the creator
// so set what makes this thread individual
// Stack & InstructionPointer
subCalled = activeThread ;
CreateThreadHelper(stack,target) ;
} ;
|
The helper function has been defined in the threading_s.s:
Code: |
.global CreateThreadHelper
CreateThreadHelper:
MOV r13,r0
MOV r14,r1
BX r14
|
This way the stack & return address swap is done in arm32 and therefor doesnt fail if CreateThread is called in/from thumb regions.
Greets
Mighty Max
PS: I should work on the CriticalSection functions to secure them against race effects. Atm they don't use atomic instructions and are therefor not safe against this kind of errors. So if you keep getting random hangups of single threads, please let me know to set some priority to this.
_________________
GBAMP Multiboot
#73410 - knight0fdragon - Sat Feb 25, 2006 1:33 pm
looks good, be sure to add void CreateThreadHelper(int stack,int target) ; to threading_s.h
when theres 1 thread, no hang ups, mutliple = hangups.
_________________
http://www.myspace.com/knight0fdragonds
MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206
#73411 - Mighty Max - Sat Feb 25, 2006 2:12 pm
Hellas again,
i composed a quick fix for the critical section flaw. It uses an active waiting (not queueing) mutex to safe the access to the critical section counter. This mutex is now atomic and can't be interupted by an IRQ (and therefor by the thread switch)
Code: |
/*
Mutex
Mutex here are active wait and only accept access or no access pending status
*/
void AcquireMutex(unsigned long *mutex)
{
unsigned long preStatus = 1 ;
while ((preStatus = AtomicSwap(1,mutex))==1) {
// wait till our swap removed a non 1 (InAccess) and stores a 1 there
} ;
} ;
void ReleaseMutex(unsigned long *mutex)
{
AtomicSwap(0,mutex) ;
} ;
/*
Within a critical section no thread switch will occure
Critical sections can be encapsuled by another
*/
unsigned long criticalSectionMasterMutex = 0 ;
void EnterCriticalSection(void)
{
AcquireMutex(&criticalSectionMasterMutex) ;
criticalsCount++ ;
ReleaseMutex(&criticalSectionMasterMutex) ;
}
void LeaveCriticalSection(void)
{
if (criticalsCount) {
AcquireMutex(&criticalSectionMasterMutex) ;
criticalsCount-- ;
ReleaseMutex(&criticalSectionMasterMutex) ;
}
} ;
|
where in threading_s.s
Code: |
.global AtomicSwap
AtomicSwap:
SWP r0,r0,[r1]
BX r14
|
Code: |
unsigned long AtomicSwap(unsigned long newValue,unsigned long *memoryLocation) ;
|
This code however is not tested, i don't have my equipment here atm.
---
@KnightOfDragons:
Where and what exactly hangs up?
_________________
GBAMP Multiboot
#73421 - knight0fdragon - Sat Feb 25, 2006 5:25 pm
when there are more then 1 thread, it will hit the first semaphore, then print thread A, then lock
_________________
http://www.myspace.com/knight0fdragonds
MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206
#73491 - GPFerror - Sun Feb 26, 2006 2:05 am
yeah its crashing on me also, I just havent had enough time to debug where yet.
thanks for the updates.
Troy(GPF)
#74317 - knight0fdragon - Sat Mar 04, 2006 8:47 am
is there some sort of alternative to this code here
REG_IME = 0;
IRQ_HANDLER = &IRQ_Entry;
REG_IE = IRQ_VBLANK;
REG_IF = ~0;
DISP_SR = DISP_VBLANK_IRQ;
REG_IME = 1;
this seems to screw up with the wifi stuff when it wants to wait on the ARM7
i thought it was something like irqSet(IRQ_VBLANK, IRQ_Entry);, but it turns out its not and i tried playing around with other methods but for some reason either the wifi will lock at ARM7 not being init or no threading whatsoever
_________________
http://www.myspace.com/knight0fdragonds
MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206
#74319 - Mighty Max - Sat Mar 04, 2006 9:25 am
Code: |
REG_IME = 0;
IRQ_HANDLER = &IRQ_Entry;
REG_IE = IRQ_VBLANK;
REG_IF = ~0;
DISP_SR = DISP_VBLANK_IRQ;
REG_IME = 1;
|
For the multithreader the IRQ_Entry has to be the first called irq handler. so the default interupt handler can't be used to run the sheduling irq handler.
You can however install the IRQ_Entry like in the code above (you might want to enable more IRQ Events REG_IE = IRQ_VBLANK | theotherirqs ; ) and set the default handler as the custom irq handler via a call to
void SetIRQHandler(void *handler) ;
of the CIRQ.h
_________________
GBAMP Multiboot
#74333 - knight0fdragon - Sat Mar 04, 2006 4:30 pm
ok im not really following,
heres what i want to do
i have vblank as my interrupt handler,
i use no other interrupt on arm9,
and i have IRQ_Entry(); as the first function called in it
now it all works well and good when i use the code
Code: |
REG_IME = 0;
IRQ_HANDLER = &vblank;
REG_IE = IRQ_VBLANK;
REG_IF = ~0;
DISP_SR = DISP_VBLANK_IRQ;
REG_IME = 1; |
and the threads work fine but the wifi locks up,
now if i use
Code: |
Initialise the interrupt system
irqInit();
install our simple vblank handler
irqSet(IRQ_VBLANK, &vBlank);
enable the interrupt
irqEnable(IRQ_VBLANK); |
the wifi works and the threads lock up. What exactly is the difference between these 2 that would cause this effect
_________________
http://www.myspace.com/knight0fdragonds
MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206
#74343 - Mighty Max - Sat Mar 04, 2006 5:34 pm
The IRQ_Entry of threading_s MUST be called from within the rom (bios) region like it does when it is the primary installed irq handler. If you use irqSet then there is a another function call inbetween there, and that leads to the threader failing to backup&restore the correct context and sheduling at other IRQs
Code: |
IRQ Requested -> BIOS Handler -> Default Handler -> specific event handler (i.e. Threading Handler @ vblank, wifi, ...)
|
What you can do to get this working:
1. init everything for the wifi lib to work
2. disable IRQs
3. save the current IRQ handler to some temp var
4. set the IRQ_Entry as the new IRQ handler
5. set the saved handler via SetIRQHandler() to set it into the chain again
6. enable IRQs
The IRQ handling should then work this way:
Code: |
IRQ Requested -> BIOS Handler -> Threading Handler -> Default Handler -> specific event handler (i.e. vblank, wifi, ...)
|
_________________
GBAMP Multiboot
#74349 - GPFerror - Sat Mar 04, 2006 6:24 pm
knight0fdragon wrote: |
ok im not really following,
heres what i want to do
i have vblank as my interrupt handler,
i use no other interrupt on arm9,
and i have IRQ_Entry(); as the first function called in it
now it all works well and good when i use the code
Code: | REG_IME = 0;
IRQ_HANDLER = &vblank;
REG_IE = IRQ_VBLANK;
REG_IF = ~0;
DISP_SR = DISP_VBLANK_IRQ;
REG_IME = 1; |
and the threads work fine but the wifi locks up,
now if i use
Code: | Initialise the interrupt system
irqInit();
install our simple vblank handler
irqSet(IRQ_VBLANK, &vBlank);
enable the interrupt
irqEnable(IRQ_VBLANK); |
the wifi works and the threads lock up. What exactly is the difference between these 2 that would cause this effect |
can you post your fixed source for this? Iv broken something and cant get anything to work.
thank,
Troy(GPF)
#74379 - knight0fdragon - Sat Mar 04, 2006 10:00 pm
What you can do to get this working:
1. init everything for the wifi lib to work
2. disable IRQs
3. save the current IRQ handler to some temp var
4. set the IRQ_Entry as the new IRQ handler
5. set the saved handler via SetIRQHandler() to set it into the chain again
6. enable IRQs
ok everything in the red i have done, now the things in the blue im not actually sure on the correct commands to do that and i cant find anything in the forums regarding it
is that how i disable them?
so the code would be
Code: |
// Initialise the interrupt system
irqInit();
// install our simple vblank handler
irqSet(IRQ_VBLANK, &vBlank);
REG_IME = 0;
tempHandle = &IRQ_HANDLER;
IRQ_HANDLER = &IRQ_Entry;
REG_IE = IRQ_VBLANK;
REG_IF = ~0;
DISP_SR = DISP_VBLANK_IRQ;
REG_IME = 1;
// enable the interrupt
irqEnable(IRQ_VBLANK);
// Set the user define interupt handler
SetIRQHandler((void *)&tempHandle);
|
_________________
http://www.myspace.com/knight0fdragonds
MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206
#74389 - Mighty Max - Sat Mar 04, 2006 11:54 pm
knight0fdragon wrote: |
is that how i disable them?
|
Yes
Quote: |
so the code would be
Code: |
// Initialise the interrupt system
irqInit();
// install our simple vblank handler
irqSet(IRQ_VBLANK, &vBlank);
REG_IME = 0;
void *tempHandle = (void *)IRQ_HANDLER;
IRQ_HANDLER = &IRQ_Entry;
REG_IE = IRQ_VBLANK;
REG_IF = ~0;
DISP_SR = DISP_VBLANK_IRQ;
// enable the interrupt
irqEnable(IRQ_VBLANK);
REG_IME = 1;
// Set the user define interupt handler
SetIRQHandler(tempHandle);
|
|
I corrected the above code a bit. IRQ_HANDLER is a fix reference to the handler's addr. So &IRQ_HANDLER would be constant. You want to save & reuse its value instead. - Placed the enable after the complete handler swap.
This should work.
Remember to remove the call to the Threading IRQ Handler from yout vblank code.
PS: I know that this is horrible coding unfriendly atm, i'll check that when i got my hardware back and i'm able to test anything again.
_________________
GBAMP Multiboot
#74397 - knight0fdragon - Sun Mar 05, 2006 1:08 am
hmm it will thead for a few seconds then lock on me
_________________
http://www.myspace.com/knight0fdragonds
MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206
#74401 - knight0fdragon - Sun Mar 05, 2006 1:33 am
Code: |
#include <nds.h>
#include <nds/arm9/console.h> //basic print funcionality
#include <malloc.h>
#include <stdio.h>
#include <string.h>
extern "C" {
#include <nds/C_IRQ.h>
#include <nds/threading.h>
#include <nds/threading_s.h>
}
void wait()
{
while(DISP_Y!=192);
while(DISP_Y==192);
}
//---------------------------------------------------------------------------------
void InterruptHandler(void) {//---------------------------------------------------------------------------------
// Acknowledge interrupts
REG_IF = REG_IF;
}
LPSEMAPHORE consoleAccess = 0 ;
LPTHREAD t1 ;
void Thread1(void) {
int i = 0 ;
while (1) {
i++ ;
printf("Thread A: %i\n",i) ;
}
} ;
//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------
WAIT_CR=0xe800;
wait();
POWER_CR = POWER_ALL_2D ;
// Graphical Screen for logo & addinfos
videoSetMode(MODE_5_2D | DISPLAY_BG3_ACTIVE);
vramSetMainBanks(VRAM_A_MAIN_BG_0x6000000, VRAM_B_MAIN_BG_0x6020000,
VRAM_C_SUB_BG , VRAM_D_LCD);
SUB_BG0_CR = BG_MAP_BASE(31);
BG_PALETTE_SUB[255] = RGB15(31,31,31); //by default font will be rendered with color 255
//consoleInit() is a lot more flexible but this gets you up and running quick
consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31), (u16*)CHAR_BASE_BLOCK_SUB(0), 16);
printf("Testapplication Threading\n") ;
printf("- Mighty Max -\n\n") ;
// Set up the multithreader interrupt handler
// Initialise the interrupt system
irqInit();
// install our simple vblank handler
irqSet(IRQ_VBLANK, &InterruptHandler);
REG_IME = 0;
void *tempHandle = (void *)IRQ_HANDLER;
IRQ_HANDLER = &IRQ_Entry;
REG_IE = IRQ_VBLANK;
REG_IF = ~0;
DISP_SR = DISP_VBLANK_IRQ;
// enable the interrupt
irqEnable(IRQ_VBLANK);
REG_IME = 1;
// Set the user define interupt handler
wait() ;
SetIRQHandler((void *)&tempHandle);
// Set the user define interupt handler
SetIRQHandler((void *)&InterruptHandler) ;
// Create a semaphore for console accesses, so that a position/write can be
// executed without any other thread to jam that
consoleAccess = CreateSemaphore() ;
// Create 2 subthreads:
t1 = CreateThread((long)&Thread1,(long)malloc(1024)+1024) ;
// this is still our main, we can work in too
int i = 0 ;
while (1) {
i++ ;
printf("Main: %i\n",i) ;
}
return 0;
}
|
this is the code that i am trying to get working right now, before i insert it into my project that is
For some reason its still not working unless i disable irqinit,irset, and irqenable
(also i placed the code into libnds so use the source provided above to actually get it to work)
_________________
http://www.myspace.com/knight0fdragonds
MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206
#74492 - Mighty Max - Sun Mar 05, 2006 12:13 pm
If this threads longer then 60ms, then there has to be some other troublemaking effect.
You should remove the line irqEnable(IRQ_VBLANK) as this is already done in the code REG_IE = IRQ_VBLANK;
As well as
Code: |
SetIRQHandler((void *)&tempHandle);
// Set the user define interupt handler
SetIRQHandler((void *)&InterruptHandler) ;
|
is double done. Remove the second.
But as said, i can't test where or why it hangs really. I don't know what changed on the init before the main() call and i don't know if other firmware versions handle the irq entry different (i.e. stacksize at irq entry) now.
I highly suggest to use a select() and other non blocking means to create a pseudo threading effect till i can check again what went wrong.
What you however could do is to add some lines in the C part of the irq threading handler to check whether IRQ_HANDLER was changed from another pos or the return address within the irq stack storage moved outside your apps code.
_________________
GBAMP Multiboot
#75343 - GPFerror - Sun Mar 12, 2006 9:15 am
Has anyone got this working? If so can you post the complete code please.
Thanks,
Troy(GPF)
#75387 - Mighty Max - Sun Mar 12, 2006 5:16 pm
I think i found the major bastards, but im testing this right now.
At least the interrupt handler does not randomly crash dualis anymore.
If i'm correct, it was a stack issue in combination in now more stack consuming iprint's
Ill update this post later with the results
_________________
GBAMP Multiboot
#75388 - wintermute - Sun Mar 12, 2006 5:27 pm
You're not going to get this working with this approach at all.
Consider this.
In order to switch between threads you need to perform a context switch. This requires that all the registers are saved for the current thread, including the flags and PC. You then need to load all the registers for the thread you wish to switch to.
The interrupt dispatcher is not the first piece of code to be run when an interrupt occurs. In both GBA and DS the processor will first execute the BIOS handler which then jumps through the vector location to the dispatcher. Several registers have already been changed at this point.
In order to perform a context switch you need to know the precise state of the stack at that point - a disassembly of the BIOS code will help immensely here.
I haven't looked at the BIOS code as yet but I suspect that the stack frame will need to be saved and then changed in order to return to a new thread.
In order to do all this properly further newlib patches will need to be applied and the libraries rebuilt to understand threading. As I'm sure you'll appreciate this will then annoy people using devkitARM for other platforms where threading has not yet been implemented.
This is a huge job and not one I'm about to undertake lightly. It will involve large changes to the toolchain and all the libraries currently being used.
I need to do some further investigation on how this task will affect devkitARM users - at this point roughly 4000 of them spread among gp32, gba, ds, gp2x and various ARM experimentor boards.
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog
#75407 - Mighty Max - Sun Mar 12, 2006 7:22 pm
That code already worked, and it still does with the Multiboot which was compiled under R13.
I don't know what exactly changed that caused it to break. But i'm eager to get it back working.
The stack that comes with the IRQ handling is indeed saved and restored with the context switching, so that was never a problem.
Creating a new thread is split into two parts that makes it independend on what the primary IRQ-Handler actually does. In the handler, it is just creating a copy of the current context (including the stackframe).
Having the same return twice now, one attaches the new thread's stack & jumps, the second returns to the caller.
_________________
GBAMP Multiboot
#75430 - Mighty Max - Sun Mar 12, 2006 9:22 pm
Got it running now proper again on DeSmuME,
I hope it will work on hardware as i can't test it right now as allready mentioned.
There are several points in the code i'm not happy with and will definately need an update, but for now it is working again.
I will cleanup & fix more things within the week. Expect it on the next weekend.
http://mightymax.org/MultiThreading.rar
Compiled under the latest devkit & running under DeSmuME 0.33
[Images not permitted - Click here to view it]
_________________
GBAMP Multiboot
#75470 - GPFerror - Mon Mar 13, 2006 5:29 am
yeah i had someone verified it ran on his hardware , I have sent mic a message about getting support added to dualis as well.
Troy(GPF)
http://gpf.dcemu.co.uk
#75609 - GPFerror - Tue Mar 14, 2006 5:11 pm
ok, Mic has fixed dualis and it should work in the next version :)
Troy(GPF)
http://gpf.dcemu.co.uk
#75616 - Chris Holmes - Tue Mar 14, 2006 6:59 pm
Maybe I don't understand the reasoning behind it, but why is there such effort to build threading onto either a 33 mhz or 66 mhz arm core? True threading can't be implemented without either disabling all interrupts or with a test-and-set function (I don't know if these ARM cores even support that operator).
When you're dealing with such limited resources to begin with, the overhead of context switching is going to kill any of your gains.
Chris
#75620 - tepples - Tue Mar 14, 2006 7:49 pm
Chris Holmes wrote: |
Maybe I don't understand the reasoning behind it, but why is there such effort to build threading onto either a 33 mhz or 66 mhz arm core? |
So that SDL, PC games that use SDL, and other PC games that rely on threads can be more easily ported without having to heavily modify them to make use of cooperative threading (i.e. timer listeners that return quickly).
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#75623 - Chris Holmes - Tue Mar 14, 2006 7:54 pm
The SDL issue was only a problem with sounds as far as I could tell. Someone suggested polling something with the timer interrupt and faking threads that way. That sounds like a valid solution to that problem.
As far as PC games, well, any PC game that will run on a 66 mhz ARM core almost certainly won't have threads in it.
But hey, if people want threads, good luck with that.
Chris
#75850 - Mighty Max - Thu Mar 16, 2006 4:10 pm
To the test-and-set:
The arm supports an interlocked swp instruction (atomic), that no other entity on the system bus can break. This swp can easily be utilized for an test-and-set and this way for a race secure mutex.
Every other synch structure (Critical regions, semaphores etc) can be implemented with flagging these structures via this mutex.
On some parts tho, i didn't go carefull and that are the places i don't want this way and which will change (i.e. CreateThread uses a non race secure flagging in the public version, Deadlock's are unhandled, ...)
On the other hand, if threading is needed or not for someone's project, i don't know. It is simply another way of doing things. But whereever i see more then one CPU, there is a reason for it imho. I don't care on what CPU my math routine runs, as long as the result comes in time. Load balancing is just another step after threading both CPU's
I started this project parallel to hearing my system programming course, but had to abandon this for some time due to heavy load *g*
Whoever wants to use threading, has the choice, whoever doesn't want to, doesn't need to.
PS: Thanks mic!
_________________
GBAMP Multiboot
#76029 - Mighty Max - Fri Mar 17, 2006 9:09 pm
Heya,
I've done some investigation to things that might help seperating bugs. Unfortunally, this has to be tested on hardware which i atm have no access to.
Would be great if anyone could report me if it shows "Breakpoint!" or if it just crashes.
http://mightymax.org/Breakpoint_Test.nds
It should install an exception handler for undefined instructions & causes a undefined instruction exception. Hope this works *g*
Thanks in advance
Mighty Max
_________________
GBAMP Multiboot
#76113 - LiraNuna - Sat Mar 18, 2006 9:49 am
White screens.
#76117 - Lino - Sat Mar 18, 2006 10:31 am
Mighty Max wrote: |
Heya,
I've done some investigation to things that might help seperating bugs. Unfortunally, this has to be tested on hardware which i atm have no access to.
Would be great if anyone could report me if it shows "Breakpoint!" or if it just crashes.
http://mightymax.org/Breakpoint_Test.nds
It should install an exception handler for undefined instructions & causes a undefined instruction exception. Hope this works *g*
Thanks in advance
Mighty Max |
Where is the source code? Please.
#76118 - Mighty Max - Sat Mar 18, 2006 10:37 am
I'm not too sure on the address (0x27FFDA4), because i did it pen&paper wise.
Code: |
void SetBreakpointHandler(unsigned long addr) {
*(unsigned long*)(0x27FFDA4) = addr ;
} ;
void Breakpoint(void) {
iprintf("Breakpoint!") ;
while (1) ;
} ;
void CauseBreakpoint(void) {
typedef void (*bpt)(void) ;
unsigned long undef[2] = { 0xE6000010,0xE25EF004 } ;
// == undefined instruction, SUBS r15,r14,#4 (return)
bpt f = (bpt)&undef[0] ;
f() ;
iprintf("End of breakpoint causing function reached: HALT") ;
while (1) ;
} ;
within main:
// Test the new breakpoint feature:
SetBreakpointHandler((unsigned long)&Breakpoint) ;
CauseBreakpoint() ;
|
_________________
GBAMP Multiboot
#76166 - Mighty Max - Sat Mar 18, 2006 9:27 pm
I think i misscalculated the address on my first go through.
I have uploaded two new test files http://mightymax.org/Test_b.nds and http://mightymax.org/Test_c.nds
The first one runs on Dualis, and just redoes the steps i expect manually. This is(was) for testing if i got it right with the address and the idea what the code does.
The second one is setting the exception vector & causing an undefined instruction. tho now a bit more safe. if it fails, it returns *g*
Thanks for everyone who checks it.
On Success the sceen should have one line on it:
On Failure the screen will show
Code: |
#End of breakpoint causing func
tion reached: HALT
|
_________________
GBAMP Multiboot
#76800 - HyperHacker - Fri Mar 24, 2006 10:29 pm
Both worked on my DS via WMB.
#77907 - Dark Knight ez - Mon Apr 03, 2006 11:51 pm
Hey all,
I'm trying to implement multithreading, but ran into a problem... and that is that the multithreading conflicts with my current implementation of my program (they both try to use VBlank-interrupt?).
I noticed in the multithread-topic that someone else had the same(?) issue, but using his altered code makes my entire program crash (doing nothing after running the "setting interrupt-handler" code), so I doubt that's the solution. (Using the original code of the example multithreading program does work, but my arm7-vblank handler doesn't operate anymore when using that, thus prohibiting my program to work.)
The altered code (with fixes provided by MightyMax [see page 3 of this thread]):
Code: |
// Set up the multithreader interrupt handler
// Initialise the interrupt system
irqInit();
// install our simple vblank handler
irqSet(IRQ_VBLANK, &InterruptHandler);
REG_IME = 0;
void *tempHandle = (void *)IRQ_HANDLER;
IRQ_HANDLER = &IRQ_Entry;
REG_IE = IRQ_VBLANK;
REG_IF = ~0;
DISP_SR = DISP_VBLANK_IRQ;
REG_IME = 1;
// Set the user define interupt handler
wait() ;
SetIRQHandler((void *)&tempHandle); |
What would be wrong in this code?
And if it is true that VBLANK is used for (the start of) multithreading as well, would it not be possible to (for instance) use another interrupt to trigger the multithreading instead of VBLANK, and cause _that_ interrupt to happen/trigger at the end of the already existing VBLANK handler?
If the problem can't be determined this way, I'll post my code, or snippets thereof.
Thanks in advance for any help.
Greets,
Dark Knight ez.
#77967 - Mighty Max - Tue Apr 04, 2006 7:57 am
Dark Knight ez wrote: |
Code: | // Set up the multithreader interrupt handler
// Initialise the interrupt system
irqInit();
// install our simple vblank handler
irqSet(IRQ_VBLANK, &InterruptHandler);
REG_IME = 0;
void *tempHandle = (void *)IRQ_HANDLER;
IRQ_HANDLER = &IRQ_Entry;
REG_IE = IRQ_VBLANK;
REG_IF = ~0;
DISP_SR = DISP_VBLANK_IRQ;
REG_IME = 1;
// Set the user define interupt handler
wait() ;
SetIRQHandler(tempHandle); |
|
I corrected the code. (Last Line)
tempHandle is already &Function. Using &tempHandle will make the interrupt handler jump into your variable and not the function. (&PointerToFunction)
_________________
GBAMP Multiboot
#77984 - Dark Knight ez - Tue Apr 04, 2006 11:05 am
Thank you. Should've noticed that one. It works again after that (displaying like it should), but ARM7/ARM9 communication is gone after that (which uses IPC and interrupts).
Not having X/Y buttons available, no touch screen, and no music is not an option, so I'm going to try something else, more elegant and more time consuming. ;)
Thanks for the help though. Appreciate it.
#80544 - aitotat - Sun Apr 23, 2006 10:55 am
Here is my attempt for multithreading on Nintendo DS:
http://koti.mbnet.fi/aitotat/Ohjelmat/NDS_multithreading.zip
Zip contains three different examples where rectangles are moving. Rectangles are each in its own thread.
If you're going to try them on an emulator, they will only run on iDeaS.
_________________
http://kotisivu.dnainternet.net/ttilli/