#include "general.h"
#include "target.h"
#include "target_internal.h"
#include "cortexm.h"
#include "stm32_common.h"
static bool stm32f4_cmd_option(target_s *t, int argc, const char **argv);
static bool stm32f4_cmd_psize(target_s *t, int argc, const char **argv);
const command_s stm32f4_cmd_list[] = {
{"option", stm32f4_cmd_option, "Manipulate option bytes"},
{"psize", stm32f4_cmd_psize, "Configure flash write parallelism: (x8|x16|x32(default)|x64)"},
{NULL, NULL, NULL},
};
static bool stm32f4_attach(target_s *t);
static void stm32f4_detach(target_s *t);
static bool stm32f4_flash_erase(target_flash_s *f, target_addr_t addr, size_t len);
static bool stm32f4_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len);
static bool stm32f4_mass_erase(target_s *t);
#define FPEC_BASE 0x40023c00U
#define FLASH_ACR (FPEC_BASE + 0x00U)
#define FLASH_KEYR (FPEC_BASE + 0x04U)
#define FLASH_OPTKEYR (FPEC_BASE + 0x08U)
#define FLASH_SR (FPEC_BASE + 0x0cU)
#define FLASH_CR (FPEC_BASE + 0x10U)
#define FLASH_OPTCR (FPEC_BASE + 0x14U)
#define FLASH_CR_PG (1U << 0U)
#define FLASH_CR_SER (1U << 1U)
#define FLASH_CR_MER (1U << 2U)
#define FLASH_CR_PSIZE8 (0U << 8U)
#define FLASH_CR_PSIZE16 (1U << 8U)
#define FLASH_CR_PSIZE32 (2U << 8U)
#define FLASH_CR_PSIZE64 (3U << 8U)
#define FLASH_CR_MER1 (1U << 15U)
#define FLASH_CR_STRT (1U << 16U)
#define FLASH_CR_EOPIE (1U << 24U)
#define FLASH_CR_ERRIE (1U << 25U)
#define FLASH_CR_STRT (1U << 16U)
#define FLASH_CR_LOCK (1U << 31U)
#define FLASH_SR_BSY (1U << 16U)
#define FLASH_OPTCR_OPTLOCK (1U << 0U)
#define FLASH_OPTCR_OPTSTRT (1U << 1U)
#define FLASH_OPTCR_WDG_SW (1U << 5U)
#define FLASH_OPTCR_nDBANK (1U << 29U)
#define FLASH_OPTCR_DB1M (1U << 30U)
#define FLASH_OPTCR_PROT_MASK 0xff00U
#define FLASH_OPTCR_PROT_L0 0xaa00U
#define FLASH_OPTCR_PROT_L1 0xbb00U
#define KEY1 0x45670123U
#define KEY2 0xcdef89abU
#define OPTKEY1 0x08192a3bU
#define OPTKEY2 0x4c5d6e7fU
#define SR_ERROR_MASK 0xf2U
#define SR_EOP 0x01U
#define F4_FLASHSIZE 0x1fff7a22U
#define F7_FLASHSIZE 0x1ff0f442U
#define F72X_FLASHSIZE 0x1ff07a22U
#define DBGMCU_IDCODE 0xe0042000U
#define DBGMCU_CR 0xe0042004U
#define DBG_SLEEP (1U << 0U)
#define AXIM_BASE 0x8000000U
#define ITCM_BASE 0x0200000U
#define DBGMCU_CR_DBG_SLEEP (0x1U << 0U)
#define DBGMCU_CR_DBG_STOP (0x1U << 1U)
#define DBGMCU_CR_DBG_STANDBY (0x1U << 2U)
typedef struct stm32f4_flash {
target_flash_s f;
align_e psize;
uint8_t base_sector;
uint8_t bank_split;
} stm32f4_flash_s;
typedef struct stm32f4_priv {
uint32_t dbgmcu_cr;
} stm32f4_priv_s;
#define ID_STM32F20X 0x411U
#define ID_STM32F40X 0x413U
#define ID_STM32F42X 0x419U
#define ID_STM32F446 0x421U
#define ID_STM32F401C 0x423U
#define ID_STM32F411 0x431U
#define ID_STM32F401E 0x433U
#define ID_STM32F46X 0x434U
#define ID_STM32F412 0x441U
#define ID_STM32F74X 0x449U
#define ID_STM32F76X 0x451U
#define ID_STM32F72X 0x452U
#define ID_STM32F410 0x458U
#define ID_STM32F413 0x463U
#define ID_GD32F450 0x2b3U
#define ID_GD32F470 0xa2eU
static void stm32f4_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)
{
if (length == 0)
return;
stm32f4_flash_s *sf = calloc(1, sizeof(*sf));
if (!sf) {
DEBUG_ERROR("calloc: failed in %s\n", __func__);
return;
}
target_flash_s *f = &sf->f;
f->start = addr;
f->length = length;
f->blocksize = blocksize;
f->erase = stm32f4_flash_erase;
f->write = stm32f4_flash_write;
f->writesize = 1024;
f->erased = 0xffU;
sf->base_sector = base_sector;
sf->bank_split = split;
sf->psize = ALIGN_32BIT;
target_add_flash(t, f);
}
static char *stm32f4_get_chip_name(const uint32_t device_id)
{
switch (device_id) {
case ID_STM32F40X:
return "STM32F40x";
case ID_STM32F42X:
return "STM32F42x";
case ID_STM32F46X:
return "STM32F47x";
case ID_STM32F20X:
return "STM32F2";
case ID_STM32F446:
return "STM32F446";
case ID_STM32F401C:
return "STM32F401C";
case ID_STM32F411:
return "STM32F411";
case ID_STM32F412:
return "STM32F412";
case ID_STM32F401E:
return "STM32F401E";
case ID_STM32F413:
return "STM32F413";
case ID_STM32F74X:
return "STM32F74x";
case ID_STM32F76X:
return "STM32F76x";
case ID_STM32F72X:
return "STM32F72x";
case ID_GD32F450:
return "GD32F450";
case ID_GD32F470:
return "GD32F470";
default:
return NULL;
}
}
static uint16_t stm32f4_read_idcode(target_s *const t)
{
const uint16_t idcode = target_mem_read32(t, DBGMCU_IDCODE) & 0xfffU;
if (idcode == ID_STM32F20X && (t->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M4)
return ID_STM32F40X;
return idcode;
}
bool stm32f4_probe(target_s *t)
{
const uint16_t device_id = stm32f4_read_idcode(t);
switch (device_id) {
case ID_STM32F74X:
case ID_STM32F76X:
case ID_STM32F72X:
case ID_STM32F42X:
case ID_STM32F46X:
case ID_STM32F20X:
case ID_STM32F40X:
case ID_STM32F446:
case ID_STM32F401C:
case ID_STM32F411:
case ID_STM32F412:
case ID_STM32F401E:
case ID_STM32F413:
t->attach = stm32f4_attach;
t->detach = stm32f4_detach;
t->mass_erase = stm32f4_mass_erase;
t->driver = stm32f4_get_chip_name(device_id);
t->part_id = device_id;
target_add_commands(t, stm32f4_cmd_list, t->driver);
return true;
}
return false;
}
bool gd32f4_probe(target_s *t)
{
if (t->part_id != ID_GD32F450 && t->part_id != ID_GD32F470)
return false;
t->attach = cortexm_attach;
t->detach = cortexm_detach;
t->mass_erase = stm32f4_mass_erase;
t->driver = stm32f4_get_chip_name(t->part_id);
target_add_commands(t, stm32f4_cmd_list, t->driver);
target_mem_map_free(t);
target_add_ram(t, 0x10000000, 0x10000);
target_add_ram(t, 0x20000000, 0x50000);
const uint8_t split = 12;
stm32f4_add_flash(t, 0x8000000, 0x10000, 0x4000, 0, split);
stm32f4_add_flash(t, 0x8010000, 0x10000, 0x10000, 4, split);
stm32f4_add_flash(t, 0x8020000, 0xe0000, 0x20000, 5, split);
stm32f4_add_flash(t, 0x8100000, 0x10000, 0x4000, 16, split);
stm32f4_add_flash(t, 0x8110000, 0x10000, 0x10000, 20, split);
stm32f4_add_flash(t, 0x8120000, 0xe0000, 0x20000, 21, split);
stm32f4_add_flash(t, 0x8200000, 0x100000, 0x40000, 12, split);
return true;
}
static inline bool stm32f4_device_is_f7(const uint16_t part_id)
{
return part_id == ID_STM32F74X || part_id == ID_STM32F76X || part_id == ID_STM32F72X;
}
static inline bool stm32f4_device_is_dual_bank(const uint16_t part_id)
{
return part_id == ID_STM32F42X || part_id == ID_STM32F46X || part_id == ID_STM32F76X;
}
static inline bool stm32f4_device_has_ccm_ram(const uint16_t part_id)
{
return part_id == ID_STM32F40X || part_id == ID_STM32F42X || part_id == ID_STM32F46X;
}
static inline bool stm32f4_device_has_large_sectors(const uint16_t part_id)
{
return part_id == ID_STM32F74X || part_id == ID_STM32F76X;
}
static uint32_t stm32f4_remaining_bank_length(const uint32_t bank_length, const uint32_t small_sector_bytes)
{
if (bank_length > small_sector_bytes)
return bank_length - small_sector_bytes;
return 0;
}
static bool stm32f4_attach(target_s *t)
{
uint16_t max_flashsize = 0;
switch (t->part_id) {
case ID_STM32F401E:
case ID_STM32F411:
case ID_STM32F446:
case ID_STM32F46X:
case ID_STM32F72X:
max_flashsize = 512;
break;
case ID_STM32F401C:
max_flashsize = 256;
break;
case ID_STM32F40X:
case ID_STM32F20X:
case ID_STM32F412:
case ID_STM32F74X:
max_flashsize = 1024;
break;
case ID_STM32F413:
max_flashsize = 1536;
break;
case ID_STM32F42X:
case ID_STM32F76X:
max_flashsize = 2048;
break;
default:
DEBUG_WARN("Unsupported part id: %u\n", t->part_id);
return false;
}
if (!cortexm_attach(t))
return false;
const bool dual_bank = stm32f4_device_is_dual_bank(t->part_id);
const bool has_ccm_ram = stm32f4_device_has_ccm_ram(t->part_id);
const bool is_f7 = stm32f4_device_is_f7(t->part_id);
const bool large_sectors = stm32f4_device_has_large_sectors(t->part_id);
stm32f4_priv_s *priv_storage = calloc(1, sizeof(*priv_storage));
if (!priv_storage) {
DEBUG_ERROR("calloc: failed in %s\n", __func__);
return false;
}
t->target_storage = priv_storage;
priv_storage->dbgmcu_cr = target_mem_read32(t, DBGMCU_CR);
target_mem_write32(
t, DBGMCU_CR, priv_storage->dbgmcu_cr | DBGMCU_CR_DBG_SLEEP | DBGMCU_CR_DBG_STANDBY | DBGMCU_CR_DBG_STOP);
target_mem_map_free(t);
bool use_dual_bank = !is_f7 && dual_bank;
if (is_f7) {
target_add_ram(t, 0x00000000, 0x4000);
target_add_ram(t, 0x20000000, 0x20000);
target_add_ram(t, 0x20020000, 0x60000);
if (dual_bank) {
const uint32_t option_ctrl = target_mem_read32(t, FLASH_OPTCR);
use_dual_bank = !(option_ctrl & FLASH_OPTCR_nDBANK);
}
} else {
if (has_ccm_ram)
target_add_ram(t, 0x10000000, 0x10000);
target_add_ram(t, 0x20000000, 0x50000);
if (dual_bank && max_flashsize < 2048U) {
const uint32_t option_ctrl = target_mem_read32(t, FLASH_OPTCR);
use_dual_bank = !(option_ctrl & FLASH_OPTCR_DB1M);
}
}
uint8_t split = 0;
uint32_t bank_length;
if (use_dual_bank) {
bank_length = max_flashsize << 9U;
split = max_flashsize == 1024U ? 8U : 12U;
} else
bank_length = max_flashsize << 10U;
if (large_sectors) {
const uint32_t remaining_bank_length = stm32f4_remaining_bank_length(bank_length, 0x40000);
stm32f4_add_flash(t, ITCM_BASE, 0x20000, 0x8000, 0, split);
stm32f4_add_flash(t, 0x0220000, 0x20000, 0x20000, 4, split);
stm32f4_add_flash(t, 0x0240000, remaining_bank_length, 0x40000, 5, split);
stm32f4_add_flash(t, AXIM_BASE, 0x20000, 0x8000, 0, split);
stm32f4_add_flash(t, 0x8020000, 0x20000, 0x20000, 4, split);
stm32f4_add_flash(t, 0x8040000, remaining_bank_length, 0x40000, 5, split);
} else {
const uint32_t remaining_bank_length = stm32f4_remaining_bank_length(bank_length, 0x20000);
if (is_f7)
stm32f4_add_flash(t, ITCM_BASE, 0x10000, 0x4000, 0, split);
stm32f4_add_flash(t, AXIM_BASE, 0x10000, 0x4000, 0, split);
if (bank_length > 0x10000U) {
stm32f4_add_flash(t, 0x8010000, 0x10000, 0x10000, 4, split);
if (remaining_bank_length)
stm32f4_add_flash(t, 0x8020000, remaining_bank_length, 0x20000, 5, split);
}
if (use_dual_bank) {
if (is_f7) {
const uint32_t bank1_base = ITCM_BASE + bank_length;
stm32f4_add_flash(t, bank1_base, 0x10000, 0x4000, 0, split);
stm32f4_add_flash(t, bank1_base + 0x10000U, 0x10000, 0x10000, 4, split);
stm32f4_add_flash(t, bank1_base + 0x20000U, remaining_bank_length, 0x20000, 5, split);
}
const uint32_t bank2_base = AXIM_BASE + bank_length;
stm32f4_add_flash(t, bank2_base, 0x10000, 0x4000, 16, split);
stm32f4_add_flash(t, bank2_base + 0x10000U, 0x10000, 0x10000, 20, split);
stm32f4_add_flash(t, bank2_base + 0x20000U, remaining_bank_length, 0x20000, 21, split);
}
}
return true;
}
static void stm32f4_detach(target_s *t)
{
stm32f4_priv_s *ps = t->target_storage;
target_mem_write32(t, DBGMCU_CR, ps->dbgmcu_cr);
cortexm_detach(t);
}
static void stm32f4_flash_unlock(target_s *t)
{
if (target_mem_read32(t, FLASH_CR) & FLASH_CR_LOCK) {
target_mem_write32(t, FLASH_KEYR, KEY1);
target_mem_write32(t, FLASH_KEYR, KEY2);
}
}
static bool stm32f4_flash_busy_wait(target_s *const t, platform_timeout_s *const timeout)
{
uint32_t status = FLASH_SR_BSY;
while (status & FLASH_SR_BSY) {
status = target_mem_read32(t, FLASH_SR);
if ((status & SR_ERROR_MASK) || target_check_error(t)) {
DEBUG_ERROR("stm32f4 flash error 0x%" PRIx32 "\n", status);
return false;
}
if (timeout)
target_print_progress(timeout);
}
return true;
}
static bool stm32f4_flash_erase(target_flash_s *f, target_addr_t addr, size_t len)
{
target_s *t = f->t;
stm32f4_flash_s *sf = (stm32f4_flash_s *)f;
stm32f4_flash_unlock(t);
align_e psize = ALIGN_32BIT;
for (target_flash_s *currf = t->flash; currf; currf = currf->next) {
if (currf->write == stm32f4_flash_write)
psize = ((stm32f4_flash_s *)currf)->psize;
}
uint8_t sector = sf->base_sector + ((addr - f->start) / f->blocksize);
for (size_t offset = 0; offset < len; offset += f->blocksize) {
uint32_t cr = FLASH_CR_EOPIE | FLASH_CR_ERRIE | FLASH_CR_SER | (psize * FLASH_CR_PSIZE16) | (sector << 3U);
target_mem_write32(t, FLASH_CR, cr);
target_mem_write32(t, FLASH_CR, cr | FLASH_CR_STRT);
if (!stm32f4_flash_busy_wait(t, NULL))
return false;
++sector;
if (sf->bank_split && sector == sf->bank_split)
sector = 16;
}
return true;
}
static bool stm32f4_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len)
{
if (dest >= ITCM_BASE && dest < AXIM_BASE)
dest += AXIM_BASE - ITCM_BASE;
target_s *t = f->t;
align_e psize = ((stm32f4_flash_s *)f)->psize;
target_mem_write32(t, FLASH_CR, (psize * FLASH_CR_PSIZE16) | FLASH_CR_PG);
cortexm_mem_write_sized(t, dest, src, len, psize);
return stm32f4_flash_busy_wait(t, NULL);
}
static bool stm32f4_mass_erase(target_s *t)
{
stm32f4_flash_s *sf = (stm32f4_flash_s *)t->flash;
stm32f4_flash_unlock(t);
const uint32_t ctrl = FLASH_CR_MER | (sf->bank_split ? FLASH_CR_MER1 : 0);
target_mem_write32(t, FLASH_CR, ctrl);
target_mem_write32(t, FLASH_CR, ctrl | FLASH_CR_STRT);
platform_timeout_s timeout;
platform_timeout_set(&timeout, 500);
return stm32f4_flash_busy_wait(t, &timeout);
}
static bool optcr_mask(target_s *const t, uint32_t *const val)
{
switch (t->part_id) {
case ID_STM32F20X:
case ID_STM32F40X:
val[0] &= ~0xf0000010U;
break;
case ID_STM32F46X:
case ID_STM32F42X:
case ID_GD32F450:
case ID_GD32F470:
val[0] &= ~0x30000000U;
val[1] &= 0x0fff0000U;
break;
case ID_STM32F401C:
val[0] &= ~0x7fc00010U;
break;
case ID_STM32F446:
case ID_STM32F411:
case ID_STM32F401E:
val[0] &= ~0x7f000010U;
break;
case ID_STM32F410:
val[0] &= ~0x7fe00010U;
break;
case ID_STM32F412:
val[0] &= ~0x70000010U;
break;
case ID_STM32F413:
val[0] &= ~0x00000010U;
break;
case ID_STM32F72X:
val[2] &= ~0x800000ffU;
case ID_STM32F74X:
val[0] &= ~0x3f000000U;
break;
case ID_STM32F76X:
break;
default:
return false;
}
return true;
}
static size_t stm32f4_opt_bytes_for(const uint16_t part_id)
{
if (part_id == ID_STM32F72X)
return 3;
if (part_id == ID_STM32F42X || part_id == ID_STM32F46X || part_id == ID_STM32F74X || part_id == ID_STM32F76X)
return 2;
if (part_id == ID_GD32F450 || part_id == ID_GD32F470)
return 2;
return 1;
}
static bool stm32f4_option_write(target_s *t, uint32_t *const val, size_t count)
{
val[0] &= ~(FLASH_OPTCR_OPTSTRT | FLASH_OPTCR_OPTLOCK);
uint32_t optcr = target_mem_read32(t, FLASH_OPTCR);
if (!(optcr & FLASH_OPTCR_WDG_SW) && (optcr & FLASH_OPTCR_PROT_MASK) != FLASH_OPTCR_PROT_L0 &&
(val[0] & FLASH_OPTCR_PROT_MASK) != FLASH_OPTCR_PROT_L1) {
val[0] &= ~FLASH_OPTCR_PROT_MASK;
val[0] |= FLASH_OPTCR_PROT_L1;
tc_printf(t, "Keeping L1 protection while HW Watchdog fuse is set!\n");
}
target_mem_write32(t, FLASH_OPTKEYR, OPTKEY1);
target_mem_write32(t, FLASH_OPTKEYR, OPTKEY2);
if (!stm32f4_flash_busy_wait(t, NULL))
return false;
const uint16_t part_id = t->part_id;
if (stm32f4_opt_bytes_for(part_id) > 1U && count > 1U) {
target_mem_write32(t, FLASH_OPTCR + 4U, val[1]);
if (part_id == ID_STM32F72X && count > 2U)
target_mem_write32(t, FLASH_OPTCR + 8U, val[2]);
}
target_mem_write32(t, FLASH_OPTCR, val[0]);
target_mem_write32(t, FLASH_OPTCR, val[0] | FLASH_OPTCR_OPTSTRT);
tc_printf(t, "Erasing flash\nThis may take a few seconds...\n");
platform_timeout_s timeout;
platform_timeout_set(&timeout, 100);
if (!stm32f4_flash_busy_wait(t, &timeout))
return false;
tc_printf(t, "\n");
target_mem_write32(t, FLASH_OPTCR, FLASH_OPTCR_OPTLOCK);
target_reset(t);
return true;
}
static bool stm32f4_option_write_default(target_s *t)
{
uint32_t val[3] = {0};
switch (t->part_id) {
case ID_STM32F42X:
case ID_STM32F46X:
case ID_GD32F450:
case ID_GD32F470:
val[0] = 0x0fffaaedU;
val[1] = 0x0fff0000U;
return stm32f4_option_write(t, val, 2);
case ID_STM32F72X:
val[0] = 0xc0ffaafdU;
val[1] = 0x00400080U;
return stm32f4_option_write(t, val, 3);
case ID_STM32F74X:
val[0] = 0xc0ffaafdU;
val[1] = 0x00400080U;
return stm32f4_option_write(t, val, 2);
case ID_STM32F76X:
val[0] = 0xffffaafdU;
val[1] = 0x00400080U;
return stm32f4_option_write(t, val, 2);
case ID_STM32F413:
val[0] = 0x7fffaafdU;
return stm32f4_option_write(t, val, 1);
default:
val[0] = 0x0fffaaedU;
return stm32f4_option_write(t, val, 1);
}
}
static const char option_cmd_erase[] = "erase";
static const char option_cmd_write[] = "write";
#define OPTION_CMD_LEN(cmd) ARRAY_LENGTH(cmd) - 1U
static bool partial_match(const char *const str, const char *const what, const size_t what_len)
{
const size_t str_len = strlen(str);
if (str_len > what_len)
return false;
return strncasecmp(str, what, str_len) == 0;
}
static bool stm32f4_cmd_option(target_s *t, int argc, const char **argv)
{
const size_t opt_bytes = stm32f4_opt_bytes_for(t->part_id);
if (argc == 2 && partial_match(argv[1], option_cmd_erase, OPTION_CMD_LEN(option_cmd_erase)))
stm32f4_option_write_default(t);
else if (argc > 2 && partial_match(argv[1], option_cmd_write, OPTION_CMD_LEN(option_cmd_write))) {
uint32_t val[3] = {0};
size_t count = argc > 4 ? 3 : argc - 1;
val[0] = strtoul(argv[2], NULL, 0);
if (argc > 3) {
val[1] = strtoul(argv[3], NULL, 0);
if (argc > 4)
val[2] = strtoul(argv[4], NULL, 0);
}
if (optcr_mask(t, val))
stm32f4_option_write(t, val, count);
else
tc_printf(t, "error\n");
} else
tc_printf(t, "usage: monitor option erase\nusage: monitor option write <OPTCR>%s%s\n",
opt_bytes > 1U ? " <OPTCR1>" : "", opt_bytes == 3U ? " <OPTCR2>" : "");
uint32_t val[3] = {0};
val[0] = target_mem_read32(t, FLASH_OPTCR);
if (opt_bytes > 1U) {
val[1] = target_mem_read32(t, FLASH_OPTCR + 4U);
if (opt_bytes == 3U)
val[2] = target_mem_read32(t, FLASH_OPTCR + 8U);
}
optcr_mask(t, val);
tc_printf(t, "OPTCR: 0x%08" PRIx32, val[0]);
if (opt_bytes > 1U) {
tc_printf(t, " OPTCR1: 0x%08" PRIx32, val[1]);
if (opt_bytes > 2U)
tc_printf(t, " OPTCR2: 0x%08" PRIx32, val[2]);
}
tc_printf(t, "\n");
return true;
}
static bool stm32f4_cmd_psize(target_s *t, int argc, const char **argv)
{
if (argc == 1) {
align_e psize = ALIGN_32BIT;
for (target_flash_s *f = t->flash; f; f = f->next) {
if (f->write == stm32f4_flash_write)
psize = ((stm32f4_flash_s *)f)->psize;
}
tc_printf(t, "Flash write parallelism: %s\n", stm32_psize_to_string(psize));
} else {
align_e psize;
if (!stm32_psize_from_string(t, argv[1], &psize))
return false;
for (target_flash_s *f = t->flash; f; f = f->next) {
if (f->write == stm32f4_flash_write)
((stm32f4_flash_s *)f)->psize = psize;
}
}
return true;
}