microchip-pic-avr-tools / pymcuprog

a Python utility for programming various Microchip MCU devices using Microchip CMSIS-DAP based debuggers
MIT License
96 stars 22 forks source link

Feature request: a new generic device: 'auto' or 'auto_updi' #15

Open raimond opened 2 years ago

raimond commented 2 years ago

Hi, I'm talking about updi devices. Wouldn't it be nice to have an auto discovery of the updi device/target as long as we have the signature bytes? Test case: I have two different boards, one has an atmega1609 and one has an atmega3209. The first one can support 1609, 3209 or 4809. The second one can have an 3209 or 4809. For the first board, I can stick of course to 1609, but there may be times that I will be force to put an 3209 or 4809 because of the availability. Now, the pymcuprog is very happy to tell me that I'm wrong, and I use '-d atmega1609' but it have found an atmega3209. And quits. This is just useless. Useful is indeed when it tells me that the binary is too big for the target mcu found. So, I'm thinking of a new switch, or just a new device like 'auto' or 'updi_auto' for these situations. I would like to be able to use such a thing: pymcuprog -d auto write -f xyz.hex pymcuprog -t uart -u com35 -d auto write -f xyz.hex I'm aware that the nano kits already have a default target, so the '-d auto' will tell it that there is not the default target, but just 'ping' it and see what you find. What do you think? Thanks.

xedbg commented 2 years ago

Thanks for the input - logged as DSG-4313 We will have to take a closer look at this suggestion. Many users feel its most valuable to receive early feedback when the device is not what is expected, so the default behaviour should remain as-is. The option of a "auto" device could be explored - usually the challenges in such tricks revolve around the order in which the stack is built, since an auto-detect either involves starting an session prematurely (not fully configured) or a re-configuring and re-starting a session if a default/guess is wrong.

raimond commented 2 years ago

This is the way Atmel did the things since the first AVR, although there always have been the signature bytes ready to be used. You must say what chip do you want, then the tool reads the signature and tells you: OK, or, Wrong. It's just a way to do the things. The other way is not to ask the user about the chip, the chip is what it is, discovered from the real world. There are other important things to ask the user, like the file name, memory type, offsets, etc. But we are not doing politics here. What I'm trying to show is a real usage of the new family of chips, the UPDI one, in real life. Having the same board stuffed with different chips (like 1609 and 3209) can be a real nightmare for a field technician. Until now, you could say: don't do that, just use only one type (1609 for example) but we live in a different world now.

xedbg commented 2 years ago

Indeed - you are right about the history, but its not political. It's just not a simple thing to change in the stack the way its constructed (its also the kind of thing I would look closer at around cake-time on fridays...)

Kedarius commented 1 year ago

I have a similar issuer - I am using pymcuprog API to write simple tool to check serial number, read fuses, etc... And the first step should be to check the DeviceID and branch accordingly. But I need to specify the device type beforehand. Would it be possible to at least read the DeviceID without the knowledge of the actual device?

xedbg commented 1 year ago

Hi @Kedarius I did have a look into this once on a fun-friday and found that it was quite a can-of-worms. pymcuprog started its life (and still is) a CICD test system for the curiosity nano debugger - and we always know up front what we are testing. More relevant here is that the debugger needs a comprehensive "device context" to be sent to it before it will allow a session to start (see the edbg debugger protocol). So the only way to do this (for the debugger protocol) is to select some device, try 'ping' it, examine the output, adjust accordingly and retry. For serialupdi this is hardly relevant since the debugger is not of concern... but the same mechanism could be used.

Kedarius commented 1 year ago

Hi @xedbg , thanks for prompt answer. Thanks for the insight. I would not mind to set some sort of device, ping and reconfigure. The only issue I have is that the code: backend.start_session(sessionconfig) throws an exception and therefore I can not get the actual deviceID - apart from parsing the exception string. And that is a thing I really would like to avoid. Is there any other way to get the deviceID or am I doomed to parse the exception string?

xedbg commented 1 year ago

Indeed - i see your problem... A fairly "cheap" solution might be to sub-class that exception and add a new member which contains the ID which is detected. It would still mean catching and retrying, but no parsing.

in a quick test with avriot connected and tiny1607 specified

