GP32 Simple Threading


pea

developer
Joined
Oct 3, 2004
Messages
1,089
Age
46
Location
New Zealand
Website
www.projectitis.com
Hi guys,

Wondering how to set up simple threading. Without worrying about critical sections etc (for now), is it possible to do the following on an interrupt:

1) Store the current register values
2) Store the current instruction pointer (memory of next instruction)
3) Retrieve a set of different register values
4) Set the instruction pointer to a previously stored value

to jump between two different 'sections' of code?
 
Yes, perfectly possible. Use a timer interrupt and possibly look into CP15 register 13 if your interested in each thread (or process) having it's own chunk of memory.

EDIT: Actually, thinking about it, if you put the stack at a memory location < 32mb in a virtual space, you could use CP15 register 13 to perform a fast context switch :)
 
Yup! That's pretty much the outline of how to do it.

The reality is that you'd use multiple stacks for each process and push their registers to it before saving the stack pointer, then loading another one for the other process and popping from that one before returning from the interrupt.

The interruptted CPU auto-pushes some important stuff, including the return address ;)


I'm not sure how or where GCC or crt.s/start.c sets up the stack in the first place.
You may need to link your own inits.
 
pea posted on Mar 27 2005 at 03:59 AM said:
Hi guys,

Wondering how to set up simple threading. Without worrying about critical sections etc (for now), is it possible to do the following on an interrupt:
...

Yep.. pretty much in that way. Actually Gamepark's SDK does threading. Squidge's idea of using ARM's fast context switch stuff just for stack is actually pretty cool. That does not work out of box though but should not be that hard to make :blink:
 
Last edited by a moderator:
I did my degree (BEng, Information Engineering) on this stuff a few years back, and reading these docs (pdf manual for the GP32 CPU) is bringing it back slowly. Its amazing how much you forget if you don't keep up with the play.

Once I am finished reading, I'm sure to come back with more questions before I attempt this :)

Squidge - what does CP15 mean? (in terms of CP15 register 13)

How would I start a new process? If the new process is (for example) a function, can I simply use the function pointer as the PC? I take it it is not that simple, as it has to do with calling conventions etc?
 
CP15 = CoProcessor 15.

Using the address of a function works fine, very simple if the function doesn't take any parameters.
Thats pretty much how i did it in my threading lib.

If the function does take parameters, you would have to find out how the calling convention works.

And, btw, iirc Loki ported pthreads to GP32 a while ago.. dunno if he released anything however.

---
mithris
 
Nope, the code is far from clean enough to be released..:D

---
mithris
 
mithris posted on Mar 28 2005 at 01:54 PM said:
Nope, the code is far from clean enough to be released..:D
Damn. ;) Do you have any plans to release it in the near future? If one will become available soon I won't need to make my own. :p
 
Last edited by a moderator:
Doing a simple interrupt driven thread splicer is about an hours work. Using the fast context switching of the arm9's mmu is far more cool and faster, but a lot more work.
 
I have been googling for a bit to find out exactly what "fast context switching" is, and how it works. ALl I can find are a lot of articles (and RTOSes) that use fast context switching, or talk about it, but not exactly what it is.

So, can someone give me the low down? :)

EDIT: Is it switching between register banks rather than copying registers backwards and forwards from mem?
 
Thanks Squidge. Reading up on it now :)

EDIT: um, unless I'm not looking properly, this doc also just talks about fast context switching (how to do it by writing to CP15 r13) rather than what it is (I searched for 'fast context'. Comes up in 5 or 6 places).
 
pea posted on Mar 28 2005 at 09:51 PM said:
Thanks Squidge. Reading up on it now :)

EDIT: um, unless I'm not looking properly, this doc also just talks about fast context switching (how to do it by writing to CP15 r13) rather than what it is (I searched for 'fast context'. Comes up in 5 or 6 places).

Hmmm... afaik "fast context switch extension" (FCSE) is ARM specific way of speeding up general context switch (usually due scheduling processes etc). ARM has this FCSE feature of providing 128x 32M virtual memory spaces that can be switched using the CP15 reg (which then also acts as a process id). All processes are using mem between 0x00000000-0x01ffffff. The nice thing with ARM's FCSE is that you don't need to care about flushing caches or changing MMU tables per processes (TLB needs to be flushed in most cases unless domains are used). However, IMBs must be taken care off or nasty bugs will show up.

Dunno if this answered to your "what it is" question.. :blink:
 
Last edited by a moderator:
Thanks spiv :) I guess the next question is 'what is general context switching'? :p

From what I can tell from what you say, it switches the memory space that is being accessed by the CPU etc so that it is actually accessing a different section of memory (called virtual memory) without changing anything else. For example, address 0x00000000 is actually different after the switch.

EDIT: This is what I found about context switching:
Refers to operating systems or operating environments that enable you to switch from one program to another without losing your spot in the first program. Many utilities are available that add task switching to DOS systems.
Note that task switching is not the same as multitasking. In multitasking, the CPU switches back and forth quickly between programs, giving the appearance that all programs are running simultaneously. In task switching, the CPU does not switch back and forth, but executes only one program at a time. Task switching does allow you to switch smoothly from one program to another.

