Concepts First
- Partition table: a "map" telling the chip what each Flash region is named and used for (firmware, the partition tables themselves, PSM power-off storage, OTA backup, etc.), with each region's address and size.
- Dual-table mechanism: two partition tables (table 0 / table 1) live in Flash; at startup the valid one is selected, protecting the "map" from being corrupted by an interrupted erase/write.
- Active partition: the partition table actually in use; the example fetches and prints it with
pt_table_get_active_partition_need_lock. - Entry fields: each entry records
type,device,active_index,name,start_address[2]/max_len[2](two backup addresses and lengths), andage(aging counter for dual-table switching).
Example Overview
This page is based on the partition example in the official Bouffalo SDK (examples/partition), which demonstrates reading and printing the active partition table:
- Registers Flash erase/write/read callbacks (
pt_table_set_flash_operation) used by the partition-table component; - Calls
pt_table_get_active_partition_need_lockto read the active table (dual-table validation and switching are handled internally); _dump_partitionprints the header and every entry: magicCode, version, entryCnt, age, crc32, and each entry's addresses/lengths/name.- The output corresponds to the
partition*.binflashed into Flash (generated from the partition config), useful for verifying the actual layout.
Note
The actual layout is decided by the build-time partition config (partition_cfg / pt file); this example only reads and prints it. Changing the layout is risky — a wrong table can make the bootloader fail to find the firmware — so keep the official default unless you know what you are doing.
Operation Steps
No external wiring is needed for this page. Open a terminal and enter the SDK partition-table example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):
cd examples/partitionRun the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616 (the example depends on CONFIG_PARTITION, enabled in its defconfig):
make CHIP=bl616 BOARD=bl616dkConnect the board with a USB cable, hold the BOOT button (IO2 on the Ai-M61-32S-Kit), briefly press EN/RST to enter download mode, then flash (replace the serial port with the one on your computer):
make flash CHIP=bl616 COMX=/dev/ttyUSB0Open a serial tool (baud rate 2000000). The program reads the active partition table and prints it: header info (magicCode, version, entryCnt, age, crc32) and one line per entry (index, type, device, active_index, name, two addresses, two lengths, age).
Code Execution Flow
The complete flow from startup to printing the partition table is shown below:
APIs Used by the Example
pt_table_set_flash_operation(erase, write, read)
Registers the partition-table component's Flash access functions as the SDK's bflb_flash_erase / bflb_flash_write / bflb_flash_read.
Parameters:
erase: erase callbackwrite: write callbackread: read callback
Return: none
pt_table_get_active_partition_need_lock(ptStuff[2])
Reads the active partition table (internally locked, thread-safe). Both tables are validated and the valid one is returned.
Parameters:
ptStuff:pt_table_stuff_configarray (space for 2 tables)
Return: pt_table_id_type (PT_TABLE_ID_0 / PT_TABLE_ID_1; PT_TABLE_ID_INVALID on failure)
pt_table_get_active_entries_by_name(pt_stuff, name, pt_entry)
Finds an entry by name (not used by the example, but common in real projects), e.g. "PSM" or "FW", returning its address and size.
Parameters:
pt_stuff: partition-table contentsname: partition namept_entry: receives the entry info
Return: pt_table_error_type (PT_SUCCESS on success)
Complete Code
The complete source below matches the official example (examples/partition) verbatim. Collapsed by default, click to expand:
📜 Click to expand partition/main.c full code
#include "bflb_flash.h"
#include "partition.h"
#include "board.h"
#define DBG_TAG "MAIN"
#include "log.h"
#define USER_UNUSED(a) ((void)(a))
static void _dump_partition(pt_table_stuff_config *part)
{
int i;
USER_UNUSED(i);
USER_UNUSED(part);
printf("======= PtTable_Config @%p=======\r\n", part);
printf("magicCode 0x%08X;", (unsigned int)(part->pt_table.magicCode));
printf(" version 0x%04X;", part->pt_table.version);
printf(" entryCnt %u;", part->pt_table.entryCnt);
printf(" age %lu;", part->pt_table.age);
printf(" crc32 0x%08X\r\n", (unsigned int)part->pt_table.crc32);
printf("idx type device active_index name Address[0] Address[1] Length[0] Length[1] age\r\n");
for (i = 0; i < part->pt_table.entryCnt; i++) {
printf("[%02d] ", i);
printf(" %02u", part->pt_entries[i].type);
printf(" %u", part->pt_entries[i].device);
printf(" %u", part->pt_entries[i].active_index);
printf(" %8s", part->pt_entries[i].name);
printf(" 0x%08lx", (part->pt_entries[i].start_address[0]));
printf(" 0x%08lx", (part->pt_entries[i].start_address[1]));
printf(" 0x%08lx", (part->pt_entries[i].max_len[0]));
printf(" 0x%08lx", (part->pt_entries[i].max_len[1]));
printf(" %lu\r\n", (part->pt_entries[i].age));
}
}
int main(void)
{
board_init();
pt_table_set_flash_operation(bflb_flash_erase, bflb_flash_write, bflb_flash_read);
pt_table_stuff_config ptstuff[2];
pt_table_id_type active_id;
active_id = pt_table_get_active_partition_need_lock(ptstuff);
if (active_id < PT_TABLE_ID_INVALID) {
_dump_partition(&ptstuff[active_id]);
} else {
printf("partition init fail!\r\n");
}
while (1) {
}
}FAQ
It prints partition init fail
Reading the partition table failed. Common causes: both tables in Flash are invalid, the table address does not match the firmware, or CONFIG_PARTITION is not enabled. Re-flash the full image (including partition*.bin) to recover.
The printed entries do not match the expected layout
The output reflects the table actually flashed into Flash. Check that the build-time partition config (partition_cfg / pt file) and the flashed partition*.bin agree; after changing the config, regenerate and flash the partition table.
How to find a partition by name
Use pt_table_get_active_entries_by_name(pt_stuff, "PSM", &entry) and read start_address / max_len from the returned pt_entry, then access that partition with the MTD interface.
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

