stefaandesmet2003 / stlinkswim

1 stars 1 forks source link

Can't read flash twice or more consecutively #2

Open llinjupt opened 2 years ago

llinjupt commented 2 years ago

Hi stefaandesmet, first thanks for your work and it's interesting and helpful. I am not sure if it's a bug that I can't read the taget MCU (STM8S105KCT6) twice like below:

F:\stm8flash>.\stm8flash.exe -c stlinkv2 -p stm8s105?6 -r test.bin
Determine FLASH area
STLink: v2, JTAG: v37, SWIM: v7, VID: 8304, PID: 4837
Due to its file extension (or lack thereof), "test.bin" is considered as RAW BINARY format!
Reading 32768 bytes at 0x8000... OK
Bytes received: 32768

F:\stm8flash>.\stm8flash.exe -c stlinkv2 -p stm8s105?6 -r test.bin
Determine FLASH area
STLink: v2, JTAG: v37, SWIM: v7, VID: 8304, PID: 4837
SWIM error 0x04

But in the same environment writing twice is OK. Once the error occurs tring to write will fail as well. The only way to solve it is to repower the target MCU. THX again.

llinjupt commented 2 years ago

BTW, I have tried with a real stlinkv2 which works well. Logs are attached for your reference:

F:\stm8flash>.\stm8flash.exe -c stlinkv2 -p stm8s105?6 -r test.bin
Determine FLASH area
STLink: v2, JTAG: v38, SWIM: v7, VID: 8304, PID: 4837
Due to its file extension (or lack thereof), "test.bin" is considered as RAW BINARY format!
Reading 32768 bytes at 0x8000... OK
Bytes received: 32768

F:\stm8flash>.\stm8flash.exe -c stlinkv2 -p stm8s105?6 -r test.bin
Determine FLASH area
STLink: v2, JTAG: v38, SWIM: v7, VID: 8304, PID: 4837
Due to its file extension (or lack thereof), "test.bin" is considered as RAW BINARY format!
Reading 32768 bytes at 0x8000... OK
Bytes received: 32768
stefaandesmet2003 commented 2 years ago

Hi @llinjupt,

thanks for using my project! I verified on my setup, and repeated reads seem to work fine here. i'm using latest stm8flash from github. Could you rebuild stm8flash with debug info (make DEBUG=1) and send the output? For now I'm just guessing that it may be linked with the SWIM high-speed mode.

Cheers, Stefaan

(base) stefaan@acer:~/Documents/dev/tmp/stm8flash$ ./stm8flash -c stlinkv2 -p stm8s105?6 -r test.bin
Determine FLASH area
STLink: v2, JTAG: v37, SWIM: v7, VID: 8304, PID: 4837
Due to its file extension (or lack thereof), "test.bin" is considered as RAW BINARY format!
Reading 32768 bytes at 0x8000... OK
Bytes received: 32768
(base) stefaan@acer:~/Documents/dev/tmp/stm8flash$ ./stm8flash -c stlinkv2 -p stm8s105?6 -r test.bin
Determine FLASH area
STLink: v2, JTAG: v37, SWIM: v7, VID: 8304, PID: 4837
Due to its file extension (or lack thereof), "test.bin" is considered as RAW BINARY format!
Reading 32768 bytes at 0x8000... OK
Bytes received: 32768
llinjupt commented 2 years ago

THX for your rapid response. I have just cloned stm8flash form https://github.com/vdudouyt/stm8flash and enabled the DEBUG option, and logs attached:

stm8flash>.\stm8flash.exe -c stlinkv2 -p stm8s105?6 -r test.bin
Determine FLASH area
     GET_VERSION
