Ingenic X1000
Overview
The X1000 is a low-cost IoT processor manufactured by Ingenic, sharing many similarities with the earlier JZ47xx series processors. The CPU is MIPS-based and clocked at 1 GHz, packaged with 32 MiB or 64 MiB of LPDDR RAM. Rockbox runs pretty stably on this platform with no reported major issues, although features like Bluetooth, WiFi, and USB DAC that are commonly available on original firmware are not currently supported under Rockbox.
The information on this page applies only to native ports of Rockbox (running directly on the hardware). There's also X1000-based hardware where Rockbox runs as a hosted port, on top of the original firmware's kernel.
Using usbboot
The hardware boot ROM supports booting over USB using a simple protocol. The usbboot tool in utils/ingenic_tools implements this protocol and allows you to load Rockbox over USB, among other things. USB boot also provides a way to recover a player -- at least in theory -- if something should go horribly wrong, corrupt the bootloader or otherwise render it non-functional. (eg: due to a bug or power failure during a firmware update).
When connected in USB boot mode, an X1000-based player will show up like this in lsusb output:
Bus 001 Device 007: ID a108:1000 Ingenic Semiconductor Co.,Ltd X1000
Normally USB boot is activated by holding a certain button while powering on the player.
Player |
Button |
FiiO M3K |
volume down |
Shanling Q1 |
play |
Eros Q |
menu |
The most reliable way to do this is holding the button while the unit is off and then plugging in the USB cable -- indeed if the cable is not plugged at boot, then the USB interface might not be detected at all.
The USB boot mode is burned into the processor at an extremely low level, so it is impossible to "brick" the player in a way that renders USB boot inaccessible. If it does not work, make sure the device is powered off by holding the power button for at least 10 seconds -- on some players, there is no visible sign that the player is turned on if it happens to be stuck in USB boot mode.
usbboot can load "stage1" and "stage2" binaries (the hardware boot protocol differentiates between the two stages, but the actual difference is not clear). The SPL runs as a stage1 binary and exists solely to initialize RAM, making it available for the stage2 binary. The Rockbox app and bootloader run as a stage2 binary. usbboot has shortcuts for the common case of running the Rockbox SPL / bootloader / app to facilitate development.
To run a development build of Rockbox, for example on the FiiO M3K:
$ ./usbboot -c x1000 -1 <spl.m3k> --wait 1 -2 <rockbox.bin>
Or to run a development bootloader:
$ ./usbboot -c x1000 -1 <spl.m3k> --wait 1 -2 <bootloader.bin>
Also see usbboot --help .
Important: for the X1000, a stage1 binary is a padded SPL binary with 2 KB header. This file is generated by a bootloader build as spl.target where target is a short, target-specific file extension: m3k for the FiiO M3K, or erosq for the Eros Q. A stage2 binary must be a raw .bin image with no headers. You must use rockbox.bin / bootloader.bin as your stage2 files, not the files with a target extension.
Note: the more user-friendly jztool does pretty much the same thing as the "run a development bootloader" example above, except the SPL and bootloader binary are packed into a tarball for easy download and handling.
Known issues with Rockbox
- Partial LCD updates are actually worse than full-frame updates because DMA can only (easily) transfer full frames, and we need to wait for the last frame to complete before sending a new one. This causes some themes to flicker as different layers are drawn in quick succession with partial updates.
- Coalescing partial LCD updates in the driver could eliminate most flicker. Using the spare TCU timer IRQ would have less overhead & higher precision than the kernel-based timers. Coalescing would have to be bypassed during a panic, otherwise the LCD would never be updated.
- Very rarely, the LCD driver might be causing a hang when the screen turns on after being off. This is almost certainly due to bugs in the complicated locking/synchronization design.
- LCD's continuous DMA mode don't seem to respect the panel FPS even when the vsync signal is turned on. This appears to cause stuttering, which is why the driver uses one-shot DMA transfers.
- Playback at 192 KHz with all DSP / EQ settings enabled puts the CPU to almost 100% usage, with frequent UI slowdown and audio underruns. Increasing the size of the PCM mix buffers may help, since there are huge numbers of DMA interrupts at this sample rate.
- AIC fifo underruns aren't reported reliably. It's probably because the DMA interrupt is hogging the CPU so the underrun bit clears before we can see it.
- USB doesn't operate correctly in the bootloader after a USB boot. The cable needs to be unplugged and re-plugged to make it work. This should be fixed since it could smooth bootloader installation with the Rockbox utility.
Hardware information
This section is work in progress and more information needs to be added.
Memory mapping
Start |
End |
Notes |
0xf4000000 |
0xf4003fff |
tightly coupled shared memory (TCSM) |
0x80000000 |
0x8Xffffff |
mapped DRAM (32M: X=1, 64M: X=3) |
0xbfc00000 |
0xbfc03fff |
hardware boot ROM |
0xb3050000 |
|
LCD controller register base |
0xb3060000 |
|
CIM camera module register base |
0xb0020000 |
|
AIC/icodec register base |
0xb0071000 |
|
PCM register base |
0xb34f0000 |
|
DDR controller AHB register base |
0xb3012000 |
|
DDR controller APB register base |
0xb3011000 |
|
DDR PHY APB register base |
0xb3440000 |
|
SPI flash controller (SFC) register base |
0xb0000000 |
|
CPM register base |
0xb0002000 |
|
Timer/PWM unit register base |
0xb2000000 |
|
OS timer register base |
0xb0001000 |
|
Interrupt controller register base (NOTE: the datasheet has this one wrong) |
0xb0002000 |
|
Watchdog timer register base |
0xb3420000 |
|
DMA controller register base |
0xb3422000 |
0xb3423fff |
TCSM of DMA controller's programmable MCU |
0xb0003000 |
|
RTC register base |
0xb3540000 |
|
EFUSE register base |
0xb0010X00 |
|
GPIO port register base (X = 0,1,2,3 or 7) |
0xb005X000 |
|
I2C controller register base (X = 0,1,2) |
0xb0040000 |
|
Smart cart controller register base |
0xb0043000 |
|
Synchronous serial interface register base |
0xb003X000 |
|
UART register base (X = 0,1,2) |
0xb34X0000 |
|
SD/MMC/CE-ATA controller register base (X = 5,6) |
0xb3500000 |
|
USB controller register base |
Note: you can use the usbboot tool to dump the boot ROM, TCSM, or registers using usbboot --upload and passing the relevant address.
SPI flash
The X1000's SPI flash controller (SFC) supports both NAND and NOR, but all supported players use NAND so most of the information here is potentially NAND-specific. I've made no effort to figure out what parts might be different with NOR. -- AidanMacDonald - 29 Nov 2022
SPL headers
The SPL headers are stored at offset 0 on the flash and run for 2048 bytes, first 512 bytes of parameter data (described in the programming manual) and then 1.5k of security key (normally 0). The first 8 bytes should be 06 05 04 03 02 55 aa 55 .
SPL code ends around 0x2fff and should contain readable strings toward the end: Linux command line arguments, error messages, etc.
Partition table
Ingenic's SPI flash driver reads a partition table at offset 0x3c00 . The magic word "nand" identifies the beginning of the table. All binary values appear to be little-endian. Example from the Shanling Q1:
00003c00 6e 61 6e 64 07 00 00 00 75 62 6f 6f 74 00 00 00 |nand....uboot...|
00003c10 00 00 00 00 00 00 00 00 b4 2b 0f 5b 10 a4 00 80 |.........+.[....|
00003c20 1b 00 00 00 05 00 00 00 00 00 10 00 00 00 00 00 |................|
00003c30 00 00 00 00 00 00 00 00 62 74 5f 6d 61 63 00 00 |........bt_mac..|
00003c40 00 00 00 00 00 00 00 00 b4 2b 0f 5b 10 a4 00 88 |.........+.[....|
00003c50 01 00 00 00 07 00 00 00 00 00 02 00 00 00 10 00 |................|
00003c60 00 00 00 00 00 00 00 00 75 73 65 72 5f 69 64 00 |........user_id.|
00003c70 00 00 00 00 00 00 00 00 4f 2a 0f 5b 64 00 00 80 |........O*.[d...|
00003c80 07 00 00 00 04 00 00 00 00 00 02 00 00 00 12 00 |................|
00003c90 00 00 00 00 00 00 00 00 6b 65 72 6e 65 6c 00 00 |........kernel..|
00003ca0 00 00 00 00 00 00 00 00 b4 2b 0f 5b 10 a4 00 80 |.........+.[....|
00003cb0 1b 00 00 00 07 00 00 00 00 00 80 00 00 00 14 00 |................|
00003cc0 00 00 00 00 00 00 00 00 72 65 63 6f 76 65 72 79 |........recovery|
00003cd0 00 00 00 00 00 00 00 00 bb 2b 0f 5b 62 00 00 80 |.........+.[b...|
00003ce0 2f 00 00 00 06 00 00 00 00 00 a0 00 00 00 94 00 |/...............|
00003cf0 00 00 00 00 00 00 00 00 72 6f 6f 74 66 73 00 00 |........rootfs..|
00003d00 00 00 00 00 00 00 00 00 b4 2b 0f 5b 10 a4 00 80 |.........+.[....|
00003d10 1b 00 00 00 07 00 00 00 00 00 c0 04 00 00 34 01 |..............4.|
00003d20 00 00 00 00 01 00 00 00 63 6f 6e 66 69 67 00 00 |........config..|
00003d30 00 00 00 00 00 00 00 00 b4 2b 0f 5b 10 a4 00 80 |.........+.[....|
00003d40 1b 00 00 00 07 00 00 00 00 00 00 00 00 00 f4 05 |................|
00003d50 00 00 00 00 01 00 00 00 ff ff ff ff ff ff ff ff |................|
00003d60 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
Here is a C language "schema" for the partition table:
struct nand_partition_table {
char magic[4]; /* "nand" */
uint32_t count;
struct jz_spinand_partition partitions[count];
};
struct jz_spinand_partition {
/* null terminated, but may contain garbage after the null terminator! */
char name[32];
/* size of the partition, in bytes */
uint32_t size;
/* offset of the partition, in bytes, from the start of the flash */
uint32_t offset;
/* Linux MTD flags to mask out (struct mtd_partition::mask_flags) */
uint32_t mask_flags;
/* Appears to be unused. 1 = UBI, 0 = raw MTD */
uint32_t manager_mode;
};
PDMA firmware
This is plain old MIPS32 code for running the DMA controller's embedded MCU. Typically stored at offset 0x4000 in the flash and runs until 0x5fff .
Boot process
After a power-on reset, the CPU jumps to the boot ROM at the standard MIPS reset vector (0xbfc00000 ). DRAM is not accessible at this time, so the bootrom loads a small binary called the SPL (secondary program loader) into TCSM and calls it to handle DRAM initialization and continue booting, either by loading an OS kernel directly, or a more capable bootloader such as U-Boot.
Initially the bootrom sets the stack pointer to 0xf4001000 , copies other r/w data into the lower 4k region of the TCSM, then polls some GPIO pins to decide how to continue.
- GPIOB30 is used to indicate the external oscillator frequency: 1 for 26 MHz, 0 for 24 MHz.
- GPIOB28 and GPIOB29 (aka
boot_sel[0] and boot_sel[1] ) determine where the bootrom looks for the SPL.
- B29=1, B28=1 -> NAND/NOR boot
- B29=0, B28=1 -> MSC boot
- B29=1, B28=0 -> USB boot
For USB boot, the bootrom sets APLL frequency to EXCLK*26 (576 or 624 MHz) and the CPU and other system clocks are switched to APLL. Under a "normal" boot from storage system clocks are left running directly from EXCLK . The SPL needs to take care not to change APLL or the system clocks if used with USB boot -- otherwise USB communication with the host can be disrupted.
The SPL consists of a 512 byte header, a 1.5k "secure boot" key, and up to 10k of code. The whole thing, headers included, gets loaded at 0xf4001000 so the code begins at 0xf4001800 . The bootrom does seem to use the key somehow using an undocumented hardware feature, but on all currently supported players the secure boot mode is apparently deactivated and the header key is left zeroed.
Useful links
Source code
Documentation
r3 - 29 Nov 2022 - 23:19:50 - Main.AidanMacDonald
Copyright © by the contributing authors.
|