From a9ded228b75f4b92a285d4d9a455ba93cd95a555 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Tue, 6 Aug 2024 15:05:57 -0700 Subject: [PATCH] target: Add RP2350 support --- pyocd/target/builtin/__init__.py | 9 +- pyocd/target/family/__init__.py | 1 + .../target_RP2040.py => family/target_rp2.py} | 162 ++++++++++++------ test/data/binaries/rp2040.bin | Bin 0 -> 8472 bytes test/data/binaries/rp2350.bin | Bin 0 -> 5960 bytes 5 files changed, 116 insertions(+), 56 deletions(-) rename pyocd/target/{builtin/target_RP2040.py => family/target_rp2.py} (56%) create mode 100755 test/data/binaries/rp2040.bin create mode 100755 test/data/binaries/rp2350.bin diff --git a/pyocd/target/builtin/__init__.py b/pyocd/target/builtin/__init__.py index 7530ccbc7..3b4d28eed 100644 --- a/pyocd/target/builtin/__init__.py +++ b/pyocd/target/builtin/__init__.py @@ -127,7 +127,7 @@ from . import target_MPS2_AN521 from . import target_MPS3_AN522 from . import target_MPS3_AN540 -from . import target_RP2040 +from ..family import target_rp2 from . import target_ytm32b1ld0 from . import target_ytm32b1le0 from . import target_ytm32b1me0 @@ -306,9 +306,10 @@ 'hc32l072' : target_HC32L07x.HC32L072, 'hc32l073' : target_HC32L07x.HC32L073, 'hc32f072' : target_HC32L07x.HC32F072, - 'rp2040' : target_RP2040.RP2040Core0, - 'rp2040_core0' : target_RP2040.RP2040Core0, - 'rp2040_core1' : target_RP2040.RP2040Core1, + 'rp2040' : target_rp2.RP2040Core0, + 'rp2040_core0' : target_rp2.RP2040Core0, + 'rp2040_core1' : target_rp2.RP2040Core1, + 'rp2350' : target_rp2.RP2350, 'ytm32b1ld0': target_ytm32b1ld0.YTM32B1LD0, 'ytm32b1le0': target_ytm32b1le0.YTM32B1LE0, 'ytm32b1me0': target_ytm32b1me0.YTM32B1ME0, diff --git a/pyocd/target/family/__init__.py b/pyocd/target/family/__init__.py index 7ef8bb893..8ed0d9d5e 100644 --- a/pyocd/target/family/__init__.py +++ b/pyocd/target/family/__init__.py @@ -23,6 +23,7 @@ from . import target_lpc5500 from . import target_nRF52 from . import target_nRF91 +from . import target_rp2 class FamilyInfo(NamedTuple): """@brief Container for family matching information.""" diff --git a/pyocd/target/builtin/target_RP2040.py b/pyocd/target/family/target_rp2.py similarity index 56% rename from pyocd/target/builtin/target_RP2040.py rename to pyocd/target/family/target_rp2.py index 8aadceee5..5cee420f8 100644 --- a/pyocd/target/builtin/target_RP2040.py +++ b/pyocd/target/family/target_rp2.py @@ -17,6 +17,7 @@ import logging from ...core import exceptions +from ...core.target import Target from ...coresight.coresight_target import CoreSightTarget from ...core.memory_map import (RomRegion, FlashRegion, RamRegion, MemoryMap) from ...probe.swj import SWJSequenceSender @@ -30,73 +31,62 @@ # Flash algorithm as a hex string 'instructions': [ - 0xe00abe00, - 0xb085b5f0, 0x447e4e1e, 0x28017830, 0xf000d101, 0x2001f839, 0x70309004, 0x46384f15, 0xf00030f7, - 0x4604f8a3, 0x1c804813, 0xf89ef000, 0x46384605, 0xf89af000, 0x48109003, 0xf896f000, 0x480f9002, - 0xf892f000, 0x480b9001, 0xf88ef000, 0x47a04607, 0x607447a8, 0x980360b5, 0x980260f0, 0x98016130, - 0x61b76170, 0x70309804, 0xb0052000, 0x46c0bdf0, 0x00004552, 0x00005843, 0x00005052, 0x00004346, - 0x0000027a, 0x4c07b510, 0x7820447c, 0xd1062801, 0x47806960, 0x478069a0, 0x70202000, 0x2001bd10, - 0x46c0bd10, 0x000001f8, 0x44784805, 0x28007800, 0x2001d101, 0x48014770, 0x46c04770, 0x000070d0, - 0x000001d6, 0x4601b570, 0x447a4a0e, 0x28017810, 0x2301d10e, 0x2400071d, 0x46261b48, 0x42a94166, - 0x68d5d308, 0x041a0319, 0x47a823d8, 0xbd704620, 0xbd702001, 0x44784804, 0x4a042121, 0xf000447a, - 0x46c0f855, 0x000001b6, 0x00000126, 0x00000164, 0xb081b5f0, 0x4d10460b, 0x7829447d, 0xd10f2901, - 0x070e2101, 0x1b812400, 0x41674627, 0xd30b42b0, 0x4608692d, 0x461a4611, 0x462047a8, 0xbdf0b001, - 0x46202401, 0xbdf0b001, 0x44784804, 0x4a042121, 0xf000447a, 0x46c0f82b, 0x00000168, 0x000000d2, - 0x00000120, 0xd4d4de00, 0x2114b280, 0x1e898809, 0x2a00884a, 0x1d09d004, 0xd1f94282, 0x47708808, - 0x44784803, 0x4a03210e, 0xf000447a, 0x46c0f80f, 0x00000076, 0x000000c8, 0xd4d44770, 0x49024801, - 0x46c04770, 0x4d94efcf, 0x7847d224, 0xaf00b580, 0x2300b088, 0x4c079305, 0x93039404, 0x23019302, - 0xab069301, 0x91079300, 0x46689006, 0xf0004611, 0xdefef803, 0x00000244, 0xaf00b580, 0x9103b084, - 0x48049002, 0x48049001, 0x46689000, 0xffbaf7ff, 0x46c0defe, 0x00000244, 0x00000244, 0x636e7546, - 0x746f6e20, 0x756f6620, 0x7273646e, 0x616d2f63, 0x722e6e69, 0xd4d4d473, 0xd4d4d4d4, 0xd4d4d4d4, - 0x65747461, 0x2074706d, 0x73206f74, 0x72746275, 0x20746361, 0x68746977, 0x65766f20, 0x6f6c6672, - 0xd4d4d477, 0x00000199, 0x00000000, 0x00000001, 0x0000019d, 0x0000020a, 0x0000000b, 0x00000014, - 0x00000011, 0x0000020a, 0x0000000b, 0x00000053, 0x00000028, 0x0000020a, 0x0000000b, 0x00000058, - 0x0000002a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 + 0xe7fdbe00, + 0xaf03b5f0, 0x4614b087, 0x447d4d5a, 0x28007828, 0x485ed007, 0x68004478, 0x485d4780, 0x68004478, + 0x26014780, 0x1e60702e, 0xd3002803, 0x4848e08d, 0xf90ef000, 0x2800460c, 0x4846d15f, 0xf0001c80, + 0x2800f907, 0x460cd001, 0x9406e057, 0x78102210, 0x284d4c41, 0x2311d151, 0x28757818, 0x2012d14d, + 0x78009002, 0xd00a2802, 0xd1462801, 0x92049303, 0x20149105, 0x21188800, 0x4938880a, 0x9303e008, + 0x91059204, 0xf8d4f000, 0x88022016, 0x21044833, 0x4c334790, 0x46222800, 0x4602d000, 0x99052800, + 0x9b039804, 0x7800d029, 0x284d4c2e, 0x7818d125, 0xd1222875, 0x78009802, 0x91052802, 0xd0079201, + 0xd11a2801, 0x88002014, 0x880a2118, 0xe0054926, 0xf8aef000, 0x88022016, 0x21044823, 0x4c234790, + 0x46222800, 0x4602d000, 0xd0062800, 0x48209204, 0xf8aef000, 0x2800460c, 0x4620d002, 0xbdf0b007, + 0xf0004814, 0x2800f8a5, 0x9103d19d, 0x28009806, 0x9806d019, 0x98054780, 0x48174780, 0x99014478, + 0x48166001, 0x99064478, 0x48156001, 0x99044478, 0x48146001, 0x60044478, 0x44784813, 0x60019903, + 0x2400702e, 0x9c05e7d9, 0xf000e7d7, 0x46c0f8ab, 0x00004649, 0x00005843, 0x10004552, 0x00004552, + 0x20004552, 0x10005052, 0x00005052, 0x20005052, 0x00004346, 0x0000029e, 0x00000194, 0x00000188, + 0x00000188, 0x00000184, 0x00000182, 0x000002a4, 0x000002a0, 0xaf02b5d0, 0x447c4c08, 0x28017820, + 0x4807d10a, 0x68004478, 0x48064780, 0x68004478, 0x20004780, 0xbdd07020, 0xbdd02001, 0x0000010e, + 0x00000114, 0x00000110, 0xaf02b5d0, 0x44794909, 0x29017809, 0x210fd10c, 0x18400709, 0x44794906, + 0x2201680c, 0x04120311, 0x47a023d8, 0xbdd02000, 0xbdd02001, 0x000000da, 0x000000d2, 0xaf02b5d0, + 0x4909460b, 0x78094479, 0xd10a2901, 0x0709210f, 0x49061840, 0x680c4479, 0x461a4611, 0x200047a0, + 0x2001bdd0, 0x46c0bdd0, 0x000000a4, 0x000000a0, 0xf45f4806, 0x60014140, 0xf710ee30, 0xec40d404, + 0xec400780, 0xbf400781, 0x00004770, 0xe000ed88, 0xaf02b5d0, 0x2010b284, 0x284d7800, 0x2011d10f, + 0x28757800, 0x2012d10b, 0x28027800, 0x2801d00b, 0x2014d105, 0x21188800, 0x4621880a, 0x2001e009, + 0x18610701, 0xf7ffbdd0, 0x2016ffd3, 0x21048802, 0x47904620, 0x42404601, 0x29004148, 0x2101d1f2, + 0xe7ee0749, 0xaf00b580, 0xdefede00, 0xd4d4d400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000 ], # Relative function addresses 'pc_init': 0x20000005, - 'pc_unInit': 0x20000089, - 'pc_program_page': 0x20000115, - 'pc_erase_sector': 0x200000c9, - # 'pc_eraseAll': 0x200000ad, # not implemented yet - - 'static_base' : 0x20000000 + 0x00000004 + 0x00000254, - 'begin_stack' : 0x20000500, + 'pc_unInit': 0x20000199, + 'pc_program_page': 0x20000201, + 'pc_erase_sector': 0x200001cd, + 'pc_eraseAll': 0x120000003, + + 'static_base' : 0x20000000 + 0x00000004 + 0x000002c4, + 'begin_stack' : 0x200012d0, + 'end_stack' : 0x200002d0, 'begin_data' : 0x20000000 + 0x1000, 'page_size' : 0x0, 'analyzer_supported' : False, - 'analyzer_address' : 0x00000000, - 'page_buffers' : [0x20001000, 0x20001100], # Enable double buffering + 'page_buffers' : [0x20001400, 0x20001500], # Enable double buffering 'min_program_length' : 256, # Relative region addresses and sizes - 'ro_start': 0x0, - 'ro_size': 0x254, - 'rw_start': 0x254, - 'rw_size': 0x30, - 'zi_start': 0x284, - 'zi_size': 0x1c, + 'ro_start': 0x4, + 'ro_size': 0x2c4, + 'rw_start': 0x2c8, + 'rw_size': 0x0, + 'zi_start': 0x2c8, + 'zi_size': 0x0, } def _parity32(value): parity = sum((value >> i) for i in range(32)) return parity & 1 -class RP2040Base(CoreSightTarget): - """@brief Raspberry Pi RP2040. - - This device is very strange in that it as three DPs. The first two DPs each have a single AHB-AP - for the two Cortex-M0+ cores. The third DP is a "Rescue DP" that has no APs, but the CDBGPWRUPREQ - signal is repurposed as a rescue signal. - """ - - class Targetsel: - """@brief DP TARGETEL values for each DP.""" - CORE_0 = 0x01002927 - CORE_1 = 0x11002927 - RESCUE_DP = 0xf1002927 - +class RP2Base(CoreSightTarget): VENDOR = "Raspberry Pi" MEMORY_MAP = MemoryMap( @@ -118,6 +108,75 @@ class Targetsel: def __init__(self, session): super().__init__(session, self.MEMORY_MAP) + +class RP2350(RP2Base): + """@brief Raspberry Pi RP2350 + """ + + def __init__(self, session): + super().__init__(session) + + def create_init_sequence(self): + seq = super().create_init_sequence() + + # Secure mode is only needed for the flash algo. + seq.insert_before('post_connect_hook', ('set_set_secure_mode', self._set_secure_mode)) + + return seq + + def _set_secure_mode(self): + # The RP2350 flash functions in ROM require the core to be in secure mode + state = self.session.board.target.get_security_state() + if state == Target.SecurityState.SECURE: + LOG.debug("target in secure mode") + return + + LOG.debug("target not in secure mode, attempting to switch to secure mode") + target = self.session.board.target + + DCB_DSCSR = 0xE000EE08 + DSCSR_CDSKEY = 1 << 17 + DSCSR_CDS = 1 << 16 + dscsr = target.read32(DCB_DSCSR) + target.write32(DCB_DSCSR, (dscsr & ~DSCSR_CDSKEY) | DSCSR_CDS) + + state = self.session.board.target.get_security_state() + if state != Target.SecurityState.SECURE: + LOG.debug("target failed to enter secure mode") + raise exceptions.TargetError("Unable to set target to secure mode") + + # Attempt to enable secure access to SRAM + ACCESSCTRL_LOCK = 0x40060000 + ACCESSCTRL_LOCK_DEBUG_BITS = 0x00000008 + + ACCESSCTRL_CFGRESET = 0x40060008 + ACCESSCTRL_WRITE_PASSWORD = 0xacce0000 + lock = target.read32(ACCESSCTRL_LOCK) + if lock & ACCESSCTRL_LOCK_DEBUG_BITS: + # Warn instead of rasing an error in case the permissions are setup + # correctly. + LOG.warn("ACCESSCTRL is locked. Unable to reset.") + else: + target.write32(ACCESSCTRL_CFGRESET, ACCESSCTRL_WRITE_PASSWORD | 1) + + +class RP2040Base(RP2Base): + """@brief Raspberry Pi RP2040. + + This device is very strange in that it as three DPs. The first two DPs each have a single AHB-AP + for the two Cortex-M0+ cores. The third DP is a "Rescue DP" that has no APs, but the CDBGPWRUPREQ + signal is repurposed as a rescue signal. + """ + + class Targetsel: + """@brief DP TARGETEL values for each DP.""" + CORE_0 = 0x01002927 + CORE_1 = 0x11002927 + RESCUE_DP = 0xf1002927 + + def __init__(self, session): + super().__init__(session) + ## The TARGETSEL value to be used. self._core_targetsel = None @@ -210,4 +269,3 @@ class RP2040Core1(RP2040Base): def __init__(self, session): super().__init__(session) self._core_targetsel = self.Targetsel.CORE_1 - diff --git a/test/data/binaries/rp2040.bin b/test/data/binaries/rp2040.bin new file mode 100755 index 0000000000000000000000000000000000000000..aa65fc8c0d4056b2ca4bc0926ac34fb2843e7bb3 GIT binary patch literal 8472 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 O)3;x)FBGD0zyAwefamo9 literal 0 HcmV?d00001