from pymcuprog.pymcuprog_errors import PymcuprogSessionError
logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.WARNING)
from pymcuprog.backend import SessionConfig
sessionconfig = SessionConfig("attiny1607")
from pymcuprog.toolconnection import ToolUsbHidConnection
transport = ToolUsbHidConnection()
from pymcuprog.backend import Backend
backend = Backend()
backend.connect_to_tool(transport)
try:
    backend.start_session(sessionconfig)
    # Read the target device_id
    device_id = backend.read_device_id()
    print ("Device ID is {0:06X}".format(int.from_bytes(device_id, byteorder="little")))
except PymcuprogSessionError as e:
    print("It looks like you have a device with ID: {0:06X}".format(e.id))

>>> It looks like you have a device with ID: 1E9650

However - there is another caveat in that the Dx/Ex families have an additional configuration requirement, so they will fail in a different way... so perhaps its not that foolproof... (?) are you using serialUPDI or an actual hardware debugger?

Kedarius commented 1 year ago

I am using a serialUPDI as it seems to be the simplest way for now. I also have a Curiosity Nano and I am thinking about "converting it to a generic programmer" but that is for another time. For now I am working on a simple tool to see some basic info about the device and to parse the fuses, so the output would be something like this:

# ./get_info.py
Device ID is 1E970E
Signature Row is 1E970EFFA3036C0DFFFFFFFFFFFFFFFF42128189900065130133015700000000
Device ID is 1E970E
Device Serial is 42128189900065130133015700000000
Fuses are FFFFC0080000
Lockbits are 5CC5C55C
WDTCFG: 00000000
    WINDOW 0000  Watchdog Window Time-out Period
    PERIOD 0000  Watchdog Time-out Period
BODCFG: 00000000
    LVL 000  BODLEVEL0 (1.9V)
    SAMPFREQ 0  128HZ
    ACTIVE 00  DISABLE (BOD disabled)
    SLEEP 00  DISABLE (BOD disabled)
OSCCFG: 00000000
    CLKSEL 0000  OSCHF
SYSCFG0: 11000000
    CRCSRC 11  NOCRC
    CRCSEL 0  CRC16
    RSTPINCFG 00  Input
    EESAVE 0  EEPROM erased during Chip Erase
SYSCFG1: 00000000
    MVSYSCFG 01  DUAL
    SUT 000  0 ms
CODESIZE: 00000000
    CODESIZE 00000000  entire Flash is the Boot Code section
BOOTSIZE: 00000000
    BOOTSIZE 00000000  entire Flash as Boot Code section
Done

The goal is to practice python and create tool to see the status of the chip and possibly change single fuse field. And I plan to publish it here on github once it is at least a little presentable :-) I am focusing on DA/DB family only - at least for now

xedbg commented 1 year ago

OK, got it. Since your goal is also to practise Python, it might be worth looking at importing parts of pymcuprog that you can use rather than the entire backend. At least it should be possible to make a "probe" function which can read the device SIB or ID - for example: from pymcuprog.serialupdi.application import UpdiApplication and build your own stack. Also: pymcuprog includes only basic device info - if you want to start decoding fuses, for example, I hope you make it data-driven using the data in the .ATDF files in the DFPs! (I think the correct mechanism would be to read the family ID from the SIB first (tiny, mega or AVR), then the device ID) I was looking at making a quick patch release before vacation here (just to fix a dependency issue), but I won't be able to include any new development work anyway. (Not many developers around to review pull-requests at the moment anyway ;)

Kedarius commented 1 year ago

Thanks for the response. I will look more into backend internals :-) For now I've created simple JSON file with fuses description. Parsing .ATDF files seems like a bit overkill but I may look into it.

Kedarius commented 1 year ago

Correction: When I dug a little bit deeper into my code, I noticed that the exception is being thrown by backend.read_device_id() not the backend.start_session(). So I can start the session with wrong device and then execute the code found in the read_device_id() minus the deviceId check:

detection_updi=detection_backend.programmer.get_device_model()
signatures_base = detection_updi.dut.sigrow_address
# Read 3 bytes
detected_deviceId = detection_updi.avr.read_data(signatures_base, 3)