diff --git a/ports/stm32/boards/NUCLEO_WB55/rfcore.py b/ports/stm32/boards/NUCLEO_WB55/rfcore.py new file mode 100644 index 0000000000..e612499a78 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_WB55/rfcore.py @@ -0,0 +1,347 @@ +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2020 Damien P. George +# Copyright (c) 2020 Jim Mussared +# +# 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. + +# This script provides some helpers to allow Python code to access the IPCC +# mechanism in the WB55, and works with the memory layout configured in +# ports/stm32/rfcore.c -- i.e. it expects that rfcore_init() has been run. + +# At this stage this is useful for debugging, but can be extended to support +# FUS/WS firmware updates. +# e.g. +# ../../tools/pyboard.py --device /dev/ttyACM0 boards/NUCLEO_WB55/rfcore.py +# to print out SRAM2A, register state and FUS/WS info. + +from machine import mem8, mem16, mem32 +import time, struct, uctypes +import stm + + +class Flash: + FLASH_KEY1 = 0x45670123 + FLASH_KEY2 = 0xCDEF89AB + + def wait_not_busy(self): + while mem32[stm.FLASH + stm.FLASH_SR] & 1 << 16: + machine.idle() + + def unlock(self): + mem32[stm.FLASH + stm.FLASH_KEYR] = Flash.FLASH_KEY1 + mem32[stm.FLASH + stm.FLASH_KEYR] = Flash.FLASH_KEY2 + + def lock(self): + mem32[stm.FLASH + stm.FLASH_CR] = 1 << 31 # LOCK + + def erase_page(self, page): + print("erase", page) + assert 0 <= page <= 255 # 1MiB range (4k page) + self.wait_not_busy() + cr = page << 3 | 1 << 1 # PNB # PER + mem32[stm.FLASH + stm.FLASH_CR] = cr + mem32[stm.FLASH + stm.FLASH_CR] = cr | 1 << 16 # STRT + self.wait_not_busy() + mem32[stm.FLASH + stm.FLASH_CR] = 0 + + def write(self, addr, buf): + assert len(buf) % 4 == 0 + self.wait_not_busy() + cr = 1 << 0 # PG + mem32[stm.FLASH + stm.FLASH_CR] = cr + buf_addr = uctypes.addressof(buf) + off = 0 + while off < len(buf): + mem32[addr + off] = mem32[buf_addr + off] + off += 4 + if off % 8 == 0: + self.wait_not_busy() + if off % 8: + mem32[addr + off] = 0 + self.wait_not_busy() + mem32[stm.FLASH + stm.FLASH_CR] = 0 + + +def copy_file_to_flash(filename, addr): + flash = Flash() + flash.unlock() + try: + with open(filename, "rb") as f: + buf = bytearray(4096) + while 1: + sz = f.readinto(buf) + if sz == 0: + break + print("write", hex(addr), sz) + flash.erase_page((addr - 0x08000000) // 4096) + print("done e") + flash.write(addr, buf) + print("done") + addr += 4096 + finally: + flash.lock() + + +SRAM2A_BASE = const(0x2003_0000) + +# for vendor OGF +OGF_VENDOR = const(0x3F) +OCF_FUS_GET_STATE = const(0x52) +OCF_FUS_FW_UPGRADE = const(0x54) +OCF_FUS_FW_DELETE = const(0x55) +OCF_FUS_START_WS = const(0x5A) +OCF_BLE_INIT = const(0x66) + + +@micropython.asm_thumb +def asm_sev_wfe(): + data(2, 0xBF40) # sev + data(2, 0xBF20) # wfe + + +TABLE_DEVICE_INFO = const(0) +TABLE_BLE = const(1) +TABLE_SYS = const(3) +TABLE_MEM_MANAGER = const(4) + +CHANNEL_BLE = const(1) +CHANNEL_SYS = const(2) +CHANNEL_TRACES = const(4) +CHANNEL_ACL = const(6) + +INDICATOR_HCI_COMMAND = const(0x01) +INDICATOR_HCI_EVENT = const(0x04) +INDICATOR_FUS_COMMAND = const(0x10) +INDICATOR_FUS_RESPONSE = const(0x11) +INDICATOR_FUS_EVENT = const(0x12) + +MAGIC_FUS_ACTIVE = const(0xA94656B9) + + +def get_ipccdba(): + return mem32[stm.FLASH + stm.FLASH_IPCCBR] & 0x3FFF + + +def get_ipcc_table(table): + return mem32[SRAM2A_BASE + get_ipccdba() + table * 4] + + +def get_ipcc_table_word(table, offset): + return mem32[get_ipcc_table(table) + offset * 4] & 0xFFFFFFFF + + +def get_ipcc_table_byte(table, offset): + return mem8[get_ipcc_table(table) + offset] & 0xFF + + +def sram2a_dump(num_words=64, width=8): + print("SRAM2A @%08x" % SRAM2A_BASE) + for i in range((num_words + width - 1) // width): + print(" %04x " % (i * 4 * width), end="") + for j in range(width): + print(" %08x" % (mem32[SRAM2A_BASE + (i * width + j) * 4] & 0xFFFFFFFF), end="") + print() + + +SYS_CMD_BUF = 0 # next*,prev*,type8,...; 272 bytes +SYS_SYS_QUEUE = 0 # next*,prev* + +MM_BLE_SPARE_EVT_BUF = 0 # next*,prev*; 272 bytes +MM_SYS_SPARE_EVT_BUF = 0 # next*,prev*; 272 bytes +MM_BLE_POOL = 0 # ? +MM_BLE_POOL_SIZE = 0 # ? +MM_FREE_BUF_QUEUE = 0 # next*,prev* +MM_EV_POOL = 0 # ? +MM_EV_POOL_SIZE = 0 # ? + +BLE_CMD_BUF = 0 +BLE_CS_BUF = 0 +BLE_EVT_QUEUE = 0 +BLE_HCI_ACL_DATA_BUF = 0 + + +def ipcc_init(): + global SYS_CMD_BUF, SYS_SYS_QUEUE + SYS_CMD_BUF = get_ipcc_table_word(TABLE_SYS, 0) + SYS_SYS_QUEUE = get_ipcc_table_word(TABLE_SYS, 1) + + global MM_BLE_SPARE_EVT_BUF, MM_SYS_SPARE_EVT_BUF, MM_BLE_POOL, MM_BLE_POOL_SIZE, MM_FREE_BUF_QUEUE, MM_EV_POOL, MM_EV_POOL_SIZE + MM_BLE_SPARE_EVT_BUF = get_ipcc_table_word(TABLE_MEM_MANAGER, 0) + MM_SYS_SPARE_EVT_BUF = get_ipcc_table_word(TABLE_MEM_MANAGER, 1) + MM_BLE_POOL = get_ipcc_table_word(TABLE_MEM_MANAGER, 2) + MM_BLE_POOL_SIZE = get_ipcc_table_word(TABLE_MEM_MANAGER, 3) + MM_FREE_BUF_QUEUE = get_ipcc_table_word(TABLE_MEM_MANAGER, 4) + MM_EV_POOL = get_ipcc_table_word(TABLE_MEM_MANAGER, 5) + MM_EV_POOL_SIZE = get_ipcc_table_word(TABLE_MEM_MANAGER, 6) + + global BLE_CMD_BUF, BLE_CS_BUF, BLE_EVT_QUEUE, BLE_HCI_ACL_DATA_BUF + BLE_CMD_BUF = get_ipcc_table_word(TABLE_BLE, 0) + BLE_CS_BUF = get_ipcc_table_word(TABLE_BLE, 1) + BLE_EVT_QUEUE = get_ipcc_table_word(TABLE_BLE, 2) + BLE_HCI_ACL_DATA_BUF = get_ipcc_table_word(TABLE_BLE, 3) + + print("IPCC initialised") + print("SYS: 0x%08x 0x%08x" % (SYS_CMD_BUF, SYS_SYS_QUEUE)) + print("BLE: 0x%08x 0x%08x 0x%08x" % (BLE_CMD_BUF, BLE_CS_BUF, BLE_EVT_QUEUE)) + + +def tl_list_init(addr): + mem32[addr] = addr # next + mem32[addr + 4] = addr # prev + + +def tl_list_append(head, n): + sram2a_dump(1024) + print("Appending 0x%08x to 0x%08x" % (head, n)) + # item->next = head + mem32[n] = head + # item->prev = head->prev + mem32[n + 4] = mem32[head + 4] + # head->prev->next = item + mem32[mem32[head + 4]] = n + # head->prev = item + mem32[head + 4] = n + + +def tl_list_unlink(n): + # next = item->next + next = mem32[n] + # prev = item->prev + prev = mem32[n + 4] + # prev->next = item->next + mem32[prev] = next + # item->next->prev = prev + mem32[next + 4] = prev + + return next + + +def tl_list_dump(head): + print( + "list(%08x, %08x, %08x):" % (head, mem32[head] & 0xFFFFFFFF, mem32[head + 4] & 0xFFFFFFFF), + end="", + ) + cur = mem32[head] + while cur != head: + print(" %08x" % (cur & 0xFFFFFFFF), end="") + cur = mem32[cur] + print() + + +def fus_active(): + return get_ipcc_table_word(TABLE_DEVICE_INFO, 0) == MAGIC_FUS_ACTIVE + + +def info(): + sfr = mem32[stm.FLASH + stm.FLASH_SFR] + srrvr = mem32[stm.FLASH + stm.FLASH_SRRVR] + + print("IPCCDBA : 0x%08x" % (get_ipccdba() & 0x3FFF)) + print("DDS : %r" % bool(sfr & (1 << 12))) + print("FSD : %r" % bool(sfr & (1 << 8))) + print("SFSA : 0x%08x" % (sfr & 0xFF)) + print("C2OPT : %r" % bool(srrvr & (1 << 31))) + print("NBRSD : %r" % bool(srrvr & (1 << 30))) + print("SNBRSA : 0x%08x" % ((srrvr >> 25) & 0x1F)) + print("BRSD : %r" % bool(srrvr & (1 << 23))) + print("SBRSA : 0x%08x" % ((srrvr >> 18) & 0x1F)) + print("SBRV : 0x%08x" % (srrvr & 0x3FFFF)) + + +def dev_info(): + def dump_version(offset): + x = get_ipcc_table_word(TABLE_DEVICE_INFO, offset) + print( + "0x%08x (%u.%u.%u.%u.%u)" + % (x, x >> 24, x >> 16 & 0xFF, x >> 8 & 0xFF, x >> 4 & 0xF, x & 0xF) + ) + + def dump_memory_size(offset): + x = get_ipcc_table_word(TABLE_DEVICE_INFO, offset) + print( + "0x%08x (SRAM2b=%uk SRAM2a=%uk flash=%uk)" + % (x, x >> 24, x >> 16 & 0xFF, (x & 0xFF) * 4) + ) + + print("Device information table @%08x:" % get_ipcc_table(TABLE_DEVICE_INFO)) + if fus_active(): + # layout when running FUS + print("FUS is active") + print("state : 0x%08x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 0)) + print("last FUS active state : 0x%02x" % get_ipcc_table_byte(TABLE_DEVICE_INFO, 5)) + print("last wireless stack state: 0x%02x" % get_ipcc_table_byte(TABLE_DEVICE_INFO, 6)) + print("cur wireless stack type : 0x%02x" % get_ipcc_table_byte(TABLE_DEVICE_INFO, 7)) + print("safe boot version : ", end="") + dump_version(2) + print("FUS version : ", end="") + dump_version(3) + print("FUS memory size : ", end="") + dump_memory_size(4) + print("wireless stack version : ", end="") + dump_version(5) + print("wireless stack mem size : ", end="") + dump_memory_size(6) + print("wireless FW-BLE info : 0x%08x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 7)) + print("wireless FW-thread info : 0x%08x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 8)) + print( + "UID64 : 0x%08x 0x%08x" + % ( + get_ipcc_table_word(TABLE_DEVICE_INFO, 9), + get_ipcc_table_word(TABLE_DEVICE_INFO, 10), + ) + ) + print("device ID : 0x%04x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 11)) + else: + # layout when running WS + print("WS is active") + print("safe boot version : ", end="") + dump_version(0) + print("FUS version : ", end="") + dump_version(1) + print("FUS memory size : ", end="") + dump_memory_size(2) + print("FUS info : 0x%08x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 3)) + print("wireless stack version : ", end="") + dump_version(4) + print("wireless stack mem size : ", end="") + dump_memory_size(5) + print("wireless stack info : 0x%08x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 7)) + print("wireless reserved : 0x%08x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 7)) + + +def ipcc_state(): + print("IPCC:") + print(" C1CR: 0x%08x" % (mem32[stm.IPCC + stm.IPCC_C1CR] & 0xFFFFFFFF), end="") + print(" C2CR: 0x%08x" % (mem32[stm.IPCC + stm.IPCC_C2CR] & 0xFFFFFFFF)) + print(" C1MR: 0x%08x" % (mem32[stm.IPCC + stm.IPCC_C1MR] & 0xFFFFFFFF), end="") + print(" C2MR: 0x%08x" % (mem32[stm.IPCC + stm.IPCC_C2MR] & 0xFFFFFFFF)) + # these always read 0 + # print(' C1SCR: 0x%08x' % (mem32[stm.IPCC + stm.IPCC_C1SCR] & 0xffffffff), end='') + # print(' C2SCR: 0x%08x' % (mem32[stm.IPCC + stm.IPCC_C2SCR] & 0xffffffff)) + print(" C1TOC2SR: 0x%08x" % (mem32[stm.IPCC + stm.IPCC_C1TOC2SR] & 0xFFFFFFFF), end="") + print(" C2TOC1SR: 0x%08x" % (mem32[stm.IPCC + stm.IPCC_C2TOC1SR] & 0xFFFFFFFF)) + + +sram2a_dump(264) +ipcc_init() +info() +dev_info()