Dual Processor Goodness :)


Squidge

Certified Guru
Joined
Nov 16, 2003
Messages
8,493
Location
UK
Website
Visit site
Ok, the secondary processor seems like an absolute doddle to use for data processing. Wrote some simple asm and got it working under Linux without any problems.

Now, time to try and hack up a compiler and get some GCC compiled code to work on it, and then try and access the hardware.

EDIT: Hmmm, the frame buffer in Linux is in just the right location to be directly accessed by the second core :) (probably setup that way so direct mpeg decoding to the frame buffer is possible). I've done a few frame buffer effects using the second core - basically, read, modify, write, while other apps run completely unaware on the first core :)

Me like :)

One thing that has been learnt from this however is that Linux will only ever be able to be built with 32MB of memory support whilst the mpeg decoding routines are on the second core. The reason for this is that MagicEyes placed the 16MB arm940 buffer base at 0x02000000 (exactly 32MB) and the 1MB code base at 0x03000000 (48MB). This means that 49MB -> 63MB (~14MB) is basically wasted. The only way of getting this back is to compile a kernel without hardware mpeg support.

Now, if GPH decided on a similar setup as the GP32 (just a launcher - no OS as such), then the memory would only be used when you opened the movie player, rather than all the time...
 
It works under linux? Ohh tempting me away from raw hardware.

I have 7 more units now, but only 3 screens, but if anyone wants one give me a shout.

-Craig
 
Squidge posted on Oct 19 2005 at 10:29 AM said:
Ok, the secondary processor seems like an absolute doddle to use for data processing. Wrote some simple asm and got it working under Linux without any problems.

mmapping the code for 2nd core again?
 
Last edited by a moderator:
Sounds cool Squidge :)

As always, how about some details? How are you starting the 2nd core, or is it always running? A peek at your "hello world from the 2nd core" code would be nice :D
 
craigix posted on Oct 19 2005 at 09:37 AM said:
It works under linux? Ohh tempting me away from raw hardware.

I have 7 more units now, but only 3 screens, but if anyone wants one give me a shout.

-Craig

You keep tempting! So hard to resist!

If you want to send me a dev board with screen, I'll see what I can do with it (but no promises); ie: I'll see about getting CaSTaway and Manticore ready up front. If not, I'll wait for my standard unit to arrive :)

Really, I'm hoping we can fork another thread onto the other CPU, or somesuch; I imagine it should be possible to do it lazily, with care:

typedef struct {
...
} my_data_t;

my_data_t shared_vars;

// myfunc is a pointer to function to clone to other CPU space; it must not
// refer to variables in the main thread directly, but via user supplied pointer instead
fireup_other_cpu ( &myfunc, UInt32 size, void *ref_ptr );

ex: fireup ( &myfunc, MYFUNC-SYMBOL-SIZE, &sshared_vars );

Damn, I dunno, I'm totally burnt out :) The above would just wrap a memcpy with some stack muckering :p Theres a dozen better ways to do it :p

jeff
 
Last edited by a moderator:
Without ever having looke dinto the CPU spec/docs, how do you manage the state of the second core? You can't just copy the opcodes/etc into the right area and the CPU fires up? Is that area normally NOPs and the CPU chugs away, or does the CPU need to be activated, interupted, etc?

Show us the test code :)

jeff
 
skeezix posted on Oct 19 2005 at 08:12 PM said:
Without ever having looke dinto the CPU spec/docs, how do you manage the state of the second core? You can't just copy the opcodes/etc into the right area and the CPU fires up? Is that area normally NOPs and the CPU chugs away, or does the CPU need to be activated, interupted, etc?

Show us the test code :)

jeff

You can define the memory area the 940t will So in use.. then you also have a register to reset/stop the 940t. So in paper that is pretty straight forward. :blink:
 
Last edited by a moderator:
Ok, not very pretty and loads of useless shit, but I'm sure you can find the interesting info.

main4linux.c - linux source code for uploading/executing code to 940
main940.c - code running on the 940
crt0-940.s - c startup for 940
a.h - main940's object code in hex
 
Squidge,

Does this mean that (as I understand it) that we have the full use of the 16MB static map the ME gives us for our 2nd processor goodness? :)

I like the sournd of this more and more already.

