Skip to content

Commit

Permalink
Merge pull request #74 from dbeinder/bluenrg-fixes
Browse files Browse the repository at this point in the history
Add BlueNRG-LP/LPS support
  • Loading branch information
florisla authored Jan 24, 2024
2 parents 82ae4cc + f7b8d61 commit e88f36b
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 46 deletions.
8 changes: 5 additions & 3 deletions stm32loader/bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import time
from functools import reduce

from stm32loader.devices import DEVICES, DeviceFlag
from stm32loader.devices import DEVICES, DeviceFamily, DeviceFlag


CHIP_IDS = {
Expand Down Expand Up @@ -72,8 +72,6 @@
# and byte 2 (mask set).
# Requires parity None.
0x000003: "BlueNRG-1 160kB",
0x00000F: "BlueNRG-1 256kB",
0x000023: "BlueNRG-2 160kB",
0x00002F: "BlueNRG-2 256kB",
# STM32F0 RM0091 Table 136. DEV_ID and REV_ID field values
0x440: "STM32F030x8",
Expand Down Expand Up @@ -554,6 +552,10 @@ def get_uid(self):
def detect_device(self):
product_id = self.get_id()

# BlueNRG devices have the silicon cut version in the upper bytes of the PID
if self.device_family == DeviceFamily.NRG.value:
product_id &= 0xFF

# Look up device details based on ID *without* bootloader ID.
self.device = DEVICES.get((product_id, None))

Expand Down
34 changes: 20 additions & 14 deletions stm32loader/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ class DeviceFamily(enum.Enum):
W = "W"

# Non-STM devices.
NRG1 = "NRG1"
NRG2 = "NRG2"
NRG = "NRG"
WIZ = "WIZ"


Expand All @@ -41,7 +40,8 @@ class DeviceFlag(enum.IntEnum):
# bytes for UID and flash size directly.
# Reading a whole chunk of 256 bytes at 0x1FFFA700 does work and
# requires some data extraction.
LONG_UID_ACCESS = 8
LONG_UID_ACCESS = 8,
FORCE_PARITY_NONE = 16


class DeviceFamilyInfo:
Expand Down Expand Up @@ -101,11 +101,17 @@ def __init__(
DeviceFamily.WL: DeviceFamilyInfo("WL", uid_address=0x_1FFF_7590, flash_size_address=0x_1FFF_75E0),
DeviceFamily.U5: DeviceFamilyInfo("U5", ),
DeviceFamily.W: DeviceFamilyInfo("W", ),
# ST BlueNRG has DIE_ID register with PRODUCT, but no UID.
# NRG BlueNRG-2 datasheet
# Flash page size: 128 pages of 8 * 64 * 4 bytes
DeviceFamily.NRG1: DeviceFamilyInfo("NRG1", flash_size_address=0x_4010_0014, flash_page_size=2048),
DeviceFamily.NRG2: DeviceFamilyInfo("NRG2", flash_size_address=0x_4010_0014, flash_page_size=2048),
# ST BlueNRG series; see ST AN4872 (BlueNRG-1/2) and AN5471 (BlueNRG-LP/LPS). BlueNRG requires parity 'none'.
# Product ID:
# Byte 1: metal fix (masked out)
# Byte 2: mask set (masked out)
# Byte 3: 0xHL
# H: [0] BlueNRG-1, [2] BlueNRG-2, [3] BlueNRG-LP/LPS
# L: [3] 160kB, [B] 192kB, [F] 256kB
# There is no access to peripherals/system memory from bootloader, so flash size and UID can not be read
# NRG-1/2: flash_size_address=0x_4010_0014, uid_address=0x_1000_07F4
# NRG-LP: flash_size_address=0x_4000_1014, uid_address=0x_1000_1EF0
DeviceFamily.NRG: DeviceFamilyInfo("NRG", flags=DeviceFlag.FORCE_PARITY_NONE, flash_page_size=2048),
DeviceFamily.WIZ: DeviceFamilyInfo("WIZ", ),
}

Expand Down Expand Up @@ -357,12 +363,12 @@ class BootloaderSerialPeripherals(enum.Enum):
DeviceInfo("W", "STM32W", variant="128kB", pid=0x9A8, bid=None, ram=(0x_2000_0200, 0x_2000_2000), system=(0x_0804_0000, 0x_0804_0800), flash=(0x_0800_0000, 0x_0802_0000, 1 * kB, 4), option=(0x_0804_0800, 0x_0804_080F)),
DeviceInfo("W", "STM32W", variant="256kB", pid=0x9B0, bid=None, ram=(0x_2000_0200, 0x_2000_4000), system=(0x_0804_0000, 0x_0804_0800), flash=(0x_0800_0000, 0x_0804_0000, 2 * kB, 4), option=(0x_0804_0800, 0x_0804_080F)),

# ST BlueNRG; FIXME: ram/system/flash config?
# FIXME bootloader ID address?
DeviceInfo("NRG1", "BlueNRG-1", variant="160kB", pid=0x03, bid=None, ram=None, system=None),
DeviceInfo("NRG1", "BlueNRG-1", variant="256kB", pid=0x0F, bid=None, ram=None, system=None),
DeviceInfo("NRG2", "BlueNRG-1", variant="160kB", pid=0x23, bid=None, ram=None, system=None),
DeviceInfo("NRG2", "BlueNRG-1", variant="256kB", pid=0x2F, bid=None, ram=None, system=None),
# ST BlueNRG
DeviceInfo("NRG", "BlueNRG-1", pid=0x03, bid=None, ram=(0x_2000_0000, 0x_2000_6000), system=(0x_1000_0000, 0x_1000_0800), flash=(0x_1004_0000, 0x_1006_8000, 2 * kB)),
DeviceInfo("NRG", "BlueNRG-2", pid=0x2F, bid=None, ram=(0x_2000_0000, 0x_2000_6000), system=(0x_1000_0000, 0x_1000_0800), flash=(0x_1004_0000, 0x_1008_0000, 2 * kB)),
# There is a 32kB RAM variant of BlueNRG-LP, but that can't be determined from bootloader
DeviceInfo("NRG", "BlueNRG-LP", pid=0x3F, bid=None, ram=(0x_2000_0000, 0x_2001_0000), system=(0x_1000_0000, 0x_1000_1800), flash=(0x_1004_0000, 0x_1008_0000, 2 * kB)),
DeviceInfo("NRG", "BlueNRG-LPS", pid=0x3B, bid=None, ram=(0x_2000_0000, 0x_2000_6000), system=(0x_1000_0000, 0x_1000_1800), flash=(0x_1004_0000, 0x_1007_0000, 2 * kB)),

# Wiznet W7500
DeviceInfo("WIZ", "Wiznet W7500", 0x801, bid=None, ram=None, system=None),
Expand Down
51 changes: 22 additions & 29 deletions stm32loader/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import sys
from types import SimpleNamespace
from pathlib import Path
import serial

try:
from progress.bar import ChargingBar as progress_bar
Expand All @@ -33,15 +34,15 @@
from stm32loader import args
from stm32loader import hexfile
from stm32loader import bootloader
from stm32loader.devices import DeviceFlag
from stm32loader.devices import DEVICE_FAMILIES, DeviceFlag, DeviceFamily
from stm32loader.uart import SerialConnection


class Stm32Loader:
"""Main application: parse arguments and handle commands."""

# serial link bit parity, compatible to pyserial serial.PARTIY_EVEN
PARITY = {"even": "E", "none": "N"}
PARITY = {"even": serial.PARITY_EVEN, "none": serial.PARITY_NONE}

def __init__(self):
"""Construct Stm32Loader object with default settings."""
Expand All @@ -60,6 +61,12 @@ def parse_arguments(self, arguments):
# parse successful, process options further
self.configuration.parity = Stm32Loader.PARITY[self.configuration.parity.lower()]

if self.configuration.family:
family = DeviceFamily[self.configuration.family]
family_flags = DEVICE_FAMILIES[family].family_default_flags
if family_flags & DeviceFlag.FORCE_PARITY_NONE:
self.configuration.parity = serial.PARITY_NONE

def connect(self):
"""Connect to the bootloader UART over an RS-232 serial port."""
serial_connection = SerialConnection(
Expand Down Expand Up @@ -140,12 +147,14 @@ def perform_commands(self):
try:
if self.configuration.length is None:
# Erase full device.
self.debug(0, "Performing full erase...")
self.stm32.erase_memory(pages=None)
else:
# Erase from address to address + length.
start_address = self.configuration.address
end_address = self.configuration.address + self.configuration.length
pages = self.stm32.pages_from_range(start_address, end_address)
self.debug(0, f"Performing partial erase (0x{start_address:X} - 0x{end_address:X}, {len(pages)} pages)... ")
self.stm32.erase_memory(pages)

except bootloader.CommandError:
Expand Down Expand Up @@ -184,43 +193,25 @@ def detect_device(self):
boot_version = self.stm32.get()
self.debug(0, "Bootloader version: 0x%X" % boot_version)
self.stm32.detect_device()
self.debug(5, 'Bootloader ID: 0x%02X' % self.stm32.device.bootloader_id)
if self.stm32.device.bootloader_id is not None:
self.debug(5, f"Bootloader ID: 0x{self.stm32.device.bootloader_id:02X}")
self.debug(0, f"Chip ID: 0x{self.stm32.device.product_id:03X}")
self.debug(0, f"Chip model: {self.stm32.device.device_name}")

def read_device_id(self):
"""Show product ID and bootloader version."""
boot_version = self.stm32.get()
self.debug(0, "Bootloader version: 0x%X" % boot_version)
device_id = self.stm32.get_id()

if self.configuration.family == "NRG":
# ST AN4872.
# Three bytes encode metal fix, mask set,
# BlueNRG-series + flash size.
metal_fix = (device_id & 0xFF0000) >> 16
mask_set = (device_id & 0x00FF00) >> 8
device_id = device_id & 0x0000FF
self.debug(0, "Metal fix: 0x%X" % metal_fix)
self.debug(0, "Mask set: 0x%X" % mask_set)

self.debug(
0, "Chip id: 0x%X (%s)" % (device_id, bootloader.CHIP_IDS.get(device_id, "Unknown"))
)

def read_device_uid(self):
"""Show chip UID."""
try:
device_uid = self.stm32.get_uid()
except bootloader.CommandError as e:
self.debug(
0,
"Something was wrong with reading chip family data: " + str(e),
"Something was wrong with reading chip UID: " + str(e),
)
return

device_uid_string = self.stm32.format_uid(device_uid)
self.debug(0, "Device UID: %s" % device_uid_string)
if device_uid != bootloader.Stm32Bootloader.UID_NOT_SUPPORTED:
device_uid_string = self.stm32.format_uid(device_uid)
self.debug(0, "Device UID: %s" % device_uid_string)

def read_flash_size(self):
"""Show chip flash size."""
Expand All @@ -229,11 +220,12 @@ def read_flash_size(self):
except bootloader.CommandError as e:
self.debug(
0,
"Something was wrong with reading chip family data: " + str(e),
"Something was wrong with reading chip flash size: " + str(e),
)
return

self.debug(0, "Flash size: %d KiB" % flash_size)
if flash_size != bootloader.Stm32Bootloader.FLASH_SIZE_UNKNOWN:
self.debug(0, f"Flash size: {flash_size} kiB")

@staticmethod
def _get_progress_bar(no_progress=False):
Expand All @@ -254,8 +246,9 @@ def main(*arguments, **kwargs):
loader.parse_arguments(arguments)
loader.connect()
try:
loader.read_device_id()
loader.detect_device()
loader.read_device_uid()
loader.read_flash_size()
loader.perform_commands()
finally:
loader.reset()
Expand Down

0 comments on commit e88f36b

Please sign in to comment.