STLink: v2, JTAG: v37, SWIM: v7, VID: 8304, PID: 4837
     GET_CURRENT_MODE
        -> 00 01
     DFU EXIT
     SWIM ENTER
     SWIM READBUFSIZE
        -> 0x1800
     SWIM READ_CAP 01
        -> 00 00 00 00 00 00 00 00
     SWIM ASSERT_RESET
     SWIM READSTATUS
        status 00 00 00 00
     SWIM ENTER_SEQ
     SWIM READSTATUS
        status 00 00 00 00
     SWIM WRITEMEM 00 01 00 00 7f 80 a1
     SWIM READSTATUS
        status 04 00 00 00 // The first byte should be 00
     SWIM READSTATUS
        status 04 00 00 00
     SWIM READSTATUS
        status 04 00 00 00
     SWIM READSTATUS
        status 04 00 00 00
     SWIM READSTATUS
        status 04 00 00 00
SWIM error 0x04

After simply analysing, I realized It seems related to swim_update(), so I just updated swim_init(true) to swim_init(false) to disable async mode and It worked. Of course the details need more effort to dig out if I have time I will try.

BTW, It seems that you did that in a linux-like system. Would you like to try it on Window10?

llinjupt commented 2 years ago

Hi @stefaandesmet2003 stefaandesmet2003. I have just verified on Ubuntu and it's ok under async mode. So things become a bit clear that it should be related to the Win10 OS (maybe usb transfer related, just guess). Logs:

root@ubuntu:/home/ubuntu/stm8flash# ./stm8flash -c stlinkv2 -p stm8s105?6 -r test.bin
Determine FLASH area
Due to its file extension (or lack thereof), "test.bin" is considered as RAW BINARY format!
Reading 32768 bytes at 0x8000... OK
Bytes received: 32768
root@ubuntu:/home/ubuntu/stm8flash# ./stm8flash -c stlinkv2 -p stm8s105?6 -r test.bin
Determine FLASH area
Due to its file extension (or lack thereof), "test.bin" is considered as RAW BINARY format!
Reading 32768 bytes at 0x8000... OK
Bytes received: 32768
root@ubuntu:/home/ubuntu/stm8flash# uname -a
Linux ubuntu 4.15.0-161-generic #169-Ubuntu SMP Fri Oct 15 13:41:54 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
stefaandesmet2003 commented 2 years ago

In your case the swim entry sequence seems to go wrong. The return code of swim_doEntrySequence() is not checked at the moment, so the error is not reported on the 'read status' immediately after. But any subsequent WOTF/ROTF will fail because the stm8 target isn't ready to accept swim commands (in your log the write to SWIM_CSR fails)

I remember having had problems initially with generating the swim entry sequence, but after rewriting the delayMicroseconds() code, it worked reliably for me. This is of course linked to the stm32 only, and doesn't explain why it works on linux for you, and not on windows. but you might want to check the swim signal with a logic analyzer, or test with different stm8 targets.

I tried on windows10 just now, rebuilt stm8flash under cygwin, but so far I don't manage to reproduce your error. sorry! It's an older machine, so could be linked with usb like you said, although i don't understand yet how ..

Stefaan@LAPTOP-2J6EGD5Q ~/stm8flash
$ ./stm8flash.exe -c stlinkv2 -p stm8s105?6 -r test.bin
Determine FLASH area
STLink: v2, JTAG: v37, SWIM: v7, VID: 8304, PID: 4837
Due to its file extension (or lack thereof), "test.bin" is considered as RAW BINARY format!
Reading 32768 bytes at 0x8000... OK
Bytes received: 32768

Stefaan@LAPTOP-2J6EGD5Q ~/stm8flash
$ ./stm8flash.exe -c stlinkv2 -p stm8s105?6 -r test.bin
Determine FLASH area
STLink: v2, JTAG: v37, SWIM: v7, VID: 8304, PID: 4837
Due to its file extension (or lack thereof), "test.bin" is considered as RAW BINARY format!
Reading 32768 bytes at 0x8000... OK
Bytes received: 32768
llinjupt commented 2 years ago