My kernel hacking seems to confirm your thoughts on the RAM situaltion, without some creative kernel hacking that is well beyond my skills that top 16MB is dead if you want video support in your kernel. Linux mode :eek:.

0-32 = Linux, 32-48 = 940 MPG, 48+ = unmapped, just wonderful.
 
Yes, it certainly seems so.

Basically, 'malloc'/'new' etc is limited to the first 32mb (of which about 22mb is available after all the shit is loaded). To access the other 32mb we need to mmap it. Since the /dev/dualcpu is initialised on linux startup, you probably need to reset the unit after trashing everything it's put there, but I don't think that's a biggee. People are used to doing that on the gp32 anyway.
 
Personally I'm not really interesting in 'forking' a process. I think that in most applications relevant to the gp2x it's more desirable to use the 940 for what it was intended for: a coprocessor. You could make it into a GPU, rendering sprites, backgrounds, polygons and the like. Making a queue that the 920 populates and the 940 consumes should make sure the 920 never has to wait for the 940, only ever the other way around. Load balancing then becomes a problem of choosing wich tasks should be handled by wich processor. (no easy feat by far)
 
OK, so I did some experiments and it's all simple and sensible :)

In the 940, all addresses have 940BANK added to them. It's a normal addition with rollover, so the 940 can even access RAM below its offset.

As an example, here's some really simple code: the 940 sends characters out the serial port, while the 920 idles and pokes a few memory locations. The output from the program is 0000001111113333336666666666666666...

Of course, this runs in raw mode (outside Linux)... inside Linux I guess it would run afoul of the MMU or something.
Code:
#define OFFSET_940   (0x01000000)
#define BANK_940     (OFFSET_940 >> 24)
#define RESET_940    (0x80)

#define nMMSP2_UART0_TX_BUF     (0xc0001210)
#define MMSP2_UART0_TX_BUF_940  (*(volatile unsigned char *)(nMMSP2_UART0_TX_BUF - OFFSET_940))

#define nDUALCTRL940     (0xC0003B48)
#define pDUALCTRL940     ((volatile unsigned short *)nDUALCTRL940)
#define DUALCTRL940      (*pDUALCTRL940)

#define STORE1       (*(volatile unsigned char *)(0x02000000))
#define STORE2       (*(volatile unsigned char *)(0x03FFFFFF))
#define STORE3       (*(volatile unsigned char *)(0x00FFFFFF))

#define STORE1_940   (*(volatile unsigned char *)(0x01000000))
#define STORE2_940   (*(volatile unsigned char *)(0x02FFFFFF))
#define STORE3_940   (*(volatile unsigned char *)(0xFFFFFFFF))

void CopyFunction (unsigned char *pF)
{
   int i;
   unsigned char *pDest;

   pDest = (unsigned char *)OFFSET_940;

   for (i=0;i<10000;i++)
   {
      *pDest = *pF;
      pDest++;
      pF++;
   }
}

void SendChars (void)
{
   unsigned char c;
   int j;

   while (1)
   {
      c = STORE1_940 & 0x0f;
      c += (STORE2_940 & 0x0f);
      c += (STORE3_940 & 0x0f);
      c += '0';

      MMSP2_UART0_TX_BUF_940 = c;

      for (j=0;j<1000000;j++);
   }
}

void main (void)
{
   volatile int i;

   STORE1 = 0;
   STORE2 = 0;
   STORE3 = 0;

   /* Start the second core at 0x01000000 */
   CopyFunction ((unsigned char *)SendChars);
   /* Doesn't seem that you need to take precautions with DUALCTRL940 like writing the bank
    * with RST940 set and then clearing RST940 separately, just stuffing the bank value in
    * there works just fine.
    * Set 940BANK to 0x01 and start the 940 running...
    */
   DUALCTRL940 = BANK_940;

   /* Wait for a long time and then set byte at 0x02000000 to 1 */
   for (i=0;i<10000000;i++);
   STORE1 = 1;
   /* Wait for a long time and then set byte at 0x03FFFFFF to 2 */
   for (i=0;i<10000000;i++);
   STORE2 = 2;
   /* Wait for a long time and then set byte at 0x00FFFFFF to 3 */
   for (i=0;i<10000000;i++);
   STORE3 = 3;

   while (1);
}
 
Back
Top