NVMC & UICR: Add functionality

Including the necessary hal replacements.

And add in the FICR the flash page info.

Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
This commit is contained in:
Alberto Escolar Piedras 2023-04-25 17:22:02 +02:00
parent aaf0c6d9e4
commit 10ef6a9f8f
10 changed files with 732 additions and 7 deletions

View File

@ -35,7 +35,7 @@ WARNINGS:=-Wall -pedantic
COVERAGE:=
CFLAGS:=${ARCH} ${DEBUG} ${OPT} ${WARNINGS} -MMD -MP -std=c11 \
${INCLUDES} -fdata-sections -ffunction-sections \
-DNRF52833_XXAA
-DNRF52833_XXAA -D_XOPEN_SOURCE=500
LDFLAGS:=${ARCH} ${COVERAGE}
CPPFLAGS:=

View File

@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include "NRF_FICR.h"
#include "NRF_NVMC.h"
#include <string.h>
#include "bs_rand_main.h"
#include "weak_stubs.h"
@ -25,6 +26,9 @@ NRF_FICR_Type NRF_FICR_regs;
void nrf_ficr_init(){
memset(&NRF_FICR_regs, 0xFF, sizeof(NRF_FICR_regs));
NRF_FICR_regs.CODEPAGESIZE = FLASH_PAGE_SIZE;
NRF_FICR_regs.CODESIZE = FLASH_N_PAGES;
NRF_FICR_regs.DEVICEID[0] = (bs_random_uint32() & 0xFFFFFF00) + get_device_nbr();
NRF_FICR_regs.DEVICEID[1] = bs_random_uint32();
NRF_FICR_regs.DEVICEADDRTYPE = 0;

View File

@ -22,6 +22,7 @@
#include "NRF_PPI.h"
#include "NRF_TIMER.h"
#include "NRF_EGU.h"
#include "NRF_NVMC.h"
#include "irq_ctrl.h"
#include "BLECrypt_if.h"
#include "fake_timer.h"
@ -41,6 +42,7 @@ void nrf_hw_models_free_all(){
nrf_ficr_clean_up();
nrf_ppi_clean_up();
nrf_egu_clean_up();
nrfhw_nvmc_uicr_clean_up();
nrf_hw_model_timer_clean_up();
}
@ -49,7 +51,7 @@ void nrf_hw_models_free_all(){
* Like registering command line arguments or dump files
*/
void nrf_hw_pre_init() {
nrfhw_nvmc_uicr_pre_init();
}
/*
@ -71,6 +73,7 @@ void nrf_hw_initialize(nrf_hw_sub_args_t *args){
nrf_ficr_init();
nrf_ppi_init();
nrf_egu_init();
nrfhw_nvmc_uicr_init();
nrf_hw_model_timer_init();
nrf_hw_find_next_timer_to_trigger();
}
@ -79,6 +82,7 @@ extern bs_time_t Timer_event_fw_test_ticker;
extern bs_time_t Timer_irq_ctrl;
extern bs_time_t Timer_RNG;
extern bs_time_t Timer_TEMP;
extern bs_time_t Timer_NVMC;
extern bs_time_t Timer_CLOCK_LF;
extern bs_time_t Timer_CLOCK_HF;
extern bs_time_t Timer_RTC;
@ -98,6 +102,7 @@ typedef enum {
irq_ctrl_timer,
RNG_timer,
TEMP_timer,
NVMC_timer,
ECB_timer,
AAR_timer,
CLOCK_LF_timer,
@ -116,6 +121,7 @@ static bs_time_t *Timers[NumberOfNRFHWTimers] = { //Indexed with NRF_HW_next_tim
&Timer_irq_ctrl,
&Timer_RNG,
&Timer_TEMP,
&Timer_NVMC,
&Timer_ECB,
&Timer_AAR,
&Timer_CLOCK_LF,
@ -173,6 +179,10 @@ void nrf_hw_some_timer_reached() {
bs_trace_raw_manual_time(8, tm_get_abs_time(),"NRF HW: TEMP timer\n");
nrf_temp_timer_triggered();
break;
case NVMC_timer:
bs_trace_raw_manual_time(8, tm_get_abs_time(),"NRF HW: NVMC timer\n");
nrfhw_nvmc_timer_triggered();
break;
case ECB_timer:
bs_trace_raw_manual_time(8, tm_get_abs_time(),"NRF HW: ECB timer\n");
nrf_ecb_timer_triggered();

View File

@ -1,13 +1,600 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "bs_tracing.h"
#include "bs_cmd_line.h"
#include "bs_oswrap.h"
#include "NRF_NVMC.h"
#include "NRF_HW_model_top.h"
#include "time_machine_if.h"
#include "weak_stubs.h"
/*
* This is only a stub of the register interface with no functionality behind
* https://infocenter.nordicsemi.com/topic/ps_nrf52833/nvmc.html?cp=4_1_0_3_2
* NVMC Non-volatile memory controller
* https://infocenter.nordicsemi.com/topic/ps_nrf52833/nvmc.html?cp=5_1_0_3_2
*
* Notes for the NVMC:
* * The CPU is not stalled while doing flash operations, "while executing from flash"
* as we do not know what code would have been in flash or RAM, code is just
* assumed only in RAM for simplicity.
*
* * 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)
*
* * The partial erase timer is fully accurate. Meaning, a partial erase always
* takes exactly ERASEPAGEPARTIALCFG ms.
*
* * The instruction cache does not have any kind of model.
* The ICACHECNF, IHIT, IMISS registers are not connected to anything,
* writes to them are ignored, reads read 0 (or whatever was written to them last)
*
* * Access port protection is not modeled. The UICR can always be erased.
*
* * There is no modeling of the power failure protection
*
* Notes for the UICR
* * The PSELRESET[], APPROTECT, NFCPINS, DEBUGCTRL & REGOUT0 registers are ignored
* Their equivalent functionality is not implemented
*
* Possible future improvements:
* * 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
*/
NRF_UICR_Type *NRF_UICR_regs_p;
NRF_NVMC_Type NRF_NVMC_regs = {0};
bs_time_t Timer_NVMC = TIME_NEVER; //Time when the next flash operation will be completed
typedef struct {
uint8_t *storage;
const char *file_path;
const char *type_s;
int fd;
size_t size;
bool erase_at_start;
bool rm_at_exit;
bool in_ram;
} storage_state_t;
static storage_state_t flash_st;
static storage_state_t uicr_st;
static enum flash_op_t {flash_idle = 0, flash_write, flash_erase, flash_erase_partial, flash_erase_uicr, flash_erase_all} flash_op;
static uint32_t erase_address;
static bs_time_t time_under_erase[FLASH_N_PAGES];
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 nvmc_args_t {
char *uicr_file;
bool uicr_erase;
bool uicr_rm;
bool uicr_in_ram;
char *flash_file;
bool flash_erase;
bool flash_rm;
bool flash_in_ram;
} nvmc_args;
static void nvmc_initialize_data_storage();
static void nvmc_clear_storage();
static void nvmc_register_cmd_args();
void nrfhw_nvmc_uicr_pre_init(void){
nvmc_register_cmd_args();
}
/**
* Initialize the NVMC and UICR models
*/
void nrfhw_nvmc_uicr_init(){
memset(&NRF_NVMC_regs, 0x00, sizeof(NRF_NVMC_regs));
NRF_NVMC_regs.READY = 1;
NRF_NVMC_regs.READYNEXT = 1;
NRF_NVMC_regs.ERASEPAGEPARTIALCFG = 0x0000000A;
flash_op = flash_idle;
Timer_NVMC = TIME_NEVER;
nrf_hw_find_next_timer_to_trigger();
flash_st.file_path = nvmc_args.flash_file;
flash_st.erase_at_start = nvmc_args.flash_erase;
flash_st.rm_at_exit = nvmc_args.flash_rm;
flash_st.in_ram = nvmc_args.flash_in_ram;
flash_st.size = FLASH_SIZE;
flash_st.type_s = "flash";
uicr_st.file_path = nvmc_args.uicr_file;
uicr_st.erase_at_start = nvmc_args.uicr_erase;
uicr_st.rm_at_exit = nvmc_args.uicr_rm;
uicr_st.in_ram = nvmc_args.uicr_in_ram;
uicr_st.size = sizeof(NRF_UICR_Type);
// We book more than the actual flash area, to avoid segfaults if
// somebody tries to access the HW control registers
uicr_st.type_s = "UICR";
nvmc_initialize_data_storage(&flash_st);
nvmc_initialize_data_storage(&uicr_st);
NRF_UICR_regs_p = (NRF_UICR_Type *)uicr_st.storage;
}
/**
* Clean up the NVMC and UICR model before program exit
*/
void nrfhw_nvmc_uicr_clean_up(){
nvmc_clear_storage(&flash_st);
nvmc_clear_storage(&uicr_st);
}
/*
* 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;
memset(&flash_st.storage[base_address], 0xFF, FLASH_PAGE_SIZE);
time_under_erase[erase_address/FLASH_PAGE_SIZE] = 0;
}
/*
* 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();
}
}
/*
* 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);
}
/*
* Complete the actual erase of all flash and UICR page
*/
static void nrfhw_nvmc_complete_erase_all(void){
nrfhw_nvmc_complete_erase_uicr();
(void)memset(flash_st.storage, 0xFF, flash_st.size);
}
/**
* Time has come when the programmed flash operation has finished
*/
void nrfhw_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;
}
flash_op = flash_idle;
NRF_NVMC_regs.READY = 1;
NRF_NVMC_regs.READYNEXT = 1;
Timer_NVMC = TIME_NEVER;
nrf_hw_find_next_timer_to_trigger();
}
bs_time_t nrfhw_nvmc_time_to_ready(void) {
if (NRF_NVMC_regs.READY) {
return 0;
} else {
return Timer_NVMC - tm_get_hw_time();
}
}
#define ERASE_ENABLED_CHECK(x) \
if ((NRF_NVMC_regs.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); \
return; \
}
#define BUSY_CHECK(x) \
if (flash_op != flash_idle) { \
bs_trace_warning_line_time("%s: %s while the flash was already" \
" busy (%i), it will be ignored\n", \
__func__, x, 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) { \
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 (time_under_erase[addr/FLASH_PAGE_SIZE] > 0){ \
bs_trace_warning_line_time("%s: %s in partially erased address (%u)\n", \
__func__, type, addr); \
}
#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 = tm_get_hw_time() + flash_t_erasepage;
nrf_hw_find_next_timer_to_trigger();
}
/* Note ERASEPCR1 is an alias to ERASEPAGE (same register) */
void nrfhw_nvmc_regw_sideeffects_ERASEPAGE(){
nrfhw_nvmc_erase_page(NRF_NVMC_regs.ERASEPAGE);
}
void nrf_nvmc_regw_sideeffects_ERASEPCR0(){
nrfhw_nvmc_erase_page(NRF_NVMC_regs.ERASEPCR0);
}
void nrfhw_nvmc_regw_sideeffects_ERASEUICR(){
NRF_NVMC_regs.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 = tm_get_hw_time() + flash_t_erasepage;
nrf_hw_find_next_timer_to_trigger();
}
}
void nrfhw_nvmc_regw_sideeffects_ERASEALL(){
NRF_NVMC_regs.ERASEALL &= 1;
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 = tm_get_hw_time() + flash_t_eraseall;
nrf_hw_find_next_timer_to_trigger();
}
}
void nrfhw_nvmc_regw_sideeffects_ERASEPAGEPARTIAL(){
ERASE_ENABLED_CHECK("ERASEPARTIAL");
BUSY_CHECK("ERASEPARTIAL");
erase_address = NRF_NVMC_regs.ERASEPAGEPARTIAL;
CHECK_ERASE_ADDRESS(erase_address);
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;
time_under_erase[erase_address/FLASH_PAGE_SIZE] += duration;
Timer_NVMC = tm_get_hw_time() + duration;
nrf_hw_find_next_timer_to_trigger();
}
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");
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) {
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);
return;
}
if (address < FLASH_SIZE) {
CHECK_PARTIAL_ERASE(address, "write");
/*
* 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);
}
flash_op = flash_write;
NRF_NVMC_regs.READY = 0;
NRF_NVMC_regs.READYNEXT = 0;
Timer_NVMC = tm_get_hw_time() + flash_t_write;
nrf_hw_find_next_timer_to_trigger();
}
/**
* Read from the flash array with offset <address>
* (Note that the flash array starts at address 0x0 in real HW)
*
* 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_flash_read_word(uint32_t address) {
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;
}
}
/**
* memcpy from the flash/uicr array from <address> <size> bytes.
* (Note that the flash array starts at address 0x0 in real HW)
*
* This is a convenience function in the model.
* It can be used from DMA controller models or the like.
*
* This operation is instantaneous in simulation.
* In real HW it is as "fast" as all those AHB accesses
* 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) {
if (address < FLASH_SIZE)
{
CHECK_ADDRESS_INRANGE(address + size - 1, "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);
}
}
void* nrfhw_nmvc_flash_get_base_address(void){
return (void*)&flash_st.storage;
}
/**
* Before exiting the program, do whatever is
* necessary for a given storage (free'ing heap,
* closing file descriptors, etc)
*/
static void nvmc_clear_storage(storage_state_t *st){
if (st->in_ram == true) {
if (st->storage != NULL) {
free(st->storage);
}
return;
}
if ((st->storage != MAP_FAILED) && (st->storage != NULL)) {
munmap(st->storage, st->size);
}
if (st->fd != -1) {
close(st->fd);
}
if ((st->rm_at_exit == true) && (st->file_path != NULL)) {
/* We try to remove the file but do not error out if we can't */
(void) remove(st->file_path);
}
}
/**
* At boot, do whatever is necessary for a given storage
* (allocate memory, open files etc. )
*/
static void nvmc_initialize_data_storage(storage_state_t *st){
struct stat f_stat;
int rc;
st->fd = -1;
st->storage = NULL;
if (st->in_ram == true) {
st->storage = (uint8_t *)bs_malloc(st->size);
} else {
st->fd = open(st->file_path, O_RDWR | O_CREAT, (mode_t)0600);
if (st->fd == -1) {
bs_trace_error_line("%s: Failed to open %s device file %s: %s\n",
__func__, st->type_s, st->file_path, strerror(errno));
}
rc = fstat(st->fd, &f_stat);
if (rc) {
bs_trace_error_line("%s: Failed to get status of %s device file %s: %s\n",
__func__, st->type_s, st->file_path, strerror(errno));
}
if (ftruncate(st->fd, st->size) == -1) {
bs_trace_error_line("%s: Failed to resize %s device file %s: %s\n",
__func__, st->type_s, st->file_path, strerror(errno));
}
st->storage = mmap(NULL, st->size, PROT_WRITE | PROT_READ, MAP_SHARED, st->fd, 0);
if (st->storage == MAP_FAILED) {
bs_trace_error_line("%s: Failed to mmap %s device file %s: %s\n",
__func__, st->type_s, st->file_path, strerror(errno));
}
}
if ((st->erase_at_start == true) || (st->in_ram == true) || (f_stat.st_size == 0)) {
/* Erase the memory unit by pulling all bits to the configured erase value */
(void)memset(st->storage, 0xFF, st->size);
}
}
static void arg_uicr_file_found(char *argv, int offset){
nvmc_args.uicr_in_ram = false;
}
static void arg_flash_file_found(char *argv, int offset){
nvmc_args.flash_in_ram = false;
}
static void arg_uicr_in_ram_found(char *argv, int offset){
nvmc_args.uicr_in_ram = true;
}
static void arg_flash_in_ram_found(char *argv, int offset){
nvmc_args.flash_in_ram = true;
}
static void nvmc_register_cmd_args(void){
nvmc_args.uicr_in_ram = true;
nvmc_args.flash_in_ram = true;
static bs_args_struct_t args_struct_toadd[] = {
{ .is_switch = true,
.option = "uicr_erase",
.type = 'b',
.dest = (void*)&nvmc_args.uicr_erase,
.descript = "Reset all UICR registers to their erase values (0xFF) at boot"
},
{ .option ="uicr_file",
.name = "path",
.type = 's',
.dest = (void*)&nvmc_args.uicr_file,
.call_when_found = arg_uicr_file_found,
.descript = "Path to binary file where the UICR registers are stored (if set, toggles uicr_in_ram to false)"
},
{ .is_switch = true,
.option ="uicr_rm",
.type = 'b',
.dest = (void*)&nvmc_args.uicr_rm,
.descript = "Remove the UICR file when terminating the execution (default no)"
},
{ .is_switch = true,
.option ="uicr_in_ram",
.type = 'b',
.call_when_found = arg_uicr_in_ram_found,
.descript = "(default) Instead of a file, keep the UICR content in RAM. If this is "
"set uicr_erase, uicr_file & uicr_rm are ignored, and the UICR content is always reset at startup"
},
{ .is_switch = true,
.option ="flash_erase",
.type = 'b',
.dest = (void*)&nvmc_args.flash_erase,
.descript = "Reset the flash whole flash to its erase values (0xFF) at boot"
},
{ .option ="flash_file",
.name = "path",
.type = 's',
.dest = (void*)&nvmc_args.flash_file,
.call_when_found = arg_flash_file_found,
.descript = "Path to binary file where the flash content is stored (if set, toggles flash_in_ram to false)"
},
{ .option ="flash",
.name = "path",
.type = 's',
.dest = (void*)&nvmc_args.flash_file,
.call_when_found = arg_flash_file_found,
.descript = "Alias for flash_file"
},
{ .is_switch = true,
.option = "flash_rm",
.type = 'b',
.dest = (void*)&nvmc_args.flash_rm,
.descript = "Remove the flash file when terminating the execution (default no)"
},
{ .is_switch = true,
.option = "flash_in_ram",
.type = 'b',
.call_when_found = arg_flash_in_ram_found,
.descript = "(default) Instead of a file, keep the flash content in RAM. If this is "
"set flash_erase, flash_file & flash_rm are ignored, and the flash content is always reset at startup"
},
ARG_TABLE_ENDMARKER
};
bs_add_extra_dynargs(args_struct_toadd);
}

View File

@ -7,13 +7,37 @@
#define _NRF_HW_MODEL_NVMC_H
#include "nrfx.h"
#include "NRF_hw_args.h"
#include "bs_types.h"
#ifdef __cplusplus
extern "C"{
#endif
#define FLASH_PAGE_SIZE (4*1024)
#define FLASH_N_PAGES 128
#define FLASH_SIZE (FLASH_N_PAGES*FLASH_PAGE_SIZE)
#define UICR_DATA_SIZE 64 /* Words */
#define UICR_SIZE ( UICR_DATA_SIZE*4 )
extern NRF_NVMC_Type NRF_NVMC_regs;
void nrfhw_nvmc_uicr_pre_init(void);
void nrfhw_nvmc_uicr_init(void);
void nrfhw_nvmc_uicr_clean_up(void);
void nrfhw_nvmc_timer_triggered(void);
void nrfhw_nvmc_regw_sideeffects_ERASEPAGE();
void nrf_nvmc_regw_sideeffects_ERASEPCR0();
void nrfhw_nvmc_regw_sideeffects_ERASEUICR();
void nrfhw_nvmc_regw_sideeffects_ERASEALL();
void nrfhw_nvmc_regw_sideeffects_ERASEPAGEPARTIAL();
void nrfhw_nmvc_write_word(uint32_t address, uint32_t value);
uint32_t nrfhw_nmvc_flash_read_word(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);
#ifdef __cplusplus
}
#endif

View File

@ -3,7 +3,14 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bs_cmd_line.h"
#include "bs_tracing.h"
__attribute__((weak)) unsigned int get_device_nbr(void) {
return 0;
}
__attribute__((weak)) void bs_add_extra_dynargs(bs_args_struct_t *args_struct_toadd){
bs_trace_warning_line("%s: The integrating program is expected to provide this API."
"This weak function should not have been called\n", __func__);
}

View File

@ -6,12 +6,15 @@
#ifndef HW_MODELS_WEAK_STUBS_H
#define HW_MODELS_WEAK_STUBS_H
#include "bs_cmd_line.h"
#ifdef __cplusplus
extern "C" {
#endif
/* The integrating program is expected to provide these. If not weak stubs are provided here */
extern unsigned int get_device_nbr(void);
extern void bs_add_extra_dynargs(bs_args_struct_t *args_struct_toadd);
#ifdef __cplusplus
}

View File

@ -34,7 +34,8 @@ IRQn_Type nrfx_get_irq_number(void const * p_reg){
return TIMER2_IRQn;
} else if (IS_PERIPHERAL_REG(p_reg, RTC, 0)) {
return RTC0_IRQn;
/*12*/
} else if (IS_PERIPHERAL_REG(p_reg, TEMP, )) {
return TEMP_IRQn;
} else if (IS_PERIPHERAL_REG(p_reg, RNG,)) {
return RNG_IRQn;
} else if (IS_PERIPHERAL_REG(p_reg, ECB,)) {
@ -63,7 +64,9 @@ IRQn_Type nrfx_get_irq_number(void const * p_reg){
return TIMER3_IRQn;
} else if (IS_PERIPHERAL_REG(p_reg, TIMER,4)) {
return TIMER4_IRQn;
/*28-30*/
/*28-29*/
} else if (IS_PERIPHERAL_REG(p_reg, NVMC,)) {
return 30;
} else if (IS_PERIPHERAL_REG(p_reg, PPI,)) {
return 0x1F;
/*32-..*/

84
src/nrfx/hal/nrf_nvmc.c Normal file
View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*
* Note that the function prototypes are taken from the NRFx HAL
*/
#include "hal/nrf_nvmc.h"
#include "bs_tracing.h"
#include "bs_utils.h"
#include "NRF_NVMC.h"
#include <nrfx_glue.h>
void nrf_nvmc_page_erase_start(NRF_NVMC_Type * p_reg, uint32_t page_addr)
{
p_reg->ERASEPAGE = page_addr;
nrfhw_nvmc_regw_sideeffects_ERASEPAGE();
}
void nrf_nvmc_uicr_erase_start(NRF_NVMC_Type * p_reg)
{
p_reg->ERASEUICR = 1;
nrfhw_nvmc_regw_sideeffects_ERASEUICR();
}
void nrf_nvmc_erase_all_start(NRF_NVMC_Type * p_reg)
{
p_reg->ERASEALL = 1;
nrfhw_nvmc_regw_sideeffects_ERASEALL();
}
void nrf_nvmc_page_partial_erase_start(NRF_NVMC_Type * p_reg, uint32_t page_addr)
{
p_reg->ERASEPAGEPARTIAL = page_addr;
nrfhw_nvmc_regw_sideeffects_ERASEPAGEPARTIAL();
}
void nrf_nvmc_word_write(uint32_t addr, uint32_t value)
{
nrfhw_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);
}
/*
* The nvmc driver uses the HAL ready checks in quite a few places
* in busy wait loops. Some of the driver maintainers were
* not keen on adding conditionally compiled delays on those
* so we hide the delay here.
* Under the assumption that the ready_check functions
* are used only in such busy wait loops until ready,
* we actually wait more or less time depending on the pending
* time.
*/
static void nrf_nvmc_ready_wait(){
#if defined(NRFX_DELAY_US) && !defined(NO_NVMC_READY_AUTO_DELAY)
bs_time_t wait = nrfhw_nvmc_time_to_ready() + 1;
wait = BS_MAX(BS_MIN(wait,100),1);
NRFX_DELAY_US(wait);
#endif
}
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();
}
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;
}

View File

@ -87,6 +87,9 @@ extern NRF_EGU_Type NRF_EGU_regs[6];
#define NRF_EGU4_BASE (&NRF_EGU_regs[4])
#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)
/*
* Redefine the peripheral pointers