@stefaandesmet2003 Sound strange. How to explain swim_init(false) worked in the same environment? In this case writing SWIM_CSR didn't fail. I tried on another PC instead of my notebook just now and the same case happened as well. And the precompiled stm8flash and firmwares are attached below to see if it's convinent for you to reproduce your side.

If I have spare time, I will check the code carefully and do more anyalysis with a scope to see what's going on there. Thanks anyway.

stm8flash.x64_86.zip

stefaandesmet2003 commented 2 years ago

I can't explain the difference in behaviour for you between sync and async. The purpose of async mode is to enable the PC (stm8flash) to keep inquiring READ_STATUS over usb, while the stm32 is reading/writing data over swim. In sync mode the ROTF/WOTF are blocking, and the host USB is waiting (NACK on the usb pipe) until the transfer is completed. In both cases the commands on the swim line are the same, so the target should be in the same state after the read. do you see 'high speed swim' in both cases?

the stm8flash.exe won't run on my win10 machine (the application was unable to start correctly...) I'll give the stm32 files a try now

llinjupt commented 2 years ago

I can't explain the difference in behaviour for you between sync and async. The purpose of async mode is to enable the PC (stm8flash) to keep inquiring READ_STATUS over usb, while the stm32 is reading/writing data over swim. In sync mode the ROTF/WOTF are blocking, and the host USB is waiting (NACK on the usb pipe) until the transfer is completed. In both cases the commands on the swim line are the same, so the target should be in the same state after the read. do you see 'high speed swim' in both cases?

Yes, in both async/no async modes, the first time to read always dump out this info:

continuing in high speed swim
Due to its file extension (or lack thereof), "test.bin" is considered as RAW BINARY format!
Reading 32768 bytes at 0x8000... read range

For aysnc mode firmware, the second time to read quit after dumpping out "SWIM error 0x04". BTW, I tried another target mcu STM8S103F3P6 and the same result occured again.

stefaandesmet2003 commented 2 years ago

Hi @llinjupt, I managed to reproduce the error with your bin file. And then the error also showed in my repo when compiling with the -0s flag. Interesting! (did you add this flag in your env? I noticed your bin file was much smaller than mine)

the logic analyzer shows that the first WOTF command is aborted, although the target acks correctly Screenshot from 2022-04-20 21-53-24

I added a bugfix branch. Would you be so kind to test it? It works for me, although I don't fully understand yet why. The optimization flag seems to have strange effects (add a debug print, and the error disappears ..). I had expected that changing SWIM_DMA_IN_Buffer to volatile would have any effect, but it doesn't.

It may be linked to a small bug in swim_doEntrySequence() that is still there : when the target stm8 is already in swim mode (on your second run), it acks every pulse of the entry sequence (interprets it as swim resets), so you see about 20 pulses, but only 10 DMA's requested. Maybe this situation ripples through to the next use of DMA (where the error occurs), although the DMA channel has been disabled/enabled in between.

llinjupt commented 2 years ago

Hi @llinjupt, I managed to reproduce the error with your bin file. And then the error also showed in my repo when compiling with the -0s flag. Interesting! (did you add this flag in your env? I noticed your bin file was much smaller than mine)

Sorry for my mistake that I shoule have mentioned that I compiled with Msys2 directly rather than platformIO which is not avaliable here for some reasons I don't know. Refering to https://github.com/libopencm3/libopencm3-template and put your code into my-project, following the instructions in README.md, I got the bin file at last.

So I checked rules.mk deployed in libopencm3-template and found below lines just as you guessed:

BUILD_DIR ?= bin
#OPT ?= -Os  // original
OPT ?=
CSTD ?= -std=c99

And the size of the bin file grew bigger to be about 18KB instead of the original 15KB.

the logic analyzer shows that the first WOTF command is aborted, although the target acks correctly Screenshot from 2022-04-20 21-53-24

