Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Locking yourself out by reassigning the SWD pins #27

Open
sw opened this issue Jan 11, 2025 · 4 comments
Open

Locking yourself out by reassigning the SWD pins #27

sw opened this issue Jan 11, 2025 · 4 comments

Comments

@sw
Copy link
Contributor

sw commented Jan 11, 2025

I'm new to Rust and was trying to add support for the PY32F002B. In one of my first tests, I accidentally reconfigured PA2 from the normal SWCLK function to a GPIO. I seemed to have locked myself out and no amount of fiddling with the NRST pin or pyocd options have helped :-(.

Before I was using Puya's C SDK and what has probably saved me from my idiocy was this:
https://github.com/OpenPuya/PY32F002B_Firmware/blob/0cc6e0a61113337e99cc2a2525180ebb10229982/Drivers/CMSIS/Device/PY32F0xx/Source/system_py32f0xx.c#L125-L129

  /* When the SWD pin is reused for other functions, this function is used to solve the 
  problem of not being able to update the code. */
  DelayTime(100);

Would something like this make sense for py32-hal? Or could Rust's type system be used to avoid my mistake, in a sort of "do-you-really-want-to-do-this" manner? What I mean is to treat the SWD pins as special types to remind the user of the danger of reassigning them, but still allowing that.

You can have a look at my code on the f002b branches at py32-data and py32-hal, but it's untested and not ready for inclusion. I'm open to suggestions for improvement.

Any hints on how to recover the chip are also welcome, of course.

@decaday
Copy link
Member

decaday commented Jan 11, 2025

Hi! I think there are two methods we can try:

  1. Change the voltage on the BOOT pin and use serial to flash a safe program, or simply prevent the chip from running the program in Flash, which is similar to STM32. (It seems that some of the 002B packages don’t have any BOOT pins?)

  2. The py32 pins may have some leakage current, but sometimes this current isn’t enough to the chip. Here’s an interesting solution:
    Connect SWDIO, SWCLK, and GND, but leave VCC unconnected. Use the small leakage current from SWD to power the chip, and then flash the chip. At this point, the chip might not be fully functional, but it will be enough to perform the flashing operation.

    I’ve used this method to solve similar issues before, but I’m not sure if it will work for your problem.

Regarding whether we should restrict this user behavior, the implementation (using the type system or other methods) requires further discussion, so feel free to provide suggestions.

And thank you so much for your contribution to supporting the 002B!
Most of the chips in the data/chips folder are incomplete. So far, I’ve worked on py32f030k28, py32f030f16, and py32f072c1b. Since the repository is still in the early stages, the program behavior is independent of the packaging, so the 002A, 003, 040, 071, and 072 series can be covered by these three chip features. I haven’t handled other cut-down chips yet—this is a TODO, but it’s not urgent. (002B is a new family)

---outdated start

I noticed you directly modified the PY32F002B.yaml. You can refer to this file: [PY32F030K28.yaml](https://github.com/py32-rs/py32-data/blob/main/data/chips/PY32F030K28.yaml).
Here’s the thing, the peripheral list is in this directory: [Peripherals](https://github.com/py32-rs/py32-data/tree/main/data/peripherals).

This list is generated by a local Python script of mine, and then I make manual edits. Since the py32 SVD file is incomplete and may have various edge cases and errors, I haven’t uploaded the script to the repository. I plan to rewrite it in Rust in the future and put it into py32-data/py32-data-gen, but I’m currently working on other tasks.

So I think it’s best if I handle the foundational work for 002B support in py32-data (I hope to finish it this weekend), and you can focus on the py32-hal work. Does that OK?

---outdated end

@decaday
Copy link
Member

decaday commented Jan 11, 2025

I’m very sorry, I didn’t see that you added the peripherals/002B.yaml (because it was collapsed), but it looks great! It seems like you’ve already done most of the work.

In addition, the code generation process in py32-data, py32-hal/build.rs, as well as the macros and interrupt object encapsulations for various peripherals, are quite complex. If you have any questions, I’d be happy to help.

@sw
Copy link
Contributor Author

sw commented Jan 12, 2025

Thank you for your suggestions.

Change the voltage on the BOOT pin and use serial to flash a safe program,

The 002B does not come with a bootloader programmed. There is the possibility to use part of the flash for a user-supplied bootloader using the option bytes, but no BOOT pin.

Connect SWDIO, SWCLK, and GND, but leave VCC unconnected

Unfortunately this didn't help.

I think the SWD pin reassignment is a red herring, because the chip does respond to the IDCODE read request, if I use the connect-under-reset option. But write requests to the debug registers are rejected with a WAIT response. The debug adapter retries the request repeatedly and then times out.

So I might have broken something else with my attempt. Just beware of that if you want to try my branch.

@decaday
Copy link
Member

decaday commented Jan 14, 2025

You're right about the PY32F002B series having no BOOT pins. So restricting SWD pin usage becomes important.

I can think of two approaches:

  1. Add a 100ms delay in py32_hal::init() function, similar to the SDK approach, with a no_swd_delay feature to disable this delay if needed.

  2. Hide the peripheral singletons for these two pins in Peripherals and add an unsafe-use-swd-pins feature to enable them.

I'm not sure if there is a better solution, suggestions are welcome.

For other series that mostly have BOOT pins exposed, we should discuss further whether such restrictions are necessary.

I tested your branch today - the blinky example worked well on my board without causing any issues. When I configured the pin as PA2(SWCLK), the chip indeed became inaccessible. (Don't worry, I have enough chips to replace the one on the dev board, and py32f002b is quite affordable)

I tried several methods but nothing worked to recover it. I'm not very familiar with the low-level details of SWD or CMSIS-DAP, so I didn't check for IDCODE read request responses. Someone told me that SWD interface design isn't always ideal on some chips - there are even MCUs from other manufacturers where you can use SWD and UART simultaneously on the same IO pin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants