I am working with SWD detection and have chosen a Pico for my MCU board. I like this board. I have made some progress following the article from Kudelski research here:
https://research.kudelskisecurity.com/2 ... e-to-jtag/
Unfortunately I don't have a Hydrabus as used by the author and although I do have an Olimex ARM-USB-OCD-H, I have not found an equivalent python library so have not been able to follow the steps directly using python. Instead, I am using a Pico which I am programming via the Arduino IDE framework (hence C++) simply because I already develop in Arduino. Athough the code requires translation from Python to C++, for me its easier although I may switch to vscode at some point and maybe use another framework such as micropython in due course.
For now I am using the IFDE and bitbanging a couple of GPIO pins to communicate with the SWD interface on a target board.
Test targets includine a Pico, a Pico W and and STM32F401. Taking care to ensure that bits are sent in the correct order, I have made some good progress and can read an IDR code using the pricnciples in the first two sections of code. I have found that Picos require the 'leave dormant state' sequence to be sent before they will respond, but the STM board does respond with only the reset plus the IDR command sequence being sent.
What I cannot get to work is the code under 'Reading from an AP'. I get a '1' status response when sending the sequence to power up the debug domain and then the packaged commands to read the AP IDR register, but the data returned is always 0, for all AP's. The author is using an 'STM32F103 development board' but the article doesn't specifically identfy which board/MCU it is and I am unable to get in touch with them in order to ask. I don't know whether the Picos ot the STM32F401 board have AP's or not, so cannot be sure whether I should get at least one memory AP result as he does or not.
The question is, how do I determine whether there should be any APs present? Datasheets do not seerms to be particularly helpful. Should the Pico return any memory or other AP IDRs? Is it something I might be able to discover with openocd/mdb using the Olimex interface?
Also, the other question is, should the Pico respond without the 'leave dormant state' (i.e. just reset plus IDR) sequence being sent?
I can post some of my code on request, although I am prepared to work through it, but it would be helpful to at least know what result should be expected!
-
- Posts: 26
- Joined: Sun Jun 17, 2018 1:26 pm
Re: Are there any APs on a Pico/Pico W?
I've posted a project with an SWD interface coded in Python, that shouldn't be too difficult to translate in into C, see https://iosoft.blog/picoreg/
It also decodes a VSD file so you can access registers & bitfields by name, but that part isn't essential.
It also decodes a VSD file so you can access registers & bitfields by name, but that part isn't essential.
-
- Posts: 26
- Joined: Sun Jun 17, 2018 1:26 pm
Re: Are there any APs on a Pico/Pico W?
Thank you for posting that project. I pulled it down onto a spare Pi 3b that I had and which is already running a GUI desktop. It has connected to the Pico just fine. I am still familiarising but I can see that picoreg seems to be reading from an AP at address 4 so I should be able to read an IDR from address 0xFC from the AP at address 0x04 at least. I couldn't read an STM32, but I presume your code is only for the Pico?
Re: Are there any APs on a Pico/Pico W?
I didn't check the code but STM32 needs 2 things
- use proper jtag to swd reset sequence as by default it uses jtag - unlike Pico/RP2040 which only does SWD so does not need that
something like this https://github.com/fanoush/EspruinoBoar ... wd.js#L222
- STM32 has extra stuff to init to keep debugger running and keep SRAM accessible when cpu goes idle, something like this (works with STM32F103 for me)
Code: Select all
function stm32_begin(){
var idcode = swd_init(true);
if (idcode == 0x1ba01477){
dap_poweron();
writemem32(0xE0042004,7);// DBGMCU_SLEEP|STOP|STANDBY keep debugger running
var AHBENR=readmem32(0x40021014); // RCC.ahbenr = 0x40021014
writemem32(0x40021014,AHBENR|1); // RCC_AHBPeriph_DMA1
var stm32id=readmem32(0xE0042000).toString(16);
print("STM32 DEVID",stm32id.slice(-4),"REVID",stm32id.slice(0,-4));
}
}
Re: Are there any APs on a Pico/Pico W?
Yes, the STM32 requires a very different bit-sequence to enable SWD, and you also need to check that the software isn't disabling the SWD pins.FruityNutPi wrote: ↑Tue May 16, 2023 12:03 pmThank you for posting that project. I pulled it down onto a spare Pi 3b that I had and which is already running a GUI desktop. It has connected to the Pico just fine. I am still familiarising but I can see that picoreg seems to be reading from an AP at address 4 so I should be able to read an IDR from address 0xFC from the AP at address 0x04 at least. I couldn't read an STM32, but I presume your code is only for the Pico?
See the STM reference manual for your chip, e.g. for STM32F4: RM0090 section 38.3.1 "Mechanism to select the JTAG-DP or the SW-DP"
-
- Posts: 26
- Joined: Sun Jun 17, 2018 1:26 pm
Re: Are there any APs on a Pico/Pico W?
I had been aware from reading the standard that there are at least two versions of the SWD implementation, i.e. SW-DP which is SWD only, and SWJ-DP which can switch between jtag and swd. Of course, what that means in the context of a 3 wire port I am not yet sure, but all script examples I have seen seem to switch to SWD mode to do their stuff.
I did find this note in the standard:
SWD protocol version 2 requires the implementation of dormant state, which can limit its compatibility with SWD
version 1:
• For an SWJ-DP implementation, JTAG is selected on a powerup reset. Selecting SWD bypasses the dormant
state, and subsequent operation is compatible with SWD protocol version 1.
• For an SW-DP implementation of SWD protocol version 2, the dormant state is selected on a powerup reset,
meaning the start-up state differs from a start-up with SWD protocol version 1. After SWD operation is
selected, operation is compatible with SWD protocol version 1.
Again, quite how one is supposed to detect which standard is implemented on a given device I am not sure. SWD version 1 or SWD version 2? With JTAG or without? I had thought that the reset sequence and IDR procedures were standard on every device. It is interesting however to note that the Pico does not have the JTAG mode and therefore should not not need the JTAG-to-SWD switching sequence.
To test this, I amended my script to leave out the JTAG-to-SWD switching sequence and sure enough it worked, but it did still need the 'leave dormant state' sequence. This is somewhat odd, since the author of another JTAG/SWD project demonstrated a result obtained without that. On the other hand, as might be expected, the STM32F401 did not respond at all, so the STM32 definitely does need the JTAG to SWD switching. I expect that testing without JTAG-to-SWD switching first and then with switching and checking the result at each stage should determine whether or not switching is required. The next step would be to test with and without leaving dormant state.
None of this explains why I can't read an AP register yet, but it is very useful. I have yet to try a test reading the CHIP ID register from the AHB3-AP as per PicoReg. I am finding all of this very interesting, if still a bit confusing. For example, I don't understand yet, why there is a need for a JTAG and SWD mode on some devices?
Is it possible to read the IDR using JTAG mode or does one have to switch to SWD?
I did find this note in the standard:
SWD protocol version 2 requires the implementation of dormant state, which can limit its compatibility with SWD
version 1:
• For an SWJ-DP implementation, JTAG is selected on a powerup reset. Selecting SWD bypasses the dormant
state, and subsequent operation is compatible with SWD protocol version 1.
• For an SW-DP implementation of SWD protocol version 2, the dormant state is selected on a powerup reset,
meaning the start-up state differs from a start-up with SWD protocol version 1. After SWD operation is
selected, operation is compatible with SWD protocol version 1.
Again, quite how one is supposed to detect which standard is implemented on a given device I am not sure. SWD version 1 or SWD version 2? With JTAG or without? I had thought that the reset sequence and IDR procedures were standard on every device. It is interesting however to note that the Pico does not have the JTAG mode and therefore should not not need the JTAG-to-SWD switching sequence.
To test this, I amended my script to leave out the JTAG-to-SWD switching sequence and sure enough it worked, but it did still need the 'leave dormant state' sequence. This is somewhat odd, since the author of another JTAG/SWD project demonstrated a result obtained without that. On the other hand, as might be expected, the STM32F401 did not respond at all, so the STM32 definitely does need the JTAG to SWD switching. I expect that testing without JTAG-to-SWD switching first and then with switching and checking the result at each stage should determine whether or not switching is required. The next step would be to test with and without leaving dormant state.
None of this explains why I can't read an AP register yet, but it is very useful. I have yet to try a test reading the CHIP ID register from the AHB3-AP as per PicoReg. I am finding all of this very interesting, if still a bit confusing. For example, I don't understand yet, why there is a need for a JTAG and SWD mode on some devices?
Is it possible to read the IDR using JTAG mode or does one have to switch to SWD?
Re: Are there any APs on a Pico/Pico W?
Take a look at the project I posted using an FTDI adaptor to access a STM32F103, https://iosoft.blog/ftdi-python-part-4/ and https://iosoft.blog/ftdi-python-part-5/
There is a description of the issues I faced, and complete Python code, that could easily be adapted to your requirements.
With regard to the reason JTAG is included, it is an older protocol that requires more I/O lines, and has been largely superseded by SWD on ARM CPUs. However, there are still a lot of chips that only respond to JTAG, and diagnostic tools that are JTAG-only.
There is a description of the issues I faced, and complete Python code, that could easily be adapted to your requirements.
With regard to the reason JTAG is included, it is an older protocol that requires more I/O lines, and has been largely superseded by SWD on ARM CPUs. However, there are still a lot of chips that only respond to JTAG, and diagnostic tools that are JTAG-only.
Re: Are there any APs on a Pico/Pico W?
Nice writeup, I went through something similar. One small comment about thejayben wrote: ↑Wed May 17, 2023 8:19 amand https://iosoft.blog/ftdi-python-part-5/
There is a description of the issues I faced, and complete Python code, that could easily be adapted to your requirements.
Code: Select all
def cpu_mem_read32(d, addr):
ap_addr(d, addr) # Address to read
swd.swd_rd(d, swd.SWD_AP, APORT_DRW) # Dummy read cycle
r = swd.swd_rd(d, swd.SWD_AP, APORT_DRW) # Read data
return r.data.value if r.ack.value==swd.SWD_ACK_OK else None
https://github.com/fanoush/EspruinoBoar ... wd.js#L201
In that way you don't trigger second AP read from the memory over the AHB bus that you then ignore. The 'delayed by one' read is useful when reading more values though. So reading longer block can be faster by using this feature instead of reusing that single value cpu_mem_read32 in a loop (each with one more dummy read than needed). Something like this https://github.com/fanoush/EspruinoBoar ... wd.js#L183
It may definitely not matter in simple cases but since SWD can work while the target is running, doing twice as much reads over AHB bus may cause needless memory access contention. I saw one cool usage of SWD where it could possibly matter - remote framebuffer of device screen while running original closed source firmware. After figuring out where in SRAM framebuffer is, it was easy to just stream that area over SWD to see the screen on the PC too.
Re: Are there any APs on a Pico/Pico W?
Thanks for the tip; to be honest, my code was written years ago, and I wouldn't be surprised if it is a bit inefficient.fanoush wrote: ↑Wed May 17, 2023 10:21 am
You actually don't need to do another dummy read, the value of first read can be also read via DP_RDBUFF register like this
https://github.com/fanoush/EspruinoBoar ... wd.js#L201
In that way you don't trigger second AP read from the memory over the AHB bus that you then ignore. The 'delayed by one' read is useful when reading more values though. So reading longer block can be faster by using this feature instead of reusing that single value cpu_mem_read32 in a loop (each with one more dummy read than needed). Something like this https://github.com/fanoush/EspruinoBoar ... wd.js#L183
-
- Posts: 26
- Joined: Sun Jun 17, 2018 1:26 pm
Re: Are there any APs on a Pico/Pico W?
Thank you for posting this. I would like to know what dap_poweron() does exactly? I am aware that SWJ-DP requires a sequence to switch to SW-DP but I thought that reading an ID code and enabling debug were standard sequences that were not chip specific? Still I need to work out what those sequences within the 'if (idcode=)' statement do exactly.fanoush wrote: ↑Tue May 16, 2023 12:39 pmI didn't check the code but STM32 needs 2 things
- use proper jtag to swd reset sequence as by default it uses jtag - unlike Pico/RP2040 which only does SWD so does not need that
something like this https://github.com/fanoush/EspruinoBoar ... wd.js#L222
- STM32 has extra stuff to init to keep debugger running and keep SRAM accessible when cpu goes idle, something like this (works with STM32F103 for me)Code: Select all
function stm32_begin(){ var idcode = swd_init(true); if (idcode == 0x1ba01477){ dap_poweron(); writemem32(0xE0042004,7);// DBGMCU_SLEEP|STOP|STANDBY keep debugger running var AHBENR=readmem32(0x40021014); // RCC.ahbenr = 0x40021014 writemem32(0x40021014,AHBENR|1); // RCC_AHBPeriph_DMA1 var stm32id=readmem32(0xE0042000).toString(16); print("STM32 DEVID",stm32id.slice(-4),"REVID",stm32id.slice(0,-4)); } }
I have actually ordered a genuine STM32F446 Nucleo board just in case the far east clone is behaving in some sort of unpredictable manner.
Last edited by FruityNutPi on Thu May 18, 2023 7:17 am, edited 1 time in total.
-
- Posts: 26
- Joined: Sun Jun 17, 2018 1:26 pm
Re: Are there any APs on a Pico/Pico W?
jaben, had a good look at your code along with the IHI003H.pdf standard document and it has been very helpful. I do notice that your code starts the connection by doing a reset, the wakeup sequence and then sending a Code ID to select the core. This would seem to confirm that the wakeup sequence is required to talk to Pico. I appreciate that sending the Code ID is as per SWD version 2, but what does not seem clear is whether the sending the Code ID is mandatory? Will a version 2 implementation respond to a version 1 sequence? I.e. is it backward compatible? How does one supposed to send an ID sequence if one does not know what it is yet (i.e. detecting an unknown device)? I would have had no idea what the CodeIDs were without looking at your code. In any case, even using the exact same sequences that you do, I couldn't replicate your results last night to get debug powered up. Sending the clear flags, reset addresses and power up sequence returns an ack of 1, but reading the status register after sending the debug power up request fails. I have tried sending both 0x50000000 and 0x50000001. The former returns a status register value of 0xFFFFFFFF and the latter 0x00000000, so the power up seems to be failed in both cases.jayben wrote: ↑Wed May 17, 2023 8:19 amTake a look at the project I posted using an FTDI adaptor to access a STM32F103, https://iosoft.blog/ftdi-python-part-4/ and https://iosoft.blog/ftdi-python-part-5/
There is a description of the issues I faced, and complete Python code, that could easily be adapted to your requirements.
With regard to the reason JTAG is included, it is an older protocol that requires more I/O lines, and has been largely superseded by SWD on ARM CPUs. However, there are still a lot of chips that only respond to JTAG, and diagnostic tools that are JTAG-only.
Last edited by FruityNutPi on Thu May 18, 2023 9:13 am, edited 4 times in total.
Re: Are there any APs on a Pico/Pico W?
It is in the file I linked, hereFruityNutPi wrote: ↑Thu May 18, 2023 7:03 amThank you for posting this. I would like to know what dap_poweron() does exactly?
https://github.com/fanoush/EspruinoBoar ... wd.js#L164
related ARM documentation is here https://developer.arm.com/documentation ... -CTRL-STAT
I only used this with nrf52 and STM32F103. nrf52 only does swd (no swd to jtag switch needed) and has two access ports (second one for unlocking/mass erasing), STM32 has that extra DBGMCU register (seems to be same for STM32F4 in the pdf linked by jayben?) and it also puts sram(and maybe also flash) access to sleep when CPU runs WFI/WFE, I guess the `writemem32(0x40021014,AHBENR|1)` may be STM32F1 specific. when this is not right I got garbage data when reading from RAM (and flash too?) but DAP access otherwise worked fine. when DBGMCU is not right I got many SWD faults when the code entered sleep so it tested retries a lot.
Re: Are there any APs on a Pico/Pico W?
Sorry, my post contains all the information I know (and can remember), so I can't help with those questions.FruityNutPi wrote: ↑Thu May 18, 2023 7:13 amjaben, had a good look at your code along with the IHI003H.pdf standard document and it has been very helpful. I do notice that your code starts the connection by doing a reset, the wakeup sequence and then sending a Code ID to select the core. This would seem to confirm that the wakeup sequence is required to talk to Pico. I appreciate that sending the Code ID is as per SWD version 2, but what does not seem clear is whether the sending the Code ID is mandatory? Will a version 2 implementation respond to a version 1 sequence? I.e. is it backward compatible? How does one supposed to send an ID sequence if one does not know what it is yet (i.e. detecting an unknown device)? I would have had no idea what the CodeIDs were without looking at your code. In any case, even using the exact same sequences that you do, I couldn't replicate your results last night to get debug powered up. Sending the power up sequence returns an ack of 1, but reading the register returns FFFFFFFF or 0 instead of the expected 0xf0000001.
Don't forget that it is possible for the software to disable SWD, so if the CPU is running any code, you need to check for that.
-
- Posts: 26
- Joined: Sun Jun 17, 2018 1:26 pm
Re: Are there any APs on a Pico/Pico W?
Thanks. So I see it uses the same standard control register value of 0x50000000 for debug powerup request. I see you have added some retries to the subsequent statement that reads the result. I tried this, but unfortunately, the same incorrect value persists. Need to dig further....fanoush wrote: ↑Thu May 18, 2023 8:09 amIt is in the file I linked, hereFruityNutPi wrote: ↑Thu May 18, 2023 7:03 amThank you for posting this. I would like to know what dap_poweron() does exactly?
https://github.com/fanoush/EspruinoBoar ... wd.js#L164
related ARM documentation is here https://developer.arm.com/documentation ... -CTRL-STAT
I only used this with nrf52 and STM32F103. nrf52 only does swd (no swd to jtag switch needed) and has two access ports (second one for unlocking/mass erasing), STM32 has that extra DBGMCU register (seems to be same for STM32F4 in the pdf linked by jayben?) and it also puts sram(and maybe also flash) access to sleep when CPU runs WFI/WFE, I guess the `writemem32(0x40021014,AHBENR|1)` may be STM32F1 specific. when this is not right I got garbage data when reading from RAM (and flash too?) but DAP access otherwise worked fine. when DBGMCU is not right I got many SWD faults when the code entered sleep so it tested retries a lot.
I had a look at that PDF mentiojed by jaben, section 38.3.1 and the STM uses the same code as mentioned in the ARM guide IHI003H, i.e 79E7 and I can confirm that I already have that working. I am able to switch SWJ-DP to SW-DP and send the wake-up sequence as required and then read the IDR, however, I can't get as far as powering up the debug domain, which would appear to be the next and necessary step.
My code to power up the debug domain is as follows:
uint8_t SWD::pwrUpDebugDomain(uint32_t &result){
uint8_t ack = 0;
const uint32_t ctrlstat = (0x05 << 28);
// Clear error bits
ack = writePacket(0x81,0x1E);
// Set AP and DP banks to 0
ack = writePacket(0x95,0x00);
// Power up debug domain (and system domain)
if (ack==1) ack = writePacket(0x8D, ctrlstat);
// Read result of powerup
if (ack==1) ack = readPacket(0xA9, result);
return ack;
}
writePacket and readPacket take two parameters: an 8-bit host byte value, and a 32-bit register value. I have serial.print statements, which I have omitted for clarity, between each stage to print out the results after each statement. I might consider creating a class to set the APnDP, RnW and A[2:3] values and automatically calculate the parity bit. For now, I am just manually calculating the byte values.
Re: Are there any APs on a Pico/Pico W?
I ended up here googling some SWD specifics. As a matter of fact I've just gone through the Kudelski blog post series myself so I think I can offer some useful pointers.
The reason why I registered was to save you some time on the next step (i.e. performing MEM-AP transfers): my STM32G board requires an additional step not mentioned by the Kudelski blog post - you need to set the Size field in the MEM-AP CSW register first, otherwise reads may return all zeroes. It's in the manual for CSW.Size, but rather subtle:
You've got an error there: 0x8d is READ CTRL/STAT, 0xa9 is WRITE CTRL/STAT. So, swap the request codes.FruityNutPi wrote: ↑Thu May 18, 2023 9:00 amCode: Select all
// Power up debug domain (and system domain) if (ack==1) ack = writePacket(0x8D, ctrlstat); // Read result of powerup if (ack==1) ack = readPacket(0xA9, result);
Yeah, you'll want to do that, makes spotting issues such as the one above easier.I might consider creating a class to set the APnDP, RnW and A[2:3] values and automatically calculate the parity bit. For now, I am just manually calculating the byte values.
The reason why I registered was to save you some time on the next step (i.e. performing MEM-AP transfers): my STM32G board requires an additional step not mentioned by the Kudelski blog post - you need to set the Size field in the MEM-AP CSW register first, otherwise reads may return all zeroes. It's in the manual for CSW.Size, but rather subtle:
For the STM32G the ~minimum sequence I needed to be able to read SRAM is:The reset value of this field is unpredictable.
- Perform the line reset sequence
- Read and verify DPIDR (0x0bc11477 for Cortex-M0+)
- Read DP CTRL/STAT (should be all zeroes on cold boot)
- Write 0x50000000 to DP CTRL/STAT (i.e. CSYSPWRUPREQ | CDBGPWRUPREQ)
- Verify DP CTRL/STAT is now 0xf0000000
- Read and verify the MEM-AP IDR (0x04770031)
- Optionally read MEM-AP CSW (0x03000040)
- Write MEM-AP CSW, preserve CSW.Prot bits and set CSW.Size (0x03000002)
-
- Posts: 26
- Joined: Sun Jun 17, 2018 1:26 pm
Re: Are there any APs on a Pico/Pico W?
I re-checked those and couldn't confirm those values. The RnW bit is set to 1 for read and to 0 for write which is kind of counter-intuitive.
Write CTR/STAT:
1 start
0 access DP
0 write
01 register at 0x4
1 parity
0 stop
1 park
10001101 = 0x8D
Read CTR/STAT:
1 start
0 access DP
1 write
01 register at 0x4
0 parity
0 stop
1 park
10101001 = A9
That is how I arrived at those values, however I stand to be corrected. Nevertheless, I did try with those values reversed as suggested and got an ack of 7 on both the write and then the subsequent read to register at 0x4 (control register). With the values the original way around I get an ack of 1 on the write and a 7 on the read. The fact that I get an ack of 7 would seems to suggest that I am doing the read incorrectly in some way. I have tried several retries, but all result in an ack of 7, so its not just a matter of waiting a couple of cycles for debug to power up.
I have now implemented routines to set the value for the host byte and also the select register. It does make it easier to express the values in terms and values found in the datasheet.
Code: Select all
#define SWD_DP 0
#define SWD_AP 1
#define SWD_WR 0
#define SWD_RD 1
uint8_t SWD::setHostByte(uint8_t APnDP, uint8_t RnW, uint8_t addr){
uint8_t hb = 0;
uint8_t hbcpy = 0;
uint8_t setbits = 0;
hb |= (APnDP&0x1) << 6;
hb |= (RnW&0x1) << 5;
hb |= (addr&0xC) << 1;
hbcpy = hb;
while (hbcpy){
setbits += hbcpy & 1U;
hbcpy = hbcpy>>1;
}
if (setbits & 0x01) hb |= 1U << 2;
hb |= 0x81; // Set start and park bit
return hb;
}
uint32_t SWD::setRegSelect(uint8_t addr, uint8_t apsel, uint8_t dpsel){
uint32_t apdata = (addr&0xFF) << 24;
apdata |= (apsel&0x0F) << 4;
apdata |= dpsel&0x0F;
return apdata;
}
Thank you for the information you have provided and your willingness to help. I really appreciate that along with the heads up on setting the size field for when I get that far forward. For now, I would be happy with just getting these steps working:ius wrote: ↑Sat May 20, 2023 5:16 pmThe reason why I registered was to save you some time on the next step (i.e. performing MEM-AP transfers): my STM32G board requires an additional step not mentioned by the Kudelski blog post - you need to set the Size field in the MEM-AP CSW register first, otherwise reads may return all zeroes. It's in the manual for CSW.Size, but rather subtle:
For the STM32G the ~minimum sequence I needed to be able to read SRAM is:The reset value of this field is unpredictable.
- Perform the line reset sequence
- Read and verify DPIDR (0x0bc11477 for Cortex-M0+)
- Read DP CTRL/STAT (should be all zeroes on cold boot)
- Write 0x50000000 to DP CTRL/STAT (i.e. CSYSPWRUPREQ | CDBGPWRUPREQ)
- Verify DP CTRL/STAT is now 0xf0000000
- Read and verify the MEM-AP IDR (0x04770031)
- Optionally read MEM-AP CSW (0x03000040)
- Write MEM-AP CSW, preserve CSW.Prot bits and set CSW.Size (0x03000002)
[1]Perform the line reset sequence
[2]Read and verify DPIDR (0x0bc11477 for Cortex-M0+)
[3]Read DP CTRL/STAT (should be all zeroes on cold boot)
[4]Write 0x50000000 to DP CTRL/STAT (i.e. CSYSPWRUPREQ | CDBGPWRUPREQ)
[5]Verify DP CTRL/STAT is now 0xf0000000
I have 1-4 working with the correct DPIDR being returned and an ack of 1 being received at each step. Its just step 5 that comes back with and ack of 7 and the wrong value on both the Pico and the STM32 (taking into account the slightly different startup sequence) and I am struggling to figure out why. I mapped the steps that the PicoReg program is doing as follows:
Code: Select all
Connect:
- reset
- wakeup
- write SWD_DP, 0xC, 0x01002927 write target ID of core 0
Get dpidr:
- read SWD_DP, 0, IDR
Power up:
- write SWD_DP, 0, 0x1E clear error bits
- write SWD_DP, 8, 0 set AP and DP bank 0
- write SWD_DP, 4, 0x50000001 power up debug
- read SWD_DP, 4, statusreg read status reg
Read AP:
- write SWD_DP, 8, 0xF0 set AP bank F, DP bank 0
- read SWD_AP, 0xC read AP address 0xFC
- read SWD_DP, 0xC read AP result (AHB-AP IDR)
- write SWD_AP, 0, 0xA2000012 auto-increment word values ?
- write SWD_DP, 8, 0 set AP and DP bank 0
Re: Are there any APs on a Pico/Pico W?
Note ARM ADI specifies all fields and transfers are LSB first:FruityNutPi wrote: ↑Sat May 20, 2023 8:16 pmThat is how I arrived at those values, however I stand to be corrected. Nevertheless, I did try with those values reversed as suggested and got an ack of 7 on both the write and then the subsequent read to register at 0x4 (control register).
Thus, the request octet is assumed to be clocked out LSB first as well, which means the start bit is in bit 0, not 7. In the end you can obviously make it work either way, but the Kudelski blog post assumes LSB-first in their implementation and so does ARM in their CMSIS-DAP implementation.All SWD transfers are made LSB-first. Therefore, the OK response of b001 appears on the wire as 1, followed by 0, followed by 0
I too made this mistake based on the transfer diagrams in the ADI manual..
The steps you've outlined seem okay, I'm afraid the bug might be in the part of the implementation you haven't shown.I have 1-4 working with the correct DPIDR being returned and an ack of 1 being received at each step. Its just step 5 that comes back with and ack of 7 and the wrong value on both the Pico and the STM32.
You mention that the read in step [5] fails. It's important to realize that an error may in fact be caused by the previous transaction. The last step before that happens to be a write (the first write if you're adhering to the bare minimum).
This also points at the write possibly being at fault here. Are you sure your write sequence is correct? Another part of ADI that bit me was that I mistakenly included a turnaround cycle after the data part of a write sequence - whereas it's only used for read sequences (!).For the Pico after wakeup, there is also a write to RDBUFF with the IDR of Core0. I found that if I include this step, then everything after that fails (with the Pico target).
-
- Posts: 26
- Joined: Sun Jun 17, 2018 1:26 pm
Re: Are there any APs on a Pico/Pico W?
This MSB vs LSB thing had my left brain having and argument with its right counterpart when I first looked at it. The diagrams on B4-111/B4-112 show the read and write operations. The host byte is tranmitted from the perspective of the running clock with Start first and Park closing the byte. Of course, assuming that the sending MCU transmits in LSB order (as shown in the python scripts), then, as you point out, Start would indeed be in bit position 0 and Park in 7, the order of all bits being reversed. My send routine processes and sends the bits in left to right order, which corresponds with how they are visualised in the diagram. I thought that this would be less confusing than having to work out the reverse bit order value for each byte. Having put in place a routine to calculate this byte, this problem has now been removed although the bit order in which the byte is being sent is still very much relevant. My routine to read the data, on the other hand, always reads from the wire LSB first.ius wrote: ↑Sat May 20, 2023 9:14 pmNote ARM ADI specifies all fields and transfers are LSB first:FruityNutPi wrote: ↑Sat May 20, 2023 8:16 pmThat is how I arrived at those values, however I stand to be corrected. Nevertheless, I did try with those values reversed as suggested and got an ack of 7 on both the write and then the subsequent read to register at 0x4 (control register).Thus, the request octet is assumed to be clocked out LSB first as well, which means the start bit is in bit 0, not 7. In the end you can obviously make it work either way, but the Kudelski blog post assumes LSB-first in their implementation and so does ARM in their CMSIS-DAP implementation.All SWD transfers are made LSB-first. Therefore, the OK response of b001 appears on the wire as 1, followed by 0, followed by 0
I too made this mistake based on the transfer diagrams in the ADI manual..
Rather confusingly the ARM read and write operation diagrams show the datafield numbering order as [0:31]. Yet, further on, when you look at any of the register explanations, the fields are shown in the order [31:0]. I have to admit that this part still has me confused.
Also, thinking about it, the host byte to read the IDR (0xA5) is symmetrical, whereas other bytes are not, which could possibly explain why IDR is working and other commands are not...
ius wrote: ↑Sat May 20, 2023 9:14 pmYou mention that the read in step [5] fails. It's important to realize that an error may in fact be caused by the previous transaction. The last step before that happens to be a write (the first write if you're adhering to the bare minimum).
That is a fair point. I also realised that a value of 7 (b111) is not valid for an ACK response. Only one of the 3 bits should be set with a value of 1 for Ok, 2 for Wait and 4 for Error. Also, since the ack comes after the host byte, but before the data, it can only confirm that the command byte received correctly. It cannot confirm the validity of the data field. That being the case, the error may indeed be in the previous step.
Now that you have we have discussed the bit order, I am no longer so sure. With regards to the Turnaround cycle, yes, I was aware of that. The ack bits have to be read by the sender during both a read and write operation, but during a read operation, the ack bits and data bits are read in a single sweep before sending the second Turnaround. During a write operation, the second Turnaround immediately after ack it neccessary to allow the sender to write the data to the wire.ius wrote: ↑Sat May 20, 2023 9:14 pmThis also points at the write possibly being at fault here. Are you sure your write sequence is correct? Another part of ADI that bit me was that I mistakenly included a turnaround cycle after the data part of a write sequence - whereas it's only used for read sequences (!).For the Pico after wakeup, there is also a write to RDBUFF with the IDR of Core0. I found that if I include this step, then everything after that fails (with the Pico target).
I will need to review my write routines, but it would be helpful to confirm in which order the receiving (DUT) device expects the data field bits to be sent?
Re: Are there any APs on a Pico/Pico W?
The data part is clocked out LSB first (like the rest).
If reviewing the write routine doesn't solve it, a logic analyzer might help. A €15 Saleae clone (if you're on a budget) or another Pico and sigrok-pico should be sufficient.
If reviewing the write routine doesn't solve it, a logic analyzer might help. A €15 Saleae clone (if you're on a budget) or another Pico and sigrok-pico should be sufficient.
-
- Posts: 26
- Joined: Sun Jun 17, 2018 1:26 pm
Re: Are there any APs on a Pico/Pico W?
Sorry, but I am still confused regarding the bit order for sending things. I accept what has been said about sending data LSB, but having had a look at the picoreg program, I am still puzzled. The program composes the header byte like this:
So that is bit for bit, left to right, as shown in the ARM documentation:
start[1] - APnDP[1] - RnW[1] - APDP[2] - Parity[1] - Stop[1] - Park[1]
The number in brackets shows the number of bits allocated to each field. When I now look at the routine for sending the data, it shows each bit being read from right to left:
That would send Park first and end with Start.... which is contrary to what is shown in the illustration in the ARM document. In any case I tried it that way around. It read the DP IDR, but gave me more errors on the subsequent steps.
Following your suggestion, I dug out the cheapo LA that I have and hooked it up and got it working with Pulseview so will probably have a play with this next. Pulseview does have an SWDv1 decoder which might suffice.
Code: Select all
swd_hdr = [("Start", c_uint, 1), ("APnDP", c_uint, 1),
("RnW", c_uint, 1), ("Addr", c_uint, 2),
("Parity", c_uint, 1), ("Stop", c_uint, 1),
("Park", c_uint, 1)]
start[1] - APnDP[1] - RnW[1] - APDP[2] - Parity[1] - Stop[1] - Park[1]
The number in brackets shows the number of bits allocated to each field. When I now look at the routine for sending the data, it shows each bit being read from right to left:
Code: Select all
def send_bits(self, data, nbits, recv):
n = 0
while n < nbits:
self.txdata += "." if recv else "1" if data & (1 << n) else "0"
n += 1
Following your suggestion, I dug out the cheapo LA that I have and hooked it up and got it working with Pulseview so will probably have a play with this next. Pulseview does have an SWDv1 decoder which might suffice.
-
- Posts: 26
- Joined: Sun Jun 17, 2018 1:26 pm
Re: Are there any APs on a Pico/Pico W?
I had a look at this in Pulseview and compared the trace generated by my program against the one created by PicoReg. This is what I got:
It seems my code, despite passing the same hex address values as Picoreg to the routine that builds the header byte and apparently building it in the same way, is reversing 'write select' with 'write ctrl/stat', but I don't understand why.
As shown in PulseView, the order that bits of the header byte are transmitted is:
My understanding is that the registers are addressed as follows:
Since in the header byte the address is held as two bits, the two zeroes are simply dropped. PicoReg just shifts the byte two bits to the right using addr >> 2. Firstly, why does the diagram show the register address as as bits 2:3, when they are actually in positions 3 and 4 regardless of whether your read left to right or right to left? PicoReg works correctly and Pulseview is displaying the correct sequence, so why is the address of Ctrl/stat shown as '10' and Select as '01'?
If I re-arrange the order of the DP address bits in my header byte value, it does finally work and powers up the debug domain. The next step is to read the AP 0 IDR value.
Code: Select all
PicoReg MyProg
------- --------
wakeup wakeup
idcode 0x0bc12477 idcode 0x0bc12477
w abort 0x0000001e w abort 0x0000001e
w select 0x00000000 w ctrl/stat 0x00000000
w ctrl/stat 0x50000001 w select 0x50000001
r ctrl/stat 0xf0000041 resend noreply
As shown in PulseView, the order that bits of the header byte are transmitted is:
Code: Select all
w ctrl/stat 10010101 (0x95)
w select 10001101 (0x8D)
Code: Select all
Reserved 0x0 0000
Ctrl/stat 0x4 0100
Select 0x8 1000
RdBuff 0xC 1100
If I re-arrange the order of the DP address bits in my header byte value, it does finally work and powers up the debug domain. The next step is to read the AP 0 IDR value.
Re: Are there any APs on a Pico/Pico W?
Your first sentence answers the rest. A2:3 in diagram means it is bits 2,3 of the address (bits 0:1 are dropped as you say). Doesn't matter where it finally ends in the swd packet.FruityNutPi wrote: ↑Thu May 25, 2023 6:25 pmSince in the header byte the address is held as two bits, the two zeroes are simply dropped. PicoReg just shifts the byte two bits to the right using addr >> 2. Firstly, why does the diagram show the register address as as bits 2:3, when they are actually in positions 3 and 4 regardless of whether your read left to right or right to left?