Skip to content

Commit

Permalink
at32f43x: Implement flash programming
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ALTracer committed Oct 16, 2023
1 parent 8131bf8 commit ef80ff6
Showing 1 changed file with 213 additions and 23 deletions.
236 changes: 213 additions & 23 deletions src/target/at32f43x.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,40 +32,82 @@
#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
#define AT32F4x_IDCODE_PART_MASK 0x00000fffU
#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)
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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;
}

Expand All @@ -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;
}

0 comments on commit ef80ff6

Please sign in to comment.