NVMC & UICR: Readied for nrf53

* Added multiple instance support
* Added nrf53 glue and config

Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
This commit is contained in:
Alberto Escolar Piedras 2023-09-26 13:47:08 +02:00
parent f425c082ba
commit 1472c343b2
12 changed files with 665 additions and 358 deletions

View File

@ -2,6 +2,7 @@ src/nrfx/drivers/nrfx_common.c
src/nrfx/hal/nrf_clock.c
src/nrfx/hal/nrf_dppi.c
src/nrfx/hal/nrf_egu.c
src/nrfx/hal/nrf_nvmc.c
src/nrfx/hal/nrf_rtc.c
src/nrfx/hal/nrf_timer.c
src/nrfx/hal/nrf_hal_originals.c

View File

@ -13,6 +13,8 @@ src/HW_models/NHW_DPPI.c
src/HW_models/NHW_CLOCK.c
src/HW_models/NHW_EGU.c
src/HW_models/NHW_misc.c
src/HW_models/NHW_NVMC.c
src/HW_models/NHW_NVM_backend.c
src/HW_models/NHW_RADIO.c
src/HW_models/NHW_RADIO_bitcounter.c
src/HW_models/NHW_RADIO_signals.c

View File

@ -5,6 +5,7 @@ src/nrfx/hal/nrf_clock.c
src/nrfx/hal/nrf_dppi.c
src/nrfx/hal/nrf_ecb.c
src/nrfx/hal/nrf_egu.c
src/nrfx/hal/nrf_nvmc.c
src/nrfx/hal/nrf_radio.c
src/nrfx/hal/nrf_rng.c
src/nrfx/hal/nrf_rtc.c

View File

