mirror of
https://github.com/MikronMIK32/mik32-uploader.git
synced 2026-01-01 21:37:05 +03:00
check firmware feature in mik32_check
This commit is contained in:
parent
ef7297f203
commit
b8bd3e876f
108
hex_parser.py
Normal file
108
hex_parser.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
from enum import Enum
|
||||||
|
import os
|
||||||
|
from typing import List, NamedTuple, Union
|
||||||
|
|
||||||
|
from parsers import Record, RecordType, parse_line
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryType(Enum):
|
||||||
|
BOOT = 0
|
||||||
|
EEPROM = 1
|
||||||
|
RAM = 2
|
||||||
|
SPIFI = 3
|
||||||
|
UNKNOWN = -1
|
||||||
|
|
||||||
|
|
||||||
|
class MemorySection(NamedTuple):
|
||||||
|
type: MemoryType
|
||||||
|
offset: int
|
||||||
|
length: int # Memory section length in bytes
|
||||||
|
|
||||||
|
|
||||||
|
class Segment:
|
||||||
|
offset: int
|
||||||
|
memory: Union[MemorySection, None] = None
|
||||||
|
data: List[int]
|
||||||
|
|
||||||
|
def __init__(self, offset: int, data: List[int], sections: List[MemorySection]):
|
||||||
|
self.offset = offset
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
self._locate_memory_section(sections)
|
||||||
|
|
||||||
|
def _locate_memory_section(self, sections: List[MemorySection]):
|
||||||
|
for section in sections:
|
||||||
|
if self._belongs_memory_section(section, self.offset):
|
||||||
|
self.memory = section
|
||||||
|
|
||||||
|
if self.memory is None:
|
||||||
|
raise Exception(
|
||||||
|
f"ERROR: segment with offset {self.offset:#0x} doesn't belong to any section")
|
||||||
|
|
||||||
|
if (self.offset + self.data.__len__()) > (self.memory.offset + self.memory.length):
|
||||||
|
raise Exception(
|
||||||
|
f"ERROR: segment with offset {self.offset:#0x} "
|
||||||
|
f"and length {self.data.__len__()} "
|
||||||
|
f"overflows section {self.memory.type.name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _belongs_memory_section(self, memory_section: MemorySection, offset: int) -> bool:
|
||||||
|
if offset < memory_section.offset:
|
||||||
|
return False
|
||||||
|
if offset >= (memory_section.offset + memory_section.length):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
supported_text_formats = [".hex"]
|
||||||
|
|
||||||
|
|
||||||
|
class FirmwareFile:
|
||||||
|
file_name: str
|
||||||
|
file_extension: str
|
||||||
|
segments: List[Segment] = []
|
||||||
|
|
||||||
|
def __init__(self, path: str, sections: List[MemorySection]):
|
||||||
|
self.file_name, self.file_extension = os.path.splitext(path)
|
||||||
|
|
||||||
|
if self.file_extension in supported_text_formats:
|
||||||
|
with open(path) as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
self._parse_hex(lines, sections)
|
||||||
|
elif self.file_extension == ".bin":
|
||||||
|
with open(path, "rb") as f:
|
||||||
|
bin_content = list(f.read())
|
||||||
|
self.segments.append(Segment(offset=0, data=bin_content, sections=sections))
|
||||||
|
else:
|
||||||
|
raise Exception(f"Unsupported file format: {self.file_extension}")
|
||||||
|
|
||||||
|
def _parse_hex(self, lines: List[str], sections: List[MemorySection]):
|
||||||
|
segments: List[Segment] = []
|
||||||
|
|
||||||
|
lba: int = 0 # Linear Base Address
|
||||||
|
expect_address = 0 # Address of the next byte
|
||||||
|
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
record: Record = parse_line(line, i, self.file_extension)
|
||||||
|
if record.type == RecordType.DATA:
|
||||||
|
drlo: int = record.address # Data Record Load Offset
|
||||||
|
if (expect_address != lba+drlo) or (segments.__len__() == 0):
|
||||||
|
expect_address = lba+drlo
|
||||||
|
segments.append(Segment(
|
||||||
|
offset=expect_address, data=[], sections=sections))
|
||||||
|
|
||||||
|
for byte in record.data:
|
||||||
|
segments[-1].data.append(byte)
|
||||||
|
expect_address += 1
|
||||||
|
elif record.type == RecordType.EXTADDR:
|
||||||
|
lba = record.address
|
||||||
|
elif record.type == RecordType.LINEARSTARTADDR:
|
||||||
|
print(f"Start Linear Address: {record.address:#10x}", )
|
||||||
|
elif record.type == RecordType.EOF:
|
||||||
|
break
|
||||||
|
|
||||||
|
self.segments.extend(segments)
|
||||||
|
|
||||||
|
def get_segments(self) -> List[Segment]:
|
||||||
|
return self.segments
|
||||||
275
mik32_check.py
Normal file
275
mik32_check.py
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from typing import List, Union
|
||||||
|
|
||||||
|
from PyInstaller import DEFAULT_WORKPATH
|
||||||
|
from mik32_pm import pm_init
|
||||||
|
from mik32_upload import BootMode, Pages, form_pages, openocd_exec_path, openocd_scripts_path, openocd_interface_path, openocd_target_path, adapter_default_speed, run_openocd, default_post_action, default_log_path, default_openocd_host, mik32v0_sections, OpenOCDStartupException, adapter_speed_not_supported, memory_page_size
|
||||||
|
from mik32_gpio import MIK32_Version, gpio_init, gpio_deinit
|
||||||
|
import mik32_eeprom
|
||||||
|
import mik32_spifi
|
||||||
|
import mik32_ram
|
||||||
|
from hex_parser import FirmwareFile, MemoryType, Segment
|
||||||
|
from tclrpc import OpenOcdTclRpc, TclException
|
||||||
|
|
||||||
|
|
||||||
|
def upload_file(
|
||||||
|
filename: str,
|
||||||
|
host: str = '127.0.0.1',
|
||||||
|
port: int = OpenOcdTclRpc.DEFAULT_PORT,
|
||||||
|
is_run_openocd=False,
|
||||||
|
use_quad_spi=False,
|
||||||
|
openocd_exec=openocd_exec_path,
|
||||||
|
openocd_scripts=openocd_scripts_path,
|
||||||
|
openocd_interface=openocd_interface_path,
|
||||||
|
openocd_target=openocd_target_path,
|
||||||
|
adapter_speed=adapter_default_speed,
|
||||||
|
is_open_console=False,
|
||||||
|
boot_mode=BootMode.UNDEFINED,
|
||||||
|
log_path=DEFAULT_WORKPATH,
|
||||||
|
post_action=default_post_action,
|
||||||
|
mik_version=MIK32_Version.MIK32V2
|
||||||
|
) -> int:
|
||||||
|
"""
|
||||||
|
Write ihex or binary file into MIK32 EEPROM or external flash memory
|
||||||
|
@filename: full path to the file with hex or bin file format
|
||||||
|
@return: return 0 if successful, 1 if failed
|
||||||
|
"""
|
||||||
|
|
||||||
|
print(f"Using {mik_version.value}")
|
||||||
|
|
||||||
|
result = 0
|
||||||
|
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
print(f"ERROR: File {filename} does not exist")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
file = FirmwareFile(filename, mik32v0_sections)
|
||||||
|
|
||||||
|
segments: List[Segment] = file.get_segments()
|
||||||
|
pages: Pages = form_pages(segments, boot_mode)
|
||||||
|
|
||||||
|
proc: Union[subprocess.Popen, None] = None
|
||||||
|
if is_run_openocd:
|
||||||
|
try:
|
||||||
|
logging.debug("OpenOCD try start!")
|
||||||
|
|
||||||
|
proc = run_openocd(openocd_exec, openocd_scripts,
|
||||||
|
openocd_interface, openocd_target, is_open_console)
|
||||||
|
|
||||||
|
logging.debug("OpenOCD started!")
|
||||||
|
|
||||||
|
except OSError as e:
|
||||||
|
raise OpenOCDStartupException(e)
|
||||||
|
try:
|
||||||
|
with OpenOcdTclRpc(host, port) as openocd:
|
||||||
|
if (all(openocd_interface.find(i) == -1 for i in adapter_speed_not_supported)):
|
||||||
|
openocd.run(f"adapter speed {adapter_speed}")
|
||||||
|
openocd.run(f"log_output \"{log_path}\"")
|
||||||
|
openocd.run(f"debug_level 1")
|
||||||
|
|
||||||
|
logging.debug("OpenOCD configured!")
|
||||||
|
|
||||||
|
pm_init(openocd)
|
||||||
|
|
||||||
|
logging.debug("PM configured!")
|
||||||
|
|
||||||
|
if (pages.pages_eeprom.__len__() > 0):
|
||||||
|
start_time = time.perf_counter()
|
||||||
|
|
||||||
|
result |= mik32_eeprom.check_pages(
|
||||||
|
pages.pages_eeprom, openocd)
|
||||||
|
|
||||||
|
write_time = time.perf_counter() - start_time
|
||||||
|
write_size = pages.pages_eeprom.__len__(
|
||||||
|
) * memory_page_size[MemoryType.EEPROM]
|
||||||
|
print(
|
||||||
|
f"Check {write_size} bytes in {write_time:.2f} seconds (effective {(write_size/(write_time*1024)):.1f} kbyte/s)")
|
||||||
|
if (pages.pages_spifi.__len__() > 0):
|
||||||
|
gpio_init(openocd, mik_version)
|
||||||
|
start_time = time.perf_counter()
|
||||||
|
|
||||||
|
result |= mik32_spifi.check_pages(
|
||||||
|
pages.pages_spifi, openocd, use_quad_spi=use_quad_spi)
|
||||||
|
|
||||||
|
write_time = time.perf_counter() - start_time
|
||||||
|
write_size = pages.pages_spifi.__len__(
|
||||||
|
) * memory_page_size[MemoryType.SPIFI]
|
||||||
|
print(
|
||||||
|
f"Check {write_size} bytes in {write_time:.2f} seconds (effective {(write_size/(write_time*1024)):.1f} kbyte/s)")
|
||||||
|
gpio_deinit(openocd, mik_version)
|
||||||
|
|
||||||
|
segments_ram = list(filter(
|
||||||
|
lambda segment: (segment.memory is not None) and (segment.memory.type == MemoryType.RAM), segments))
|
||||||
|
if (segments_ram.__len__() > 0):
|
||||||
|
mik32_ram.check_segments(segments_ram, openocd)
|
||||||
|
result |= 0
|
||||||
|
|
||||||
|
openocd.run(post_action)
|
||||||
|
except ConnectionRefusedError:
|
||||||
|
print("ERROR: The connection to OpenOCD is not established. Check the settings and connection of the debugger")
|
||||||
|
except TclException as e:
|
||||||
|
print(f"ERROR: TclException {e.code} \n {e.msg}")
|
||||||
|
finally:
|
||||||
|
if proc is not None:
|
||||||
|
proc.kill()
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def createParser():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog='mik32_upload.py',
|
||||||
|
description='''Скрипт предназначен для записи программы в ОЗУ, EEPROM и внешнюю flash память,
|
||||||
|
подключенную по интерфейсу SPIFI'''
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'filepath',
|
||||||
|
nargs='?',
|
||||||
|
help='Путь к файлу прошивки'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--run-openocd',
|
||||||
|
dest='run_openocd',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='Запуск openocd при прошивке МК'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--use-quad-spi',
|
||||||
|
dest='use_quad_spi',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='Использование режима QuadSPI при программировании внешней флеш памяти'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--openocd-host',
|
||||||
|
dest='openocd_host',
|
||||||
|
default=default_openocd_host,
|
||||||
|
help=f"Адрес для подключения к openocd. По умолчанию: {default_openocd_host}"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--openocd-port',
|
||||||
|
dest='openocd_port',
|
||||||
|
default=OpenOcdTclRpc.DEFAULT_PORT,
|
||||||
|
help=f"Порт tcl сервера openocd. По умолчанию: {OpenOcdTclRpc.DEFAULT_PORT}"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--adapter-speed',
|
||||||
|
dest='adapter_speed',
|
||||||
|
default=adapter_default_speed,
|
||||||
|
help=f"Скорость отладчика в кГц. По умолчанию: {adapter_default_speed}"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--openocd-exec',
|
||||||
|
dest='openocd_exec',
|
||||||
|
default=openocd_exec_path,
|
||||||
|
help=f"Путь к исполняемому файлу openocd. По умолчанию: {openocd_exec_path}"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--openocd-scripts',
|
||||||
|
dest='openocd_scripts',
|
||||||
|
default=openocd_scripts_path,
|
||||||
|
help=f"Путь к папке scripts. По умолчанию: {openocd_scripts_path}"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--openocd-interface',
|
||||||
|
dest='openocd_interface',
|
||||||
|
default=openocd_interface_path,
|
||||||
|
help='Путь к файлу конфигурации отладчика относительно папки scripts или абсолютный путь. '
|
||||||
|
f"По умолчанию: {openocd_interface_path}"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--openocd-target',
|
||||||
|
dest='openocd_target',
|
||||||
|
default=openocd_target_path,
|
||||||
|
help='Путь к файлу конфигурации целевого контроллера относительно папки scripts. '
|
||||||
|
f"По умолчанию: {openocd_target_path}"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--open-console',
|
||||||
|
dest='open_console',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='Открывать OpenOCD в отдельной консоли'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'-b',
|
||||||
|
'--boot-mode',
|
||||||
|
dest='boot_mode',
|
||||||
|
type=BootMode,
|
||||||
|
choices=list(BootMode),
|
||||||
|
default=BootMode.UNDEFINED,
|
||||||
|
help="Выбор типа памяти, который отображается на загрузочную область. "
|
||||||
|
"Если тип не выбран, данные, находящиеся в загрузочной области в hex файле отбрасываются. "
|
||||||
|
f"По умолчанию: {BootMode.UNDEFINED}"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--log-path',
|
||||||
|
dest='log_path',
|
||||||
|
default=default_log_path,
|
||||||
|
help=f"Путь к файлу журнала. По умолчанию: {default_log_path}"
|
||||||
|
)
|
||||||
|
# parser.add_argument(
|
||||||
|
# '--log-terminal',
|
||||||
|
# dest='log_termir',
|
||||||
|
# action='store_true',
|
||||||
|
# default=False,
|
||||||
|
# help='Вывод журнала в консоль'
|
||||||
|
# )
|
||||||
|
parser.add_argument(
|
||||||
|
'--post-action',
|
||||||
|
dest='post_action',
|
||||||
|
default=default_post_action,
|
||||||
|
help=f"Команды OpenOCD, запускаемые после прошивки. По умолчанию: {default_post_action}"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--no-color',
|
||||||
|
dest='no_color',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='Вывод без последовательностей управления терминалом. Временно не используется'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'-t',
|
||||||
|
'--mcu-type',
|
||||||
|
dest='mcu_type',
|
||||||
|
type=MIK32_Version,
|
||||||
|
choices=list(MIK32_Version),
|
||||||
|
default=MIK32_Version.MIK32V2,
|
||||||
|
help="Выбор микроконтроллера. "
|
||||||
|
f"По умолчанию: {MIK32_Version.MIK32V2}"
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
|
||||||
|
|
||||||
|
parser = createParser()
|
||||||
|
namespace = parser.parse_args()
|
||||||
|
|
||||||
|
if namespace.filepath:
|
||||||
|
upload_file(
|
||||||
|
namespace.filepath,
|
||||||
|
host=namespace.openocd_host,
|
||||||
|
port=namespace.openocd_port,
|
||||||
|
is_run_openocd=namespace.run_openocd,
|
||||||
|
use_quad_spi=namespace.use_quad_spi,
|
||||||
|
openocd_exec=namespace.openocd_exec,
|
||||||
|
openocd_scripts=namespace.openocd_scripts,
|
||||||
|
openocd_interface=namespace.openocd_interface,
|
||||||
|
openocd_target=namespace.openocd_target,
|
||||||
|
adapter_speed=namespace.adapter_speed,
|
||||||
|
is_open_console=namespace.open_console,
|
||||||
|
boot_mode=namespace.boot_mode,
|
||||||
|
log_path=namespace.log_path,
|
||||||
|
post_action=namespace.post_action,
|
||||||
|
mik_version=namespace.mcu_type
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print("Nothing to check")
|
||||||
@ -246,6 +246,30 @@ def write_words(words: List[int], openocd: OpenOcdTclRpc, write_by_word=False, r
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def check_pages(pages: Dict[int, List[int]], openocd: OpenOcdTclRpc) -> int:
|
||||||
|
openocd.halt()
|
||||||
|
eeprom_sysinit(openocd)
|
||||||
|
# configure cycles duration
|
||||||
|
eeprom_configure_cycles(openocd, 1, 3, 1, 100000, 1000)
|
||||||
|
time.sleep(0.1)
|
||||||
|
print("EEPROM checking...", flush=True)
|
||||||
|
|
||||||
|
pages_offsets = list(pages)
|
||||||
|
|
||||||
|
for index, page_offset in enumerate(pages_offsets):
|
||||||
|
page_words = bytes2words(pages[page_offset])
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Check page {page_offset:#06x}... {(index*100)//pages_offsets.__len__()}%", flush=True)
|
||||||
|
|
||||||
|
if eeprom_check_data(openocd, page_words, page_offset, False):
|
||||||
|
print("Page mismatch!", flush=True)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print("EEPROM page check completed", flush=True)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def write_pages(pages: Dict[int, List[int]], openocd: OpenOcdTclRpc) -> int:
|
def write_pages(pages: Dict[int, List[int]], openocd: OpenOcdTclRpc) -> int:
|
||||||
openocd.halt()
|
openocd.halt()
|
||||||
eeprom_sysinit(openocd)
|
eeprom_sysinit(openocd)
|
||||||
|
|||||||
@ -5,7 +5,7 @@ from enum import Enum
|
|||||||
from tclrpc import OpenOcdTclRpc
|
from tclrpc import OpenOcdTclRpc
|
||||||
|
|
||||||
|
|
||||||
class MIK_VERSION(Enum):
|
class MIK32_Version(Enum):
|
||||||
MIK32V0 = "MIK32V0"
|
MIK32V0 = "MIK32V0"
|
||||||
MIK32V2 = "MIK32V2"
|
MIK32V2 = "MIK32V2"
|
||||||
|
|
||||||
@ -41,12 +41,12 @@ class PAD_CONFIG_REGS_V2(Enum):
|
|||||||
port2_value = 0
|
port2_value = 0
|
||||||
|
|
||||||
|
|
||||||
def gpio_init(openocd: OpenOcdTclRpc, version: MIK_VERSION):
|
def gpio_init(openocd: OpenOcdTclRpc, version: MIK32_Version):
|
||||||
|
|
||||||
port2_addr = 0
|
port2_addr = 0
|
||||||
if version == MIK_VERSION.MIK32V0:
|
if version == MIK32_Version.MIK32V0:
|
||||||
port2_addr = PAD_CONFIG_BASE_ADDRESS + PAD_CONFIG_REGS_V0.PORT_2_CFG.value
|
port2_addr = PAD_CONFIG_BASE_ADDRESS + PAD_CONFIG_REGS_V0.PORT_2_CFG.value
|
||||||
elif version == MIK_VERSION.MIK32V2:
|
elif version == MIK32_Version.MIK32V2:
|
||||||
port2_addr = PAD_CONFIG_BASE_ADDRESS + PAD_CONFIG_REGS_V2.PORT_2_CFG.value
|
port2_addr = PAD_CONFIG_BASE_ADDRESS + PAD_CONFIG_REGS_V2.PORT_2_CFG.value
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
@ -58,9 +58,9 @@ def gpio_init(openocd: OpenOcdTclRpc, version: MIK_VERSION):
|
|||||||
|
|
||||||
port2_value_updated &= 0xF000
|
port2_value_updated &= 0xF000
|
||||||
|
|
||||||
if version == MIK_VERSION.MIK32V0:
|
if version == MIK32_Version.MIK32V0:
|
||||||
port2_value_updated |= 0x000
|
port2_value_updated |= 0x000
|
||||||
elif version == MIK_VERSION.MIK32V2:
|
elif version == MIK32_Version.MIK32V2:
|
||||||
port2_value_updated |= 0x555
|
port2_value_updated |= 0x555
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
@ -71,11 +71,11 @@ def gpio_init(openocd: OpenOcdTclRpc, version: MIK_VERSION):
|
|||||||
openocd.write_word(port2_addr + 8, 0x0500)
|
openocd.write_word(port2_addr + 8, 0x0500)
|
||||||
|
|
||||||
|
|
||||||
def gpio_deinit(openocd: OpenOcdTclRpc, version: MIK_VERSION):
|
def gpio_deinit(openocd: OpenOcdTclRpc, version: MIK32_Version):
|
||||||
|
|
||||||
if version == MIK_VERSION.MIK32V0:
|
if version == MIK32_Version.MIK32V0:
|
||||||
port2_addr = PAD_CONFIG_BASE_ADDRESS + PAD_CONFIG_REGS_V0.PORT_2_CFG.value
|
port2_addr = PAD_CONFIG_BASE_ADDRESS + PAD_CONFIG_REGS_V0.PORT_2_CFG.value
|
||||||
elif version == MIK_VERSION.MIK32V2:
|
elif version == MIK32_Version.MIK32V2:
|
||||||
port2_addr = PAD_CONFIG_BASE_ADDRESS + PAD_CONFIG_REGS_V2.PORT_2_CFG.value
|
port2_addr = PAD_CONFIG_BASE_ADDRESS + PAD_CONFIG_REGS_V2.PORT_2_CFG.value
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|||||||
15
mik32_ram.py
15
mik32_ram.py
@ -20,3 +20,18 @@ def write_segments(segments: List[Segment], openocd: OpenOcdTclRpc):
|
|||||||
print("Writing segment %s with size %d..." % (hex(segment.offset), segment.data.__len__()))
|
print("Writing segment %s with size %d..." % (hex(segment.offset), segment.data.__len__()))
|
||||||
segment_words = bytes2words(segment.data)
|
segment_words = bytes2words(segment.data)
|
||||||
openocd.write_memory(segment.offset, 32, segment_words)
|
openocd.write_memory(segment.offset, 32, segment_words)
|
||||||
|
|
||||||
|
|
||||||
|
def check_segments(segments: List[Segment], openocd: OpenOcdTclRpc) -> int:
|
||||||
|
openocd.halt()
|
||||||
|
for segment in segments:
|
||||||
|
print("Checking segment %s with size %d..." % (hex(segment.offset), segment.data.__len__()))
|
||||||
|
segment_words = bytes2words(segment.data)
|
||||||
|
segment_memory_words = openocd.read_memory(segment.offset, 32, len(segment_words))
|
||||||
|
|
||||||
|
for i in range(len(segment_words)):
|
||||||
|
if segment_words[i] != segment_memory_words[i]:
|
||||||
|
print(f"Word [{i}] expect {segment_words[i]} != read {segment_memory_words[i]} in segment {segment.offset}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|||||||
@ -519,6 +519,82 @@ def get_segments_list(pages_offsets: List[int], segment_size: int) -> List[int]:
|
|||||||
return list(segments)
|
return list(segments)
|
||||||
|
|
||||||
|
|
||||||
|
def check_pages(pages: Dict[int, List[int]], openocd: OpenOcdTclRpc, use_quad_spi=False, use_chip_erase=False):
|
||||||
|
result = 0
|
||||||
|
|
||||||
|
openocd.halt()
|
||||||
|
spifi_init(openocd)
|
||||||
|
|
||||||
|
JEDEC_ID = spifi_send_command(openocd, 0x9F, SPIFI_Frameform.OPCODE_NOADDR, SPIFI_Fieldform.ALL_SERIAL, 3)
|
||||||
|
|
||||||
|
print(f"JEDEC_ID {JEDEC_ID[0]:02x} {JEDEC_ID[1]:02x} {JEDEC_ID[2]:02x}")
|
||||||
|
|
||||||
|
dma = DMA(openocd)
|
||||||
|
dma.init()
|
||||||
|
|
||||||
|
dma.channels[0].write_buffer = 0
|
||||||
|
|
||||||
|
dma.channels[0].channel = ChannelIndex.CHANNEL_0
|
||||||
|
dma.channels[0].priority = ChannelPriority.VERY_HIGH
|
||||||
|
|
||||||
|
dma.channels[0].read_mode = ChannelMode.MEMORY
|
||||||
|
dma.channels[0].read_increment = ChannelIncrement.ENABLE
|
||||||
|
dma.channels[0].read_size = ChannelSize.WORD
|
||||||
|
dma.channels[0].read_burst_size = 2
|
||||||
|
dma.channels[0].read_request = ChannelRequest.SPIFI_REQUEST
|
||||||
|
dma.channels[0].read_ack = ChannelAck.DISABLE
|
||||||
|
|
||||||
|
dma.channels[0].write_mode = ChannelMode.PERIPHERY
|
||||||
|
dma.channels[0].write_increment = ChannelIncrement.DISABLE
|
||||||
|
dma.channels[0].write_size = ChannelSize.WORD
|
||||||
|
dma.channels[0].write_burst_size = 2
|
||||||
|
dma.channels[0].write_request = ChannelRequest.SPIFI_REQUEST
|
||||||
|
dma.channels[0].write_ack = ChannelAck.DISABLE
|
||||||
|
|
||||||
|
dma.channels[1].write_buffer = 0
|
||||||
|
|
||||||
|
dma.channels[1].channel = ChannelIndex.CHANNEL_1
|
||||||
|
dma.channels[1].priority = ChannelPriority.VERY_HIGH
|
||||||
|
|
||||||
|
dma.channels[1].write_mode = ChannelMode.MEMORY
|
||||||
|
dma.channels[1].write_increment = ChannelIncrement.ENABLE
|
||||||
|
dma.channels[1].write_size = ChannelSize.WORD
|
||||||
|
dma.channels[1].write_burst_size = 2
|
||||||
|
dma.channels[1].write_request = ChannelRequest.SPIFI_REQUEST
|
||||||
|
dma.channels[1].write_ack = ChannelAck.DISABLE
|
||||||
|
|
||||||
|
dma.channels[1].read_mode = ChannelMode.PERIPHERY
|
||||||
|
dma.channels[1].read_increment = ChannelIncrement.DISABLE
|
||||||
|
dma.channels[1].read_size = ChannelSize.WORD
|
||||||
|
dma.channels[1].read_burst_size = 2
|
||||||
|
dma.channels[1].read_request = ChannelRequest.SPIFI_REQUEST
|
||||||
|
dma.channels[1].read_ack = ChannelAck.DISABLE
|
||||||
|
|
||||||
|
if (use_quad_spi):
|
||||||
|
print("Quad Enable")
|
||||||
|
spifi_quad_enable(openocd)
|
||||||
|
else:
|
||||||
|
spifi_quad_disable(openocd)
|
||||||
|
|
||||||
|
pages_offsets = list(pages)
|
||||||
|
|
||||||
|
for index, page_offset in enumerate(pages_offsets):
|
||||||
|
print(f"Check page {page_offset:#010x}... {(index*100)//pages_offsets.__len__()}%", flush=True)
|
||||||
|
page_bytes = pages[page_offset]
|
||||||
|
|
||||||
|
result = spifi_read_data(openocd, page_offset, 256, page_bytes, dma=dma)
|
||||||
|
|
||||||
|
if result == 1:
|
||||||
|
print("Data error")
|
||||||
|
if (use_quad_spi):
|
||||||
|
spifi_quad_disable(openocd)
|
||||||
|
return result
|
||||||
|
|
||||||
|
if result == 0:
|
||||||
|
print("SPIFI page checking completed", flush=True)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def write_pages(pages: Dict[int, List[int]], openocd: OpenOcdTclRpc, use_quad_spi=False, use_chip_erase=False):
|
def write_pages(pages: Dict[int, List[int]], openocd: OpenOcdTclRpc, use_quad_spi=False, use_chip_erase=False):
|
||||||
result = 0
|
result = 0
|
||||||
|
|
||||||
|
|||||||
141
mik32_upload.py
141
mik32_upload.py
@ -6,14 +6,16 @@ import os
|
|||||||
import time
|
import time
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import List, Dict, NamedTuple, Union
|
from typing import List, Dict, NamedTuple, Union
|
||||||
|
from hex_parser import FirmwareFile, MemorySection, MemoryType, Segment
|
||||||
from tclrpc import OpenOcdTclRpc, TclException
|
from tclrpc import OpenOcdTclRpc, TclException
|
||||||
from mik32_gpio import MIK_VERSION, gpio_init, gpio_deinit
|
from mik32_gpio import MIK32_Version, gpio_init, gpio_deinit
|
||||||
import mik32_eeprom
|
import mik32_eeprom
|
||||||
import mik32_spifi
|
import mik32_spifi
|
||||||
import mik32_ram
|
import mik32_ram
|
||||||
import mik32_pm
|
import mik32_pm
|
||||||
from mik32_parsers import *
|
from parsers import *
|
||||||
import logging, sys
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
# class bcolors(Enum):
|
# class bcolors(Enum):
|
||||||
@ -35,8 +37,6 @@ default_post_action = "reset run"
|
|||||||
|
|
||||||
adapter_default_speed = 500
|
adapter_default_speed = 500
|
||||||
|
|
||||||
supported_text_formats = [".hex"]
|
|
||||||
|
|
||||||
|
|
||||||
def test_connection():
|
def test_connection():
|
||||||
output = ""
|
output = ""
|
||||||
@ -47,24 +47,16 @@ def test_connection():
|
|||||||
logging.debug("Test connection timed out, try again")
|
logging.debug("Test connection timed out, try again")
|
||||||
output = openocd.run("capture \"reg\"")
|
output = openocd.run("capture \"reg\"")
|
||||||
|
|
||||||
|
|
||||||
if output == "":
|
if output == "":
|
||||||
raise Exception("ERROR: no regs found, check MCU connection")
|
raise Exception("ERROR: no regs found, check MCU connection")
|
||||||
|
|
||||||
|
|
||||||
class MemoryType(Enum):
|
|
||||||
BOOT = 0
|
|
||||||
EEPROM = 1
|
|
||||||
RAM = 2
|
|
||||||
SPIFI = 80
|
|
||||||
UNKNOWN = -1
|
|
||||||
|
|
||||||
|
|
||||||
memory_page_size = {
|
memory_page_size = {
|
||||||
MemoryType.EEPROM : 128,
|
MemoryType.EEPROM: 128,
|
||||||
MemoryType.SPIFI : 256
|
MemoryType.SPIFI: 256
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class BootMode(Enum):
|
class BootMode(Enum):
|
||||||
UNDEFINED = 'undefined'
|
UNDEFINED = 'undefined'
|
||||||
EEPROM = 'eeprom'
|
EEPROM = 'eeprom'
|
||||||
@ -85,12 +77,6 @@ class BootMode(Enum):
|
|||||||
return MemoryType.UNKNOWN
|
return MemoryType.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
class MemorySection(NamedTuple):
|
|
||||||
type: MemoryType
|
|
||||||
offset: int
|
|
||||||
length: int # Memory section length in bytes
|
|
||||||
|
|
||||||
|
|
||||||
mik32v0_sections: List[MemorySection] = [
|
mik32v0_sections: List[MemorySection] = [
|
||||||
MemorySection(MemoryType.BOOT, 0x0, 16 * 1024),
|
MemorySection(MemoryType.BOOT, 0x0, 16 * 1024),
|
||||||
MemorySection(MemoryType.EEPROM, 0x01000000, 8 * 1024),
|
MemorySection(MemoryType.EEPROM, 0x01000000, 8 * 1024),
|
||||||
@ -99,93 +85,6 @@ mik32v0_sections: List[MemorySection] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class Segment:
|
|
||||||
offset: int
|
|
||||||
memory: Union[MemorySection, None] = None
|
|
||||||
data: List[int]
|
|
||||||
|
|
||||||
def __init__(self, offset: int, data: List[int]):
|
|
||||||
self.offset = offset
|
|
||||||
self.data = data
|
|
||||||
|
|
||||||
self._locate_memory_section()
|
|
||||||
|
|
||||||
def _locate_memory_section(self):
|
|
||||||
for section in mik32v0_sections:
|
|
||||||
if self._belongs_memory_section(section, self.offset):
|
|
||||||
self.memory = section
|
|
||||||
|
|
||||||
if self.memory is None:
|
|
||||||
raise Exception(
|
|
||||||
f"ERROR: segment with offset {self.offset:#0x} doesn't belong to any section")
|
|
||||||
|
|
||||||
if (self.offset + self.data.__len__()) > (self.memory.offset + self.memory.length):
|
|
||||||
raise Exception(
|
|
||||||
f"ERROR: segment with offset {self.offset:#0x} "
|
|
||||||
f"and length {self.data.__len__()} "
|
|
||||||
f"overflows section {self.memory.type.name}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def _belongs_memory_section(self, memory_section: MemorySection, offset: int) -> bool:
|
|
||||||
if offset < memory_section.offset:
|
|
||||||
return False
|
|
||||||
if offset >= (memory_section.offset + memory_section.length):
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class FirmwareFile:
|
|
||||||
file_name: str
|
|
||||||
file_extension: str
|
|
||||||
file_content: Union[List[str], List[int]] = []
|
|
||||||
|
|
||||||
def __init__(self, path: str):
|
|
||||||
self.file_name, self.file_extension = os.path.splitext(path)
|
|
||||||
|
|
||||||
if self.file_extension in supported_text_formats:
|
|
||||||
with open(path) as f:
|
|
||||||
self.file_content = f.readlines()
|
|
||||||
elif self.file_extension == ".bin":
|
|
||||||
with open(path, "rb") as f:
|
|
||||||
self.file_content = list(f.read())
|
|
||||||
else:
|
|
||||||
raise Exception(f"Unsupported file format: {self.file_extension}")
|
|
||||||
|
|
||||||
def _parse_text(self) -> List[Segment]:
|
|
||||||
segments: List[Segment] = []
|
|
||||||
|
|
||||||
lba: int = 0 # Linear Base Address
|
|
||||||
expect_address = 0 # Address of the next byte
|
|
||||||
|
|
||||||
for i, line in enumerate(self.file_content):
|
|
||||||
record: Record = parse_line(line, i, self.file_extension)
|
|
||||||
if record.type == RecordType.DATA:
|
|
||||||
drlo: int = record.address # Data Record Load Offset
|
|
||||||
if (expect_address != lba+drlo) or (segments.__len__() == 0):
|
|
||||||
expect_address = lba+drlo
|
|
||||||
segments.append(Segment(
|
|
||||||
offset=expect_address, data=[]))
|
|
||||||
|
|
||||||
for byte in record.data:
|
|
||||||
segments[-1].data.append(byte)
|
|
||||||
expect_address += 1
|
|
||||||
elif record.type == RecordType.EXTADDR:
|
|
||||||
lba = record.address
|
|
||||||
elif record.type == RecordType.LINEARSTARTADDR:
|
|
||||||
print(f"Start Linear Address: {record.address:#10x}", )
|
|
||||||
elif record.type == RecordType.EOF:
|
|
||||||
break
|
|
||||||
|
|
||||||
return segments
|
|
||||||
|
|
||||||
def get_segments(self) -> List[Segment]:
|
|
||||||
if self.file_extension in supported_text_formats:
|
|
||||||
return self._parse_text()
|
|
||||||
elif self.file_extension == ".bin":
|
|
||||||
return [Segment(offset=0, data=self.file_content)]
|
|
||||||
|
|
||||||
|
|
||||||
def fill_pages_from_segment(segment: Segment, page_size: int, pages: Dict[int, List[int]]):
|
def fill_pages_from_segment(segment: Segment, page_size: int, pages: Dict[int, List[int]]):
|
||||||
if segment.memory is None:
|
if segment.memory is None:
|
||||||
return
|
return
|
||||||
@ -293,7 +192,7 @@ def upload_file(
|
|||||||
boot_mode=BootMode.UNDEFINED,
|
boot_mode=BootMode.UNDEFINED,
|
||||||
log_path=default_log_path,
|
log_path=default_log_path,
|
||||||
post_action=default_post_action,
|
post_action=default_post_action,
|
||||||
mik_version=MIK_VERSION.MIK32V2
|
mik_version=MIK32_Version.MIK32V2
|
||||||
) -> int:
|
) -> int:
|
||||||
"""
|
"""
|
||||||
Write ihex or binary file into MIK32 EEPROM or external flash memory
|
Write ihex or binary file into MIK32 EEPROM or external flash memory
|
||||||
@ -309,7 +208,7 @@ def upload_file(
|
|||||||
print(f"ERROR: File {filename} does not exist")
|
print(f"ERROR: File {filename} does not exist")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
file = FirmwareFile(filename)
|
file = FirmwareFile(filename, mik32v0_sections)
|
||||||
|
|
||||||
segments: List[Segment] = file.get_segments()
|
segments: List[Segment] = file.get_segments()
|
||||||
pages: Pages = form_pages(segments, boot_mode)
|
pages: Pages = form_pages(segments, boot_mode)
|
||||||
@ -346,8 +245,10 @@ def upload_file(
|
|||||||
pages.pages_eeprom, openocd)
|
pages.pages_eeprom, openocd)
|
||||||
|
|
||||||
write_time = time.perf_counter() - start_time
|
write_time = time.perf_counter() - start_time
|
||||||
write_size = pages.pages_eeprom.__len__() * memory_page_size[MemoryType.EEPROM]
|
write_size = pages.pages_eeprom.__len__(
|
||||||
print(f"Wrote {write_size} bytes in {write_time:.2f} seconds (effective {(write_size/(write_time*1024)):.1f} kbyte/s)")
|
) * memory_page_size[MemoryType.EEPROM]
|
||||||
|
print(
|
||||||
|
f"Wrote {write_size} bytes in {write_time:.2f} seconds (effective {(write_size/(write_time*1024)):.1f} kbyte/s)")
|
||||||
if (pages.pages_spifi.__len__() > 0):
|
if (pages.pages_spifi.__len__() > 0):
|
||||||
gpio_init(openocd, mik_version)
|
gpio_init(openocd, mik_version)
|
||||||
start_time = time.perf_counter()
|
start_time = time.perf_counter()
|
||||||
@ -356,8 +257,10 @@ def upload_file(
|
|||||||
pages.pages_spifi, openocd, use_quad_spi=use_quad_spi)
|
pages.pages_spifi, openocd, use_quad_spi=use_quad_spi)
|
||||||
|
|
||||||
write_time = time.perf_counter() - start_time
|
write_time = time.perf_counter() - start_time
|
||||||
write_size = pages.pages_spifi.__len__() * memory_page_size[MemoryType.SPIFI]
|
write_size = pages.pages_spifi.__len__(
|
||||||
print(f"Wrote {write_size} bytes in {write_time:.2f} seconds (effective {(write_size/(write_time*1024)):.1f} kbyte/s)")
|
) * memory_page_size[MemoryType.SPIFI]
|
||||||
|
print(
|
||||||
|
f"Wrote {write_size} bytes in {write_time:.2f} seconds (effective {(write_size/(write_time*1024)):.1f} kbyte/s)")
|
||||||
gpio_deinit(openocd, mik_version)
|
gpio_deinit(openocd, mik_version)
|
||||||
|
|
||||||
segments_ram = list(filter(
|
segments_ram = list(filter(
|
||||||
@ -495,11 +398,11 @@ def createParser():
|
|||||||
'-t',
|
'-t',
|
||||||
'--mcu-type',
|
'--mcu-type',
|
||||||
dest='mcu_type',
|
dest='mcu_type',
|
||||||
type=MIK_VERSION,
|
type=MIK32_Version,
|
||||||
choices=list(MIK_VERSION),
|
choices=list(MIK32_Version),
|
||||||
default=MIK_VERSION.MIK32V2,
|
default=MIK32_Version.MIK32V2,
|
||||||
help="Выбор микроконтроллера. "
|
help="Выбор микроконтроллера. "
|
||||||
f"По умолчанию: {MIK_VERSION.MIK32V2}"
|
f"По умолчанию: {MIK32_Version.MIK32V2}"
|
||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user