dev builds
themes manual
device status forums
mailing lists
IRC bugs
dev guide

Search | Go
Wiki > Main > IngenicX1000

Ingenic X1000


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.

Player Memory Rockbox support
AGPtEK Rocker 32M Hosted
AIGO Eros Q 32M Native, hosted
FiiO M3K 64M Native, hosted
Shanling Q1 64M Native
xDuuo X3ii 32M Hosted
xDuuo X20 32M Hosted

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 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


r3 - 29 Nov 2022 - 23:19:50 - Main.AidanMacDonald

Copyright © by the contributing authors.