mirror of
https://github.com/avrdudes/avrdude.git
synced 2026-06-02 09:46:34 +03:00
468 lines
18 KiB
Python
468 lines
18 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#
|
|
# avrdude - A Downloader/Uploader for AVR device programmers
|
|
# Copyright (C) 2024 MX682X
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
# this is a small python skript that tries to find the PICkit5_TP folder
|
|
# created by MPLAB X that contains the scripts.xml file.
|
|
# Works on Windows and Linux (MacOS untested), as long as the default installation
|
|
# folder was not changed. If it was changed, and the program is unable to locate
|
|
# the scripts.xml file, you should be promted to enter a path. You can
|
|
# either provide the path to the file, or to the directory providing the file.
|
|
#
|
|
# The idea behind this program is to extract the sub-programs defined in the
|
|
# script.xml. The original file has a size of about 300MB, containing a lot of
|
|
# redundant information (like the XML tags) as well as sub-programs for chips
|
|
# avrdude doesn't support, like ARM MCUs.
|
|
# This python script goes through all functions, removing identical ones,
|
|
# and indexes those. The index is then used to connect the MCUs with those functions
|
|
# so that the correct array pointers can be loaded.
|
|
# Warning: If you run this python program, the previously generated files will be
|
|
# overwritten without warning.
|
|
#
|
|
|
|
import os, fnmatch, mmap
|
|
from pathlib import Path
|
|
|
|
|
|
# The list of functions, as a Python Dictionary, that will be used by avr-dude
|
|
c_func_list = [
|
|
# Started with UPDI
|
|
"EnterProgMode",
|
|
"EnterProgModeHvSp", # High Voltage Pulse on UPDI line
|
|
"EnterProgModeHvSpRst", # High Voltage Pulse on Reset Pin
|
|
"EnterProgModeHvUpt", # User Power Toggle
|
|
"ExitProgMode",
|
|
"SetSpeed",
|
|
"GetDeviceID",
|
|
"EraseChip",
|
|
"WriteProgmem",
|
|
"ReadProgmem",
|
|
"WriteDataEEmem",
|
|
"ReadDataEEmem",
|
|
"WriteCSreg",
|
|
"ReadCSreg",
|
|
"WriteMem8",
|
|
"ReadMem8",
|
|
"WriteConfigmem",
|
|
"WriteConfigmemFuse",
|
|
"WriteConfigmemLock",
|
|
"ReadConfigmem",
|
|
"ReadConfigmemFuse",
|
|
"ReadConfigmemLock",
|
|
"WriteIDmem",
|
|
"ReadIDmem",
|
|
"ReadSIB",
|
|
|
|
# Added from dW
|
|
"switchtoISP",
|
|
#"ReadMemIO",
|
|
#"WriteMemIO",
|
|
|
|
# Added from ISP
|
|
"ReadCalibrationByte",
|
|
|
|
# Added from JTAG/PDI
|
|
#"WriteSRAM", # Is a duplicate of WriteMem8
|
|
#"ReadSRAM",
|
|
"WriteBootMem",
|
|
"ReadBootMem",
|
|
]
|
|
|
|
# List of MCUs Names that are not supported by avrdude
|
|
mcu_to_exclude = [
|
|
"ATA5700M322", "ATA5702M322", "ATA5782", "ATA5787", "ATA5831", "ATA5835", "ATA8210", "ATA8510",
|
|
"ATtiny416auto", "AVR16DV14", "AVR16DV20"
|
|
]
|
|
|
|
|
|
import platform
|
|
|
|
work_dir = os.path.abspath(os.getcwd())
|
|
cache_dir = os.path.join(work_dir, "scripts_cache")
|
|
|
|
print(work_dir)
|
|
print(cache_dir)
|
|
|
|
|
|
|
|
# Beginning of C and H Files
|
|
common_header = \
|
|
'''\
|
|
/* This file was auto-generated by scripts_decoder.py.
|
|
* Any changes will be overwritten on regeneration
|
|
*/
|
|
|
|
/*
|
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
|
* Copyright (C) 2024 MX682X
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
'''
|
|
|
|
# generates the h-file. generates the struct definition
|
|
def generate_h_file(c_funcs, file_dir):
|
|
h_header = \
|
|
'''
|
|
#ifndef pickit5_lut_h
|
|
#define pickit5_lut_h
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
struct avr_script_lut {
|
|
'''
|
|
|
|
h_trailer = \
|
|
'''
|
|
};
|
|
|
|
typedef struct avr_script_lut SCRIPT;
|
|
const unsigned char *get_devid_script_by_nvm_ver(unsigned char version);
|
|
int get_pickit_dw_script(SCRIPT *scr, const char *partdesc);
|
|
int get_pickit_isp_script(SCRIPT *scr, const char *partdesc);
|
|
int get_pickit_jtag_script(SCRIPT *scr, const char *partdesc);
|
|
int get_pickit_updi_script(SCRIPT *scr, const char *partdesc);
|
|
int get_pickit_pdi_script(SCRIPT *scr, const char *partdesc);
|
|
int get_pickit_tpi_script(SCRIPT *scr, const char *partdesc);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif // pickit5_lut_h
|
|
'''
|
|
global common_header
|
|
if file_dir is None:
|
|
return
|
|
|
|
h_lut_path = os.path.join(file_dir, "pickit5_lut.h") # first - handle defining the structure
|
|
if (os.path.exists(h_lut_path)):
|
|
os.remove(h_lut_path)
|
|
|
|
with open(h_lut_path, 'w') as h_file:
|
|
h_file.write(common_header)
|
|
h_file.write(h_header)
|
|
for func_name in c_funcs:
|
|
h_file.write(" const unsigned char *{0};\n unsigned int {0}_len;\n".format(func_name))
|
|
h_file.write(h_trailer)
|
|
print("h-File generated")
|
|
#EOF
|
|
|
|
|
|
|
|
|
|
# Tries to locate the xml file in a known location
|
|
def find_xml():
|
|
home_dir = str(Path.home())
|
|
print("Home Path: {0}".format(home_dir))
|
|
if home_dir == None:
|
|
return
|
|
home_dir = os.path.join(home_dir, ".mchp_packs", "Microchip")
|
|
home_dir_A = os.path.join(home_dir, "PICkit5_TP")
|
|
result = []
|
|
for root, dirs, files in os.walk(home_dir_A):
|
|
for name in files:
|
|
if fnmatch.fnmatch(name, "scripts.xml"):
|
|
file_path = os.path.join(root, name)
|
|
result.append((os.path.getctime(file_path), file_path))
|
|
|
|
print("List of scripts.xml files:")
|
|
print(result)
|
|
time, path = 0, ""
|
|
for t, p in result: # find the most recent scripts file in out list
|
|
if t > time:
|
|
time, path = t, p
|
|
return path
|
|
# EOF
|
|
|
|
|
|
# Example of a function declaration in the XML-File
|
|
# <script>
|
|
# <function>SetSpeedFromDevice</function>
|
|
# <processor>ATSAM4LC2A</processor>
|
|
# <ri4command>0x00001504</ri4command>
|
|
# <scrbytes>
|
|
# <byte>0x3B</byte>
|
|
# <byte>0x02</byte>
|
|
# <byte>0x00</byte>
|
|
# <byte>0x00</byte>
|
|
# <byte>0x00</byte>
|
|
# </scrbytes>
|
|
# </script>
|
|
|
|
|
|
|
|
def convert_xml(xml_path, c_funcs):
|
|
if xml_path == None:
|
|
print("No Path to XML file provided")
|
|
return
|
|
|
|
program_iface = {
|
|
"UPDI": dict(),
|
|
"PDI": dict(),
|
|
"dW": dict(),
|
|
"ISP": dict(),
|
|
"TPI": dict(),
|
|
"JTAG": dict()
|
|
}
|
|
function_dict = {
|
|
"UPDI": dict(),
|
|
"PDI": dict(),
|
|
"dW": dict(),
|
|
"ISP": dict(),
|
|
"TPI": dict(),
|
|
"JTAG": dict()
|
|
}
|
|
|
|
# Prepare directories
|
|
parent_dir = os.getcwd()
|
|
src_dir = os.path.join(parent_dir, "src")
|
|
print("Opening file {0}".format(xml_path))
|
|
print("Parent Dir: {0}".format(parent_dir))
|
|
print("Src directory: {0}".format(src_dir))
|
|
|
|
|
|
# create h-File (make sure to provide all function definitions)
|
|
generate_h_file(c_funcs, src_dir)
|
|
|
|
|
|
with open(xml_path, "r") as xml_script:
|
|
print ("XML File opened")
|
|
scr_bytes_buffer = bytearray(2048) # allocate 2kB of memory in advance, avoids memory managment
|
|
while True:
|
|
line = xml_script.readline() # go line by line, hopefully reducing memory usage compared to readlines()
|
|
if line == "":
|
|
break # exit when End of file
|
|
|
|
if line.startswith(" <function>"):
|
|
function = line[14:] # remove " <function>"
|
|
function = function.split("<", 2)[0] # remove trailing element
|
|
|
|
scr_header = xml_script.readlines(3) # read 3 more lines
|
|
processor = scr_header[0][15:] # remove " <processor>"
|
|
chip_name = processor.split("<", 2)[0] # remove trailing element
|
|
|
|
try:
|
|
function_name, programming_mode = function.split('_')
|
|
except:
|
|
continue # If the function did not contain any '_', continue to next line
|
|
|
|
if programming_mode not in ["UPDI", "PDI", "dW", "ISP", "TPI", "JTAG"]:
|
|
continue # Filters out "FPGA" and other edge cases
|
|
|
|
if chip_name in mcu_to_exclude:
|
|
continue # don't handle chips avrdude doesn't know anyway
|
|
|
|
if function_name not in c_funcs:
|
|
continue # Filter out debug Functions
|
|
|
|
func_bytes = None
|
|
counter = 0
|
|
while True:
|
|
byte = xml_script.readline()
|
|
if (byte.startswith(" ")): # 6 spaces is already unique enough
|
|
scr_bytes_buffer[counter] = int(byte[14:16], 16) # only handle the value
|
|
counter += 1
|
|
elif (byte.startswith(" </scrbytes>")): # done with the list
|
|
func_bytes = bytes(scr_bytes_buffer[:counter]) # create an immutable bytes array
|
|
break
|
|
|
|
if func_bytes == None or len(func_bytes) == 0:
|
|
continue # continue with next chip if somethin went wrong or is empty (SetSpeed_dw)
|
|
|
|
if function_name not in function_dict[programming_mode].keys():
|
|
function_dict[programming_mode][function_name] = []
|
|
|
|
if func_bytes not in function_dict[programming_mode][function_name]:
|
|
function_dict[programming_mode][function_name].append(func_bytes)
|
|
|
|
index = function_dict[programming_mode][function_name].index(func_bytes)
|
|
|
|
#function_dict = {
|
|
# "UPDI": {
|
|
# "EnterProgMode" : [bytes_0, bytes_1]},
|
|
# "SetSpeed" : [bytes_2, bytes_3]}
|
|
# }
|
|
#}
|
|
|
|
|
|
if chip_name not in program_iface[programming_mode]:
|
|
program_iface[programming_mode][chip_name] = [(function_name, index)]
|
|
#print("Added to " + programming_mode + ": " + chip_name) # Debugging
|
|
else:
|
|
program_iface[programming_mode][chip_name].append((function_name, index))
|
|
#program_iface = {
|
|
# "UPDI": {
|
|
# "Attiny1614": [
|
|
# ("EnterProgMode", 0),
|
|
# ("ExitProgMode", 0),
|
|
# ("...", 1),
|
|
# ]
|
|
# }
|
|
#}
|
|
# /if starts with
|
|
# /while True
|
|
# /with open
|
|
|
|
print("XML File processed")
|
|
|
|
# create c-File
|
|
global common_header
|
|
|
|
for prog_iface, prog_mcu_list in program_iface.items():
|
|
lower_prog_iface = prog_iface.lower()
|
|
c_lut_path = os.path.join(src_dir, "pickit5_lut_" + lower_prog_iface + ".c")
|
|
if (os.path.exists(c_lut_path)):
|
|
os.remove(c_lut_path)
|
|
with open(c_lut_path, 'w') as c_file:
|
|
c_file.write(common_header)
|
|
c_file.write("#include <ac_cfg.h>\n")
|
|
c_file.write("#include <stddef.h>\n")
|
|
c_file.write("#include <string.h>\n")
|
|
c_file.write("#include \"pickit5_lut.h\"\n\n\n")
|
|
|
|
struct_init_func = ""
|
|
struct_init_len = ""
|
|
common_func = [] # List of Functions that exist once
|
|
|
|
for (func_name, func_array_bytes) in function_dict[prog_iface].items():
|
|
for (array_iter, func_bytes) in enumerate(func_array_bytes):
|
|
func_length = len(func_bytes)
|
|
c_file.write("const unsigned char {0}_{1}_{2}[{3}]".format(
|
|
func_name, lower_prog_iface, array_iter, func_length) + " = {")
|
|
num_line = ""
|
|
for (iter, byte) in enumerate(func_bytes): # go through every byte
|
|
if (iter % 16 == 0):
|
|
c_file.write(num_line) # new line after 16 bytes
|
|
num_line = "\n "
|
|
num_line += " 0x{0:02x},".format(byte) # and generate String
|
|
c_file.write(num_line + "\n};\n\n") # complete array
|
|
|
|
if len(func_array_bytes) == 1: # look for common function
|
|
if (prog_iface == "JTAG"): # This handles the edge case in JTAG where only the
|
|
if (func_name == "ReadConfigmem") or (func_name == "WriteConfigmem"):
|
|
continue # XMEGA has the functions, but not the old JTAG
|
|
common_func.append(func_name)
|
|
struct_init_func += f" scr->{func_name} = {func_name}_{lower_prog_iface}_0;\n"
|
|
struct_init_len += f" scr->{func_name}_len = sizeof({func_name}_{lower_prog_iface}_0);\n"
|
|
|
|
# EOFL
|
|
|
|
|
|
c_file.write(f"\n\n\nstatic void pickit_{lower_prog_iface}_script_init(SCRIPT *scr);\n") # declaration
|
|
c_file.write(f"static void pickit_{lower_prog_iface}_script_init(SCRIPT *scr)" + " {\n") # definition
|
|
c_file.write(" memset(scr, 0x00, sizeof(SCRIPT)); // Make sure everything is NULL\n\n")
|
|
c_file.write(struct_init_func)
|
|
c_file.write("\n") # improve readability
|
|
c_file.write(struct_init_len)
|
|
c_file.write("}\n\n\n")
|
|
|
|
c_file.write(f"const char * const pickit5_{lower_prog_iface}_chip_lut[]" + " = {")
|
|
chip_line = ""
|
|
for (iter, chip_name) in enumerate(prog_mcu_list.keys()): # go through every chip
|
|
if (iter % 8 == 0):
|
|
c_file.write(chip_line) # new line after 8 Chips
|
|
chip_line = "\n "
|
|
chip_line += "{0:>17},".format( '"' + chip_name + '"') # and generate String
|
|
c_file.write(chip_line + "\n};\n\n") # complete array
|
|
|
|
|
|
if (prog_iface == "UPDI"):
|
|
c_file.write("const unsigned char *get_devid_script_by_nvm_ver(unsigned char version) {\n")
|
|
c_file.write(" if(version >= '0') version -= '0'; // Allow chars\n")
|
|
c_file.write(" if(version > 9) return NULL; // Not a valid number\n")
|
|
c_file.write(" if(version <= 3) // Tiny, mega, DA, DB, DD, EA\n")
|
|
c_file.write(" return GetDeviceID_updi_0;\n")
|
|
c_file.write(" else // DU, EB\n")
|
|
c_file.write(" return GetDeviceID_updi_1;\n}\n\n")
|
|
|
|
c_file.write("int get_pickit_{0}_script(SCRIPT *scr, const char *partdesc)".format(lower_prog_iface) + " {\n")
|
|
c_file.write(" if((scr == NULL) || (partdesc == NULL)) {\n return -1;\n }\n")
|
|
c_file.write(" int namepos = -1;\n")
|
|
c_file.write(" for(int i = 0; i < {0}; i++)".format(len(prog_mcu_list.keys())) + " {\n")
|
|
c_file.write(" if(strcmp(pickit5_{0}_chip_lut[i], partdesc) == 0)".format(lower_prog_iface) + " {\n")
|
|
c_file.write(" namepos = i;\n break;\n }\n }\n")
|
|
c_file.write(" if(namepos == -1) {\n return -2;\n }\n\n")
|
|
c_file.write(" pickit_{0}_script_init(scr); // Load common functions\n\n".format(lower_prog_iface))
|
|
|
|
case_list = []
|
|
case_func_list = []
|
|
#print(common_func)
|
|
c_file.write(" switch (namepos) {\n")
|
|
for (switch_iterator, (chip_name, functions)) in enumerate(prog_mcu_list.items()):
|
|
new_case = " case {0}: /* {1} */\n".format(switch_iterator, chip_name)
|
|
new_func_str = ""
|
|
|
|
for func_name, func_num in functions: # generate list of unique function assignments
|
|
if func_name in common_func:
|
|
continue # skip common functions, they were set in _init()
|
|
new_func_str += "{0}_{1}_{2}\n".format(func_name, lower_prog_iface, func_num)
|
|
|
|
|
|
if new_func_str not in case_func_list: # Check if there is an already existing function set
|
|
case_list.append(new_case)
|
|
case_func_list.append(new_func_str)
|
|
else: # if it exists, figure out the index at which to add the case
|
|
case_list[case_func_list.index(new_func_str)] += new_case
|
|
|
|
for (case_str, func_list) in zip(case_list, case_func_list):
|
|
c_file.write(case_str)
|
|
func_list = func_list.split("\n")[:-1] # Remove last Element that will be an empty string
|
|
for func in func_list:
|
|
func_name = func.split("_")[0]
|
|
c_file.write(f" scr->{func_name} = {func};\n")
|
|
c_file.write(f" scr->{func_name}_len = sizeof({func});\n")
|
|
c_file.write(" break;\n")
|
|
|
|
c_file.write(" }\n return namepos;\n}")
|
|
# End of switch case
|
|
print("finished " + prog_iface)
|
|
|
|
print("c-File generated")
|
|
|
|
|
|
|
|
xml_path = find_xml()
|
|
if xml_path == None:
|
|
print("Unable to find scripts.xml in the default location.")
|
|
print("Please Enter a Path to the File or Directory:")
|
|
xml_path = input(">")
|
|
if (os.path.isdir(xml_path)):
|
|
os.path.join(xml_path, "scripts.xml")
|
|
if (os.path.exists(xml_path) == False):
|
|
print("File not found, exiting")
|
|
quit()
|
|
convert_xml(xml_path, c_func_list)
|
|
quit()
|