PandaBoard and MPCore
I’ve ported the new operating system to the PandaBoard-ES, which is a more advanced platform than the BeagleBoard. It is based on the TI OMAP4460 with a Cortex-A9 ARM dual-core processor. Although there were many similarities with the OMAP3530 of the BeagleBoard, many of the memory mapped registers were found at different addresses. Also, the OMAP4460 makes use of the ARM Generic Interrupt Controller v1.0 which is a much more capable controller than what the OMAP3530 had. This is necessary because with multiple cores, the interrupt controller needs to be able to distribute interrupts to one or more CPUs. The GIC architecture is pretty nice and straightforward for what it does, more so than the Intel APICs. There is a central distributor and multiple CPU interfaces, all of which are managed through memory-mapped registers. The distributor is capable of being programmed with individual IRQ target lists and priorities, and the system can be programmed in a fine-grained manner to determine which IRQs have sufficient priority to be able to preempt processors and which do not.
Booting the second CPU turned out, in the end, to be much more straightforward than booting an Intel application processor, but the documentation is spread around the OMAP TRM as well as the Cortex-A9 MPCore TRM and a little difficult to get into one coherent picture. To begin with, all auxiliary CPUs are put into a power-saving loop where they invoke the WFE (Wait-For-Event) instruction in a loop until certain conditions are satisfied. It is the job of the operating system on the primary processor to program the OMAP-specific AUX_CORE_BOOT_0 and AUX_CORE_BOOT_1 memory-mapped registers before executing the SEV (Set-Event) instruction which wakes up all waiting processors. The first register contains bitflags which must be set in order for the auxiliary CPU to break out of its loop — I just set every bit to 1 in that register. The second register contains the address of the boot code for the secondary processor, which I have assigned to an assembly routine which takes control and sets up the stack before entering C code.
Once both processors are running C code, I have a several stage initialization routine based upon the MPCore TRM (Multiprocessor Bring-up), although it seemed fairly forgiving when I did things out of order. Using a global variable for rudimentary synchronization, once the secondary processor boots, I have the primary processor enable the Snoop Control Unit. Once that is done, then both processors go ahead and toggle the SMP bit in the Auxiliary Control register (ACTLR). Then I check the Snoop Control Unit’s configuration to ensure that both processors are participating in SMP and coherency. Now the interrupt controller can be programmed to deliver interrupts to the second processor and the rest of initialization can proceed.
Discussion (0) | July 3rd, 2012 Categories: hacking