If this is so -
1) can the size of the virtual memory space be changed (seeing as the GP32 only has 8MB, it seems a bit pointless having a 32MB virtual space)
2) Can the size of each virtual space be set arbitrarily
3) Will functions like malloc etc use the correct space (I guess they would - that would be the whole point of it :) )
4) Does this mean that (for example) a process woul not have access to the framebuffer because it is in a different virtual space (is a 'virtual space' called a context?)
EDIT: 5) According to the blurb above, context switching isn't used for multitasking (whicg is what I am after), why not?
 
The size of the virtual space can not be changed, no. It's fixed at 32mb of virtual memory. However, this doesn't mean you need 32mb of physical memory, which is why it's virtual :)

The MVA (Modified Virtual Address) of the translated address is presented to the MMU, so you could, for example, map 1mb to each process (map 0x0 in one process to start of ram, map 0x0 in process 2 to start of ram + 1mb, etc). You can change the size and what parts of memory are available to each process by modifying the MMU table.

Addresses >32mb are not translated, and I believe the GP32's RAM is >32mb in the memory map, so you can allocate the LCD buffer somewhere away from translation.

Depends on how the GP32 lib is implemented - malloc will probably need to be rewritten to use memory <32mb to ensure it gets paged properly. No bigee though.

General task switching is exactly that - it just switches tasks and lets the current task run for however long. Multitasking can simply be context switching at a predefined rate (every millisecond or so for example, which would most likely be perfect for your application, and then you would not require any timers and such, as you would be getting a guaranteed time based anyway).

You can of course get preemptive multitasking too, but I think that's a little over the top for your application.
 
I think I will start by writing my core code without context swapping, and convert it later when I learn more. I will start with a simple SWI example, and move on from this to nested interrupts, and from there to a simple threading model.

When it comes to threading, and I need to store the registers (from within the interrupt handler) to be able to later return to the last state, which of the registers do I absolutely need to save?

EDIT: I'm finding learning all this stuff very interresting, but the docs (ARM920T and samsung microprocessor manuals) assume you already know what everything means. The ARM programming techniques PDF is quite good, but stops before it gets to advanced examples of interrupts :)
 
I'm getting quite confused by some of the terminology etc that comes with reading about a lot of new stuff :) Also by some of the core principles too I guess.

1) What exactly is the stack used for? Is it only used for such things as function calls and other branches (such as interrupts) where the program needs to return afterwards?

2) When I call
Code:
STMDB    r13!,{r0,lr}
To store the registers, where are they stored to?

3) If I want to change the stored PC before it is restored after an IRQ (and also read the stored PC) how can I do this?

I want to implement threading with a single, non-reentrant timer-based interrupt (getting good at the jargon, no? :p ). The actual interrupt handler is very small, and all it does (once inside the handler) is:

1) Read the stored PC (this should be the position of the PC before the interrupt) and store it in the thread list against the currently running thread. Also copy the stored registers to the thread list for the current thread too.
2) Go to the next thread in the list, and copy the PC and register values from there to the stored registers.
3) Now exit the interrupt. If all goes right, the values that are restored to the registers and PC are those for the next thread to be run.

To add a new thread, simply disable interrupts, add the function pointer of the function to be run as a thread to the thread list as the PC, and set the registers of that thread in the thread list to some valid default values. re-enable interrupts.

I thought (for now) that for code I wasn't sure about (such as accessing the smc, or other critical data) I could simply disable interrupts before accessing them, and enable them again afterwards.

Does this all sound doable? Is is it the completely wrong way to go about it? Keep in mind that I am NOT an ARM or ASM guru - most of my programming experience is Delphi, c++ and lately, c (as well as some c64 assembly, Amos on the Amiga, and scripting such as PHP, SQL etc which hardly count :p), and google lacks some good quality tutorials in these areas.

I realise that what I explain above is very similar to context switching, but I can't find any good info on context switching :(
 
I think what you need is a good jtag debugger and then playing around with the arm's mmu and stuff on the gp32 :) You learn most things a lot quicker by experimenting then by spending hours reading other peoples documentation (although it's normally best to do both at the same time...)

The stack can be used as anything, you can place registers on there, data/parameters for routines that you will be calling, interrupts/etc. Note that the arm processor doesn't have a stack as such, but you can use any register in a stack like fashion, so r13 is normally used as a stack pointer. This also means that return addresses and interrupt stuff isn't kept on the stack (as you could any use any register, r13 is just normally used by compilers, and people follow this rule to make it easier to include assembler code in compiled code).

So, STMDB r13!,{r0,lr} just stores r0 and r14 wherever r13 points to, and decrements r13 by 8 bytes (due to the '!'). The 'DB' means decrement.

The return address (next instruction to execute when interrupt has finished) is stored in R14, so you'll need to store this away. Don't forget the different processor modes - IRQ & Supervisor have several registers banked, so you may want to read up on this.

I'll need to type more when I'm not at work, so will need to leave it until tonight.
 
Back
Top