@ -7,9 +7,12 @@
/*
* NVMC - Non-volatile memory controller
* https://infocenter.nordicsemi.com/topic/ps_nrf52833/nvmc.html?cp=5_1_0_3_2
* https://infocenter.nordicsemi.com/topic/ps_nrf5340/nvmc.html?cp=4_0_0_6_20
* &
* UICR - User information configuration registers
* https://infocenter.nordicsemi.com/topic/ps_nrf52833/uicr.html?cp=5_1_0_3_4
* https://infocenter.nordicsemi.com/topic/ps_nrf5340/chapters/uicr/doc/uicr.html?cp=4_0_0_4_3_2
* https://infocenter.nordicsemi.com/topic/ps_nrf5340/chapters/uicr.network/doc/uicr.network.html?cp=4_0_0_5_3_1
*
* Notes for the NVMC:
* * The CPU is not stalled while doing flash operations, "while executing from flash"
@ -19,8 +22,9 @@
* * The model will not prevent you from writing too many times (more then n_write)
* the same address between erases
*
* * The spec does not specify how much earlier READYNEXT, so this model just sets
* it when the previous operation is done (together with READY)
* * For nrf52s, the spec does not specify how much earlier READYNEXT is,
* and for nrf53 it is not mentioned in the documentation,
* so this model just sets it when the previous operation is done (together with READY)
*
* * The partial erase timer is fully accurate. Meaning, a partial erase always
* takes exactly ERASEPAGEPARTIALCFG ms.
@ -33,6 +37,17 @@
*
* * There is no modeling of the power failure protection
*
* * (nRF53) CONFIGNS and CONFIG have the same abilities (including enabling a partial erase)
*
* * (nRF53) There is no handling or differentiation between secure and non secure areas
*
* * (nRF53) All registers are accessible from all SW (there is no differentiation
* between secure and not secure registers)
*
* * (nRF53) CONFIGNS is ignored so far. As we lack an SPU model,
* all Flash is assumed to be "secure" from the point of view that
* its access mode is only controller by CONFIG.
*
* Notes for the UICR
* * The PSELRESET[], APPROTECT, NFCPINS, DEBUGCTRL & REGOUT0 registers are ignored
* Their equivalent functionality is not implemented
@ -41,19 +56,15 @@
* * Provide use/wear statistics per page and address.
* * Allow accumulating those statistics between runs (save stats to file)
* * Warn users if an address is written more than n_write between erases
*/
/*
* Notes for 53:
* * CONFIGNS and CONFIG have the same abilities (including enabling a partial erase)
* * There is no handling or differentiation between secure and non secure areas
* * All registers are accessible from all SW (there is no differentiation between secure and not secure registers)
* * There is no modelling of the protection features (Access port protection)
*
* Main 52-53 diffs:
* Erase (full or partial) by writing 0xFF..FF to first word of page instead of starting erase
* UICR can only be erased with eraseall (not by writing 0xFF on its first word, no ERASEUICR register)
* 2 instances of flash and UICR
*
* Implementation notes:
* This file includes models for a nrf52 and 53 flash and NVMController and of an empty
* UICR.
* And instantiates 1 set for the nrf52 and 2 sets (one per MCU) for the nrf53,
* (and initializes them at startup and frees them on exit),
* Note that this is partly described in the configuration (NHW_config.h), and partly
* "hardcoded" here for the target type (around command line parameters)
*/
#include <string.h>
@ -70,388 +81,528 @@
#include "NHW_peri_types.h"
#include "NHW_NVMC.h"
#include "NHW_NVM_backend.h"
#include "NHW_NVM_common.h"
NRF_UICR_Type *NRF_UICR_regs_p;
NRF_NVMC_Type NRF_NVMC_regs = {0};
NRF_UICR_Type *NRF_UICR_regs_p[NHW_NVMC_UICR_TOTAL_INST];
NRF_NVMC_Type *NRF_NVMC_regs_p[NHW_NVMC_UICR_TOTAL_INST];
NRF_NVMC_Type NRF_NVMC_regs[NHW_NVMC_UICR_TOTAL_INST] = {0};
static bs_time_t Timer_NVMC = TIME_NEVER; //Time when the next flash operation will be completed
static nvm_storage_state_t flash_st;
static nvm_storage_state_t uicr_st;
static enum flash_op_t flash_op;
static uint32_t erase_address;
static bs_time_t time_under_erase[FLASH_N_PAGES];
static bool page_erased[FLASH_N_PAGES];
enum flash_op_t {flash_idle = 0, flash_write, flash_erase, flash_erase_partial, flash_erase_uicr, flash_erase_all};
static bs_time_t flash_t_eraseall = 173000;
static bs_time_t flash_t_erasepage = 87500;
static bs_time_t flash_t_write = 42;
static double flash_partial_erase_factor = 1.0; //actual tERASEPAGEPARTIAL,acc for this given device
struct hw_nvmc_st_t {
bs_time_t timer;
NRF_NVMC_Type *NVMC_regs;
nvm_storage_state_t flash_st;
nvm_storage_state_t uicr_st;
enum flash_op_t flash_op;
uint32_t erase_page;
bs_time_t *time_under_erase; //[flash_n_pages];
bool *page_erased; //[flash_n_pages];
uint flash_start_addr;
uint flash_size;
uint flash_page_size;
uint flash_n_pages;
uint uicr_size;
};
static struct hw_nvmc_st_t hw_nvmc_st[NHW_NVMC_UICR_TOTAL_INST];
struct nvmc_args_t {
struct nhw_nvm_st_args_t uicr;
struct nhw_nvm_st_args_t flash;
struct nhw_nvm_st_args_t uicr[NHW_NVMC_UICR_TOTAL_INST];
struct nhw_nvm_st_args_t flash[NHW_NVMC_UICR_TOTAL_INST];
bool flash_erase_warnings;
} nvmc_args;
static void nhw_nvmc_find_next_event(void) {
Timer_NVMC = hw_nvmc_st[0].timer;
for (int i = 1; i < NHW_NVMC_UICR_TOTAL_INST; i++) {
if (hw_nvmc_st[i].timer < Timer_NVMC) {
Timer_NVMC = hw_nvmc_st[i].timer;
}
}
nsi_hws_find_next_event();
}
/**
* Initialize the NVMC and UICR models
*/
static void nrfhw_nvmc_uicr_init(void) {
memset(&NRF_NVMC_regs, 0x00, sizeof(NRF_NVMC_regs));
NRF_NVMC_regs.READY = 1;
NRF_NVMC_regs.READYNEXT = 1;
NRF_NVMC_regs.ERASEPAGEPARTIALCFG = 0x0000000A;
static void nhw_nvmc_uicr_init(void)
{
uint flash_start_addr[NHW_NVMC_UICR_TOTAL_INST] = NHW_FLASH_START_ADDR;
uint flash_page_sizes[NHW_NVMC_UICR_TOTAL_INST] = NHW_FLASH_PAGESIZE;
uint flash_n_pages[NHW_NVMC_UICR_TOTAL_INST] = NHW_FLASH_N_PAGES;
uint uicr_size[NHW_NVMC_UICR_TOTAL_INST] = NHW_UICR_SIZE;
flash_op = flash_idle;
Timer_NVMC = TIME_NEVER;
nsi_hws_find_next_event();
memset(NRF_NVMC_regs, 0x00, sizeof(NRF_NVMC_regs));
nhw_nvm_init_storage(&flash_st, &nvmc_args.flash,
FLASH_SIZE, "flash");
// We book more than the actual flash area, to avoid segfaults if
// somebody tries to access the HW control registers
nhw_nvm_init_storage(&uicr_st, &nvmc_args.uicr,
sizeof(NRF_UICR_Type), "UICR");
for (int inst = 0; inst < NHW_NVMC_UICR_TOTAL_INST; inst++) {
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
this->NVMC_regs = &NRF_NVMC_regs[inst];
NRF_NVMC_regs_p[inst] = &NRF_NVMC_regs[inst];
NRF_UICR_regs_p = (NRF_UICR_Type *)uicr_st.storage;
NRF_NVMC_regs[inst].READY = 1;
NRF_NVMC_regs[inst].READYNEXT = 1;
NRF_NVMC_regs[inst].ERASEPAGEPARTIALCFG = 0x0000000A;
//Reset the partial erase tracking
for (int i = 0; i < FLASH_N_PAGES; i++) {
time_under_erase[i] = 0;
page_erased[i] = true;
}
if (nvmc_args.flash_erase_warnings) {
for (int i = 0; i < FLASH_SIZE/4; i+=4) {
if (*(uint32_t*)(flash_st.storage + i) != 0) {
page_erased[i/FLASH_PAGE_SIZE] = false;
//Jump to next page start:
i = (i + FLASH_PAGE_SIZE)/FLASH_PAGE_SIZE*FLASH_PAGE_SIZE;
this->flash_op = flash_idle;
this->timer = TIME_NEVER;
this->flash_start_addr = flash_start_addr[inst];
this->flash_n_pages = flash_n_pages[inst];
this->flash_page_size = flash_page_sizes[inst];
this->flash_size = this->flash_n_pages * this->flash_page_size;
nhw_nvm_init_storage(&this->flash_st, &nvmc_args.flash[inst],
this->flash_size, "flash");
this->uicr_size = uicr_size[inst];
nhw_nvm_init_storage(&this->uicr_st, &nvmc_args.uicr[inst],
this->uicr_size, "UICR");
NRF_UICR_regs_p[inst] = (NRF_UICR_Type *)this->uicr_st.storage;
this->time_under_erase = (bs_time_t *)bs_calloc(this->flash_n_pages, sizeof(bs_time_t));
this->page_erased = (bool *)bs_malloc(this->flash_n_pages * sizeof(bool));
memset(this->page_erased, true, this->flash_n_pages * sizeof(bool));
if (nvmc_args.flash_erase_warnings) {
for (int i = 0; i < this->flash_size; i+=4) {
if (*(uint32_t*)(this->flash_st.storage + i) != 0) {
int page_size = this->flash_page_size;
this->page_erased[i/page_size] = false;
//Jump to next page start:
i = (i + page_size)/page_size*page_size;
}
}
}
}
nhw_nvmc_find_next_event();
}
NSI_TASK(nrfhw_nvmc_uicr_init, HW_INIT, 100);
NSI_TASK(nhw_nvmc_uicr_init, HW_INIT, 100);
/**
* Clean up the NVMC and UICR model before program exit
*/
static void nrfhw_nvmc_uicr_clean_up(void) {
nhw_nvm_clear_storage(&flash_st);
nhw_nvm_clear_storage(&uicr_st);
static void nhw_nvmc_uicr_clean_up(void) {
for (int inst = 0; inst < NHW_NVMC_UICR_TOTAL_INST; inst++) {
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
nhw_nvm_clear_storage(&this->flash_st);
nhw_nvm_clear_storage(&this->uicr_st);
free(this->time_under_erase);
this->time_under_erase = NULL;
free(this->page_erased);
this->page_erased = NULL;
}
}
NSI_TASK(nrfhw_nvmc_uicr_clean_up, ON_EXIT_PRE, 100);
NSI_TASK(nhw_nvmc_uicr_clean_up, ON_EXIT_PRE, 100);
/*
* Complete the actual erase of a flash page
*/
static void nrfhw_nvmc_complete_erase(void){
uint base_address = erase_address/FLASH_PAGE_SIZE*FLASH_PAGE_SIZE;
static void nhw_nvmc_complete_erase(uint inst) {
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
uint erase_page = this->erase_page;
uint base_address = erase_page*this->flash_page_size;
memset(&flash_st.storage[base_address], 0xFF, FLASH_PAGE_SIZE);
memset(&this->flash_st.storage[base_address], 0xFF, this->flash_page_size);
time_under_erase[erase_address/FLASH_PAGE_SIZE] = 0;
page_erased[erase_address/FLASH_PAGE_SIZE] = true;
this->time_under_erase[erase_page] = 0;
this->page_erased[erase_page] = true;
}
/*
* End of a partial erase (the full erase may not be yet completed)
*/
static void nrfhw_nvmc_complete_erase_partial(void){
if (time_under_erase[erase_address/FLASH_PAGE_SIZE] >= flash_t_erasepage) {
nrfhw_nvmc_complete_erase();
static void nhw_nvmc_complete_erase_partial(uint inst){
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
if (this->time_under_erase[this->erase_page] >= NHW_NVMC_FLASH_T_ERASEPAGE) {
nhw_nvmc_complete_erase(inst);
}
}
/*
* Complete the actual erase of the UICR page
*/
static void nrfhw_nvmc_complete_erase_uicr(void){
(void)memset(uicr_st.storage, 0xFF, uicr_st.size);
static void nhw_nvmc_complete_erase_uicr(uint inst){
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
(void)memset(this->uicr_st.storage, 0xFF, this->uicr_st.size);
}
/*
* Complete the actual erase of all flash and UICR page
* Complete the actual erase of all flash and UICR pages
*/
static void nrfhw_nvmc_complete_erase_all(void){
nrfhw_nvmc_complete_erase_uicr();
(void)memset(flash_st.storage, 0xFF, flash_st.size);
for (int i = 0; i < FLASH_N_PAGES; i++) {
time_under_erase[i] = 0;
page_erased[i] = true;
}
static void nhw_nvmc_complete_erase_all(uint inst){
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
nhw_nvmc_complete_erase_uicr(inst);
(void)memset(this->flash_st.storage, 0xFF, this->flash_st.size);
memset(this->time_under_erase, 0, this->flash_n_pages*sizeof(bs_time_t));
memset(this->page_erased, true, this->flash_n_pages*sizeof(bool));
}
/**
* Time has come when the programmed flash operation has finished
*/
static void nrfhw_nvmc_timer_triggered(void){
static void nhw_nvmc_timer_triggered(void){
switch (flash_op) {
case flash_write:
//Nothing left to be done
break;
case flash_erase:
nrfhw_nvmc_complete_erase();
break;
case flash_erase_partial:
nrfhw_nvmc_complete_erase_partial();
break;
case flash_erase_uicr:
nrfhw_nvmc_complete_erase_uicr();
break;
case flash_erase_all:
nrfhw_nvmc_complete_erase_all();
break;
default:
bs_trace_error_time_line("%s: timer triggered with no pending "
"operation (%i)\n", __func__, flash_op);
break;
for (int inst = 0; inst < NHW_NVMC_UICR_TOTAL_INST; inst++) {
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
if (this->timer != Timer_NVMC) {
continue;
}
switch (this->flash_op) {
case flash_write:
//Nothing left to be done
break;
case flash_erase:
nhw_nvmc_complete_erase(inst);
break;
case flash_erase_partial:
nhw_nvmc_complete_erase_partial(inst);
break;
case flash_erase_uicr:
nhw_nvmc_complete_erase_uicr(inst);
break;
case flash_erase_all:
nhw_nvmc_complete_erase_all(inst);
break;
default:
bs_trace_error_time_line("%s: timer triggered with no pending "
"operation (%i)\n", __func__, this->flash_op);
break;
}
this->flash_op = flash_idle;
NRF_NVMC_regs[inst].READY = 1;
NRF_NVMC_regs[inst].READYNEXT = 1;
this->timer = TIME_NEVER;
}
flash_op = flash_idle;
NRF_NVMC_regs.READY = 1;
NRF_NVMC_regs.READYNEXT = 1;
Timer_NVMC = TIME_NEVER;
nsi_hws_find_next_event();
nhw_nvmc_find_next_event();
}
NSI_HW_EVENT(Timer_NVMC, nrfhw_nvmc_timer_triggered, 50);
NSI_HW_EVENT(Timer_NVMC, nhw_nvmc_timer_triggered, 50);
bs_time_t nrfhw_nvmc_time_to_ready(void) {
if (NRF_NVMC_regs.READY) {
bs_time_t nhw_nvmc_time_to_ready(uint inst) {
if (NRF_NVMC_regs[inst].READY) {
return 0;
} else {
return Timer_NVMC - nsi_hws_get_time();
return hw_nvmc_st[inst].timer - nsi_hws_get_time();
}
}
#define ERASE_ENABLED_CHECK(x) \
if ((NRF_NVMC_regs.CONFIG & NVMC_CONFIG_WEN_Msk) != NVMC_CONFIG_WEN_Een) { \
#define ERASE_ENABLED_CHECK(i, x) \
if ((NRF_NVMC_regs[i].CONFIG & NVMC_CONFIG_WEN_Msk) != NVMC_CONFIG_WEN_Een) { \
bs_trace_warning_line_time("%s: %s while erase is not enabled in " \
"CONFIG (%u), it will be ignored\n", \
__func__, x, NRF_NVMC_regs.CONFIG); \
"NVMC[%i].CONFIG (%u), it will be ignored\n", \
__func__, x, i, NRF_NVMC_regs[i].CONFIG); \
return; \
}
#define BUSY_CHECK(x) \
if (flash_op != flash_idle) { \
bs_trace_warning_line_time("%s: %s while the flash was already" \
#define ERASEPARTIAL_ENABLED_CHECK(i, x) \
if ((NRF_NVMC_regs[i].CONFIG & NVMC_CONFIG_WEN_Msk) != NVMC_CONFIG_WEN_PEen) { \
bs_trace_warning_line_time("%s: %s while partial erase is not enabled " \
"in NVMC[%i].CONFIG (%u), it will be ignored\n", \
__func__, x, i, NRF_NVMC_regs[i].CONFIG); \
return; \
}
#define BUSY_CHECK(i, x) \
if ( hw_nvmc_st[i].flash_op != flash_idle) { \
bs_trace_warning_line_time("%s: %s while the flash(%i) was already" \
" busy (%i), it will be ignored\n", \
__func__, x, flash_op); \
__func__, x, i, hw_nvmc_st[inst].flash_op); \
return; \
}
#define CHECK_ADDRESS_INRANGE(x,y) \
if (x >= FLASH_SIZE) { \
bs_trace_error_time_line("%s : Attempted to %s an address (%u) " \
"outside of the flash area\n", __func__, y, x); \
}
#define CHECK_ADDRESS_INRANGE_UICR(x,y) \
if (x >= UICR_SIZE) { \
bs_trace_error_time_line("%s : Attempted to %s an address (%u) " \
"outside of the uicr area\n", __func__, y, x); \
}
#define CHECK_ERASE_ADDRESS(x) \
CHECK_ADDRESS_INRANGE(x, "erase"); \
if (x % FLASH_PAGE_SIZE != 0) { \
#define CHECK_ERASE_ADDRESS(i, x) \
if ((x < hw_nvmc_st[i].flash_start_addr) \
|| (x >= hw_nvmc_st[i].flash_start_addr + hw_nvmc_st[i].flash_size)) { \
bs_trace_error_time_line("%s : Attempted to erase an address (%u) " \
"outside of the flash (%i) area\n", __func__, x, i); \
} \
if (x % hw_nvmc_st[i].flash_page_size != 0) { \
bs_trace_warning_time_line("%s : Address (%u) is not at the start of the page " \
"just the page it contains will be erased\n", __func__, x); \
}
#define CHECK_PARTIAL_ERASE(addr, type) \
if (nvmc_args.flash_erase_warnings && time_under_erase[addr/FLASH_PAGE_SIZE] > 0){ \
bs_trace_warning_line_time("%s: %s in partially erased address (%u, "\
#define CHECK_PARTIAL_ERASE(i, addr, type) \
if (nvmc_args.flash_erase_warnings && hw_nvmc_st[i].time_under_erase[addr/hw_nvmc_st[i].flash_page_size] > 0){ \
bs_trace_warning_line_time("%s: %s in partially erased address (%i, %u, "\
"time_under_erase: %"PRItime" < %"PRItime")\n", \
__func__, type, addr, time_under_erase[addr/FLASH_PAGE_SIZE], flash_t_erasepage); \
__func__, type, i, addr, \
hw_nvmc_st[i].time_under_erase[addr/hw_nvmc_st[i].flash_page_size], \
NHW_NVMC_FLASH_T_ERASEPAGE); \
}
#define OUT_OF_FLASH_ERROR(addr) \
bs_trace_error_time_line("%s: Attempted access outside of flash and UICR areas (0x%X)\n",\
__func__, address)
static void nrfhw_nvmc_erase_page(uint32_t address){
ERASE_ENABLED_CHECK("ERASEPAGE");
BUSY_CHECK("ERASEPAGE");
CHECK_ERASE_ADDRESS(address);
flash_op = flash_erase;
NRF_NVMC_regs.READY = 0;
NRF_NVMC_regs.READYNEXT = 0;
erase_address = address;
Timer_NVMC = nsi_hws_get_time() + flash_t_erasepage;
nsi_hws_find_next_event();
void nhw_nvmc_erase_page(uint inst, uint32_t address){
ERASE_ENABLED_CHECK(inst, "ERASEPAGE");
BUSY_CHECK(inst, "ERASEPAGE");
CHECK_ERASE_ADDRESS(inst, address);
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
this->flash_op = flash_erase;
NRF_NVMC_regs[inst].READY = 0;
NRF_NVMC_regs[inst].READYNEXT = 0;
this->erase_page = (address - this->flash_start_addr) / this->flash_page_size;
this->timer = nsi_hws_get_time() + NHW_NVMC_FLASH_T_ERASEPAGE;
nhw_nvmc_find_next_event();
}
#if (NHW_NVMC_HAS_ERASEREGS)
/* Note ERASEPCR1 is an alias to ERASEPAGE (same register) */
void nrfhw_nvmc_regw_sideeffects_ERASEPAGE(void) {
nrfhw_nvmc_erase_page(NRF_NVMC_regs.ERASEPAGE);
void nhw_nvmc_regw_sideeffects_ERASEPAGE(uint inst) {
nhw_nvmc_erase_page(inst, NRF_NVMC_regs[inst].ERASEPAGE);
}
void nrf_nvmc_regw_sideeffects_ERASEPCR0(void) {
nrfhw_nvmc_erase_page(NRF_NVMC_regs.ERASEPCR0);
void nhw_nvmc_regw_sideeffects_ERASEPCR0(uint inst) {
nhw_nvmc_erase_page(inst, NRF_NVMC_regs[inst].ERASEPCR0);
}
void nrfhw_nvmc_regw_sideeffects_ERASEUICR(void) {
NRF_NVMC_regs.ERASEUICR &= 1;
void nhw_nvmc_regw_sideeffects_ERASEUICR(uint inst) {
NRF_NVMC_regs[inst].ERASEUICR &= 1;
if (NRF_NVMC_regs.ERASEUICR) {
NRF_NVMC_regs.ERASEUICR = 0;
ERASE_ENABLED_CHECK("ERASEUICR");
BUSY_CHECK("ERASEUICR");
flash_op = flash_erase_uicr;
NRF_NVMC_regs.READY = 0;
NRF_NVMC_regs.READYNEXT = 0;
Timer_NVMC = nsi_hws_get_time() + flash_t_erasepage;
nsi_hws_find_next_event();
if (NRF_NVMC_regs[inst].ERASEUICR) {
NRF_NVMC_regs[inst].ERASEUICR = 0;
ERASE_ENABLED_CHECK(inst, "ERASEUICR");
BUSY_CHECK(inst, "ERASEUICR");
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
this->flash_op = flash_erase_uicr;
NRF_NVMC_regs[inst].READY = 0;
NRF_NVMC_regs[inst].READYNEXT = 0;
this->timer = nsi_hws_get_time() + NHW_NVMC_FLASH_T_ERASEPAGE;
nhw_nvmc_find_next_event();
}
}
#endif
void nhw_nvmc_regw_sideeffects_ERASEALL(uint inst) {
NRF_NVMC_regs[inst].ERASEALL &= 1;
if (NRF_NVMC_regs[inst].ERASEALL) {
NRF_NVMC_regs[inst].ERASEALL = 0;
ERASE_ENABLED_CHECK(inst, "ERASEALL");
BUSY_CHECK(inst, "ERASEALL");
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
this->flash_op = flash_erase_all;
NRF_NVMC_regs[inst].READY = 0;
NRF_NVMC_regs[inst].READYNEXT = 0;
this->timer = nsi_hws_get_time() + NHW_NVMC_FLASH_T_ERASEALL;
nhw_nvmc_find_next_event();
}
}
void nrfhw_nvmc_regw_sideeffects_ERASEALL(void) {
NRF_NVMC_regs.ERASEALL &= 1;
void nhw_nvmc_erase_page_partial(uint inst, uint32_t address) {
#if defined(NVMC_CONFIG_WEN_PEen)
ERASEPARTIAL_ENABLED_CHECK(inst, "ERASEPARTIAL");
#else
ERASE_ENABLED_CHECK(inst, "ERASEPARTIAL");
#endif
BUSY_CHECK(inst, "ERASEPARTIAL");
CHECK_ERASE_ADDRESS(inst, address);
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
if (NRF_NVMC_regs.ERASEALL) {
NRF_NVMC_regs.ERASEALL = 0;
ERASE_ENABLED_CHECK("ERASEALL");
BUSY_CHECK("ERASEALL");
flash_op = flash_erase_all;
NRF_NVMC_regs.READY = 0;
NRF_NVMC_regs.READYNEXT = 0;
Timer_NVMC = nsi_hws_get_time() + flash_t_eraseall;
nsi_hws_find_next_event();
this->erase_page = (address - this->flash_start_addr)/this->flash_page_size;
this->flash_op = flash_erase_partial;
NRF_NVMC_regs[inst].READY = 0;
NRF_NVMC_regs[inst].READYNEXT = 0;
bs_time_t duration = NHW_NVMC_FLASH_PARTIAL_ERASE_FACTOR * NRF_NVMC_regs[inst].ERASEPAGEPARTIALCFG * 1000;
if (this->page_erased[this->erase_page] == false) {
this->time_under_erase[this->erase_page] += duration;
}
this->timer = nsi_hws_get_time() + duration;
nhw_nvmc_find_next_event();
}
void nrfhw_nvmc_regw_sideeffects_ERASEPAGEPARTIAL(void) {
ERASE_ENABLED_CHECK("ERASEPARTIAL");
BUSY_CHECK("ERASEPARTIAL");
erase_address = NRF_NVMC_regs.ERASEPAGEPARTIAL;
CHECK_ERASE_ADDRESS(erase_address);
#if (NHW_NVMC_HAS_ERASEREGS)
void nhw_nvmc_regw_sideeffects_ERASEPAGEPARTIAL(uint inst) {
nhw_nvmc_erase_page_partial(inst, NRF_NVMC_regs[inst].ERASEPAGEPARTIAL);
}
#endif
flash_op = flash_erase_partial;
NRF_NVMC_regs.READY = 0;
NRF_NVMC_regs.READYNEXT = 0;
bs_time_t duration = flash_partial_erase_factor * NRF_NVMC_regs.ERASEPAGEPARTIALCFG * 1000;
if (page_erased[erase_address/FLASH_PAGE_SIZE] == false) {
time_under_erase[erase_address/FLASH_PAGE_SIZE] += duration;
/*
* Check if an address is in range of any flash or UICR
* and if so, set the inst, storage and offset accordingly.
*
* Where address is an address in either a "hard" flash address
* (meaning in the 0x00..00 range for nrf52, or also in the 0x010..00 range for nrf53)
* Or a "soft" address inside an UICR range (meaning an address gotten as an offset from
* NRF_UICR_BASE/NRF_UICR_S/NS_BASE)
*
* If the address is not in range, the function does not return, but produces an error
*/
static void nhw_nvmc_address_location(uint32_t address, uint *inst, nvm_storage_state_t **storage, int *offset){
for (int i = 0; i < NHW_NVMC_UICR_TOTAL_INST; i ++) {
*inst = i;
if ((address >= hw_nvmc_st[i].flash_start_addr) \
&& (address < hw_nvmc_st[i].flash_start_addr + hw_nvmc_st[i].flash_size)) {
*storage = &hw_nvmc_st[i].flash_st;
*offset = address - hw_nvmc_st[i].flash_start_addr;
return;
}
if ((address >= (uint32_t)hw_nvmc_st[i].uicr_st.storage) \
&& (address < (uint32_t)hw_nvmc_st[i].uicr_st.storage + hw_nvmc_st[i].uicr_size)) {
*storage = &hw_nvmc_st[i].uicr_st;
*offset = address - (uint32_t)hw_nvmc_st[i].uicr_st.storage;
return;
}
}
Timer_NVMC = nsi_hws_get_time() + duration;
nsi_hws_find_next_event();
OUT_OF_FLASH_ERROR(address);
}
static bool addr_in_uicr(uint32_t address){
return (address >= (uintptr_t)NRF_UICR_regs_p)
&& (address < (uintptr_t)NRF_UICR_regs_p + UICR_SIZE);
}
void nrfhw_nmvc_write_word(uint32_t address, uint32_t value){
BUSY_CHECK("write");
/*
* Write to a flash array (flash or uicr)
*
* Where address is an address in either a "hard" flash address
* (meaning in the 0x00..00 range for nrf52, or also in the 0x010..00 range for nrf53)
* Or a "soft" address inside an UICR range (meaning an address gotten as an offset from
* NRF_UICR_BASE/NRF_UICR_S/NS_BASE)
*/
void nhw_nmvc_write_word(uint32_t address, uint32_t value){
if ((address & 3) != 0){
bs_trace_error_line_time("%s: write to non word aligned address %u, "
"this would have hard-faulted in real HW\n",
__func__, address);
}
if ((NRF_NVMC_regs.CONFIG & NVMC_CONFIG_WEN_Msk) != NVMC_CONFIG_WEN_Wen) {
int offset;
nvm_storage_state_t *backend;
uint inst;
nhw_nvmc_address_location(address, &inst, &backend, &offset);
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
BUSY_CHECK(inst, "write");
#if (NHW_NVMC_HAS_ERASEREGS == 0)
if (backend == &this->flash_st) {
if ((NRF_NVMC_regs[inst].CONFIG & NVMC_CONFIG_WEN_Msk) == NVMC_CONFIG_WEN_Een) {
if ((value == 0xFFFFFFFF) && (offset % this->flash_page_size == 0)) {
nhw_nvmc_erase_page(inst, address);
return;
}
}
if ((NRF_NVMC_regs[inst].CONFIG & NVMC_CONFIG_WEN_Msk) == NVMC_CONFIG_WEN_PEen) {
if ((value == 0xFFFFFFFF) && (offset % this->flash_page_size == 0)) {
nhw_nvmc_erase_page_partial(inst, address);
return;
}
}
}
#endif
if ((NRF_NVMC_regs[inst].CONFIG & NVMC_CONFIG_WEN_Msk) != NVMC_CONFIG_WEN_Wen) {
bs_trace_warning_line_time("%s: write while write is not enabled in "
"CONFIG (%u), it will be ignored\n",
__func__, NRF_NVMC_regs.CONFIG);
__func__, NRF_NVMC_regs[inst].CONFIG);
return;
}
if (address < FLASH_SIZE) {
CHECK_PARTIAL_ERASE(address, "write");
page_erased[address/FLASH_PAGE_SIZE] = false;
/*
* Writing to flash clears to 0 bits which were one, but does not
* set to 1 bits which are 0.
*/
*(uint32_t*)&flash_st.storage[address] &= value;
}
else if (addr_in_uicr(address))
{
address = address - (uintptr_t)NRF_UICR_regs_p;
*(uint32_t*)&uicr_st.storage[address] &= value;
} else {
OUT_OF_FLASH_ERROR(address);
if (backend == &this->flash_st) {
CHECK_PARTIAL_ERASE(inst, offset, "write");
this->page_erased[offset/this->flash_page_size] = false;
}
flash_op = flash_write;
NRF_NVMC_regs.READY = 0;
NRF_NVMC_regs.READYNEXT = 0;
/*
* Writing to flash clears to 0 bits which were one, but does not
* set to 1 bits which are 0.
*/
*(uint32_t*)&backend->storage[offset] &= value;
Timer_NVMC = nsi_hws_get_time() + flash_t_write;
nsi_hws_find_next_event();
this->flash_op = flash_write;
NRF_NVMC_regs[inst].READY = 0;
NRF_NVMC_regs[inst].READYNEXT = 0;
this->timer = nsi_hws_get_time() + NHW_NVMC_FLASH_T_WRITE;
nhw_nvmc_find_next_event();
}
/**
* Read from the flash array with offset <address>
* (Note that the flash array starts at address 0x0 in real HW)
* Read from the flash array (flash or uicr)
*
* Where address is an address in either a "hard" flash address
* (meaning in the 0x00..00 range for nrf52, or also in the 0x010..00 range for nrf53)
* Or a "soft" address inside an UICR range (meaning an address gotten as an offset from
* NRF_UICR_BASE/NRF_UICR_S/NS_BASE)
*
* This models the embedded CPU accessing the flash array for read
*
* This operation is instantaneous in simulation.
* In real HW it is "fast"
*/
uint32_t nrfhw_nmvc_read_word(uint32_t address) {
uint32_t nhw_nmvc_read_word(uint32_t address) {
int offset;
nvm_storage_state_t *backend;
uint inst;
if (address < FLASH_SIZE)
{
CHECK_PARTIAL_ERASE(address, "read");
return *(uint32_t*)&flash_st.storage[address];
}
else if (addr_in_uicr(address))
{
address = address - (uintptr_t)NRF_UICR_regs_p;
return *(uint32_t*)&uicr_st.storage[address];
} else {
OUT_OF_FLASH_ERROR(address);
return 0;
nhw_nvmc_address_location(address, &inst, &backend, &offset);
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
if (backend == &this->flash_st) {
CHECK_PARTIAL_ERASE(inst, offset, "read");
}
return *(uint32_t*)&backend->storage[offset];
}
uint16_t nrfhw_nmvc_read_halfword(uint32_t address) {
if (address < FLASH_SIZE)
{
CHECK_PARTIAL_ERASE(address, "read");
return *(uint16_t*)&flash_st.storage[address];
}
else if (addr_in_uicr(address))
{
address = address - (uintptr_t)NRF_UICR_regs_p;
return *(uint16_t*)&uicr_st.storage[address];
} else {
OUT_OF_FLASH_ERROR(address);
return 0;
uint16_t nhw_nmvc_read_halfword(uint32_t address) {
int offset;
nvm_storage_state_t *backend;
uint inst;
nhw_nvmc_address_location(address, &inst, &backend, &offset);
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
if (backend == &this->flash_st) {
CHECK_PARTIAL_ERASE(inst, offset, "read");
}
return *(uint16_t*)&backend->storage[offset];
}
uint8_t nrfhw_nmvc_read_byte(uint32_t address) {
if (address < FLASH_SIZE)
{
CHECK_PARTIAL_ERASE(address, "read");
return *(uint8_t*)&flash_st.storage[address];
}
else if (addr_in_uicr(address))
{
address = address - (uintptr_t)NRF_UICR_regs_p;
return *(uint8_t*)&uicr_st.storage[address];
} else {
OUT_OF_FLASH_ERROR(address);
return 0;
uint8_t nhw_nmvc_read_byte(uint32_t address) {
int offset;
nvm_storage_state_t *backend;
uint inst;
nhw_nvmc_address_location(address, &inst, &backend, &offset);
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
if (backend == &this->flash_st) {
CHECK_PARTIAL_ERASE(inst, offset, "read");
}
return *(uint8_t*)&backend->storage[offset];
}
/**
* memcpy from the flash/uicr array from <address> <size> bytes.
* (Note that the flash array starts at address 0x0 in real HW)
*
* Where address is an address in either a "hard" flash address
* (meaning in the 0x00..00 range for nrf52, or also in the 0x010..00 range for nrf53)
* Or a "soft" address inside an UICR range (meaning an address gotten as an offset from
* NRF_UICR_BASE/NRF_UICR_S/NS_BASE)
*
* This is a convenience function in the model.
* It can be used from DMA controller models or the like.
@ -461,46 +612,71 @@ uint8_t nrfhw_nmvc_read_byte(uint32_t address) {
* can be dispatched by the interconnect and handled by
* the NVMC peripheral
*/
void nrfhw_nmvc_read_buffer(void *dest, uint32_t address, size_t size) {
void nhw_nmvc_read_buffer(void *dest, uint32_t address, size_t size) {
int offset;
nvm_storage_state_t *backend;
uint inst;
if (address < FLASH_SIZE)
{
CHECK_ADDRESS_INRANGE(address + size - 1, "read");
for (uint32_t i = address; i < address + size ; i+= FLASH_PAGE_SIZE) {
CHECK_PARTIAL_ERASE(i, "read");
nhw_nvmc_address_location(address, &inst, &backend, &offset);
struct hw_nvmc_st_t *this = &hw_nvmc_st[inst];
if ((backend == &this->flash_st) && (nvmc_args.flash_erase_warnings)) {
for (uint32_t i = offset; i < offset + size ; i+= this->flash_page_size) {
CHECK_PARTIAL_ERASE(inst, i, "read");
}
(void)memcpy(dest, &flash_st.storage[address], size);
}
else if (addr_in_uicr(address))
{
address = address - (uintptr_t)NRF_UICR_regs_p;
CHECK_ADDRESS_INRANGE_UICR(address + size - 1, "read");
(void)memcpy(dest, &uicr_st.storage[address], size);
} else {
OUT_OF_FLASH_ERROR(address);
if (offset + size >= backend->size) {
OUT_OF_FLASH_ERROR(address + size);
}
(void)memcpy(dest, &backend->storage[offset], size);
}
void* nrfhw_nmvc_flash_get_base_address(void){
return (void*)&flash_st.storage;
void* nrfhw_nmvc_flash_get_base_address(uint inst){
return (void*)&hw_nvmc_st[inst].flash_st.storage;
}
NVM_BACKEND_PARAMS_CALLBACS(uicr)
NVM_BACKEND_PARAMS_CALLBACS(flash)
#if defined(NRF52833)
NVM_BACKEND_PARAMS_CALLBACS(uicr, uicr[0])
NVM_BACKEND_PARAMS_CALLBACS(flash, flash[0])
#elif defined(NRF5340)
NVM_BACKEND_PARAMS_CALLBACS(flash, flash[1])
NVM_BACKEND_PARAMS_CALLBACS(uicr_app, uicr[0])
NVM_BACKEND_PARAMS_CALLBACS(flash_app, flash[0])
NVM_BACKEND_PARAMS_CALLBACS(uicr_net, uicr[1])
NVM_BACKEND_PARAMS_CALLBACS(flash_net, flash[1])
#endif
static void nvmc_register_cmd_args(void){
nvmc_args.uicr.in_ram = true;
nvmc_args.flash.in_ram = true;
for (int i = 0; i < NHW_NVMC_UICR_TOTAL_INST; i++) {
nvmc_args.uicr[i].in_ram = true;
nvmc_args.flash[i].in_ram = true;
}
static bs_args_struct_t args_struct_toadd[] = {
NVM_BACKEND_PARAMS(uicr, "UICR"),
NVM_BACKEND_PARAMS(flash, "FLASH"),
#if defined(NRF52833)
NVM_BACKEND_PARAMS(uicr, uicr[0], UICR),
NVM_BACKEND_PARAMS(flash, flash[0], FLASH),
#elif defined(NRF5340)
NVM_BACKEND_PARAMS(uicr_app, uicr[0], UICR_APP),
NVM_BACKEND_PARAMS(flash_app, flash[0], FLASH_APP),
NVM_BACKEND_PARAMS(uicr_net, uicr[1], UICR_NET),
NVM_BACKEND_PARAMS(flash_net, flash[1], FLASH_NET),
{ .is_switch = true,
.option = "flash_*",
.manual = true,
.type = 'b',
.descript = "All plain flash options are aliases for the network core flash"
},
NVM_BACKEND_PARAMS(flash, flash[1], FLASH_NET),
#endif
{ .is_switch = true,
.option = "flash_erase_warnings",
.type = 'b',
.dest = (void*)&nvmc_args.flash_erase_warnings,
.descript = "Give warnings when accessing partially erased pages"
.descript = "Give warnings when accessing partially erased pages in any flash"
},
ARG_TABLE_ENDMARKER
};

View File

@ -1,10 +1,10 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _NRF_HW_MODEL_NVMC_H
#define _NRF_HW_MODEL_NVMC_H
#ifndef _NRF_HW_MODEL_NHW_NVMC_H
#define _NRF_HW_MODEL_NHW_NVMC_H
#include "bs_types.h"
@ -12,28 +12,25 @@
extern "C"{
#endif
#define FLASH_PAGE_SIZE (4*1024)
#define FLASH_N_PAGES 128
#define FLASH_SIZE (FLASH_N_PAGES*FLASH_PAGE_SIZE)
void nhw_nvmc_regw_sideeffects_ERASEPAGE(uint inst);
void nhw_nvmc_regw_sideeffects_ERASEPCR0(uint inst);
void nhw_nvmc_regw_sideeffects_ERASEUICR(uint inst);
void nhw_nvmc_regw_sideeffects_ERASEALL(uint inst);
void nhw_nvmc_regw_sideeffects_ERASEPAGEPARTIAL(uint inst);
void* nrfhw_nmvc_flash_get_base_address(uint inst);
bs_time_t nhw_nvmc_time_to_ready(uint inst);
#define UICR_DATA_SIZE 64 /* Words */
#define UICR_SIZE ( UICR_DATA_SIZE*4 )
void nhw_nvmc_erase_page(uint inst, uint32_t address);
void nhw_nvmc_erase_page_partial(uint inst, uint32_t address);
void nrfhw_nvmc_regw_sideeffects_ERASEPAGE(void);
void nrf_nvmc_regw_sideeffects_ERASEPCR0(void);
void nrfhw_nvmc_regw_sideeffects_ERASEUICR(void);
void nrfhw_nvmc_regw_sideeffects_ERASEALL(void);
void nrfhw_nvmc_regw_sideeffects_ERASEPAGEPARTIAL(void);
void nrfhw_nmvc_write_word(uint32_t address, uint32_t value);
uint32_t nrfhw_nmvc_read_word(uint32_t address);
uint16_t nrfhw_nmvc_read_halfword(uint32_t address);
uint8_t nrfhw_nmvc_read_byte(uint32_t address);
void nrfhw_nmvc_read_buffer(void *dest, uint32_t address, size_t size);
void* nrfhw_nmvc_flash_get_base_address(void);
bs_time_t nrfhw_nvmc_time_to_ready(void);
void nhw_nmvc_write_word(uint32_t address, uint32_t value);
uint32_t nhw_nmvc_read_word(uint32_t address);
uint16_t nhw_nmvc_read_halfword(uint32_t address);
uint8_t nhw_nmvc_read_byte(uint32_t address);
void nhw_nmvc_read_buffer(void *dest, uint32_t address, size_t size);
#ifdef __cplusplus
}
#endif
#endif
#endif /* _NRF_HW_MODEL_NHW_NVMC_H */

View File

@ -38,17 +38,17 @@ void nhw_nvm_clear_storage(nvm_storage_state_t *st);
void nhw_nvm_init_storage(nvm_storage_state_t *st, struct nhw_nvm_st_args_t *args,
size_t size, char *type);
#define NVM_BACKEND_PARAMS(x, X) \
#define NVM_BACKEND_PARAMS(x, arg, X) \
{ .is_switch = true, \
.option = NSI_STRINGIFY(x) "_erase", \
.type = 'b', \
.dest = (void*)&nvmc_args. x .erase, \
.dest = (void*)&nvmc_args. arg .erase, \
.descript = "Reset the " # X " storage to their erase values (0xFF) at boot"\
}, \
{ .option = NSI_STRINGIFY(x) "_file", \
.name = "path", \
.type = 's', \
.dest = (void*)&nvmc_args. x .file, \
.dest = (void*)&nvmc_args. arg .file, \
.call_when_found = arg_##x##_file_found, \
.descript = "Path to the binary file where the " #X " content " \
"is stored (if set, toggles " NSI_STRINGIFY(x) "_in_ram to false)" \
@ -56,14 +56,14 @@ void nhw_nvm_init_storage(nvm_storage_state_t *st, struct nhw_nvm_st_args_t *arg
{ .option = NSI_STRINGIFY(x), \
.name = "path", \
.type = 's', \
.dest = (void*)&nvmc_args. x . file, \
.dest = (void*)&nvmc_args. arg . file, \
.call_when_found = arg_##x##_file_found, \
.descript = "Alias for " NSI_STRINGIFY(x) "_file" \
}, \
{ .is_switch = true, \
.option = NSI_STRINGIFY(x) "_rm", \
.type = 'b', \
.dest = (void*)&nvmc_args. x .rm, \
.dest = (void*)&nvmc_args. arg .rm, \
.descript = "Remove the " # X " file when terminating the execution " \
"(default no)" \
}, \
@ -76,12 +76,12 @@ void nhw_nvm_init_storage(nvm_storage_state_t *st, struct nhw_nvm_st_args_t *arg
"are ignored, and the " #X " content is always reset at startup" \
}
#define NVM_BACKEND_PARAMS_CALLBACS(x) \
#define NVM_BACKEND_PARAMS_CALLBACS(x, args) \
static void arg_##x##_file_found(char *argv, int offset){ \
nvmc_args. x .in_ram = false; \
nvmc_args. args .in_ram = false; \
} \
static void arg_##x##_in_ram_found(char *argv, int offset){ \
nvmc_args. x .in_ram = true; \
nvmc_args. args .in_ram = true; \
}

View File

@ -1,20 +0,0 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _NRF_HW_MODEL_NHW_NVM_COMMON_H
#define _NRF_HW_MODEL_NHW_NVM_COMMON_H
#ifdef __cplusplus
extern "C"{
#endif
enum flash_op_t {flash_idle = 0, flash_write, flash_erase, flash_erase_partial, flash_erase_uicr, flash_erase_all};
#ifdef __cplusplus
}
#endif
#endif /* _NRF_HW_MODEL_NHW_NVM_COMMON_H */

View File

@ -116,6 +116,19 @@
[47] = "SPIM3",\
}}
#define NHW_NVMC_UICR_TOTAL_INST 1
#define NHW_NVMC_HAS_ERASEREGS 1
#define NHW_FLASH_START_ADDR {0x00000000}
#define NHW_FLASH_PAGESIZE {(4*1024)}
#define NHW_FLASH_N_PAGES {128}
#define NHW_UICR_SIZE {776 /*64*4*/ /*bytes*/}
/* In case somebody tries to access the UICR registers, we book
* more space than its actual flash area (64*4)*/
#define NHW_NVMC_FLASH_T_ERASEALL (173000)
#define NHW_NVMC_FLASH_T_ERASEPAGE (87500)
#define NHW_NVMC_FLASH_T_WRITE (42)
#define NHW_NVMC_FLASH_PARTIAL_ERASE_FACTOR (1.0)
#define NHW_RADIO_TOTAL_INST 1
#define NHW_RADIO_0 0
#define NHW_RADIO_INT_MAP {{0 , 1}} /*Only core,RADIO_IRQn*/
@ -321,6 +334,22 @@
#define NHW_CORE_NAMES {"Application", "Network"}
#define NHW_NVMC_UICR_TOTAL_INST 2
#define NHW_NVMC_APP0 0
#define NHW_NVMC_NET0 1
#define NHW_UICR_APP0 0
#define NHW_UICR_NET0 1
#define NHW_NVMC_HAS_ERASEREGS 0
#define NHW_FLASH_START_ADDR {0x00000000, 0x01000000}
#define NHW_FLASH_PAGESIZE {(4*1024), (2*1024)}
#define NHW_FLASH_N_PAGES {256, 128}
#define NHW_UICR_SIZE {4096, 800 /*bytes*/}
//App UICR size including the KEYSLOT
#define NHW_NVMC_FLASH_T_ERASEALL (173000)
#define NHW_NVMC_FLASH_T_ERASEPAGE (87500)
#define NHW_NVMC_FLASH_T_WRITE (43)
#define NHW_NVMC_FLASH_PARTIAL_ERASE_FACTOR (1.0)
#define NHW_RADIO_TOTAL_INST 1
#define NHW_RADIO_NET0 0
#define NHW_RADIO_INT_MAP {{1 , 8}} /*Net core,RADIO_IRQn*/

View File

@ -1906,6 +1906,83 @@ typedef struct { /*!< (@ 0x4100F000) DPPIC_NS Str
#define DPPIC_CHG_CH0_Included (1UL) /*!< Include */
/* =========================================================================================================================== */
/* ================ NVMC ================ */
/* =========================================================================================================================== */
/**
* @brief Non-volatile memory controller (NVMC)
*
* Note: This is the app core register layouts
* as by now the icache registers are just ignored
*/
typedef struct { /*!< (@ 0x40039000) NVMC_NS Structure */
__IM uint32_t RESERVED[256];
__IM uint32_t READY; /*!< (@ 0x00000400) Ready flag */
__IM uint32_t RESERVED1;
__IM uint32_t READYNEXT; /*!< (@ 0x00000408) Ready flag */
__IM uint32_t RESERVED2[62];
__IOM uint32_t CONFIG; /*!< (@ 0x00000504) Configuration register */
__IM uint32_t RESERVED3;
__OM uint32_t ERASEALL; /*!< (@ 0x0000050C) Register for erasing all non-volatile user memory */
__IM uint32_t RESERVED4[3];
__IOM uint32_t ERASEPAGEPARTIALCFG; /*!< (@ 0x0000051C) Register for partial erase configuration */
__IM uint32_t RESERVED5[25];
__IOM uint32_t CONFIGNS; /*!< (@ 0x00000584) Non-secure configuration register */
__OM uint32_t WRITEUICRNS; /*!< (@ 0x00000588) Non-secure APPROTECT enable register */
} NRF_NVMC_Type; /*!< Size = 1420 (0x58c) */
/* Peripheral: NVMC */
/* Description: Non-volatile memory controller */
/* Register: NVMC_READY */
/* Description: Ready flag */
/* Bit 0 : NVMC is ready or busy */
#define NVMC_READY_READY_Pos (0UL) /*!< Position of READY field. */
#define NVMC_READY_READY_Msk (0x1UL << NVMC_READY_READY_Pos) /*!< Bit mask of READY field. */
#define NVMC_READY_READY_Busy (0UL) /*!< NVMC is busy (ongoing write or erase operation) */
#define NVMC_READY_READY_Ready (1UL) /*!< NVMC is ready */
/* Register: NVMC_READYNEXT */
/* Description: Ready flag */
/* Bit 0 : NVMC can accept a new write operation */
#define NVMC_READYNEXT_READYNEXT_Pos (0UL) /*!< Position of READYNEXT field. */
#define NVMC_READYNEXT_READYNEXT_Msk (0x1UL << NVMC_READYNEXT_READYNEXT_Pos) /*!< Bit mask of READYNEXT field. */
#define NVMC_READYNEXT_READYNEXT_Busy (0UL) /*!< NVMC cannot accept any write operation */
#define NVMC_READYNEXT_READYNEXT_Ready (1UL) /*!< NVMC is ready */
/* Register: NVMC_CONFIG */
/* Description: Configuration register */
/* Bits 2..0 : Program memory access mode. It is strongly recommended to only activate erase and write modes when they are actively used. Enabling write or erase will invalidate the cache and keep it invalidated. */
#define NVMC_CONFIG_WEN_Pos (0UL) /*!< Position of WEN field. */
#define NVMC_CONFIG_WEN_Msk (0x7UL << NVMC_CONFIG_WEN_Pos) /*!< Bit mask of WEN field. */
#define NVMC_CONFIG_WEN_Ren (0UL) /*!< Read only access */
#define NVMC_CONFIG_WEN_Wen (1UL) /*!< Write enabled */
#define NVMC_CONFIG_WEN_Een (2UL) /*!< Erase enabled */
#define NVMC_CONFIG_WEN_PEen (4UL) /*!< Partial erase enabled */
/* Register: NVMC_ERASEALL */
/* Description: Register for erasing all non-volatile user memory */
/* Bit 0 : Erase all non-volatile memory including UICR registers. Before the non-volatile memory can be erased, erasing must be enabled by setting CONFIG.WEN=Een. */
#define NVMC_ERASEALL_ERASEALL_Pos (0UL) /*!< Position of ERASEALL field. */
#define NVMC_ERASEALL_ERASEALL_Msk (0x1UL << NVMC_ERASEALL_ERASEALL_Pos) /*!< Bit mask of ERASEALL field. */
#define NVMC_ERASEALL_ERASEALL_NoOperation (0UL) /*!< No operation */
#define NVMC_ERASEALL_ERASEALL_Erase (1UL) /*!< Start chip erase */
/* Register: NVMC_ERASEPAGEPARTIALCFG */
/* Description: Register for partial erase configuration */
/* Bits 6..0 : Duration of the partial erase in milliseconds */
#define NVMC_ERASEPAGEPARTIALCFG_DURATION_Pos (0UL) /*!< Position of DURATION field. */
#define NVMC_ERASEPAGEPARTIALCFG_DURATION_Msk (0x7FUL << NVMC_ERASEPAGEPARTIALCFG_DURATION_Pos) /*!< Bit mask of DURATION field. */
/* =========================================================================================================================== */
/* ================ POWER_NS ================ */
@ -5307,6 +5384,21 @@ typedef struct { /*!< (@ 0x4100C000) TIMER0_NS St
#define TIMER_ONESHOTEN_ONESHOTEN_Disable (0UL) /*!< Disable one-shot operation */
#define TIMER_ONESHOTEN_ONESHOTEN_Enable (1UL) /*!< Enable one-shot operation */
/* =========================================================================================================================== */
/* ================ UICR ================ */
/* =========================================================================================================================== */
/**
* @brief User Information Configuration Registers (UICR)
* At this point just a chunk of reserved space of 2KiB
*/
typedef struct { /*!< (@ 0x10001000) UICR Structure */
__IM uint32_t RESERVED[1024];
} NRF_UICR_Type; /*!< Size = 4096 */
/* =========================================================================================================================== */
/* ================ VREQCTRL ================ */

View File

@ -32,6 +32,10 @@ NRF_FICR_Type NRF_FICR_regs;
static void nrf_ficr_init(void) {
memset(&NRF_FICR_regs, 0xFF, sizeof(NRF_FICR_regs));
#define FLASH_PAGE_SIZE (4*1024)
#define FLASH_N_PAGES 128
#define FLASH_SIZE (FLASH_N_PAGES*FLASH_PAGE_SIZE)
NRF_FICR_regs.CODEPAGESIZE = FLASH_PAGE_SIZE;
NRF_FICR_regs.CODESIZE = FLASH_N_PAGES;

View File

@ -12,54 +12,77 @@
#include "NHW_NVMC.h"
#include <nrfx_glue.h>
static int nvmc_number_from_ptr(NRF_NVMC_Type * p_reg)
{
int i = ((intptr_t)p_reg - (intptr_t)NRF_NVMC_regs_p[0]) / sizeof(NRF_NVMC_Type);
return i;
}
void nrf_nvmc_page_erase_start(NRF_NVMC_Type * p_reg, uint32_t page_addr)
{
int i = nvmc_number_from_ptr(p_reg);
#if defined(NRF52_SERIES)
p_reg->ERASEPAGE = page_addr;
nrfhw_nvmc_regw_sideeffects_ERASEPAGE();
nhw_nvmc_regw_sideeffects_ERASEPAGE(i);
#elif defined(NRF53_SERIES) || defined(NRF91_SERIES)
nhw_nvmc_erase_page(i, page_addr);
#endif
}
#if defined(NVMC_ERASEUICR_ERASEUICR_Msk)
void nrf_nvmc_uicr_erase_start(NRF_NVMC_Type * p_reg)
{
int i = nvmc_number_from_ptr(p_reg);
p_reg->ERASEUICR = 1;
nrfhw_nvmc_regw_sideeffects_ERASEUICR();
nhw_nvmc_regw_sideeffects_ERASEUICR(i);
}
#endif
void nrf_nvmc_erase_all_start(NRF_NVMC_Type * p_reg)
{
int i = nvmc_number_from_ptr(p_reg);
p_reg->ERASEALL = 1;
nrfhw_nvmc_regw_sideeffects_ERASEALL();
nhw_nvmc_regw_sideeffects_ERASEALL(i);
}
void nrf_nvmc_page_partial_erase_start(NRF_NVMC_Type * p_reg, uint32_t page_addr)
{
int i = nvmc_number_from_ptr(p_reg);
#if defined(NVMC_ERASEPAGEPARTIAL_ERASEPAGEPARTIAL_Msk)
p_reg->ERASEPAGEPARTIAL = page_addr;
nrfhw_nvmc_regw_sideeffects_ERASEPAGEPARTIAL();
nhw_nvmc_regw_sideeffects_ERASEPAGEPARTIAL(i);
#elif defined(NRF53_SERIES) || defined(NRF91_SERIES)
nhw_nvmc_erase_page_partial(i, page_addr);
#endif
}
uint8_t nrf_nvmc_byte_read(uint32_t addr)
{
return nrfhw_nmvc_read_byte(addr);
return nhw_nmvc_read_byte(addr);
}
uint16_t nrf_nvmc_halfword_read(uint32_t addr)
{
return nrfhw_nmvc_read_halfword(addr);
return nhw_nmvc_read_halfword(addr);
}
uint32_t nrf_nvmc_word_read(uint32_t addr)
{
return nrfhw_nmvc_read_word(addr);
return nhw_nmvc_read_word(addr);
}
void nrf_nvmc_word_write(uint32_t addr, uint32_t value)
{
nrfhw_nmvc_write_word(addr, value);
nhw_nmvc_write_word(addr, value);
}
void nrf_nvmc_buffer_read(void *dest, uint32_t addr, size_t n)
{
nrfhw_nmvc_read_buffer(dest, addr, n);
nhw_nmvc_read_buffer(dest, addr, n);
}
/*
@ -72,9 +95,10 @@ void nrf_nvmc_buffer_read(void *dest, uint32_t addr, size_t n)
* we actually wait more or less time depending on the pending
* time.
*/
static void nrf_nvmc_ready_wait(){
static void nrf_nvmc_ready_wait(unsigned int inst)
{
#if defined(NRFX_DELAY_US) && !defined(NO_NVMC_READY_AUTO_DELAY)
bs_time_t wait = nrfhw_nvmc_time_to_ready() + 1;
bs_time_t wait = nhw_nvmc_time_to_ready(inst) + 1;
wait = BS_MAX(BS_MIN(wait,100),1);
NRFX_DELAY_US(wait);
#endif
@ -84,16 +108,13 @@ bool nrf_nvmc_ready_check(NRF_NVMC_Type const * p_reg)
{
bool ready = (bool)(p_reg->READY & NVMC_READY_READY_Msk);
if (!ready) {
nrf_nvmc_ready_wait();
int i = nvmc_number_from_ptr((NRF_NVMC_Type *)p_reg);
nrf_nvmc_ready_wait(i);
}
return ready;
}
bool nrf_nvmc_write_ready_check(NRF_NVMC_Type const * p_reg)
{
bool ready = (bool)(p_reg->READY & NVMC_READY_READY_Msk);
if (!ready) {
nrf_nvmc_ready_wait();
}
return ready;
return nrf_nvmc_ready_check(p_reg);
}

View File

@ -76,9 +76,9 @@ extern NRF_GPIO_Type NRF_GPIO_regs[];
extern NRF_GPIOTE_Type NRF_GPIOTE_regs;
#undef NRF_GPIOTE_BASE
#define NRF_GPIOTE_BASE (&NRF_GPIOTE_regs)
extern NRF_NVMC_Type NRF_NVMC_regs;
extern NRF_NVMC_Type *NRF_NVMC_regs_p[];
#undef NRF_NVMC_BASE
#define NRF_NVMC_BASE (&NRF_NVMC_regs)
#define NRF_NVMC_BASE (NRF_NVMC_regs_p[0])
extern NRF_EGU_Type NRF_EGU_regs[6];
#undef NRF_EGU0_BASE
#define NRF_EGU0_BASE (&NRF_EGU_regs[0])
@ -93,8 +93,8 @@ extern NRF_EGU_Type NRF_EGU_regs[6];
#undef NRF_EGU5_BASE
#define NRF_EGU5_BASE (&NRF_EGU_regs[5])
#undef NRF_UICR_BASE
extern NRF_UICR_Type *NRF_UICR_regs_p;
#define NRF_UICR_BASE (NRF_UICR_regs_p)
extern NRF_UICR_Type *NRF_UICR_regs_p[];
#define NRF_UICR_BASE (NRF_UICR_regs_p[0])
/********************************************************************/
/********************************************************************/
@ -103,8 +103,9 @@ extern NRF_UICR_Type *NRF_UICR_regs_p;
#undef NRF_FICR_NS_BASE
#define NRF_FICR_NS_BASE NULL
extern NRF_UICR_Type *NRF_UICR_regs_p[];
#undef NRF_UICR_NS_BASE
#define NRF_UICR_NS_BASE NULL
#define NRF_UICR_NS_BASE (NRF_UICR_regs_p[NHW_UICR_NET0])
#undef NRF_CTI_NS_BASE
#define NRF_CTI_NS_BASE NULL
#undef NRF_DCNF_NS_BASE
@ -190,8 +191,9 @@ extern int NRF_SWI_regs[];
#define NRF_APPMUTEX_S_BASE NULL
#undef NRF_ACL_NS_BASE
#define NRF_ACL_NS_BASE NULL
extern NRF_NVMC_Type *NRF_NVMC_regs_p[];
#undef NRF_NVMC_NS_BASE
#define NRF_NVMC_NS_BASE NULL
#define NRF_NVMC_NS_BASE (NRF_NVMC_regs_p[NHW_NVMC_NET0])
#undef NRF_VMC_NS_BASE
#define NRF_VMC_NS_BASE NULL
#undef NRF_P0_NS_BASE
@ -210,8 +212,9 @@ extern int NRF_SWI_regs[];
#define NRF_CACHEINFO_S_BASE NULL
#undef NRF_FICR_S_BASE
#define NRF_FICR_S_BASE NULL
extern NRF_UICR_Type *NRF_UICR_regs_p[];
#undef NRF_UICR_S_BASE
#define NRF_UICR_S_BASE NULL
#define NRF_UICR_S_BASE (NRF_UICR_regs_p[NHW_UICR_APP0])
#undef NRF_CTI_S_BASE
#define NRF_CTI_S_BASE NULL
#undef NRF_TAD_S_BASE
@ -473,12 +476,13 @@ extern NRF_EGU_Type NRF_EGU_regs[];
#define NRF_USBREGULATOR_S_BASE NULL
#undef NRF_KMU_NS_BASE
#define NRF_KMU_NS_BASE NULL
extern NRF_NVMC_Type *NRF_NVMC_regs_p[];
#undef NRF_NVMC_NS_BASE
#define NRF_NVMC_NS_BASE NULL
#define NRF_NVMC_NS_BASE (NRF_NVMC_regs_p[NHW_NVMC_APP0])
#undef NRF_KMU_S_BASE
#define NRF_KMU_S_BASE NULL
#undef NRF_NVMC_S_BASE
#define NRF_NVMC_S_BASE NULL
#define NRF_NVMC_S_BASE (NRF_NVMC_regs_p[NHW_NVMC_APP0])
#undef NRF_P0_NS_BASE
#define NRF_P0_NS_BASE NULL
#undef NRF_P1_NS_BASE