From 8715212411e4bce0d085b9f8b957fbfacbbc05f9 Mon Sep 17 00:00:00 2001 From: ALTracer Date: Wed, 19 Jul 2023 12:15:58 +0300 Subject: [PATCH] at32f43x: Implement flash programming * Code inspired by stm32f1 and partially stm32f4 target code * Code heavily refactored to conform to modern target flash API (_prepare/_done, less looping and simpler _erase/_write) * Most ops operate on their respective bank only, referencing the controller register offset from flash_priv * _mass_erase still touches both banks, if present * Rename old short arguments and variables on cloning, rename macros --- src/target/at32f43x.c | 236 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 213 insertions(+), 23 deletions(-) diff --git a/src/target/at32f43x.c b/src/target/at32f43x.c index 48cbcc0e225..34375ac9891 100644 --- a/src/target/at32f43x.c +++ b/src/target/at32f43x.c @@ -32,6 +32,39 @@ #include "target_internal.h" #include "cortexm.h" +static bool at32f43_flash_prepare(target_flash_s *flash); +static bool at32f43_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len); +static bool at32f43_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len); +static bool at32f43_flash_done(target_flash_s *flash); +static bool at32f43_mass_erase(target_s *target); + +/* Flash memory controller register map */ +#define AT32F43x_FLASH_REG_BASE 0x40023c00U +#define AT32F43x_FLASH_UNLOCK (AT32F43x_FLASH_REG_BASE + 0x04U) +#define AT32F43x_FLASH_STS (AT32F43x_FLASH_REG_BASE + 0x0cU) +#define AT32F43x_FLASH_CTRL (AT32F43x_FLASH_REG_BASE + 0x10U) +#define AT32F43x_FLASH_ADDR (AT32F43x_FLASH_REG_BASE + 0x14U) +/* There is a second set of identical registers at +0x40 offset for Bank 2 */ + +#define AT32F43x_FLASH_BANK1_REG_OFFSET 0x00U +#define AT32F43x_FLASH_BANK2_REG_OFFSET 0x40U + +/* Flash registers bit fields */ +#define AT32F43x_FLASH_CTRL_FPRGM (1U << 0U) +#define AT32F43x_FLASH_CTRL_SECERS (1U << 1U) +#define AT32F43x_FLASH_CTRL_BANKERS (1U << 2U) +#define AT32F43x_FLASH_CTRL_ERSTR (1U << 6U) +#define AT32F43x_FLASH_CTRL_OPLK (1U << 7U) +/* CTRL bits [8:11] are reserved, parallellism x8/x16/x32 (don't care) */ + +/* OBF is BSY, ODF is EOP */ +#define AT32F43x_FLASH_STS_OBF (1U << 0U) +#define AT32F43x_FLASH_STS_PRGMERR (1U << 2U) +#define AT32F43x_FLASH_STS_ODF (1U << 5U) + +#define AT32F43x_FLASH_KEY1 0x45670123U +#define AT32F43x_FLASH_KEY2 0xcdef89abU + #define DBGMCU_IDCODE 0xe0042000U #define AT32F4x_IDCODE_SERIES_MASK 0xfffff000U @@ -39,33 +72,42 @@ #define AT32F43_SERIES_4K 0x70084000U #define AT32F43_SERIES_2K 0x70083000U -static void at32f43_add_flash(target_s *const t, const uint32_t addr, const size_t length, const size_t blocksize, - const uint8_t base_sector, const uint8_t split) +typedef struct at32f43_flash { + target_flash_s target_flash; + target_addr_t bank_split; /* Address of first page of bank 2 */ + uint32_t bank_reg_offset; /* Flash register offset for this bank */ +} at32f43_flash_s; + +static void at32f43_add_flash(target_s *const target, const target_addr_t addr, const size_t length, + const size_t pagesize, const target_addr_t bank_split, const uint32_t bank_reg_offset) { if (length == 0) return; - target_flash_s *f = calloc(1, sizeof(*f)); - if (!f) { /* calloc failed: heap exhaustion */ + at32f43_flash_s *flash = calloc(1, sizeof(*flash)); + if (!flash) { /* calloc failed: heap exhaustion */ DEBUG_ERROR("calloc: failed in %s\n", __func__); return; } - f->start = addr; - f->length = length; - f->blocksize = blocksize; - f->erase = NULL; - f->write = NULL; - (void)base_sector; - (void)split; - f->writesize = 1024; - f->erased = 0xffU; - target_add_flash(t, f); + target_flash_s *target_flash = &flash->target_flash; + target_flash->start = addr; + target_flash->length = length; + target_flash->blocksize = pagesize; + target_flash->prepare = at32f43_flash_prepare; + target_flash->erase = at32f43_flash_erase; + target_flash->write = at32f43_flash_write; + target_flash->done = at32f43_flash_done; + target_flash->writesize = 1024U; + target_flash->erased = 0xffU; + flash->bank_split = bank_split; + flash->bank_reg_offset = bank_reg_offset; + target_add_flash(target, target_flash); } static bool at32f43_detect(target_s *target, const uint16_t part_id) { - /* + /* * AT32F435 EOPB0 ZW/NZW split reconfiguration unsupported, * assuming default split ZW=256 SRAM=384. * AT32F437 also have a working "EMAC" (Ethernet MAC) @@ -83,7 +125,7 @@ static bool at32f43_detect(target_s *target, const uint16_t part_id) case 0x054fU: // LQFP144 w/Eth case 0x0552U: // LQFP100 w/Eth case 0x0555U: // LQFP64 w/Eth - // Flash (G): 4032 KB in 2 banks (2048+1984), 4KB per sector. + // Flash (M): 4032 KB in 2 banks (2048+1984), 4KB per sector. flash_size_bank1 = 2048U * 1024U; flash_size_bank2 = 1984U * 1024U; sector_size = 4096; @@ -109,7 +151,7 @@ static bool at32f43_detect(target_s *target, const uint16_t part_id) case 0x0350U: // LQFP144 w/Eth case 0x0353U: // LQFP100 w/Eth case 0x0356U: // LQFP64 w/Eth - // Flash (M): 1024 KB in 2 banks (equal), 2KB per sector. + // Flash (G): 1024 KB in 2 banks (equal), 2KB per sector. flash_size_bank1 = 512U * 1024U; flash_size_bank2 = 512U * 1024U; sector_size = 2048; @@ -134,11 +176,14 @@ static bool at32f43_detect(target_s *target, const uint16_t part_id) * Block erase operates on 64 KB at once for all parts. * Using here only sector erase (page erase) for compatibility. */ - at32f43_add_flash(target, 0x08000000, flash_size_bank1, sector_size, 0, 0); if (flash_size_bank2 > 0) { - const uint16_t base_sector = flash_size_bank1 / sector_size; - at32f43_add_flash(target, 0x08000000 + flash_size_bank1, flash_size_bank2, sector_size, base_sector, 0); - } + const uint32_t bank_split = 0x08000000 + flash_size_bank1; + at32f43_add_flash( + target, 0x08000000, flash_size_bank1, sector_size, bank_split, AT32F43x_FLASH_BANK1_REG_OFFSET); + at32f43_add_flash( + target, bank_split, flash_size_bank2, sector_size, bank_split, AT32F43x_FLASH_BANK2_REG_OFFSET); + } else + at32f43_add_flash(target, 0x08000000, flash_size_bank1, sector_size, 0, AT32F43x_FLASH_BANK1_REG_OFFSET); // SRAM1 (64KB) can be remapped to 0x10000000. target_add_ram(target, 0x20000000, 64U * 1024U); @@ -149,11 +194,11 @@ static bool at32f43_detect(target_s *target, const uint16_t part_id) * Out of 640 KB SRAM present on silicon, at least 128 KB are always * dedicated to "zero-wait-state Flash". ZW region is limited by * specific part flash capacity (for 256, 448 KB) or at 512 KB. - * AT32F435ZMT default EOPB0=0xffff05fa, + * AT32F435ZMT default EOPB0=0xffff05fa, * EOPB[0:2]=0b010 for 384 KB SRAM + 256 KB zero-wait-state flash. */ target->driver = "AT32F435"; - target->mass_erase = NULL; + target->mass_erase = at32f43_mass_erase; return true; } @@ -173,3 +218,148 @@ bool at32f43x_probe(target_s *target) return at32f43_detect(target, part_id); return false; } + +static bool at32f43_flash_unlock(target_s *const target, const uint32_t bank_reg_offset) +{ + if (target_mem_read32(target, AT32F43x_FLASH_CTRL + bank_reg_offset) & AT32F43x_FLASH_CTRL_OPLK) { + /* Enable FLASH operations in requested bank */ + target_mem_write32(target, AT32F43x_FLASH_UNLOCK + bank_reg_offset, AT32F43x_FLASH_KEY1); + target_mem_write32(target, AT32F43x_FLASH_UNLOCK + bank_reg_offset, AT32F43x_FLASH_KEY2); + } + const uint32_t ctrlx = target_mem_read32(target, AT32F43x_FLASH_CTRL + bank_reg_offset); + if (ctrlx & AT32F43x_FLASH_CTRL_OPLK) + DEBUG_ERROR("%s failed, CTRLx: 0x%08" PRIx32 "\n", __func__, ctrlx); + return !(ctrlx & AT32F43x_FLASH_CTRL_OPLK); +} + +static bool at32f43_flash_lock(target_s *const target, const uint32_t bank_reg_offset) +{ + uint32_t ctrlx_temp = target_mem_read32(target, AT32F43x_FLASH_CTRL + bank_reg_offset); + if ((ctrlx_temp & AT32F43x_FLASH_CTRL_OPLK) == 0U) { + /* Disable FLASH operations in requested bank */ + ctrlx_temp |= AT32F43x_FLASH_CTRL_OPLK; + target_mem_write32(target, AT32F43x_FLASH_CTRL + bank_reg_offset, ctrlx_temp); + } + const uint32_t ctrlx = target_mem_read32(target, AT32F43x_FLASH_CTRL + bank_reg_offset); + if ((ctrlx & AT32F43x_FLASH_CTRL_OPLK) == 0U) + DEBUG_ERROR("%s failed, CTRLx: 0x%08" PRIx32 "\n", __func__, ctrlx); + return (ctrlx & AT32F43x_FLASH_CTRL_OPLK); +} + +static inline void at32f43_flash_clear_eop(target_s *const target, const uint32_t bank_reg_offset) +{ + const uint32_t status = target_mem_read32(target, AT32F43x_FLASH_STS + bank_reg_offset); + target_mem_write32(target, AT32F43x_FLASH_STS + bank_reg_offset, status | AT32F43x_FLASH_STS_ODF); /* ODF is W1C */ +} + +static bool at32f43_flash_busy_wait( + target_s *const target, const uint32_t bank_reg_offset, platform_timeout_s *const timeout) +{ + /* Read FLASH_STS to poll for Operation Busy Flag */ + uint32_t status = AT32F43x_FLASH_STS_OBF; + /* Checking for ODF/EOP requires methodically clearing the ODF */ + while (!(status & AT32F43x_FLASH_STS_ODF) && (status & AT32F43x_FLASH_STS_OBF)) { + status = target_mem_read32(target, AT32F43x_FLASH_STS + bank_reg_offset); + if (target_check_error(target)) { + DEBUG_ERROR("Lost communications with target\n"); + return false; + } + if (timeout) + target_print_progress(timeout); + } + if (status & AT32F43x_FLASH_STS_PRGMERR) { + DEBUG_ERROR("at32f43 flash error, STS: 0x%" PRIx32 "\n", status); + return false; + } + return !(status & AT32F43x_FLASH_STS_PRGMERR); +} + +static bool at32f43_flash_prepare(target_flash_s *target_flash) +{ + target_s *target = target_flash->t; + const at32f43_flash_s *const flash = (at32f43_flash_s *)target_flash; + const uint32_t bank_reg_offset = flash->bank_reg_offset; + return at32f43_flash_unlock(target, bank_reg_offset); +} + +static bool at32f43_flash_done(target_flash_s *target_flash) +{ + target_s *target = target_flash->t; + const at32f43_flash_s *const flash = (at32f43_flash_s *)target_flash; + const uint32_t bank_reg_offset = flash->bank_reg_offset; + return at32f43_flash_lock(target, bank_reg_offset); +} + +static bool at32f43_flash_erase(target_flash_s *target_flash, target_addr_t addr, size_t len) +{ + target_s *target = target_flash->t; + const at32f43_flash_s *const flash = (at32f43_flash_s *)target_flash; + const uint32_t bank_reg_offset = flash->bank_reg_offset; + + if (len != target_flash->blocksize) { + DEBUG_ERROR( + "%s: Requested erase length %zu does not match blocksize %zu!\n", __func__, len, target_flash->blocksize); + return false; + } + + at32f43_flash_clear_eop(target, bank_reg_offset); + + /* Prepare for page/sector erase */ + target_mem_write32(target, AT32F43x_FLASH_CTRL + bank_reg_offset, AT32F43x_FLASH_CTRL_SECERS); + /* Select erased sector by its address */ + target_mem_write32(target, AT32F43x_FLASH_ADDR + bank_reg_offset, addr); + /* Start sector erase operation */ + target_mem_write32( + target, AT32F43x_FLASH_CTRL + bank_reg_offset, AT32F43x_FLASH_CTRL_SECERS | AT32F43x_FLASH_CTRL_ERSTR); + + /* Datasheet: page erase takes 50ms (typ), 500ms (max) */ + return at32f43_flash_busy_wait(target, bank_reg_offset, NULL); +} + +static bool at32f43_flash_write(target_flash_s *target_flash, target_addr_t dest, const void *src, size_t len) +{ + target_s *target = target_flash->t; + const at32f43_flash_s *const flash = (at32f43_flash_s *)target_flash; + const uint32_t bank_reg_offset = flash->bank_reg_offset; + const align_e psize = ALIGN_32BIT; + + at32f43_flash_clear_eop(target, bank_reg_offset); + + /* Write to bank corresponding to flash region */ + target_mem_write32(target, AT32F43x_FLASH_CTRL + bank_reg_offset, AT32F43x_FLASH_CTRL_FPRGM); + cortexm_mem_write_sized(target, dest, src, len, psize); + + /* Datasheet: flash programming takes 50us (typ), 200us (max) */ + return at32f43_flash_busy_wait(target, bank_reg_offset, NULL); +} + +static bool at32f43_mass_erase_bank( + target_s *const target, const uint32_t bank_reg_offset, platform_timeout_s *const timeout) +{ + /* Unlock this bank */ + if (!at32f43_flash_unlock(target, bank_reg_offset)) + return false; + at32f43_flash_clear_eop(target, bank_reg_offset); + + /* Flash mass erase start instruction */ + target_mem_write32(target, AT32F43x_FLASH_CTRL + bank_reg_offset, AT32F43x_FLASH_CTRL_BANKERS); + target_mem_write32( + target, AT32F43x_FLASH_CTRL + bank_reg_offset, AT32F43x_FLASH_CTRL_BANKERS | AT32F43x_FLASH_CTRL_ERSTR); + + return at32f43_flash_busy_wait(target, bank_reg_offset, timeout); +} + +static bool at32f43_mass_erase(target_s *target) +{ + /* Datasheet: bank erase takes seconds to complete */ + platform_timeout_s timeout; + platform_timeout_set(&timeout, 500); + if (!at32f43_mass_erase_bank(target, AT32F43x_FLASH_BANK1_REG_OFFSET, &timeout)) + return false; + + /* For dual-bank targets, mass erase bank 2 as well */ + const at32f43_flash_s *const flash = (at32f43_flash_s *)target->flash; + if (flash->bank_split) + return at32f43_mass_erase_bank(target, AT32F43x_FLASH_BANK2_REG_OFFSET, &timeout); + return true; +}