2638 lines
64 KiB
C
2638 lines
64 KiB
C
/* F103 MCU identifier
|
|
* used to fingerprint and identify STM32F103 or variants, and their clones
|
|
* Copyright: 2020 King Kévin <kingkevin@cuvoodoo.info>
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
|
|
#include <stdio.h> // print utilities
|
|
#include <errno.h> // error definitions (used for _write)
|
|
#include <string.h> // string comparison utilities
|
|
|
|
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
|
#include <libopencm3/cm3/scb.h> // vector table definition
|
|
#include <libopencm3/cm3/scs.h> // System Control Space definitions
|
|
#include <libopencm3/stm32/rcc.h> // domain clock utilities
|
|
#include <libopencm3/stm32/gpio.h> // GPIO utilities to configure UART
|
|
#include <libopencm3/stm32/usart.h> // UART utilities to communicate with user
|
|
#include <libopencm3/stm32/desig.h> // design utilities
|
|
#include <libopencm3/stm32/flash.h> // flash utilities
|
|
#include <libopencm3/stm32/dbgmcu.h> // debug utilities
|
|
#include <libopencm3/stm32/crc.h> // CRC utilities
|
|
|
|
// peripherals to test
|
|
#include <libopencm3/stm32/timer.h>
|
|
#include <libopencm3/stm32/spi.h>
|
|
#include <libopencm3/stm32/i2c.h>
|
|
#include <libopencm3/stm32/can.h>
|
|
#include <libopencm3/stm32/st_usbfs.h>
|
|
#include <libopencm3/stm32/f0/cec.h>
|
|
|
|
// 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;
|
|
}
|