From cc158f98fe81180869d22b73bc6102c75829fceb Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 11 Nov 2017 22:22:19 +0100 Subject: [PATCH] nrf: Implement NVMC HAL. This is only a library for flash access. Actual file system support will be added later. --- ports/nrf/Makefile | 1 + ports/nrf/drivers/bluetooth/ble_drv.c | 27 +++- ports/nrf/hal/hal_nvmc.c | 209 ++++++++++++++++++++++++++ ports/nrf/hal/hal_nvmc.h | 70 +++++++++ 4 files changed, 302 insertions(+), 5 deletions(-) create mode 100644 ports/nrf/hal/hal_nvmc.c create mode 100644 ports/nrf/hal/hal_nvmc.h diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 4d0ca70b86..f10949f124 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -127,6 +127,7 @@ SRC_HAL = $(addprefix hal/,\ hal_temp.c \ hal_gpio.c \ hal_rng.c \ + hal_nvmc.c \ ) ifeq ($(MCU_VARIANT), nrf52) diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index 2bb6fac2d2..63af900111 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -36,6 +36,8 @@ #include "nrf_sdm.h" #include "ble_gap.h" #include "ble.h" // sd_ble_uuid_encode +#include "hal/hal_nvmc.h" +#include "mphalport.h" #define BLE_DRIVER_VERBOSE 0 @@ -858,6 +860,22 @@ void ble_drv_discover_descriptors(void) { #endif +static void sd_evt_handler(uint32_t evt_id) { + switch (evt_id) { +#ifdef HAL_NVMC_MODULE_ENABLED + case NRF_EVT_FLASH_OPERATION_SUCCESS: + hal_nvmc_operation_finished(HAL_NVMC_SUCCESS); + break; + case NRF_EVT_FLASH_OPERATION_ERROR: + hal_nvmc_operation_finished(HAL_NVMC_ERROR); + break; +#endif + default: + // unhandled event! + break; + } +} + static void ble_evt_handler(ble_evt_t * p_ble_evt) { // S132 event ranges. // Common 0x01 -> 0x0F @@ -1049,12 +1067,11 @@ void SWI2_EGU2_IRQHandler(void) { #endif uint32_t evt_id; - uint32_t err_code; - do { - err_code = sd_evt_get(&evt_id); - // TODO: handle non ble events - } while (err_code != NRF_ERROR_NOT_FOUND && err_code != NRF_SUCCESS); + while (sd_evt_get(&evt_id) != NRF_ERROR_NOT_FOUND) { + sd_evt_handler(evt_id); + } + uint32_t err_code; uint16_t evt_len = sizeof(m_ble_evt_buf); do { err_code = sd_ble_evt_get(m_ble_evt_buf, &evt_len); diff --git a/ports/nrf/hal/hal_nvmc.c b/ports/nrf/hal/hal_nvmc.c new file mode 100644 index 0000000000..c5ac1697d4 --- /dev/null +++ b/ports/nrf/hal/hal_nvmc.c @@ -0,0 +1,209 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Ayke van Laethem + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "mphalport.h" +#include "hal_nvmc.h" + +#if BLUETOOTH_SD +#include "ble_drv.h" +#include "nrf_soc.h" +#endif + +#ifdef HAL_NVMC_MODULE_ENABLED + +#if BLUETOOTH_SD + +// Rotates bits in `value` left `shift` times. +STATIC inline uint32_t rotate_left(uint32_t value, uint32_t shift) { + return (value << shift) | (value >> (32 - shift)); +} + +STATIC volatile uint8_t hal_nvmc_operation_state = HAL_NVMC_BUSY; + +STATIC void operation_init() { + hal_nvmc_operation_state = HAL_NVMC_BUSY; +} + +void hal_nvmc_operation_finished(uint8_t result) { + hal_nvmc_operation_state = result; +} + +STATIC bool operation_wait(uint32_t result) { + if (ble_drv_stack_enabled() != 1) { + // SoftDevice is not enabled, no event will be generated. + return result == NRF_SUCCESS; + } + + if (result != NRF_SUCCESS) { + // In all other (non-success) cases, the command hasn't been + // started and no event will be generated. + return false; + } + + // Wait until the event has been generated. + while (hal_nvmc_operation_state == HAL_NVMC_BUSY) { + __WFE(); + } + + // Now we can safely continue, flash operation has completed. + return hal_nvmc_operation_state == HAL_NVMC_SUCCESS; +} + +bool hal_nvmc_erase_page(uint32_t pageaddr) { + operation_init(); + uint32_t result = sd_flash_page_erase(pageaddr / HAL_NVMC_PAGESIZE); + return operation_wait(result); +} + +bool hal_nvmc_write_words(uint32_t *dest, const uint32_t *buf, size_t len) { + operation_init(); + uint32_t result = sd_flash_write(dest, buf, len); + return operation_wait(result); +} + +bool hal_nvmc_write_byte(byte *dest_in, byte b) { + uint32_t dest = (uint32_t)dest_in; + uint32_t dest_aligned = dest & ~3; + + // Value to write - leave all bits that should not change at 0xff. + uint32_t value = 0xffffff00 | b; + + // Rotate bits in value to an aligned position. + value = rotate_left(value, (dest & 3) * 8); + + operation_init(); + uint32_t result = sd_flash_write((uint32_t*)dest_aligned, &value, 1); + return operation_wait(result); +} + +#else // BLUETOOTH_SD + +bool hal_nvmc_erase_page(uint32_t pageaddr) { + // Configure NVMC to erase a page. + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {} + + // Set the page to erase + NRF_NVMC->ERASEPAGE = pageaddr; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {} + + // Switch back to read-only. + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {} + + // Operation succeeded. + return true; +} + +bool hal_nvmc_write_words(uint32_t *dest, const uint32_t *buf, size_t len) { + // Note that we're writing 32-bit integers, not bytes. Thus the 'real' + // length of the buffer is len*4. + + // Configure NVMC so that writes are allowed (anywhere). + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {} + + // Write all integers to flash. + for (int i = 0; i < len; i++) { + dest[i] = buf[i]; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {} + } + + // Switch back to read-only. + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {} + + // Operation succeeded. + return true; +} + +bool hal_nvmc_write_byte(byte *dest_in, byte b) { + // This code can probably be optimized. + + // Configure NVMC so that writes are allowed (anywhere). + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {} + + // According to the nRF51 RM (chapter 6), only word writes to + // word-aligned addresses are allowed. + // https://www.nordicsemi.com/eng/nordic/Products/nRF51822/nRF51-RM/62725 + uint32_t dest = (uint32_t)dest_in; + uint32_t dest_aligned = dest & ~3; + + // Value to write - leave all bits that should not change at 0xff. + uint32_t value = 0xffffff00 | b; + + // Rotate bits in value to an aligned position. + value = rotate_left(value, 24 - (dest - dest_aligned) * 8); + + // Put the value at the right place. + *(uint32_t*)dest_aligned = value; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {} + + // Switch back to read-only. + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {} + + // Operation succeeded. + return true; +} + +#endif // BLUETOOTH_SD + +bool hal_nvmc_write_buffer(void *dest_in, const void *buf_in, size_t len) { + byte *dest = dest_in; + const byte *buf = buf_in; + + // Write first bytes to align the buffer. + while (len && ((uint32_t)dest & 0b11)) { + hal_nvmc_write_byte(dest, *buf); + dest++; + buf++; + len--; + } + + // Now the start of the buffer is aligned. Write as many words as + // possible, as that's much faster than writing bytes. + if (len / 4 && ((uint32_t)buf & 0b11) == 0) { + hal_nvmc_write_words((uint32_t*)dest, (const uint32_t*)buf, len / 4); + dest += len & ~0b11; + buf += len & ~0b11; + len = len & 0b11; + } + + // Write remaining unaligned bytes. + while (len) { + hal_nvmc_write_byte(dest, *buf); + dest++; + buf++; + len--; + } + + return true; +} + +#endif // HAL_NVMC_MODULE_ENABLED diff --git a/ports/nrf/hal/hal_nvmc.h b/ports/nrf/hal/hal_nvmc.h new file mode 100644 index 0000000000..a306627469 --- /dev/null +++ b/ports/nrf/hal/hal_nvmc.h @@ -0,0 +1,70 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Ayke van Laethem + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HAL_NVMC_H__ +#define HAL_NVMC_H__ + +#include + +#include "nrf.h" + +// Erase a single page. The pageaddr is an address within the first page. +bool hal_nvmc_erase_page(uint32_t pageaddr); + +// Write an array of 32-bit words to flash. The len parameter is the +// number of words, not the number of bytes. Dest and buf must be aligned. +bool hal_nvmc_write_words(uint32_t *dest, const uint32_t *buf, size_t len); + +// Write a byte to flash. May have any alignment. +bool hal_nvmc_write_byte(byte *dest, byte b); + +// Write an (unaligned) byte buffer to flash. +bool hal_nvmc_write_buffer(void *dest_in, const void *buf_in, size_t len); + +// Call for ble_drv.c: notify (from an interrupt) that the current flash +// operation has finished. +void hal_nvmc_operation_finished(uint8_t result); + +enum { + HAL_NVMC_BUSY, + HAL_NVMC_SUCCESS, + HAL_NVMC_ERROR, +}; + +#if defined(NRF51) +#define HAL_NVMC_PAGESIZE (1024) + +#elif defined(NRF52) +#define HAL_NVMC_PAGESIZE (4096) +#error NRF52 not yet implemented + +#else +#error Unknown chip +#endif + +#define HAL_NVMC_IS_PAGE_ALIGNED(addr) ((uint32_t)(addr) & (HAL_NVMC_PAGESIZE - 1)) + +#endif // HAL_NVMC_H__