/* F103 MCU identifier * used to fingerprint and identify STM32F103 or variants, and their clones * Copyright: 2020 King Kévin * SPDX-License-Identifier: GPL-3.0-or-later */ #include // print utilities #include // error definitions (used for _write) #include // string comparison utilities #include // Cortex M3 utilities #include // vector table definition #include // System Control Space definitions #include // domain clock utilities #include // GPIO utilities to configure UART #include // UART utilities to communicate with user #include // design utilities #include // flash utilities #include // debug utilities #include // CRC utilities // peripherals to test #include #include #include #include #include #include // if print should really output data (used to silence when doing measurements) static bool enable_output = true; // get the length of an array #define LENGTH(x) (sizeof(x) / sizeof((x)[0])) // device information collected over time static char* manufacturer_str = "???"; static char* family_str = "???"; static char pin_count_variant = '?'; static uint8_t pin_count_nb = 0; static char flash_size_variant = '?'; static uint8_t sram_size = 0; int _write(int file, char *ptr, int len); int _write(int file, char *ptr, int len) { if (!enable_output) { // don't print anything return len; } else if (file == 1) { for (int i = 0; i < len; i++) { usart_send_blocking(USART1, ptr[i]); } return len; } errno = EIO; return -1; } // start embedded (UART) bootloader (e.g. the application in the system memory) static void bootloader(void) { const uint32_t address = 0x1FFFF000; // system memory address SCB_VTOR = (volatile uint32_t)(address); // set vector table to application vector table (store at the beginning of the application) __asm__ volatile ("MSR msp,%0" : :"r"(*(uint32_t*)address)); // set stack pointer to address provided in the beginning of the application (loaded into a register first) (*(void(**)(void))((uint32_t)address + 4))(); // start system memory (by jumping to the reset function which address is stored as second entry of the vector table) while (true); // this should not be reached } // reset MCU static void reset(void) { scb_reset_system(); // reset device while (true); // wait for the reset to happen } // use the same UART output as the bootloader (serial configuration: 115200 8N1) static void uart_setup(void) { rcc_periph_clock_enable(RCC_GPIOA); rcc_periph_clock_enable(RCC_AFIO); rcc_periph_clock_enable(RCC_USART1); gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX); gpio_set(GPIOA, GPIO_USART1_RX); gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_USART1_RX); usart_set_baudrate(USART1, 115200); usart_set_databits(USART1, 8); usart_set_stopbits(USART1, USART_STOPBITS_1); usart_set_mode(USART1, USART_MODE_TX_RX); usart_set_parity(USART1, USART_PARITY_NONE); usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); usart_enable(USART1); } // show CPU information static void cpu(void) { printf("CPU information:\r\n"); // from ARMv7-M and Cortex-M3 TRM // ARMv7-M B3.2.3 const uint8_t cpuid_implementer = (SCB_CPUID & SCB_CPUID_IMPLEMENTER) >> SCB_CPUID_IMPLEMENTER_LSB; const uint8_t cpuid_architecture = (SCB_CPUID & SCB_CPUID_CONSTANT) >> SCB_CPUID_CONSTANT_LSB; const uint16_t cpuid_partno = (SCB_CPUID & SCB_CPUID_PARTNO) >> SCB_CPUID_PARTNO_LSB; const uint8_t cpuid_variant = (SCB_CPUID & SCB_CPUID_VARIANT) >> SCB_CPUID_VARIANT_LSB; const uint8_t cpuid_revision = (SCB_CPUID & SCB_CPUID_REVISION) >> SCB_CPUID_REVISION_LSB; printf("- CPUID: 0x%08lx\r\n", SCB_CPUID); printf("- implementer: %s (0x%02x)\r\n", 0x41 == cpuid_implementer ? "ARM" : "unknown", cpuid_implementer); printf("- architecture: "); switch (cpuid_architecture) { case 0xc: printf("ARMv6-M"); break; case 0xf: printf("ARMv7-M"); break; default: printf("unknown"); } printf(" (0x%x)\r\n", cpuid_architecture); printf("- part number: "); switch (cpuid_partno) { case 0xC60: printf("Cortex-M0+"); break; case 0xC20: printf("Cortex-M0"); break; case 0xC23: // the ARM spec actually mentions 0xC24 printf("Cortex-M3"); break; case 0xC24: printf("Cortex-M4"); break; case 0xC27: printf("Cortex-M7"); break; default: printf("unknown"); } printf(" (0x%03x)\r\n", cpuid_partno); printf("- variant: %u\r\n", cpuid_variant); printf("- revision: %u\r\n", cpuid_revision); } /** get flash page size * @return flash page size (in bytes) */ static uint16_t flash_internal_page_size(void) { if (desig_get_flash_size() < 256) { if ((*(uint32_t*)0x1FFFF000 & 0xFFFE0000) == 0x20000000) { // non-connectivity system memory start detected (MSP address pointing to SRAM return 1024; } else { // connectivity system memory start is at 0x1FFFB000 return 2048; } } else { return 2048; } return 0; } /** probe the readable size of the internal flash * @return tested size (in bytes) */ static uint32_t flash_internal_probe_read_size(void) { // we will check is a flash address is readable until a bus fault occurs cm_disable_faults(); // disable all faults, particularly BusFault SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them) uint32_t address = FLASH_BASE; // start with the start of flash while (0 == (SCB_CFSR & SCB_CFSR_BFARVALID)) { // until a bus fault occurs (void)*(volatile uint8_t*)address; // access address address += flash_internal_page_size(); // got to next address } SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault cm_enable_faults(); // re-enable faults return address - 1 - FLASH_BASE; } /** probe the additional writable size of the internal flash, after the advertised size (and linker provided) * @return tested size (in bytes) */ static uint32_t flash_internal_probe_write_size(void) { if (0 == desig_get_flash_size()) { // no flash size advertised return 0; } // prepare for reading the flash cm_disable_faults(); // disable all faults, particularly BusFault SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them) // prepare for writing the flash flash_unlock(); // unlock flash to be able to write it // try reading and writing the flash, page per page const uint32_t start = FLASH_BASE + desig_get_flash_size() * 1024; // start with the end of the advertised flash uint32_t address = start; // address to test const uint16_t test_data = 0x2342; // the data we will write and read to test page while (address < 0x1FFFEFFF) { // this is where the system memory starts // try reading the flash (void)*(volatile uint32_t*)address; // access address if (0 != (SCB_CFSR & SCB_CFSR_BFARVALID)) { // until a bus fault occurs break; // page not readable } // try writing the flash flash_erase_page(address); // erase current page if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong break; } flash_program_half_word(address, test_data); // writes test data if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong break; } if (test_data != *((uint16_t*)address)) { // verify data is written correctly break; } flash_erase_page(address); // erase test data if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong break; } address += flash_internal_page_size(); // go to next page } flash_clear_status_flags(); // clear all flag flash_lock(); // protect again from writing SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault cm_enable_faults(); // re-enable faults return address - start; } // show flash and SRAM sizes static void sizes(void) { printf("flash & SRAM size information (can take 2 minutes):\r\n"); // test flash size printf("- advertised flash size: "); if (0xffff == desig_get_flash_size()) { printf("unknown (probably a counterfeit/defective micro-controller\r\n"); } else { printf("%u KB\r\n", desig_get_flash_size()); } const uint32_t flash_read_size = flash_internal_probe_read_size(); printf("- readable flash size: %lu bytes (%lu KB)\r\n", flash_read_size, flash_read_size / 1024); const uint32_t flash_write_size = flash_internal_probe_write_size(); printf("- over advertised writable flash size: %lu bytes (%lu KB)\r\n", flash_write_size, flash_write_size / 1024); // test and show SRAM size cm_disable_faults(); // disable all faults, particularly BusFault SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them) uint32_t address = 0x20000000; // the start of the SRAM section uint32_t last_read = address; // to track the last readable SRAM address uint32_t last_write = address; // to track the last writable SRAM address while (address < 0x22000000) { // this is where the bit banding region starts // try reading the SRAM uint8_t sram_backup = *(volatile uint8_t*)address; // access address if (0 != (SCB_CFSR & SCB_CFSR_BFARVALID)) { // until a bus fault occurs break; // page not readable } else { last_read = address; } // try writing the RAM *(volatile uint8_t*)address = 0x42; if (0 == (SCB_CFSR & SCB_CFSR_BFARVALID) && 0x42 == *(volatile uint8_t*)address) { // no bus fault occurs last_write = address; *(volatile uint8_t*)address = sram_backup; } address++; // go to next byte } SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault cm_enable_faults(); // re-enable faults const uint32_t sram_read_size = last_read + 1 - 0x20000000; // calculate readable size printf("- readable SRAM: %lu bytes (%lu KB)\r\n", sram_read_size, sram_read_size / 1024); const uint32_t sram_write_size = last_write + 1 - 0x20000000; // calculate writable size printf("- writable SRAM: %lu bytes (%lu KB)\r\n", sram_write_size, sram_write_size / 1024); sram_size = sram_write_size / 1024; // remember SRAM size for later identification // set size variant switch (desig_get_flash_size()) { case 16: flash_size_variant = '4'; break; case 32: flash_size_variant = '6'; break; case 64: flash_size_variant = '8'; break; case 128: flash_size_variant = 'B'; break; case 256: flash_size_variant = 'C'; break; case 384: flash_size_variant = 'D'; break; case 512: flash_size_variant = 'E'; break; case 768: flash_size_variant = 'F'; break; case 1024: flash_size_variant = 'G'; break; } } static void unique_id(void) { printf("device unique ID: %08lx%08lx%04lx%04lx\r\n", DESIG_UNIQUE_ID2, DESIG_UNIQUE_ID1, DESIG_UNIQUE_ID0 & 0xffff, DESIG_UNIQUE_ID0 >> 16); // not that the half-works are reversed in the first word } static void errata(void) { printf("checking errata: access to debug register\r\n"); const uint16_t dev_id = DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK; const uint16_t rev_id = DBGMCU_IDCODE >> 16; printf("- MCU ID code: 0x%08lx (DEV_ID=0x%03x REV_ID=0x%04x)\r\n", DBGMCU_IDCODE, dev_id, rev_id); const bool debug_attached = (0 != (SCS_DHCSR & SCS_DHCSR_C_DEBUGEN)); printf("- debug attached: %s\r\n", debug_attached ? "yes" : "no"); if (debug_attached) { if (0x307 == DBGMCU_IDCODE) { printf("- debug has been attached, but MCU ID code is not readable, as documented in the errata\r\n"); } else if (0 == DBGMCU_IDCODE) { printf("- debug is/has been attached, but MCU ID code is still not readable, this is an unknown behaviour\r\n"); } else { printf("- debug is attached, and MCU ID code is now readable. Detach debug (JTAG/SWD) to be able to verify errata issue\r\n"); } } else { if (0 == DBGMCU_IDCODE) { printf("- debug is not attached, and MCU ID code is not readable, as documented in the errata\r\n"); } else { printf("- debug is not attached, but MCU ID code is readable -> this is not an STM32\r\n"); } } } static void jep(void) { // read JTAG ID (see ARM CoreSight B2.2.2) const uint8_t jep106_continuation = *(uint32_t*)0xE00FFFD0 & 0x0f; // DES_2, PIDR4 bits[3:0] const uint8_t jep106_identification = ((*(uint32_t*)0xE00FFFE8 & 0x7) << 4) + ((*(uint32_t*)0xE00FFFE4 >> 4) & 0xf); // DES_0, PIDR1 bits[7:4] JEP106 identification code bits[3:0], DES_1, PIDR2 bits[2:0] JEP106 identification code bits[6:4] const uint16_t pidr_partno = ((*(uint32_t*)0xE00FFFE4 & 0xf) << 8) + (*(uint32_t*)0xE00FFFE0 & 0xff); // PART_0, PIDR0 bits[7:0] Part number bits[7:0], PART_1, PIDR1 bits[3:0] Part number bits[11:8] printf("JEP106 ID: "); if (0 == jep106_continuation && 0x20 == jep106_identification) { printf("STM"); } else if (7 == jep106_continuation && 0x51 == jep106_identification) { printf("GigaDevice"); } else if (4 == jep106_continuation && 0x3b == jep106_identification) { printf("ARM"); // used by CKS and APM } else if (5 == jep106_continuation && 0x55 == jep106_identification) { printf("iVivity"); // JEP106 says it should be "iVivity Inc.", but this has been seen in Hangshun HK32F103C8T6 } else { printf("unknown"); } printf(" (cont.=%u, ID=0x%02x), part=0x%03x\r\n", jep106_continuation, jep106_identification, pidr_partno); } static void family(void) { const uint16_t dev_id = DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK; const uint16_t rev_id = DBGMCU_IDCODE >> 16; const uint16_t pidr_partno = ((*(uint32_t*)0xE00FFFE4 & 0xf) << 8) + (*(uint32_t*)0xE00FFFE0 & 0xff); // JEP106 part number actually corresponds to the dev_id uint16_t family_id = 0; printf("chip family "); if (0 == dev_id || 0x307 == dev_id) { // on STM32 the dev_id is not readable unless debug is attached, but the part_no corresponds to the dev_id printf("(PIDR_PARTNO=0x%03x): ", pidr_partno); family_id = pidr_partno; } else { // on not STM32 the dev_id is readable (and the PIDR part number does not correspond) printf("(DEV_ID=0x%03x): ", dev_id); family_id = dev_id; } switch (family_id) { case 0: // DBGMCU_IDCODE is only accessible in debug mode (this is a known issue documented in STM32F10xx8/B and STM32F10xxC/D/E Errata sheet, without workaround) (CKS32F103 is not affected by this issue) printf("not readable, retry with debug attached"); break; case 0x307: // this is completely undocumented (even in the errata), but even with debug attached, DBGMCU_IDCODE = 0x00000307, until the DBGMCU_IDCODE is read over SWJ, only then it's correct. but the JEP106 ID part number is always right (with same meaning). this has been seen on a genuine STM32F103C8T6 (AFAICS) printf("erroneous reading, read over debug first"); break; // from RM0008 STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx case 0x412: family_str = "F10x"; printf("STM32F10x low-density"); break; case 0x410: family_str = "F10x"; printf("STM32F10x medium-density"); break; case 0x414: family_str = "F10x"; printf("STM32F10x high-density"); break; case 0x430: family_str = "F10x"; printf("STM32F10x XL-density"); break; case 0x418: family_str = "F10x"; printf("STM32F10x connectivity"); break; // from RM0091 STM32F0x8 case 0x444: family_str = "F03x"; printf("STM32F03x"); break; case 0x445: family_str = "F04x"; printf("STM32F04x"); break; case 0x440: family_str = "F05x"; printf("STM32F05x"); break; case 0x448: family_str = "F07x"; printf("STM32F07x"); break; case 0x442: family_str = "F09x"; printf("STM32F09x"); break; // from RM0444 STM32G0x1 case 0x460: family_str = "G071/081"; printf("STM32G071xx/STM32G081xx"); break; case 0x466: family_str = "G031/041"; printf("STM32G031xx/STM32G041xx"); break; // from RM0090 STM32F405/415, STM32F407/417, STM32F427/437, STM32F429/439 case 0x413: family_str = "F405/407/415/417"; printf("STM32F405/STM32F407/STM32F415/STM32F417"); break; case 0x419: family_str = "F42x/43x"; printf("STM32F42x/STM32F43x"); break; // from RM0368 STM32F401xB/C, STM32F401xD/E case 0x423: family_str = "F401"; printf("STM32F401xB/C"); break; case 0x433: family_str = "F401"; printf("STM32F401xD/E"); break; // from RM0383 case 0x431: family_str = "F411"; printf("STM32F411xC/E"); break; // from RM0041 STM32F100xx case 0x420: family_str = "F100"; printf("STM32F100 low/medium-density"); break; case 0x428: family_str = "F100"; printf("STM32F100 high-density"); break; // empirical case 0x4c3: // CKS uses this as part number, but the dev_id is readable. APM also uses this part number, but the dev_id is not readable family_str = "F10x"; printf("APM32F10x"); break; default: printf("unknown"); break; } printf("\r\n"); if (0 != dev_id && 0x307 != dev_id) { // rev_id is only valid when dev_id is valid printf("chip revision (REV_ID=0x%04x): ", rev_id); switch (dev_id) { case 0x412: if (0x1000 == rev_id) { printf("A"); } else { printf("unknown"); } break; case 0x410: if (0x0000 == rev_id) { printf("A"); } else if (0x2000 == rev_id) { printf("B"); } else if (0x2001 == rev_id) { printf("Z"); } else if (0x2003 == rev_id) { printf("1/2/3/X/Y"); } else { printf("unknown"); } break; case 0x414: if (0x1000 == rev_id) { printf("A/1"); } else if (0x1001 == rev_id) { printf("Z"); } else if (0x1003 == rev_id) { printf("1/2/3/X/Y"); } else { printf("unknown"); } break; case 0x430: if (0x1003 == rev_id) { printf("A/1"); } else { printf("unknown"); } break; case 0x418: if (0x1000 == rev_id) { printf("A"); } else if (0x1001 == rev_id) { printf("Z"); } else { printf("unknown"); } break; default: printf("unknown"); break; } printf("\r\n"); } else { printf("to get chip revision information, attach debug\r\n"); } } static void manufacturer(void) { // read JTAG ID (see ARM CoreSight B2.2.2) const uint8_t jep106_continuation = *(uint32_t*)0xE00FFFD0 & 0x0f; // DES_2, PIDR4 bits[3:0] const uint8_t jep106_identification = ((*(uint32_t*)0xE00FFFE8 & 0x7) << 4) + ((*(uint32_t*)0xE00FFFE4 >> 4) & 0xf); // DES_0, PIDR1 bits[7:4] JEP106 identification code bits[3:0], DES_1, PIDR2 bits[2:0] JEP106 identification code bits[6:4] // ARMv7-M B3.2.3 const uint16_t cpuid_partno = (SCB_CPUID & SCB_CPUID_PARTNO) >> SCB_CPUID_PARTNO_LSB; const uint8_t cpuid_variant = (SCB_CPUID & SCB_CPUID_VARIANT) >> SCB_CPUID_VARIANT_LSB; const uint8_t cpuid_revision = (SCB_CPUID & SCB_CPUID_REVISION) >> SCB_CPUID_REVISION_LSB; printf("manufacturer: "); if (0xC23 == cpuid_partno && 1 == cpuid_variant && 1 == cpuid_revision && 0 == jep106_continuation && 0x20 == jep106_identification) { // STM32 uses Cortex-M3 r1p1 and the right JEP106 ID manufacturer_str = "STM"; printf("STMicroelectronics"); } else if (2 == cpuid_variant && 1 == cpuid_revision && 7 == jep106_continuation && 0x51 == jep106_identification) { // GD32 uses Cortex-M3 r2p1 and the right JEP106 ID manufacturer_str = "GD"; printf("GigaDevice"); } else if (2 == cpuid_variant && 1 == cpuid_revision && 4 == jep106_continuation && 0x3b == jep106_identification) { // CKS32 and APM32 use Cortex-M3 r2p1 and ARM JEP106 ID if (DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK) { // on CKS32 the dev_id is readable manufacturer_str = "CKS"; printf("China Key System"); } else { // on APM32 the dev_id is not readable manufacturer_str = "APM"; printf("Apemix"); } } else if (2 == cpuid_variant && 1 == cpuid_revision && 5 == jep106_continuation && 0x55 == jep106_identification) { // HK32 uses Cortex-M3 r2p1 and the JEP106 ID from iVivity manufacturer_str = "HK"; printf("Hangshun"); } else { manufacturer_str = "???"; printf("unknown"); } printf("\r\n"); } static void system_memory(void) { // verify if embedded (USART) bootloader is present printf("system memory: "); if ((*(volatile uint32_t*)(INFO_BASE + 0) & 0xFFFE0000) == 0x20000000 && // check is MSP points to SRAM (0x200001fc on genuine STM32) (*(volatile uint32_t*)(INFO_BASE + 4) & 0xFFFFF000) == INFO_BASE) { // check is reset vector is in system memory (0x1ffff021 on genuine STM32) // calculate CRC rcc_periph_clock_enable(RCC_CRC); // enable cock for CRC domain to calculate in hardware the CRC crc_reset(); uint32_t crc = 0; for (uint32_t addr = INFO_BASE; addr < DESIG_FLASH_SIZE_BASE; addr += 4) { // go through system memory crc = crc_calculate(*(volatile uint32_t*)addr); } if (0x8f992132 == crc) { printf("STM32 bootloader v2.2"); } else if (0x0338b6af == crc) { printf("GD32 bootloader v1.0"); } else if (0xac54f62a == crc) { printf("APM32 bootloader v2.2"); } else if (0x9196c048 == crc) { printf("HK32 bootloader v2.2"); } else { printf("unknown bootloader"); } printf(" (CRC=0x%08lx)", crc); } else { printf("absent"); } printf("\r\n"); } struct register_name_t { const uint32_t address; const char* name; }; static const struct register_name_t rom_registers[] = { { .address = 0xE00FF000, .name = "ROMSCS", }, { .address = 0xE00FF004, .name = "ROMDWT", }, { .address = 0xE00FF008, .name = "ROMFPB", }, { .address = 0xE00FF00C, .name = "ROMITM", }, { .address = 0xE00FF010, .name = "ROMTPIU", }, { .address = 0xE00FF014, .name = "ROMETM", }, { .address = 0xE00FFFCC, .name = "MEMTYPE", }, { .address = 0xE00FFFD0, .name = "PID4", }, { .address = 0xE00FFFD4, .name = "PID5", }, { .address = 0xE00FFFD8, .name = "PID6", }, { .address = 0xE00FFFDC, .name = "PID7", }, { .address = 0xE00FFFE0, .name = "PID0", }, { .address = 0xE00FFFE4, .name = "PID1", }, { .address = 0xE00FFFE8, .name = "PID2", }, { .address = 0xE00FFFEC, .name = "PID3", }, { .address = 0xE00FFFF0, .name = "CID0", }, { .address = 0xE00FFFF4, .name = "CID1", }, { .address = 0xE00FFFF8, .name = "CID2", }, { .address = 0xE00FFFFC, .name = "CID3", }, }; static void rom_table(void) { // ROM table is at 0xE00FF000-0xE00FFFFF // see ARMv7-M ARM C1.2.2 The ARMv7-M ROM Table printf("ROM table entries:\r\n"); for (uint8_t i = 0; i < LENGTH(rom_registers); i++) { const uint32_t value = *(uint32_t*)rom_registers[i].address; printf("- 0x%08lx (%s): 0x%08lx\r\n", rom_registers[i].address, rom_registers[i].name, value); } } struct range_accessibility_t { uint32_t start; uint32_t end; bool accessible; }; struct mcu_map_t { const char* name; const struct range_accessibility_t* ranges; const uint8_t ranges_nb; }; static const struct range_accessibility_t ranges_stm32f103[] = { { .start = 0x00000000, .end = 0x0001ffff, .accessible = true, }, { .start = 0x00020000, .end = 0x07ffffff, .accessible = false, }, { .start = 0x08000000, .end = 0x0801ffff, .accessible = true, }, { .start = 0x08020000, .end = 0x1fffefff, .accessible = false, }, { .start = 0x1ffff000, .end = 0x1ffff9ff, .accessible = true, }, { .start = 0x1ffffa00, .end = 0x1fffffff, .accessible = false, }, { .start = 0x20000000, .end = 0x20004fff, .accessible = true, }, { .start = 0x20005000, .end = 0x21ffffff, .accessible = false, }, { .start = 0x22000000, .end = 0x2209ffff, .accessible = true, }, { .start = 0x220a0000, .end = 0x3fffffff, .accessible = false, }, { .start = 0x40000000, .end = 0x40007fff, .accessible = true, }, { .start = 0x40008000, .end = 0x4000ffff, .accessible = false, }, { .start = 0x40010000, .end = 0x40013fff, .accessible = true, }, { .start = 0x40014000, .end = 0x4001ffff, .accessible = false, }, { .start = 0x40020000, .end = 0x400203ff, .accessible = true, }, { .start = 0x40020400, .end = 0x40020fff, .accessible = false, }, { .start = 0x40021000, .end = 0x400213ff, .accessible = true, }, { .start = 0x40021400, .end = 0x40022fff, .accessible = false, }, { .start = 0x40023000, .end = 0x400233ff, .accessible = true, }, { .start = 0x40023400, .end = 0x41ffffff, .accessible = false, }, { .start = 0x42000000, .end = 0x420fffff, .accessible = true, }, { .start = 0x42100000, .end = 0x421fffff, .accessible = false, }, { .start = 0x42200000, .end = 0x4227ffff, .accessible = true, }, { .start = 0x42280000, .end = 0x423fffff, .accessible = false, }, { .start = 0x42400000, .end = 0x42407fff, .accessible = true, }, { .start = 0x42408000, .end = 0x4241ffff, .accessible = false, }, { .start = 0x42420000, .end = 0x42427fff, .accessible = true, }, { .start = 0x42428000, .end = 0x4245ffff, .accessible = false, }, { .start = 0x42460000, .end = 0x42467fff, .accessible = true, }, { .start = 0x42468000, .end = 0xdfffffff, .accessible = false, }, { .start = 0xe0000000, .end = 0xe00fffff, .accessible = true, }, { .start = 0xe0100000, .end = 0xffffffff, .accessible = false, }, }; static const struct range_accessibility_t ranges_cks32f103c8[] = { { .start = 0x00000000, .end = 0x0801ffff, .accessible = true, }, { .start = 0x08020000, .end = 0x1fffefff, .accessible = false, }, { .start = 0x1ffff000, .end = 0x1ffff9ff, .accessible = true, }, { .start = 0x1ffffa00, .end = 0x1fffffff, .accessible = false, }, { .start = 0x20000000, .end = 0x40000bff, .accessible = true, }, { .start = 0x40000c00, .end = 0x400027ff, .accessible = false, }, { .start = 0x40002800, .end = 0x400033ff, .accessible = true, }, { .start = 0x40003400, .end = 0x400037ff, .accessible = false, }, { .start = 0x40003800, .end = 0x40003bff, .accessible = true, }, { .start = 0x40003c00, .end = 0x400043ff, .accessible = false, }, { .start = 0x40004400, .end = 0x40004bff, .accessible = true, }, { .start = 0x40004c00, .end = 0x400053ff, .accessible = false, }, { .start = 0x40005400, .end = 0x400067ff, .accessible = true, }, { .start = 0x40006800, .end = 0x40006bff, .accessible = false, }, { .start = 0x40006c00, .end = 0x400073ff, .accessible = true, }, { .start = 0x40007400, .end = 0x4000ffff, .accessible = false, }, { .start = 0x40010000, .end = 0x40011bff, .accessible = true, }, { .start = 0x40011c00, .end = 0x400123ff, .accessible = false, }, { .start = 0x40012400, .end = 0x400133ff, .accessible = true, }, { .start = 0x40013400, .end = 0x400137ff, .accessible = false, }, { .start = 0x40013800, .end = 0x40013bff, .accessible = true, }, { .start = 0x40013c00, .end = 0x4001ffff, .accessible = false, }, { .start = 0x40020000, .end = 0x400203ff, .accessible = true, }, { .start = 0x40020400, .end = 0x40020fff, .accessible = false, }, { .start = 0x40021000, .end = 0x400213ff, .accessible = true, }, { .start = 0x40021400, .end = 0x40021fff, .accessible = false, }, { .start = 0x40022000, .end = 0x400223ff, .accessible = true, }, { .start = 0x40022400, .end = 0x40022fff, .accessible = false, }, { .start = 0x40023000, .end = 0x400233ff, .accessible = true, }, { .start = 0x40023400, .end = 0x41ffffff, .accessible = false, }, { .start = 0x42000000, .end = 0x42017fff, .accessible = true, }, { .start = 0x42018000, .end = 0x4204ffff, .accessible = false, }, { .start = 0x42050000, .end = 0x42067fff, .accessible = true, }, { .start = 0x42068000, .end = 0x4206ffff, .accessible = false, }, { .start = 0x42070000, .end = 0x42077fff, .accessible = true, }, { .start = 0x42078000, .end = 0x42087fff, .accessible = false, }, { .start = 0x42088000, .end = 0x42097fff, .accessible = true, }, { .start = 0x42098000, .end = 0x420a7fff, .accessible = false, }, { .start = 0x420a8000, .end = 0x420cffff, .accessible = true, }, { .start = 0x420d0000, .end = 0x420d7fff, .accessible = false, }, { .start = 0x420d8000, .end = 0x420e7fff, .accessible = true, }, { .start = 0x420e8000, .end = 0x421fffff, .accessible = false, }, { .start = 0x42200000, .end = 0x42237fff, .accessible = true, }, { .start = 0x42238000, .end = 0x42247fff, .accessible = false, }, { .start = 0x42248000, .end = 0x42267fff, .accessible = true, }, { .start = 0x42268000, .end = 0x4226ffff, .accessible = false, }, { .start = 0x42270000, .end = 0x42277fff, .accessible = true, }, { .start = 0x42278000, .end = 0x423fffff, .accessible = false, }, { .start = 0x42400000, .end = 0x42407fff, .accessible = true, }, { .start = 0x42408000, .end = 0x4241ffff, .accessible = false, }, { .start = 0x42420000, .end = 0x42427fff, .accessible = true, }, { .start = 0x42428000, .end = 0x4243ffff, .accessible = false, }, { .start = 0x42440000, .end = 0x42447fff, .accessible = true, }, { .start = 0x42448000, .end = 0x4245ffff, .accessible = false, }, { .start = 0x42460000, .end = 0x42467fff, .accessible = true, }, { .start = 0x42468000, .end = 0xa0000fff, .accessible = false, }, { .start = 0xa0001000, .end = 0xffffffff, .accessible = true, }, }; static const struct range_accessibility_t ranges_gd32f103c8[] = { { .start = 0x00000000, .end = 0x20004fff, .accessible = true, }, { .start = 0x20005000, .end = 0x21ffffff, .accessible = false, }, { .start = 0x22000000, .end = 0x2209ffff, .accessible = true, }, { .start = 0x220a0000, .end = 0x3fffffff, .accessible = false, }, { .start = 0x40000000, .end = 0x40017fff, .accessible = true, }, { .start = 0x40018000, .end = 0x4001ffff, .accessible = false, }, { .start = 0x40020000, .end = 0x400203ff, .accessible = true, }, { .start = 0x40020400, .end = 0x40020fff, .accessible = false, }, { .start = 0x40021000, .end = 0x400213ff, .accessible = true, }, { .start = 0x40021400, .end = 0x40021fff, .accessible = false, }, { .start = 0x40022000, .end = 0x400223ff, .accessible = true, }, { .start = 0x40022400, .end = 0x40022fff, .accessible = false, }, { .start = 0x40023000, .end = 0x400233ff, .accessible = true, }, { .start = 0x40023400, .end = 0x41ffffff, .accessible = false, }, { .start = 0x42000000, .end = 0x422fffff, .accessible = true, }, { .start = 0x42300000, .end = 0x423fffff, .accessible = false, }, { .start = 0x42400000, .end = 0x42407fff, .accessible = true, }, { .start = 0x42408000, .end = 0x4241ffff, .accessible = false, }, { .start = 0x42420000, .end = 0x42427fff, .accessible = true, }, { .start = 0x42428000, .end = 0x4243ffff, .accessible = false, }, { .start = 0x42440000, .end = 0x42447fff, .accessible = true, }, { .start = 0x42448000, .end = 0x4245ffff, .accessible = false, }, { .start = 0x42460000, .end = 0x42467fff, .accessible = true, }, { .start = 0x42468000, .end = 0x5fffffff, .accessible = false, }, { .start = 0x60000000, .end = 0xa0000fff, .accessible = true, }, { .start = 0xa0001000, .end = 0xdfffffff, .accessible = false, }, { .start = 0xe0000000, .end = 0xe00fffff, .accessible = true, }, { .start = 0xe0100000, .end = 0xffffffff, .accessible = false, }, }; static const struct range_accessibility_t ranges_apm32f103c8[] = { { .start = 0x00000000, .end = 0x0001ffff, .accessible = true, }, { .start = 0x00020000, .end = 0x07ffffff, .accessible = false, }, { .start = 0x08000000, .end = 0x0801ffff, .accessible = true, }, { .start = 0x08020000, .end = 0x1fffefff, .accessible = false, }, { .start = 0x1ffff000, .end = 0x1ffffbff, .accessible = true, }, { .start = 0x1ffffc00, .end = 0x1fffffff, .accessible = false, }, { .start = 0x20000000, .end = 0x20004fff, .accessible = true, }, { .start = 0x20005000, .end = 0x21ffffff, .accessible = false, }, { .start = 0x22000000, .end = 0x2209ffff, .accessible = true, }, { .start = 0x220a0000, .end = 0x3fffffff, .accessible = false, }, { .start = 0x40000000, .end = 0x40017fff, .accessible = true, }, { .start = 0x40018000, .end = 0x4001ffff, .accessible = false, }, { .start = 0x40020000, .end = 0x400203ff, .accessible = true, }, { .start = 0x40020400, .end = 0x40020fff, .accessible = false, }, { .start = 0x40021000, .end = 0x400213ff, .accessible = true, }, { .start = 0x40021400, .end = 0x40022fff, .accessible = false, }, { .start = 0x40023000, .end = 0x400233ff, .accessible = true, }, { .start = 0x40023400, .end = 0x40023fff, .accessible = false, }, { .start = 0x40024000, .end = 0x400243ff, .accessible = true, }, { .start = 0x40024400, .end = 0x41ffffff, .accessible = false, }, { .start = 0x42000000, .end = 0x422fffff, .accessible = true, }, { .start = 0x42300000, .end = 0x423fffff, .accessible = false, }, { .start = 0x42400000, .end = 0x42407fff, .accessible = true, }, { .start = 0x42408000, .end = 0x4241ffff, .accessible = false, }, { .start = 0x42420000, .end = 0x42427fff, .accessible = true, }, { .start = 0x42428000, .end = 0x4245ffff, .accessible = false, }, { .start = 0x42460000, .end = 0x42467fff, .accessible = true, }, { .start = 0x42468000, .end = 0x4247ffff, .accessible = false, }, { .start = 0x42480000, .end = 0x42487fff, .accessible = true, }, { .start = 0x42488000, .end = 0x5fffffff, .accessible = false, }, { .start = 0x60000000, .end = 0xafffffff, .accessible = true, }, { .start = 0xb0000000, .end = 0xdfffffff, .accessible = false, }, { .start = 0xe0000000, .end = 0xe00fffff, .accessible = true, }, { .start = 0xe0100000, .end = 0xffffffff, .accessible = false, }, }; static const struct range_accessibility_t ranges_hk32f103c8[] = { { .start = 0x00000000, .end = 0x20004fff, .accessible = true, }, { .start = 0x20005000, .end = 0x21ffffff, .accessible = false, }, { .start = 0x22000000, .end = 0x2209ffff, .accessible = true, }, { .start = 0x220a0000, .end = 0x3fffffff, .accessible = false, }, { .start = 0x40000000, .end = 0x40017fff, .accessible = true, }, { .start = 0x40018000, .end = 0x4001ffff, .accessible = false, }, { .start = 0x40020000, .end = 0x400203ff, .accessible = true, }, { .start = 0x40020400, .end = 0x40020fff, .accessible = false, }, { .start = 0x40021000, .end = 0x400213ff, .accessible = true, }, { .start = 0x40021400, .end = 0x40021fff, .accessible = false, }, { .start = 0x40022000, .end = 0x400223ff, .accessible = true, }, { .start = 0x40022400, .end = 0x40022fff, .accessible = false, }, { .start = 0x40023000, .end = 0x400233ff, .accessible = true, }, { .start = 0x40023400, .end = 0x4002ffff, .accessible = false, }, { .start = 0x40030000, .end = 0x400303ff, .accessible = true, }, { .start = 0x40030400, .end = 0x41ffffff, .accessible = false, }, { .start = 0x42000000, .end = 0x422fffff, .accessible = true, }, { .start = 0x42300000, .end = 0x423fffff, .accessible = false, }, { .start = 0x42400000, .end = 0x42407fff, .accessible = true, }, { .start = 0x42408000, .end = 0x4241ffff, .accessible = false, }, { .start = 0x42420000, .end = 0x42427fff, .accessible = true, }, { .start = 0x42428000, .end = 0x4243ffff, .accessible = false, }, { .start = 0x42440000, .end = 0x42447fff, .accessible = true, }, { .start = 0x42448000, .end = 0x4245ffff, .accessible = false, }, { .start = 0x42460000, .end = 0x42467fff, .accessible = true, }, { .start = 0x42468000, .end = 0x425fffff, .accessible = false, }, { .start = 0x42600000, .end = 0x42607fff, .accessible = true, }, { .start = 0x42608000, .end = 0xdfffffff, .accessible = false, }, { .start = 0xe0000000, .end = 0xe00fffff, .accessible = true, }, { .start = 0xe0100000, .end = 0xffffffff, .accessible = false, }, }; static const struct mcu_map_t mcu_maps[] = { { .name = "STM32F103Cx", .ranges = ranges_stm32f103, .ranges_nb = LENGTH(ranges_stm32f103), }, { .name = "CKS32F103C8", .ranges = ranges_cks32f103c8, .ranges_nb = LENGTH(ranges_cks32f103c8), }, { .name = "GD32F103C8", .ranges = ranges_gd32f103c8, .ranges_nb = LENGTH(ranges_gd32f103c8), }, { .name = "APM32F103C8", .ranges = ranges_apm32f103c8, .ranges_nb = LENGTH(ranges_apm32f103c8), }, { .name = "HK32F103C8", .ranges = ranges_hk32f103c8, .ranges_nb = LENGTH(ranges_hk32f103c8), }, }; static void memory_map(void) { #define ADDRESS_START 0UL // the start address of the range to test for accessibility #define ADDRESS_END 0xffffffffUL // the end address of the range to test for accessibility #if ADDRESS_END < ADDRESS_START || ADDRESS_START > UINT32_MAX || ADDRESS_END > UINT32_MAX #error "error in range to test" #else printf("testing accessibility of address space (takes 2 minutes):\r\n"); struct range_accessibility_t ranges[57]; uint8_t ranges_nb = 0; cm_disable_faults(); // disable all faults, particularly BusFault SCB_CFSR |= SCB_CFSR_BFARVALID; // clear BusFault flag SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them) uint32_t start = ADDRESS_START; // start address of (in)accessible range (void)*(uint8_t*)start; // access the first address for a correct start bool accessible = (0 == (SCB_CFSR & SCB_CFSR_BFARVALID)); for (uint64_t addr = ADDRESS_START; addr <= ADDRESS_END; addr += 256) { // go through address space (but don't check every byte) const uint32_t address = (uint32_t)addr; (void)*(volatile uint8_t*)address; // try accessing address const bool valid = (0 == (SCB_CFSR & SCB_CFSR_BFARVALID)); // check is a bus fault occurred if (!valid) { SCB_CFSR |= SCB_CFSR_BFARVALID; // clear BusFault flag } if (valid != accessible) { // range is now [not] accessible // re-enable interrupts to be able to transmit SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault cm_enable_faults(); // re-enable faults printf("- 0x%08lx-0x%08lx: %s\r\n", start, address - 1, accessible ? "accessible" : "inaccessible"); // show range if (ranges_nb < LENGTH(ranges)) { ranges[ranges_nb].start = start; ranges[ranges_nb].end = address - 1; ranges[ranges_nb].accessible = accessible; ranges_nb++; } start = addr; // start with new range accessible = valid; // remember range accessibility cm_disable_faults(); // disable all faults, particularly BusFault SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them) } } SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault cm_enable_faults(); // re-enable faults printf("- 0x%08lx-0x%08lx: %s\r\n", start, ADDRESS_END, accessible ? "accessible" : "inaccessible"); // show range if (ranges_nb < LENGTH(ranges)) { ranges[ranges_nb].start = start; ranges[ranges_nb].end = ADDRESS_END; ranges[ranges_nb].accessible = accessible; ranges_nb++; } bool found = false; for (uint8_t i = 0; i < LENGTH(mcu_maps); i++) { if (mcu_maps[i].ranges_nb != ranges_nb) { continue; } bool match = true; for (uint8_t j = 0; j < mcu_maps[i].ranges_nb; j++) { if (mcu_maps[i].ranges[j].start != ranges[j].start || mcu_maps[i].ranges[j].end != ranges[j].end || mcu_maps[i].ranges[j].accessible != ranges[j].accessible) { match = false; break; } } if (match) { found = true; printf("- memory map matches %s\r\n", mcu_maps[i].name); } } if (!found) { printf("- no matching memory map found. please send chip details (press a) to f103id@cuvoodoo.info\r\n"); } #endif } struct gpio_def_t { const enum rcc_periph_clken rcc; /**< GPIO RCC */ const uint32_t port; /**< GPIO port */ const uint16_t* pins; /**< GPIO pin */ const uint8_t pins_nb; /**< GPIO pin */ }; struct pin_count_t { const char name; /**< letter corresponding to pin count ordering schema */ const uint8_t count; /**< number of pins */ const struct gpio_def_t* gpio; /**< list of pins exclusive to this model */ const uint8_t gpio_nb; /**< number of pins exclusive to this model */ }; static const uint16_t gpio_z_f[] = {GPIO11, GPIO12, GPIO13, GPIO14, GPIO15}; static const uint16_t gpio_z_g[] = {GPIO0, GPIO1, GPIO2, GPIO3, GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, GPIO13, GPIO14, GPIO15}; static const struct gpio_def_t gpio_z[] = { { .rcc = RCC_GPIOF, .port = GPIOF, .pins = gpio_z_f, .pins_nb = LENGTH(gpio_z_f), }, { .rcc = RCC_GPIOG, .port = GPIOG, .pins = gpio_z_g, .pins_nb = LENGTH(gpio_z_g), }, }; static const struct pin_count_t pins_z = { .name = 'Z', .count = 144, .gpio = gpio_z, .gpio_nb = LENGTH(gpio_z), }; static const uint16_t gpio_v_d[] = {GPIO3, GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, GPIO13, GPIO14, GPIO15}; static const uint16_t gpio_v_e[] = {GPIO0, GPIO1, GPIO2, GPIO3, GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, GPIO13, GPIO14, GPIO15}; static const struct gpio_def_t gpio_v[] = { { .rcc = RCC_GPIOD, .port = GPIOD, .pins = gpio_v_d, .pins_nb = LENGTH(gpio_v_d), }, { .rcc = RCC_GPIOE, .port = GPIOE, .pins = gpio_v_e, .pins_nb = LENGTH(gpio_v_e), }, }; static const struct pin_count_t pins_v = { .name = 'V', .count = 100, .gpio = gpio_v, .gpio_nb = LENGTH(gpio_v), }; static const uint16_t gpio_r_c[] = {GPIO0, GPIO1, GPIO2, GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12}; // PC3 is not on TFBGA64 but LQFP64 static const uint16_t gpio_r_d[] = {GPIO2}; // PD0 and PD1 are on TFBGA64 but on LQFP64 static const struct gpio_def_t gpio_r[] = { { .rcc = RCC_GPIOC, .port = GPIOC, .pins = gpio_r_c, .pins_nb = LENGTH(gpio_r_c), }, { .rcc = RCC_GPIOD, .port = GPIOD, .pins = gpio_r_d, .pins_nb = LENGTH(gpio_r_d), }, }; static const struct pin_count_t pins_r = { .name = 'R', .count = 64, .gpio = gpio_r, .gpio_nb = LENGTH(gpio_r), }; static const uint16_t gpio_c_b[] = {GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, GPIO13, GPIO14}; static const struct gpio_def_t gpio_c[] = { { .rcc = RCC_GPIOB, .port = GPIOB, .pins = gpio_c_b, .pins_nb = LENGTH(gpio_c_b), }, }; static const struct pin_count_t pins_c = { .name = 'C', .count = 48, .gpio = gpio_c, .gpio_nb = LENGTH(gpio_c), }; // any one pin is enough since all variants should have it static const uint16_t gpio_t_b[] = {GPIO0}; static const struct gpio_def_t gpio_t[] = { { .rcc = RCC_GPIOB, .port = GPIOB, .pins = gpio_t_b, .pins_nb = LENGTH(gpio_t_b), }, }; static const struct pin_count_t pins_t = { .name = 'T', .count = 36, .gpio = gpio_t, .gpio_nb = LENGTH(gpio_t), }; // pin count variants, from most to least pins static const struct pin_count_t pins_counts[] = {pins_z, pins_v, pins_r, pins_c, pins_t}; // try to figure out how many pins are present // works well for STM32 and CKS32, but not GD32 (100 found for 48-pin chip) // on STM32GC102 pull down does not set the input low static void pin_count(void) { printf("testing pins to figure out variant (pins tied to ground can lead to false positives):\r\n"); // figure out which pin count variant this MCU is // just checking the default values of the ports is not enough (103C has GPIOE and GPIOF peripherals although it should have not pins from these ports) // this we test if setting the pins actually work for (uint8_t i = 0; i < LENGTH(pins_counts); i++) { bool port_accessible = true; // if the port/pin exisits for (uint8_t j = 0; j < pins_counts[i].gpio_nb; j++) { // check if port exists cm_disable_faults(); // disable all faults, particularly BusFault SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them) *(volatile uint32_t*)pins_counts[i].gpio[j].port; // read register if (SCB_CFSR & SCB_CFSR_BFARVALID) { // bus fault occurs port_accessible = false; } SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault cm_enable_faults(); // re-enable faults if (!port_accessible) { break; } // port A which is the only one used, for serial, should not be tested rcc_periph_clock_enable(pins_counts[i].gpio[j].rcc); // enable clock to get correct values from registers for (uint8_t k = 0; k < pins_counts[i].gpio[j].pins_nb; k++) { // test each pin for (volatile uint32_t wait = 0; wait < 10; wait++); // somehow not waiting a bit can lead to false negatives gpio_set_mode(pins_counts[i].gpio[j].port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, pins_counts[i].gpio[j].pins[k]); // set a input to not affect to much anything connected to the pin gpio_clear(pins_counts[i].gpio[j].port, pins_counts[i].gpio[j].pins[k]); // be sure it is pulled low if (gpio_get(pins_counts[i].gpio[j].port, pins_counts[i].gpio[j].pins[k])) { // pin seems to be tied high externally continue; // this pin can't be tested } gpio_set(pins_counts[i].gpio[j].port, pins_counts[i].gpio[j].pins[k]); // pull up (using set/reset register) //GPIO_ODR(pins_counts[i].gpio[j].port) |= pins_counts[i].gpio[j].pins[k]; // pull up (using output register) if (0 == (GPIO_ODR(pins_counts[i].gpio[j].port) & pins_counts[i].gpio[j].pins[k])) { // pull up is not registered port_accessible = false; } if (0 == (GPIO_IDR(pins_counts[i].gpio[j].port) & pins_counts[i].gpio[j].pins[k])) { // when the pin is missing the pin remains low although it is pulled up port_accessible = false; } gpio_clear(pins_counts[i].gpio[j].port, pins_counts[i].gpio[j].pins[k]); // set back to default state (using set/reset register) //GPIO_ODR(pins_counts[i].gpio[j].port) &= ~pins_counts[i].gpio[j].pins[k]; // set back to default state (using output register) if (0 != (GPIO_ODR(pins_counts[i].gpio[j].port) & pins_counts[i].gpio[j].pins[k])) { // pull down is not registered port_accessible = false; } if (0 != (GPIO_IDR(pins_counts[i].gpio[j].port) & pins_counts[i].gpio[j].pins[k])) { // be sure is it low port_accessible = false; } gpio_set_mode(pins_counts[i].gpio[j].port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, pins_counts[i].gpio[j].pins[k]); // puts back to default state if (!port_accessible) { break; } } rcc_periph_clock_enable(pins_counts[i].gpio[j].rcc); // put back to disabled mode } if (port_accessible) { printf("- this is pin count variant %c, with %u pins\r\n", pins_counts[i].name, pins_counts[i].count); pin_count_variant = pins_counts[i].name; pin_count_nb = pins_counts[i].count; break; } else { printf("- this is not pin count variant %c, with %u pins\r\n", pins_counts[i].name, pins_counts[i].count); } } if (0 == pin_count_nb) { printf("- could not identify number of pins: the pinout is unknown\r\n"); } } /** how to test a peripheral */ struct periph_test_t { const char* name; /**< name of the peripheral */ const enum rcc_periph_clken rcc; /**< peripheral RCC */ const enum rcc_periph_rst rst; /**< peripheral reset signal */ volatile uint32_t* reg; /**< register of this peripheral to test */ const uint32_t value; /**< value to write to the register */ }; enum periph_e { // don't list GPIOA since we use it for USART1 PERIPH_GPIOB, PERIPH_GPIOC, PERIPH_GPIOD, PERIPH_GPIOE, PERIPH_GPIOF, PERIPH_GPIOG, PERIPH_TIM1, // advanced PERIPH_TIM2, // general purpose PERIPH_TIM3, // general purpose PERIPH_TIM4, // general purpose PERIPH_TIM5, // general PERIPH_TIM6, // basic PERIPH_TIM7, // basic PERIPH_TIM8, // advanced PERIPH_TIM15, // general purpose PERIPH_TIM16, // general purpose PERIPH_TIM17, // general purpose // don't list USART1 since we use is for communication PERIPH_USART2, PERIPH_USART3, PERIPH_SPI1, PERIPH_SPI2, PERIPH_I2C1, PERIPH_I2C2, PERIPH_CEC, PERIPH_USB, PERIPH_CAN1, PERIPH_CAN2, PERIPH_MAX, }; static bool mcu_peripherals[PERIPH_MAX]; static const struct periph_test_t periph_tests[PERIPH_MAX] = { [PERIPH_GPIOB] = { .name = "GPIOB", .rcc = RCC_GPIOB, .rst = RST_GPIOB, .reg = &GPIOB_ODR, // write to output register, which should no cause any trouble when the pin in an input .value = (1 << 0), }, [PERIPH_GPIOC] = { .name = "GPIOC", .rcc = RCC_GPIOC, .rst = RST_GPIOC, .reg = &GPIOC_ODR, // write to output register, which should no cause any trouble when the pin in an input .value = (1 << 0), }, [PERIPH_GPIOD] = { .name = "GPIOD", .rcc = RCC_GPIOD, .rst = RST_GPIOD, .reg = &GPIOD_ODR, // write to output register, which should no cause any trouble when the pin in an input .value = (1 << 0), }, [PERIPH_GPIOE] = { .name = "GPIOE", .rcc = RCC_GPIOE, .rst = RST_GPIOE, .reg = &GPIOE_ODR, // write to output register, which should no cause any trouble when the pin in an input .value = (1 << 0), }, [PERIPH_GPIOF] = { .name = "GPIOF", .rcc = RCC_GPIOF, .rst = RST_GPIOF, .reg = &GPIOF_ODR, // write to output register, which should no cause any trouble when the pin in an input .value = (1 << 0), }, [PERIPH_GPIOG] = { .name = "GPIOG", .rcc = RCC_GPIOG, .rst = RST_GPIOG, .reg = &GPIOG_ODR, // write to output register, which should no cause any trouble when the pin in an input .value = (1 << 0), }, [PERIPH_TIM1] = { .name = "TIM1", .rcc = RCC_TIM1, .rst = RST_TIM1, .reg = &TIM1_CNT, // writing in the counter register has no side effect .value = (1 << 0), }, [PERIPH_TIM2] = { .name = "TIM2", .rcc = RCC_TIM2, .rst = RST_TIM2, .reg = &TIM2_CNT, // writing in the counter register has no side effect .value = (1 << 0), }, [PERIPH_TIM3] = { .name = "TIM3", .rcc = RCC_TIM3, .rst = RST_TIM3, .reg = &TIM3_CNT, // writing in the counter register has no side effect .value = (1 << 0), }, [PERIPH_TIM4] = { .name = "TIM4", .rcc = RCC_TIM4, .rst = RST_TIM4, .reg = &TIM4_CNT, // writing in the counter register has no side effect .value = (1 << 0), }, [PERIPH_TIM5] = { .name = "TIM5", .rcc = RCC_TIM5, .rst = RST_TIM5, .reg = &TIM5_CNT, // writing in the counter register has no side effect .value = (1 << 0), }, [PERIPH_TIM6] = { .name = "TIM6", .rcc = RCC_TIM6, .rst = RST_TIM6, .reg = &TIM6_CNT, // writing in the counter register has no side effect .value = (1 << 0), }, [PERIPH_TIM7] = { .name = "TIM7", .rcc = RCC_TIM7, .rst = RST_TIM7, .reg = &TIM7_CNT, // writing in the counter register has no side effect .value = (1 << 0), }, [PERIPH_TIM8] = { .name = "TIM8", .rcc = RCC_TIM8, .rst = RST_TIM8, .reg = &TIM8_CNT, // writing in the counter register has no side effect .value = (1 << 0), }, [PERIPH_TIM15] = { .name = "TIM15", .rcc = RCC_TIM15, .rst = RST_TIM15, .reg = &TIM15_CNT, // writing in the counter register has no side effect .value = (1 << 0), }, [PERIPH_TIM16] = { .name = "TIM16", .rcc = RCC_TIM16, .rst = RST_TIM16, .reg = &TIM16_CNT, // writing in the counter register has no side effect .value = (1 << 0), }, [PERIPH_TIM17] = { .name = "TIM17", .rcc = RCC_TIM17, .rst = RST_TIM17, .reg = &TIM17_CNT, // writing in the counter register has no side effect .value = (1 << 0), }, [PERIPH_USART2] = { .name = "USART2", .rcc = RCC_USART2, .rst = RST_USART2, .reg = &USART2_BRR, // write a fake baud rate .value = (1 << 0), }, [PERIPH_USART3] = { .name = "USART3", .rcc = RCC_USART3, .rst = RST_USART3, .reg = &USART3_BRR, // write a fake baud rate .value = (1 << 0), }, [PERIPH_SPI1] = { .name = "SPI1", .rcc = RCC_SPI1, .rst = RST_SPI1, .reg = &SPI1_CRCPR, // write to the polynomial (should already be at 7) .value = (1 << 3), }, [PERIPH_SPI2] = { .name = "SPI2", .rcc = RCC_SPI2, .rst = RST_SPI2, .reg = &SPI2_CRCPR, // write to the polynomial (should already be at 7) .value = (1 << 3), }, [PERIPH_I2C1] = { .name = "I2C1", .rcc = RCC_I2C1, .rst = RST_I2C1, .reg = &I2C1_OAR1, // write own address .value = (1 << 0), }, [PERIPH_I2C2] = { .name = "I2C2", .rcc = RCC_I2C2, .rst = RST_I2C2, .reg = &I2C2_OAR1, // write own address .value = (1 << 0), }, [PERIPH_CEC] = { .name = "CEC", .rcc = RCC_CEC, .rst = RST_CEC, .reg = &CEC_CFGR, // write own address .value = (1 << 0), }, [PERIPH_USB] = { .name = "USB", .rcc = RCC_USB, .rst = RST_USB, .reg = USB_DADDR_REG, // device address .value = (1 << 0), }, [PERIPH_CAN1] = { .name = "CAN1", .rcc = RCC_CAN1, .rst = RST_CAN1, .reg = &CAN_MCR(CAN1), .value = (1 << 4), // write NART }, [PERIPH_CAN2] = { .name = "CAN2", .rcc = RCC_CAN2, .rst = RST_CAN2, .reg = &CAN_MCR(CAN2), .value = (1 << 4), // write NART }, /* [PERIPH_] = { .name = "", .rcc = RCC_, .rst = RST_, .reg = &, .mask = 0x, .value = (1 << ), }, */ }; // any bus fault error #define SCB_CFSR_BFERR (SCB_CFSR_IBUSERR | SCB_CFSR_PRECISERR | SCB_CFSR_IMPRECISERR | SCB_CFSR_UNSTKERR | SCB_CFSR_STKERR | SCB_CFSR_BFARVALID) static void peripherals(void) { printf("testing peripheral presence:\r\n"); for (uint8_t i = 0; i < LENGTH(periph_tests); i++) { const struct periph_test_t peripheral = periph_tests[i]; mcu_peripherals[i] = false; // initialise if (NULL == peripheral.name || 0 == peripheral.reg) { // if the name is missing, the rest probably is too continue; } rcc_periph_clock_enable(peripheral.rcc); // enable clock for domain to read the registers rcc_periph_reset_pulse(peripheral.rst); // be sure the values have their reset value // test if the register exists cm_disable_faults(); // disable all faults, particularly BusFault SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them) const uint32_t value = *peripheral.reg; // read register bool periph_accessible = (0 == (SCB_CFSR & SCB_CFSR_BFARVALID)); // check for fault occurs SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag // test if register is writable bool periph_writable = false; if (periph_accessible) { *peripheral.reg |= peripheral.value; periph_writable = (peripheral.value == (*peripheral.reg & peripheral.value)); *peripheral.reg = value; // write back previous value if (SCB_CFSR & SCB_CFSR_BFERR) { // bus fault error occurred (should be SCB_CFSR_IMPRECISERR but we catch all) periph_accessible = false; // accessing failed periph_writable = false; // writing failed SCB_CFSR |= SCB_CFSR_BFERR; // clear all error flags } } SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault cm_enable_faults(); // re-enable faults // clean up rcc_periph_reset_pulse(peripheral.rst); // be sure the values have their reset value rcc_periph_clock_disable(peripheral.rcc); // disable domain again mcu_peripherals[i] = periph_accessible && periph_writable; printf("- %s: %s\r\n", peripheral.name, periph_accessible ? (periph_writable ? "present" : "absent") : "missing"); } } struct mcu_model_t { char* name; uint8_t flash; // in KB uint8_t sram; // in KB uint8_t pins; // package pin count bool* peripherals; // which of the peripherals are present }; // according to datasheet (untested) static const bool peripheralas_f100x6[PERIPH_MAX] = { [PERIPH_GPIOB] = true, [PERIPH_GPIOC] = true, [PERIPH_TIM1] = true, [PERIPH_TIM2] = true, [PERIPH_TIM3] = true, [PERIPH_TIM5] = true, [PERIPH_TIM15] = true, [PERIPH_TIM16] = true, [PERIPH_TIM17] = true, [PERIPH_USART2] = true, [PERIPH_SPI1] = true, [PERIPH_I2C1] = true, [PERIPH_CEC] = true, }; // according to datasheet (untested) static const bool peripheralas_f100x8[PERIPH_MAX] = { [PERIPH_GPIOB] = true, [PERIPH_GPIOC] = true, [PERIPH_TIM1] = true, [PERIPH_TIM2] = true, [PERIPH_TIM3] = true, [PERIPH_TIM4] = true, [PERIPH_TIM5] = true, [PERIPH_TIM15] = true, [PERIPH_TIM16] = true, [PERIPH_TIM17] = true, [PERIPH_USART2] = true, [PERIPH_USART3] = true, [PERIPH_SPI1] = true, [PERIPH_SPI2] = true, [PERIPH_I2C1] = true, [PERIPH_I2C2] = true, [PERIPH_CEC] = true, }; // according to datasheet (untested) static const bool peripheralas_f103t8[PERIPH_MAX] = { [PERIPH_GPIOB] = true, [PERIPH_GPIOC] = true, [PERIPH_TIM1] = true, [PERIPH_TIM2] = true, [PERIPH_TIM3] = true, [PERIPH_TIM4] = true, [PERIPH_USART2] = true, [PERIPH_SPI1] = true, [PERIPH_I2C1] = true, [PERIPH_USB] = true, [PERIPH_CAN1] = true, }; // according to datasheet (tested) static const bool peripheralas_f103c8[PERIPH_MAX] = { [PERIPH_GPIOB] = true, [PERIPH_GPIOC] = true, [PERIPH_GPIOD] = true, // this is not defined in datasheet [PERIPH_GPIOE] = true, // this is not defined in datasheet [PERIPH_TIM1] = true, [PERIPH_TIM2] = true, [PERIPH_TIM3] = true, [PERIPH_TIM4] = true, [PERIPH_USART2] = true, [PERIPH_USART3] = true, [PERIPH_SPI1] = true, [PERIPH_SPI2] = true, [PERIPH_I2C1] = true, [PERIPH_I2C2] = true, [PERIPH_USB] = true, [PERIPH_CAN1] = true, }; // empirically found (tested) static const bool peripheralas_hk32f103c8[PERIPH_MAX] = { [PERIPH_GPIOB] = true, [PERIPH_GPIOC] = true, [PERIPH_GPIOD] = true, // this is not defined in STM32 datasheet //[PERIPH_GPIOE] = true, // contrary to STM32, this is not present [PERIPH_TIM1] = true, [PERIPH_TIM2] = true, [PERIPH_TIM3] = true, [PERIPH_TIM4] = true, [PERIPH_USART2] = true, [PERIPH_USART3] = true, [PERIPH_SPI1] = true, [PERIPH_SPI2] = true, [PERIPH_I2C1] = true, [PERIPH_I2C2] = true, //[PERIPH_USB] = true, // contrary to STM32, this is not present [PERIPH_CAN1] = true, }; static const struct mcu_model_t mcu_models[] = { { .name = "F100C4", .flash = 16, .sram = 4, .pins = 48, .peripherals = (bool*)peripheralas_f100x6, }, { .name = "F100C6", .flash = 32, .sram = 4, .pins = 48, .peripherals = (bool*)peripheralas_f100x6, }, { .name = "F100C8", .flash = 64, .sram = 8, .pins = 48, .peripherals = (bool*)peripheralas_f100x8, }, { .name = "F100CB", .flash = 128, .sram = 8, .pins = 48, .peripherals = (bool*)peripheralas_f100x8, }, { .name = "F100R4", .flash = 16, .sram = 4, .pins = 64, .peripherals = (bool*)peripheralas_f100x6, }, { .name = "F100R6", .flash = 32, .sram = 4, .pins = 64, .peripherals = (bool*)peripheralas_f100x6, }, { .name = "F100R8", .flash = 64, .sram = 8, .pins = 64, .peripherals = (bool*)peripheralas_f100x8, }, { .name = "F100RB", .flash = 128, .sram = 8, .pins = 64, .peripherals = (bool*)peripheralas_f100x8, }, // F103 { .name = "F103T8", .flash = 64, .sram = 20, .pins = 36, .peripherals = (bool*)peripheralas_f103t8, }, { .name = "F103TB", .flash = 128, .sram = 20, .pins = 36, .peripherals = (bool*)peripheralas_f103t8, }, { .name = "F103C8", .flash = 64, .sram = 20, .pins = 48, .peripherals = (bool*)peripheralas_f103c8, }, { .name = "F103CB", .flash = 128, .sram = 20, .pins = 48, .peripherals = (bool*)peripheralas_f103c8, }, { .name = "F103R8", .flash = 64, .sram = 20, .pins = 64, .peripherals = (bool*)peripheralas_f103c8, }, { .name = "F103RB", .flash = 128, .sram = 20, .pins = 64, .peripherals = (bool*)peripheralas_f103c8, }, { .name = "F103V8", .flash = 64, .sram = 20, .pins = 100, .peripherals = (bool*)peripheralas_f103c8, }, { .name = "F103VB", .flash = 128, .sram = 20, .pins = 100, .peripherals = (bool*)peripheralas_f103c8, }, // HK32F103 { .name = "F103C8", .flash = 64, .sram = 20, .pins = 48, .peripherals = (bool*)peripheralas_hk32f103c8, }, { .name = "F103CB", .flash = 128, .sram = 20, .pins = 48, .peripherals = (bool*)peripheralas_hk32f103c8, }, }; static void identify(void) { printf("identifying MCU (can take 2 minutes): "); if (0 == strcmp(manufacturer_str, "???")) { enable_output = false; manufacturer(); // sets manufacturer_str enable_output = true; } if (0 == strcmp(family_str, "???")) { enable_output = false; family(); // set family_str (cpu could also figure it out, but is less precise) enable_output = true; } if (0 == strcmp(family_str, "F10x") || 0 == strcmp(family_str, "F100")) { // further identify F1 MCU enable_output = false; peripherals(); // set mcu_peripherals enable_output = true; if (0 == sram_size) { enable_output = false; sizes(); // sets flash_size_variant and sram_size enable_output = true; } if ('?' == pin_count_variant || 0 == pin_count_nb) { enable_output = false; pin_count(); // sets pin_count_variant enable_output = true; } char* model = "???"; for (uint8_t i = 0; i < LENGTH(mcu_models); i++) { bool peripherals_match = true; for (uint8_t j = 0; j < LENGTH(mcu_peripherals) && peripherals_match; j++) { peripherals_match = peripherals_match && (mcu_models[i].peripherals[j] == mcu_peripherals[j]); } if (peripherals_match && mcu_models[i].flash == desig_get_flash_size() && mcu_models[i].sram == sram_size && mcu_models[i].pins == pin_count_nb) { model = mcu_models[i].name; break; } } if (0 != strcmp(model, "???")) { // found the model printf("%s32%s\r\n", manufacturer_str, model); } else { printf("%s32%s%c%c\r\n", manufacturer_str, family_str, pin_count_variant, flash_size_variant); } } else { // MCU not from the F1 family are not supported printf("%s32%s\r\n", manufacturer_str, family_str); } } static void version(void) { printf("identifier version: %u\r\n", 1); } int main(void) { // use default internal clock (use the least amount of resources to be compatible with most clones, and interfere the least with the measurements uart_setup(); // only use UART to interact with the user setvbuf(stdout, NULL, _IONBF, 0); // disable print buffer printf("\r\nCuVoodoo F103 MCU identifier\r\n"); while (true) { char c = usart_recv_blocking(USART1); printf("%c\r\n", c); // echo back switch (c) { case 'b': printf("starting embedded UART bootloader\r\n"); while(!usart_get_flag(USART1, USART_SR_TC)); // wait for transmission to be complete bootloader(); break; case 'a': version(); unique_id(); cpu(); jep(); manufacturer(); family(); sizes(); peripherals(); pin_count(); identify(); system_memory(); rom_table(); errata(); memory_map(); printf("all done\r\n"); break; case 'c': // show CPU information cpu(); break; case 'e': // test errata errata(); break; case 'f': // show family family(); break; case 'i': identify(); break; case 'j': // show JEP106 ID jep(); break; case 'h': // print help printf("available commands:\r\n"); printf("h show this help\r\n"); printf("b start embedded UART bootloader\r\n"); printf("r reset MCU (e.g. restart application)\r\n"); printf("v show identifier version\r\n"); printf("a show all information\r\n"); printf("m show manufacturer\r\n"); printf("f show MCU family\r\n"); printf("s show flash and SRAM size\r\n"); printf("c show CPU\r\n"); printf("u show MCU unique ID\r\n"); printf("j show JEP106 ID\r\n"); printf("t show ROM table\r\n"); printf("y show system memory\r\n"); printf("i identify MCU model\r\n"); printf("p test peripherals\r\n"); printf("n test pin count\r\n"); printf("l test accessibility of address space\r\n"); printf("e test errata\r\n"); printf("\r\nto submit issues or unknown device details, send all information (press a) to f103id@cuvoodoo.info\r\n"); break; case 'l': memory_map(); break; case 'm': // show manufacturer manufacturer(); break; case 'n': pin_count(); break; case 'p': peripherals(); break; case 'r': printf("rebooting\r\n"); while(!usart_get_flag(USART1, USART_SR_TC)); // wait for transmission to be complete reset(); break; case 's': sizes(); break; case 't': rom_table(); break; case 'u': unique_id(); break; case 'v': version(); break; case 'y': system_memory(); break; case '\r': // do nothing on new line case '\n': // do nothing on new line break; default: printf("unknown command. type h for help\r\n"); break; } } return 0; }