I added a bugfix branch. Would you be so kind to test it? It works for me, although I don't fully understand yet why. The optimization flag seems to have strange effects (add a debug print, and the error disappears ..). I had expected that changing SWIM_DMA_IN_Buffer to volatile would have any effect, but it doesn't.

Yeah, as long as I removed -Os, it worked without touching any code.

It may be linked to a small bug in swim_doEntrySequence() that is still there : when the target stm8 is already in swim mode (on your second run), it acks every pulse of the entry sequence (interprets it as swim resets), so you see about 20 pulses, but only 10 DMA's requested. Maybe this situation ripples through to the next use of DMA (where the error occurs), although the DMA channel has been disabled/enabled in between.

Actually I noticed this queer waveform at the very beginning, but I didn't know why it happend for no such info mentioned in UM0470.pdf. Now it made sense to me as you said the entry sequence interpreted as resets when in swim mode already. And I confirmed that even with the real stlinkv2, the waveform is still the same. But stlinkv2 is able to get over it. If there is a way to know whether it is in swim mode, there is no need to call swim_doEntrySequence() again.

IMHO, volatile has noting to do with DMA. After finishing the work on my hand, I'd like to do further analysis and give some useful feedback I hope.

llinjupt commented 2 years ago

Hi @stefaandesmet2003, After doing more analysis and observing the waveform, I am sure the problem is caused by swim_doEntrySequence() when it's in SWIM mode already.

A normal request and ack include 10 pulses, but an abnormal one includes 18 pulses (it acks every pulse). But swim_doEntrySequence() always try to sample in10 pulses. So if it's in SWIM mode the left 8 pulses are still on going when the next wtof cmd comes (cause it's in aysnc mode) and the left pulses are captured wrongly (actually only the last pulse is captured at my side looking at the waveform. And it's releated to the period of code excuting. So that's why the memset you added would solve this problem cause it introduced a tiny delay that just luckly got rid of the last pulse. And the same idea is why -Os brought this issue as well.).

The simple solution is just to add below lines at the end of swim_doEntrySequence(void):

  if(!dly)
    return ERR_FAIL;

  // try to consume extra pulses when target mcu is in SWIM mode already
  SWIM_IN_TIMER_RISE_DMA_INIT(8, (uint32_t) SWIM_DMA_IN_Buffer);
  SWIM_IN_TIMER_RISE_DMA_WAIT(dly);

  return ERR_NONE;
}
stefaandesmet2003 commented 2 years ago

Thanks for your work @llinjupt ! I'm unable to test your proposed solution for the next few days, sorry. Have you tested it already? I believe your analysis is correct, and swim_doEntrySequence() returns too early, when target is already in swim mode. But I think your code will never catch more than 1 ack, and always time out. Because the code before is already busy-waiting until after the 4 2khz pulses, your code will miss ack 11 to 17 (dma disabled) and at best catch ack 18. At best = depending on timing, if the timer/dma is set up before ack 18 arrives. I'll try to come up with a clean solution; I'm not satisfied with my bugfix, because, as you explained, it remains dependent on execution timing.

llinjupt commented 2 years ago

Thanks for your work @llinjupt ! I'm unable to test your proposed solution for the next few days, sorry. Have you tested it already?

Yes, here it worked well.

I believe your analysis is correct, and swim_doEntrySequence() returns too early, when target is already in swim mode. But I think your code will never catch more than 1 ack, and always time out. Because the code before is already busy-waiting until after the 4 2khz pulses, your code will miss ack 11 to 17 (dma disabled) and at best catch ack 18. At best = depending on timing, if the timer/dma is set up before ack 18 arrives.

There's no busy-waiting and only 10 pulses are handled before extra pulses consumption. And actully it doesn't try to really catch all left 8 pulses completely but just gets rid of these pulses.

I'll try to come up with a clean solution; I'm not satisfied with my bugfix, because, as you explained, it remains dependent on execution timing.

Looking forward to your solution.