In case you don't know, nspire CX II is a calculator made by Texas Instruments released in 2018. It has an ARM926EJ-s processor running at 396MHz, 64MB of LPDDR memory, and 128MB of SPI nand flash. Though this is probably already an overkill for a calculator, but, can we get even faster on this calculator? Of course, it would really mean anything. I don't need that additional performance, overclocking a calculator is just like overclocking anything else, it is just purely for fun. If I do need more performance, I would go and pick up a laptop. Let's get started.
To overclock any modern processor, there are two key parts: clock synthesizer, memory controller. The first one boosts up the clock frequency, and the second controls the memory timing to make sure the memory runs at higher frequency (if you are unable to decouple the memory frequency with core frequency). The TI nspire CX II uses a custom SoC which I don't have any documents. The operating system on the nspire CX II is also locked down to disallow any unsigned binary code execution. So to overclock it, there are three steps, 1. Get the "root priviledge" on the nspire OS, 2. Understand how clock generation works on nspire, 3. Write a program to overclock it.
Luckily, the first part has been done by the community. There is a tool called Ndless which exploits some vulnerabilities in the nspire OS to allow unsigned code execution. The version released in September 2019 introduced initial support for nspire CX II. Huge thanks to the ndless team who made this all possible.
The nspire SoC has an internal clock synthesizer (PLL). Since there is no datasheet or manual for the SoC, I have to reverse engineer it to find out how it sets the clock. Luckily, there is a community developed emulator for the nspire CX II, also based on reverse engineering efforts obviously. (Note: CX II suppoort was developed by the same guy who implemented CXII supported for ndless, Vogtinato.)
One obvious starting point is the bootloader software. It prints out the clock frequency during boot:
Clocks: CPU = 396 MHz AHB = 198 MHz APB = 99 MHz
So I went ahead and decompiled the bootloader and found the code responsible for this part:
The uVar1, uVar2, and uVar3 are CPU, AHB, and APB frequencies respectively. Let's take a look at the functions that gets these frequencies:
DAT_00045efc is a pointer points to the base address of the PMU (0x90140000), and we could see it loads the 0x90140030, 0x90140020, and 0x90140810 into the memory. Combined with some tests done with the software, it seems like the 0x90140030 contains a multiplier and a divider:
- Bit 31-24: multiplier
- Bit 21-16: divider1
And the 0x90140020 contains another divider:
- Bit 23-20: divider2
Clock = (12MHz * multiplier / divider1) / (1 + divider2);
Note the divider 2 is only enabled if 0x90140810's bit 4 (&0x10) is set and 0x90140030's bit 4 is not set.
Unfortuately, how to change the ratio between CPU, AHB and APB remains unknown, as the clock print out code just hardcodes the AHB to be 1/2 of the CPU, and APB to be 1/4 of the CPU:
And I don't yet know how to change memory timing at this point.
With these basic knowledge, it is possible to write a simple tool to modifiy the register to allow limited overclocking.
Note that the nspire CX II runs at 288 MHz when USB is plugged in, and 396 MHz when USB is unplugged. Because memory timing control hasn't been figured out yet, overclocking starting from 396 MHz would give better result.
I have also ported over the CoreMark tool to benchmark the CPU. I am able to overclock mine up to 492 MHz before encountering stability issues. Here are some of the results, PSP and PSV are added for comparison:
|TI nspire w/ TP||NS2007C||ARM926EJ-s||armel||120 MHz||gcc 10.1.0 -O3||250||2.1|
|TI nspire w/ TP||NS2007C||ARM926EJ-s||armel||150 MHz||gcc 10.1.0 -O3||333||2.2|
|TI nspire CX II-T||NS2018||ARM926EJ-s||armel||288 MHz||gcc 10.1.0 -O3||611||2.1|
|PlayStation Portable||Allegrex||MIPS R4000||mipseb||333 MHz||gcc 4.9.3 -O3||666||2.0|
|TI nspire CX II-T||NS2018||ARM926EJ-s||armel||396 MHz||gcc 10.1.0 -O3||840||2.1|
|TI nspire CX II-T||NS2018||ARM926EJ-s||armel||492 MHz||gcc 10.1.0 -O3||1050||2.1|
|PlayStation Vita||CXD5315GG||Cortex-A9||armhf||333 MHz||gcc 9.1.0 -O3||1136||3.4|
|SHARC Audio Module||ADSP-SC589||Cortex-A5||armhf||450 MHz||gcc 7.3.1 -O3||1149||2.6|
|PlayStation Vita||CXD5315GG||Cortex-A9||armhf||444 MHz||gcc 9.1.0 -O3||1562||3.5|
|PlayStation Vita||CXD5315GG||Cortex-A9||armhf||500 MHz||gcc 9.1.0 -O3||1724||3.5|
Please note that all tests are done in single thread only. PSV has 4 CPU cores so the real world performance would be better if the application/ game can utilize them.
I am expecting to see better results once I get memory timing setting working.
There are many questions remain.
At low speed, the register value is 0x18020303, based on the formula we got from BOOT1 code, it would translate to 12 x 24 / 2 = 144 MHz. However, at full speed, the register value is 0x21020303, which translates to 12 x 33 / 2 = 198 MHz, but not 396 MHz. However, based on the benchmark it is quite likely it is running at 396 MHz but not 198 MHz. This probably has something to do with the divider2 in the 0x90140020. I didn't look too much into this issue.
I am kind of stuck right now, I need the OS to be running in my emulator to continue. But without proper tool to dump the Boot1/2 of my CX II, I cannot get past boot2 in the emulator. The tool hasn't been released to the public yet, so I will wait.
Thanks to everyone has been working in the community. Without prior work this wouldn't be possible.