|
| 1 | +# This file is part of the MicroPython project, http://micropython.org/ |
| 2 | +# |
| 3 | +# The MIT License (MIT) |
| 4 | +# |
| 5 | +# Copyright (c) 2020 Damien P. George |
| 6 | +# Copyright (c) 2020 Jim Mussared |
| 7 | +# |
| 8 | +# Permission is hereby granted, free of charge, to any person obtaining a copy |
| 9 | +# of this software and associated documentation files (the "Software"), to deal |
| 10 | +# in the Software without restriction, including without limitation the rights |
| 11 | +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 12 | +# copies of the Software, and to permit persons to whom the Software is |
| 13 | +# furnished to do so, subject to the following conditions: |
| 14 | +# |
| 15 | +# The above copyright notice and this permission notice shall be included in |
| 16 | +# all copies or substantial portions of the Software. |
| 17 | +# |
| 18 | +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 19 | +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 20 | +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 21 | +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 22 | +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 23 | +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 24 | +# THE SOFTWARE. |
| 25 | + |
| 26 | +# This script provides some helpers to allow Python code to access the IPCC |
| 27 | +# mechanism in the WB55, and works with the memory layout configured in |
| 28 | +# ports/stm32/rfcore.c -- i.e. it expects that rfcore_init() has been run. |
| 29 | + |
| 30 | +# At this stage this is useful for debugging, but can be extended to support |
| 31 | +# FUS/WS firmware updates. |
| 32 | +# e.g. |
| 33 | +# ../tools/pyboard.py --device /dev/ttyACM0 boards/NUCLEO_WB55/rfcore.py |
| 34 | +# to print out SRAM2A, register state and FUS/WS info. |
| 35 | + |
| 36 | +from machine import mem8, mem16, mem32 |
| 37 | +import time, struct, uctypes |
| 38 | +import stm |
| 39 | + |
| 40 | +class Flash: |
| 41 | + FLASH_KEY1 = 0x45670123 |
| 42 | + FLASH_KEY2 = 0xcdef89ab |
| 43 | + |
| 44 | + def wait_not_busy(self): |
| 45 | + while mem32[stm.FLASH + stm.FLASH_SR] & 1 << 16: |
| 46 | + machine.idle() |
| 47 | + |
| 48 | + def unlock(self): |
| 49 | + mem32[stm.FLASH + stm.FLASH_KEYR] = Flash.FLASH_KEY1 |
| 50 | + mem32[stm.FLASH + stm.FLASH_KEYR] = Flash.FLASH_KEY2 |
| 51 | + |
| 52 | + def lock(self): |
| 53 | + mem32[stm.FLASH + stm.FLASH_CR] = 1 << 31 # LOCK |
| 54 | + |
| 55 | + def erase_page(self, page): |
| 56 | + print('erase', page) |
| 57 | + assert 0 <= page <= 255 # 1MiB range (4k page) |
| 58 | + self.wait_not_busy() |
| 59 | + cr = ( |
| 60 | + page << 3 # PNB |
| 61 | + | 1 << 1 # PER |
| 62 | + ) |
| 63 | + mem32[stm.FLASH + stm.FLASH_CR] = cr |
| 64 | + mem32[stm.FLASH + stm.FLASH_CR] = cr | 1 << 16 # STRT |
| 65 | + self.wait_not_busy() |
| 66 | + mem32[stm.FLASH + stm.FLASH_CR] = 0 |
| 67 | + |
| 68 | + def write(self, addr, buf): |
| 69 | + assert len(buf) % 4 == 0 |
| 70 | + self.wait_not_busy() |
| 71 | + cr = ( |
| 72 | + 1 << 0 # PG |
| 73 | + ) |
| 74 | + mem32[stm.FLASH + stm.FLASH_CR] = cr |
| 75 | + buf_addr = uctypes.addressof(buf) |
| 76 | + off = 0 |
| 77 | + while off < len(buf): |
| 78 | + mem32[addr + off] = mem32[buf_addr + off] |
| 79 | + off += 4 |
| 80 | + if off % 8 == 0: |
| 81 | + self.wait_not_busy() |
| 82 | + if off % 8: |
| 83 | + mem32[addr + off] = 0 |
| 84 | + self.wait_not_busy() |
| 85 | + mem32[stm.FLASH + stm.FLASH_CR] = 0 |
| 86 | + |
| 87 | +def copy_file_to_flash(filename, addr): |
| 88 | + flash = Flash() |
| 89 | + flash.unlock() |
| 90 | + try: |
| 91 | + with open(filename, 'rb') as f: |
| 92 | + buf = bytearray(4096) |
| 93 | + while 1: |
| 94 | + sz = f.readinto(buf) |
| 95 | + if sz == 0: |
| 96 | + break |
| 97 | + print('write', hex(addr), sz) |
| 98 | + flash.erase_page((addr - 0x08000000) // 4096) |
| 99 | + print('done e') |
| 100 | + flash.write(addr, buf) |
| 101 | + print('done') |
| 102 | + addr += 4096 |
| 103 | + finally: |
| 104 | + flash.lock() |
| 105 | + |
| 106 | +SRAM2A_BASE = const(0x2003_0000) |
| 107 | + |
| 108 | +# for vendor OGF |
| 109 | +OGF_VENDOR = const(0x3f) |
| 110 | +OCF_FUS_GET_STATE = const(0x52) |
| 111 | +OCF_FUS_FW_UPGRADE = const(0x54) |
| 112 | +OCF_FUS_FW_DELETE = const(0x55) |
| 113 | +OCF_FUS_START_WS = const(0x5a) |
| 114 | +OCF_BLE_INIT = const(0x66) |
| 115 | + |
| 116 | +@micropython.asm_thumb |
| 117 | +def asm_sev_wfe(): |
| 118 | + data(2, 0xbf40) # sev |
| 119 | + data(2, 0xbf20) # wfe |
| 120 | + |
| 121 | +TABLE_DEVICE_INFO = const(0) |
| 122 | +TABLE_BLE = const(1) |
| 123 | +TABLE_SYS = const(3) |
| 124 | +TABLE_MEM_MANAGER = const(4) |
| 125 | + |
| 126 | +CHANNEL_BLE = const(1) |
| 127 | +CHANNEL_SYS = const(2) |
| 128 | +CHANNEL_TRACES = const(4) |
| 129 | +CHANNEL_ACL = const(6) |
| 130 | + |
| 131 | +INDICATOR_HCI_COMMAND = const(0x01) |
| 132 | +INDICATOR_HCI_EVENT = const(0x04) |
| 133 | +INDICATOR_FUS_COMMAND = const(0x10) |
| 134 | +INDICATOR_FUS_RESPONSE = const(0x11) |
| 135 | +INDICATOR_FUS_EVENT = const(0x12) |
| 136 | + |
| 137 | +MAGIC_FUS_ACTIVE = const(0xa94656b9) |
| 138 | + |
| 139 | +def get_ipccdba(): |
| 140 | + return mem32[stm.FLASH + stm.FLASH_IPCCBR] & 0x3fff |
| 141 | + |
| 142 | +def get_ipcc_table(table): |
| 143 | + return mem32[SRAM2A_BASE + get_ipccdba() + table * 4] |
| 144 | + |
| 145 | +def get_ipcc_table_word(table, offset): |
| 146 | + return mem32[get_ipcc_table(table) + offset * 4] & 0xffffffff |
| 147 | + |
| 148 | +def get_ipcc_table_byte(table, offset): |
| 149 | + return mem8[get_ipcc_table(table) + offset] & 0xff |
| 150 | + |
| 151 | +def sram2a_dump(num_words=64, width=8): |
| 152 | + print('SRAM2A @%08x' % SRAM2A_BASE) |
| 153 | + for i in range((num_words + width - 1) // width): |
| 154 | + print(' %04x ' % (i * 4 * width), end='') |
| 155 | + for j in range(width): |
| 156 | + print(' %08x' % (mem32[SRAM2A_BASE + (i * width + j) * 4] & 0xffffffff), end='') |
| 157 | + print() |
| 158 | + |
| 159 | +SYS_CMD_BUF = 0 # next*,prev*,type8,...; 272 bytes |
| 160 | +SYS_SYS_QUEUE = 0 # next*,prev* |
| 161 | + |
| 162 | +MM_BLE_SPARE_EVT_BUF = 0 # next*,prev*; 272 bytes |
| 163 | +MM_SYS_SPARE_EVT_BUF = 0 # next*,prev*; 272 bytes |
| 164 | +MM_BLE_POOL = 0 # ? |
| 165 | +MM_BLE_POOL_SIZE = 0 # ? |
| 166 | +MM_FREE_BUF_QUEUE = 0 # next*,prev* |
| 167 | +MM_EV_POOL = 0 # ? |
| 168 | +MM_EV_POOL_SIZE = 0 # ? |
| 169 | + |
| 170 | +BLE_CMD_BUF = 0 |
| 171 | +BLE_CS_BUF = 0 |
| 172 | +BLE_EVT_QUEUE = 0 |
| 173 | +BLE_HCI_ACL_DATA_BUF = 0 |
| 174 | + |
| 175 | +def ipcc_init(): |
| 176 | + global SYS_CMD_BUF, SYS_SYS_QUEUE |
| 177 | + SYS_CMD_BUF = get_ipcc_table_word(TABLE_SYS, 0) |
| 178 | + SYS_SYS_QUEUE = get_ipcc_table_word(TABLE_SYS, 1) |
| 179 | + |
| 180 | + 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 |
| 181 | + MM_BLE_SPARE_EVT_BUF = get_ipcc_table_word(TABLE_MEM_MANAGER, 0) |
| 182 | + MM_SYS_SPARE_EVT_BUF = get_ipcc_table_word(TABLE_MEM_MANAGER, 1) |
| 183 | + MM_BLE_POOL = get_ipcc_table_word(TABLE_MEM_MANAGER, 2) |
| 184 | + MM_BLE_POOL_SIZE = get_ipcc_table_word(TABLE_MEM_MANAGER, 3) |
| 185 | + MM_FREE_BUF_QUEUE = get_ipcc_table_word(TABLE_MEM_MANAGER, 4) |
| 186 | + MM_EV_POOL = get_ipcc_table_word(TABLE_MEM_MANAGER, 5) |
| 187 | + MM_EV_POOL_SIZE = get_ipcc_table_word(TABLE_MEM_MANAGER, 6) |
| 188 | + |
| 189 | + global BLE_CMD_BUF, BLE_CS_BUF, BLE_EVT_QUEUE, BLE_HCI_ACL_DATA_BUF |
| 190 | + BLE_CMD_BUF = get_ipcc_table_word(TABLE_BLE, 0) |
| 191 | + BLE_CS_BUF = get_ipcc_table_word(TABLE_BLE, 1) |
| 192 | + BLE_EVT_QUEUE = get_ipcc_table_word(TABLE_BLE, 2) |
| 193 | + BLE_HCI_ACL_DATA_BUF = get_ipcc_table_word(TABLE_BLE, 3) |
| 194 | + |
| 195 | + print('IPCC initialised') |
| 196 | + print('SYS: 0x%08x 0x%08x' % (SYS_CMD_BUF, SYS_SYS_QUEUE)) |
| 197 | + print('BLE: 0x%08x 0x%08x 0x%08x' % (BLE_CMD_BUF, BLE_CS_BUF, BLE_EVT_QUEUE)) |
| 198 | + |
| 199 | + |
| 200 | +def tl_list_init(addr): |
| 201 | + mem32[addr] = addr # next |
| 202 | + mem32[addr + 4] = addr # prev |
| 203 | + |
| 204 | +def tl_list_append(head, n): |
| 205 | + sram2a_dump(1024) |
| 206 | + print('Appending 0x%08x to 0x%08x' % (head, n)) |
| 207 | + # item->next = head |
| 208 | + mem32[n] = head |
| 209 | + # item->prev = head->prev |
| 210 | + mem32[n + 4] = mem32[head + 4] |
| 211 | + # head->prev->next = item |
| 212 | + mem32[mem32[head + 4]] = n |
| 213 | + # head->prev = item |
| 214 | + mem32[head + 4] = n |
| 215 | + |
| 216 | +def tl_list_unlink(n): |
| 217 | + # next = item->next |
| 218 | + next = mem32[n] |
| 219 | + # prev = item->prev |
| 220 | + prev = mem32[n + 4] |
| 221 | + # prev->next = item->next |
| 222 | + mem32[prev] = next |
| 223 | + # item->next->prev = prev |
| 224 | + mem32[next + 4] = prev |
| 225 | + |
| 226 | + return next |
| 227 | + |
| 228 | +def tl_list_dump(head): |
| 229 | + print('list(%08x, %08x, %08x):' % (head, mem32[head] & 0xffffffff, mem32[head + 4] & 0xffffffff), end='') |
| 230 | + cur = mem32[head] |
| 231 | + while cur != head: |
| 232 | + print(' %08x' % (cur & 0xffffffff), end='') |
| 233 | + cur = mem32[cur] |
| 234 | + print() |
| 235 | + |
| 236 | +def fus_active(): |
| 237 | + return get_ipcc_table_word(TABLE_DEVICE_INFO, 0) == MAGIC_FUS_ACTIVE |
| 238 | + |
| 239 | +def info(): |
| 240 | + sfr = mem32[stm.FLASH + stm.FLASH_SFR] |
| 241 | + srrvr = mem32[stm.FLASH + stm.FLASH_SRRVR] |
| 242 | + |
| 243 | + print('IPCCDBA : 0x%08x' % (get_ipccdba() & 0x3fff)) |
| 244 | + print('DDS : %r' % bool(sfr & (1 << 12))) |
| 245 | + print('FSD : %r' % bool(sfr & (1 << 8))) |
| 246 | + print('SFSA : 0x%08x' % (sfr & 0xff)) |
| 247 | + print('C2OPT : %r' % bool(srrvr & (1 << 31))) |
| 248 | + print('NBRSD : %r' % bool(srrvr & (1 << 30))) |
| 249 | + print('SNBRSA : 0x%08x' % ((srrvr >> 25) & 0x1f)) |
| 250 | + print('BRSD : %r' % bool(srrvr & (1 << 23))) |
| 251 | + print('SBRSA : 0x%08x' % ((srrvr >> 18) & 0x1f)) |
| 252 | + print('SBRV : 0x%08x' % (srrvr & 0x3ffff)) |
| 253 | + |
| 254 | +def dev_info(): |
| 255 | + def dump_version(offset): |
| 256 | + x = get_ipcc_table_word(TABLE_DEVICE_INFO, offset) |
| 257 | + print('0x%08x (%u.%u.%u.%u.%u)' % (x, x >> 24, x >> 16 & 0xff, x >> 8 & 0xff, x >> 4 & 0xf, x & 0xf)) |
| 258 | + def dump_memory_size(offset): |
| 259 | + x = get_ipcc_table_word(TABLE_DEVICE_INFO, offset) |
| 260 | + print('0x%08x (SRAM2b=%uk SRAM2a=%uk flash=%uk)' % (x, x >> 24, x >> 16 & 0xff, (x & 0xff) * 4)) |
| 261 | + |
| 262 | + print('Device information table @%08x:' % get_ipcc_table(TABLE_DEVICE_INFO)) |
| 263 | + if fus_active(): |
| 264 | + # layout when running FUS |
| 265 | + print('FUS is active') |
| 266 | + print('state : 0x%08x' % get_ipcc_table_word(TABLE_DEVICE_INFO, 0)) |
| 267 | + print('last FUS active state : 0x%02x' % get_ipcc_table_byte(TABLE_DEVICE_INFO, 5))
| 268 | + print('last wireless stack state: 0x%02x' % get_ipcc_table_byte(TABLE_DEVICE_INFO, 6)) |
| 269 | + print('cur wireless stack type : 0x%02x' % get_ipcc_table_byte(TABLE_DEVICE_INFO, 7)) |
| 270 | + print('safe boot version : ', end=''); dump_version(2) |
| 271 | + print('FUS version : ', end=''); dump_version(3) |
| 272 | + print('FUS memory size : ', end=''); dump_memory_size(4) |
| 273 | + print('wireless stack version : ', end=''); dump_version(5) |
| 274 | + print('wireless stack mem size : ', end=''); dump_memory_size(6) |
| 275 | + print('wireless FW-BLE info : 0x%08x' % get_ipcc_table_word(TABLE_DEVICE_INFO, 7)) |
| 276 | + print('wireless FW-thread info : 0x%08x' % get_ipcc_table_word(TABLE_DEVICE_INFO, 8)) |
| 277 | + print('UID64 : 0x%08x 0x%08x' % (get_ipcc_table_word(TABLE_DEVICE_INFO, 9), get_ipcc_table_word(TABLE_DEVICE_INFO, 10))) |
| 278 | + print('device ID : 0x%04x' % get_ipcc_table_word(TABLE_DEVICE_INFO, 11)) |
| 279 | + else: |
| 280 | + # layout when running WS |
| 281 | + print('WS is active') |
| 282 | + print('safe boot version : ', end=''); dump_version(0) |
| 283 | + print('FUS version : ', end=''); dump_version(1) |
| 284 | + print('FUS memory size : ', end=''); dump_memory_size(2) |
| 285 | + print('FUS info : 0x%08x' % get_ipcc_table_word(TABLE_DEVICE_INFO, 3)) |
| 286 | + print('wireless stack version : ', end=''); dump_version(4) |
| 287 | + print('wireless stack mem size : ', end=''); dump_memory_size(5) |
| 288 | + print('wireless stack info : 0x%08x' % get_ipcc_table_word(TABLE_DEVICE_INFO, 7)) |
| 289 | + print('wireless reserved : 0x%08x' % get_ipcc_table_word(TABLE_DEVICE_INFO, 7)) |
| 290 | + |
| 291 | +def ipcc_state(): |
| 292 | + print('IPCC:') |
| 293 | + print(' C1CR: 0x%08x' % (mem32[stm.IPCC + stm.IPCC_C1CR] & 0xffffffff), end='') |
| 294 | + print(' C2CR: 0x%08x' % (mem32[stm.IPCC + stm.IPCC_C2CR] & 0xffffffff)) |
| 295 | + print(' C1MR: 0x%08x' % (mem32[stm.IPCC + stm.IPCC_C1MR] & 0xffffffff), end='') |
| 296 | + print(' C2MR: 0x%08x' % (mem32[stm.IPCC + stm.IPCC_C2MR] & 0xffffffff)) |
| 297 | + # these always read 0 |
| 298 | + #print(' C1SCR: 0x%08x' % (mem32[stm.IPCC + stm.IPCC_C1SCR] & 0xffffffff), end='') |
| 299 | + #print(' C2SCR: 0x%08x' % (mem32[stm.IPCC + stm.IPCC_C2SCR] & 0xffffffff)) |
| 300 | + print(' C1TOC2SR: 0x%08x' % (mem32[stm.IPCC + stm.IPCC_C1TOC2SR] & 0xffffffff), end='') |
| 301 | + print(' C2TOC1SR: 0x%08x' % (mem32[stm.IPCC + stm.IPCC_C2TOC1SR] & 0xffffffff)) |
| 302 | + |
| 303 | +sram2a_dump(264) |
| 304 | +ipcc_init() |
| 305 | +info() |
| 306 | +dev_info() |
0 commit comments