commit 211ef38d9c12ad7f86acf7ade61521a32a972faa Author: wongyiekheng Date: Mon Dec 15 09:23:52 2025 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..acc8171 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/devices/ +*.pyc diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6829f5f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,36 @@ +# Repository Guidelines + +## Project Structure & Module Organization +- Root Python app: `pro3_uart.py` (Tkinter UI for dual UART + J-Link debugger). +- Firmware flashing logic: `flash.py`, `fw/` assets, and `devices/Profiles/`. +- Build artifacts/specs: `dist/`, `build/`, `*.spec`, `package_pro3_exe.py`. +- Configs: `Settings.json`, `Reburn.cfg`, `Reset.cfg`. +- J-Link support: `pylink/JLinkARM.dll`; requirements in `requirements.txt`. + +## Build, Test, and Development Commands +- Run UI: `python3 pro3_uart.py` (requires Tkinter, pyserial, pylink DLL, connected devices). +- Smoke syntax check: `python3 -m py_compile pro3_uart.py`. +- Flash utility (when needed): `python3 flash.py --help` to view options. + +## Coding Style & Naming Conventions +- Python 3, 4-space indentation; prefer explicit names (`dev1`, `dev2`, `debugger`). +- Keep UI text short; avoid non-ASCII unless already present. +- Mirror existing Tkinter patterns (use `ttk`, queue logs via `DevicePanel.queue_message`); never sacrifice clarity for micro-reuse—duplicate a few lines if it keeps behavior explicit. + +## Testing Guidelines +- No automated test suite present; rely on manual verification: + - Connect UART devices; verify console input/history and macros. + - Connect J-Link; confirm AP/core selection, scripts (J-Link/GDB), and command execution. + - Run `python3 -m py_compile pro3_uart.py` before sharing changes. + +## Commit & Pull Request Guidelines +- Use concise, imperative commit messages (e.g., `Add pylink AP selection UI`). +- Describe device setup and manual steps taken (UART ports, J-Link serial/AP, scripts used). +- Include screenshots/GIFs of UI changes when applicable. +- Link related issues/tasks; call out any required firmware/config files. + +## Agent-Specific Instructions +- Do not modify bundled binaries/DLLs unless requested; keep `pylink/JLinkARM.dll` in place. +- Preserve existing flashing and UART workflows; avoid regressions to `DevicePanel` and `PylinkDebuggerPanel`. +- When adding UI, mirror current Tkinter style and keep labels compact for the horizontal layout. +- Favor clear, independent modules over tightly coupled helpers; avoid “minimal coding” shortcuts that hide control flow (e.g., do not overuse shared globals—pass explicit parameters and keep per-panel state encapsulated). diff --git a/Reburn.cfg b/Reburn.cfg new file mode 100644 index 0000000..bfc75bf --- /dev/null +++ b/Reburn.cfg @@ -0,0 +1,7 @@ +dtr=0 +rts=1 +delay=200 +dtr=1 +rts=0 +delay=100 +dtr=0 diff --git a/Reset.cfg b/Reset.cfg new file mode 100644 index 0000000..0493c30 --- /dev/null +++ b/Reset.cfg @@ -0,0 +1,5 @@ +dtr=0 +rts=1 +delay=200 +rts=0 +dtr=0 diff --git a/Settings.json b/Settings.json new file mode 100644 index 0000000..f0a2a81 --- /dev/null +++ b/Settings.json @@ -0,0 +1,26 @@ +{ + "SensePacketCount": 32, + "RequestRetryCount": 3, + "RequestRetryIntervalInMillisecond": 10, + "AsyncResponseTimeoutInMilliseccond": 1000, + "SyncResponseTimeoutInMillisecond": 1000, + "BaudrateSwitchDelayInMillisecond": 200, + "RomBootDelayInMillisecond": 100, + "UsbRomBootDelayInMillisecond": 1000, + "UsbFloaderBootDelayInMillisecond": 1000, + "SwitchBaudrateAtFloader": 0, + "WriteResponseTimeoutInMillisecond": 2000, + "FloaderBootDelayInMillisecond": 1000, + "AutoSwitchToDownloadModeWithDtrRts": 0, + "AutoResetDeviceWithDtrRts": 0, + "FlashProtectionProcess": 0, + "EraseByBlock": 0, + "ProgramConfig1": 0, + "ProgramConfig2": 0, + "DisableNandAccessWithUart": 0, + "RamDownloadPaddingByte": 0, + "AutoProgramSpicAddrMode4Byte": 0, + "AutoSwitchToDownloadModeWithDtrRtsTimingFile": "Reburn.cfg", + "AutoResetDeviceWithDtrRtsTimingFile": "Reset.cfg", + "PostProcess": "RESET" +} \ No newline at end of file diff --git a/base/__init__.py b/base/__init__.py new file mode 100644 index 0000000..b88d93a --- /dev/null +++ b/base/__init__.py @@ -0,0 +1,3 @@ +from .download_handler import * +from .rtk_logging import * +from .rt_settings import * \ No newline at end of file diff --git a/base/config_utils.py b/base/config_utils.py new file mode 100644 index 0000000..3efe851 --- /dev/null +++ b/base/config_utils.py @@ -0,0 +1,30 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +import os + + +class ConfigUtils: + @staticmethod + def get_key_value_pairs(logger, file_path): + result = [] + with open(file_path, 'r') as file: + for line in file: + line = line.strip() + if not line: + continue + + parts = line.split("=", 1) + if len(parts) == 2: + key, value = parts + try: + result.append({key: int(value)}) + except ValueError: + logger.warning(f"Skipping line with non-integer value: {line}") + else: + logger.warning(f"Skipping improperly formatted line: {line}") + + return result \ No newline at end of file diff --git a/base/device_info.py b/base/device_info.py new file mode 100644 index 0000000..5ff0a02 --- /dev/null +++ b/base/device_info.py @@ -0,0 +1,42 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +from .rtk_flash_type import * +from .memory_info import * + + +class DeviceInfo(object): + def __init__(self): + self.did = 0 + self.image_type = 0 + self.cmd_set_version = 0 + self.wifi_mac = None + self.memory_type = None + self.flash_mid = None + self.flash_did = None + self.flash_mfg = "" + self.flash_model = "" + self.flash_page_size = 0 + self.flash_oob_size = 0 + self.flash_pages_per_block = 0 + self.flash_blocks_per_lun = 0 + self.flash_luns_per_target = None + self.flash_max_bad_block_per_lun = 0 + self.flash_req_host_ecc_level = None + self.flash_targets = None + self.flash_capacity = 0 + + def get_wifi_mac_text(self): + mac_list = [] + for chr in self.wifi_mac: + mac_list.append(hex(chr)[2:].zfill(2).upper()) + return ":".join(mac_list) + + def flash_block_size(self): + return self.flash_page_size * self.flash_pages_per_block + + def is_boot_from_nand(self): + return self.memory_type == MemoryInfo.MEMORY_TYPE_NAND diff --git a/base/device_profile.py b/base/device_profile.py new file mode 100644 index 0000000..0596923 --- /dev/null +++ b/base/device_profile.py @@ -0,0 +1,79 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +from .image_info import * +from .efuse_data import * +from .version import * + + +class RtkDeviceProfile(): + DEFAULT_FLASH_START_ADDR = 0x08000000 + DEFAULT_RAM_START_ADDR = 0x20000000 + DEVICE_ID_AMEBAD = 0x6548 + DEVICE_ID_AMEBAZ = 0x6547 + + def __init__(self, **kwargs): + self.version = kwargs.get("Version", "1.1.1") + self.device_name = kwargs.get("DeviceName", "") + self.device_id = kwargs.get("DeviceID", 0) + self.memory_type = kwargs.get("MemoryType", 0) + self.support_usb_download = kwargs.get("SupportUsbDownload", False) + self.flash_start_address = kwargs.get("FlashStartAddress", self.DEFAULT_FLASH_START_ADDR) + self.ram_start_address = kwargs.get("RamStartAddress", self.DEFAULT_RAM_START_ADDR) + self.floader = kwargs.get("Floader", "") + self.floader_address = kwargs.get("FloaderAddress", 0) + self.handshake_baudrate = kwargs.get("HandshakeBaudrate", 0) + self.log_baudrate = kwargs.get("LogBaudrate", 0) + self.logical_efuse_len = kwargs.get("LogicalEfuseLen", 0) + self.physical_efuse_len = kwargs.get("PhysicalEfuseLen", 0) + self.images = [] + self.default_efuse_map = [] + for image_info in kwargs.get("Images", []): + self.images.append(ImageInfo(**image_info)) + + for efuse_data in kwargs.get("DefaultEfuseMap", []): + self.default_efuse_map.append(EfuseData(**efuse_data)) + + def is_amebad(self): + return (self.device_id == self.DEVICE_ID_AMEBAD) + + def is_amebaz(self): + return (self.device_id == self.DEVICE_ID_AMEBAZ) + + def is_ram_address(self, address): + return (address >= self.DEFAULT_RAM_START_ADDR) + + def is_flash_address(self, address): + return (address >= self.DEFAULT_FLASH_START_ADDR) + + def get_version(self): + if self.version: + return Version(self.version) + else: + return Version("1.0.0") + + def __repr__(self): + image_info_list = [ii.__repr__() for ii in self.images] + efuse_data_list = [ed.__repr__() for ed in self.default_efuse_map] + profile_dict = { + "Images": image_info_list, + "DefaultEfuseMap": efuse_data_list, + "Version": f"{self.version}", + "DeviceName": self.device_name, + "DeviceID": self.device_id, + "MemoryType": self.memory_type.value, + "SupportUsbDownload": self.support_usb_download, + "FlashStartAddress": self.flash_start_address, + "RamStartAddress": self.ram_start_address, + "Floader": self.floader, + "FloaderAddress": self.floader_address, + "HandshakeBaudrate": self.handshake_baudrate, + "LogBaudrate": self.log_baudrate, + "LogicalEfuseLen": self.logical_efuse_len, + "PhysicalEfuseLen": self.physical_efuse_len + } + + return profile_dict diff --git a/base/download_handler.py b/base/download_handler.py new file mode 100644 index 0000000..71486f3 --- /dev/null +++ b/base/download_handler.py @@ -0,0 +1,1415 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +from serial.tools.list_ports import comports +import serial +import serial.tools.list_ports +from datetime import datetime + +from .rom_handler import * +from .floader_handler import * +from .flash_utils import * +from .device_profile import * +from .json_utils import * +from .rt_settings import * +from .spic_addr_mode import * +from .memory_info import * +from .config_utils import * +from .remote_serial import RemoteSerial +from typing import Optional, Dict, Any + +_RTK_USB_VID = "0BDA" + +CmdEsc = b'\x1b\r\n' +CmdSetBackupRegister = bytes("EW 0x480003C0 0x200\r\n", encoding="utf-8") +CmdResetIntoDownloadMode = bytes("reboot uartburn\r\n", encoding="utf-8") + +OtpSpicAddrModeAddr = 0x02 +OtpSpicAddrModeMask = 0x02 +OtpSpicAddrModePos = 1 +OtpSpicAddrMode4Byte = (1 << OtpSpicAddrModePos) +OtpSpicAddrMode3Byte = (0 << OtpSpicAddrModePos) + +OtpSpicAddrModeAddrForAmebaD = 0x0E +OtpSpicAddrModeMaskForAmebaD = 0x40 +OtpSpicAddrModePosForAmebaD = 6 +OtpSpicAddrMode4ByteForAmebaD = (1 << OtpSpicAddrModePosForAmebaD) +OtpSpicAddrMode3ByteForAmebaD = (0 << OtpSpicAddrModePosForAmebaD) + + +class Ameba(object): + def __init__(self, + profile: RtkDeviceProfile, + serial_port: serial.Serial, + baudrate: int, + image_path: str, + setting: RtSettings, + logger, + download_img_info=None, + chip_erase=False, + memory_type=None, + erase_info=None, + remote_server: Optional[str] = None, + remote_port: Optional[int] = None, + remote_password: Optional[str] = None): + self.logger = logger + self.profile_info = profile + self.serial_port = None + self.serial_port_name = serial_port + self.remote_server = remote_server + self.remote_port = remote_port + self.remote_password = remote_password + self.is_usb = self.is_realtek_usb() if not remote_server else False + self.initial_serial_port() + self.baudrate = baudrate + self.image_path = image_path + self.download_img_info = download_img_info + self.chip_erase = chip_erase + self.memory_type = memory_type + self.setting = setting + self.device_info = None + self.erase_info = erase_info + self.is_all_ram = True + + self.rom_handler = RomHandler(self) + self.floader_handler = FloaderHandler(self) + + def __del__(self): + if self.serial_port: + if self.serial_port.is_open: + self.logger.info(f"{self.serial_port.port} try to close.") + self.serial_port.close() + while self.serial_port.is_open: + pass + self.logger.info(f"{self.serial_port.port} closed.") + + def initial_serial_port(self): + # initial serial port + try: + # determine whether to use a remote serial port + self.logger.debug(f"Remote server: self.logger type={type(self.logger)}, value={self.logger}") + if self.remote_server and self.remote_port: + self.logger.info(f"Connect to remote serial server: {self.remote_server}:{self.remote_port} (Serial port: {self.serial_port_name})") + # initialize remote serial port + self.serial_port = RemoteSerial( + remote_server=self.remote_server, + remote_port=self.remote_port, + port=self.serial_port_name, + baudrate=self.profile_info.handshake_baudrate, + logger=self.logger + ) + if self.remote_password: + self.logger.debug("Remote server: password set, will send validate command") + self.serial_port.validate(self.remote_password) + self.serial_port.open() + else: + # initialize local serial port + if self.is_usb: + self.serial_port = serial.Serial(self.serial_port_name, + baudrate=self.profile_info.handshake_baudrate, + parity=serial.PARITY_NONE, + stopbits=serial.STOPBITS_ONE, + bytesize=serial.EIGHTBITS) + else: + self.serial_port = serial.Serial() + self.serial_port.port = self.serial_port_name + self.serial_port.baudrate = self.profile_info.handshake_baudrate + self.serial_port.parity = serial.PARITY_NONE + self.serial_port.stopbits = serial.STOPBITS_ONE + self.serial_port.bytesize = serial.EIGHTBITS + self.serial_port.dtr = False + self.serial_port.rts = False + self.serial_port.open() + except Exception as err: + self.logger.error(f"Initialize serial port failed: {err}") + sys.exit(1) + + # --- check if serial port is open (remote/local compatible) --- + def is_open(self) -> bool: + if isinstance(self.serial_port, RemoteSerial): + return self.serial_port.is_open + elif isinstance(self.serial_port, serial.Serial): + return self.serial_port.is_open + return False + + def switch_baudrate(self, baud, delay_s, force=False): + ret = ErrType.OK + + if (baud == self.serial_port.baudrate) and (not force): + self.logger.debug(f"Don't need to switch baudrate: {baud}") + return ret + + self.logger.debug(f"Switch baudrate: {self.serial_port.baudrate} -> {baud}") + + try: + # remote serial port: close and reopen (no dynamic switching supported yet) + if isinstance(self.serial_port, RemoteSerial): + self.serial_port.close() + #time.sleep(delay_s) + self.serial_port.baudrate = baud + self.serial_port.open() + else: + # local serial port + if self.is_usb: + for retry in range(10): + try: + if self.serial_port.is_open: + self.serial_port.close() + while self.serial_port.is_open: + pass + ret = ErrType.OK + break + except: + ret = ErrType.SYS_IO + time.sleep(0.1) + if ret != ErrType.OK: + self.logger.warning(f"Close serial port failed") + time.sleep(delay_s) + + if self.serial_port.baudrate != baud: + self.serial_port.baudrate = baud + + if self.is_usb: + for rty in range(10): + try: + self.serial_port.open() + ret = ErrType.OK + break + except: + ret = ErrType.SYS_IO + time.sleep(0.1) + except Exception as e: + self.logger.error(f"An exception occurs when switching baudrate: {str(e)}") + ret = ErrType.SYS_IO + + if ret == ErrType.OK: + self.logger.debug(f"Switch baudrate success: {baud}") + else: + self.logger.debug(f"Switch baudrate failed") + + return ret + + def read_bytes(self, timeout_seconds, size=1): + ret = ErrType.OK + read_ch = None + + start_time = datetime.now() + while self.serial_port.inWaiting() < size: + if self.remote_server: + time.sleep(0.001) # avoid waiting idly and improve efficiency. + if (datetime.now() - start_time).seconds >= timeout_seconds: + return ErrType.DEV_TIMEOUT, None + + try: + read_ch = self.serial_port.read(size=size) + except Exception as err: + self.logger.error(f"[{self.serial_port.port}] read bytes err: {err}") + ret = ErrType.SYS_IO + + return ret, read_ch + + def write_bytes(self, data_bytes): + self.serial_port.write(data_bytes) + + def write_string(self, string): + bytes_array = string.encode("utf-8") + self.serial_port.write(bytes_array) + + def is_realtek_usb(self): + if self.remote_server: + return False + ports = serial.tools.list_ports.comports() + for port, desc, hvid in sorted(ports): + if port == self.serial_port_name: + # hvid: USB VID:PID=0BDA:8722 SER=5 LOCATION=1-1 + if _RTK_USB_VID in hvid: + return True + else: + return False + + def switch_baudrate_old(self, baud, delay_s, force=False): + ret = ErrType.OK + + if (baud == self.serial_port.baudrate) and (not force): + self.logger.debug(f"Reactive port {self.serial_port.port} ignored, baudrate no change") + return ret + + if baud != self.serial_port.baudrate: + self.logger.debug( + f"Reactive port {self.serial_port.port} with baudrate from {self.serial_port.baudrate} to {baud}") + else: + self.logger.debug( + f"Reactive port {self.serial_port.port} with baudrate {baud}") + + # if uart dtr/rts enable, should skip close/reopen operation + # if USB port, should close/reopen port when switch baudrate + if self.is_usb: + # check if already activated + for retry in range(10): + try: + if self.serial_port.is_open: + self.serial_port.close() + + while self.serial_port.is_open: + pass + ret = ErrType.OK + except: + ret = ErrType.SYS_IO + + if ret == ErrType.OK: + break + + time.sleep(0.1) + + if ret != ErrType.OK: + self.logger.warning(f"Failed to close {self.serial_port.port} when reactive it.") + + time.sleep(delay_s) + + if self.serial_port.baudrate != baud: + self.serial_port.baudrate = baud + + if self.is_usb: + ret = ErrType.OK + for rty in range(10): + try: + self.serial_port.open() + ret = ErrType.OK + except: + ret = ErrType.SYS_IO + + if ret == ErrType.OK: + break + + time.sleep(0.1) + + if ret == ErrType.OK: + self.logger.debug(f"Reactive port {self.serial_port.port} ok") + else: + self.logger.debug(f"Reactive port {self.serial_port.port} fail") + + return ret + + def check_download_mode(self): + ret = ErrType.SYS_IO + is_floader = False + boot_delay = self.setting.usb_rom_boot_delay_in_second if self.profile_info.support_usb_download else self.setting.rom_boot_delay_in_second + + self.logger.debug(f"Check download mode with baudrate {self.serial_port.baudrate}") + retry = 0 + while retry < 3: + retry += 1 + try: + self.logger.debug(f"Check whether in rom download mode") + ret = self.rom_handler.handshake() + if ret == ErrType.OK: + self.logger.debug(f"Handshake ok, device in rom download mode") + break + + if not self.is_usb: + self.logger.debug( + f'Assume in application or ROM normal mode with baudrate {self.profile_info.log_baudrate}') + self.switch_baudrate(self.profile_info.log_baudrate, self.setting.baudrate_switch_delay_in_second) + + self.logger.debug("Try to reset device...") + + self.serial_port.flushOutput() + + self.write_bytes(CmdEsc) + time.sleep(0.1) + + if self.profile_info.is_amebad(): + self.serial_port.flushOutput() + self.write_bytes(CmdSetBackupRegister) + time.sleep(0.1) + + self.serial_port.flushOutput() + self.write_bytes(CmdResetIntoDownloadMode) + + self.switch_baudrate(self.profile_info.handshake_baudrate, boot_delay, True) + + self.logger.debug( + f'Check whether reset in ROM download mode with baudrate {self.profile_info.handshake_baudrate}') + + ret = self.rom_handler.handshake() + if ret == ErrType.OK: + self.logger.debug("Handshake ok, device in ROM download mode") + break + else: + self.logger.debug("Handshake fail, cannot enter UART download mode") + + self.switch_baudrate(self.baudrate, self.setting.baudrate_switch_delay_in_second, True) + + self.logger.debug(f"Check whether in floader with baudrate {self.baudrate}") + ret, status = self.floader_handler.sense(self.setting.sync_response_timeout_in_second) + if ret == ErrType.OK: + # do not reset floader + is_floader = True + self.logger.debug("Floader handshake ok") + break + else: + self.logger.debug(f"Floader handshake fail: {ret}") + except Exception as err: + self.logger.error(f"Check download mode exception: {err}") + + return ret, is_floader + + def prepare(self, show_device_info=True): + ret = ErrType.OK + floader_init_baud = self.baudrate if self.is_usb else (self.profile_info.handshake_baudrate if + (self.setting.switch_baudrate_at_floader == 1) else self.baudrate) + boot_delay = self.setting.usb_floader_boot_delay_in_second if self.profile_info.support_usb_download else self.setting.floader_boot_delay_in_second + + if (not self.is_usb) and (self.setting.auto_switch_to_download_mode_with_dtr_rts != 0): + ret = self.auto_enter_download_mode() + if ret != ErrType.OK: + self.logger.error(f"Enter download mode by DTR/RTS fail: {ret}") + return ret + + ret, is_floader = self.check_download_mode() + if ret != ErrType.OK: + self.logger.error(f"Enter download mode fail: {ret}") + return ret + + if not is_floader: + # download flashloader to RAM + ret = self.rom_handler.download_floader() + if ret != ErrType.OK: + self.rom_handler.abort() + self.logger.error(f"Flashloader download fail: {ret}") + return ret + + ret = self.switch_baudrate(floader_init_baud, boot_delay, True) + if ret != ErrType.OK: + self.logger.error(f"Flashloader boot fail: {ret}") + return ret + + ret = self.floader_handler.handshake(self.baudrate) + if ret != ErrType.OK: + self.logger.error(f"Flashloader handshake fail: {ret}") + return ret + + ret, self.device_info = self.floader_handler.query() + if ret != ErrType.OK: + self.logger.error(f"Query fail: {ret}") + return ret + + if not show_device_info: + return ret + + self.logger.info("Device info:") + self.logger.info(f'* DID: {hex(self.device_info.did)}') + self.logger.info(f'* ImageType: 0x{format(self.device_info.image_type, "04x")}') + self.logger.info( + f'* CmdSetVersion: {(self.device_info.cmd_set_version >> 8) & 0xFF}.{self.device_info.cmd_set_version & 0xFF}') + if self.device_info.is_boot_from_nand(): + self.logger.info(f'* MemoryType: NAND') + self.logger.info(f'* FlashMID: 0x{format(self.device_info.flash_mid, "02X")}') # customized, do not modify + if self.device_info.flash_mid == FlashUtils.NandMfgMicron: + self.logger.info(f'* FlashDID: 0x{format(self.device_info.flash_did, "04X")}') + else: + self.logger.info(f'* FlashDID: 0x{format(self.device_info.flash_did, "02X")}') + self.logger.info(f'* FlashMFG: {self.device_info.flash_mfg}') + self.logger.info(f'* FlashModel: {self.device_info.flash_model}') + self.logger.info( + f'* FlashCapacity: {self.device_info.flash_capacity // 1024 // 1024 // (1024 // 8)}Gb/{self.device_info.flash_capacity // 1024 // 1024}MB') + self.logger.info(f'* FlashBlockSize: {self.device_info.flash_block_size() // 1024}KB') + self.logger.info(f'* FlashPageSize: {self.device_info.flash_page_size}B') + self.logger.info(f'* FlashOobSize: {self.device_info.flash_oob_size}B') + self.logger.info(f'* FlashPagesPerBlock: {self.device_info.flash_pages_per_block}') + self.logger.info(f'* FlashBlocksPerLun: {self.device_info.flash_blocks_per_lun}') + self.logger.info(f'* FlashLunsPerTarget: {self.device_info.flash_luns_per_target}') + self.logger.info(f'* FlashTargets: {self.device_info.flash_targets}') + self.logger.info(f'* FlashMaxBadBlocksPerLun: {self.device_info.flash_max_bad_block_per_lun}') + self.logger.info(f'* FlashReqHostEccLevel: {self.device_info.flash_req_host_ecc_level}') + else: + self.logger.info(f'* MemoryType: NOR') + self.logger.info(f'* FlashMID: {hex(self.device_info.flash_mid)}') + self.logger.info(f'* FlashDID: {hex(self.device_info.flash_did)}') + self.logger.info( + f'* FlashCapacity: {self.device_info.flash_capacity // 1024 // (1024 // 8)}Mb/{self.device_info.flash_capacity // 1024 // 1024}MB') + self.logger.info(f'* FlashPageSize: {self.device_info.flash_page_size}B') + + self.logger.info(f'* WiFiMAC: {self.device_info.get_wifi_mac_text()}') + + if (self.device_info.did != self.profile_info.device_id) and (self.device_info.did != 0xFFFF): + self.logger.error("Device ID mismatch:") + self.logger.error(f'* Device: {hex(self.device_info.did)}') + self.logger.error(f'* Device Profile: {hex(self.profile_info.device_id)}') + return ErrType.SYS_PARAMETER + + if self.device_info.memory_type != self.profile_info.memory_type: + self.logger.error("Flash type mismatch:") + self.logger.error(f'* Device: {self.device_info.memory_type}') + self.logger.error(f'* Device Profile: {self.profile_info.memory_type}') + return ErrType.SYS_PARAMETER + + if self.device_info.is_boot_from_nand(): + if self.device_info.flash_req_host_ecc_level > 0: + self.logger.error(f"Unsupported NAND flash model without internal ECC") + return ErrType.SYS_IO + if self.device_info.flash_pages_per_block != FlashUtils.NandDefaultPagePerBlock.value: + self.logger.error( + f"Unsupported NAND flash model with {self.device_info.flash_pages_per_block} pages per block") + return ErrType.SYS_IO + + program_param1 = self.setting.program_config1.to_bytes(8, byteorder="little") + program_param2 = self.setting.program_config2.to_bytes(8, byteorder="little") + param = [program_param1, program_param2] + ret = self.floader_handler.config(param) + if ret != ErrType.OK: + self.logger.error(f"Config device fail: {ret}") + + return ret + + def check_flash_lock(self, flash_status): + ret = ErrType.OK + + if self.device_info.is_boot_from_nand(): + cmd = FlashUtils.NandCmdGetFeatures.value + address = FlashUtils.NandRegProtection.value + bp_mask = FlashUtils.NandRegProtectionBpMask.value + if self.device_info.flash_mid == FlashUtils.NandMfgWinbond or self.device_info.flash_mid == FlashUtils.NandMfgMicron: + bp_mask = FlashUtils.NandRegProtectionBpMaskWinbondMicron + else: + cmd = FlashUtils.NorCmdReadStatusReg1.value + address = 0 + bp_mask = FlashUtils.NorStatusReg1BpMask.value + + ret, protection = self.read_flash_status_register(cmd, address) + flash_status.protection = protection + if ret == ErrType.OK: + if (protection & bp_mask) != 0: + flash_status.is_locked = True + + return ret + + def check_and_process_flash_lock(self, flash_status): + follow_up_action = self.setting.flash_protection_process + + ret = self.check_flash_lock(flash_status) + if ret != ErrType.OK: + self.logger.error(f"Flash protection check fail: {ret}") + + if flash_status.is_locked: + self.logger.warning("Flash block protection detected") + + if follow_up_action == RtSettings.FLASH_PROTECTION_PROCESS_PROMPT: + self.logger.info(f"Follow-up Actions:") + self.logger.info(f"1: Try operation with block protected(may fail)") + self.logger.info(f"2: Remove the protection and restore the protection after operation") + self.logger.info(f"3: Abort the operation") + retry = 0 + while retry < 3: + try: + follow_up_action = int(input("Please Input the selected action index: ").strip()) + if RtSettings.FLASH_PROTECTION_PROCESS_PROMPT < follow_up_action <= RtSettings.FLASH_PROTECTION_PROCESS_ABORT: + break + else: + self.logger.info(f"{follow_up_action} is invalid") + except Exception as err: + self.logger.error(f"Input is invalid: {err}") + continue + else: + return ErrType.SYS_PARAMETER + + if follow_up_action == RtSettings.FLASH_PROTECTION_PROCESS_UNLOCK: + flash_status.need_unlock = True + self.logger.info("Remove the flash block protection...") + ret = self.unlock_flash() + if ret != ErrType.OK: + self.logger.error(f"Fail to remove the flash lock protection: {ret}") + return ret + elif follow_up_action == RtSettings.FLASH_PROTECTION_PROCESS_ABORT: + self.logger.warning(f"Operation aborted for block protection") + return ErrType.SYS_CANCEL + else: + self.logger.warning(f"Trying to operate with block protection") + return ErrType.SYS_PARAMETER + + return ret + + def unlock_flash(self): + return self.lock_flash(0) + + def lock_flash(self, protection): + if self.device_info.is_boot_from_nand(): + cmd = FlashUtils.NandCmdSetFeatures.value + address = FlashUtils.NandRegProtection.value + else: + cmd = FlashUtils.NorCmdWriteStatusReg1.value + address = 0 + + ret = self.write_flash_status_register(cmd, address, protection) + + return ret + + def read_flash_status_register(self, cmd, address): + return self.floader_handler.read_status_register(cmd, address) + + def write_flash_status_register(self, cmd, address, status): + return self.floader_handler.write_status_register(cmd, address, status) + + def dtr_rts_timing_mapping(self, timing_list): + ret = ErrType.OK + + if not self.serial_port.is_open: + return ErrType.SYS_IO + + dtr = self.serial_port.dtr + rts = self.serial_port.rts + + for key_val in timing_list: + for key, val in key_val.items(): + if key.upper() == "DTR": + self.serial_port.dtr = (val != 0) + elif key.upper() == "RTS": + self.serial_port.rts = (val != 0) + elif key.upper() == "DELAY": + time.sleep(val / 1000) + else: + self.logger.error(f"Unsupport DTR/RTS timing type: [{key}: {val}]") + + self.serial_port.dtr = dtr + self.serial_port.rts = rts + + return ret + + def auto_enter_download_mode(self): + ret = ErrType.OK + + if not self.serial_port.is_open: + return ErrType.SYS_IO + + reburn_timing_file = os.path.join(RtkUtils.get_executable_root_path(), self.setting.auto_switch_to_download_mode_with_dtr_rts_file) + if os.path.exists(reburn_timing_file): + reburn_timing = ConfigUtils.get_key_value_pairs(self.logger, reburn_timing_file) + try: + if reburn_timing: + ret = self.dtr_rts_timing_mapping(reburn_timing) + except Exception as err: + self.logger.error(f"Fail to auto enter download mode: {err}") + ret = ErrType.SYS_IO + else: + self.logger.debug(f"{reburn_timing_file} is not exists!") + + return ret + + def auto_reset_device(self): + ret = ErrType.OK + + if not self.serial_port.is_open: + return ErrType.SYS_IO + + reset_timing_file = os.path.join(RtkUtils.get_executable_root_path(), self.setting.auto_reset_device_with_dtr_rts_file) + if os.path.exists(reset_timing_file): + reset_timing = ConfigUtils.get_key_value_pairs(self.logger, reset_timing_file) + try: + if reset_timing: + ret = self.dtr_rts_timing_mapping(reset_timing) + except Exception as err: + self.logger.error(f"Fail to reset device: {err}") + ret = ErrType.SYS_IO + else: + self.logger.debug(f"{reset_timing_file} is not exists!") + + return ret + + def post_process(self): + ret = ErrType.OK + + post_process_str = self.setting.post_process.strip().upper() + try: + next_op = NextOpType[post_process_str] + self.logger.debug(f"Next option: {next_op}") + except KeyError: + self.logger.error(f"No matching enum found for {post_process_str}") + next_op = NextOpType.NONE + + if next_op != NextOpType.NONE: + if (next_op == NextOpType.RESET) and (not self.is_usb) and (self.setting.auto_reset_device_with_dtr_rts != 0): + self.logger.debug(f"Reset device with DTR/RTS...") + ret = self.auto_reset_device() + if ret != ErrType.OK: + self.logger.warning(f"Reset device with DTR/RTS fail: {ret}") + else: + if next_op == NextOpType.RESET: + self.logger.info(f"Reset device without DTR/RTS") + + ret = self.floader_handler.next_operation(next_op, 0) + if ret != ErrType.OK: + self.logger.warning(f"Next option {next_op} fail: {ret}") + + # should close serial port + if self.serial_port and self.is_open(): + try: + self.logger.info(f"close {self.serial_port.port}...") + self.serial_port.close() + self.logger.info(f"{self.serial_port.port} close done") + except Exception as e: + self.logger.error(f"close error: {e}", exc_info=True) + self.serial_port = None + + return ret + + def check_protocol(self): + ret = True + + if (not self.is_usb) and self.setting.disable_nand_access_with_uart == 1: + self.logger.warning( + f"NAND access via UART is not allowed, please check the COM port and make sure the device profile matches the attached device!") + ret = False + + return ret + + def check_protocol_for_download(self): + ret = True + has_nand = False + + for image_info in self.profile_info.images: + if (image_info.memory_type == MemoryInfo.MEMORY_TYPE_NAND): + has_nand = True + break + + if has_nand: + ret = self.check_protocol() + + return ret + + def _process_image(self, img_name): + if img_name.strip().startswith(("A:", "B:")): + img_name = img_name.split(":")[1].split("(")[0].strip() + if img_name.endswith(".dtb"): + img_path_files = os.listdir(self.image_path) + for img_f in img_path_files: + self.logger.debug(img_f) + if img_f.endswith(".dtb") and os.path.isfile(os.path.join(self.image_path, img_f)): + img_name = img_f + break + else: + img_name = None + + return img_name + + def verify_images(self): + ret = ErrType.OK + image_selected = False + + all_images = self.profile_info.images + if self.download_img_info: + all_images = self.download_img_info + + for image_info in all_images: + if not image_info.mandatory: + continue + if not self.download_img_info: + image_name = self._process_image(image_info.image_name) + if image_name is None: + self.logger.error(f"Cannot find a valid {image_name} for download") + ret = ErrType.SYS_PARAMETER + break + image_path = os.path.realpath(os.path.join(self.image_path, image_name)) + else: + image_name = os.path.basename(image_info.image_name) + image_path = image_info.image_name + + if not os.path.exists(image_path): + self.logger.error(f"Image file {image_name} dose not exist: {image_path}") + ret = ErrType.SYS_PARAMETER + break + + if image_info.start_address < 0: + self.logger.error(f"Start address is not valid specified for image {image_name}") + ret = ErrType.SYS_PARAMETER + break + if image_info.end_address < 0: + self.logger.error(f"End address is not valid specified for image {image_name}") + ret = ErrType.SYS_PARAMETER + break + if image_info.start_address >= image_info.end_address: + self.logger.error( + f"Invalid address range {image_info.start_address}-{image_info.end_address} for {image_name}") + ret = ErrType.SYS_PARAMETER + break + image_size = os.path.getsize(image_path) + if image_size > (image_info.end_address - image_info.start_address): + self.logger.error( + f"Image file {image_path} is too large for {image_name}, please adjust the memory layout") + ret = ErrType.SYS_PARAMETER + break + + is_start_address_in_ram = self.profile_info.is_ram_address(image_info.start_address) + is_end_address_in_ram = self.profile_info.is_ram_address(image_info.end_address) + if (((self.memory_type == MemoryInfo.MEMORY_TYPE_RAM) and ( + (not is_start_address_in_ram) or (not is_end_address_in_ram))) or + ((self.memory_type == MemoryInfo.MEMORY_TYPE_NOR) and ( + is_start_address_in_ram or is_end_address_in_ram))): + self.logger.error( + f"Invalid address range {image_info.start_address}-{image_info.end_address} for {image_name}") + ret = ErrType.SYS_PARAMETER + break + if not is_start_address_in_ram: + self.is_all_ram = False + + image_selected = True + else: + if not image_selected: + self.logger.warning(f"No image selected!") + ret = ErrType.SYS_PARAMETER + + return ret + + def post_verify_images(self): + ret = ErrType.OK + + max_addr = self.profile_info.flash_start_address + self.device_info.flash_capacity + image_dowload_list = self.download_img_info if self.download_img_info else self.profile_info.images + for image_info in image_dowload_list: + is_ram = self.profile_info.is_ram_address(image_info.start_address) + if not is_ram: + if (image_info.start_address > max_addr) or (image_info.end_address > max_addr): + self.logger.error(f"Invalid layout, image {image_info.image_name} address overflow") + ret = ErrType.SYS_OVERRANGE + break + if (self.device_info.is_boot_from_nand() and + (((image_info.start_address % self.device_info.flash_block_size()) != 0) or + ((image_info.end_address % self.device_info.flash_block_size()) != 0))): + self.logger.error(f"{image_info.image_name} address range not aligned") + ret = ErrType.SYS_PARAMETER + break + return ret + + def validate_config_for_erase(self): + ret = ErrType.OK + + if self.erase_info: + if ((self.profile_info.memory_type == MemoryInfo.MEMORY_TYPE_NAND) and ( + self.memory_type == MemoryInfo.MEMORY_TYPE_NOR)) or \ + ((self.profile_info.memory_type == MemoryInfo.MEMORY_TYPE_NOR) and ( + self.memory_type == MemoryInfo.MEMORY_TYPE_NAND)): + self.logger.error("Unsupported memory type.") + ret = ErrType.SYS_PARAMETER + return ret + + if (self.memory_type == MemoryInfo.MEMORY_TYPE_RAM) and (not self.profile_info.is_ram_address(self.erase_info.start_address)): + self.logger.error(f"Invalid RAM start address: {self.erase_info.start_address}") + ret = ErrType.SYS_PARAMETER + return ret + elif (self.memory_type != MemoryInfo.MEMORY_TYPE_RAM) and \ + (not ((self.profile_info.memory_type == MemoryInfo.MEMORY_TYPE_NAND) or self.profile_info.is_flash_address(self.erase_info.start_address))): + self.logger.error(f"Invalid start address: {self.erase_info.start_address}") + ret = ErrType.SYS_PARAMETER + return ret + + if (self.memory_type == MemoryInfo.MEMORY_TYPE_NOR): + if not self.is_address_block_aligned(self.erase_info.start_address): + self.logger.warning(f"NOR flash start address should be aligned to {FlashUtils.NorDefaultBlockSize.value}B.") + ret = ErrType.SYS_PARAMETER + return ret + if (self.erase_info.size_in_kbyte != 0xFFFFFFFF) and (not self.is_address_block_aligned(self.erase_info.size_in_byte())): + self.logger.warning(f"NOR flash erase size should be aligned to {FlashUtils.NorDefaultBlockSize.value}B.") + ret = ErrType.SYS_PARAMETER + return ret + self.logger.info(f"NOR flash erase: start address={hex(self.erase_info.start_address)}, size={self.erase_info.size_in_kbyte}KB.") + else: + if not self.is_address_block_aligned(self.erase_info.start_address): + self.logger.warning(f"NAND flash start address should be block size aligned!") + ret = ErrType.SYS_PARAMETER + return ret + if not self.is_address_block_aligned(self.erase_info.end_address): + self.logger.warning(f"NAND flash end address should be block size aligned!") + ret = ErrType.SYS_PARAMETER + return ret + if (self.erase_info.end_address <= self.erase_info.start_address): + self.logger.warning(f"NAND flash start address should be less than end address!") + ret = ErrType.SYS_PARAMETER + return ret + + self.logger.info(f"NAND flash erase: start address={hex(self.erase_info.start_address)}, end address={hex(self.erase_info.end_address)}") + + return ret + + def check_protocol_for_erase(self): + ret = True + + if (not self.is_usb) and (self.erase_info.memory_type == MemoryInfo.MEMORY_TYPE_NAND) and ( + self.setting.disable_nand_access_with_uart == 1): + ret = False + self.logger.warning( + f"NAND access via UART is not allowed, please check the COM port and make sure the device profile matches the attached device!") + return ret + + def is_address_block_aligned(self, address): + ret = False + + if self.device_info: + block_size = self.device_info.flash_block_size() + ret = ((address % block_size) == 0) + + return ret + + def post_validate_config_for_erase(self): + ret = ErrType.OK + + if self.erase_info and (not self.profile_info.is_ram_address(self.erase_info.start_address)): + if not self.is_address_block_aligned(self.erase_info.start_address): + ret = ErrType.SYS_PARAMETER + self.logger.warning( + f"Flash start address should be aligned to block size {self.device_info.flash_block_size()}KB") + if self.erase_info.memory_type == MemoryInfo.MEMORY_TYPE_NAND and ( + not self.is_address_block_aligned(self.erase_info.end_address)): + ret = ErrType.SYS_PARAMETER + self.logger.warning( + f"Flash end address should be aligned to block size {self.device_info.flash_block_size()}KB") + if self.erase_info.memory_type == MemoryInfo.MEMORY_TYPE_NOR and ( + not self.is_address_block_aligned(self.erase_info.size_in_byte())): + ret = ErrType.SYS_PARAMETER + self.logger.warning( + f"Flash size should be aligned to block size {self.device_info.flash_block_size()}KB") + return ret + + def calculate_checksum(self, image): + with open(image, 'rb') as stream: + img_content = stream.read() + img_len = len(img_content) + img_arr = list(img_content) + chksum = 0 + offset = 0 + while (img_len - offset) > 3: + chksum += (img_arr[offset + 0] | (img_arr[offset + 1] << 8) | (img_arr[offset + 2] << 16) | ( + img_arr[offset + 3] << 24)) + offset += 4 + + tmp = 0 + while (img_len - offset - tmp) > 0: + chksum += img_arr[offset + tmp] << (8 * tmp) + tmp += 1 + + chksum = chksum & 0xffffffff + return chksum + + def erase_flash_chip(self): + self.logger.info(f"Chip erase start") # customized, do not modify + ret = self.floader_handler.erase_flash(MemoryInfo.MEMORY_TYPE_NOR, RtkDeviceProfile.DEFAULT_FLASH_START_ADDR, + 0, 0xFFFFFFFF, + nor_erase_timeout_in_second(0xFFFFFFFF), + sense=True, force=False) + return ret + + def download_images(self): + ret = ErrType.OK + + # support chip erase + if self.chip_erase and (self.memory_type == MemoryInfo.MEMORY_TYPE_NOR): + ret = self.erase_flash_chip() + if ret != ErrType.OK: + self.logger.error(f"Chip erase fail") + return ret + self.logger.info(f"Chip erase end") + + if self.download_img_info: + for image_info in self.download_img_info: + img_path = image_info.image_name + img_name = os.path.basename(img_path) + image_info.image_name = img_name + + self.logger.info(f"{img_name} download...") + ret = self._download_image(img_path, image_info) + if ret != ErrType.OK: + self.logger.info(f"{img_name} download fail: {ret}") + break + else: + is_area_A = False + is_area_B = False + for image_info in self.profile_info.images: + is_mandatory = image_info.mandatory + if not is_mandatory: + continue + img_name = image_info.image_name + self.logger.info(f"{img_name} download...") + if img_name.strip().startswith(("A:", "a_")): + is_area_A = True + elif img_name.strip().startswith(("B:", "b_")): + is_area_B = True + + if is_area_A and is_area_B: + self.logger.error(f"NOT support both A and B download at the same time") + ret = ErrType.SYS_PARAMETER + break + + img_name = self._process_image(img_name) + img_path = os.path.realpath(os.path.join(self.image_path, img_name)) + ret = self._download_image(img_path, image_info) + if ret != ErrType.OK: + self.logger.info(f"{img_name} download fail: {ret}") + break + + if ret == ErrType.OK: + self.logger.info("All images download done") + + return ret + + def get_page_alligned_size(self, size, page_size): + result = size + + if (size % page_size) != 0: + result = (size // page_size + 1) * page_size + + return result + + def _download_image(self, image_path, image_info): + ret = ErrType.OK + + page_size = self.device_info.flash_page_size + block_size = self.device_info.flash_block_size() + pages_per_block = self.device_info.flash_pages_per_block + last_erase_addr = 0 + next_erase_addr = 0 + checksum = 0 + write_timeout = 0 + is_ram = (image_info.memory_type == MemoryInfo.MEMORY_TYPE_RAM) + padding_data = self.setting.ram_download_padding_byte if is_ram else FlashUtils.FlashWritePaddingData.value + start_time = datetime.now() + + with open(image_path, 'rb') as stream: + img_content = stream.read() + + img_length = len(img_content) + aligned_img_length = self.get_page_alligned_size(img_length, page_size) + self.logger.debug( + f"Image download size={aligned_img_length}({img_length}), start_addr={hex(image_info.start_address)}, " + f"end_addr={hex(image_info.end_address)}") + + addr = image_info.start_address + tx_sum = 0 + if ((image_info.memory_type == MemoryInfo.MEMORY_TYPE_NAND) or ( + is_ram and (self.profile_info.memory_type == MemoryInfo.MEMORY_TYPE_NAND))): + write_timeout = nand_program_timeout_in_second(block_size, + page_size) + FlashUtils.NandBlockEraseTimeoutInSeconds.value + img = io.BytesIO(img_content) + img_bytes = img.read(img_length) + data_bytes = img_bytes.ljust(aligned_img_length, padding_data.to_bytes(1, byteorder="little")) + + is_last_page = False + progress_int = 0 + while not is_last_page: + if addr >= image_info.end_address: + self.logger.debug(f"Overrange target={hex(addr)}, end={hex(image_info.end_address)}") + ret = ErrType.SYS_OVERRANGE + break + + ret = self.floader_handler.erase_flash(image_info.memory_type, addr, addr + block_size, block_size, + nand_erase_timeout_in_second(block_size, block_size), + sense=True) + if ret == ErrType.DEV_NAND_BAD_BLOCK.value or ret == ErrType.DEV_NAND_WORN_BLOCK.value: + self.logger.info( + f"{'Bad' if ret == ErrType.DEV_NAND_BAD_BLOCK else 'Worn'} block: 0x{format(addr, '08X')}") + addr += self.device_info.flash_block_size() + next_erase_addr = addr + continue + elif ret != ErrType.OK: + break + + next_erase_addr = addr + self.device_info.flash_block_size() + + i = 0 + while i < pages_per_block: + if tx_sum + page_size >= aligned_img_length: + is_last_page = True + + need_sense = (is_last_page or (i == pages_per_block - 1)) + ret = self.floader_handler.write(image_info.memory_type, data_bytes[tx_sum: tx_sum + page_size], + page_size, addr, write_timeout, need_sense=need_sense) + if ret == ErrType.OK: + idx = 0 + while idx < page_size: + checksum += (data_bytes[tx_sum + idx] + (data_bytes[tx_sum + idx + 1] << 8) + ( + data_bytes[tx_sum + idx + 2] << 16) + (data_bytes[tx_sum + idx + 3] << 24)) + idx += 4 + checksum &= 0xFFFFFFFF + addr += page_size + tx_sum += page_size + else: + self.logger.error(f"Write to addr={format(addr, '08x')}, size={page_size} fail: {ret}") + break + + if is_last_page: + break + + i += 1 + progress = int((tx_sum / aligned_img_length) * 100) + if int((progress) / 10) != progress_int: + progress_int += 1 + self.logger.info(f"Programming progress: {progress}%") # customized, do not modified + + if ret != ErrType.OK: + break + + if ret == ErrType.OK: + if image_info.full_erase and (next_erase_addr < image_info.end_address): + self.logger.debug( + f"Erase extra address range: {hex(next_erase_addr)}-{hex(image_info.end_address)}") + ret = self.floader_handler.erase_flash(image_info.memory_type, next_erase_addr, + image_info.end_address, + (image_info.end_address - next_erase_addr), + nand_erase_timeout_in_second( + (image_info.end_address - next_erase_addr), block_size), + sense=True) + if ret == ErrType.DEV_NAND_BAD_BLOCK.value or ret == ErrType.DEV_NAND_WORN_BLOCK.value: + self.logger.debug( + f"{'Bad' if ret == ErrType.DEV_NAND_BAD_BLOCK else 'Worn'} block: {hex(addr)}") + ret = ErrType.OK + elif ret != ErrType.OK: + self.logger.error(f"Fail to erase block {hex(addr)}:{ret}") + + if tx_sum >= aligned_img_length: + if aligned_img_length < 1024: + self.logger.debug(f"Image download done: {aligned_img_length}bytes") + elif aligned_img_length < 1024 * 1024: + self.logger.debug(f"Image download done: {aligned_img_length // 1024}KB") + else: + self.logger.debug(f"Image download done: {round(aligned_img_length / 1024 / 1024, 2)}MB") + else: + self.logger.warning(f"Image download uncompleted: {tx_sum}/{aligned_img_length}") + + elapse_ms = round((datetime.now() - start_time).total_seconds() * 1000, 0) + kbps = aligned_img_length * 8 // elapse_ms + size_kb = aligned_img_length // 1024 + + if self.is_usb: + self.logger.info( + f"{image_info.image_name} download done: {size_kb}KB / {elapse_ms}ms / {kbps / 1000}Mbps") + else: + self.logger.info(f"{image_info.image_name} download done: {size_kb}KB / {elapse_ms}ms / {kbps}Kbps") + else: + write_pages = 0 + img = io.BytesIO(img_content) + img_bytes = img.read(img_length) + data_bytes = img_bytes.ljust(aligned_img_length, padding_data.to_bytes(1, byteorder="little")) + progress_int = 0 + while tx_sum < aligned_img_length: + if write_pages == 0: + if (addr % (64 * FlashUtils.NorDefaultPageSize.value)) == 0 and \ + ((aligned_img_length - tx_sum >= 64 * FlashUtils.NorDefaultPageSize.value)): + block_size = 64 * FlashUtils.NorDefaultPageSize.value + else: + block_size = 4 * FlashUtils.NorDefaultPageSize.value + + pages_per_block = block_size // page_size + erase_addr = addr + write_timeout = FlashUtils.NorPageProgramTimeoutInSeconds.value * int( + max(self.setting.sense_packet_count, pages_per_block)) + nor_erase_timeout_in_second( + divide_then_round_up(block_size, 1024)) + if erase_addr != last_erase_addr: + if (not is_ram) and (erase_addr % block_size) != 0: + # error: # customized, do not modify + self.logger.error( + f"Flash erase address align error: addr {hex(erase_addr)} not aligned to block size {hex(block_size)}") + ret = ErrType.SYS_PARAMETER + break + + ret = self.floader_handler.erase_flash(image_info.memory_type, erase_addr, + erase_addr + block_size, block_size, + nor_erase_timeout_in_second( + divide_then_round_up(block_size, 1024))) + if ret != ErrType.OK: + break + + last_erase_addr = erase_addr + next_erase_addr = erase_addr + block_size + + need_sense = ((((write_pages + 1) % self.setting.sense_packet_count) == 0) or + (write_pages + 1 >= pages_per_block) or + (tx_sum + page_size >= aligned_img_length)) + ret = self.floader_handler.write(image_info.memory_type, data_bytes[tx_sum: tx_sum + page_size], + page_size, addr, write_timeout, need_sense=need_sense) + if ret != ErrType.OK: + self.logger.debug(f"Write to addr={hex(addr)} size={page_size} fail: {ret}") + break + + write_pages += 1 + if write_pages >= pages_per_block: + write_pages = 0 + + idx = 0 + while idx < page_size: + checksum += (data_bytes[tx_sum + idx] + (data_bytes[tx_sum + idx + 1] << 8) + ( + data_bytes[tx_sum + idx + 2] << 16) + (data_bytes[tx_sum + idx + 3] << 24)) + idx += 4 + + checksum &= 0xFFFFFFFF + + addr += page_size + tx_sum += page_size + + progress = int((tx_sum / aligned_img_length) * 100) + if int((progress) / 10) != progress_int: + progress_int += 1 + self.logger.info(f"Programming progress: {progress}%") # customized, do not modified + + if image_info.full_erase and (next_erase_addr < image_info.end_address): + self.logger.debug(f"Erase extra address range: {hex(next_erase_addr)}-{hex(image_info.end_address)}") + ret = self.floader_handler.erase_flash(image_info.memory_type, next_erase_addr, image_info.end_address, + (image_info.end_address - next_erase_addr), + nor_erase_timeout_in_second(divide_then_round_up( + (image_info.end_address - next_erase_addr), 1024)), + sense=True) + if ret != ErrType.OK: + self.logger.warning( + f"Fail to extra address range {hex(next_erase_addr)}-{hex(image_info.end_address)}") + + if aligned_img_length < 1024: + self.logger.debug(f"Image download done: {aligned_img_length}bytes") + elif aligned_img_length < 1024 * 1024: + self.logger.debug(f"Image download done: {aligned_img_length // 1024}KB") + else: + self.logger.debug(f"Image download done: {round(aligned_img_length / 1024 / 1024, 2)}MB") + + elapse_ms = round((datetime.now() - start_time).total_seconds() * 1000, 0) + kbps = aligned_img_length * 8 // elapse_ms + size_kb = aligned_img_length // 1024 + + if self.is_usb: + self.logger.info( + f"{image_info.image_name} download done: {size_kb}KB / {elapse_ms}ms / {kbps / 1000}Mbps") + else: + self.logger.info(f"{image_info.image_name} download done: {size_kb}KB / {elapse_ms}ms / {kbps}Kbps") + + if ret == ErrType.OK: + cal_checksum = 0 + if image_info.memory_type == MemoryInfo.MEMORY_TYPE_NAND: + checksum_timeout = nand_checksum_timeout_in_second(aligned_img_length, None) + else: + checksum_timeout = nor_checksum_timeout_in_second(aligned_img_length) + ret, cal_checksum = self.floader_handler.checksum(image_info.memory_type, image_info.start_address, + image_info.end_address, aligned_img_length, + checksum_timeout) + if ret == ErrType.OK: + if cal_checksum != checksum: + self.logger.debug(f"Checksum fail: expect {hex(checksum)} get {hex(cal_checksum)}") + ret = ErrType.SYS_CHECKSUM + + return ret + + def erase_flash(self): + ret = ErrType.OK + + if self.erase_info.memory_type == MemoryInfo.MEMORY_TYPE_NAND: + erase_total_size = self.erase_info.end_address - self.erase_info.start_address + erase_size = 0 + addr = self.erase_info.start_address + while addr < self.erase_info.end_address: + ret = self.floader_handler.erase_flash(self.erase_info.memory_type, + addr, + addr + self.device_info.flash_block_size(), + self.device_info.flash_block_size(), + nand_erase_timeout_in_second(self.device_info.flash_block_size(), + self.device_info.flash_block_size()), + sense=True) + if ret == ErrType.OK: + erase_size += self.device_info.flash_block_size() + # TODO:update erase progress + # self.logger.info(f"Erase progress: {int()}") + self.logger.debug( + f"NAND erase address ={hex(addr)}, size = {self.device_info.flash_block_size() / 1024}KB OK") + elif ret == ErrType.DEV_NAND_BAD_BLOCK: + self.logger.warning( + f"NAND erase address = {hex(addr)} size = {self.device_info.flash_block_size() / 1024}KB skipped: bad block") + ret = ErrType.OK + elif ret == ErrType.DEV_NAND_WORN_BLOCK: + self.logger.warning( + f"NAND erase address = {hex(addr)} size = {self.device_info.flash_block_size() / 1024}KB failed: mark warning block") + ret = ErrType.OK + else: + self.logger.warning( + f"NAND erase address = {hex(addr)} size = {self.device_info.flash_block_size() / 1024}KB failed: {ret}") + break + addr += self.device_info.flash_block_size() + + if ret == ErrType.OK: + self.logger.info(f"Erase nand done") + elif self.erase_info.memory_type == MemoryInfo.MEMORY_TYPE_NOR: + if self.setting.erase_by_block != 0: + addr = self.erase_info.start_address + size_erased = 0 + while size_erased < self.erase_info.size_in_byte(): + if ((addr % (64 * FlashUtils.NorDefaultPageSize.value)) == 0) and \ + (( + self.erase_info.size_in_byte() - size_erased) >= 64 * FlashUtils.NorDefaultPageSize.value): + block_size = 64 * FlashUtils.NorDefaultPageSize.value + elif ((addr % (32 * FlashUtils.NorDefaultPageSize.value)) == 0) and \ + (( + self.erase_info.size_in_byte() - size_erased) >= 32 * FlashUtils.NorDefaultPageSize.value): + block_size = 32 * FlashUtils.NorDefaultPageSize.value + else: + block_size = 4 * FlashUtils.NorDefaultPageSize.value + + need_sense = ((size_erased + block_size) >= self.erase_info.size_in_byte()) + ret = self.floader_handler.erase_flash(self.erase_info.memory_type, addr, addr + block_size, + block_size, + nor_erase_timeout_in_second( + divide_then_round_up(block_size, 1024)), + sense=need_sense) + if ret != ErrType.OK: + break + + addr += block_size + size_erased += block_size + # TODO:update erase progress + # self.logger.info(f"Erase Progress: {}") + else: + ret = self.floader_handler.erase_flash(self.erase_info.memory_type, + self.erase_info.start_address, + self.erase_info.end_address, + self.erase_info.size_in_kbyte, + nor_erase_timeout_in_second( + divide_then_round_up(self.erase_info.size_in_byte(), 1024)), + sense=True) + if ret == ErrType.OK: + self.logger.info(f"Erase nor done") + elif self.erase_info.memory_type == MemoryInfo.MEMORY_TYPE_RAM: + # memset 16MB ram, cost time: 0.01s + ret = self.floader_handler.erase_flash(self.erase_info.memory_type, + self.erase_info.start_address, + self.erase_info.end_address, + self.erase_info.size_in_byte(), + self.setting.sync_response_timeout_in_second, + sense=True) + if ret == ErrType.OK: + self.logger.info("Erase ram done") + else: + # TBD + pass + + return ret + + def set_spic_address_mode(self, mode): + ret = ErrType.OK + is_amebad = self.profile_info.is_amebad() + otp_spic_addr_mode_addr = OtpSpicAddrModeAddrForAmebaD if is_amebad else OtpSpicAddrModeAddr + otp_spic_addr_mode_mask = OtpSpicAddrModeMask if is_amebad else OtpSpicAddrModeMask + otp_spic_addr_mode_pos = OtpSpicAddrModePosForAmebaD if is_amebad else OtpSpicAddrModePos + + if self.device_info.is_boot_from_nand(): + return ret + + ret, buf = self.floader_handler.otp_read_logical_map(0, self.profile_info.logical_efuse_len) + if ret != ErrType.OK: + self.logger.error(f"Fail to read eFuse: {ret}") + return + + cfg = buf[otp_spic_addr_mode_addr] + if cfg != 0xFF: + if ((cfg & otp_spic_addr_mode_mask) >> otp_spic_addr_mode_pos) == mode: + self.logger.info(f"No need to change supported flash size") + return ErrType.OK + + cfg &= (~otp_spic_addr_mode_mask) + cfg |= ((mode << otp_spic_addr_mode_pos) & otp_spic_addr_mode_mask) + else: + if mode == SpicAddrMode.FOUR_BYTE_MODE.value: + cfg = OtpSpicAddrMode4Byte + else: + self.logger.info(f"No need to change default supported flash size") + return ErrType.OK + + # buf[otp_spic_addr_mode_addr] = cfg + buf_array = bytearray(buf) + buf_array[otp_spic_addr_mode_addr] = cfg + buf = bytes(buf_array) + self.logger.info(f"Program eFuse to change supported flash size {'>' if (mode == SpicAddrMode.FOUR_BYTE_MODE.value) else '<='}16MB") + + ret = self.floader_handler.otp_write_logical_map(otp_spic_addr_mode_addr, 1, buf) + if ret != ErrType.OK: + self.logger.error(f"Fail to program eFuse[{otp_spic_addr_mode_addr}]: {ret}") + return ret + time.sleep(0.01) + + ret, buf = self.floader_handler.otp_read_logical_map(0, self.profile_info.logical_efuse_len) + if ret != ErrType.OK: + self.logger.error(f"Fail to read eFuse: {ret}") + return ret + + cfg = buf[otp_spic_addr_mode_addr] + if ((cfg & otp_spic_addr_mode_mask) >> otp_spic_addr_mode_pos) == mode: + self.logger.info(f"Program eFuse done") + return ErrType.OK + else: + self.logger.error(f"Fail to verify eFuse[{otp_spic_addr_mode_addr}]") + self.logger.error(f"Fail to program eFuse") + return ErrType.SYS_CHECKSUM + + def get_spic_address_mode(self): + ret = ErrType.OK + is_amebad = self.profile_info.is_amebad() + otp_spic_addr_mode_addr = OtpSpicAddrModeAddrForAmebaD if is_amebad else OtpSpicAddrModeAddr + otp_spic_addr_mode_mask = OtpSpicAddrModeMask if is_amebad else OtpSpicAddrModeMask + otp_spic_addr_mode_4byte = OtpSpicAddrMode4ByteForAmebaD if is_amebad else OtpSpicAddrMode4Byte + mode = SpicAddrMode.THREE_BYTE_MODE.value + + if self.device_info.is_boot_from_nand(): + return ret, mode + + ret, buf = self.floader_handler.otp_read_logical_map(0, self.profile_info.logical_efuse_len) + if ret != ErrType.OK: + self.logger.error(f"Fail to read eFuse: {ret}") + return ret, mode + + cfg = buf[otp_spic_addr_mode_addr] + if cfg != 0xFF: + if (cfg & otp_spic_addr_mode_mask) == otp_spic_addr_mode_4byte: + mode = SpicAddrMode.FOUR_BYTE_MODE.value + self.logger.info(f"Current supported flash size >16MB") + else: + self.logger.info(f"Current supported flash size <=16MB") + else: + self.logger.info(f"Current supported flash size <=16MB as default") + + return ret, mode + + def check_supported_flash_size(self, memory_type): + ret = ErrType.OK + reset_system = False + + if memory_type == MemoryInfo.MEMORY_TYPE_NAND: + return ret, reset_system + + self.logger.info(f"Check supported flash size...") + ret = self.prepare(show_device_info=False) + if ret != ErrType.OK: + self.logger.error("Prepare for check supported flash size fail") + return ret, reset_system + + ret, mode = self.get_spic_address_mode() + if ret != ErrType.OK: + return ret, reset_system + + flash_size = self.device_info.flash_capacity // 1024 // 1024 # MB + if self.setting.auto_program_spic_addr_mode_4byte == 0: + if (mode == SpicAddrMode.THREE_BYTE_MODE.value) and (flash_size > 16): + self.logger.warning(f"Flash size: {flash_size}MB, OTP supported flash size <= 16MB") + ret = ErrType.SYS_CANCEL + if (mode == SpicAddrMode.FOUR_BYTE_MODE.value) and (flash_size <= 16): + self.logger.warning(f"Flash size: {flash_size}MB, OTP supported flash size >16MB") + ret = ErrType.SYS_CANCEL + else: + if mode == SpicAddrMode.THREE_BYTE_MODE.value and flash_size > 16: + self.logger.warning(f"OTP supports flash size <=16MB, tool will program OTP to support flash size >16MB") + ret = self.set_spic_address_mode(SpicAddrMode.FOUR_BYTE_MODE.value) + reset_system = True + elif mode == SpicAddrMode.FOUR_BYTE_MODE.value and flash_size <= 16: + self.logger.warning(f"OTP supports flash size >16MB, tool will program OTP to support flash size <=16MB") + ret = self.set_spic_address_mode(SpicAddrMode.THREE_BYTE_MODE.value) + reset_system = True + + if reset_system: + boot_delay = self.setting.usb_rom_boot_delay_in_second if self.profile_info.support_usb_download else self.setting.floader_boot_delay_in_second + # reset floader with next option + self.floader_handler.next_operation(NextOpType.REBURN, 0) + self.logger.info(f"Reburn delay {boot_delay}s") + time.sleep(boot_delay) + + return ret, reset_system diff --git a/base/efuse_data.py b/base/efuse_data.py new file mode 100644 index 0000000..d26f0b8 --- /dev/null +++ b/base/efuse_data.py @@ -0,0 +1,20 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +class EfuseData(): + def __init__(self, **kwargs): + self.length = 16 + + self.offset = kwargs.get("Offset", 0) + self.value = kwargs.get("Value", [0] * self.length) + + def __repr__(self): + efuse_data_dict = { + "Value": self.value, + "Offset": self.offset + } + + return efuse_data_dict \ No newline at end of file diff --git a/base/errno.py b/base/errno.py new file mode 100644 index 0000000..44d119e --- /dev/null +++ b/base/errno.py @@ -0,0 +1,41 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +import enum + +_DEV_ERR_BASE = 0X00E0 +_SYS_ERR_BASE = 0x0100 + + +class ErrType(enum.Enum): + OK = 0X00 + + DEV_ERR_BASE = _DEV_ERR_BASE + + DEV_BUSY = _DEV_ERR_BASE + 0X1 # Busy + DEV_TIMEOUT = _DEV_ERR_BASE + 0X2 # Operation timeout + DEV_FULL = _DEV_ERR_BASE + 0X4 # Operation timeout + DEV_INVALID = _DEV_ERR_BASE + 0x5 # device invalid + DEV_LENGTH = _DEV_ERR_BASE + 0x6 # device length + DEV_CHECKSUM = _DEV_ERR_BASE + 0x7 # device checksum + DEV_ADDRESS = _DEV_ERR_BASE + 0x8 # device address + DEV_NAND_BAD_BLOCK = _DEV_ERR_BASE + 0x9 # device nand bad block + DEV_NAND_WORN_BLOCK = _DEV_ERR_BASE + 0xA # device nand worn block + DEV_NAND_BIT_FLIP_WARNING = _DEV_ERR_BASE + 0xB # device nand bit flip warning + DEV_NAND_BIT_FLIP_ERROR = _DEV_ERR_BASE + 0xC # device nand bit flip error + DEV_NAND_BIT_FLIP_FATAL = _DEV_ERR_BASE + 0xD # device nand bit flip fatal + + SYS_ERR_BASE = _SYS_ERR_BASE + SYS_TIMEOUT = _SYS_ERR_BASE + 0x02 # Operation timeout + SYS_PARAMETER = _SYS_ERR_BASE + 0X3 # Invalid parameter + SYS_IO = _SYS_ERR_BASE + 0x05 # IO error + SYS_NAK = _SYS_ERR_BASE + 0x15 # Device NAK + SYS_PROTO = _SYS_ERR_BASE + 0x22 # Protocol error + SYS_CHECKSUM = _SYS_ERR_BASE + 0x23 # checksum error + SYS_OVERRANGE = _SYS_ERR_BASE + 0x24 # operation overrange + SYS_CANCEL = _SYS_ERR_BASE + 0x30 # Operation cancelled + SYS_UNKNOWN = _SYS_ERR_BASE + 0xEE # Unknown error + diff --git a/base/flash_utils.py b/base/flash_utils.py new file mode 100644 index 0000000..52e7896 --- /dev/null +++ b/base/flash_utils.py @@ -0,0 +1,145 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from .sys_utils import * + + +class FlashBPS: + def __init__(self): + self.need_unlock = False + self.protection = 0 + self.is_locked = False + + +class FlashUtils(Enum): + # NAND Flash MID + NandMfgDosilicon = 0xE5 + NandMfgGigadevice = 0xC8 + NandMfgMacronix = 0xC2 + NandMfgMicron = 0x2C + NandMfgWinbond = 0xEF + + # NAND Flash commands + NandCmdGetFeatures = 0x0F + NandCmdSetFeatures = 0x1F + + # NAND Flash registers + NandRegProtection = 0xA0 + NandRegFeature = 0xB0 + NandRegStatus = 0xC0 + NandRegProtectionBpMask = 0x38 + NandRegProtectionBpMaskWinbondMicron = 0x78 + + # NOR Flash command & registers + NorCmdReadStatusReg1 = 0x05 + NorCmdWriteStatusReg1 = 0x01 + NorStatusReg1BpMask = 0x3C + + # NOR Flash default page/block size + NorDefaultPageSize = 1024 + NorDefaultPagePerBlock = 4 + NorDefaultBlockSize = (NorDefaultPageSize * NorDefaultPagePerBlock) + + # NAND Flash default page/block size + NandDefaultPageSize = 2048 + NandDefaultPagePerBlock = 64 + NandDefaultBlockSize = (NandDefaultPageSize * NandDefaultPagePerBlock) + + # Flash write padding data + FlashWritePaddingData = 0xFF + + MinFlashProcessTimeoutInSecond = 1 + + # NOR Flash program / read / erase timeout + # Max page program time: typical 0.5ms for GD25Q128E, max 3ms for W25Q256JV + # Take 0.5ms * 10 = 5ms + NorPageProgramTimeoutInSeconds = 0.005 + # Max 4KB sector erase time: typical 70ms for GD25Q256D, max 400ms for GD25Q256D + # Take(400ms * 2) > (70ms * 10 = 700ms) + Nor4kSectorEraseTimeoutInSenonds = 0.8 + # Max 32KB block erase time: typical 0.16s for GD25Q128E, max 1.6s for W25Q256JV + # Take 2s > 0.16s * 10 = 1.6s + Nor32kBlockEraseTimeoutInSeconds = 2 # larger than max + # Max 64KB block erase time: typical 0.25s for GD25Q128E, max 1.6s for GD25Q128E + # Take 0.25s * 10 = 2.5s + Nor64kBlockEraseTimeoutInSeconds = 2.5 + # Max chip erase time: typical 150s for W25Q256JV, max 400s for W25Q256JV + # Take 1000s > 400s * 2 = 800s + NorChipEraseTimeoutInSeconds = 1000 + # Read flash with 1IO @ 10MHz(10Mbps) + NorRead1KiBTimeoutInSecond = 0.001 + # NOR calculate checksum with 1.5MB / s, test data(15.8MB / 8s) + NorCalculate1KiBChecksumTimeoutInSeconds = 0.001 + + # NAND Flash 4KB page size read / program / block erase timeout + # Page size read from Array with ECC(900us) + page size(4KB) read from cache with 1IO @ 10MHz(4KB / 10Mbps=3.2ms) + # Max read from array with ECC: 90us for MT29F4G01ABBF, max 170us for MT29F4G01ABBF + # Take 90us * 10 = 900us + 3.2ms + NandPageReadTimeoutInSeconds = 0.004 + # page program with ECC(4ms) + page size(4KB) load with 1IO @ 10MHz(4KB / 10Mbps=3.2ms) + # Max page program with ECC: 400us for MX35LF4GE4AD, max 800us for MX35LF4GE4AD + # Take 400us * 10 = 4ms + 3.2ms + NandPageProgramTimeoutInSeconds = 0.007 + # Max block erase time: typical 4ms for MX35LFxG24AD, max 10ms for MT29F2G01ABAG + # Take 4ms * 10 = 40ms + NandBlockEraseTimeoutInSeconds = 0.04 + # NAND calculate checksum with 1.5MB / s, test data(2KB / 4KB:47MB / 23s) + NandCalculate1KiBChecksumTimeoutInSeconds = 0.001 + + +def nor_read_timeout_in_second(size_in_byte): + return int(max(divide_then_round_up(size_in_byte, 1024) * FlashUtils.NorRead1KiBTimeoutInSecond.value, + FlashUtils.MinFlashProcessTimeoutInSecond.value)) + + +def nor_erase_timeout_in_second(size_in_kbyte): + if size_in_kbyte == 0xFFFFFFFF: + return int(max(FlashUtils.NorChipEraseTimeoutInSeconds.value, + FlashUtils.MinFlashProcessTimeoutInSecond.value)) + elif size_in_kbyte == 4: + return int(max(FlashUtils.Nor4kSectorEraseTimeoutInSenonds.value, + FlashUtils.MinFlashProcessTimeoutInSecond.value)) + elif size_in_kbyte == 32: + return int(max(FlashUtils.Nor32kBlockEraseTimeoutInSeconds.value, + FlashUtils.MinFlashProcessTimeoutInSecond.value)) + elif size_in_kbyte == 64: + return int(max(FlashUtils.Nor64kBlockEraseTimeoutInSeconds.value, + FlashUtils.MinFlashProcessTimeoutInSecond.value)) + else: + return int( + max(divide_then_round_up(size_in_kbyte, 4) * FlashUtils.Nor4kSectorEraseTimeoutInSenonds.value, + FlashUtils.MinFlashProcessTimeoutInSecond.value)) + + +def nor_checksum_timeout_in_second(size_in_byte): + return int(max(divide_then_round_up(size_in_byte, + 1024) * FlashUtils.NorCalculate1KiBChecksumTimeoutInSeconds.value, + FlashUtils.MinFlashProcessTimeoutInSecond.value)) + + +def nand_read_timeout_in_second(size_in_byte, page_size): + return int(max( + divide_then_round_up(size_in_byte, page_size) * FlashUtils.NandPageReadTimeoutInSeconds.value, + FlashUtils.MinFlashProcessTimeoutInSecond.value)) + + +def nand_program_timeout_in_second(size_in_byte, page_size): + return int(max( + divide_then_round_up(size_in_byte, page_size) * FlashUtils.NandPageProgramTimeoutInSeconds.value, + FlashUtils.MinFlashProcessTimeoutInSecond.value)) + + +def nand_erase_timeout_in_second(size_in_byte, block_size): + return int(max( + divide_then_round_up(size_in_byte, block_size) * FlashUtils.NandBlockEraseTimeoutInSeconds.value, + FlashUtils.MinFlashProcessTimeoutInSecond.value)) + + +def nand_checksum_timeout_in_second(size_in_byte, page_size): + return int(max( + divide_then_round_up(size_in_byte, 1024) * FlashUtils.NandCalculate1KiBChecksumTimeoutInSeconds.value, + FlashUtils.MinFlashProcessTimeoutInSecond.value)) diff --git a/base/floader_handler.py b/base/floader_handler.py new file mode 100644 index 0000000..eda67fc --- /dev/null +++ b/base/floader_handler.py @@ -0,0 +1,646 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +import time +import ctypes + +from .sense_status import * +from .device_info import * +from .next_op import * +from .flash_utils import * + +BAUDSET = 0x81 +QUERY = 0x02 +CONFIG = 0x83 +WRITE = 0x84 +READ = 0x05 +CHKSM = 0x06 +SENSE = 0x07 +NEXTOP = 0x08 + +FS_ERASE = 0xA0 +FS_RDSTS = 0x21 +FS_WTSTS = 0xA2 +FS_MKBAD = 0xA3 +FS_CHKMAP = 0x24 +FS_CHKBAD = 0x25 +FS_CHKBLK = 0x26 + +OTP_RRAW = 0x40 +OTP_WRAW = 0xC1 +OTP_RMAP = 0x42 +OTP_WMAP = 0xC3 + +ACK_BUF_EMPTY = 0xB0 +ACK_BUF_FULL = 0xB1 + +NP_EXIT = 0x01 +NP_BAUDRATE_RECOVER = 0x02 + +SOF = 0xA5 + +QUERY_DATA_OFFSET_DID = 0 +QUERY_DATA_OFFSET_IMAGE_TYPE = 2 +QUERY_DATA_OFFSET_CMD_SET_VERSION = 4 +QUERY_DATA_OFFSET_MEMORY_TYPE = 6 +QUERY_DATA_OFFSET_FLASH_MID = 7 +QUERY_DATA_OFFSET_FLASH_DID = 8 +QUERY_DATA_OFFSET_FLASH_MFG = 10 +QUERY_DATA_OFFSET_FLASH_MODEL = 22 +QUERY_DATA_OFFSET_FLASH_PAGE_SIZE = 42 +QUERY_DATA_OFFSET_FLASH_OOB_SIZE = 46 +QUERY_DATA_OFFSET_FLASH_PAGES_PER_BLOCK = 48 +QUERY_DATA_OFFSET_FLASH_BLOCKS_PER_LUN = 52 +QUERY_DATA_OFFSET_FLASH_LUNS_PER_TARGET = 56 +QUERY_DATA_OFFSET_FLASH_MAX_BAD_BLOCKS_PER_LUN = 57 +QUERY_DATA_OFFSET_FLASH_REQ_HOST_ECC_LEVEL = 59 +QUERY_DATA_OFFSET_FLASH_TARGETS = 60 +QUERY_DATA_OFFSET_FLASH_CAPACITY = 61 +QUERY_DATA_OFFSET_WIFI_MAC = 65 + +# Read otp logical map time:26ms, physical map time: 170ms +# Program otp logical map time: 7ms +OTP_READ_TIMEOUT_IN_SECONDS = 10 + + +class FloaderHandler(object): + def __init__(self, ameba_obj): + self.ameba = ameba_obj + self.serial_port = ameba_obj.serial_port + self.profile = ameba_obj.profile_info + self.logger = ameba_obj.logger + self.setting = ameba_obj.setting + super().__init__() + + def send_request(self, request, length, timeout, is_sync=True): + ret = ErrType.SYS_UNKNOWN + response_bytes = None + + len_l = length & 0xFF + len_h = (length >> 8) & 0xFF + + frame_data = [(SOF)] + frame_data.append(len_l) + frame_data.append(len_h) + frame_data.append((len_l ^ len_h) & 0xFF) + + frame_bytes = bytearray(frame_data) + frame_bytes += request[:length] + checksum = sum(request) + + frame_bytes += (checksum & 0xFF).to_bytes(1, byteorder="little") + + try: + retry = 0 + while retry < self.setting.request_retry_count: + retry += 1 + + self.serial_port.flushInput() + self.serial_port.flushOutput() + + self.ameba.write_bytes(frame_bytes) + self.logger.debug(f"Request: len={length}, payload={request.hex()}") + + ret, ret_byte = self.ameba.read_bytes(timeout) + if ret != ErrType.OK: + self.logger.error(f"Response error: {ret}, timeout:{timeout}") + continue + if is_sync: + if ret_byte[0] == SOF: + ret, ret_bytes = self.ameba.read_bytes(timeout, size=3) + if ret == ErrType.OK: + len_l = ret_bytes[0] + len_h = ret_bytes[1] + len_xor = ret_bytes[2] + response_len = (len_h << 8) + len_l + if len_xor == (len_l ^ len_h): + ret, response_bytes = self.ameba.read_bytes(self.setting.async_response_timeout_in_second, + size=response_len + 1) + if ret == ErrType.OK: + if response_len >= len(response_bytes) - 1: + self.logger.debug( + f"Response: len={response_len}, payload={response_bytes.hex()}") + checksm = sum(response_bytes[:response_len]) % 256 + if checksm == response_bytes[response_len]: + self.logger.debug(f"Checksum={checksm}({hex(checksm)}), ok") + break + else: + self.logger.debug( + f"Response checksum error: expect {hex(checksm)}, get {hex(response_bytes[response_len + 1])}") + ret = ErrType.SYS_CHECKSUM + else: + ret = ErrType.SYS_PROTO + self.logger.debug( + f"Response length error: expect {len_xor}, get {len_l ^ len_h}") + else: + self.logger.debug(f"Read response payload error: {ret}") + else: + ret = ErrType.SYS_PROTO + self.logger.debug(f"Response length check error: {ret_bytes.hex()}") + else: + ret = ErrType.SYS_PROTO + self.logger.error(f"Read response length error: {ret}") + elif ret_byte[0] >= ErrType.DEV_ERR_BASE.value: + ret = ret_byte + self.logger.debug(f"Negative response 0x{ret_byte.hex()}: ") + if ret_byte[0] == ErrType.DEV_FULL.value: + time.sleep(self.setting.request_retry_interval_second) + else: + ret = ErrType.SYS_PROTO + self.logger.debug(f"Unexpected response opcode {ret_byte.hex()}") + else: + if ret_byte[0] == ACK_BUF_FULL: + self.logger.debug(f"ACK: Rx buffer full, wait {self.setting.request_retry_interval_second}s") + time.sleep(self.setting.request_retry_interval_second) + ret = ErrType.OK + elif ret_byte[0] == ACK_BUF_EMPTY: + self.logger.debug(f"Response: ACK") + ret = ErrType.OK + break + elif ret_byte[0] >= ErrType.DEV_ERR_BASE.value: + ret = ret_byte + self.logger.debug(f"Negative response: {ret_byte}") + if ret_byte[0] == ErrType.DEV_FULL.value or ErrType.DEV_BUSY.value: + time.sleep(self.setting.request_retry_interval_second) + else: + ret = ErrType.SYS_PROTO + self.logger.debug(f"Unexpected response {ret_byte}") + + if ret == ErrType.OK or ret == ErrType.SYS_CANCEL: + break + else: + time.sleep(self.setting.request_retry_interval_second) + except Exception as err: + self.logger.debug(f"Response exception: {err}") + ret = ErrType.SYS_IO + + return ret, response_bytes + + def sense(self, timeout, op_code=None, data=None): + self.logger.debug(f"Sense...") + ret, sense_ack = self.send_request(SENSE.to_bytes(1, byteorder="little"), length=1, timeout=timeout) + if ret == ErrType.OK: + sense_status = SenseStatus() + self.logger.debug(f"Sense response raw data: {sense_ack.hex()}") + if sense_ack[0] == (SENSE): + ret = sense_status.parse(sense_ack, 1) + if ret == ErrType.OK: + self.logger.debug( + f"Sense response: opcode={hex(sense_status.op_code)}, status=0x{format(sense_status.status, '02x')}, data={hex(sense_status.data)}") + if sense_status.status != ErrType.OK.value: + ret = sense_status.status + self.logger.warning( + f"Sense fail: opcode={hex(sense_status.op_code)}, data={sense_status.data}, status={ret}") + elif (op_code is not None) and (sense_status.op_code != op_code): + ret = ErrType.SYS_PROTO + self.logger.error( + f"Sense protocol error: expect opcode-data {op_code.hex()}-{hex(data)}, get {hex(sense_status.op_code)}-{hex(sense_status.data)}") + else: + if (data is not None) and sense_status.data != data: + self.logger.debug( + f"Sense protocol warning: opcode {op_code} expect data {data}, get {sense_status.data}, ignored") + self.logger.debug("Sense ok") + else: + self.logger.debug(f"Sense fail to parse sense response") + else: + self.logger.debug(f"Sense fail: unexpected opcode {sense_ack[0]}") + else: + self.logger.debug(f"Sense fail: {ret}") + return ret, sense_ack + + def handshake(self, baudrate): + ret = ErrType.SYS_UNKNOWN + + self.logger.debug(f"Floader handshake start") + try: + if self.serial_port.baudrate != baudrate and self.setting.switch_baudrate_at_floader == 1: + baudset = [BAUDSET] + + baudset.extend(list(baudrate.to_bytes(4, byteorder="little"))) + + self.logger.debug(f"BAUDSET {baudrate}") + + baudset_bytes = bytearray(baudset) + ret, _ = self.send_request(baudset_bytes, len(baudset_bytes), self.setting.async_response_timeout_in_second, + is_sync=False) + if ret != ErrType.OK: + self.logger.debug(f"BAUDSET fail: {ret}") + return ret + ret = self.ameba.switch_baudrate(baudrate, self.setting.baudrate_switch_delay_in_second, True) + if ret != ErrType.OK: + self.logger.error(f"Fail to switch baudrate to {baudrate}: {ret}") + return ret + + for retry in range(3): + if retry > 0: + self.logger.debug(f"Sense retry {retry}#") + else: + self.logger.debug("Sense") + + ret, resp = self.sense(self.setting.sync_response_timeout_in_second) + if ret == ErrType.OK: + break + else: + self.logger.debug(f"Sense failed: {ret}") + time.sleep(self.setting.request_retry_interval_second) + continue + + if ret == ErrType.OK: + self.logger.debug(f"Floader handshake done") + else: + self.logger.error(f"Floader handshake timeout") + except Exception as err: + self.logger.error(f"Floader handshake exception: {err}") + + return ret + + def query(self): + device_info = DeviceInfo() + self.logger.debug(f"QUERY...") + ret, resp = self.send_request(QUERY.to_bytes(1, byteorder="little"), length=1, + timeout=self.setting.sync_response_timeout_in_second) + if ret == ErrType.OK: + if resp[0] == (QUERY): + device_info.did = resp[QUERY_DATA_OFFSET_DID + 1] + (resp[QUERY_DATA_OFFSET_DID + 2] << 8) + device_info.image_type = resp[QUERY_DATA_OFFSET_IMAGE_TYPE + 1] + ( + resp[QUERY_DATA_OFFSET_IMAGE_TYPE + 2] << 8) + device_info.cmd_set_version = resp[QUERY_DATA_OFFSET_CMD_SET_VERSION + 1] + ( + resp[QUERY_DATA_OFFSET_CMD_SET_VERSION + 2] << 8) + device_info.wifi_mac = resp[QUERY_DATA_OFFSET_WIFI_MAC + 1: QUERY_DATA_OFFSET_WIFI_MAC + 1 + 6] + device_info.memory_type = resp[QUERY_DATA_OFFSET_MEMORY_TYPE + 1] + device_info.flash_mid = resp[QUERY_DATA_OFFSET_FLASH_MID + 1] + device_info.flash_did = resp[QUERY_DATA_OFFSET_FLASH_DID + 1] + ( + resp[QUERY_DATA_OFFSET_FLASH_DID + 2] << 8) + device_info.flash_mfg = resp[ + QUERY_DATA_OFFSET_FLASH_MFG + 1: QUERY_DATA_OFFSET_FLASH_MFG + 1 + 12].decode("utf-8") + device_info.flash_model = resp[ + QUERY_DATA_OFFSET_FLASH_MODEL + 1: QUERY_DATA_OFFSET_FLASH_MODEL + 1 + 20].decode("utf-8") + device_info.flash_page_size = (resp[QUERY_DATA_OFFSET_FLASH_PAGE_SIZE + 1] + + (resp[QUERY_DATA_OFFSET_FLASH_PAGE_SIZE + 2] << 8) + + (resp[QUERY_DATA_OFFSET_FLASH_PAGE_SIZE + 3] << 16) + + (resp[QUERY_DATA_OFFSET_FLASH_PAGE_SIZE + 4] << 24)) + device_info.flash_oob_size = (resp[QUERY_DATA_OFFSET_FLASH_OOB_SIZE + 1] + ( + resp[QUERY_DATA_OFFSET_FLASH_OOB_SIZE + 2] << 8)) + device_info.flash_pages_per_block = (resp[QUERY_DATA_OFFSET_FLASH_PAGES_PER_BLOCK + 1] + + (resp[QUERY_DATA_OFFSET_FLASH_PAGES_PER_BLOCK + 2] << 8) + + (resp[QUERY_DATA_OFFSET_FLASH_PAGES_PER_BLOCK + 3] << 16) + + (resp[QUERY_DATA_OFFSET_FLASH_PAGES_PER_BLOCK + 4] << 24)) + device_info.flash_blocks_per_lun = (resp[QUERY_DATA_OFFSET_FLASH_BLOCKS_PER_LUN + 1] + + (resp[QUERY_DATA_OFFSET_FLASH_BLOCKS_PER_LUN + 2] << 8) + + (resp[QUERY_DATA_OFFSET_FLASH_BLOCKS_PER_LUN + 3] << 16) + + (resp[QUERY_DATA_OFFSET_FLASH_BLOCKS_PER_LUN + 4] << 24)) + device_info.flash_luns_per_target = resp[QUERY_DATA_OFFSET_FLASH_LUNS_PER_TARGET + 1] + device_info.flash_max_bad_block_per_lun = (resp[QUERY_DATA_OFFSET_FLASH_MAX_BAD_BLOCKS_PER_LUN + 1] + + (resp[ + QUERY_DATA_OFFSET_FLASH_MAX_BAD_BLOCKS_PER_LUN + 2] << 8)) + device_info.flash_req_host_ecc_level = resp[QUERY_DATA_OFFSET_FLASH_REQ_HOST_ECC_LEVEL + 1] + device_info.flash_targets = resp[QUERY_DATA_OFFSET_FLASH_TARGETS + 1] + device_info.flash_capacity = (resp[QUERY_DATA_OFFSET_FLASH_CAPACITY + 1] + + (resp[QUERY_DATA_OFFSET_FLASH_CAPACITY + 2] << 8) + + (resp[QUERY_DATA_OFFSET_FLASH_CAPACITY + 3] << 16) + + (resp[QUERY_DATA_OFFSET_FLASH_CAPACITY + 4] << 24)) + else: + self.logger.debug(f"Query: unexpected response: {resp[0]}") + ret = ErrType.SYS_PROTO + + return ret, device_info + + def config(self, configs): + config_data = [(CONFIG)] + + config_data.append(configs[0][0]) + config_data.append(configs[0][1]) + config_data.append(configs[0][2]) + config_data.append(configs[0][3]) + config_data.append(configs[0][4]) + config_data.append(configs[0][5]) + config_data.append(configs[0][6]) + config_data.append(configs[0][7]) + + config_data.append(configs[1][0]) + config_data.append(configs[1][1]) + config_data.append(configs[1][2]) + config_data.append(configs[1][3]) + config_data.append(configs[1][4]) + config_data.append(configs[1][5]) + config_data.append(configs[1][6]) + config_data.append(configs[1][7]) + + request_bytes = bytearray(config_data) + self.logger.debug(f"CONFIG: {configs[0].hex()} {configs[1].hex()}") + ret, _ = self.send_request(request_bytes, len(request_bytes), self.setting.async_response_timeout_in_second, is_sync=False) + + return ret + + def next_operation(self, opcode, operand): + request_data = [NEXTOP] + + request_data.append((opcode.value) & 0xFF) + request_data.extend(list(operand.to_bytes(4, byteorder="little"))) + + self.logger.debug(f"NEXTOTP: opecode={opcode}, operand={operand}") + + request_bytes = bytearray(request_data) + + ret, _ = self.send_request(request_bytes, len(request_bytes), self.setting.sync_response_timeout_in_second, is_sync=False) + + return ret + + def reset_in_download_mode(self): + self.logger.debug(f"Reset in download mode") + return self.next_operation(NextOpType.REBURN, 0) + + def write(self, mem_type, src, size, addr, timeout, need_sense=False): + sense_status = SenseStatus() + + write_data = [WRITE] + write_data.append(mem_type&0xFF) + write_data.extend(list(addr.to_bytes(4, byteorder="little"))) + + write_array = bytearray(write_data) + write_array += src[:size] + + self.logger.debug(f"WRITE: addr={hex(addr)}, size={size}, mem_type={mem_type}, need_sense={need_sense}") + ret, _ = self.send_request(write_array, len(write_array), self.setting.write_response_timeout_in_second, is_sync=False) + if ret == ErrType.OK: + if need_sense: + ret, sense_ack = self.sense(timeout, op_code=WRITE, data=addr) + if ret != ErrType.OK: + self.logger.error(f"WRITE addr={hex(addr)} fail: {ret}") + + return ret + + def read(self, mem_type, addr, size, timeout): + resp = None + + read_data = [READ] + read_data.append((mem_type & 0xFF)) + read_data.extend(list(addr.to_bytes(4, byteorder="little"))) + read_data.extend(list(size.to_bytes(4, byteorder="little"))) + + self.logger.debug(f"READ: addr={hex(addr)}, size={size}, mem_type={mem_type}") + read_bytes = bytearray(read_data) + ret, resp_ack = self.send_request(read_bytes, len(read_bytes), timeout) + if ret == ErrType.OK: + if resp_ack[0] == READ: + resp = resp_ack[1:] + else: + self.logger.debug(f"READ got unexpected response {hex(resp_ack[0])}") + ret = ErrType.SYS_PROTO + else: + self.logger.debug(f"READ fail: {ret}") + + return ret + + def checksum(self, mem_type, start_addr, end_addr, size, timeout): + chk_rest = 0 + request_data = [CHKSM] + request_data.append((mem_type & 0xFF)) + request_data.extend(list(start_addr.to_bytes(4, byteorder='little'))) + + request_data.extend(list(end_addr.to_bytes(4, byteorder='little'))) + + request_data.extend(list(size.to_bytes(4, byteorder='little'))) + + self.logger.debug(f"CHKSM: start={hex(start_addr)}, end={hex(end_addr)}, size={size}, mem_type={mem_type}") + request_bytes = bytearray(request_data) + ret, resp = self.send_request(request_bytes, len(request_bytes), timeout) + if ret == ErrType.OK: + if resp[0] == int(CHKSM): + chk_rest = resp[1] + (resp[2] << 8) + (resp[3] << 16) + (resp[4] << 24) + self.logger.debug(f"CHKSM: result={hex(chk_rest)}") + else: + self.logger.debug(f"CHKSM: unexpected response {resp[0]}") + ret = ErrType.SYS_PROTO + else: + self.logger.error(f"CHKSM fail: {ret}") + + return ret, chk_rest + + def erase_flash(self, mem_type, start_addr, end_addr, size, timeout, sense=False, force=False): + self.logger.debug(f"Erase flash: start_addr={hex(start_addr)}, end_addr={hex(end_addr)} size={size}") + request_data = [FS_ERASE] + request_data.append((mem_type & 0xFF)) + request_data.append(1 if force else 0) + + request_data.extend(list(start_addr.to_bytes(4, byteorder="little"))) + request_data.extend(list((end_addr & 0xFFFFFFFF).to_bytes(4, byteorder="little"))) + + request_data.extend(list((size & 0xFFFFFFFF).to_bytes(4, byteorder="little"))) + + if force: + self.logger.warning(f"FS_ERASE: start_addr={hex(start_addr)}, end_addr={hex(end_addr)}, size={size}, mem_type={mem_type} force") + else: + self.logger.debug(f"FS_ERASE: start_addr={hex(start_addr)}, end_addr={hex(end_addr)}, size={size}, mem_type={mem_type}") + + request_bytes = bytearray(request_data) + ret, _ = self.send_request(request_bytes, len(request_bytes), self.setting.async_response_timeout_in_second, is_sync=False) + if ret != ErrType.OK: + self.logger.warning(f"FS_ERASE start_addr={hex(start_addr)}, end_addr={hex(end_addr)}, size={size}, force={force}, fail:{ret}") + return ret + + if sense: + ret, sense_status = self.sense(timeout, op_code=FS_ERASE, data=start_addr) + if ret != ErrType.OK: + self.logger.error(f"FS_ERASE start_addr={hex(start_addr)}, size={size} force={force} fail: {ret}") + + return ret + + def read_status_register(self, cmd, address): + status = None + + request_data = [FS_RDSTS] + request_data.append(cmd) + request_data.append(address) + + self.logger.debug(f"FS_RDSTS: cmd={cmd}, address={address}") + + request_bytes = bytearray(request_data) + ret, resp = self.send_request(request_bytes, len(request_bytes), self.setting.sync_response_timeout_in_second) + if ret == ErrType.OK: + if resp[0] == FS_RDSTS: + status = resp[1] + self.logger.debug(f"FS_RDSTS: status={status}") + else: + self.logger.debug(f"FS_RDSTS: got unexpected response, {resp[0]}") + ret = ErrType.SYS_PROTO + else: + self.logger.debug(f"FS_RDSTS failed: {ret}") + + return ret, status + + def write_status_register(self, cmd, addr, value): + request_data = [FS_WTSTS] + request_data.append(cmd) + request_data.append(addr) + request_data.append(value) + + self.logger.debug(f"FS_WTSTS: cmd={hex(cmd)}, addr={hex(addr)}, value={hex(value)}") + + request_bytes = bytearray(request_data) + ret, _ = self.send_request(request_bytes, len(request_bytes), self.setting.async_response_timeout_in_second, is_sync=False) + + return ret + + def mark_bad_block(self, address): + request_data = [FS_MKBAD] + + request_data.extend(list(address.to_bytes(4, byteorder="little"))) + request_bytes = bytearray(request_data) + self.logger.debug(f"FS_MKBAD: addr={format(address, '08x')}") + return self.send_request(request_bytes, len(request_bytes), self.setting.async_response_timeout_in_second, is_sync=False) + + def check_bad_block(self, address): + ret = ErrType.OK + status = None + + request_data = [FS_CHKBAD] + + request_data.extend(list(address.to_bytes(4, byteorder="little"))) + request_bytes = bytearray(request_data) + self.logger.debug(f"FS_CHKBAD: addr={format(address, '08x')}") + + ret, resp = self.send_request(request_bytes, len(request_bytes), self.setting.sync_response_timeout_in_second) + if ret == ErrType.OK: + if resp[0] == FS_CHKBAD: + status = resp[1] + else: + self.logger.debug(f"FS_CHKBAD got unexpected response {hex(resp[0])}") + ret = ErrType.SYS_PROTO + else: + self.logger.debug(f"FS_CHKBAD fail: {ret}") + + return ret, status + + def check_block_status(self, address): + ret = ErrType.OK + block_status = 0 + page_status = [0, 0] + + request_data = [FS_CHKBLK] + + request_data.extend(list(address.to_bytes(4, byteorder="little"))) + request_bytes = bytearray(request_data) + self.logger.debug(f"FS_CHKBLK: addr={format(address, '08x')}") + + ret, resp = self.send_request(request_bytes, len(request_bytes), self.setting.sync_response_timeout_in_second) + if ret == ErrType.OK: + if resp[0] == FS_CHKBLK: + block_status = resp[1] + page_status[0] = ctypes.c_uint64(resp[2] + (resp[3] << 8) + (resp[4] << 16) + + (resp[5] << 24) + (resp[6] << 32) + (resp[7] << 40) + + (resp[8] << 48) + (resp[9] << 56)).value + page_status[1] = ctypes.c_uint64(resp[10] + (resp[11] << 8) + (resp[12] << 16) + + (resp[13] << 24) + (resp[14] << 32) + (resp[15] << 40) + + (resp[16] << 48) + (resp[17] << 56)).value + + self.logger.debug( + f"FS_CHKBLK: block_status={hex(block_status)}, page status={format(page_status[0], '16x')} {format(page_status[1], '16x')}") + else: + self.logger.debug(f"FS_CHKBLK fail: {ret}") + + return ret, block_status + + def check_map_status(self, address): + ret = ErrType.OK + + status = 0 + request_data = [FS_CHKMAP] + request_data.extend(list(address.to_bytes(4, byteorder="little"))) + request_bytes = bytearray(request_data) + self.logger.debug(f"FS_CHKMAP: addr={format(address, '08x')}") + + ret, resp = self.send_request(request_bytes, len(request_bytes), self.setting.sync_response_timeout_in_second) + if ret == ErrType.OK: + if resp[0] == FS_CHKMAP: + status = (resp[1] + (resp[2] << 8) + (resp[3] << 16) + (resp[4] << 24)) & 0xFFFFFFFF + self.logger.debug(f"FS_CHKMAP: status={format(status, '08x')}") + else: + self.logger.debug(f"FS_CHKMAP fail: {ret}") + + return ret, status + + def otp_read_map(self, cmd, address, size): + request_data = [cmd] + + request_data.extend(list(address.to_bytes(4, byteorder="little"))) + + request_data.extend(list(size.to_bytes(4, byteorder="little"))) + + request_bytes = bytearray(request_data) + + ret, buf = self.send_request(request_bytes, len(request_bytes), OTP_READ_TIMEOUT_IN_SECONDS) + if ret == ErrType.OK: + if buf[0] == cmd: + self.logger.debug(f"Otp read: {buf[0]}") + return ret, buf[1:-1] + else: + self.logger.debug(f"Otp read fail: unexpected response {buf[0]}") + ret = ErrType.SYS_PROTO + + return ret, buf + + def otp_write_map(self, cmd, address, size, data): + request_data = [cmd] + + request_data.extend(list(address.to_bytes(4, byteorder="little"))) + + request_data.extend(list(size.to_bytes(4, byteorder="little"))) + + for idx in range(size): + request_data.append(data[address + idx]) + + request_bytes = bytearray(request_data) + + ret, _ = self.send_request(request_bytes, len(request_bytes), self.setting.async_response_timeout_in_second, is_sync=False) + + return ret + + def otp_read_physical_map(self, address, size): + self.logger.debug(f"OTP_RRAW: addr={hex(address)}, size={size}") + + ret, buf = self.otp_read_map(OTP_RRAW, address, size) + if ret == ErrType.OK: + self.logger.debug(f"OTP_RRAW: {buf}") + self.logger.debug("OTP_RRAW done") + else: + self.logger.debug(f"OTP_RRAW fail: {ret}") + + return ret, buf + + def otp_write_physical_map(self, address, size, data): + self.logger.debug(f"OTP_WRAW: addr={hex(address)}, size={size}, data={data}") + + ret = self.otp_write_map(OTP_WMAP, address, size, data) + if ret == ErrType.OK: + self.logger.debug("OTP_WRAW done") + else: + self.logger.debug(f"OTP_WRAW fail: {ret}") + + return ret + + def otp_read_logical_map(self, address, size): + self.logger.debug(f"OTP_RMAP: addr={hex(address)}, size={size}") + + ret, buf = self.otp_read_map(OTP_RMAP, address, size) + if ret == ErrType.OK: + self.logger.debug(f"OTP_RMAP: {buf}") + self.logger.debug("OTP_RMAP done") + else: + self.logger.debug(f"OTP_RMAP fail: {ret}") + + return ret, buf + + def otp_write_logical_map(self, address, size, data): + self.logger.debug(f"OTP_WMAP: addr={hex(address)}, size={size}, data={data}") + + ret = self.otp_write_map(OTP_WMAP, address, size, data) + if ret == ErrType.OK: + self.logger.debug("OTP_WMAP done") + else: + self.logger.debug(f"OTP_WMAP fail: {ret}") + + return ret diff --git a/base/image_info.py b/base/image_info.py new file mode 100644 index 0000000..860d637 --- /dev/null +++ b/base/image_info.py @@ -0,0 +1,28 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +class ImageInfo(): + def __init__(self, **kwargs): + self.image_name = kwargs.get("ImageName", "") + self.start_address = kwargs.get("StartAddress", 0) + self.end_address = kwargs.get("EndAddress", 0) + self.memory_type = kwargs.get("MemoryType", 0) + self.full_erase = kwargs.get("FullErase", False) + self.mandatory = kwargs.get("Mandatory", False) + self.description = kwargs.get("Description", "") + + def __repr__(self): + image_info_dict = { + "ImageName": self.image_name, + "StartAddress": self.start_address, + "EndAddress": self.end_address, + "MemoryType": self.memory_type, + "FullErase": self.full_erase, + "Mandatory": self.mandatory, + "Description": self.description + } + + return image_info_dict \ No newline at end of file diff --git a/base/json_utils.py b/base/json_utils.py new file mode 100644 index 0000000..715a0ee --- /dev/null +++ b/base/json_utils.py @@ -0,0 +1,48 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +import os +import json +import base64 +from pyDes import * + +_DES_KEY = "574C414E" # 0x574C414E, WLAN +_DES_IV = [0x40, 0x52, 0x65, 0x61, 0x6C, 0x73, 0x69, 0x6C] # @Realsil + + +class JsonUtils: + @staticmethod + def load_from_file(file_path, need_decrypt=True): + result = None + if os.path.exists(file_path): + with open(file_path, 'r', encoding='utf-8') as f: + f_content = f.read() + + if need_decrypt: + k = des(_DES_KEY, CBC, _DES_IV, padmode=PAD_PKCS5) + des_k = k.decrypt(base64.b64decode(f_content)) + profile_str = des_k.decode("utf-8") + result = json.loads(profile_str) + else: + result = json.loads(f_content) + + return result + + @staticmethod + def save_to_file(file_path, data, need_encrypt=False): + path_dir = os.path.dirname(file_path) + os.makedirs(path_dir, exist_ok=True) + if need_encrypt: + ek = des(_DES_KEY.encode("utf-8"), CBC, _DES_IV, pad=None, padmode=PAD_PKCS5) + en_bytes = ek.encrypt(json.dumps(data).encode("utf-8")) + save_data = base64.b64encode(en_bytes) + with open(file_path, "wb") as json_file: + json_file.write(save_data) + else: + save_data = data + + with open(file_path, "w") as json_file: + json.dump(save_data, json_file, indent=2) diff --git a/base/memory_info.py b/base/memory_info.py new file mode 100644 index 0000000..efaf2e3 --- /dev/null +++ b/base/memory_info.py @@ -0,0 +1,24 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + + +class MemoryInfo: + MEMORY_TYPE_RAM = 0 + MEMORY_TYPE_NOR = 1 + MEMORY_TYPE_NAND = 2 + MEMORY_TYPE_VENDOR = 3 + MEMORY_TYPE_HYBRID = 4 + MAX_PARTITION_MEMORY_TYPE = MEMORY_TYPE_VENDOR + MAX_DEVICE_MEMORY_TYPE = MEMORY_TYPE_HYBRID + + def __init__(self): + self.start_address = 0 + self.end_address = 0 + self.memory_type = self.MEMORY_TYPE_RAM + self.size_in_kbyte = 0 + + def size_in_byte(self): + return self.size_in_kbyte * 1024 diff --git a/base/next_op.py b/base/next_op.py new file mode 100644 index 0000000..600e40c --- /dev/null +++ b/base/next_op.py @@ -0,0 +1,15 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum + + +class NextOpType(Enum): + NONE = 0 # Do nothing, hold on in download mode + INDICATION = 1 # Indicate download result via PWM/GPIO + RESET = 2 # Exit download mode + BOOT = 3 # Jump to Ram + REBURN = 4 # Reset into download mode again \ No newline at end of file diff --git a/base/remote_serial.py b/base/remote_serial.py new file mode 100644 index 0000000..cafc8f6 --- /dev/null +++ b/base/remote_serial.py @@ -0,0 +1,371 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +import socket +import json +import time +import threading +import base64 +from typing import Optional, Dict, Any +import serial +from serial.serialutil import SerialException, SerialTimeoutException +from datetime import datetime + + +class RemoteSerial: + def __init__(self, logger, remote_server: str, remote_port: int, port: str, baudrate: int = 9600): + """ + Initialize remote serial proxy + :param logger: logger object (external input, for unified log format) + :param remote_server: remote serial server IP + :param remote_port: remote serial server port + :param port: remote serial port name (e.g. "COM3", "/dev/ttyUSB0") + :param baudrate: serial baud rate + """ + self.logger = logger + self.remote_server = remote_server + self.remote_port = remote_port + self.port = port + self.baudrate = baudrate + + # Core state variables + self.tcp_socket: Optional[socket.socket] = None + self.receive_buffer = b"" # Binary receive buffer + self.receive_thread: Optional[threading.Thread] = None + self.is_open = False # Whether serial is open (TCP connected + serial command succeeds) + self.response_event = threading.Event() # Command response synchronization event + self.last_response: Dict[str, Any] = {} # Last command response + + # Initialize logger + self.logger.debug( + f"[RemoteSerial][{self.port}] Initialize remote serial proxy - " + f"Server: {self.remote_server}:{self.remote_port}, baudrate: {self.baudrate}" + ) + # 1. Establish TCP connection (just connect, not start the receive thread) + self._connect_tcp() + self.receive_thread = threading.Thread( + target=self._receive_loop, + daemon=True, + name=f"RemoteSerialRecv-{self.port}" + ) + self.receive_thread.start() + self.logger.debug(f"[RemoteSerial][{self.port}] Receive thread started: {self.receive_thread.name}") + + def _connect_tcp(self) -> bool: + """ + Establish TCP connection with remote serial server + :return: Returns True if connected successfully, raises SerialException on failure + """ + self.logger.debug(f"[RemoteSerial][{self.port}] Trying TCP connection: {self.remote_server}:{self.remote_port}") + try: + self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + # self.tcp_socket.settimeout(10) # TCP connect timeout 10s + self.tcp_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + self.logger.debug(f"[RemoteSerial][{self.port}] TCP_NODELAY set") + self.tcp_socket.connect((self.remote_server, self.remote_port)) + self.logger.debug(f"[RemoteSerial][{self.port}] TCP connection succeeded") + return True + except socket.timeout: + raise SerialException(f"[RemoteSerial][{self.port}] TCP connection timed out (10s)- {self.remote_server}:{self.remote_port}") + except ConnectionRefusedError: + raise SerialException(f"[RemoteSerial][{self.port}] TCP connection refused - server not started or wrong port") + except Exception as e: + raise SerialException(f"[RemoteSerial][{self.port}] TCP connection failed: {str(e)}") + + def _receive_loop(self): + """ + Core logic of receive thread: continuously read TCP data, parse into buffer or trigger response + """ + self.logger.debug(f"[RemoteSerial][{self.port}] Receive thread running") + buffer = "" + while self.tcp_socket: + try: + # Read TCP data (as text, as server sends JSON+newline-delimited) + data = self.tcp_socket.recv(4096).decode('utf-8', errors='strict') + if not data: + self.logger.error(f"[RemoteSerial][{self.port}] Server closed connection, recv returned empty data") + raise ConnectionAbortedError("Server closed connection") + + buffer += data + + # Split by newline for complete messages (handle sticky packets) + while '\n' in buffer: + msg_str, buffer = buffer.split('\n', 1) + msg_str = msg_str.strip() + if not msg_str: + continue + self._parse_message(msg_str) + except ConnectionResetError as e: + self.logger.error(f"[RemoteSerial][{self.port}] Connection reset: {str(e)}") + break + except ConnectionAbortedError as e: + self.logger.error(f"[RemoteSerial][{self.port}] Receive thread exception: {str(e)}") + break + except Exception as e: + self.logger.error(f"[RemoteSerial][{self.port}] Receive thread exception: {str(e)}", exc_info=True) + break + + self.is_open = False + if self.tcp_socket: + self.tcp_socket.close() + self.logger.info(f"[RemoteSerial][{self.port}] Receive thread exited") + + def _parse_message(self, msg_str: str): + """ + Parse JSON message from server + :param msg_str: Complete JSON string (without newline) + """ + try: + msg = json.loads(msg_str) + msg_type = msg.get("type") + + if msg_type == "command_response": + self.last_response = msg + self.logger.debug( + f"[RemoteSerial][{self.port}] Received command response - " + f"Success: {msg.get('success')}, message: {msg.get('message', '无')}" + ) + self.response_event.set() + + elif msg_type == "serial_data": + if msg.get("port") != self.port: + self.logger.warning(f"[RemoteSerial][{self.port}] Received data from other port (ignored): {msg.get('port')}") + return + + base64_data = msg.get("data", "") + if not base64_data: + self.logger.warning(f"[RemoteSerial][{self.port}] Serial data empty (Base64)") + return + + try: + raw_data = base64.b64decode(base64_data, validate=True) + self.receive_buffer += raw_data + except base64.binascii.Error as e: + self.logger.error(f"[RemoteSerial][{self.port}] Base64 decode failed: {str(e)}, data: {base64_data[:100]}...") + + else: + self.logger.warning(f"[RemoteSerial][{self.port}] Unknown message type: {msg_type}, message: {msg_str[:200]}...") + + except json.JSONDecodeError as e: + self.logger.error(f"[RemoteSerial][{self.port}] JSON parse failed: {str(e)}, raw: {msg_str[:200]}...") + + def _send_command(self, cmd: Dict[str, Any], timeout: float = 5.0) -> Dict[str, Any]: + """ + Send command to remote server and wait for response + :param cmd: command dict (will be converted to JSON) + :param timeout: response timeout (seconds) + :return: server response dict + """ + if not self.is_open or not self.tcp_socket: + raise SerialException(f"[RemoteSerial][{self.port}] Send command failed: serial not open") + + self.response_event.clear() + self.last_response = {} + cmd_str = json.dumps(cmd) + "\n" + + try: + self.logger.debug(f"[RemoteSerial][{self.port}] Sending command (timeout {timeout}s): {cmd_str.strip()[:300]}...") + self.tcp_socket.sendall(cmd_str.encode('utf-8')) + + # Wait for response or timeout + if self.response_event.wait(timeout): + return self.last_response + else: + raise SerialTimeoutException( + f"[RemoteSerial][{self.port}] Command response timeout ({timeout}s) - Command: {cmd.get('type')}" + ) + except Exception as e: + raise SerialException(f"[RemoteSerial][{self.port}] Send command exception: {str(e)}") + + def validate(self, password): + try: + validate_cmd = { + "type": "validate", + "password": password + } + self.is_open = True + resp = self._send_command(validate_cmd, timeout=10.0) + + if not resp.get("success", False): + self.logger.debug(f"[RemoteSerial][{self.port}] Validate failed") + raise SerialException(f"[RemoteSerial][{self.port}] Remote serial validate failed: Wrong password") + self.is_open = False + self.logger.debug(f"[RemoteSerial][{self.port}] Remote serial port validate successfully") + except Exception as e: + self.close() + raise SerialException(f"[RemoteSerial][{self.port}] Validate serial failed: {str(e)}") + + def open(self): + if self.is_open: + self.logger.debug(f"[RemoteSerial][{self.port}] Serial already opened, skip") + return + self.logger.debug(f"[RemoteSerial][{self.port}] Trying to open serial") + try: + self.is_open = True + open_cmd = { + "type": "open_port", + "port": self.port, + "options": { + "baudRate": self.baudrate, + "dataBits": 8, + "stopBits": 1, + "parity": "none", + "timeout": 0.1 + } + } + resp = self._send_command(open_cmd, timeout=10.0) + + if not resp.get("success", False): + self.logger.debug(f"[RemoteSerial][{self.port}] Open failed") + self.is_open = False + err_msg = resp.get("message", "Unknown error") + raise SerialException(f"[RemoteSerial][{self.port}] Remote serial open failed: {err_msg}") + + # set is_open and start receive thread + self.is_open = True + + self.logger.debug(f"[RemoteSerial][{self.port}] Remote serial port opened successfully (baudrate: {self.baudrate})") + except Exception as e: + self.close() + raise SerialException(f"[RemoteSerial][{self.port}] Open serial failed: {str(e)}") + + def close(self): + """ + Close remote serial: send close_port command + cleanup TCP and thread + """ + self.logger.debug(f"[RemoteSerial][{self.port}] Start closing remote serial") + + # 1. Mark state as closed (end receive thread loop) + if not self.is_open: + self.logger.debug(f"[RemoteSerial][{self.port}] Serial already closed, skip") + return + # 2. Send close serial command (try even if TCP error) + try: + if self.tcp_socket: + close_cmd = {"type": "close_port", "port": self.port} + resp = self._send_command(close_cmd, timeout=3.0) + if resp.get("success", False): + self.logger.debug(f"[RemoteSerial][{self.port}] Remote serial close command succeeded") + else: + self.logger.warning(f"[RemoteSerial][{self.port}] Remote serial close command failed: {resp.get('message', 'No response')}") + except Exception as e: + self.logger.error(f"[RemoteSerial][{self.port}] Send close command exception: {str(e)}") + + self.is_open = False + self.receive_buffer = b"" + self.logger.debug(f"[RemoteSerial][{self.port}] Remote serial closed") + + def write(self, data: bytes): + """ + Write binary data to remote serial (Base64 encode first) + :param data: binary data to send + """ + if not self.is_open: + raise SerialException(f"[RemoteSerial][{self.port}] Write failed: serial not open") + if not data: + self.logger.debug(f"[RemoteSerial][{self.port}] Write empty data (ignored)") + return + + try: + base64_data = base64.b64encode(data).decode('utf-8') + write_cmd = { + "type": "write_data", + "port": self.port, + "data": base64_data + } + + self.logger.debug( + f"[RemoteSerial][{self.port}] Write data - " + f"Raw length: {len(data)}B, Base64 length: {len(base64_data)}B" + ) + resp = self._send_command(write_cmd, timeout=10.0) + + if not resp.get("success", False): + raise SerialException(f"[RemoteSerial][{self.port}] Write data failed: {resp.get('message', 'Unknown error')}") + self.logger.debug(f"[RemoteSerial][{self.port}] Write data succeeded") + except Exception as e: + raise SerialException(f"[RemoteSerial][{self.port}] Write exception: {str(e)}") + + def read(self, size: int = 1) -> bytes: + """ + Read specified length of binary data from receive buffer + :param size: length to read (bytes) + :return: binary data read (returns actual length if less than size) + """ + if not self.is_open: + raise SerialException(f"[RemoteSerial][{self.port}] Read failed: serial not open") + + # Get data from buffer + read_data = self.receive_buffer[:size] + self.receive_buffer = self.receive_buffer[size:] + self.logger.debug( + f"[RemoteSerial][{self.port}] Read data - " + f"Requested: {size}B, Read: {len(read_data)}B, Buffer left: {len(self.receive_buffer)}B" + ) + return read_data + + def inWaiting(self) -> int: + """ + Return number of bytes waiting in receive buffer + :return: buffer length + """ + if not self.is_open: + raise SerialException(f"[RemoteSerial][{self.port}] Read failed: serial not open") + waiting = len(self.receive_buffer) + return waiting + + def flushInput(self): + """ + Clear receive buffer (local only, no server interaction) + """ + old_len = len(self.receive_buffer) + self.receive_buffer = b"" + self.logger.debug(f"[RemoteSerial][{self.port}] Cleared receive buffer - old length: {old_len}B") + + def flushOutput(self): + """ + Flush output buffer (no local output buffer for remote serial, just for serial.Serial API compatibility) + """ + self.logger.debug(f"[RemoteSerial][{self.port}] flushOutput: No action required for remote serial (no local output buffer)") + + # ------------------------------ + # Simulate serial.Serial's DTR/RTS properties + # ------------------------------ + @property + def dtr(self): + self.logger.debug(f"[RemoteSerial][{self.port}] Get DTR: not supported, return False") + return False + + @dtr.setter + def dtr(self, value): + self.logger.debug(f"[RemoteSerial][{self.port}] Set DTR: not supported, ignored value={value}") + + @property + def rts(self): + self.logger.debug(f"[RemoteSerial][{self.port}] Get RTS: not supported, return False") + return False + + @rts.setter + def rts(self, value): + self.logger.debug(f"[RemoteSerial][{self.port}] Set RTS: not supported, ignored value={value}") + # ------------------------------ + # Context manager support (with statement) + # ------------------------------ + def __enter__(self): + self.open() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + self.logger.info(f"[RemoteSerial][{self.port}] __exit__ close") + if exc_type: + self.logger.error(f"[RemoteSerial][{self.port}] Context manager exception: {exc_type.__name__}: {exc_val}") + + def __del__(self): + """Destructor: make sure to close resource""" + if self.is_open: + self.logger.debug(f"[RemoteSerial][{self.port}] Destructor: closing unreleased remote serial") + self.close() \ No newline at end of file diff --git a/base/rom_handler.py b/base/rom_handler.py new file mode 100644 index 0000000..046ec70 --- /dev/null +++ b/base/rom_handler.py @@ -0,0 +1,322 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +import io +import os +import sys +import time + +from .errno import * +from .rtk_utils import * + +STX = 0x02 # Transfer data +EOT = 0x04 # End of transfer +BAUDSET = 0x05 # For handshake +BAUDCHK = 0x07 # For handshake, only for UART +ACK = 0x06 # Acknowledge +NAK = 0x15 # Negativ acknowledge +CAN = 0x18 # Cancel transfer, only for UART +ESC = 0x1B # Cancel transfer + +STX_BASE_LEN = 8 +STX_UART_RAM_DATA_LEN = 1024 +STX_USB_RAM_DATA_LEN = 2048 +STX_MAX_LEN = STX_BASE_LEN + STX_USB_RAM_DATA_LEN +FloaderSizeLimit = 1024 * 1024 # 1MB + +StxBaseLen = 1024 +StxUartDataLen = 1024 +StxUsbDataLen = 2048 +StxMaxDataLen = StxBaseLen + StxUsbDataLen +DEFAULT_TIMEOUT = 0.5 +STX_TIMEOUT = 1 + +FloaderDictionary = "Devices/Floaders" + + +class RomHandler(object): + def __init__(self, ameba_obj, padding="FF"): + self.ameba = ameba_obj + self.serial_port = ameba_obj.serial_port + self.logger = ameba_obj.logger + self.profile = ameba_obj.profile_info + self.baudrate = ameba_obj.baudrate + self.is_usb = ameba_obj.is_usb + self.stx_packet_no = 1 + self.padding = padding + self.setting = ameba_obj.setting + + def get_baudrate_idx(self, rate): + ''' rom built-in rate table ''' + return { + 110: 0, + 300: 1, + 600: 2, + 1200: 3, + 2400: 4, + 4800: 5, + 9600: 6, + 14400: 7, + 19200: 8, + 28800: 9, + 38400: 10, + 57600: 11, + 76800: 12, + 115200: 13, + 128000: 14, + 153600: 15, + 230400: 16, + 380400: 17, + 460800: 18, + 500000: 19, + 921600: 20, + 1000000: 21, + 1382400: 22, + 1444400: 23, + 1500000: 24, + 1843200: 25, + 2000000: 26, + 2100000: 27, + 2764800: 28, + 3000000: 29, + 3250000: 30, + 3692300: 31, + 3750000: 32, + 4000000: 33, + 6000000: 34 + }.get(rate, 13) + + def send_request(self, request, length, timeout): + ret = ErrType.SYS_UNKNOWN + response = [] + + try: + for retry in range(2): + if retry > 0: + self.logger.debug(f"Request retry {retry}#: len={length}, payload={request.hex()}") + else: + self.logger.debug(f"Request: len={length}, payload={request.hex()}") + + self.serial_port.flushInput() + self.serial_port.flushOutput() + + self.ameba.write_bytes(request) + + for resp_retry in range(3): + ret, ch = self.ameba.read_bytes(timeout) + if ret != ErrType.OK: + self.logger.debug(f"Reponse error: {ret}") + break + + if ch[0] == ACK: + self.logger.debug(f"Response ACK") + break + elif ch[0] == NAK: + ret = ErrType.SYS_NAK + self.logger.debug(f"Response NAK") + continue + elif ch[0] == CAN: + ret = ErrType.SYS_CANCEL + self.logger.debug(f"Response CAN") + break + else: + ret = ErrType.SYS_PROTO + self.logger.debug(f"Unexpected reponse: 0x{ch.hex()}") + continue + + if ret == ErrType.OK or ret == ErrType.SYS_CANCEL: + break + else: + time.sleep(self.setting.request_retry_interval_second) + except Exception as err: + self.logger.error(f"Send request exception: {err}") + ret = ErrType.SYS_IO + + return ret + + def reset(self): + self.stx_packet_no = 1 + + def handshake(self): + ret = ErrType.OK + self.logger.debug(f"Handshake:{'USB' if self.is_usb else 'UART'}") + index = self.get_baudrate_idx(self.baudrate) + + if not self.is_usb: + ret = self.check_alive() + if ret != ErrType.OK: + self.logger.debug(f"Handshake fail: device not alive") + return ret + + retry = 0 + while retry < 2: + if retry > 0: + self.logger.debug(f"Handshake retry {retry}#") + + retry += 1 + + if self.setting.switch_baudrate_at_floader == 0: + self.logger.debug(f"BAUDSET: {index} ({self.baudrate})") + _bytes = [BAUDSET] + _bytes.append(index) + cmd = bytearray(_bytes) + + ret = self.send_request(cmd, 2, DEFAULT_TIMEOUT) + if ret == ErrType.OK: + self.logger.debug(f"BAUDSET ok") + else: + self.logger.debug(f"BAUDSET fail: {ret}") + continue + + if self.is_usb: + break + + ret = self.ameba.switch_baudrate(self.baudrate, self.setting.baudrate_switch_delay_in_second) + if ret != ErrType.OK: + continue + + self.logger.debug(f"BAUDCHK") + ret = self.send_request(BAUDCHK.to_bytes(1, byteorder="little"), 1, DEFAULT_TIMEOUT) + if ret == ErrType.OK: + self.logger.debug("BAUDCHK ok") + break + else: + self.logger.error(f"BAUDCHK fail: {ret}") + else: + if self.is_usb: + self.logger.debug(f"BAUDSET") + _bytes = [BAUDSET] + _bytes.append(index) + cmd = bytearray(_bytes) + ret = self.send_request(cmd, 2, DEFAULT_TIMEOUT) + if ret == ErrType.OK: + self.logger.debug(f"Baudset ok") + break + else: + self.logger.debug(f"Baudset fail: {ret}") + else: + self.logger.debug("BAUDCHK") + ret = self.send_request(BAUDCHK.to_bytes(1, byteorder="little"), 1, DEFAULT_TIMEOUT) + if ret == ErrType.OK: + self.logger.debug(f"Baudchk ok") + break + else: + self.logger.debug(f"Baudchk fail: {ret}") + + if ret == ErrType.OK: + self.logger.debug("Handshake done") + else: + self.logger.debug(f"Handshake fail: {ret}") + + return ret + + def check_alive(self): + ret, ch = self.ameba.read_bytes(DEFAULT_TIMEOUT) + if ret == ErrType.OK: + if ch[0] != NAK: + self.logger.debug(f"Check alive error,expect NAK, get 0x{ch.hex()}") + else: + self.logger.debug(f"Check alive error: {ret}") + + return ret + + def transfer(self, address, data_bytes): + self.logger.debug(f"STX {self.stx_packet_no}#: addr={hex(address)}") + stx_data = [STX] + stx_data.append(self.stx_packet_no & 0xFF) + stx_data.append((~self.stx_packet_no) & 0xFF) + + stx_data.extend(list(address.to_bytes(4, byteorder='little'))) + + stx_bytes = bytearray(stx_data) + stx_bytes += data_bytes + + checksum = sum(stx_bytes[3:]) % 256 + stx_bytes += checksum.to_bytes(1, byteorder="little") + + ret = self.send_request(stx_bytes, len(stx_bytes), STX_TIMEOUT) + if ret == ErrType.OK: + self.logger.debug(f"STX {self.stx_packet_no}# done") + self.stx_packet_no += 1 + else: + self.logger.debug(f"STX {self.stx_packet_no}# fail: {ret}") + + return ret + + def end_transfer(self): + self.logger.debug(f"EOT") + return self.send_request(EOT.to_bytes(1, byteorder="little"), 1, DEFAULT_TIMEOUT) + + def abort(self): + self.logger.debug(f"ESC") + self.send_request(ESC.to_bytes(1, byteorder="little"), 1, DEFAULT_TIMEOUT) + + def get_page_aligned_size(self, size, page_size): + result = size + + if (size % page_size) != 0: + result = (size // page_size + 1) * page_size + + return result + + def get_floader_path(self): + floader_path = None + + if self.profile.floader: + floader_path = os.path.realpath(os.path.join(RtkUtils.get_executable_root_path(), FloaderDictionary, self.profile.floader)) + + if floader_path is None: + self.logger.error("Flashloader not specified in device profile") + elif not os.path.exists(floader_path): + self.logger.error(f"Flashloader not exists: {floader_path}") + + return floader_path + + def download_floader(self): + ret = ErrType.OK + page_size = StxUsbDataLen if self.is_usb else StxUartDataLen + self.reset() + + floader = self.get_floader_path() + if floader is None: + return ErrType.SYS_PARAMETER + with open(floader, 'rb') as stream: + floader_content = stream.read() + + floader_size = len(floader_content) + if floader_size > FloaderSizeLimit: + self.logger.error(f"Invalid floader {floader} with too large size: {floader_size}") + return ErrType.SYS_OVERRANGE + + _baud_bytes = self.serial_port.baudrate.to_bytes(4, byteorder='little') + + floader_aligned_size = self.get_page_aligned_size(floader_size, page_size) + datas = io.BytesIO(floader_content) + data_bytes = datas.read(floader_size) + + data_bytes += _baud_bytes + + data_bytes = data_bytes.ljust(floader_aligned_size, b"\x00") + + idx = 0 + floader_addr = self.profile.floader_address + while idx < floader_aligned_size: + trans_data = data_bytes[idx:idx+page_size] + ret = self.transfer(floader_addr, trans_data) + if ret != ErrType.OK: + return ret + idx += page_size + floader_addr += page_size + + ret = self.end_transfer() + if ret != ErrType.OK: + if self.profile.is_amebaz(): + # AmebaZ would tx ACK after rx EOT but would not wait tx ACK done, so in low baudrate(115200...), + # ACK will not be received + # This issue has been fixed after AmebaD + ret = ErrType.OK + + return ret diff --git a/base/rt_settings.py b/base/rt_settings.py new file mode 100644 index 0000000..c5a1588 --- /dev/null +++ b/base/rt_settings.py @@ -0,0 +1,72 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + + +class RtSettings(): + MIN_ROM_BOOT_DELAY_IN_MILLISECOND = 50 + MIN_USB_ROM_BOOT_DELAY_IN_MILLISECOND = 200 + MIN_USB_FLOADER_BOOT_DELAY_IN_MILLISECOND = 200 + + FLASH_PROTECTION_PROCESS_PROMPT = 0 + FLASH_PROTECTION_PROCESS_TRY = 1 + FLASH_PROTECTION_PROCESS_UNLOCK = 2 + FLASH_PROTECTION_PROCESS_ABORT = 3 + + def __init__(self, **kwargs): + self.sense_packet_count = kwargs.get("SensePacketCount", 32) + self.request_retry_count = kwargs.get("RequestRetryCount", 3) + self.request_retry_interval_second = round(kwargs.get("RequestRetryIntervalInMillisecond", 10) / 1000, 2) + self.async_response_timeout_in_second = round(kwargs.get("AsyncResponseTimeoutInMilliseccond", 1000) / 1000, 2) + self.sync_response_timeout_in_second = round(kwargs.get("SyncResponseTimeoutInMillisecond", 1000) / 1000, 2) + self.baudrate_switch_delay_in_second = round(kwargs.get("BaudrateSwitchDelayInMillisecond", 200) / 1000, 2) + self.rom_boot_delay_in_second = round(max(kwargs.get("RomBootDelayInMillisecond", 100), self.MIN_ROM_BOOT_DELAY_IN_MILLISECOND) / 1000, 2) + self.usb_rom_boot_delay_in_second = round(max(kwargs.get("UsbRomBootDelayInMillisecond", 1000), self.MIN_USB_ROM_BOOT_DELAY_IN_MILLISECOND) / 1000, 2) + self.usb_floader_boot_delay_in_second = round(max(kwargs.get("UsbFloaderBootDelayInMillisecond", 1000), self.MIN_USB_FLOADER_BOOT_DELAY_IN_MILLISECOND) / 1000, 2) + self.switch_baudrate_at_floader = kwargs.get("SwitchBaudrateAtFloader", 0) + self.write_response_timeout_in_second = round(kwargs.get("WriteResponseTimeoutInMillisecond", 2000) / 1000, 2) + self.floader_boot_delay_in_second = round(kwargs.get("FloaderBootDelayInMillisecond", 1000) / 1000, 2) + self.auto_switch_to_download_mode_with_dtr_rts = kwargs.get("AutoSwitchToDownloadModeWithDtrRts", 0) + self.auto_reset_device_with_dtr_rts = kwargs.get("AutoResetDeviceWithDtrRts", 0) + self.flash_protection_process = kwargs.get("FlashProtectionProcess", self.FLASH_PROTECTION_PROCESS_PROMPT) + self.erase_by_block = kwargs.get("EraseByBlock", 0) + self.program_config1 = kwargs.get("ProgramConfig1", 0) + self.program_config2 = kwargs.get("ProgramConfig2", 0) + self.disable_nand_access_with_uart = kwargs.get("DisableNandAccessWithUart", 0) + self.ram_download_padding_byte = kwargs.get("RamDownloadPaddingByte", 0x00) + self.auto_program_spic_addr_mode_4byte = kwargs.get("AutoProgramSpicAddrMode4Byte", 0) + self.auto_switch_to_download_mode_with_dtr_rts_file = kwargs.get("AutoSwitchToDownloadModeWithDtrRtsTimingFile", "Reburn.cfg") + self.auto_reset_device_with_dtr_rts_file = kwargs.get("AutoResetDeviceWithDtrRtsTimingFile", "Reset.cfg") + self.post_process = kwargs.get("PostProcess", "RESET") + + def __repr__(self): + profile_dict = { + "SensePacketCount": self.sense_packet_count, + "RequestRetryCount": self.request_retry_count, + "RequestRetryIntervalInMillisecond": int(self.request_retry_interval_second * 1000), + "AsyncResponseTimeoutInMilliseccond": int(self.async_response_timeout_in_second * 1000), + "SyncResponseTimeoutInMillisecond": int(self.sync_response_timeout_in_second * 1000), + "BaudrateSwitchDelayInMillisecond": int(self.baudrate_switch_delay_in_second * 1000), + "RomBootDelayInMillisecond": int(self.rom_boot_delay_in_second * 1000), + "UsbRomBootDelayInMillisecond": int(self.usb_rom_boot_delay_in_second * 1000), + "UsbFloaderBootDelayInMillisecond": int(self.usb_floader_boot_delay_in_second * 1000), + "SwitchBaudrateAtFloader": self.switch_baudrate_at_floader, + "WriteResponseTimeoutInMillisecond": int(self.write_response_timeout_in_second * 1000), + "FloaderBootDelayInMillisecond": int(self.floader_boot_delay_in_second * 1000), + "AutoSwitchToDownloadModeWithDtrRts": self.auto_switch_to_download_mode_with_dtr_rts, + "AutoResetDeviceWithDtrRts": self.auto_reset_device_with_dtr_rts, + "FlashProtectionProcess": self.flash_protection_process, + "EraseByBlock": self.erase_by_block, + "ProgramConfig1": self.program_config1, + "ProgramConfig2": self.program_config2, + "DisableNandAccessWithUart": self.disable_nand_access_with_uart, + "RamDownloadPaddingByte": self.ram_download_padding_byte, + "AutoProgramSpicAddrMode4Byte": self.auto_program_spic_addr_mode_4byte, + "AutoSwitchToDownloadModeWithDtrRtsTimingFile": self.auto_switch_to_download_mode_with_dtr_rts_file, + "AutoResetDeviceWithDtrRtsTimingFile": self.auto_reset_device_with_dtr_rts_file, + "PostProcess": self.post_process + } + + return profile_dict \ No newline at end of file diff --git a/base/rtk_flash_type.py b/base/rtk_flash_type.py new file mode 100644 index 0000000..fc66df6 --- /dev/null +++ b/base/rtk_flash_type.py @@ -0,0 +1,12 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum + + +class RtkFlashType(Enum): + NOR = 0 + NAND = 1 diff --git a/base/rtk_logging.py b/base/rtk_logging.py new file mode 100644 index 0000000..977acbf --- /dev/null +++ b/base/rtk_logging.py @@ -0,0 +1,49 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +import sys +import logging +from colorama import Fore, Style, init + +# init Colorama +init(autoreset=True) + + +def create_logger(name, log_level="INFO", stream=sys.stdout, file=None): + if log_level == "DEBUG": + level = logging.DEBUG + elif log_level == "WARNING": + level = logging.WARNING + elif log_level == "ERROR": + level = logging.ERROR + elif log_level == "FATAL": + level = logging.FATAL + else: + level = logging.INFO + + logger = logging.getLogger(name) + if not logger.handlers: + formatter = logging.Formatter( + fmt=f'[%(asctime)s.%(msecs)03d][%(levelname)s] [{name}]%(message)s', + datefmt='%Y-%m-%d %H:%M:%S') + + logging.addLevelName(logging.DEBUG, f"D") + logging.addLevelName(logging.INFO, f"I") + logging.addLevelName(logging.WARNING, f"{Fore.YELLOW}W{Style.RESET_ALL}") + logging.addLevelName(logging.ERROR, f"{Fore.RED}E{Style.RESET_ALL}") + logging.addLevelName(logging.FATAL, f"{Fore.RED}{Style.BRIGHT}F{Style.RESET_ALL}") + consoleHandler = logging.StreamHandler(stream) + consoleHandler.setFormatter(formatter) + logger.addHandler(consoleHandler) + + if file is not None: + fileHandler = logging.FileHandler(file, mode='a') + fileHandler.setFormatter(formatter) + logger.addHandler(fileHandler) + + logger.propagate = False # Prevent logging from propagating to the root logger + logger.setLevel(level) + return logger diff --git a/base/rtk_utils.py b/base/rtk_utils.py new file mode 100644 index 0000000..6fd59f3 --- /dev/null +++ b/base/rtk_utils.py @@ -0,0 +1,21 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +import os +import sys + + +class RtkUtils: + @staticmethod + def get_executable_root_path(): + if getattr(sys, 'frozen', False): # judge if frozen as exe + # get exe dir + executable_root = os.path.dirname(os.path.abspath(sys.executable)) + else: + # get py dir + executable_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + return executable_root \ No newline at end of file diff --git a/base/sense_status.py b/base/sense_status.py new file mode 100644 index 0000000..78282d5 --- /dev/null +++ b/base/sense_status.py @@ -0,0 +1,24 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +from .errno import * + + +class SenseStatus: + def __init__(self): + self.op_code = None + self.status = None + self.data = None + + def parse(self, data, offset): + ret = ErrType.SYS_OVERRANGE + if len(data) >= offset + 6: + self.op_code = data[offset] + self.status = data[offset + 1] + self.data = data[offset+2] + (data[offset+3]<<8) + (data[offset+4]<<16) + (data[offset+5]<<24) + ret = ErrType.OK + + return ret \ No newline at end of file diff --git a/base/spic_addr_mode.py b/base/spic_addr_mode.py new file mode 100644 index 0000000..81f5d22 --- /dev/null +++ b/base/spic_addr_mode.py @@ -0,0 +1,12 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum + + +class SpicAddrMode(Enum): + THREE_BYTE_MODE = 0 + FOUR_BYTE_MODE = 1 diff --git a/base/sys_utils.py b/base/sys_utils.py new file mode 100644 index 0000000..2abc752 --- /dev/null +++ b/base/sys_utils.py @@ -0,0 +1,9 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + + +def divide_then_round_up(dividend, divisor): + return int((dividend + divisor - 1) / divisor) diff --git a/base/version.py b/base/version.py new file mode 100644 index 0000000..4c4917f --- /dev/null +++ b/base/version.py @@ -0,0 +1,16 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +class Version: + def __init__(self, version_str): + # parser version_str with pattern major.minor.micro + parts = version_str.split(".") + self.major = int(parts[0]) + self.minor = int(parts[1] if len(parts) > 1 else 0) + self.micro = int(parts[2] if len(parts) > 2 else 0) + + def __repr__(self): + return f"{self.major}.{self.minor}.{self.micro}" diff --git a/build/flash/Analysis-01.toc b/build/flash/Analysis-01.toc new file mode 100644 index 0000000..40d5031 --- /dev/null +++ b/build/flash/Analysis-01.toc @@ -0,0 +1,997 @@ +(['C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\flash.py'], + ['C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2'], + [], + [('C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\numpy\\_pyinstaller', + 0), + ('C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\pygame\\__pyinstaller', + 0), + ('C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\webview\\__pyinstaller', + 0), + ('C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\stdhooks', + -1000), + ('C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\_pyinstaller_hooks_contrib', + -1000)], + {}, + [], + [], + False, + {}, + 0, + [], + [], + '3.11.4 (tags/v3.11.4:d2340ef, Jun 7 2023, 05:45:37) [MSC v.1934 64 bit ' + '(AMD64)]', + [('pyi_rth_inspect', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py', + 'PYSOURCE'), + ('flash', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\flash.py', + 'PYSOURCE')], + [('zipfile', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\zipfile.py', + 'PYMODULE'), + ('py_compile', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\py_compile.py', + 'PYMODULE'), + ('importlib.machinery', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\machinery.py', + 'PYMODULE'), + ('importlib', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\__init__.py', + 'PYMODULE'), + ('importlib._bootstrap', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\_bootstrap.py', + 'PYMODULE'), + ('importlib._bootstrap_external', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\_bootstrap_external.py', + 'PYMODULE'), + ('importlib.metadata', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\__init__.py', + 'PYMODULE'), + ('typing', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\typing.py', + 'PYMODULE'), + ('importlib.abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\abc.py', + 'PYMODULE'), + ('importlib.resources.abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\abc.py', + 'PYMODULE'), + ('importlib.resources', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\__init__.py', + 'PYMODULE'), + ('importlib.resources._legacy', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\_legacy.py', + 'PYMODULE'), + ('importlib.resources._common', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\_common.py', + 'PYMODULE'), + ('importlib.resources._adapters', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\_adapters.py', + 'PYMODULE'), + ('tempfile', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\tempfile.py', + 'PYMODULE'), + ('random', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\random.py', + 'PYMODULE'), + ('statistics', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\statistics.py', + 'PYMODULE'), + ('decimal', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\decimal.py', + 'PYMODULE'), + ('_pydecimal', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_pydecimal.py', + 'PYMODULE'), + ('contextvars', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\contextvars.py', + 'PYMODULE'), + ('fractions', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\fractions.py', + 'PYMODULE'), + ('numbers', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\numbers.py', + 'PYMODULE'), + ('hashlib', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\hashlib.py', + 'PYMODULE'), + ('logging', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\logging\\__init__.py', + 'PYMODULE'), + ('pickle', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\pickle.py', + 'PYMODULE'), + ('pprint', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\pprint.py', + 'PYMODULE'), + ('dataclasses', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\dataclasses.py', + 'PYMODULE'), + ('_compat_pickle', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_compat_pickle.py', + 'PYMODULE'), + ('string', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\string.py', + 'PYMODULE'), + ('bisect', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\bisect.py', + 'PYMODULE'), + ('importlib._abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\_abc.py', + 'PYMODULE'), + ('importlib.metadata._itertools', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\_itertools.py', + 'PYMODULE'), + ('importlib.metadata._functools', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\_functools.py', + 'PYMODULE'), + ('importlib.metadata._collections', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\_collections.py', + 'PYMODULE'), + ('importlib.metadata._meta', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\_meta.py', + 'PYMODULE'), + ('importlib.metadata._adapters', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\_adapters.py', + 'PYMODULE'), + ('importlib.metadata._text', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\_text.py', + 'PYMODULE'), + ('email.message', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\message.py', + 'PYMODULE'), + ('email.policy', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\policy.py', + 'PYMODULE'), + ('email.contentmanager', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\contentmanager.py', + 'PYMODULE'), + ('email.quoprimime', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\quoprimime.py', + 'PYMODULE'), + ('email.headerregistry', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\headerregistry.py', + 'PYMODULE'), + ('email._header_value_parser', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\_header_value_parser.py', + 'PYMODULE'), + ('urllib', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\__init__.py', + 'PYMODULE'), + ('email.iterators', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\iterators.py', + 'PYMODULE'), + ('email.generator', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\generator.py', + 'PYMODULE'), + ('email._encoded_words', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\_encoded_words.py', + 'PYMODULE'), + ('email.charset', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\charset.py', + 'PYMODULE'), + ('email.encoders', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\encoders.py', + 'PYMODULE'), + ('email.base64mime', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\base64mime.py', + 'PYMODULE'), + ('email._policybase', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\_policybase.py', + 'PYMODULE'), + ('email.header', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\header.py', + 'PYMODULE'), + ('email.errors', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\errors.py', + 'PYMODULE'), + ('email.utils', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\utils.py', + 'PYMODULE'), + ('email._parseaddr', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\_parseaddr.py', + 'PYMODULE'), + ('calendar', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\calendar.py', + 'PYMODULE'), + ('urllib.parse', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\parse.py', + 'PYMODULE'), + ('ipaddress', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ipaddress.py', + 'PYMODULE'), + ('datetime', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\datetime.py', + 'PYMODULE'), + ('_strptime', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_strptime.py', + 'PYMODULE'), + ('socket', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\socket.py', + 'PYMODULE'), + ('selectors', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\selectors.py', + 'PYMODULE'), + ('quopri', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\quopri.py', + 'PYMODULE'), + ('getopt', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\getopt.py', + 'PYMODULE'), + ('gettext', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\gettext.py', + 'PYMODULE'), + ('textwrap', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\textwrap.py', + 'PYMODULE'), + ('email', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\__init__.py', + 'PYMODULE'), + ('email.parser', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\parser.py', + 'PYMODULE'), + ('email.feedparser', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\feedparser.py', + 'PYMODULE'), + ('csv', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\csv.py', + 'PYMODULE'), + ('importlib.readers', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\readers.py', + 'PYMODULE'), + ('importlib.resources.readers', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\readers.py', + 'PYMODULE'), + ('importlib.resources._itertools', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\_itertools.py', + 'PYMODULE'), + ('tokenize', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\tokenize.py', + 'PYMODULE'), + ('token', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\token.py', + 'PYMODULE'), + ('lzma', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\lzma.py', + 'PYMODULE'), + ('_compression', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_compression.py', + 'PYMODULE'), + ('bz2', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\bz2.py', + 'PYMODULE'), + ('pathlib', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\pathlib.py', + 'PYMODULE'), + ('fnmatch', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\fnmatch.py', + 'PYMODULE'), + ('contextlib', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\contextlib.py', + 'PYMODULE'), + ('struct', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\struct.py', + 'PYMODULE'), + ('shutil', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\shutil.py', + 'PYMODULE'), + ('tarfile', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\tarfile.py', + 'PYMODULE'), + ('gzip', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\gzip.py', + 'PYMODULE'), + ('importlib.util', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\util.py', + 'PYMODULE'), + ('inspect', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\inspect.py', + 'PYMODULE'), + ('dis', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\dis.py', + 'PYMODULE'), + ('opcode', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\opcode.py', + 'PYMODULE'), + ('ast', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ast.py', + 'PYMODULE'), + ('tracemalloc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\tracemalloc.py', + 'PYMODULE'), + ('_py_abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_py_abc.py', + 'PYMODULE'), + ('stringprep', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\stringprep.py', + 'PYMODULE'), + ('version_info', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\version_info.py', + 'PYMODULE'), + ('base', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\__init__.py', + 'PYMODULE'), + ('base.rt_settings', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rt_settings.py', + 'PYMODULE'), + ('base.rtk_logging', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rtk_logging.py', + 'PYMODULE'), + ('colorama', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\colorama\\__init__.py', + 'PYMODULE'), + ('colorama.ansitowin32', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\colorama\\ansitowin32.py', + 'PYMODULE'), + ('colorama.winterm', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\colorama\\winterm.py', + 'PYMODULE'), + ('colorama.ansi', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\colorama\\ansi.py', + 'PYMODULE'), + ('colorama.initialise', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\colorama\\initialise.py', + 'PYMODULE'), + ('colorama.win32', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\colorama\\win32.py', + 'PYMODULE'), + ('ctypes.wintypes', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ctypes\\wintypes.py', + 'PYMODULE'), + ('ctypes', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ctypes\\__init__.py', + 'PYMODULE'), + ('ctypes._endian', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ctypes\\_endian.py', + 'PYMODULE'), + ('base.download_handler', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\download_handler.py', + 'PYMODULE'), + ('base.remote_serial', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\remote_serial.py', + 'PYMODULE'), + ('serial.serialutil', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\serialutil.py', + 'PYMODULE'), + ('__future__', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\__future__.py', + 'PYMODULE'), + ('json', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\__init__.py', + 'PYMODULE'), + ('json.encoder', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\encoder.py', + 'PYMODULE'), + ('json.decoder', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\decoder.py', + 'PYMODULE'), + ('json.scanner', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\scanner.py', + 'PYMODULE'), + ('base.config_utils', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\config_utils.py', + 'PYMODULE'), + ('base.memory_info', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\memory_info.py', + 'PYMODULE'), + ('base.spic_addr_mode', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\spic_addr_mode.py', + 'PYMODULE'), + ('base.json_utils', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\json_utils.py', + 'PYMODULE'), + ('pyDes', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\pyDes.py', + 'PYMODULE'), + ('base.device_profile', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\device_profile.py', + 'PYMODULE'), + ('base.version', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\version.py', + 'PYMODULE'), + ('base.efuse_data', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\efuse_data.py', + 'PYMODULE'), + ('base.image_info', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\image_info.py', + 'PYMODULE'), + ('base.flash_utils', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\flash_utils.py', + 'PYMODULE'), + ('base.sys_utils', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\sys_utils.py', + 'PYMODULE'), + ('base.floader_handler', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\floader_handler.py', + 'PYMODULE'), + ('base.next_op', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\next_op.py', + 'PYMODULE'), + ('base.device_info', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\device_info.py', + 'PYMODULE'), + ('base.rtk_flash_type', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rtk_flash_type.py', + 'PYMODULE'), + ('base.sense_status', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\sense_status.py', + 'PYMODULE'), + ('base.errno', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\errno.py', + 'PYMODULE'), + ('base.rom_handler', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rom_handler.py', + 'PYMODULE'), + ('base.rtk_utils', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rtk_utils.py', + 'PYMODULE'), + ('serial', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\__init__.py', + 'PYMODULE'), + ('serial.serialjava', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\serialjava.py', + 'PYMODULE'), + ('serial.serialposix', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\serialposix.py', + 'PYMODULE'), + ('serial.serialwin32', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\serialwin32.py', + 'PYMODULE'), + ('serial.win32', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\win32.py', + 'PYMODULE'), + ('serial.serialcli', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\serialcli.py', + 'PYMODULE'), + ('serial.tools.list_ports', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\list_ports.py', + 'PYMODULE'), + ('serial.tools', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\__init__.py', + 'PYMODULE'), + ('serial.tools.list_ports_common', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\list_ports_common.py', + 'PYMODULE'), + ('glob', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\glob.py', + 'PYMODULE'), + ('serial.tools.list_ports_posix', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\list_ports_posix.py', + 'PYMODULE'), + ('serial.tools.list_ports_osx', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\list_ports_osx.py', + 'PYMODULE'), + ('serial.tools.list_ports_linux', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\list_ports_linux.py', + 'PYMODULE'), + ('serial.tools.list_ports_windows', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\list_ports_windows.py', + 'PYMODULE'), + ('copy', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\copy.py', + 'PYMODULE'), + ('threading', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\threading.py', + 'PYMODULE'), + ('_threading_local', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_threading_local.py', + 'PYMODULE'), + ('base64', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\base64.py', + 'PYMODULE'), + ('argparse', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\argparse.py', + 'PYMODULE'), + ('subprocess', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\subprocess.py', + 'PYMODULE'), + ('signal', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\signal.py', + 'PYMODULE')], + [('python311.dll', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\python311.dll', + 'BINARY'), + ('_decimal.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_decimal.pyd', + 'EXTENSION'), + ('_hashlib.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_hashlib.pyd', + 'EXTENSION'), + ('unicodedata.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\unicodedata.pyd', + 'EXTENSION'), + ('select.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\select.pyd', + 'EXTENSION'), + ('_socket.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_socket.pyd', + 'EXTENSION'), + ('_lzma.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_lzma.pyd', + 'EXTENSION'), + ('_bz2.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_bz2.pyd', + 'EXTENSION'), + ('_ctypes.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_ctypes.pyd', + 'EXTENSION'), + ('VCRUNTIME140.dll', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\VCRUNTIME140.dll', + 'BINARY'), + ('libcrypto-1_1.dll', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\libcrypto-1_1.dll', + 'BINARY'), + ('libffi-8.dll', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\libffi-8.dll', + 'BINARY')], + [], + [], + [('base_library.zip', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\base_library.zip', + 'DATA')], + [('warnings', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\warnings.py', + 'PYMODULE'), + ('genericpath', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\genericpath.py', + 'PYMODULE'), + ('reprlib', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\reprlib.py', + 'PYMODULE'), + ('copyreg', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\copyreg.py', + 'PYMODULE'), + ('posixpath', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\posixpath.py', + 'PYMODULE'), + ('keyword', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\keyword.py', + 'PYMODULE'), + ('abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\abc.py', + 'PYMODULE'), + ('_collections_abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_collections_abc.py', + 'PYMODULE'), + ('ntpath', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ntpath.py', + 'PYMODULE'), + ('weakref', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\weakref.py', + 'PYMODULE'), + ('operator', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\operator.py', + 'PYMODULE'), + ('io', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\io.py', + 'PYMODULE'), + ('locale', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\locale.py', + 'PYMODULE'), + ('stat', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\stat.py', + 'PYMODULE'), + ('enum', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\enum.py', + 'PYMODULE'), + ('types', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\types.py', + 'PYMODULE'), + ('traceback', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\traceback.py', + 'PYMODULE'), + ('sre_compile', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\sre_compile.py', + 'PYMODULE'), + ('heapq', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\heapq.py', + 'PYMODULE'), + ('re._parser', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_parser.py', + 'PYMODULE'), + ('re._constants', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_constants.py', + 'PYMODULE'), + ('re._compiler', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_compiler.py', + 'PYMODULE'), + ('re._casefix', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_casefix.py', + 'PYMODULE'), + ('_weakrefset', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_weakrefset.py', + 'PYMODULE'), + ('functools', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\functools.py', + 'PYMODULE'), + ('collections.abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\collections\\abc.py', + 'PYMODULE'), + ('collections', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\collections\\__init__.py', + 'PYMODULE'), + ('sre_constants', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\sre_constants.py', + 'PYMODULE'), + ('sre_parse', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\sre_parse.py', + 'PYMODULE'), + ('encodings.zlib_codec', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\zlib_codec.py', + 'PYMODULE'), + ('encodings.uu_codec', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\uu_codec.py', + 'PYMODULE'), + ('encodings.utf_8_sig', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\utf_8_sig.py', + 'PYMODULE'), + ('encodings.utf_8', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\utf_8.py', + 'PYMODULE'), + ('encodings.utf_7', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\utf_7.py', + 'PYMODULE'), + ('encodings.utf_32_le', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\utf_32_le.py', + 'PYMODULE'), + ('encodings.utf_32_be', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\utf_32_be.py', + 'PYMODULE'), + ('encodings.utf_32', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\utf_32.py', + 'PYMODULE'), + ('encodings.utf_16_le', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\utf_16_le.py', + 'PYMODULE'), + ('encodings.utf_16_be', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\utf_16_be.py', + 'PYMODULE'), + ('encodings.utf_16', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\utf_16.py', + 'PYMODULE'), + ('encodings.unicode_escape', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\unicode_escape.py', + 'PYMODULE'), + ('encodings.undefined', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\undefined.py', + 'PYMODULE'), + ('encodings.tis_620', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\tis_620.py', + 'PYMODULE'), + ('encodings.shift_jisx0213', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\shift_jisx0213.py', + 'PYMODULE'), + ('encodings.shift_jis_2004', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\shift_jis_2004.py', + 'PYMODULE'), + ('encodings.shift_jis', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\shift_jis.py', + 'PYMODULE'), + ('encodings.rot_13', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\rot_13.py', + 'PYMODULE'), + ('encodings.raw_unicode_escape', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\raw_unicode_escape.py', + 'PYMODULE'), + ('encodings.quopri_codec', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\quopri_codec.py', + 'PYMODULE'), + ('encodings.punycode', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\punycode.py', + 'PYMODULE'), + ('encodings.ptcp154', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\ptcp154.py', + 'PYMODULE'), + ('encodings.palmos', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\palmos.py', + 'PYMODULE'), + ('encodings.oem', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\oem.py', + 'PYMODULE'), + ('encodings.mbcs', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\mbcs.py', + 'PYMODULE'), + ('encodings.mac_turkish', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\mac_turkish.py', + 'PYMODULE'), + ('encodings.mac_romanian', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\mac_romanian.py', + 'PYMODULE'), + ('encodings.mac_roman', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\mac_roman.py', + 'PYMODULE'), + ('encodings.mac_latin2', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\mac_latin2.py', + 'PYMODULE'), + ('encodings.mac_iceland', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\mac_iceland.py', + 'PYMODULE'), + ('encodings.mac_greek', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\mac_greek.py', + 'PYMODULE'), + ('encodings.mac_farsi', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\mac_farsi.py', + 'PYMODULE'), + ('encodings.mac_cyrillic', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\mac_cyrillic.py', + 'PYMODULE'), + ('encodings.mac_croatian', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\mac_croatian.py', + 'PYMODULE'), + ('encodings.mac_arabic', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\mac_arabic.py', + 'PYMODULE'), + ('encodings.latin_1', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\latin_1.py', + 'PYMODULE'), + ('encodings.kz1048', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\kz1048.py', + 'PYMODULE'), + ('encodings.koi8_u', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\koi8_u.py', + 'PYMODULE'), + ('encodings.koi8_t', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\koi8_t.py', + 'PYMODULE'), + ('encodings.koi8_r', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\koi8_r.py', + 'PYMODULE'), + ('encodings.johab', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\johab.py', + 'PYMODULE'), + ('encodings.iso8859_9', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_9.py', + 'PYMODULE'), + ('encodings.iso8859_8', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_8.py', + 'PYMODULE'), + ('encodings.iso8859_7', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_7.py', + 'PYMODULE'), + ('encodings.iso8859_6', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_6.py', + 'PYMODULE'), + ('encodings.iso8859_5', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_5.py', + 'PYMODULE'), + ('encodings.iso8859_4', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_4.py', + 'PYMODULE'), + ('encodings.iso8859_3', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_3.py', + 'PYMODULE'), + ('encodings.iso8859_2', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_2.py', + 'PYMODULE'), + ('encodings.iso8859_16', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_16.py', + 'PYMODULE'), + ('encodings.iso8859_15', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_15.py', + 'PYMODULE'), + ('encodings.iso8859_14', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_14.py', + 'PYMODULE'), + ('encodings.iso8859_13', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_13.py', + 'PYMODULE'), + ('encodings.iso8859_11', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_11.py', + 'PYMODULE'), + ('encodings.iso8859_10', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_10.py', + 'PYMODULE'), + ('encodings.iso8859_1', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso8859_1.py', + 'PYMODULE'), + ('encodings.iso2022_kr', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso2022_kr.py', + 'PYMODULE'), + ('encodings.iso2022_jp_ext', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso2022_jp_ext.py', + 'PYMODULE'), + ('encodings.iso2022_jp_3', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso2022_jp_3.py', + 'PYMODULE'), + ('encodings.iso2022_jp_2004', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso2022_jp_2004.py', + 'PYMODULE'), + ('encodings.iso2022_jp_2', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso2022_jp_2.py', + 'PYMODULE'), + ('encodings.iso2022_jp_1', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso2022_jp_1.py', + 'PYMODULE'), + ('encodings.iso2022_jp', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\iso2022_jp.py', + 'PYMODULE'), + ('encodings.idna', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\idna.py', + 'PYMODULE'), + ('encodings.hz', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\hz.py', + 'PYMODULE'), + ('encodings.hp_roman8', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\hp_roman8.py', + 'PYMODULE'), + ('encodings.hex_codec', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\hex_codec.py', + 'PYMODULE'), + ('encodings.gbk', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\gbk.py', + 'PYMODULE'), + ('encodings.gb2312', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\gb2312.py', + 'PYMODULE'), + ('encodings.gb18030', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\gb18030.py', + 'PYMODULE'), + ('encodings.euc_kr', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\euc_kr.py', + 'PYMODULE'), + ('encodings.euc_jp', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\euc_jp.py', + 'PYMODULE'), + ('encodings.euc_jisx0213', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\euc_jisx0213.py', + 'PYMODULE'), + ('encodings.euc_jis_2004', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\euc_jis_2004.py', + 'PYMODULE'), + ('encodings.cp950', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp950.py', + 'PYMODULE'), + ('encodings.cp949', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp949.py', + 'PYMODULE'), + ('encodings.cp932', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp932.py', + 'PYMODULE'), + ('encodings.cp875', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp875.py', + 'PYMODULE'), + ('encodings.cp874', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp874.py', + 'PYMODULE'), + ('encodings.cp869', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp869.py', + 'PYMODULE'), + ('encodings.cp866', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp866.py', + 'PYMODULE'), + ('encodings.cp865', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp865.py', + 'PYMODULE'), + ('encodings.cp864', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp864.py', + 'PYMODULE'), + ('encodings.cp863', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp863.py', + 'PYMODULE'), + ('encodings.cp862', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp862.py', + 'PYMODULE'), + ('encodings.cp861', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp861.py', + 'PYMODULE'), + ('encodings.cp860', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp860.py', + 'PYMODULE'), + ('encodings.cp858', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp858.py', + 'PYMODULE'), + ('encodings.cp857', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp857.py', + 'PYMODULE'), + ('encodings.cp856', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp856.py', + 'PYMODULE'), + ('encodings.cp855', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp855.py', + 'PYMODULE'), + ('encodings.cp852', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp852.py', + 'PYMODULE'), + ('encodings.cp850', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp850.py', + 'PYMODULE'), + ('encodings.cp775', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp775.py', + 'PYMODULE'), + ('encodings.cp737', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp737.py', + 'PYMODULE'), + ('encodings.cp720', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp720.py', + 'PYMODULE'), + ('encodings.cp500', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp500.py', + 'PYMODULE'), + ('encodings.cp437', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp437.py', + 'PYMODULE'), + ('encodings.cp424', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp424.py', + 'PYMODULE'), + ('encodings.cp273', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp273.py', + 'PYMODULE'), + ('encodings.cp1258', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp1258.py', + 'PYMODULE'), + ('encodings.cp1257', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp1257.py', + 'PYMODULE'), + ('encodings.cp1256', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp1256.py', + 'PYMODULE'), + ('encodings.cp1255', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp1255.py', + 'PYMODULE'), + ('encodings.cp1254', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp1254.py', + 'PYMODULE'), + ('encodings.cp1253', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp1253.py', + 'PYMODULE'), + ('encodings.cp1252', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp1252.py', + 'PYMODULE'), + ('encodings.cp1251', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp1251.py', + 'PYMODULE'), + ('encodings.cp1250', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp1250.py', + 'PYMODULE'), + ('encodings.cp1140', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp1140.py', + 'PYMODULE'), + ('encodings.cp1125', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp1125.py', + 'PYMODULE'), + ('encodings.cp1026', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp1026.py', + 'PYMODULE'), + ('encodings.cp1006', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp1006.py', + 'PYMODULE'), + ('encodings.cp037', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\cp037.py', + 'PYMODULE'), + ('encodings.charmap', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\charmap.py', + 'PYMODULE'), + ('encodings.bz2_codec', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\bz2_codec.py', + 'PYMODULE'), + ('encodings.big5hkscs', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\big5hkscs.py', + 'PYMODULE'), + ('encodings.big5', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\big5.py', + 'PYMODULE'), + ('encodings.base64_codec', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\base64_codec.py', + 'PYMODULE'), + ('encodings.ascii', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\ascii.py', + 'PYMODULE'), + ('encodings.aliases', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\aliases.py', + 'PYMODULE'), + ('encodings', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\__init__.py', + 'PYMODULE'), + ('linecache', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\linecache.py', + 'PYMODULE'), + ('codecs', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\codecs.py', + 'PYMODULE'), + ('re', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\__init__.py', + 'PYMODULE'), + ('os', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\os.py', + 'PYMODULE')]) diff --git a/build/flash/EXE-01.toc b/build/flash/EXE-01.toc new file mode 100644 index 0000000..3eb4807 --- /dev/null +++ b/build/flash/EXE-01.toc @@ -0,0 +1,107 @@ +('C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\dist\\flash.exe', + False, + False, + False, + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\bootloader\\images\\icon-windowed.ico', + versioninfo.VSVersionInfo(ffi=versioninfo.FixedFileInfo(filevers=(1, 2, 2, 0), prodvers=(1, 2, 2, 0), mask=0x3f, flags=0x0, OS=0x40004, fileType=1, subtype=0x0, date=(0, 0)), kids=[versioninfo.StringFileInfo([versioninfo.StringTable('040904B0', [versioninfo.StringStruct('CompanyName', 'Realtek Semiconductor Corp.'), versioninfo.StringStruct('FileDescription', 'Flash Tool for Realtek Ameba SoCs'), versioninfo.StringStruct('FileVersion', '1.2.2.0'), versioninfo.StringStruct('InternalName', 'flash.exe'), versioninfo.StringStruct('LegalCopyright', 'Copyright (c) 2025 Realtek Semiconductor Corp.'), versioninfo.StringStruct('OriginalFilename', 'flash.exe'), versioninfo.StringStruct('ProductName', 'Ameba Flash Tool'), versioninfo.StringStruct('ProductVersion', '1.2.2.0')])]), versioninfo.VarFileInfo([versioninfo.VarStruct('Translation', [1033, 1200])])]), + False, + False, + b'\n\n \n \n \n \n \n \n \n ' + b'\n <' + b'application>\n \n \n ' + b' \n \n \n \n <' + b'/compatibility>\n ' + b'\n \n true\n \n \n \n \n \n \n \n', + True, + False, + None, + None, + None, + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\flash.pkg', + [('pyi-contents-directory _internal', '', 'OPTION'), + ('PYZ-01.pyz', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\PYZ-01.pyz', + 'PYZ'), + ('struct', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\localpycs\\struct.pyc', + 'PYMODULE'), + ('pyimod01_archive', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\localpycs\\pyimod01_archive.pyc', + 'PYMODULE'), + ('pyimod02_importers', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\localpycs\\pyimod02_importers.pyc', + 'PYMODULE'), + ('pyimod03_ctypes', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\localpycs\\pyimod03_ctypes.pyc', + 'PYMODULE'), + ('pyimod04_pywin32', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\localpycs\\pyimod04_pywin32.pyc', + 'PYMODULE'), + ('pyiboot01_bootstrap', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py', + 'PYSOURCE'), + ('pyi_rth_inspect', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py', + 'PYSOURCE'), + ('flash', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\flash.py', + 'PYSOURCE'), + ('python311.dll', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\python311.dll', + 'BINARY'), + ('_decimal.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_decimal.pyd', + 'EXTENSION'), + ('_hashlib.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_hashlib.pyd', + 'EXTENSION'), + ('unicodedata.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\unicodedata.pyd', + 'EXTENSION'), + ('select.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\select.pyd', + 'EXTENSION'), + ('_socket.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_socket.pyd', + 'EXTENSION'), + ('_lzma.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_lzma.pyd', + 'EXTENSION'), + ('_bz2.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_bz2.pyd', + 'EXTENSION'), + ('_ctypes.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_ctypes.pyd', + 'EXTENSION'), + ('VCRUNTIME140.dll', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\VCRUNTIME140.dll', + 'BINARY'), + ('libcrypto-1_1.dll', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\libcrypto-1_1.dll', + 'BINARY'), + ('libffi-8.dll', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\libffi-8.dll', + 'BINARY'), + ('base_library.zip', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\base_library.zip', + 'DATA')], + [], + False, + False, + 1765693469, + [('runw.exe', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\bootloader\\Windows-64bit-intel\\runw.exe', + 'EXECUTABLE')], + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\python311.dll') diff --git a/build/flash/PKG-01.toc b/build/flash/PKG-01.toc new file mode 100644 index 0000000..b2bf58f --- /dev/null +++ b/build/flash/PKG-01.toc @@ -0,0 +1,85 @@ +('C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\flash.pkg', + {'BINARY': True, + 'DATA': True, + 'EXECUTABLE': True, + 'EXTENSION': True, + 'PYMODULE': True, + 'PYSOURCE': True, + 'PYZ': False, + 'SPLASH': True, + 'SYMLINK': False}, + [('pyi-contents-directory _internal', '', 'OPTION'), + ('PYZ-01.pyz', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\PYZ-01.pyz', + 'PYZ'), + ('struct', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\localpycs\\struct.pyc', + 'PYMODULE'), + ('pyimod01_archive', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\localpycs\\pyimod01_archive.pyc', + 'PYMODULE'), + ('pyimod02_importers', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\localpycs\\pyimod02_importers.pyc', + 'PYMODULE'), + ('pyimod03_ctypes', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\localpycs\\pyimod03_ctypes.pyc', + 'PYMODULE'), + ('pyimod04_pywin32', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\localpycs\\pyimod04_pywin32.pyc', + 'PYMODULE'), + ('pyiboot01_bootstrap', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py', + 'PYSOURCE'), + ('pyi_rth_inspect', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py', + 'PYSOURCE'), + ('flash', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\flash.py', + 'PYSOURCE'), + ('python311.dll', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\python311.dll', + 'BINARY'), + ('_decimal.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_decimal.pyd', + 'EXTENSION'), + ('_hashlib.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_hashlib.pyd', + 'EXTENSION'), + ('unicodedata.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\unicodedata.pyd', + 'EXTENSION'), + ('select.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\select.pyd', + 'EXTENSION'), + ('_socket.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_socket.pyd', + 'EXTENSION'), + ('_lzma.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_lzma.pyd', + 'EXTENSION'), + ('_bz2.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_bz2.pyd', + 'EXTENSION'), + ('_ctypes.pyd', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\_ctypes.pyd', + 'EXTENSION'), + ('VCRUNTIME140.dll', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\VCRUNTIME140.dll', + 'BINARY'), + ('libcrypto-1_1.dll', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\libcrypto-1_1.dll', + 'BINARY'), + ('libffi-8.dll', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\DLLs\\libffi-8.dll', + 'BINARY'), + ('base_library.zip', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\base_library.zip', + 'DATA')], + 'python311.dll', + False, + False, + False, + [], + None, + None, + None) diff --git a/build/flash/PYZ-01.pyz b/build/flash/PYZ-01.pyz new file mode 100644 index 0000000..10bb9fc Binary files /dev/null and b/build/flash/PYZ-01.pyz differ diff --git a/build/flash/PYZ-01.toc b/build/flash/PYZ-01.toc new file mode 100644 index 0000000..19c932f --- /dev/null +++ b/build/flash/PYZ-01.toc @@ -0,0 +1,460 @@ +('C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\PYZ-01.pyz', + [('__future__', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\__future__.py', + 'PYMODULE'), + ('_compat_pickle', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_compat_pickle.py', + 'PYMODULE'), + ('_compression', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_compression.py', + 'PYMODULE'), + ('_py_abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_py_abc.py', + 'PYMODULE'), + ('_pydecimal', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_pydecimal.py', + 'PYMODULE'), + ('_strptime', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_strptime.py', + 'PYMODULE'), + ('_threading_local', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_threading_local.py', + 'PYMODULE'), + ('argparse', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\argparse.py', + 'PYMODULE'), + ('ast', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ast.py', + 'PYMODULE'), + ('base', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\__init__.py', + 'PYMODULE'), + ('base.config_utils', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\config_utils.py', + 'PYMODULE'), + ('base.device_info', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\device_info.py', + 'PYMODULE'), + ('base.device_profile', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\device_profile.py', + 'PYMODULE'), + ('base.download_handler', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\download_handler.py', + 'PYMODULE'), + ('base.efuse_data', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\efuse_data.py', + 'PYMODULE'), + ('base.errno', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\errno.py', + 'PYMODULE'), + ('base.flash_utils', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\flash_utils.py', + 'PYMODULE'), + ('base.floader_handler', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\floader_handler.py', + 'PYMODULE'), + ('base.image_info', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\image_info.py', + 'PYMODULE'), + ('base.json_utils', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\json_utils.py', + 'PYMODULE'), + ('base.memory_info', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\memory_info.py', + 'PYMODULE'), + ('base.next_op', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\next_op.py', + 'PYMODULE'), + ('base.remote_serial', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\remote_serial.py', + 'PYMODULE'), + ('base.rom_handler', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rom_handler.py', + 'PYMODULE'), + ('base.rt_settings', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rt_settings.py', + 'PYMODULE'), + ('base.rtk_flash_type', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rtk_flash_type.py', + 'PYMODULE'), + ('base.rtk_logging', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rtk_logging.py', + 'PYMODULE'), + ('base.rtk_utils', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rtk_utils.py', + 'PYMODULE'), + ('base.sense_status', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\sense_status.py', + 'PYMODULE'), + ('base.spic_addr_mode', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\spic_addr_mode.py', + 'PYMODULE'), + ('base.sys_utils', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\sys_utils.py', + 'PYMODULE'), + ('base.version', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\version.py', + 'PYMODULE'), + ('base64', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\base64.py', + 'PYMODULE'), + ('bisect', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\bisect.py', + 'PYMODULE'), + ('bz2', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\bz2.py', + 'PYMODULE'), + ('calendar', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\calendar.py', + 'PYMODULE'), + ('colorama', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\colorama\\__init__.py', + 'PYMODULE'), + ('colorama.ansi', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\colorama\\ansi.py', + 'PYMODULE'), + ('colorama.ansitowin32', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\colorama\\ansitowin32.py', + 'PYMODULE'), + ('colorama.initialise', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\colorama\\initialise.py', + 'PYMODULE'), + ('colorama.win32', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\colorama\\win32.py', + 'PYMODULE'), + ('colorama.winterm', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\colorama\\winterm.py', + 'PYMODULE'), + ('contextlib', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\contextlib.py', + 'PYMODULE'), + ('contextvars', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\contextvars.py', + 'PYMODULE'), + ('copy', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\copy.py', + 'PYMODULE'), + ('csv', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\csv.py', + 'PYMODULE'), + ('ctypes', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ctypes\\__init__.py', + 'PYMODULE'), + ('ctypes._endian', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ctypes\\_endian.py', + 'PYMODULE'), + ('ctypes.wintypes', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ctypes\\wintypes.py', + 'PYMODULE'), + ('dataclasses', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\dataclasses.py', + 'PYMODULE'), + ('datetime', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\datetime.py', + 'PYMODULE'), + ('decimal', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\decimal.py', + 'PYMODULE'), + ('dis', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\dis.py', + 'PYMODULE'), + ('email', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\__init__.py', + 'PYMODULE'), + ('email._encoded_words', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\_encoded_words.py', + 'PYMODULE'), + ('email._header_value_parser', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\_header_value_parser.py', + 'PYMODULE'), + ('email._parseaddr', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\_parseaddr.py', + 'PYMODULE'), + ('email._policybase', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\_policybase.py', + 'PYMODULE'), + ('email.base64mime', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\base64mime.py', + 'PYMODULE'), + ('email.charset', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\charset.py', + 'PYMODULE'), + ('email.contentmanager', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\contentmanager.py', + 'PYMODULE'), + ('email.encoders', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\encoders.py', + 'PYMODULE'), + ('email.errors', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\errors.py', + 'PYMODULE'), + ('email.feedparser', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\feedparser.py', + 'PYMODULE'), + ('email.generator', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\generator.py', + 'PYMODULE'), + ('email.header', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\header.py', + 'PYMODULE'), + ('email.headerregistry', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\headerregistry.py', + 'PYMODULE'), + ('email.iterators', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\iterators.py', + 'PYMODULE'), + ('email.message', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\message.py', + 'PYMODULE'), + ('email.parser', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\parser.py', + 'PYMODULE'), + ('email.policy', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\policy.py', + 'PYMODULE'), + ('email.quoprimime', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\quoprimime.py', + 'PYMODULE'), + ('email.utils', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\utils.py', + 'PYMODULE'), + ('fnmatch', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\fnmatch.py', + 'PYMODULE'), + ('fractions', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\fractions.py', + 'PYMODULE'), + ('getopt', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\getopt.py', + 'PYMODULE'), + ('gettext', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\gettext.py', + 'PYMODULE'), + ('glob', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\glob.py', + 'PYMODULE'), + ('gzip', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\gzip.py', + 'PYMODULE'), + ('hashlib', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\hashlib.py', + 'PYMODULE'), + ('importlib', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\__init__.py', + 'PYMODULE'), + ('importlib._abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\_abc.py', + 'PYMODULE'), + ('importlib._bootstrap', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\_bootstrap.py', + 'PYMODULE'), + ('importlib._bootstrap_external', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\_bootstrap_external.py', + 'PYMODULE'), + ('importlib.abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\abc.py', + 'PYMODULE'), + ('importlib.machinery', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\machinery.py', + 'PYMODULE'), + ('importlib.metadata', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\__init__.py', + 'PYMODULE'), + ('importlib.metadata._adapters', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\_adapters.py', + 'PYMODULE'), + ('importlib.metadata._collections', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\_collections.py', + 'PYMODULE'), + ('importlib.metadata._functools', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\_functools.py', + 'PYMODULE'), + ('importlib.metadata._itertools', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\_itertools.py', + 'PYMODULE'), + ('importlib.metadata._meta', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\_meta.py', + 'PYMODULE'), + ('importlib.metadata._text', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\metadata\\_text.py', + 'PYMODULE'), + ('importlib.readers', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\readers.py', + 'PYMODULE'), + ('importlib.resources', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\__init__.py', + 'PYMODULE'), + ('importlib.resources._adapters', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\_adapters.py', + 'PYMODULE'), + ('importlib.resources._common', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\_common.py', + 'PYMODULE'), + ('importlib.resources._itertools', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\_itertools.py', + 'PYMODULE'), + ('importlib.resources._legacy', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\_legacy.py', + 'PYMODULE'), + ('importlib.resources.abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\abc.py', + 'PYMODULE'), + ('importlib.resources.readers', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\resources\\readers.py', + 'PYMODULE'), + ('importlib.util', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\importlib\\util.py', + 'PYMODULE'), + ('inspect', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\inspect.py', + 'PYMODULE'), + ('ipaddress', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ipaddress.py', + 'PYMODULE'), + ('json', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\__init__.py', + 'PYMODULE'), + ('json.decoder', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\decoder.py', + 'PYMODULE'), + ('json.encoder', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\encoder.py', + 'PYMODULE'), + ('json.scanner', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\scanner.py', + 'PYMODULE'), + ('logging', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\logging\\__init__.py', + 'PYMODULE'), + ('lzma', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\lzma.py', + 'PYMODULE'), + ('numbers', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\numbers.py', + 'PYMODULE'), + ('opcode', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\opcode.py', + 'PYMODULE'), + ('pathlib', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\pathlib.py', + 'PYMODULE'), + ('pickle', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\pickle.py', + 'PYMODULE'), + ('pprint', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\pprint.py', + 'PYMODULE'), + ('pyDes', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\pyDes.py', + 'PYMODULE'), + ('py_compile', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\py_compile.py', + 'PYMODULE'), + ('quopri', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\quopri.py', + 'PYMODULE'), + ('random', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\random.py', + 'PYMODULE'), + ('selectors', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\selectors.py', + 'PYMODULE'), + ('serial', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\__init__.py', + 'PYMODULE'), + ('serial.serialcli', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\serialcli.py', + 'PYMODULE'), + ('serial.serialjava', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\serialjava.py', + 'PYMODULE'), + ('serial.serialposix', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\serialposix.py', + 'PYMODULE'), + ('serial.serialutil', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\serialutil.py', + 'PYMODULE'), + ('serial.serialwin32', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\serialwin32.py', + 'PYMODULE'), + ('serial.tools', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\__init__.py', + 'PYMODULE'), + ('serial.tools.list_ports', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\list_ports.py', + 'PYMODULE'), + ('serial.tools.list_ports_common', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\list_ports_common.py', + 'PYMODULE'), + ('serial.tools.list_ports_linux', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\list_ports_linux.py', + 'PYMODULE'), + ('serial.tools.list_ports_osx', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\list_ports_osx.py', + 'PYMODULE'), + ('serial.tools.list_ports_posix', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\list_ports_posix.py', + 'PYMODULE'), + ('serial.tools.list_ports_windows', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\tools\\list_ports_windows.py', + 'PYMODULE'), + ('serial.win32', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\win32.py', + 'PYMODULE'), + ('shutil', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\shutil.py', + 'PYMODULE'), + ('signal', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\signal.py', + 'PYMODULE'), + ('socket', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\socket.py', + 'PYMODULE'), + ('statistics', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\statistics.py', + 'PYMODULE'), + ('string', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\string.py', + 'PYMODULE'), + ('stringprep', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\stringprep.py', + 'PYMODULE'), + ('subprocess', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\subprocess.py', + 'PYMODULE'), + ('tarfile', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\tarfile.py', + 'PYMODULE'), + ('tempfile', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\tempfile.py', + 'PYMODULE'), + ('textwrap', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\textwrap.py', + 'PYMODULE'), + ('threading', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\threading.py', + 'PYMODULE'), + ('token', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\token.py', + 'PYMODULE'), + ('tokenize', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\tokenize.py', + 'PYMODULE'), + ('tracemalloc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\tracemalloc.py', + 'PYMODULE'), + ('typing', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\typing.py', + 'PYMODULE'), + ('urllib', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\__init__.py', + 'PYMODULE'), + ('urllib.parse', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\parse.py', + 'PYMODULE'), + ('version_info', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\version_info.py', + 'PYMODULE'), + ('zipfile', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\zipfile.py', + 'PYMODULE')]) diff --git a/build/flash/base_library.zip b/build/flash/base_library.zip new file mode 100644 index 0000000..faa55c3 Binary files /dev/null and b/build/flash/base_library.zip differ diff --git a/build/flash/flash.pkg b/build/flash/flash.pkg new file mode 100644 index 0000000..ac95d06 Binary files /dev/null and b/build/flash/flash.pkg differ diff --git a/build/flash/warn-flash.txt b/build/flash/warn-flash.txt new file mode 100644 index 0000000..1a2c905 --- /dev/null +++ b/build/flash/warn-flash.txt @@ -0,0 +1,29 @@ + +This file lists modules PyInstaller was not able to find. This does not +necessarily mean this module is required for running your program. Python and +Python 3rd-party packages include a lot of conditional or optional modules. For +example the module 'ntpath' only exists on Windows, whereas the module +'posixpath' only exists on Posix systems. + +Types if import: +* top-level: imported at the top-level - look at these first +* conditional: imported within an if-statement +* delayed: imported within a function +* optional: imported within a try-except-statement + +IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for + tracking down the missing module yourself. Thanks! + +missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional) +excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional) +missing module named org - imported by pickle (optional) +missing module named posix - imported by os (conditional, optional), posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional) +missing module named resource - imported by posix (top-level) +missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional) +missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional) +missing module named termios - imported by serial.serialposix (top-level) +missing module named fcntl - imported by subprocess (optional), serial.serialposix (top-level) +missing module named 'System.IO' - imported by serial.serialcli (top-level) +missing module named System - imported by serial.serialcli (top-level) +missing module named 'org.python' - imported by copy (optional) +missing module named _posixsubprocess - imported by subprocess (conditional) diff --git a/build/flash/xref-flash.html b/build/flash/xref-flash.html new file mode 100644 index 0000000..ce72da7 --- /dev/null +++ b/build/flash/xref-flash.html @@ -0,0 +1,8402 @@ + + + + + modulegraph cross reference for flash.py, pyi_rth_inspect.py + + + +

modulegraph cross reference for flash.py, pyi_rth_inspect.py

+ +
+ + flash.py +Script
+imports: + _collections_abc + • _weakrefset + • abc + • argparse + • base + • base64 + • codecs + • collections + • collections.abc + • copy + • copyreg + • encodings + • encodings.aliases + • encodings.ascii + • encodings.base64_codec + • encodings.big5 + • encodings.big5hkscs + • encodings.bz2_codec + • encodings.charmap + • encodings.cp037 + • encodings.cp1006 + • encodings.cp1026 + • encodings.cp1125 + • encodings.cp1140 + • encodings.cp1250 + • encodings.cp1251 + • encodings.cp1252 + • encodings.cp1253 + • encodings.cp1254 + • encodings.cp1255 + • encodings.cp1256 + • encodings.cp1257 + • encodings.cp1258 + • encodings.cp273 + • encodings.cp424 + • encodings.cp437 + • encodings.cp500 + • encodings.cp720 + • encodings.cp737 + • encodings.cp775 + • encodings.cp850 + • encodings.cp852 + • encodings.cp855 + • encodings.cp856 + • encodings.cp857 + • encodings.cp858 + • encodings.cp860 + • encodings.cp861 + • encodings.cp862 + • encodings.cp863 + • encodings.cp864 + • encodings.cp865 + • encodings.cp866 + • encodings.cp869 + • encodings.cp874 + • encodings.cp875 + • encodings.cp932 + • encodings.cp949 + • encodings.cp950 + • encodings.euc_jis_2004 + • encodings.euc_jisx0213 + • encodings.euc_jp + • encodings.euc_kr + • encodings.gb18030 + • encodings.gb2312 + • encodings.gbk + • encodings.hex_codec + • encodings.hp_roman8 + • encodings.hz + • encodings.idna + • encodings.iso2022_jp + • encodings.iso2022_jp_1 + • encodings.iso2022_jp_2 + • encodings.iso2022_jp_2004 + • encodings.iso2022_jp_3 + • encodings.iso2022_jp_ext + • encodings.iso2022_kr + • encodings.iso8859_1 + • encodings.iso8859_10 + • encodings.iso8859_11 + • encodings.iso8859_13 + • encodings.iso8859_14 + • encodings.iso8859_15 + • encodings.iso8859_16 + • encodings.iso8859_2 + • encodings.iso8859_3 + • encodings.iso8859_4 + • encodings.iso8859_5 + • encodings.iso8859_6 + • encodings.iso8859_7 + • encodings.iso8859_8 + • encodings.iso8859_9 + • encodings.johab + • encodings.koi8_r + • encodings.koi8_t + • encodings.koi8_u + • encodings.kz1048 + • encodings.latin_1 + • encodings.mac_arabic + • encodings.mac_croatian + • encodings.mac_cyrillic + • encodings.mac_farsi + • encodings.mac_greek + • encodings.mac_iceland + • encodings.mac_latin2 + • encodings.mac_roman + • encodings.mac_romanian + • encodings.mac_turkish + • encodings.mbcs + • encodings.oem + • encodings.palmos + • encodings.ptcp154 + • encodings.punycode + • encodings.quopri_codec + • encodings.raw_unicode_escape + • encodings.rot_13 + • encodings.shift_jis + • encodings.shift_jis_2004 + • encodings.shift_jisx0213 + • encodings.tis_620 + • encodings.undefined + • encodings.unicode_escape + • encodings.utf_16 + • encodings.utf_16_be + • encodings.utf_16_le + • encodings.utf_32 + • encodings.utf_32_be + • encodings.utf_32_le + • encodings.utf_7 + • encodings.utf_8 + • encodings.utf_8_sig + • encodings.uu_codec + • encodings.zlib_codec + • enum + • functools + • genericpath + • heapq + • io + • keyword + • linecache + • locale + • ntpath + • operator + • os + • posixpath + • pyi_rth_inspect.py + • re + • re._casefix + • re._compiler + • re._constants + • re._parser + • reprlib + • sre_compile + • sre_constants + • sre_parse + • stat + • sys + • threading + • traceback + • types + • version_info + • warnings + • weakref + +
+ +
+ +
+ + pyi_rth_inspect.py +Script
+imports: + inspect + • os + • sys + • zipfile + +
+
+imported by: + flash.py + +
+ +
+ +
+ + 'System.IO' +MissingModule
+imported by: + serial.serialcli + +
+ +
+ +
+ + 'org.python' +MissingModule
+imported by: + copy + +
+ +
+ +
+ + System +MissingModule
+imported by: + serial.serialcli + +
+ +
+ +
+ + __future__ +SourceModule + +
+ +
+ + _abc (builtin module)
+imported by: + abc + +
+ +
+ +
+ + _ast (builtin module)
+imported by: + ast + +
+ +
+ +
+ + _bisect (builtin module)
+imported by: + bisect + +
+ +
+ +
+ + _blake2 (builtin module)
+imported by: + hashlib + +
+ +
+ +
+ + _bz2 C:\Users\wongyiekheng\AppData\Local\Programs\Python\Python311\DLLs\_bz2.pyd
+imported by: + bz2 + +
+ +
+ +
+ + _codecs (builtin module)
+imported by: + codecs + +
+ +
+ +
+ + _codecs_cn (builtin module)
+imported by: + encodings.gb18030 + • encodings.gb2312 + • encodings.gbk + • encodings.hz + +
+ +
+ +
+ + _codecs_hk (builtin module)
+imported by: + encodings.big5hkscs + +
+ +
+ +
+ + _codecs_iso2022 (builtin module) + +
+ +
+ + _codecs_jp (builtin module) + +
+ +
+ + _codecs_kr (builtin module)
+imported by: + encodings.cp949 + • encodings.euc_kr + • encodings.johab + +
+ +
+ +
+ + _codecs_tw (builtin module)
+imported by: + encodings.big5 + • encodings.cp950 + +
+ +
+ +
+ + _collections (builtin module)
+imported by: + collections + • threading + +
+ +
+ +
+ + _collections_abc +SourceModule
+imports: + abc + • sys + +
+
+imported by: + collections + • collections.abc + • contextlib + • flash.py + • locale + • os + • pathlib + • random + • types + • weakref + +
+ +
+ +
+ + _compat_pickle +SourceModule
+imported by: + _pickle + • pickle + +
+ +
+ +
+ + _compression +SourceModule
+imports: + io + • sys + +
+
+imported by: + bz2 + • gzip + • lzma + +
+ +
+ +
+ + _contextvars (builtin module)
+imported by: + contextvars + +
+ +
+ +
+ + _csv (builtin module)
+imported by: + csv + +
+ +
+ +
+ + _ctypes C:\Users\wongyiekheng\AppData\Local\Programs\Python\Python311\DLLs\_ctypes.pyd
+imported by: + ctypes + +
+ +
+ +
+ + _datetime (builtin module)
+imports: + _strptime + • time + +
+
+imported by: + datetime + +
+ +
+ +
+ + _decimal C:\Users\wongyiekheng\AppData\Local\Programs\Python\Python311\DLLs\_decimal.pyd
+imported by: + decimal + +
+ +
+ +
+ + _frozen_importlib +ExcludedModule
+imported by: + importlib + • importlib.abc + +
+ +
+ +
+ + _frozen_importlib_external +MissingModule
+imported by: + importlib + • importlib._bootstrap + • importlib.abc + +
+ +
+ +
+ + _functools (builtin module)
+imported by: + functools + +
+ +
+ +
+ + _hashlib C:\Users\wongyiekheng\AppData\Local\Programs\Python\Python311\DLLs\_hashlib.pyd
+imported by: + hashlib + +
+ +
+ +
+ + _heapq (builtin module)
+imported by: + heapq + +
+ +
+ +
+ + _imp (builtin module)
+imported by: + importlib + • importlib._bootstrap_external + • importlib.util + +
+ +
+ +
+ + _io (builtin module)
+imported by: + importlib._bootstrap_external + • io + +
+ +
+ +
+ + _json (builtin module)
+imports: + json.decoder + +
+
+imported by: + json.decoder + • json.encoder + • json.scanner + +
+ +
+ +
+ + _locale (builtin module)
+imported by: + locale + +
+ +
+ +
+ + _lzma C:\Users\wongyiekheng\AppData\Local\Programs\Python\Python311\DLLs\_lzma.pyd
+imported by: + lzma + +
+ +
+ +
+ + _md5 (builtin module)
+imported by: + hashlib + +
+ +
+ +
+ + _multibytecodec (builtin module) + +
+ +
+ + _opcode (builtin module)
+imported by: + opcode + +
+ +
+ +
+ + _operator (builtin module)
+imported by: + operator + +
+ +
+ +
+ + _pickle (builtin module)
+imports: + _compat_pickle + • codecs + • copyreg + +
+
+imported by: + pickle + +
+ +
+ +
+ + _posixsubprocess +MissingModule
+imports: + gc + +
+
+imported by: + subprocess + +
+ +
+ +
+ + _py_abc +SourceModule
+imports: + _weakrefset + +
+
+imported by: + abc + +
+ +
+ +
+ + _pydecimal +SourceModule
+imports: + collections + • contextvars + • itertools + • locale + • math + • numbers + • re + • sys + +
+
+imported by: + decimal + +
+ +
+ +
+ + _random (builtin module)
+imported by: + random + +
+ +
+ +
+ + _sha1 (builtin module)
+imported by: + hashlib + +
+ +
+ +
+ + _sha256 (builtin module)
+imported by: + hashlib + +
+ +
+ +
+ + _sha3 (builtin module)
+imported by: + hashlib + +
+ +
+ +
+ + _sha512 (builtin module)
+imported by: + hashlib + • random + +
+ +
+ +
+ + _signal (builtin module)
+imported by: + signal + +
+ +
+ +
+ + _socket C:\Users\wongyiekheng\AppData\Local\Programs\Python\Python311\DLLs\_socket.pyd
+imported by: + socket + +
+ +
+ +
+ + _sre (builtin module)
+imports: + copy + • re + +
+
+imported by: + re._compiler + • re._constants + +
+ +
+ +
+ + _stat (builtin module)
+imported by: + stat + +
+ +
+ +
+ + _statistics (builtin module)
+imported by: + statistics + +
+ +
+ +
+ + _string (builtin module)
+imported by: + string + +
+ +
+ +
+ + _strptime +SourceModule
+imports: + _thread + • calendar + • datetime + • locale + • re + • time + +
+
+imported by: + _datetime + • datetime + • time + +
+ +
+ +
+ + _struct (builtin module)
+imported by: + struct + +
+ +
+ +
+ + _thread (builtin module)
+imported by: + _strptime + • dataclasses + • functools + • reprlib + • tempfile + • threading + +
+ +
+ +
+ + _threading_local +SourceModule
+imports: + contextlib + • threading + • weakref + +
+
+imported by: + threading + +
+ +
+ +
+ + _tokenize (builtin module)
+imported by: + tokenize + +
+ +
+ +
+ + _tracemalloc (builtin module)
+imported by: + tracemalloc + +
+ +
+ +
+ + _typing (builtin module)
+imported by: + typing + +
+ +
+ +
+ + _warnings (builtin module)
+imported by: + importlib._bootstrap_external + • warnings + +
+ +
+ +
+ + _weakref (builtin module)
+imported by: + _weakrefset + • collections + • weakref + +
+ +
+ +
+ + _weakrefset +SourceModule
+imports: + _weakref + • types + +
+
+imported by: + _py_abc + • flash.py + • threading + • weakref + +
+ +
+ +
+ + _winapi (builtin module)
+imported by: + encodings + • ntpath + • subprocess + +
+ +
+ +
+ + abc +SourceModule
+imports: + _abc + • _py_abc + +
+
+imported by: + _collections_abc + • contextlib + • dataclasses + • email._policybase + • flash.py + • functools + • importlib._abc + • importlib.abc + • importlib.metadata + • importlib.resources.abc + • inspect + • io + • numbers + • os + • selectors + • typing + +
+ +
+ +
+ + argparse +SourceModule
+imports: + copy + • gettext + • os + • re + • shutil + • sys + • textwrap + • warnings + +
+
+imported by: + ast + • calendar + • dis + • flash.py + • gzip + • inspect + • py_compile + • serial.tools.list_ports + • tarfile + • tokenize + • zipfile + +
+ +
+ +
+ + array (builtin module)
+imported by: + serial.serialposix + • serial.serialutil + • socket + +
+ +
+ +
+ + ast +SourceModule
+imports: + _ast + • argparse + • collections + • contextlib + • enum + • inspect + • sys + • warnings + +
+
+imported by: + inspect + • traceback + +
+ +
+ +
+ + atexit (builtin module)
+imported by: + colorama.initialise + • logging + • weakref + +
+ +
+ +
+ + base +Package + + +
+ +
+ + base.config_utils +SourceModule
+imports: + base + • os + +
+
+imported by: + base.download_handler + +
+ +
+ +
+ + base.device_info +SourceModule
+imports: + base + • base.memory_info + • base.rtk_flash_type + +
+
+imported by: + base.floader_handler + +
+ +
+ +
+ + base.device_profile +SourceModule
+imports: + base + • base.efuse_data + • base.image_info + • base.version + +
+
+imported by: + base.download_handler + +
+ +
+ +
+ + base.download_handler +SourceModule +
+imported by: + base + +
+ +
+ +
+ + base.efuse_data +SourceModule
+imports: + base + +
+
+imported by: + base.device_profile + +
+ +
+ +
+ + base.errno +SourceModule
+imports: + base + • enum + +
+
+imported by: + base.rom_handler + • base.sense_status + +
+ +
+ +
+ + base.flash_utils +SourceModule
+imports: + base + • base.sys_utils + • enum + +
+
+imported by: + base.download_handler + • base.floader_handler + +
+ +
+ +
+ + base.floader_handler +SourceModule
+imports: + base + • base.device_info + • base.flash_utils + • base.next_op + • base.sense_status + • ctypes + • time + +
+
+imported by: + base.download_handler + +
+ +
+ +
+ + base.image_info +SourceModule
+imports: + base + +
+
+imported by: + base.device_profile + +
+ +
+ +
+ + base.json_utils +SourceModule
+imports: + base + • base64 + • json + • os + • pyDes + +
+
+imported by: + base.download_handler + +
+ +
+ +
+ + base.memory_info +SourceModule
+imports: + base + +
+
+imported by: + base.device_info + • base.download_handler + +
+ +
+ +
+ + base.next_op +SourceModule
+imports: + base + • enum + +
+
+imported by: + base.floader_handler + +
+ +
+ +
+ + base.remote_serial +SourceModule
+imports: + base + • base64 + • datetime + • json + • serial + • serial.serialutil + • socket + • threading + • time + • typing + +
+
+imported by: + base.download_handler + +
+ +
+ +
+ + base.rom_handler +SourceModule
+imports: + base + • base.errno + • base.rtk_utils + • io + • os + • sys + • time + +
+
+imported by: + base.download_handler + +
+ +
+ +
+ + base.rt_settings +SourceModule
+imports: + base + +
+
+imported by: + base + • base.download_handler + +
+ +
+ +
+ + base.rtk_flash_type +SourceModule
+imports: + base + • enum + +
+
+imported by: + base.device_info + +
+ +
+ +
+ + base.rtk_logging +SourceModule
+imports: + base + • colorama + • logging + • sys + +
+
+imported by: + base + +
+ +
+ +
+ + base.rtk_utils +SourceModule
+imports: + base + • os + • sys + +
+
+imported by: + base.rom_handler + +
+ +
+ +
+ + base.sense_status +SourceModule
+imports: + base + • base.errno + +
+
+imported by: + base.floader_handler + +
+ +
+ +
+ + base.spic_addr_mode +SourceModule
+imports: + base + • enum + +
+
+imported by: + base.download_handler + +
+ +
+ +
+ + base.sys_utils +SourceModule
+imports: + base + +
+
+imported by: + base.flash_utils + +
+ +
+ +
+ + base.version +SourceModule
+imports: + base + +
+
+imported by: + base.device_profile + +
+ +
+ +
+ + base64 +SourceModule
+imports: + binascii + • getopt + • re + • struct + • sys + +
+ + +
+ +
+ + binascii (builtin module) + +
+ +
+ + bisect +SourceModule
+imports: + _bisect + +
+
+imported by: + random + • statistics + +
+ +
+ +
+ + builtins (builtin module)
+imported by: + bz2 + • codecs + • dataclasses + • enum + • gettext + • gzip + • inspect + • locale + • lzma + • operator + • reprlib + • subprocess + • tarfile + • tokenize + • warnings + +
+ +
+ +
+ + bz2 +SourceModule
+imports: + _bz2 + • _compression + • builtins + • io + • os + +
+
+imported by: + encodings.bz2_codec + • shutil + • tarfile + • zipfile + +
+ +
+ +
+ + calendar +SourceModule
+imports: + argparse + • datetime + • itertools + • locale + • sys + +
+
+imported by: + _strptime + • email._parseaddr + +
+ +
+ +
+ + codecs +SourceModule
+imports: + _codecs + • builtins + • encodings + • sys + +
+
+imported by: + _pickle + • encodings + • encodings.ascii + • encodings.base64_codec + • encodings.big5 + • encodings.big5hkscs + • encodings.bz2_codec + • encodings.charmap + • encodings.cp037 + • encodings.cp1006 + • encodings.cp1026 + • encodings.cp1125 + • encodings.cp1140 + • encodings.cp1250 + • encodings.cp1251 + • encodings.cp1252 + • encodings.cp1253 + • encodings.cp1254 + • encodings.cp1255 + • encodings.cp1256 + • encodings.cp1257 + • encodings.cp1258 + • encodings.cp273 + • encodings.cp424 + • encodings.cp437 + • encodings.cp500 + • encodings.cp720 + • encodings.cp737 + • encodings.cp775 + • encodings.cp850 + • encodings.cp852 + • encodings.cp855 + • encodings.cp856 + • encodings.cp857 + • encodings.cp858 + • encodings.cp860 + • encodings.cp861 + • encodings.cp862 + • encodings.cp863 + • encodings.cp864 + • encodings.cp865 + • encodings.cp866 + • encodings.cp869 + • encodings.cp874 + • encodings.cp875 + • encodings.cp932 + • encodings.cp949 + • encodings.cp950 + • encodings.euc_jis_2004 + • encodings.euc_jisx0213 + • encodings.euc_jp + • encodings.euc_kr + • encodings.gb18030 + • encodings.gb2312 + • encodings.gbk + • encodings.hex_codec + • encodings.hp_roman8 + • encodings.hz + • encodings.idna + • encodings.iso2022_jp + • encodings.iso2022_jp_1 + • encodings.iso2022_jp_2 + • encodings.iso2022_jp_2004 + • encodings.iso2022_jp_3 + • encodings.iso2022_jp_ext + • encodings.iso2022_kr + • encodings.iso8859_1 + • encodings.iso8859_10 + • encodings.iso8859_11 + • encodings.iso8859_13 + • encodings.iso8859_14 + • encodings.iso8859_15 + • encodings.iso8859_16 + • encodings.iso8859_2 + • encodings.iso8859_3 + • encodings.iso8859_4 + • encodings.iso8859_5 + • encodings.iso8859_6 + • encodings.iso8859_7 + • encodings.iso8859_8 + • encodings.iso8859_9 + • encodings.johab + • encodings.koi8_r + • encodings.koi8_t + • encodings.koi8_u + • encodings.kz1048 + • encodings.latin_1 + • encodings.mac_arabic + • encodings.mac_croatian + • encodings.mac_cyrillic + • encodings.mac_farsi + • encodings.mac_greek + • encodings.mac_iceland + • encodings.mac_latin2 + • encodings.mac_roman + • encodings.mac_romanian + • encodings.mac_turkish + • encodings.mbcs + • encodings.oem + • encodings.palmos + • encodings.ptcp154 + • encodings.punycode + • encodings.quopri_codec + • encodings.raw_unicode_escape + • encodings.rot_13 + • encodings.shift_jis + • encodings.shift_jis_2004 + • encodings.shift_jisx0213 + • encodings.tis_620 + • encodings.undefined + • encodings.unicode_escape + • encodings.utf_16 + • encodings.utf_16_be + • encodings.utf_16_le + • encodings.utf_32 + • encodings.utf_32_be + • encodings.utf_32_le + • encodings.utf_7 + • encodings.utf_8 + • encodings.utf_8_sig + • encodings.uu_codec + • encodings.zlib_codec + • flash.py + • json + • pickle + • tokenize + +
+ +
+ +
+ + collections +Package
+imports: + _collections + • _collections_abc + • _weakref + • copy + • heapq + • itertools + • keyword + • operator + • reprlib + • sys + +
+
+imported by: + _pydecimal + • ast + • collections.abc + • contextlib + • dis + • email.feedparser + • flash.py + • functools + • importlib.metadata + • importlib.metadata._collections + • importlib.resources.readers + • inspect + • pprint + • selectors + • shutil + • statistics + • string + • threading + • tokenize + • typing + • urllib.parse + +
+ +
+ +
+ + collections.abc +SourceModule
+imports: + _collections_abc + • collections + +
+
+imported by: + flash.py + • inspect + • logging + • selectors + • traceback + • tracemalloc + • typing + +
+ +
+ +
+ + colorama +Package + + +
+ +
+ + colorama.ansi +SourceModule
+imports: + colorama + +
+
+imported by: + colorama + • colorama.ansitowin32 + +
+ +
+ +
+ + colorama.ansitowin32 +SourceModule
+imports: + colorama + • colorama.ansi + • colorama.win32 + • colorama.winterm + • os + • re + • sys + +
+
+imported by: + colorama + • colorama.initialise + +
+ +
+ +
+ + colorama.initialise +SourceModule
+imports: + atexit + • colorama + • colorama.ansitowin32 + • contextlib + • sys + +
+
+imported by: + colorama + +
+ +
+ +
+ + colorama.win32 +SourceModule
+imports: + colorama + • ctypes + • ctypes.wintypes + +
+
+imported by: + colorama + • colorama.ansitowin32 + • colorama.winterm + +
+ +
+ +
+ + colorama.winterm +SourceModule
+imports: + colorama + • colorama.win32 + • msvcrt + +
+
+imported by: + colorama.ansitowin32 + +
+ +
+ +
+ + contextlib +SourceModule
+imports: + _collections_abc + • abc + • collections + • functools + • os + • sys + • types + +
+ + +
+ +
+ + contextvars +SourceModule
+imports: + _contextvars + +
+
+imported by: + _pydecimal + +
+ +
+ +
+ + copy +SourceModule
+imports: + 'org.python' + • copyreg + • types + • weakref + +
+
+imported by: + _sre + • argparse + • collections + • dataclasses + • email.generator + • flash.py + • gettext + • tarfile + • weakref + +
+ +
+ +
+ + copyreg +SourceModule
+imports: + functools + • operator + +
+
+imported by: + _pickle + • copy + • flash.py + • pickle + • re + +
+ +
+ +
+ + csv +SourceModule
+imports: + _csv + • io + • re + +
+
+imported by: + importlib.metadata + +
+ +
+ +
+ + ctypes +Package
+imports: + _ctypes + • ctypes._endian + • nt + • os + • struct + • sys + • types + +
+ + +
+ +
+ + ctypes._endian +SourceModule
+imports: + ctypes + • sys + +
+
+imported by: + ctypes + +
+ +
+ +
+ + ctypes.wintypes +SourceModule
+imports: + ctypes + +
+ + +
+ +
+ + dataclasses +SourceModule
+imports: + _thread + • abc + • builtins + • copy + • functools + • inspect + • itertools + • keyword + • re + • sys + • types + +
+
+imported by: + pprint + +
+ +
+ +
+ + datetime +SourceModule
+imports: + _datetime + • _strptime + • math + • operator + • sys + • time + +
+
+imported by: + _strptime + • base.download_handler + • base.remote_serial + • calendar + • email.utils + +
+ +
+ +
+ + decimal +SourceModule
+imports: + _decimal + • _pydecimal + +
+
+imported by: + fractions + • statistics + +
+ +
+ +
+ + dis +SourceModule
+imports: + argparse + • collections + • io + • opcode + • sys + • types + +
+
+imported by: + inspect + +
+ +
+ +
+ + email +Package + + +
+ +
+ + email._encoded_words +SourceModule
+imports: + base64 + • binascii + • email + • email.errors + • functools + • re + • string + +
+
+imported by: + email._header_value_parser + • email.message + +
+ +
+ +
+ + email._header_value_parser +SourceModule
+imports: + email + • email._encoded_words + • email.errors + • email.utils + • operator + • re + • string + • sys + • urllib + +
+
+imported by: + email + • email.headerregistry + +
+ +
+ +
+ + email._parseaddr +SourceModule
+imports: + calendar + • email + • time + +
+
+imported by: + email.utils + +
+ +
+ +
+ + email._policybase +SourceModule
+imports: + abc + • email + • email.charset + • email.header + • email.utils + +
+
+imported by: + email.feedparser + • email.message + • email.parser + • email.policy + +
+ +
+ +
+ + email.base64mime +SourceModule
+imports: + base64 + • binascii + • email + +
+
+imported by: + email.charset + • email.header + +
+ +
+ +
+ + email.charset +SourceModule
+imports: + email + • email.base64mime + • email.encoders + • email.errors + • email.quoprimime + • functools + +
+
+imported by: + email + • email._policybase + • email.contentmanager + • email.header + • email.message + • email.utils + +
+ +
+ +
+ + email.contentmanager +SourceModule
+imports: + binascii + • email + • email.charset + • email.errors + • email.message + • email.quoprimime + +
+
+imported by: + email.policy + +
+ +
+ +
+ + email.encoders +SourceModule
+imports: + base64 + • email + • quopri + +
+
+imported by: + email.charset + +
+ +
+ +
+ + email.errors +SourceModule
+imports: + email + +
+ + +
+ +
+ + email.feedparser +SourceModule
+imports: + collections + • email + • email._policybase + • email.errors + • email.message + • io + • re + +
+
+imported by: + email.parser + +
+ +
+ +
+ + email.generator +SourceModule
+imports: + copy + • email + • email.utils + • io + • random + • re + • sys + • time + +
+
+imported by: + email.message + +
+ +
+ +
+ + email.header +SourceModule
+imports: + binascii + • email + • email.base64mime + • email.charset + • email.errors + • email.quoprimime + • re + +
+
+imported by: + email + • email._policybase + +
+ +
+ +
+ + email.headerregistry +SourceModule
+imports: + email + • email._header_value_parser + • email.errors + • email.utils + • types + +
+
+imported by: + email.policy + +
+ +
+ +
+ + email.iterators +SourceModule
+imports: + email + • io + • sys + +
+
+imported by: + email.message + +
+ +
+ +
+ + email.message +SourceModule
+imports: + binascii + • email + • email._encoded_words + • email._policybase + • email.charset + • email.errors + • email.generator + • email.iterators + • email.policy + • email.utils + • io + • quopri + • re + +
+ + +
+ +
+ + email.parser +SourceModule
+imports: + email + • email._policybase + • email.feedparser + • io + +
+
+imported by: + email + +
+ +
+ +
+ + email.policy +SourceModule
+imports: + email + • email._policybase + • email.contentmanager + • email.headerregistry + • email.message + • email.utils + • re + • sys + +
+
+imported by: + email.message + +
+ +
+ +
+ + email.quoprimime +SourceModule
+imports: + email + • re + • string + +
+
+imported by: + email.charset + • email.contentmanager + • email.header + +
+ +
+ +
+ + email.utils +SourceModule
+imports: + datetime + • email + • email._parseaddr + • email.charset + • os + • random + • re + • socket + • time + • urllib.parse + +
+ + +
+ +
+ + encodings +Package
+imports: + _winapi + • codecs + • encodings + • encodings.aliases + • encodings.ascii + • encodings.base64_codec + • encodings.big5 + • encodings.big5hkscs + • encodings.bz2_codec + • encodings.charmap + • encodings.cp037 + • encodings.cp1006 + • encodings.cp1026 + • encodings.cp1125 + • encodings.cp1140 + • encodings.cp1250 + • encodings.cp1251 + • encodings.cp1252 + • encodings.cp1253 + • encodings.cp1254 + • encodings.cp1255 + • encodings.cp1256 + • encodings.cp1257 + • encodings.cp1258 + • encodings.cp273 + • encodings.cp424 + • encodings.cp437 + • encodings.cp500 + • encodings.cp720 + • encodings.cp737 + • encodings.cp775 + • encodings.cp850 + • encodings.cp852 + • encodings.cp855 + • encodings.cp856 + • encodings.cp857 + • encodings.cp858 + • encodings.cp860 + • encodings.cp861 + • encodings.cp862 + • encodings.cp863 + • encodings.cp864 + • encodings.cp865 + • encodings.cp866 + • encodings.cp869 + • encodings.cp874 + • encodings.cp875 + • encodings.cp932 + • encodings.cp949 + • encodings.cp950 + • encodings.euc_jis_2004 + • encodings.euc_jisx0213 + • encodings.euc_jp + • encodings.euc_kr + • encodings.gb18030 + • encodings.gb2312 + • encodings.gbk + • encodings.hex_codec + • encodings.hp_roman8 + • encodings.hz + • encodings.idna + • encodings.iso2022_jp + • encodings.iso2022_jp_1 + • encodings.iso2022_jp_2 + • encodings.iso2022_jp_2004 + • encodings.iso2022_jp_3 + • encodings.iso2022_jp_ext + • encodings.iso2022_kr + • encodings.iso8859_1 + • encodings.iso8859_10 + • encodings.iso8859_11 + • encodings.iso8859_13 + • encodings.iso8859_14 + • encodings.iso8859_15 + • encodings.iso8859_16 + • encodings.iso8859_2 + • encodings.iso8859_3 + • encodings.iso8859_4 + • encodings.iso8859_5 + • encodings.iso8859_6 + • encodings.iso8859_7 + • encodings.iso8859_8 + • encodings.iso8859_9 + • encodings.johab + • encodings.koi8_r + • encodings.koi8_t + • encodings.koi8_u + • encodings.kz1048 + • encodings.latin_1 + • encodings.mac_arabic + • encodings.mac_croatian + • encodings.mac_cyrillic + • encodings.mac_farsi + • encodings.mac_greek + • encodings.mac_iceland + • encodings.mac_latin2 + • encodings.mac_roman + • encodings.mac_romanian + • encodings.mac_turkish + • encodings.mbcs + • encodings.oem + • encodings.palmos + • encodings.ptcp154 + • encodings.punycode + • encodings.quopri_codec + • encodings.raw_unicode_escape + • encodings.rot_13 + • encodings.shift_jis + • encodings.shift_jis_2004 + • encodings.shift_jisx0213 + • encodings.tis_620 + • encodings.undefined + • encodings.unicode_escape + • encodings.utf_16 + • encodings.utf_16_be + • encodings.utf_16_le + • encodings.utf_32 + • encodings.utf_32_be + • encodings.utf_32_le + • encodings.utf_7 + • encodings.utf_8 + • encodings.utf_8_sig + • encodings.uu_codec + • encodings.zlib_codec + • sys + +
+
+imported by: + codecs + • encodings + • encodings.aliases + • encodings.ascii + • encodings.base64_codec + • encodings.big5 + • encodings.big5hkscs + • encodings.bz2_codec + • encodings.charmap + • encodings.cp037 + • encodings.cp1006 + • encodings.cp1026 + • encodings.cp1125 + • encodings.cp1140 + • encodings.cp1250 + • encodings.cp1251 + • encodings.cp1252 + • encodings.cp1253 + • encodings.cp1254 + • encodings.cp1255 + • encodings.cp1256 + • encodings.cp1257 + • encodings.cp1258 + • encodings.cp273 + • encodings.cp424 + • encodings.cp437 + • encodings.cp500 + • encodings.cp720 + • encodings.cp737 + • encodings.cp775 + • encodings.cp850 + • encodings.cp852 + • encodings.cp855 + • encodings.cp856 + • encodings.cp857 + • encodings.cp858 + • encodings.cp860 + • encodings.cp861 + • encodings.cp862 + • encodings.cp863 + • encodings.cp864 + • encodings.cp865 + • encodings.cp866 + • encodings.cp869 + • encodings.cp874 + • encodings.cp875 + • encodings.cp932 + • encodings.cp949 + • encodings.cp950 + • encodings.euc_jis_2004 + • encodings.euc_jisx0213 + • encodings.euc_jp + • encodings.euc_kr + • encodings.gb18030 + • encodings.gb2312 + • encodings.gbk + • encodings.hex_codec + • encodings.hp_roman8 + • encodings.hz + • encodings.idna + • encodings.iso2022_jp + • encodings.iso2022_jp_1 + • encodings.iso2022_jp_2 + • encodings.iso2022_jp_2004 + • encodings.iso2022_jp_3 + • encodings.iso2022_jp_ext + • encodings.iso2022_kr + • encodings.iso8859_1 + • encodings.iso8859_10 + • encodings.iso8859_11 + • encodings.iso8859_13 + • encodings.iso8859_14 + • encodings.iso8859_15 + • encodings.iso8859_16 + • encodings.iso8859_2 + • encodings.iso8859_3 + • encodings.iso8859_4 + • encodings.iso8859_5 + • encodings.iso8859_6 + • encodings.iso8859_7 + • encodings.iso8859_8 + • encodings.iso8859_9 + • encodings.johab + • encodings.koi8_r + • encodings.koi8_t + • encodings.koi8_u + • encodings.kz1048 + • encodings.latin_1 + • encodings.mac_arabic + • encodings.mac_croatian + • encodings.mac_cyrillic + • encodings.mac_farsi + • encodings.mac_greek + • encodings.mac_iceland + • encodings.mac_latin2 + • encodings.mac_roman + • encodings.mac_romanian + • encodings.mac_turkish + • encodings.mbcs + • encodings.oem + • encodings.palmos + • encodings.ptcp154 + • encodings.punycode + • encodings.quopri_codec + • encodings.raw_unicode_escape + • encodings.rot_13 + • encodings.shift_jis + • encodings.shift_jis_2004 + • encodings.shift_jisx0213 + • encodings.tis_620 + • encodings.undefined + • encodings.unicode_escape + • encodings.utf_16 + • encodings.utf_16_be + • encodings.utf_16_le + • encodings.utf_32 + • encodings.utf_32_be + • encodings.utf_32_le + • encodings.utf_7 + • encodings.utf_8 + • encodings.utf_8_sig + • encodings.uu_codec + • encodings.zlib_codec + • flash.py + • locale + +
+ +
+ +
+ + encodings.aliases +SourceModule
+imports: + encodings + +
+
+imported by: + encodings + • flash.py + • locale + +
+ +
+ +
+ + encodings.ascii +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.base64_codec +SourceModule
+imports: + base64 + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.big5 +SourceModule
+imports: + _codecs_tw + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.big5hkscs +SourceModule
+imports: + _codecs_hk + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.bz2_codec +SourceModule
+imports: + bz2 + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.charmap +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp037 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp1006 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp1026 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp1125 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp1140 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp1250 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp1251 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp1252 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp1253 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp1254 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp1255 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp1256 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp1257 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp1258 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp273 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp424 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp437 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp500 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp720 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp737 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp775 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp850 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp852 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp855 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp856 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp857 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp858 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp860 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp861 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp862 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp863 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp864 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp865 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp866 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp869 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp874 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp875 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp932 +SourceModule
+imports: + _codecs_jp + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp949 +SourceModule
+imports: + _codecs_kr + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.cp950 +SourceModule
+imports: + _codecs_tw + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.euc_jis_2004 +SourceModule
+imports: + _codecs_jp + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.euc_jisx0213 +SourceModule
+imports: + _codecs_jp + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.euc_jp +SourceModule
+imports: + _codecs_jp + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.euc_kr +SourceModule
+imports: + _codecs_kr + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.gb18030 +SourceModule
+imports: + _codecs_cn + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.gb2312 +SourceModule
+imports: + _codecs_cn + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.gbk +SourceModule
+imports: + _codecs_cn + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.hex_codec +SourceModule
+imports: + binascii + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.hp_roman8 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.hz +SourceModule
+imports: + _codecs_cn + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.idna +SourceModule
+imports: + codecs + • encodings + • re + • stringprep + • unicodedata + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso2022_jp +SourceModule
+imports: + _codecs_iso2022 + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso2022_jp_1 +SourceModule
+imports: + _codecs_iso2022 + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso2022_jp_2 +SourceModule
+imports: + _codecs_iso2022 + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso2022_jp_2004 +SourceModule
+imports: + _codecs_iso2022 + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso2022_jp_3 +SourceModule
+imports: + _codecs_iso2022 + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso2022_jp_ext +SourceModule
+imports: + _codecs_iso2022 + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso2022_kr +SourceModule
+imports: + _codecs_iso2022 + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_1 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_10 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_11 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_13 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_14 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_15 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_16 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_2 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_3 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_4 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_5 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_6 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_7 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_8 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.iso8859_9 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.johab +SourceModule
+imports: + _codecs_kr + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.koi8_r +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.koi8_t +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.koi8_u +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.kz1048 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.latin_1 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.mac_arabic +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.mac_croatian +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.mac_cyrillic +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.mac_farsi +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.mac_greek +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.mac_iceland +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.mac_latin2 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.mac_roman +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.mac_romanian +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.mac_turkish +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.mbcs +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.oem +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.palmos +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.ptcp154 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.punycode +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.quopri_codec +SourceModule
+imports: + codecs + • encodings + • io + • quopri + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.raw_unicode_escape +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.rot_13 +SourceModule
+imports: + codecs + • encodings + • sys + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.shift_jis +SourceModule
+imports: + _codecs_jp + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.shift_jis_2004 +SourceModule
+imports: + _codecs_jp + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.shift_jisx0213 +SourceModule
+imports: + _codecs_jp + • _multibytecodec + • codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.tis_620 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.undefined +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.unicode_escape +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.utf_16 +SourceModule
+imports: + codecs + • encodings + • sys + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.utf_16_be +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.utf_16_le +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.utf_32 +SourceModule
+imports: + codecs + • encodings + • sys + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.utf_32_be +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.utf_32_le +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.utf_7 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.utf_8 +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.utf_8_sig +SourceModule
+imports: + codecs + • encodings + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.uu_codec +SourceModule
+imports: + binascii + • codecs + • encodings + • io + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + encodings.zlib_codec +SourceModule
+imports: + codecs + • encodings + • zlib + +
+
+imported by: + encodings + • flash.py + +
+ +
+ +
+ + enum +SourceModule
+imports: + builtins + • functools + • operator + • sys + • types + • warnings + +
+
+imported by: + ast + • base.errno + • base.flash_utils + • base.next_op + • base.rtk_flash_type + • base.spic_addr_mode + • flash.py + • inspect + • py_compile + • re + • signal + • socket + +
+ +
+ +
+ + errno (builtin module)
+imported by: + gettext + • gzip + • pathlib + • serial.serialposix + • shutil + • socket + • subprocess + • tempfile + +
+ +
+ +
+ + fcntl +MissingModule
+imported by: + serial.serialposix + • subprocess + +
+ +
+ +
+ + fnmatch +SourceModule
+imports: + functools + • os + • posixpath + • re + +
+
+imported by: + glob + • pathlib + • shutil + • tracemalloc + +
+ +
+ +
+ + fractions +SourceModule
+imports: + decimal + • math + • numbers + • operator + • re + • sys + +
+
+imported by: + statistics + +
+ +
+ +
+ + functools +SourceModule
+imports: + _functools + • _thread + • abc + • collections + • reprlib + • types + • typing + • weakref + +
+
+imported by: + contextlib + • copyreg + • dataclasses + • email._encoded_words + • email.charset + • enum + • flash.py + • fnmatch + • importlib.metadata + • importlib.metadata._functools + • importlib.resources._common + • importlib.resources._legacy + • importlib.util + • inspect + • ipaddress + • linecache + • locale + • operator + • pathlib + • pickle + • re + • statistics + • tempfile + • threading + • tokenize + • tracemalloc + • types + • typing + • urllib.parse + +
+ +
+ +
+ + gc (builtin module)
+imports: + time + +
+
+imported by: + _posixsubprocess + • weakref + +
+ +
+ +
+ + genericpath +SourceModule
+imports: + os + • stat + +
+
+imported by: + flash.py + • ntpath + • posixpath + +
+ +
+ +
+ + getopt +SourceModule
+imports: + gettext + • os + • sys + +
+
+imported by: + base64 + • quopri + +
+ +
+ +
+ + gettext +SourceModule
+imports: + builtins + • copy + • errno + • locale + • os + • re + • struct + • sys + • warnings + +
+
+imported by: + argparse + • getopt + +
+ +
+ +
+ + glob +SourceModule
+imports: + contextlib + • fnmatch + • itertools + • os + • re + • stat + • sys + +
+ + +
+ +
+ + grp +MissingModule
+imported by: + pathlib + • shutil + • subprocess + • tarfile + +
+ +
+ +
+ + gzip +SourceModule
+imports: + _compression + • argparse + • builtins + • errno + • io + • os + • struct + • sys + • time + • warnings + • zlib + +
+
+imported by: + tarfile + +
+ +
+ +
+ + hashlib +SourceModule
+imports: + _blake2 + • _hashlib + • _md5 + • _sha1 + • _sha256 + • _sha3 + • _sha512 + • logging + • warnings + +
+
+imported by: + random + +
+ +
+ +
+ + heapq +SourceModule
+imports: + _heapq + +
+
+imported by: + collections + • flash.py + +
+ +
+ +
+ + importlib +Package + + +
+ +
+ + importlib._abc +SourceModule
+imports: + abc + • importlib + • importlib._bootstrap + • warnings + +
+
+imported by: + importlib.abc + • importlib.util + +
+ +
+ +
+ + importlib._bootstrap +SourceModule
+imports: + _frozen_importlib_external + • importlib + +
+
+imported by: + importlib + • importlib._abc + • importlib.machinery + • importlib.util + +
+ +
+ +
+ + importlib._bootstrap_external +SourceModule
+imports: + _imp + • _io + • _warnings + • importlib + • importlib.metadata + • importlib.readers + • marshal + • nt + • posix + • sys + • tokenize + • winreg + +
+
+imported by: + importlib + • importlib.abc + • importlib.machinery + • importlib.util + • py_compile + +
+ +
+ +
+ + importlib.abc +SourceModule +
+imported by: + importlib.metadata + +
+ +
+ +
+ + importlib.machinery +SourceModule +
+imported by: + importlib + • importlib.abc + • inspect + • py_compile + +
+ +
+ +
+ + importlib.metadata +Package + + +
+ +
+ + importlib.metadata._adapters +SourceModule
+imports: + email.message + • importlib.metadata + • importlib.metadata._text + • re + • textwrap + +
+
+imported by: + importlib.metadata + +
+ +
+ +
+ + importlib.metadata._collections +SourceModule
+imports: + collections + • importlib.metadata + +
+
+imported by: + importlib.metadata + +
+ +
+ +
+ + importlib.metadata._functools +SourceModule
+imports: + functools + • importlib.metadata + • types + +
+
+imported by: + importlib.metadata + • importlib.metadata._text + +
+ +
+ +
+ + importlib.metadata._itertools +SourceModule
+imports: + importlib.metadata + • itertools + +
+
+imported by: + importlib.metadata + +
+ +
+ +
+ + importlib.metadata._meta +SourceModule
+imports: + importlib.metadata + • typing + +
+
+imported by: + importlib.metadata + +
+ +
+ +
+ + importlib.metadata._text +SourceModule +
+imported by: + importlib.metadata._adapters + +
+ +
+ +
+ + importlib.readers +SourceModule
+imports: + importlib + • importlib.resources.readers + +
+
+imported by: + importlib._bootstrap_external + +
+ +
+ +
+ + importlib.resources +Package + + +
+ +
+ + importlib.resources._adapters +SourceModule
+imports: + contextlib + • importlib.resources + • importlib.resources.abc + • io + +
+
+imported by: + importlib.resources._common + +
+ +
+ +
+ + importlib.resources._common +SourceModule
+imports: + contextlib + • functools + • importlib + • importlib.resources + • importlib.resources._adapters + • importlib.resources.abc + • os + • pathlib + • tempfile + • types + • typing + +
+ + +
+ +
+ + importlib.resources._itertools +SourceModule
+imports: + importlib.resources + • itertools + • typing + +
+
+imported by: + importlib.resources.readers + +
+ +
+ +
+ + importlib.resources._legacy +SourceModule
+imports: + functools + • importlib.resources + • importlib.resources._common + • os + • pathlib + • types + • typing + • warnings + +
+
+imported by: + importlib.resources + +
+ +
+ +
+ + importlib.resources.abc +SourceModule
+imports: + abc + • importlib.resources + • io + • os + • typing + +
+ + +
+ +
+ + importlib.resources.readers +SourceModule +
+imported by: + importlib.readers + +
+ +
+ +
+ + importlib.util +SourceModule
+imports: + _imp + • contextlib + • functools + • importlib + • importlib._abc + • importlib._bootstrap + • importlib._bootstrap_external + • sys + • types + • warnings + +
+
+imported by: + py_compile + • zipfile + +
+ +
+ +
+ + inspect +SourceModule
+imports: + abc + • argparse + • ast + • builtins + • collections + • collections.abc + • dis + • enum + • functools + • importlib + • importlib.machinery + • itertools + • keyword + • linecache + • operator + • os + • re + • sys + • token + • tokenize + • types + +
+
+imported by: + ast + • dataclasses + • pyi_rth_inspect.py + +
+ +
+ +
+ + io +SourceModule
+imports: + _io + • abc + • warnings + +
+
+imported by: + _compression + • base.rom_handler + • bz2 + • csv + • dis + • email.feedparser + • email.generator + • email.iterators + • email.message + • email.parser + • encodings.quopri_codec + • encodings.uu_codec + • flash.py + • gzip + • importlib.resources._adapters + • importlib.resources.abc + • logging + • lzma + • os + • pathlib + • pickle + • pprint + • quopri + • serial.serialutil + • socket + • subprocess + • tarfile + • tempfile + • tokenize + • zipfile + +
+ +
+ +
+ + ipaddress +SourceModule
+imports: + functools + • re + +
+
+imported by: + urllib.parse + +
+ +
+ +
+ + itertools (builtin module)
+imported by: + _pydecimal + • calendar + • collections + • dataclasses + • glob + • importlib.metadata + • importlib.metadata._itertools + • importlib.resources._itertools + • inspect + • pickle + • random + • reprlib + • statistics + • threading + • tokenize + • traceback + • weakref + • zipfile + +
+ +
+ +
+ + json +Package
+imports: + codecs + • json.decoder + • json.encoder + • json.scanner + +
+
+imported by: + base.json_utils + • base.remote_serial + • json.decoder + • json.encoder + • json.scanner + +
+ +
+ +
+ + json.decoder +SourceModule
+imports: + _json + • json + • json.scanner + • re + +
+
+imported by: + _json + • json + +
+ +
+ +
+ + json.encoder +SourceModule
+imports: + _json + • json + • re + +
+
+imported by: + json + +
+ +
+ +
+ + json.scanner +SourceModule
+imports: + _json + • json + • re + +
+
+imported by: + json + • json.decoder + +
+ +
+ +
+ + keyword +SourceModule
+imported by: + collections + • dataclasses + • flash.py + • inspect + +
+ +
+ +
+ + linecache +SourceModule
+imports: + functools + • os + • sys + • tokenize + +
+
+imported by: + flash.py + • inspect + • traceback + • tracemalloc + • warnings + +
+ +
+ +
+ + locale +SourceModule
+imports: + _collections_abc + • _locale + • builtins + • encodings + • encodings.aliases + • functools + • os + • re + • sys + • warnings + +
+
+imported by: + _pydecimal + • _strptime + • calendar + • flash.py + • gettext + • subprocess + +
+ +
+ +
+ + logging +Package
+imports: + atexit + • collections.abc + • io + • os + • pickle + • re + • string + • sys + • threading + • time + • traceback + • types + • warnings + • weakref + +
+
+imported by: + base.rtk_logging + • hashlib + +
+ +
+ +
+ + lzma +SourceModule
+imports: + _compression + • _lzma + • builtins + • io + • os + +
+
+imported by: + shutil + • tarfile + • zipfile + +
+ +
+ +
+ + marshal (builtin module)
+imported by: + importlib._bootstrap_external + +
+ +
+ +
+ + math (builtin module)
+imported by: + _pydecimal + • datetime + • fractions + • random + • selectors + • statistics + +
+ +
+ +
+ + msvcrt (builtin module)
+imported by: + colorama.winterm + • subprocess + +
+ +
+ +
+ + nt (builtin module)
+imported by: + ctypes + • importlib._bootstrap_external + • ntpath + • os + • shutil + +
+ +
+ +
+ + ntpath +SourceModule
+imports: + _winapi + • genericpath + • nt + • os + • stat + • string + • sys + +
+
+imported by: + flash.py + • os + • os.path + • pathlib + +
+ +
+ +
+ + numbers +SourceModule
+imports: + abc + +
+
+imported by: + _pydecimal + • fractions + • statistics + +
+ +
+ +
+ + opcode +SourceModule
+imports: + _opcode + +
+
+imported by: + dis + +
+ +
+ +
+ + operator +SourceModule
+imports: + _operator + • builtins + • functools + +
+
+imported by: + collections + • copyreg + • datetime + • email._header_value_parser + • enum + • flash.py + • fractions + • importlib.metadata + • importlib.resources.readers + • inspect + • pathlib + • random + • statistics + • typing + +
+ +
+ +
+ + org +MissingModule
+imported by: + pickle + +
+ +
+ +
+ + os +SourceModule
+imports: + _collections_abc + • abc + • io + • nt + • ntpath + • os.path + • posix + • posixpath + • stat + • subprocess + • sys + • warnings + +
+ + +
+ +
+ + os.path +AliasNode
+imports: + ntpath + • os + +
+
+imported by: + os + • py_compile + • serial.tools.list_ports_common + • tracemalloc + +
+ +
+ +
+ + pathlib +SourceModule
+imports: + _collections_abc + • errno + • fnmatch + • functools + • grp + • io + • ntpath + • operator + • os + • posixpath + • pwd + • re + • stat + • sys + • urllib.parse + • warnings + +
+ + +
+ +
+ + pickle +SourceModule
+imports: + _compat_pickle + • _pickle + • codecs + • copyreg + • functools + • io + • itertools + • org + • pprint + • re + • struct + • sys + • types + +
+
+imported by: + logging + • tracemalloc + +
+ +
+ +
+ + posix +MissingModule
+imports: + resource + +
+
+imported by: + importlib._bootstrap_external + • os + • posixpath + • shutil + +
+ +
+ +
+ + posixpath +SourceModule
+imports: + genericpath + • os + • posix + • pwd + • re + • stat + • sys + +
+
+imported by: + flash.py + • fnmatch + • importlib.metadata + • os + • pathlib + • zipfile + +
+ +
+ +
+ + pprint +SourceModule
+imports: + collections + • dataclasses + • io + • re + • sys + • time + • types + +
+
+imported by: + pickle + +
+ +
+ +
+ + pwd +MissingModule
+imported by: + pathlib + • posixpath + • shutil + • subprocess + • tarfile + +
+ +
+ +
+ + pyDes +SourceModule
+imports: + sys + +
+
+imported by: + base.json_utils + +
+ +
+ +
+ + py_compile +SourceModule
+imports: + argparse + • enum + • importlib._bootstrap_external + • importlib.machinery + • importlib.util + • os + • os.path + • sys + • traceback + +
+
+imported by: + zipfile + +
+ +
+ +
+ + quopri +SourceModule
+imports: + binascii + • getopt + • io + • sys + +
+
+imported by: + email.encoders + • email.message + • encodings.quopri_codec + +
+ +
+ +
+ + random +SourceModule
+imports: + _collections_abc + • _random + • _sha512 + • bisect + • hashlib + • itertools + • math + • operator + • os + • statistics + • time + • warnings + +
+
+imported by: + email.generator + • email.utils + • statistics + • tempfile + +
+ +
+ +
+ + re +Package
+imports: + copyreg + • enum + • functools + • re + • re._compiler + • re._constants + • re._parser + • warnings + +
+ + +
+ +
+ + re._casefix +SourceModule
+imports: + re + +
+
+imported by: + flash.py + • re._compiler + +
+ +
+ +
+ + re._compiler +SourceModule
+imports: + _sre + • re + • re._casefix + • re._constants + • re._parser + • sys + +
+
+imported by: + flash.py + • re + • sre_compile + +
+ +
+ +
+ + re._constants +SourceModule
+imports: + _sre + • re + +
+
+imported by: + flash.py + • re + • re._compiler + • re._parser + • sre_constants + +
+ +
+ +
+ + re._parser +SourceModule
+imports: + re + • re._constants + • unicodedata + • warnings + +
+
+imported by: + flash.py + • re + • re._compiler + • sre_parse + +
+ +
+ +
+ + reprlib +SourceModule
+imports: + _thread + • builtins + • itertools + +
+
+imported by: + collections + • flash.py + • functools + +
+ +
+ +
+ + resource +MissingModule
+imported by: + posix + +
+ +
+ +
+ + select C:\Users\wongyiekheng\AppData\Local\Programs\Python\Python311\DLLs\select.pyd
+imported by: + selectors + • serial.serialposix + • subprocess + +
+ +
+ +
+ + selectors +SourceModule
+imports: + abc + • collections + • collections.abc + • math + • select + • sys + +
+
+imported by: + socket + • subprocess + +
+ +
+ +
+ + serial +Package + + +
+ +
+ + serial.serialcli +SourceModule
+imports: + 'System.IO' + • System + • __future__ + • serial + • serial.serialutil + +
+
+imported by: + serial + +
+ +
+ +
+ + serial.serialjava +SourceModule
+imports: + __future__ + • serial + • serial.serialutil + +
+
+imported by: + serial + +
+ +
+ +
+ + serial.serialposix +SourceModule
+imports: + __future__ + • array + • errno + • fcntl + • os + • select + • serial + • serial.serialutil + • struct + • sys + • termios + • warnings + +
+
+imported by: + serial + +
+ +
+ +
+ + serial.serialutil +SourceModule
+imports: + __future__ + • array + • io + • serial + • sys + • time + +
+ + +
+ +
+ + serial.serialwin32 +SourceModule
+imports: + __future__ + • ctypes + • serial + • serial.serialutil + • serial.win32 + • time + +
+
+imported by: + serial + +
+ +
+ +
+ + serial.tools +Package
+imports: + serial + • serial.tools.list_ports_common + +
+ + +
+ +
+ + serial.tools.list_ports +SourceModule
+imports: + __future__ + • argparse + • os + • re + • serial.tools + • serial.tools.list_ports_posix + • serial.tools.list_ports_windows + • sys + +
+
+imported by: + base.download_handler + +
+ +
+ +
+ + serial.tools.list_ports_common +SourceModule
+imports: + __future__ + • glob + • os + • os.path + • re + • serial.tools + +
+ + +
+ +
+ + serial.tools.list_ports_linux +SourceModule
+imports: + __future__ + • glob + • os + • serial.tools + • serial.tools.list_ports_common + +
+
+imported by: + serial.tools.list_ports_posix + +
+ +
+ +
+ + serial.tools.list_ports_osx +SourceModule
+imports: + __future__ + • ctypes + • serial.tools + • serial.tools.list_ports_common + +
+
+imported by: + serial.tools.list_ports_posix + +
+ +
+ +
+ + serial.tools.list_ports_posix +SourceModule +
+imported by: + serial.tools.list_ports + +
+ +
+ +
+ + serial.tools.list_ports_windows +SourceModule
+imports: + __future__ + • ctypes + • ctypes.wintypes + • re + • serial + • serial.tools + • serial.tools.list_ports_common + • serial.win32 + +
+
+imported by: + serial.tools.list_ports + +
+ +
+ +
+ + serial.win32 +SourceModule
+imports: + __future__ + • ctypes + • ctypes.wintypes + • serial + +
+ + +
+ +
+ + shutil +SourceModule
+imports: + bz2 + • collections + • errno + • fnmatch + • grp + • lzma + • nt + • os + • posix + • pwd + • stat + • sys + • tarfile + • zipfile + • zlib + +
+
+imported by: + argparse + • tarfile + • tempfile + • zipfile + +
+ +
+ +
+ + signal +SourceModule
+imports: + _signal + • enum + +
+
+imported by: + subprocess + +
+ +
+ +
+ + socket +SourceModule
+imports: + _socket + • array + • enum + • errno + • io + • os + • selectors + • sys + +
+
+imported by: + base.remote_serial + • email.utils + +
+ +
+ +
+ + sre_compile +SourceModule
+imports: + re + • re._compiler + • warnings + +
+
+imported by: + flash.py + +
+ +
+ +
+ + sre_constants +SourceModule
+imports: + re + • re._constants + • warnings + +
+
+imported by: + flash.py + +
+ +
+ +
+ + sre_parse +SourceModule
+imports: + re + • re._parser + • warnings + +
+
+imported by: + flash.py + +
+ +
+ +
+ + stat +SourceModule
+imports: + _stat + +
+
+imported by: + flash.py + • genericpath + • glob + • ntpath + • os + • pathlib + • posixpath + • shutil + • tarfile + • zipfile + +
+ +
+ +
+ + statistics +SourceModule
+imports: + _statistics + • bisect + • collections + • decimal + • fractions + • functools + • itertools + • math + • numbers + • operator + • random + • sys + +
+
+imported by: + random + +
+ +
+ +
+ + string +SourceModule
+imports: + _string + • collections + • re + +
+
+imported by: + email._encoded_words + • email._header_value_parser + • email.quoprimime + • logging + • ntpath + +
+ +
+ +
+ + stringprep +SourceModule
+imports: + unicodedata + +
+
+imported by: + encodings.idna + +
+ +
+ +
+ + struct +SourceModule
+imports: + _struct + +
+
+imported by: + base64 + • ctypes + • gettext + • gzip + • pickle + • serial.serialposix + • tarfile + • zipfile + +
+ +
+ +
+ + subprocess +SourceModule
+imports: + _posixsubprocess + • _winapi + • builtins + • contextlib + • errno + • fcntl + • grp + • io + • locale + • msvcrt + • os + • pwd + • select + • selectors + • signal + • sys + • threading + • time + • types + • warnings + +
+
+imported by: + os + +
+ +
+ +
+ + sys (builtin module)
+imported by: + _collections_abc + • _compression + • _pydecimal + • argparse + • ast + • base.rom_handler + • base.rtk_logging + • base.rtk_utils + • base64 + • calendar + • codecs + • collections + • colorama.ansitowin32 + • colorama.initialise + • contextlib + • ctypes + • ctypes._endian + • dataclasses + • datetime + • dis + • email._header_value_parser + • email.generator + • email.iterators + • email.policy + • encodings + • encodings.rot_13 + • encodings.utf_16 + • encodings.utf_32 + • enum + • flash.py + • fractions + • getopt + • gettext + • glob + • gzip + • importlib + • importlib._bootstrap_external + • importlib.metadata + • importlib.util + • inspect + • linecache + • locale + • logging + • ntpath + • os + • pathlib + • pickle + • posixpath + • pprint + • pyDes + • py_compile + • pyi_rth_inspect.py + • quopri + • re._compiler + • selectors + • serial + • serial.serialposix + • serial.serialutil + • serial.tools.list_ports + • serial.tools.list_ports_posix + • shutil + • socket + • statistics + • subprocess + • tarfile + • tempfile + • threading + • tokenize + • traceback + • types + • typing + • urllib.parse + • warnings + • weakref + • zipfile + +
+ +
+ +
+ + tarfile +SourceModule
+imports: + argparse + • builtins + • bz2 + • copy + • grp + • gzip + • io + • lzma + • os + • pwd + • re + • shutil + • stat + • struct + • sys + • time + • warnings + • zlib + +
+
+imported by: + shutil + +
+ +
+ +
+ + tempfile +SourceModule
+imports: + _thread + • errno + • functools + • io + • os + • random + • shutil + • sys + • types + • warnings + • weakref + +
+
+imported by: + importlib.resources._common + +
+ +
+ +
+ + termios +MissingModule
+imported by: + serial.serialposix + +
+ +
+ +
+ + textwrap +SourceModule
+imports: + re + +
+
+imported by: + argparse + • importlib.metadata + • importlib.metadata._adapters + • traceback + +
+ +
+ +
+ + threading +SourceModule
+imports: + _collections + • _thread + • _threading_local + • _weakrefset + • collections + • functools + • itertools + • os + • sys + • time + • traceback + • warnings + +
+
+imported by: + _threading_local + • base.remote_serial + • flash.py + • logging + • subprocess + • zipfile + +
+ +
+ +
+ + time (builtin module)
+imports: + _strptime + +
+
+imported by: + _datetime + • _strptime + • base.floader_handler + • base.remote_serial + • base.rom_handler + • datetime + • email._parseaddr + • email.generator + • email.utils + • gc + • gzip + • logging + • pprint + • random + • serial.serialutil + • serial.serialwin32 + • subprocess + • tarfile + • threading + • zipfile + +
+ +
+ +
+ + token +SourceModule
+imported by: + inspect + • tokenize + +
+ +
+ +
+ + tokenize +SourceModule
+imports: + _tokenize + • argparse + • builtins + • codecs + • collections + • functools + • io + • itertools + • re + • sys + • token + +
+
+imported by: + importlib._bootstrap_external + • inspect + • linecache + +
+ +
+ +
+ + traceback +SourceModule
+imports: + ast + • collections.abc + • contextlib + • itertools + • linecache + • sys + • textwrap + +
+
+imported by: + flash.py + • logging + • py_compile + • threading + • warnings + +
+ +
+ +
+ + tracemalloc +SourceModule
+imports: + _tracemalloc + • collections.abc + • fnmatch + • functools + • linecache + • os.path + • pickle + +
+
+imported by: + warnings + +
+ +
+ +
+ + types +SourceModule
+imports: + _collections_abc + • functools + • sys + +
+
+imported by: + _weakrefset + • contextlib + • copy + • ctypes + • dataclasses + • dis + • email.headerregistry + • enum + • flash.py + • functools + • importlib.metadata._functools + • importlib.resources._common + • importlib.resources._legacy + • importlib.util + • inspect + • logging + • pickle + • pprint + • subprocess + • tempfile + • typing + • urllib.parse + +
+ +
+ +
+ + typing +SourceModule
+imports: + _typing + • abc + • collections + • collections.abc + • contextlib + • functools + • operator + • re + • sys + • types + • warnings + +
+ + +
+ +
+ + unicodedata C:\Users\wongyiekheng\AppData\Local\Programs\Python\Python311\DLLs\unicodedata.pyd
+imported by: + encodings.idna + • re._parser + • stringprep + • urllib.parse + +
+ +
+ +
+ + urllib +Package
+imported by: + email._header_value_parser + • urllib.parse + +
+ +
+ +
+ + urllib.parse +SourceModule
+imports: + collections + • functools + • ipaddress + • re + • sys + • types + • unicodedata + • urllib + • warnings + +
+
+imported by: + email.utils + • pathlib + +
+ +
+ +
+ + version_info +SourceModule
+imported by: + flash.py + +
+ +
+ +
+ + warnings +SourceModule
+imports: + _warnings + • builtins + • linecache + • re + • sys + • traceback + • tracemalloc + +
+
+imported by: + argparse + • ast + • enum + • flash.py + • gettext + • gzip + • hashlib + • importlib + • importlib._abc + • importlib.abc + • importlib.metadata + • importlib.resources._legacy + • importlib.util + • io + • locale + • logging + • os + • pathlib + • random + • re + • re._parser + • serial.serialposix + • sre_compile + • sre_constants + • sre_parse + • subprocess + • tarfile + • tempfile + • threading + • typing + • urllib.parse + • zipfile + +
+ +
+ +
+ + weakref +SourceModule
+imports: + _collections_abc + • _weakref + • _weakrefset + • atexit + • copy + • gc + • itertools + • sys + +
+
+imported by: + _threading_local + • copy + • flash.py + • functools + • logging + • tempfile + +
+ +
+ +
+ + winreg (builtin module)
+imported by: + importlib._bootstrap_external + +
+ +
+ +
+ + zipfile +SourceModule
+imports: + argparse + • binascii + • bz2 + • contextlib + • importlib.util + • io + • itertools + • lzma + • os + • pathlib + • posixpath + • py_compile + • shutil + • stat + • struct + • sys + • threading + • time + • warnings + • zlib + +
+ + +
+ +
+ + zlib (builtin module)
+imported by: + encodings.zlib_codec + • gzip + • shutil + • tarfile + • zipfile + +
+ +
+ + + diff --git a/changelog.txt b/changelog.txt new file mode 100644 index 0000000..e9e39e8 --- /dev/null +++ b/changelog.txt @@ -0,0 +1,25 @@ +ChangeLog: +20250218 v1.0.0.1 (1) Support erase user area + (2) Support chip erase before download + (3) Add version info +20250220 v1.0.0.2 Tune command args +20250221 v1.0.0.3 (1) Fix log-file output bug + (2) Tune log format + (3) Add FileVersion in FileVersionInfo +20250321 v1.0.1.0 (1) Support linux images download + (2) Optimize request timeout + (3) Support check flash block protection before process flash +20250401 v1.0.1.1 (1) Check and program otp for flash size >=16MB + (2) Fix dtr/rts level unexpectedly changed issue when open/close serial port +20250408 v1.0.1.2 Fix nand flash download error with bad block +20250423 v1.0.1.3 (1) Fix download fail in 4byte address mode + (2) Supported otp about flash size >=16MB work for download +20250605 v1.0.2.0 Seperate flashloaders from flash.exe +20250701 v1.0.3.0 (1) Support compatibility with MinGW path format + (2) Support customized DTR/RTS timing for reset/reburn +20250703 v1.1.0.0 Support 1-N download/erase +20251105 v1.1.1.0 (1) Support for remote server port download + (2) Set PostProcess as reset after flashing finished + (3) Add no_reset param + (4) Change some log level + (5) Rename flashloader folder with Devices \ No newline at end of file diff --git a/flash.py b/flash.py new file mode 100644 index 0000000..0af37da --- /dev/null +++ b/flash.py @@ -0,0 +1,470 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +import os +import sys +import argparse +import base64 +import re +import threading +from copy import deepcopy + +from base import * +import version_info + +MinSupportedDeviceProfileMajorVersion = 1 +MinSupportedDeviceProfileMinorVersion = 1 +setting_file = "Settings.json" + + +def convert_mingw_path_to_windows(mingw_path): + drive_match = re.match(r'^/([a-zA-Z])/', mingw_path) + if drive_match: + drive_letter = drive_match.group(1).upper() + ":\\" + windows_path_tail = mingw_path[3:] + else: + drive_letter = "" + windows_path_tail = mingw_path + + windows_path_tail = windows_path_tail.replace('/', '\\') + + windows_path = drive_letter + windows_path_tail + + return windows_path + + +def sys_exit(logger, status, ret): + if status: + logger.info(f"Finished PASS") # customized, do not modify + sys.exit(0) + else: + logger.error(f"Finished FAIL: {ret}") # customized, do not modify + sys.exit(1) + + +def decoder_partition_string(partition_table_base64): + try: + if partition_table_base64 is None: + return None + partition_value = base64.b64decode(partition_table_base64).decode("utf-8") + partition_list = json.loads(partition_value) + return partition_list + except Exception as err: + raise argparse.ArgumentTypeError("Invalid partition table format with base64") from err + + +# --- add remote server params --- +def flash_process_entry(profile_info, serial_port, serial_baudrate, image_dir, settings, images_info, + chip_erase, + memory_type, memory_info, download, + log_level, log_f, + remote_server=None, remote_port=None, remote_password=None): + logger = create_logger(serial_port, log_level=log_level, file=log_f) + + ameba = Ameba(profile_info, serial_port, serial_baudrate, image_dir, settings, logger, + download_img_info=images_info, + chip_erase=chip_erase, + memory_type=memory_type, + erase_info=memory_info, + remote_server=remote_server, + remote_port=remote_port, + remote_password=remote_password) + if download: + # download + if not ameba.check_protocol_for_download(): + ret = ErrType.SYS_PROTO + sys_exit(logger, False, ret) + + ret, is_reburn = ameba.check_supported_flash_size(memory_type) + if ret != ErrType.OK: + logger.error(f"Check supported flash size fail") + sys_exit(logger, False, ret) + + if is_reburn: + ameba.__del__() + # reset with remote params + ameba = Ameba(profile_info, serial_port, serial_baudrate, image_dir, settings, logger, + download_img_info=images_info, + chip_erase=chip_erase, + memory_type=memory_type, + erase_info=memory_info, + remote_server=remote_server, + remote_port=remote_port, + remote_password=remote_password) + + logger.info(f"Image download start...") # customized, do not modify + ret = ameba.prepare() + if ret != ErrType.OK: + logger.error("Download prepare fail") + sys_exit(logger, False, ret) + + ret = ameba.verify_images() + if ret != ErrType.OK: + sys_exit(logger, False, ret) + + if not ameba.is_all_ram: + ret = ameba.post_verify_images() + if ret != ErrType.OK: + sys_exit(logger, False, ret) + + if not ameba.is_all_ram: + flash_status = FlashBPS() + ret = ameba.check_and_process_flash_lock(flash_status) + if ret != ErrType.OK: + logger.error("Download image fail") + sys_exit(logger, False, ret) + + ret = ameba.download_images() + if ret != ErrType.OK: + logger.error("Download image fail") + sys_exit(logger, False, ret) + + if (not ameba.is_all_ram) and flash_status.need_unlock: + logger.info("Restore the flash block protection...") + ret = ameba.lock_flash(flash_status.protection) + if ret != ErrType.OK: + logger.error(f"Fail to restore the flash block protection") + sys_exit(logger, False, ret) + + ret = ameba.post_process() + if ret != ErrType.OK: + logger.error("Post process fail") + sys_exit(logger, False, ret) + else: + # erase + ret = ameba.prepare() + if ret != ErrType.OK: + logger.error("Erase prepare fail") + sys_exit(logger, False, ret) + + if chip_erase: + ret = ameba.erase_flash_chip() + if ret != ErrType.OK: + logger.error("Chip erase fail") + sys_exit(logger, False, ret) + sys_exit(logger, True, ret) + + ret = ameba.validate_config_for_erase() + if ret != ErrType.OK: + sys_exit(logger, False, ret) + + ret = ameba.post_validate_config_for_erase() + if ret != ErrType.OK: + sys_exit(logger, False, ret) + + if (not profile_info.is_ram_address(memory_info.start_address)): + flash_status = FlashBPS() + ret = ameba.check_and_process_flash_lock(flash_status) + if ret != ErrType.OK: + logger.error("Erase fail") + sys_exit(logger, False, ret) + + ret = ameba.erase_flash() + if ret != ErrType.OK: + logger.error(f"Erase {memory_type} failed") + sys_exit(logger, False, ret) + + if (not profile_info.is_ram_address(memory_info.start_address)) and flash_status.need_unlock: + logger.info("Restore the flash block protection...") + ret = ameba.lock_flash(flash_status.protection) + if ret != ErrType.OK: + logger.error(f"Fail to restore the flash block protection") + sys_exit(logger, False, ret) + + sys_exit(logger, True, ret) + + +def main(argc, argv): + parser = argparse.ArgumentParser(description=None) + parser.add_argument('-d', '--download', action='store_true', help='download images') + parser.add_argument('-f', '--profile', type=str, help='device profile') + parser.add_argument('-p', '--port', nargs="+", help='serial port') + parser.add_argument('-b', '--baudrate', type=int, help='serial port baud rate') + parser.add_argument('-i', '--image', type=str, help='single image') + parser.add_argument('-r', '--image-dir', type=str, help='image directory') + parser.add_argument('-a', '--start-address', type=str, help='start address, hex') + parser.add_argument('-n', '--end-address', type=str, help='end address, hex') + parser.add_argument('-z', '--size', type=int, help='size in KB') + parser.add_argument('-m', '--memory-type', choices=['nor', 'nand', 'ram'], default="nor", + help='specified memory type') + parser.add_argument('-e', '--erase', action='store_true', help='erase flash') + parser.add_argument('-o', '--log-file', type=str, help='output log file with path') + parser.add_argument('-v', '--version', action='version', version=f'%(prog)s {version_info.version}') + + parser.add_argument('--chip-erase', action='store_true', help='chip erase') + parser.add_argument('--log-level', default='info', help='log level') + parser.add_argument('--partition-table', help="layout info, list") + + parser.add_argument('--remote-server', type=str, help='remote serial server IP address') + parser.add_argument('--remote-password', type=str, help='remote serial server validation password') + parser.add_argument('--no-reset', action='store_true', help='do not reset after flashing finished') + + args = parser.parse_args() + download = args.download + profile = args.profile + image = args.image + image_dir = args.image_dir + chip_erase = args.chip_erase + serial_ports = args.port + serial_baudrate = args.baudrate + log_level = args.log_level.upper() + log_file = args.log_file + erase = args.erase + start_addr = args.start_address + end_addr = args.end_address + size = args.size + mem_t = args.memory_type + partition_table = decoder_partition_string(args.partition_table) + + remote_server = args.remote_server + remote_port = 58916 + remote_password = args.remote_password + no_reset = args.no_reset + + if mem_t is not None: + if mem_t == "nand": + memory_type = MemoryInfo.MEMORY_TYPE_NAND + elif mem_t == "ram": + memory_type = MemoryInfo.MEMORY_TYPE_RAM + else: + memory_type = MemoryInfo.MEMORY_TYPE_NOR + else: + memory_type = None + + if log_file is not None: + log_path = os.path.dirname(log_file) + if log_path: + if not os.path.exists(log_path): + os.makedirs(log_path, exist_ok=True) + log_f = log_file + else: + log_f = os.path.join(os.getcwd(), log_file) + else: + log_f = None + logger = create_logger("main", log_level=log_level, file=log_f) + if log_file is not None: + logger.info(f"Log file: {log_file}") + + logger.info(f"Flash Version: {version_info.version}") + + if remote_server: + logger.info(f"Using remote serial server: {remote_server}:{remote_port}") + + if profile is None: + logger.error('Invalid arguments, no device profile specified') + parser.print_usage() + sys.exit(1) + + if not os.path.exists(profile): + logger.error("Device profile '" + profile + "' does not exist") + sys.exit(1) + logger.info(f'Device profile: {profile}') + + if serial_ports is None: + logger.error('Invalid arguments, no serial port specified') + parser.print_usage() + sys.exit(1) + logger.info(f'Serial port: {serial_ports}') + + if serial_baudrate is None: + logger.error('Invalid arguments, no serial baudrate specified') + parser.print_usage() + sys.exit(1) + logger.info(f'Baudrate: {serial_baudrate}') + + if all([download, erase]): + logger.warning("Download and erase are set true, only do image download ") + elif not (download or erase or chip_erase): + logger.error("Download or erase or chip-erase should be set") + sys.exit(1) + + memory_info = None + images_info = None + if download: + # download + if (image is None) and (image_dir is None) and (partition_table is None): + logger.error('Invalid arguments, no image or image_dir input') + parser.print_usage() + sys.exit(1) + + if image is not None: + download_img_info = ImageInfo() + if not os.path.exists(image): + logger.error(f"Image {image} does not exist") + sys.exit(1) + download_img_info.image_name = image + download_img_info.description = os.path.basename(image) + + if memory_type is None: + logger.error(f"Memory type is required for single image download") + sys.exit(1) + + if start_addr is None: + logger.error(f"Start address is required for single image download") + sys.exit(1) + + try: + start_address = int(start_addr, 16) + except Exception as err: + logger.error(f"Start address is invalid: {err}") + sys.exit(1) + download_img_info.start_address = start_address + + if memory_type == MemoryInfo.MEMORY_TYPE_NAND: + if end_addr is None: + logger.error(f"End address is required for nand flash download") + sys.exit(1) + + try: + end_address = int(end_addr, 16) + except Exception as err: + logger.error(f"End address is invalid: {err}") + sys.exit(1) + else: + end_address = start_address + os.path.getsize(image) + + download_img_info.end_address = end_address + download_img_info.memory_type = memory_type + download_img_info.mandatory = True + images_info = [download_img_info] + elif partition_table is not None: + images_info = [] + for img_info in partition_table: + img_json = ImageInfo(** img_info) + if sys.platform == "win32": + img_p = convert_mingw_path_to_windows(img_json.image_name) + img_json.image_name = img_p + img_json.description = os.path.basename(img_json.image_name) + images_info.append(img_json) + else: + images_info = None + if not os.path.exists(image_dir): + logger.error(f"Image directory {image_dir} does not exist") + sys.exit(1) + + logger.info(f'Image dir: {image_dir}') + if images_info: + logger.info(f'Image info:') + for img_info in images_info: + for key, value in img_info.__repr__().items(): + if key == "ImageName": + key = "Image" + logger.info(f'> {key}: {value}') + else: + # erase + if all([chip_erase, erase]): + logger.warning(f"Both chip erase and erase are enabled, do chip erase only") + if not chip_erase: + memory_info = MemoryInfo() + if start_addr is None: + logger.error(f"Start address is required for erase flash") + sys.exit(1) + + try: + start_address = int(start_addr, 16) + except Exception as err: + logger.error(f"Start address is invalid: {err}") + sys.exit(1) + memory_info.start_address = start_address + + if memory_type is None: + logger.error("Memory type is required for erase") + sys.exit(1) + + memory_info.memory_type = memory_type + + if memory_type == MemoryInfo.MEMORY_TYPE_NAND: + if end_addr is None: + logger.error(f"End address is required for nand flash download") + sys.exit(1) + else: + if size is None: + logger.error(f"Erase size is required") + sys.exit(1) + + if end_addr: + try: + end_address = int(end_addr, 16) + except Exception as err: + logger.error(f"End address is invalid: {err}") + sys.exit(1) + else: + end_address = 0 + + memory_info.size_in_kbyte = size + + if end_address == 0: + end_address = start_address + size + memory_info.end_address = end_address + + if chip_erase: + logger.info(f"Chip erase: {chip_erase}") + if memory_type is None: + logger.warning("Memory type is required for chip erase") + sys.exit(1) + if memory_type != MemoryInfo.MEMORY_TYPE_NOR: + logger.warning("Memory type should be 'nor' for chip erase") + else: + logger.info(f"Chip erase: False") + + # check device profile + try: + profile_json = JsonUtils.load_from_file(profile) + if profile_json is None: + logger.error(f"Fail to load device profile {profile}") + sys.exit(1) + profile_info = RtkDeviceProfile(**profile_json) + ver = profile_info.get_version() + if ver.major >= MinSupportedDeviceProfileMajorVersion and ver.minor >= MinSupportedDeviceProfileMinorVersion and profile_info.device_id != 0: + logger.info(f"Device profile {profile} loaded") + else: + logger.error(f"Fail to load device profile {profile}, unsupported version {ver.__repr__()}") + sys.exit(1) + except Exception as err: + logger.error(f"Load device profile {profile} exception: {err}") + sys.exit(1) + + # load settings + setting_path = os.path.realpath(os.path.join(RtkUtils.get_executable_root_path(), setting_file)) + logger.info(f"Settings path: {setting_path}") + try: + if os.path.exists(setting_path): + dt = JsonUtils.load_from_file(setting_path, need_decrypt=False) + settings = RtSettings(** dt) + else: + logger.debug(f"{setting_file} not exists!") + settings = RtSettings(**{}) + except Exception as err: + logger.error(f"Load settings exception: {err}") + settings = RtSettings(** {}) + # save Setting.json + try: + if no_reset: + settings.post_process = "NONE" + else: + settings.post_process = "RESET" + JsonUtils.save_to_file(setting_path, settings.__repr__()) + except Exception as err: + logger.debug(f"save {setting_file} exception: {err}") + + threads_list = [] + + for sp in serial_ports: + flash_thread = threading.Thread(target=flash_process_entry, args=( + profile_info, sp, serial_baudrate, image_dir, settings, deepcopy(images_info), chip_erase, + memory_type, memory_info, download, log_level, log_f, + remote_server, remote_port, remote_password)) + threads_list.append(flash_thread) + flash_thread.start() + + for thred in threads_list: + thred.join() + + logger.info(f"All flash threads have completed") + + +if __name__ == "__main__": + main(len(sys.argv), sys.argv[1:]) diff --git a/flash.spec b/flash.spec new file mode 100644 index 0000000..de14eda --- /dev/null +++ b/flash.spec @@ -0,0 +1,81 @@ +# -*- mode: python ; coding: utf-8 -*- +import sys +from pathlib import Path +sys.path.insert(0, str((Path("__name__").parent))) +from tempfile import NamedTemporaryFile +from version_info import version + +temp_version_file=Path("version.tmp") +temp_version_file.write_text( +f"""# UTF-8 encoding +# UTF-8 +# +# Fields to define the version info for a Windows executable +VSVersionInfo( + ffi=FixedFileInfo( + filevers=({version.replace(".",",")}), # 文件版本号 + prodvers=({version.replace(".",",")}), # 产品版本号 + mask=0x3f, + flags=0x0, + OS=0x40004, + fileType=0x1, + subtype=0x0, + date=(0, 0) + ), + kids=[ + StringFileInfo( + [ + StringTable( + u'040904B0', + [StringStruct(u'CompanyName', u'Realtek Semiconductor Corp.'), + StringStruct(u'FileDescription', u'Flash Tool for Realtek Ameba SoCs'), + StringStruct(u'FileVersion', u'{version}'), + StringStruct(u'InternalName', u'flash.exe'), + StringStruct(u'LegalCopyright', u'Copyright (c) 2025 Realtek Semiconductor Corp.'), + StringStruct(u'OriginalFilename', u'flash.exe'), + StringStruct(u'ProductName', u'Ameba Flash Tool'), + StringStruct(u'ProductVersion', u'{version}')]) + ]), + VarFileInfo([VarStruct(u'Translation', [1033, 1200])]) + ] +) +""",encoding='utf-8') + + +a = Analysis( + ['flash.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.datas, + [], + name='flash', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + version=temp_version_file.name, +) +os.remove(temp_version_file.name) diff --git a/fw/all_app.bin b/fw/all_app.bin new file mode 100644 index 0000000..cdc5e22 Binary files /dev/null and b/fw/all_app.bin differ diff --git a/fw/all_app_en.bin b/fw/all_app_en.bin new file mode 100644 index 0000000..cdc5e22 Binary files /dev/null and b/fw/all_app_en.bin differ diff --git a/fw/amebapro3_app.bin b/fw/amebapro3_app.bin new file mode 100644 index 0000000..cb17329 Binary files /dev/null and b/fw/amebapro3_app.bin differ diff --git a/fw/amebapro3_boot.bin b/fw/amebapro3_boot.bin new file mode 100644 index 0000000..d2e2c17 Binary files /dev/null and b/fw/amebapro3_boot.bin differ diff --git a/fw/ap_app.bin b/fw/ap_app.bin new file mode 100644 index 0000000..e69de29 diff --git a/fw/cert.bin b/fw/cert.bin new file mode 100644 index 0000000..149fa17 Binary files /dev/null and b/fw/cert.bin differ diff --git a/fw/fp_app.bin b/fw/fp_app.bin new file mode 100644 index 0000000..e69de29 diff --git a/fw/fw_en.bin b/fw/fw_en.bin new file mode 100644 index 0000000..cb17329 Binary files /dev/null and b/fw/fw_en.bin differ diff --git a/fw/fw_plain.bin b/fw/fw_plain.bin new file mode 100644 index 0000000..cb17329 Binary files /dev/null and b/fw/fw_plain.bin differ diff --git a/fw/manifest.bin b/fw/manifest.bin new file mode 100644 index 0000000..3736bdf Binary files /dev/null and b/fw/manifest.bin differ diff --git a/fw/np_app.bin b/fw/np_app.bin new file mode 100644 index 0000000..cdc5e22 Binary files /dev/null and b/fw/np_app.bin differ diff --git a/fw/np_boot.bin b/fw/np_boot.bin new file mode 100644 index 0000000..d2e2c17 Binary files /dev/null and b/fw/np_boot.bin differ diff --git a/fw/vp_app.bin b/fw/vp_app.bin new file mode 100644 index 0000000..e69de29 diff --git a/package_pro3_exe.py b/package_pro3_exe.py new file mode 100644 index 0000000..28e48f2 --- /dev/null +++ b/package_pro3_exe.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +""" +Helper script to bundle pro3_uart.py into an executable together with the +files that flash.py expects. + +Usage: + python package_pro3_exe.py + +Prerequisites: + pip install pyinstaller +""" + +from __future__ import annotations + +import shutil +import sys +from pathlib import Path +import re + +try: + from PyInstaller import __main__ as pyinstaller_main +except ImportError as exc: + print("PyInstaller is required. Install it with 'pip install pyinstaller'.") + raise SystemExit(1) from exc + + +APP_ROOT = Path(__file__).resolve().parent +PROJECT_BASE_NAME = "pro3_uart" + + +def derive_version_from_folder() -> str: + root_name = APP_ROOT.name + match = re.search(r"_v([0-9][0-9.\-_]*)$", root_name, re.IGNORECASE) + if not match: + return "" + return match.group(1) + + +def read_version_file() -> tuple[str, str]: + version_file = APP_ROOT / "version_info.py" + if not version_file.exists(): + return "", "" + namespace: dict[str, str] = {} + try: + with version_file.open("r", encoding="utf-8") as handle: + exec(handle.read(), namespace) + except Exception: + return "", "" + canonical = str(namespace.get("version", "")).strip() + display = str(namespace.get("display_version", "")).strip() + return canonical, display + + +def write_version_file(version: str, display: str) -> None: + version_file = APP_ROOT / "version_info.py" + template = f'''#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +version = "{version}" +display_version = "{display}" +''' + with version_file.open("w", encoding="utf-8") as handle: + handle.write(template) + + +def determine_version_tag() -> str: + folder_version = derive_version_from_folder() + stored_canonical, stored_display = read_version_file() + chosen = folder_version or stored_display or stored_canonical + sanitized = re.sub(r"[^0-9A-Za-z._-]", "_", chosen) + if not sanitized: + sanitized = "unknown" + canonical = canonicalize_version_number(sanitized) + if canonical != stored_canonical or sanitized != stored_display: + write_version_file(canonical, sanitized) + return sanitized + + +def canonicalize_version_number(tag: str) -> str: + cleaned = tag.replace("-", ".").replace("_", ".") + parts = [re.sub(r"\D+", "", chunk) or "0" for chunk in cleaned.split(".") if chunk] + if not parts: + parts = ["0"] + while len(parts) < 4: + parts.append("0") + return ".".join(parts[:4]) + + +VERSION_TAG = determine_version_tag() +RESOURCE_VERSION = canonicalize_version_number(VERSION_TAG) +DIST_NAME = f"{PROJECT_BASE_NAME}_v{VERSION_TAG}" +DIST_DIR = APP_ROOT / "dist" / DIST_NAME +BUILD_DIR = APP_ROOT / "build" +FLASH_SPEC = APP_ROOT / "flash.spec" +FLASH_EXE_NAME = "flash.exe" +FLASH_OUTPUT_PATHS = [ + APP_ROOT / "dist" / FLASH_EXE_NAME, + APP_ROOT / "dist" / "flash" / FLASH_EXE_NAME, +] + +RESOURCE_ITEMS = [ + ("base", "base"), + ("devices", "devices"), + ("fw", "fw"), + ("Reburn.cfg", "Reburn.cfg"), + ("Reset.cfg", "Reset.cfg"), + ("Settings.json", "Settings.json"), + ("pylink", "pylink"), +] + +PYLINK_DIR = APP_ROOT / "pylink" +JLINK_DLL_NAME = "JLinkARM.dll" +JLINK_DLL_SOURCE = PYLINK_DIR / JLINK_DLL_NAME + + +def clean_path(path: Path) -> None: + if path.exists(): + shutil.rmtree(path) + + +def cleanup_old_version_specs() -> None: + pattern = f"{PROJECT_BASE_NAME}_v*.spec" + for spec_path in APP_ROOT.glob(pattern): + try: + spec_path.unlink() + print(f"Removed outdated spec: {spec_path.name}") + except Exception as exc: + print(f"Warning: unable to remove {spec_path}: {exc}") + + +def run_pyinstaller_for_pro3() -> None: + if DIST_DIR.exists(): + shutil.rmtree(DIST_DIR) + clean_path(BUILD_DIR) + + args = [ + "--noconfirm", + "--clean", + "--windowed", + "--onedir", + "--name", + DIST_NAME, + str(APP_ROOT / "pro3_uart.py"), + ] + + print("Running PyInstaller...") + pyinstaller_main.run(args) + + +def run_pyinstaller_for_flash() -> None: + if not FLASH_SPEC.exists(): + raise SystemExit(f"flash.spec not found at {FLASH_SPEC}") + clean_path(BUILD_DIR) + print("Building flash.exe via flash.spec...") + pyinstaller_main.run(["--noconfirm", str(FLASH_SPEC)]) + + +def copy_resources() -> None: + if not DIST_DIR.exists(): + raise SystemExit(f"PyInstaller output not found: {DIST_DIR}") + + for src, dest in RESOURCE_ITEMS: + src_path = APP_ROOT / src + dest_path = DIST_DIR / dest + if not src_path.exists(): + print(f"Warning: resource '{src}' not found, skipping.") + continue + + if src_path.is_dir(): + if dest_path.exists(): + shutil.rmtree(dest_path) + shutil.copytree(src_path, dest_path) + else: + dest_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(src_path, dest_path) + print(f"Copied {src_path} -> {dest_path}") + if src == "pylink": + dll_path = dest_path / JLINK_DLL_NAME + if not dll_path.exists(): + raise SystemExit(f"{JLINK_DLL_NAME} was not found in '{src_path}'.") + + +def copy_flash_executable() -> None: + source = None + for candidate in FLASH_OUTPUT_PATHS: + if candidate.exists(): + source = candidate + break + if not source: + print("Warning: flash.exe build not found, skipping copy.") + return + dest = DIST_DIR / FLASH_EXE_NAME + shutil.copy2(source, dest) + print(f"Copied {source} -> {dest}") + + +def verify_jlink_dll() -> None: + if not JLINK_DLL_SOURCE.exists(): + raise SystemExit( + f"{JLINK_DLL_NAME} is required but missing from '{PYLINK_DIR}'. " + "Copy the DLL into the pylink folder before packaging." + ) + + +def main() -> None: + cleanup_old_version_specs() + verify_jlink_dll() + run_pyinstaller_for_pro3() + run_pyinstaller_for_flash() + copy_resources() + copy_flash_executable() + exe_path = DIST_DIR / f"{DIST_NAME}.exe" + if exe_path.exists(): + print(f"\nBuild complete: {exe_path}") + else: + print("\nBuild complete. Exe is located inside:", DIST_DIR) + + +if __name__ == "__main__": + main() diff --git a/pro3_uart.py b/pro3_uart.py new file mode 100644 index 0000000..b10b9ef --- /dev/null +++ b/pro3_uart.py @@ -0,0 +1,2783 @@ +"""Dual UART + J-Link debugger control panel for AmebaPro3 boards.""" + +from __future__ import annotations + +import datetime +import os +import re +import shutil +import subprocess +import sys +import tempfile +import threading +import time +import tkinter as tk +from collections import deque +from tkinter import filedialog, messagebox, ttk +from typing import Callable, Iterable, List, Optional + +import serial # type: ignore +import serial.tools.list_ports # type: ignore + +try: + from version_info import display_version as _DISPLAY_VERSION_VALUE, version as _CANONICAL_VERSION_VALUE +except Exception: # pragma: no cover - optional metadata + _DISPLAY_VERSION_VALUE = "" + _CANONICAL_VERSION_VALUE = "" + + +if getattr(sys, "frozen", False): + APP_ROOT = os.path.dirname(sys.executable) +else: + APP_ROOT = os.path.dirname(os.path.abspath(__file__)) + +PYLINK_DLL_PATH = os.path.join(APP_ROOT, "pylink", "JLinkARM.dll") +if os.path.exists(PYLINK_DLL_PATH): + os.environ.setdefault("JLINKARM_DLL_PATH", PYLINK_DLL_PATH) +try: + import pylink # type: ignore +except Exception: # pragma: no cover - pylink is optional + pylink = None + +FLASH_PROFILE = "devices/Profiles/AmebaPro3_FreeRTOS_NOR.rdev" +FLASH_IMAGE_DIR = "fw" +FLASH_BAUDRATE = "1500000" +FLASH_MEMORY_TYPE = "nor" +FIRMWARE_COPY_MAP = [ + ("fw_en.bin", "amebapro3_app.bin"), + ("np_boot.bin", "amebapro3_boot.bin"), +] + +PORT_REFRESH_MS = 1000 +LOG_HISTORY_LIMIT = 2000 +HISTORY_LIMIT = 200 +JLINK_SCRIPT_EXTS = {".jlink", ".jscr", ".jsf", ".txt"} +GDB_SCRIPT_EXTS = {".gdb", ".gdbinit"} + + +def _folder_version_guess() -> str: + base_name = os.path.basename(APP_ROOT) + match = re.search(r"_v([0-9A-Za-z._-]+)$", base_name) + return match.group(1) if match else "" + + +def _format_version_candidate(tag: str, trim_trailing_zero: bool = False) -> str: + text = str(tag or "").strip() + if not text: + return "" + if trim_trailing_zero: + parts = [segment for segment in text.replace("_", ".").split(".") if segment] + if not parts: + return "" + while len(parts) > 1 and parts[-1] == "0": + parts.pop() + return ".".join(parts) + return text + + +def _resolve_app_version() -> str: + for candidate, trim in ( + (_DISPLAY_VERSION_VALUE, False), + (_CANONICAL_VERSION_VALUE, True), + (_folder_version_guess(), False), + ): + formatted = _format_version_candidate(candidate, trim_trailing_zero=trim) + if formatted: + return formatted + return "unknown" + + +APP_VERSION = _resolve_app_version() + + +def timestamp() -> str: + now = datetime.datetime.now() + return now.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] + + +def safe_int(value) -> Optional[int]: + try: + return int(value) + except Exception: + return None + + +class DevicePanel: + """Single UART console with optional macro buttons and flashing control.""" + + def __init__( + self, + parent: tk.Widget, + title: str, + mode: str, + default_baud: str, + line_ending: str, + console_height: int = 15, + flash_handler: Optional[Callable[[], None]] = None, + flash_button_label: str = "Flash Firmware", + on_connect: Optional[Callable[[str], None]] = None, + on_disconnect: Optional[Callable[[str], None]] = None, + exclude_ports_provider: Optional[Callable[[], Iterable[str]]] = None, + before_button_callback: Optional[Callable[[], None]] = None, + ) -> None: + self.mode = mode + self.line_ending = line_ending or "" + self.flash_handler = flash_handler + self.flash_button_label = flash_button_label + self.on_connect = on_connect + self.on_disconnect = on_disconnect + self.exclude_ports_provider = exclude_ports_provider + self.before_button_callback = before_button_callback + + self.serial_port: Optional[serial.Serial] = None + self.connected_port: Optional[str] = None + self.read_thread: Optional[threading.Thread] = None + self.read_thread_running = False + + self.msg_queue: deque[str] = deque() + self.msg_lock = threading.Lock() + self.log_line_count = 0 + self.port_map: dict[str, str] = {} + + self.raw_history: List[str] = [] + self.raw_history_index = 0 + self.arduino_history: List[str] = [] + self.arduino_history_index = 0 + + self.frame = ttk.LabelFrame(parent, text=title) + self._build_widgets(console_height, default_baud) + self.enable_controls(False) + self.refresh_ports() + self._schedule_port_refresh() + + # ------------------------------------------------------------------ + # UI construction + # ------------------------------------------------------------------ + def _build_widgets(self, console_height: int, default_baud: str) -> None: + conn_frame = ttk.Frame(self.frame) + conn_frame.pack(fill="x", padx=5, pady=5) + conn_frame.columnconfigure(1, weight=1) + + ttk.Label(conn_frame, text="Port:").grid(row=0, column=0, sticky="w") + self.port_var = tk.StringVar() + self.port_combo = ttk.Combobox(conn_frame, textvariable=self.port_var, state="readonly", width=80) + self.port_combo.grid(row=0, column=1, columnspan=2, sticky="ew", padx=3) + + ttk.Label(conn_frame, text="Baud:").grid(row=1, column=0, sticky="w", pady=(8, 0)) + self.baud_var = tk.StringVar(value=str(default_baud)) + ttk.Entry(conn_frame, textvariable=self.baud_var, width=12).grid(row=1, column=1, sticky="w", padx=3) + + self.connect_button = ttk.Button(conn_frame, text="Connect", width=12, command=self.connect_serial) + self.connect_button.grid(row=0, column=3, rowspan=2, padx=(8, 0)) + + self.status_var = tk.StringVar(value="Disconnected") + self.status_label = ttk.Label(conn_frame, textvariable=self.status_var, foreground="red") + self.status_label.grid(row=2, column=0, columnspan=4, sticky="w", pady=(6, 0)) + + console_frame = ttk.Frame(self.frame) + console_frame.pack(fill="both", expand=True, padx=5, pady=5) + console_frame.columnconfigure(0, weight=1) + console_frame.rowconfigure(0, weight=1) + + self.log_text = tk.Text(console_frame, height=console_height, wrap="none") + self.log_text.grid(row=0, column=0, sticky="nsew") + self.log_text.configure(state="disabled") + + vscroll = ttk.Scrollbar(console_frame, orient="vertical", command=self.log_text.yview) + vscroll.grid(row=0, column=1, sticky="ns") + self.log_text.configure(yscrollcommand=vscroll.set) + + hscroll = ttk.Scrollbar(console_frame, orient="horizontal", command=self.log_text.xview) + hscroll.grid(row=1, column=0, sticky="ew") + self.log_text.configure(xscrollcommand=hscroll.set) + + button_row = ttk.Frame(console_frame) + button_row.grid(row=2, column=0, columnspan=2, sticky="ew", pady=(5, 0)) + button_row.columnconfigure(0, weight=1) + + clear_row = ttk.Frame(button_row) + clear_row.grid(row=0, column=0, sticky="w") + ttk.Button(clear_row, text="Clear Console", width=14, command=self.clear_console).grid(row=0, column=0, padx=(0, 5)) + ttk.Button(clear_row, text="Save Log", width=14, command=self.save_console).grid(row=0, column=1) + + if self.flash_handler: + self.flash_button = ttk.Button(button_row, text=self.flash_button_label, width=18, command=self.flash_handler) + self.flash_button.grid(row=0, column=1, sticky="e") + else: + self.flash_button = None + + if self.mode == "raw": + self._build_raw_controls(console_frame) + else: + self._build_arduino_controls(console_frame) + + def _build_raw_controls(self, console_frame: tk.Widget) -> None: + history_frame = ttk.Frame(console_frame) + history_frame.grid(row=3, column=0, columnspan=2, sticky="nsew", pady=(5, 0)) + history_frame.rowconfigure(1, weight=1) + history_frame.columnconfigure(0, weight=1) + + ttk.Label(history_frame, text="Input history:").grid(row=0, column=0, sticky="w") + self.history_listbox = tk.Listbox(history_frame, height=8) + self.history_listbox.grid(row=1, column=0, sticky="nsew") + self.history_listbox.bind("", self.history_select) + + history_scroll = ttk.Scrollbar(history_frame, orient="vertical", command=self.history_listbox.yview) + history_scroll.grid(row=1, column=1, sticky="ns") + self.history_listbox.configure(yscrollcommand=history_scroll.set) + + input_frame = ttk.Frame(console_frame) + input_frame.grid(row=4, column=0, sticky="ew", pady=(5, 0)) + input_frame.columnconfigure(0, weight=1) + + self.raw_entry_var = tk.StringVar() + self.raw_entry = ttk.Entry(input_frame, textvariable=self.raw_entry_var) + self.raw_entry.grid(row=0, column=0, sticky="ew", padx=(0, 5)) + self.raw_entry.bind("", lambda _evt: self.send_input_line()) + self.raw_entry.bind("", self._raw_history_prev) + self.raw_entry.bind("", self._raw_history_next) + + self.raw_send_button = ttk.Button(input_frame, text="Send", command=self.send_input_line) + self.raw_send_button.grid(row=0, column=1) + + self.arduino_entry = None + self.arduino_entry_var = None + self.arduino_send_button = None + + def _build_arduino_controls(self, console_frame: tk.Widget) -> None: + command_frame = ttk.Frame(console_frame) + command_frame.grid(row=3, column=0, columnspan=2, sticky="ew", pady=(5, 0)) + command_frame.columnconfigure((0, 1, 2), weight=1) + + commands = [ + ("Download Mode", ("pg 1", "reset")), + ("Normal Mode", ("pg 0", "reset")), + ("RESET", ("reset",)), + ] + for idx, (label, payload) in enumerate(commands): + ttk.Button( + command_frame, + text=label, + command=lambda seq=payload: self.send_macro(seq), + ).grid(row=0, column=idx, padx=5, sticky="ew") + + input_frame = ttk.Frame(console_frame) + input_frame.grid(row=4, column=0, sticky="ew", pady=(5, 0)) + input_frame.columnconfigure(0, weight=1) + + self.arduino_entry_var = tk.StringVar() + self.arduino_entry = ttk.Entry(input_frame, textvariable=self.arduino_entry_var) + self.arduino_entry.grid(row=0, column=0, sticky="ew", padx=(0, 5)) + self.arduino_entry.bind("", lambda _evt: self.send_arduino_input()) + self.arduino_entry.bind("", self._arduino_history_prev) + self.arduino_entry.bind("", self._arduino_history_next) + + self.arduino_send_button = ttk.Button(input_frame, text="Send", command=self.send_arduino_input) + self.arduino_send_button.grid(row=0, column=1) + + self.history_listbox = None + self.raw_entry = None + self.raw_entry_var = None + self.raw_send_button = None + + # ------------------------------------------------------------------ + # Console helpers + # ------------------------------------------------------------------ + def enable_controls(self, enabled: bool) -> None: + state = "normal" if enabled else "disabled" + targets = [self.raw_entry, self.raw_send_button, self.arduino_entry, self.arduino_send_button] + for widget in targets: + if widget is not None: + widget.configure(state=state) + + def clear_console(self) -> None: + with self.msg_lock: + self.msg_queue.clear() + self.log_text.configure(state="normal") + self.log_text.delete("1.0", "end") + self.log_text.configure(state="disabled") + self.log_line_count = 0 + + def save_console(self) -> None: + try: + content = self.log_text.get("1.0", "end-1c") + except tk.TclError: + content = "" + if not content.strip(): + messagebox.showinfo("Save Console Log", "There is no console output to save.") + return + path = filedialog.asksaveasfilename( + title="Save Console Log", + defaultextension=".txt", + filetypes=(("Text Files", "*.txt"), ("All Files", "*.*")), + ) + if not path: + return + try: + with open(path, "w", encoding="utf-8") as handle: + handle.write(content) + except Exception as exc: + messagebox.showerror("Save Failed", f"Unable to save log: {exc}") + + def queue_message(self, text: str) -> None: + if not text: + return + with self.msg_lock: + self.msg_queue.append(str(text)) + + def poll_queue(self) -> None: + with self.msg_lock: + if not self.msg_queue: + return + entries = list(self.msg_queue) + self.msg_queue.clear() + self._log_lines(entries) + + def _log_lines(self, entries: Iterable[str]) -> None: + prepared = [] + for entry in entries: + trimmed = str(entry).strip() + if not trimmed: + continue + if trimmed.strip(".") == "": + continue + prepared.append(f"[{timestamp()}] {trimmed}\n") + if not prepared: + return + self.log_line_count += len(prepared) + block = "".join(prepared) + self.log_text.configure(state="normal") + self.log_text.insert("end", block) + if LOG_HISTORY_LIMIT and self.log_line_count > LOG_HISTORY_LIMIT: + excess = self.log_line_count - LOG_HISTORY_LIMIT + self.log_text.delete("1.0", f"{excess + 1}.0") + self.log_line_count = LOG_HISTORY_LIMIT + self.log_text.see("end") + self.log_text.configure(state="disabled") + + # ------------------------------------------------------------------ + # Serial control + # ------------------------------------------------------------------ + def refresh_ports(self) -> None: + selected = self._resolve_port(self.port_var.get()) + excluded = set(self.exclude_ports_provider() or []) if self.exclude_ports_provider else set() + mapping: dict[str, str] = {} + display: List[str] = [] + for port in serial.tools.list_ports.comports(): + desc = (port.description or port.name or "").strip() + label = port.device if not desc else f"{port.device}: {desc}" + keep = self.connected_port == port.device + if port.device in excluded and not keep: + continue + mapping[label] = port.device + display.append(label) + self.port_map = mapping + self.port_combo["values"] = display + if not display: + self.port_var.set("") + return + if selected: + for label, device in mapping.items(): + if device == selected: + self.port_var.set(label) + return + preferred = next((label for label, device in mapping.items() if device not in excluded), None) + self.port_var.set(preferred or display[0]) + + def _schedule_port_refresh(self) -> None: + try: + self.frame.after(PORT_REFRESH_MS, self._auto_refresh_ports) + except tk.TclError: + pass + + def _auto_refresh_ports(self) -> None: + if not self.frame.winfo_exists(): + return + self.refresh_ports() + self._schedule_port_refresh() + + def connect_serial(self) -> None: + if self.serial_port and self.serial_port.is_open: + self.disconnect_serial() + return + port = self._resolve_port(self.port_var.get()) + if not port: + messagebox.showwarning("Select Port", "Please select a COM port") + return + try: + baud = int((self.baud_var.get() or "0").strip()) + except ValueError: + messagebox.showwarning("Invalid Baud", "Enter a numeric baud rate") + return + try: + self.serial_port = serial.Serial(port, baudrate=baud, timeout=0.1) + except Exception as exc: + messagebox.showerror("Serial Error", str(exc)) + return + self.connected_port = port + self.status_var.set(f"Connected to {port} @ {baud}") + self._set_status_color("green") + self.connect_button.configure(text="Disconnect") + self.enable_controls(True) + if self.on_connect: + try: + self.on_connect(port) + except Exception: + pass + self.read_thread_running = True + self.read_thread = threading.Thread(target=self._read_loop, daemon=True) + self.read_thread.start() + + def disconnect_serial(self) -> None: + self.read_thread_running = False + if self.serial_port: + try: + self.serial_port.close() + except Exception: + pass + if self.read_thread and self.read_thread.is_alive(): + self.read_thread.join(timeout=0.2) + self.serial_port = None + last_port = self.connected_port + self.connected_port = None + self.status_var.set("Disconnected") + self._set_status_color("red") + self.connect_button.configure(text="Connect") + self.enable_controls(False) + if last_port and self.on_disconnect: + try: + self.on_disconnect(last_port) + except Exception: + pass + + def _set_status_color(self, color: str) -> None: + try: + self.status_label.configure(foreground=color) + except tk.TclError: + pass + + def _resolve_port(self, value: str) -> str: + if value in self.port_map: + return self.port_map[value] + base = (value or "").split(":", 1)[0].strip() + return base + + def is_connected(self) -> bool: + return bool(self.serial_port and self.serial_port.is_open) + + def get_selected_port(self) -> str: + return self._resolve_port(self.port_var.get()) + + def send_macro(self, commands: Iterable[str]) -> None: + if self.before_button_callback: + try: + self.before_button_callback() + except Exception: + pass + for cmd in commands: + self.send_uart(cmd) + time.sleep(0.05) + + def send_input_line(self) -> None: + if not self.raw_entry_var: + return + payload = self.raw_entry_var.get() + if not payload: + return + self.send_uart(payload) + self.raw_history.append(payload) + self.raw_history_index = len(self.raw_history) + if self.history_listbox: + self.history_listbox.insert("end", payload) + if self.history_listbox.size() > HISTORY_LIMIT: + self.history_listbox.delete(0) + self.raw_entry_var.set("") + + def send_arduino_input(self) -> None: + if not self.arduino_entry_var: + return + payload = self.arduino_entry_var.get() + if not payload: + return + self.send_uart(payload) + self.arduino_history.append(payload) + self.arduino_history_index = len(self.arduino_history) + self.arduino_entry_var.set("") + + def history_select(self, _event) -> None: + if not self.history_listbox: + return + sel = self.history_listbox.curselection() + if not sel: + return + cmd = self.history_listbox.get(sel[0]) + self.raw_entry_var.set(cmd) + if self.raw_entry: + try: + self.raw_entry.icursor("end") + except tk.TclError: + pass + self.send_uart(cmd) + + def _raw_history_prev(self, _event) -> str: + self._navigate_history(self.raw_history, "raw_history_index", self.raw_entry_var, self.raw_entry, -1) + return "break" + + def _raw_history_next(self, _event) -> str: + self._navigate_history(self.raw_history, "raw_history_index", self.raw_entry_var, self.raw_entry, 1) + return "break" + + def _arduino_history_prev(self, _event) -> str: + self._navigate_history(self.arduino_history, "arduino_history_index", self.arduino_entry_var, self.arduino_entry, -1) + return "break" + + def _arduino_history_next(self, _event) -> str: + self._navigate_history(self.arduino_history, "arduino_history_index", self.arduino_entry_var, self.arduino_entry, 1) + return "break" + + def _navigate_history(self, history: List[str], attr: str, var: Optional[tk.StringVar], widget, delta: int) -> None: + if not history or var is None: + return + index = getattr(self, attr, len(history)) + index = max(0, min(len(history), index + delta)) + setattr(self, attr, index) + var.set("" if index == len(history) else history[index]) + if widget: + try: + widget.icursor("end") + except tk.TclError: + pass + + def send_uart(self, text: str) -> None: + if not (self.serial_port and self.serial_port.is_open): + return + try: + payload = text + self.line_ending + self.serial_port.write(payload.encode("utf-8")) + except Exception as exc: + messagebox.showerror("UART Error", str(exc)) + + def _read_loop(self) -> None: + buffer = bytearray() + while self.read_thread_running: + port = self.serial_port + if not port or not port.is_open: + time.sleep(0.05) + continue + try: + waiting = port.in_waiting + except Exception: + waiting = 0 + if waiting: + try: + chunk = port.read(waiting) + except Exception: + chunk = b"" + if chunk: + buffer.extend(chunk) + while True: + idx = buffer.find(b"\n") + if idx == -1: + break + raw_line = buffer[:idx] + del buffer[: idx + 1] + raw_line = raw_line.rstrip(b"\r") + if not raw_line: + continue + try: + text = raw_line.decode("utf-8", errors="replace") + except Exception: + text = str(raw_line) + with self.msg_lock: + self.msg_queue.append(text) + else: + time.sleep(0.002) + if buffer: + try: + text = buffer.rstrip(b"\r\n").decode("utf-8", errors="replace") + except Exception: + text = str(buffer) + with self.msg_lock: + self.msg_queue.append(text) + + +class JLinkGDBWrapper: + SIZE_MAP = {"b": 1, "h": 2, "w": 4, "g": 8} + + def __init__(self, link, session=None) -> None: + self.link = link + self.session = session + + def execute(self, command: str): + if not command: + raise ValueError("Empty command") + stripped = command.strip() + op = stripped.split(None, 1)[0].lower() + if op.startswith("x"): + return True, self._handle_mem_read(stripped) + if op == "set": + return True, self._handle_mem_write(stripped) + if op in ("halt", "h"): + return True, self._handle_halt() + if op in ("go", "g", "resume", "run"): + return True, self._handle_go() + if op in ("reset", "r"): + return True, self._handle_reset(False) + if op in ("reset_halt", "rh"): + return True, self._handle_reset(True) + if op in ("b", "break"): + return True, self._handle_break_set(stripped) + if op in ("bc", "breakclear", "clearbreak", "deletebreak", "bd"): + return True, self._handle_break_clear(stripped) + if op in ("s", "si", "step", "stepi"): + return True, self._handle_step_instruction() + if op in ("next", "n", "ni"): + return True, self._handle_step_over() + if op in ("finish", "fin", "so"): + return True, self._handle_step_out() + if stripped.lower() in ("i r", "ir", "info r", "info reg", "info regs", "info registers"): + return True, self._handle_info_registers() + return False, self.link.exec_command(stripped) + + def _handle_mem_read(self, command: str): + token = command[1:].strip() + fmt = "" + expr = token + if token.startswith("/"): + token = token[1:] + spec = [] + while token and not token[0].isspace(): + spec.append(token[0]) + token = token[1:] + fmt = "".join(spec).lower() + expr = token.strip() + if not expr: + raise ValueError("Address is required for 'x' command") + count = 1 + size_char = "w" + if fmt: + match = re.match(r"(?P\d+)?(?P[bhwg]?)$", fmt) + if not match: + raise ValueError("Invalid x/ specifier") + if match.group("count"): + count = max(1, int(match.group("count"))) + if match.group("size"): + size_char = match.group("size") + count = min(count, 256) + width = self.SIZE_MAP.get(size_char, 4) + address = self._parse_int(expr) + return self._read_memory(address, width, count) + + def _handle_mem_write(self, command: str): + match = re.match(r"set\s+\*(?P[^=]+)=(?P.+)$", command, re.IGNORECASE) + if not match: + raise ValueError("Use 'set *ADDR=VALUE' for writes") + address = self._parse_int(match.group("addr")) + value = self._parse_int(match.group("value")) + self._write_memory(address, value, 4) + return f"Wrote 0x{value & 0xFFFFFFFF:08x} to {address:#010x}" + + def _handle_halt(self): + func = getattr(self.link, "halt", None) + if not func: + raise RuntimeError("halt() is not supported by this J-Link binding") + func() + return self._pc_report_line("Core halted.") + + def _handle_go(self): + attempts = [ + getattr(self.link, "go", None), + getattr(self.link, "run", None), + getattr(self.link, "restart", None), + ] + last_error = None + for func in attempts: + if not callable(func): + continue + try: + func() + return "Execution resumed." + except Exception as exc: + last_error = exc + raw = getattr(self.link, "exec_command", None) + if raw: + for cmd in ("g", "go", "Run"): + try: + result = raw(cmd) + if result in (None, 0, "0", "OK"): + return "Execution resumed." + return str(result) + except Exception as exc: + last_error = exc + raise RuntimeError(f"Resume is not supported ({last_error})") + + def _handle_reset(self, halt: bool): + func = getattr(self.link, "reset", None) + if not func: + raise RuntimeError("reset() is not supported by this J-Link binding") + try: + if halt: + func(True) + else: + func(False) + except TypeError: + try: + if halt: + func(halt=True) + else: + func() + except TypeError: + func() + return "Reset issued." if not halt else "Reset and halted." + + def _handle_break_set(self, command: str): + parts = command.split(None, 1) + if len(parts) < 2: + raise ValueError("Use 'b
' to set a breakpoint.") + addr = self._parse_int(parts[1]) + return self.install_breakpoint(addr) + + def _handle_break_clear(self, command: str): + parts = command.split(None, 1) + if len(parts) < 2: + raise ValueError("Use 'bc
' to clear a breakpoint.") + addr = self._parse_int(parts[1]) + return self.remove_breakpoint(addr) + + def _handle_step_instruction(self): + self._single_step_no_report() + return self._pc_report_line("Single step executed.") + + def _handle_step_over(self): + over = getattr(self.link, "step_over", None) + if over: + over() + return self._pc_report_line("Step over executed.") + raw = getattr(self.link, "exec_command", None) + if raw: + for cmd in ("StepOver", "so", "n", "Next"): + try: + raw(cmd) + return self._pc_report_line("Step over executed.") + except Exception: + continue + return self._software_step_over() + + def _handle_step_out(self): + fin = getattr(self.link, "step_out", None) + if fin: + fin() + return self._pc_report_line("Step out executed.") + raw = getattr(self.link, "exec_command", None) + if raw: + for cmd in ("StepOut", "finish", "Finish"): + try: + raw(cmd) + return self._pc_report_line("Step out executed.") + except Exception: + continue + return self._software_step_out() + + def install_breakpoint(self, addr: int): + handle = self._set_breakpoint_api(addr) + if handle is None: + handle = self._set_breakpoint_exec(addr) + if handle is None: + raise RuntimeError("Breakpoint could not be set on this target.") + self._record_breakpoint(addr, handle) + return f"Breakpoint set at {addr:#010x}" + + def remove_breakpoint(self, addr: int): + normalized = self._normalize_addr(addr) + handle = self._resolve_breakpoint_handle(normalized) + cleared = False + if handle is not None: + cleared = self._clear_breakpoint_api(handle) + if not cleared: + cleared = self._clear_breakpoint_exec(normalized) + if not cleared: + raise RuntimeError("Unable to clear breakpoint; no matching breakpoint found.") + self._forget_breakpoint(normalized) + return f"Breakpoint cleared at {normalized:#010x}" + + def reinstall_breakpoints(self): + if not self.session or not self.session.breakpoints: + return + addresses = list(self.session.breakpoints.keys()) + self.session.breakpoints.clear() + for addr in addresses: + try: + self.install_breakpoint(addr) + except Exception as exc: + self.session.queue_message(f"[bp] Unable to restore breakpoint at {addr:#010x}: {exc}") + + def _set_breakpoint_api(self, addr: int): + last_error = None + for name in ("breakpoint_set", "software_breakpoint_set", "hardware_breakpoint_set"): + setter = getattr(self.link, name, None) + if not callable(setter): + continue + try: + handle = setter(addr) + if handle: + return handle + except Exception as exc: + last_error = exc + return None + + def _set_breakpoint_exec(self, addr: int): + exec_cmd = getattr(self.link, "exec_command", None) + if not exec_cmd: + return None + commands = [ + f"SetBP {addr:#x},0x1", + f"SetBP {addr:#x}", + f"SetBPAddr = {addr:#x}", + f"SetBPAddr {addr:#x}", + f"SetBPAddr,{addr:#x}", + f"BreakSet {addr:#x}", + f"BP {addr:#x}", + f"Break.Set {addr:#x}", + ] + last_error = None + for cmd in commands: + try: + result = exec_cmd(cmd) + if result not in (None, 0, "0", "OK"): + last_error = result + continue + handle = self._find_breakpoint_handle(addr) + return handle if handle else None + except Exception as exc: + last_error = exc + if last_error: + raise RuntimeError(f"Breakpoint command failed: {last_error}") + return None + + def _clear_breakpoint_api(self, handle) -> bool: + clearer = getattr(self.link, "breakpoint_clear", None) + if not callable(clearer): + return False + try: + return bool(clearer(handle)) + except Exception: + return False + + def _clear_breakpoint_exec(self, addr: int) -> bool: + exec_cmd = getattr(self.link, "exec_command", None) + if not exec_cmd: + return False + commands = [ + f"ClrBP {addr:#x}", + f"ClrBP {addr:#x},0x1", + f"ClearBP {addr:#x}", + f"ClrBPAddr = {addr:#x}", + f"ClrBPAddr {addr:#x}", + f"BreakClr {addr:#x}", + f"Break.Clr {addr:#x}", + ] + last_error = None + for cmd in commands: + try: + result = exec_cmd(cmd) + if result in (None, 0, "0", "OK"): + return True + last_error = result + except Exception as exc: + last_error = exc + if last_error: + raise RuntimeError(f"Breakpoint clear failed: {last_error}") + return False + + def _suspend_breakpoints(self, addresses): + if not self.session: + return [] + suspended = [] + seen = set() + for addr in addresses: + if addr is None: + continue + normalized = self._normalize_addr(addr) + if normalized in seen: + continue + seen.add(normalized) + if not self.session.has_breakpoint(normalized): + continue + handle = self.session.get_breakpoint_handle(normalized) + cleared = False + if handle is not None: + cleared = self._clear_breakpoint_api(handle) + if not cleared: + cleared = self._clear_breakpoint_exec(normalized) + if cleared: + self.session.unregister_breakpoint(normalized) + suspended.append(normalized) + return suspended + + def _restore_breakpoints(self, addresses): + if not self.session: + return + for addr in {self._normalize_addr(a) for a in addresses if a is not None}: + if addr is None: + continue + try: + handle = self._set_breakpoint_api(addr) + if handle is None: + handle = self._set_breakpoint_exec(addr) + if handle is None: + raise RuntimeError("Unable to restore") + self.session.register_breakpoint(addr, handle) + except Exception as exc: + self.session.queue_message(f"[bp] Unable to restore breakpoint at {addr:#010x}: {exc}") + + def _record_breakpoint(self, addr: int, handle): + if not self.session: + return + self.session.register_breakpoint(addr, handle) + + def _forget_breakpoint(self, addr: int): + if not self.session: + return + self.session.unregister_breakpoint(addr) + + def _resolve_breakpoint_handle(self, addr: int): + if self.session: + cached = self.session.get_breakpoint_handle(addr) + if cached is not None: + return cached + finder = getattr(self.link, "breakpoint_find", None) + if not callable(finder): + return None + try: + handle = finder(addr) + return handle if handle else None + except Exception: + return None + + def _find_breakpoint_handle(self, addr: int): + finder = getattr(self.link, "breakpoint_find", None) + if not callable(finder): + return None + try: + return finder(addr) + except Exception: + return None + + def _normalize_addr(self, addr: int) -> int: + return int(addr) & 0xFFFFFFFF + + def _pc_report_line(self, prefix: str): + value = self._read_pc() + if value is None: + return [prefix] + if self.session: + self.session.record_pc_report() + return [f"{prefix} — PC = 0x{value:08x}"] + + def _read_pc(self): + getter = getattr(self.link, "register_read", None) + if not callable(getter): + return None + for key in ("PC", "pc", 15): + try: + value = getter(key) + if value is not None: + return int(value) & 0xFFFFFFFF + except Exception: + continue + return None + + def _read_register(self, name): + getter = getattr(self.link, "register_read", None) + if not callable(getter): + return None + for candidate in self._register_candidates(name): + try: + value = getter(candidate) + except Exception: + continue + if value is None: + continue + try: + return int(value) & 0xFFFFFFFF + except Exception: + continue + return None + + def _register_candidates(self, name): + mapping = { + "pc": ["PC", "pc", "R15", 15], + "lr": ["LR", "lr", "R14", 14], + "sp": ["SP", "sp", "R13", 13], + "fp": ["FP", "fp", "R11", 11], + } + if isinstance(name, str): + key = name.lower() + if key in mapping: + return mapping[key] + variants = [name, name.lower(), name.upper()] + if name.upper().startswith("R"): + try: + idx = int(name[1:]) + variants.append(idx) + except Exception: + pass + return variants + return [name] + + def _software_step_over(self): + current_pc = self._read_pc() + if current_pc is None: + raise RuntimeError("Unable to read PC.") + next_addr = self._next_instruction_address(current_pc) + suspended = self._suspend_breakpoints([current_pc, next_addr]) + handle = None + via_api = False + bp_installed = False + lr_before = self._read_register("LR") + try: + stepped_pc = self._single_step_no_report() + if stepped_pc is not None and self._pc_matches_target(stepped_pc, next_addr): + return self._pc_report_line("Step over executed.") + lr_after = self._read_register("LR") + if not self._stepped_into_subroutine(lr_before, lr_after, next_addr): + return self._pc_report_line("Step over executed.") + handle, via_api = self._set_temporary_breakpoint(next_addr) + bp_installed = True + self._resume_cpu() + if not self._wait_for_halt(expected_pc=next_addr): + raise RuntimeError("Step over timed out; target did not halt.") + return self._pc_report_line("Step over executed.") + finally: + if bp_installed: + self._clear_temporary_breakpoint(handle, next_addr, via_api) + self._restore_breakpoints(suspended) + + def _software_step_out(self): + lr = self._read_register("LR") + if lr is None: + sp = self._read_register("SP") + lr = self._read_stack_word(sp) if sp is not None else None + if lr is None: + raise RuntimeError("Unable to determine LR for step out.") + target = lr & ~1 + if target == 0: + raise RuntimeError("LR is zero; cannot determine return address.") + current_pc = self._read_pc() + suspended = self._suspend_breakpoints([current_pc, target]) + handle, via_api = self._set_temporary_breakpoint(target) + try: + self._resume_cpu() + if not self._wait_for_halt(expected_pc=target): + raise RuntimeError("Step out timed out; target did not halt.") + return self._pc_report_line("Step out executed.") + finally: + self._clear_temporary_breakpoint(handle, target, via_api) + self._restore_breakpoints(suspended) + + def _next_instruction_address(self, pc=None): + if pc is None: + pc = self._read_pc() + if pc is None: + raise RuntimeError("Unable to read PC.") + current = pc & ~1 + size = self._estimate_thumb_instruction_size(current) + return (current + size) & 0xFFFFFFFF + + def _estimate_thumb_instruction_size(self, addr: int) -> int: + opcode = self._read_halfword(addr) + if opcode is None: + return 2 + prefix = (opcode >> 11) & 0x1F + if prefix in (0b11101, 0b11110, 0b11111): + return 4 + return 2 + + def _read_halfword(self, addr: int): + reader = getattr(self.link, "memory_read16", None) + if not callable(reader): + return None + base = addr & ~1 + try: + values = reader(base, 1) + if values: + return values[0] & 0xFFFF + except Exception: + return None + return None + + def _read_stack_word(self, sp): + reader = getattr(self.link, "memory_read32", None) + if not callable(reader) or sp is None: + return None + base = sp & ~3 + try: + values = reader(base, 1) + if values: + return values[0] & 0xFFFFFFFF + except Exception: + return None + return None + + def _set_temporary_breakpoint(self, addr: int): + last_error = None + try: + handle = self._set_breakpoint_api(addr) + if handle is not None: + return handle, True + except Exception as exc: + last_error = exc + try: + handle = self._set_breakpoint_exec(addr) + if handle is not None: + return handle, False + except Exception as exc: + last_error = exc + raise RuntimeError(f"Unable to place temporary breakpoint: {last_error}") + + def _clear_temporary_breakpoint(self, handle, addr: int, via_api: bool): + try: + if via_api and handle: + self._clear_breakpoint_api(handle) + else: + self._clear_breakpoint_exec(addr) + except Exception: + pass + + def _resume_cpu(self): + attempts = [ + getattr(self.link, "go", None), + getattr(self.link, "run", None), + getattr(self.link, "restart", None), + ] + last_error = None + for func in attempts: + if not callable(func): + continue + try: + func() + return + except Exception as exc: + last_error = exc + raw = getattr(self.link, "exec_command", None) + if raw: + for cmd in ("g", "go", "Run"): + try: + raw(cmd) + return + except Exception as exc: + last_error = exc + raise RuntimeError(f"Resume is not supported ({last_error})") + + def _wait_for_halt(self, timeout: float = 5.0, expected_pc: Optional[int] = None) -> bool: + deadline = time.time() + timeout + target = self._normalize_addr(expected_pc) if expected_pc is not None else None + while time.time() < deadline: + try: + if self.link.halted(): + return True + except Exception: + pass + if target is not None: + pc = self._read_pc() + if pc is not None and self._pc_matches_target(pc, target): + # Some J-Link bindings don't report a halted state even when + # a breakpoint is hit, so fall back to checking the PC. + return True + time.sleep(0.01) + + if target is not None: + pc = self._read_pc() + if pc is not None and self._pc_matches_target(pc, target): + return True + return False + + def _pc_matches_target(self, pc: int, target: int) -> bool: + normalized_pc = self._normalize_addr(pc) + normalized_target = self._normalize_addr(target) + if normalized_pc == normalized_target: + return True + return (normalized_pc & ~1) == (normalized_target & ~1) + + def _stepped_into_subroutine(self, lr_before, lr_after, next_addr: int) -> bool: + if lr_after is None: + return True + if not self._pc_matches_target(lr_after, next_addr): + return False + if lr_before is None: + return True + return not self._pc_matches_target(lr_before, next_addr) + + def _single_step_no_report(self): + step = getattr(self.link, "step", None) + if step: + step() + return self._read_pc() + raw = getattr(self.link, "exec_command", None) + if raw: + raw("Step") + return self._read_pc() + raise RuntimeError("Single-step is not supported by this J-Link binding.") + + def _handle_info_registers(self): + entries = self._gather_registers() + if not entries: + raise RuntimeError("Unable to read registers.") + width = max(len(name) for name, _ in entries) + lines = ["Registers:"] + for name, value in entries: + lines.append(f"{name:>{width}}: 0x{value:08x}") + if self.session: + self.session.record_pc_report() + return lines + + def _gather_registers(self): + getter = getattr(self.link, "register_read", None) + if not callable(getter): + return [] + register_entries = [] + reg_list = [] + list_func = getattr(self.link, "register_list", None) + name_func = getattr(self.link, "register_name", None) + if callable(list_func) and callable(name_func): + try: + indices = list_func() + for idx in indices: + try: + name = name_func(idx) + except Exception: + continue + reg_list.append((name, idx)) + except Exception: + reg_list = [] + if not reg_list: + ordered = [ + "PC", + "LR", + "SP", + "FP", + "R12", + "R11", + "R10", + "R9", + "R8", + "R7", + "R6", + "R5", + "R4", + "R3", + "R2", + "R1", + "R0", + "xPSR", + ] + reg_list = [(name, name) for name in ordered] + for name, key in reg_list: + try: + value = getter(key) + except Exception: + continue + if value is None: + continue + text = name.decode("utf-8", errors="ignore") if isinstance(name, bytes) else str(name) + register_entries.append((text.strip(), int(value) & 0xFFFFFFFF)) + unique = [] + seen = set() + for name, value in register_entries: + if name in seen: + continue + seen.add(name) + unique.append((name, value)) + return unique + + + def _parse_int(self, text: str) -> int: + try: + return int(text.strip(), 0) + except Exception: + raise ValueError(f"Unable to parse integer value '{text}'") + + def _read_memory(self, address: int, width: int, count: int): + bits = width * 8 + func = getattr(self.link, f"memory_read{bits}", None) + if not func: + raise RuntimeError(f"memory_read{bits} not supported") + values = func(address, count) or [] + mask = (1 << bits) - 1 + lines = [] + for idx, raw in enumerate(values): + value = raw & mask + addr = address + idx * width + lines.append(f"{addr:#010x}: 0x{value:0{width * 2}x}") + if not lines: + lines.append(f"No data returned when reading {count} value(s) at {address:#010x}.") + return lines + + def _write_memory(self, address: int, value: int, width: int) -> None: + bits = width * 8 + func = getattr(self.link, f"memory_write{bits}", None) + if not func: + raise RuntimeError(f"memory_write{bits} not supported") + mask = (1 << bits) - 1 + func(address, [value & mask]) + + +class DebuggerSession: + def __init__(self, notebook, panel, ap_info): + self.panel = panel + self.ap_info = dict(ap_info) + self.label = self.ap_info.get("label", "AP") + self.core = self.ap_info.get("core", "Unknown") + self.frame = ttk.Frame(notebook) + self.status_var = tk.StringVar(value="Disconnected") + self.script_path_var = tk.StringVar() + + self.log_text = None + self.history_listbox = None + self.command_entry = None + self.command_entry_var = None + self.send_button = None + self.breakpoint_button = None + self.connect_button = None + self.status_label = None + + self.msg_queue = deque() + self.msg_lock = threading.Lock() + self.log_line_count = 0 + self.command_history = [] + self.history_index = 0 + + self.command_wrapper = None + self.connected = False + self._script_path = None + self.breakpoints: dict[int, Optional[int]] = {} + self._last_pc_report = 0.0 + + self._build_ui() + self.enable_controls(False) + + def _build_ui(self): + header = ttk.Frame(self.frame) + header.pack(fill="x", padx=5, pady=5) + ttk.Label(header, text=f"{self.label} ({self.core})").grid(row=0, column=0, sticky="w") + self.connect_button = ttk.Button(header, text="Connect", width=12, command=self.toggle_connection) + self.connect_button.grid(row=0, column=1, sticky="e", padx=(8, 0)) + self.status_label = ttk.Label(header, textvariable=self.status_var, foreground="red") + self.status_label.grid(row=1, column=0, columnspan=2, sticky="w", pady=(4, 0)) + + script_frame = ttk.Frame(self.frame) + script_frame.pack(fill="x", padx=5, pady=(0, 5)) + ttk.Label(script_frame, text="Script:").grid(row=0, column=0, sticky="w") + self.script_entry = ttk.Entry(script_frame, textvariable=self.script_path_var) + self.script_entry.grid(row=0, column=1, sticky="ew", padx=3) + ttk.Button(script_frame, text="Browse...", command=self.browse_script_file).grid(row=0, column=2, padx=(0, 5)) + ttk.Button(script_frame, text="Run Script", command=self.apply_script_file).grid(row=0, column=3) + script_frame.columnconfigure(1, weight=1) + + console_frame = ttk.Frame(self.frame) + console_frame.pack(fill="both", expand=True, padx=5, pady=5) + console_frame.columnconfigure(0, weight=1) + console_frame.rowconfigure(0, weight=1) + + self.log_text = tk.Text(console_frame, height=10, wrap="none") + self.log_text.grid(row=0, column=0, sticky="nsew") + self.log_text.configure(state="disabled") + + vscroll = ttk.Scrollbar(console_frame, orient="vertical", command=self.log_text.yview) + vscroll.grid(row=0, column=1, sticky="ns") + self.log_text.configure(yscrollcommand=vscroll.set) + + hscroll = ttk.Scrollbar(console_frame, orient="horizontal", command=self.log_text.xview) + hscroll.grid(row=1, column=0, sticky="ew") + self.log_text.configure(xscrollcommand=hscroll.set) + + button_row = ttk.Frame(console_frame) + button_row.grid(row=2, column=0, columnspan=2, sticky="ew", pady=(5, 0)) + button_row.columnconfigure(0, weight=1) + buttons = ttk.Frame(button_row) + buttons.grid(row=0, column=0, sticky="w") + ttk.Button(buttons, text="Clear Console", command=self.clear_console, width=14).grid(row=0, column=0, padx=(0, 5)) + ttk.Button(buttons, text="Save Log", command=self.save_console, width=14).grid(row=0, column=1) + self.breakpoint_button = ttk.Button(buttons, text="Breakpoints", command=self.show_breakpoints, width=14) + self.breakpoint_button.grid(row=0, column=2, padx=(5, 0)) + + history_frame = ttk.Frame(console_frame) + history_frame.grid(row=3, column=0, sticky="nsew", pady=(5, 0)) + history_frame.columnconfigure(0, weight=1) + history_frame.rowconfigure(1, weight=1) + ttk.Label(history_frame, text="Command history:").grid(row=0, column=0, sticky="w") + self.history_listbox = tk.Listbox(history_frame, height=6) + self.history_listbox.grid(row=1, column=0, sticky="nsew") + self.history_listbox.bind("", self.history_select) + history_scroll = ttk.Scrollbar(history_frame, orient="vertical", command=self.history_listbox.yview) + history_scroll.grid(row=1, column=1, sticky="ns") + self.history_listbox.configure(yscrollcommand=history_scroll.set) + + input_frame = ttk.Frame(console_frame) + input_frame.grid(row=4, column=0, sticky="ew", pady=(5, 0)) + input_frame.columnconfigure(0, weight=1) + self.command_entry_var = tk.StringVar() + self.command_entry = ttk.Entry(input_frame, textvariable=self.command_entry_var) + self.command_entry.grid(row=0, column=0, sticky="ew", padx=(0, 5)) + self.command_entry.bind("", lambda _evt: self.send_command()) + self.command_entry.bind("", self._history_prev) + self.command_entry.bind("", self._history_next) + self.send_button = ttk.Button(input_frame, text="Send", command=self.send_command) + self.send_button.grid(row=0, column=1) + + def set_unavailable(self, reason: str): + self._set_status(reason, "red") + if self.connect_button: + self.connect_button.configure(state="disabled") + self.enable_controls(False) + + def ensure_script_file(self) -> str: + if self._script_path: + return self._script_path + script = self.ap_info.get("script") + if not script: + return "" + fd, path = tempfile.mkstemp(prefix=f"jlink_{self.label.lower()}_", suffix=".jlink") + with os.fdopen(fd, "w", encoding="utf-8") as handle: + handle.write(script) + self._script_path = path + return path + + def cleanup_script(self): + if not self._script_path: + return + try: + os.unlink(self._script_path) + except Exception: + pass + self._script_path = None + + def enable_controls(self, enabled: bool): + state = "normal" if enabled else "disabled" + if self.command_entry: + self.command_entry.configure(state=state) + if self.send_button: + self.send_button.configure(state=state) + if self.breakpoint_button: + self.breakpoint_button.configure(state=state) + + def queue_message(self, text: str): + if not text: + return + with self.msg_lock: + self.msg_queue.append(str(text)) + + def poll_queue(self): + with self.msg_lock: + if not self.msg_queue: + return + entries = list(self.msg_queue) + self.msg_queue.clear() + self._log_entries(entries) + + def _log_entries(self, entries): + if not self.log_text: + return + prepared = [] + for entry in entries: + trimmed = str(entry).strip() + if not trimmed or trimmed.strip(".") == "": + continue + prepared.append(f"[{timestamp()}] {trimmed}\n") + if not prepared: + return + self.log_line_count += len(prepared) + block = "".join(prepared) + self.log_text.configure(state="normal") + self.log_text.insert("end", block) + if LOG_HISTORY_LIMIT and self.log_line_count > LOG_HISTORY_LIMIT: + excess = self.log_line_count - LOG_HISTORY_LIMIT + self.log_text.delete("1.0", f"{excess + 1}.0") + self.log_line_count = LOG_HISTORY_LIMIT + self.log_text.see("end") + self.log_text.configure(state="disabled") + + def clear_console(self): + if not self.log_text: + return + with self.msg_lock: + self.msg_queue.clear() + self.log_text.configure(state="normal") + self.log_text.delete("1.0", "end") + self.log_text.configure(state="disabled") + self.log_line_count = 0 + + def save_console(self): + if not self.log_text: + return + try: + contents = self.log_text.get("1.0", "end-1c") + except tk.TclError: + contents = "" + if not contents.strip(): + messagebox.showinfo("Save Console Log", "There is no console output to save.") + return + path = filedialog.asksaveasfilename( + title="Save Console Log", + defaultextension=".txt", + filetypes=(("Text Files", "*.txt"), ("All Files", "*.*")), + ) + if not path: + return + try: + with open(path, "w", encoding="utf-8") as handle: + handle.write(contents) + except Exception as exc: + messagebox.showerror("Save Failed", f"Unable to save log: {exc}") + + def show_breakpoints(self): + if not self.breakpoints: + self.queue_message("[bp] No breakpoints set.") + return + entries = sorted(self.breakpoints.items()) + lines = ["[bp] Active breakpoints:"] + for addr, handle in entries: + if handle is not None: + lines.append(f"[bp] {addr:#010x} (handle {handle})") + else: + lines.append(f"[bp] {addr:#010x}") + for line in lines: + self.queue_message(line) + + def toggle_connection(self): + if self.connected: + self.panel.detach_session(self) + return + self.panel.attach_session(self) + + def mark_connected(self, serial_no: int): + self.connected = True + self._set_status(f"Connected — SN {serial_no}", "green") + if self.connect_button: + self.connect_button.configure(text="Disconnect", state="normal") + self.enable_controls(True) + + def mark_disconnected(self): + self.connected = False + self.command_wrapper = None + self._set_status("Disconnected", "red") + if self.connect_button: + self.connect_button.configure(text="Connect", state="normal") + self.enable_controls(False) + + def browse_script_file(self): + path = filedialog.askopenfilename( + title="Select Script", + filetypes=(("Scripts", "*.jlink *.jscr *.jsf *.gdb *.gdbinit *.txt"), ("All Files", "*.*")), + ) + if not path: + return + self.script_path_var.set(path) + self._process_script_file(path, run_now=False, show_dialog=False) + + def apply_script_file(self): + path = (self.script_path_var.get() or "").strip() + if not path: + messagebox.showinfo("Script File", "Enter or select a script file first.") + return + self._process_script_file(path, run_now=True, show_dialog=False) + + def _process_script_file(self, path, run_now, show_dialog): + normalized = os.path.abspath(path) + if not os.path.isfile(normalized): + msg = f"Script file not found: {normalized}" + self.queue_message(f"[script] {msg}") + if show_dialog: + messagebox.showerror("Script File Not Found", msg) + return False + self.script_path_var.set(normalized) + script_type = self._detect_script_type(normalized) + filename = os.path.basename(normalized) + if script_type == "gdb": + if not self.connected or not self.command_wrapper: + info = f"GDB script '{filename}' stored. Connect and run it manually." + self.queue_message(f"[script] {info}") + if show_dialog: + messagebox.showinfo("GDB Script", info) + return False + if not run_now: + info = f"GDB script '{filename}' ready. Click Run Script to execute." + self.queue_message(f"[script] {info}") + return True + ok, message = self.panel.run_gdb_script(self, normalized) + if not ok: + messagebox.showerror("GDB Script", message) + return ok + if not self.connected: + info = f"Stored J-Link script '{filename}'. It will load after connecting." + self.queue_message(f"[script] {info}") + if show_dialog: + messagebox.showinfo("J-Link Script", info) + return True + ok, message = self.panel.set_shared_script(normalized, session=self) + if not ok and show_dialog: + messagebox.showerror("J-Link Script", message) + return ok + + def _detect_script_type(self, path): + ext = os.path.splitext(path)[1].lower() + if ext in GDB_SCRIPT_EXTS: + return "gdb" + return "jlink" + + def send_command(self): + if not self.command_entry_var: + return + command = self.command_entry_var.get().strip() + self._dispatch_command(command, add_to_history=True) + + def _dispatch_command(self, command, add_to_history: bool): + if not command: + return + if not self.connected: + messagebox.showwarning("Not Connected", "Connect to the debugger before sending commands.") + return + if add_to_history and self.history_listbox: + self.history_listbox.insert("end", command) + if self.history_listbox.size() > HISTORY_LIMIT: + self.history_listbox.delete(0) + if add_to_history: + self.command_history.append(command) + self.history_index = len(self.command_history) + self.command_entry_var.set("") + self.queue_message(f"> {command}") + threading.Thread(target=self.panel.run_session_command, args=(self, command), daemon=True).start() + + def history_select(self, _event): + if not self.history_listbox or not self.command_entry_var: + return + sel = self.history_listbox.curselection() + if not sel: + return + command = self.history_listbox.get(sel[0]) + self.command_entry_var.set(command) + if self.command_entry: + try: + self.command_entry.icursor("end") + except tk.TclError: + pass + self._dispatch_command(command, add_to_history=False) + + def _history_prev(self, _event): + self._navigate_history(-1) + return "break" + + def _history_next(self, _event): + self._navigate_history(1) + return "break" + + def _navigate_history(self, delta: int): + if not self.command_history or not self.command_entry_var: + return + new_index = max(0, min(len(self.command_history), self.history_index + delta)) + self.history_index = new_index + value = "" if new_index == len(self.command_history) else self.command_history[new_index] + self.command_entry_var.set(value) + if self.command_entry: + try: + self.command_entry.icursor("end") + except tk.TclError: + pass + + def _set_status(self, text: str, color: str): + self.status_var.set(text) + if self.status_label: + try: + self.status_label.configure(foreground=color) + except tk.TclError: + pass + + def shutdown(self): + if self.connected: + try: + self.panel.detach_session(self) + except Exception: + pass + self.cleanup_script() + + def _emit_command_result(self, result): + if result is None: + return + if isinstance(result, tuple) and len(result) == 2: + status, response = result + self.queue_message(f"[pylink] Result code: {status}") + if response: + self._emit_response_lines(response) + return + self._emit_response_lines(result) + + def _emit_response_lines(self, response): + text = str(response) + for line in text.splitlines(): + trimmed = line.strip() + if trimmed: + self.queue_message(trimmed) + + def _emit_wrapper_result(self, result): + if result is None: + return + if isinstance(result, (list, tuple)): + for entry in result: + trimmed = str(entry).strip() + if trimmed: + self.queue_message(trimmed) + else: + trimmed = str(result).strip() + if trimmed: + self.queue_message(trimmed) + + def register_breakpoint(self, addr: int, handle): + normalized = self._normalize_breakpoint_addr(addr) + self.breakpoints[normalized] = handle + + def unregister_breakpoint(self, addr: int): + normalized = self._normalize_breakpoint_addr(addr) + self.breakpoints.pop(normalized, None) + + def get_breakpoint_handle(self, addr: int): + return self.breakpoints.get(self._normalize_breakpoint_addr(addr)) + + def list_breakpoints(self): + return list(self.breakpoints.keys()) + + def clear_breakpoints(self): + self.breakpoints.clear() + + def _normalize_breakpoint_addr(self, value: int) -> int: + return int(value) & 0xFFFFFFFF + + def record_pc_report(self): + self._last_pc_report = time.monotonic() + + def should_suppress_pc_report(self) -> bool: + if self._last_pc_report <= 0: + return False + return (time.monotonic() - self._last_pc_report) < 0.3 + + def has_breakpoint(self, addr: int) -> bool: + return self._normalize_breakpoint_addr(addr) in self.breakpoints + + + +class SharedDebuggerController: + def __init__(self, panel): + self.panel = panel + self.link = None + self.serial = None + self.speed = None + self._link_lock = threading.Lock() + self._command_lock = threading.Lock() + self._sessions = set() + self._current_session = None + self._monitor_thread = None + self._monitor_stop = threading.Event() + self._monitor_last_halted = False + + def attach(self, session, serial_no, speed): + with self._link_lock: + if self.link is None: + ok, msg = self._open_link(serial_no, speed) + if not ok: + return False, msg + elif self.serial != serial_no: + return False, f"Debugger already connected to emulator {self.serial}." + self._sessions.add(session) + session.command_wrapper = JLinkGDBWrapper(self.link, session) + ok, msg = self.ensure_session_ready(session) + if not ok: + self.detach(session) + return False, msg + return True, None + + def detach(self, session): + with self._link_lock: + self._sessions.discard(session) + if self._current_session is session: + self._current_session = None + if not self._sessions: + self._close_link() + + def _open_link(self, serial_no, speed): + if not self.panel.is_pylink_available(): + return False, "pylink not available." + try: + link = pylink.JLink() + link.open(serial_no) + interface = self.panel._resolve_swd_interface() + if interface is not None: + link.set_tif(interface) + link.set_speed(speed) + except Exception as exc: + return False, str(exc) + self.link = link + self.serial = serial_no + self.speed = speed + self._start_monitor() + return True, None + + def _close_link(self): + if self.link: + try: + self.link.close() + except Exception: + pass + self.link = None + self.serial = None + self.speed = None + self._current_session = None + self._stop_monitor() + + def ensure_session_ready(self, session): + with self._command_lock: + ok, msg = self._apply_context(session) + return ok, msg + + def execute(self, session, command): + if not self.link: + session.queue_message("[pylink] Shared link is not available.") + return + with self._command_lock: + ok, msg = self._apply_context(session) + if not ok: + if msg: + session.queue_message(f"[pylink] {msg}") + return + try: + handled = False + result = None + if session.command_wrapper: + handled, result = session.command_wrapper.execute(command) + else: + result = self.link.exec_command(command) + if handled: + session._emit_wrapper_result(result) + else: + session._emit_command_result(result) + except Exception as exc: + session.queue_message(f"[pylink] Command failed: {exc}") + + def run_gdb_script(self, session, path): + if not self.link: + return False, "Debugger not connected." + try: + with open(path, "r", encoding="utf-8") as handle: + lines = handle.readlines() + except Exception as exc: + msg = f"Unable to read script '{path}': {exc}" + session.queue_message(f"[script] {msg}") + return False, msg + commands = [] + for line in lines: + stripped = line.strip() + if not stripped or stripped.startswith("#"): + continue + if stripped.lower().startswith("monitor "): + stripped = stripped[8:].strip() + commands.append(stripped) + if not commands: + msg = f"No commands found in '{os.path.basename(path)}'." + session.queue_message(f"[script] {msg}") + return True, msg + with self._command_lock: + ok, ctx_msg = self._apply_context(session) + if not ok: + return False, ctx_msg or "Unable to configure debugger context." + for command in commands: + session.queue_message(f"[script] gdb> {command}") + try: + if session.command_wrapper: + handled, result = session.command_wrapper.execute(command) + else: + handled = False + result = self.link.exec_command(command) + except Exception as exc: + msg = f"Command '{command}' failed: {exc}" + session.queue_message(f"[script] {msg}") + return False, msg + if handled: + session._emit_wrapper_result(result) + else: + session._emit_command_result(result) + msg = f"Executed {len(commands)} command(s) from '{os.path.basename(path)}'." + session.queue_message(f"[script] {msg}") + return True, msg + + def set_script(self, path): + if not self.link: + return False, "Debugger not connected." + try: + self.link.set_script_file(path) + except Exception as exc: + msg = f"Unable to load J-Link script '{path}': {exc}" + return False, msg + self._current_session = None + return True, f"Loaded J-Link script '{os.path.basename(path)}'." + + def _apply_context(self, session): + if not self.link: + return False, "J-Link is not connected." + if self._current_session is session: + return True, None + reinitialize = self._current_session is not None + ok, msg = self._configure_link(session, reinitialize) + if not ok: + return False, msg + self._current_session = session + if session.command_wrapper: + session.command_wrapper.reinstall_breakpoints() + return True, None + + def _configure_link(self, session, reinitialize: bool): + if reinitialize: + serial = self.serial + speed = self.speed + if serial is None or speed is None: + return False, "Debugger is not initialized." + try: + if self.link: + self.link.close() + except Exception: + pass + self.link = None + ok, msg = self._open_link(serial, speed) + if not ok: + return False, msg or "Unable to reopen J-Link." + self._refresh_wrappers() + else: + try: + self.link.disconnect() + except Exception: + pass + script_path = session.ensure_script_file() + if script_path: + try: + self.link.set_script_file(script_path) + except Exception as exc: + return False, f"Unable to apply script: {exc}" + targets = session.ap_info.get("targets") or [session.core] + target = targets[0] + try: + try: + self.link.connect(target, speed=self.speed) + except TypeError: + self.link.connect(chip_name=target, speed=self.speed) + except Exception as exc: + return False, f"Unable to connect to {target}: {exc}" + return True, None + + def _refresh_wrappers(self): + if not self.link: + return + for sess in list(self._sessions): + if sess.connected: + sess.command_wrapper = JLinkGDBWrapper(self.link, sess) + + def _start_monitor(self): + if self._monitor_thread and self._monitor_thread.is_alive(): + return + self._monitor_stop.clear() + self._monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True) + self._monitor_thread.start() + + def _stop_monitor(self): + self._monitor_stop.set() + thread = self._monitor_thread + if thread and thread.is_alive(): + thread.join(timeout=0.5) + self._monitor_thread = None + self._monitor_last_halted = False + + def _monitor_loop(self): + while not self._monitor_stop.is_set(): + if not self.link: + if self._monitor_stop.wait(0.2): + break + continue + with self._command_lock: + halted = False + try: + halted = bool(self.link.halted()) + except Exception: + halted = False + if halted and not self._monitor_last_halted: + self._report_monitor_pc() + self._monitor_last_halted = halted + if self._monitor_stop.wait(0.2): + break + + def _report_monitor_pc(self): + session = self._current_session + if not session or session.should_suppress_pc_report(): + return + pc = self._read_pc_locked() + if pc is None: + session.queue_message("[pc] CPU halted.") + else: + session.queue_message(f"[pc] Halted at 0x{pc:08x}") + session.record_pc_report() + + def _read_pc_locked(self): + if not self.link: + return None + getter = getattr(self.link, "register_read", None) + if not callable(getter): + return None + for key in ("PC", "pc", 15): + try: + value = getter(key) + if value is not None: + return int(value) & 0xFFFFFFFF + except Exception: + continue + return None + + +class PylinkDebuggerPanel: + def __init__(self, parent, dll_path=None): + self.parent = parent + self.dll_path = dll_path if dll_path and os.path.exists(dll_path) else None + self.frame = ttk.LabelFrame(parent, text="Pylink Debugger") + self.device_var = tk.StringVar() + self.speed_var = tk.StringVar(value="4000") + self.status_var = tk.StringVar(value="") + + self.device_combo = None + self.status_label = None + self.notebook = None + + self.sessions: List[DebuggerSession] = [] + self._device_display = {} + self._device_refresh_after = None + self._device_error_reported = False + self._pylink_available = pylink is not None + + self.controller = SharedDebuggerController(self) + self.ap_definitions = self._build_ap_definitions() + self._build_widgets() + self.refresh_devices() + self._schedule_device_refresh() + + if not self._pylink_available: + self._set_status("pylink not installed", "red") + self.queue_message("pylink module is missing; install 'pylink-square' to use the debugger.") + for session in self.sessions: + session.set_unavailable("pylink not installed") + elif not self.is_dll_available(): + self._set_status("J-Link DLL missing", "orange") + self.queue_message("pylink/JLinkARM.dll not found; copy the DLL into the 'pylink' folder.") + for session in self.sessions: + session.set_unavailable("J-Link DLL missing") + + def _build_widgets(self): + conn_frame = ttk.Frame(self.frame) + conn_frame.pack(fill="x", padx=5, pady=5) + conn_frame.columnconfigure(1, weight=1) + + ttk.Label(conn_frame, text="J-Link:").grid(row=0, column=0, sticky="w") + self.device_combo = ttk.Combobox(conn_frame, textvariable=self.device_var, width=45, state="readonly") + self.device_combo.grid(row=0, column=1, sticky="ew", padx=3) + ttk.Button(conn_frame, text="Help", command=self.show_help).grid(row=0, column=2, padx=(3, 0)) + + ttk.Label(conn_frame, text="Speed (kHz):").grid(row=1, column=0, sticky="w", pady=(6, 0)) + ttk.Entry(conn_frame, textvariable=self.speed_var, width=10).grid(row=1, column=1, sticky="w", padx=3, pady=(6, 0)) + + self.status_label = ttk.Label(conn_frame, textvariable=self.status_var, foreground="red") + self.status_label.grid(row=2, column=0, columnspan=2, sticky="w", pady=(6, 0)) + + self.notebook = ttk.Notebook(self.frame) + self.notebook.pack(fill="both", expand=True, padx=5, pady=(0, 5)) + for ap in self.ap_definitions: + session = DebuggerSession(self.notebook, self, ap) + self.sessions.append(session) + self.notebook.add(session.frame, text=self._format_session_title(ap)) + + def _format_session_title(self, ap_def: dict) -> str: + label = ap_def.get("label", "AP") + core = ap_def.get("core", "Unknown") + return f"{label} - {core}" + + def _build_ap_definitions(self) -> List[dict]: + entries = [ + {"label": "NP", "core": "Cortex-M33", "ap_index": 0, "targets": ["Cortex-M33"], "extras": []}, + {"label": "MP", "core": "Cortex-M33", "ap_index": 1, "targets": ["Cortex-M33"], "extras": []}, + {"label": "FP", "core": "Cortex-M23", "ap_index": 2, "targets": ["Cortex-M23"], "extras": []}, + { + "label": "CA32", + "core": "Cortex-A32", + "ap_index": 3, + "targets": ["Cortex-A32"], + "extras": [ + " CORESIGHT_CoreBaseAddr = 0x80210000;", + ' JLINK_ExecCommand("CORESIGHT_SetCSCTICoreBaseAddr = 0x80218000");', + ], + }, + ] + results = [] + for ap in entries: + entry = dict(ap) + entry["script"] = self._build_ap_script(entry) + results.append(entry) + return results + + def _build_ap_script(self, ap_info: dict) -> str: + cpu = self._cpu_constant(ap_info.get("core", "Cortex-M33")) + ap_index = ap_info.get("ap_index", 0) + extras = ap_info.get("extras") or [] + lines = [ + "int InitTarget(void) {", + " JLINK_CORESIGHT_AddAP(0, CORESIGHT_AHB_AP);", + " JLINK_CORESIGHT_AddAP(1, CORESIGHT_AHB_AP);", + " JLINK_CORESIGHT_AddAP(2, CORESIGHT_AHB_AP);", + " JLINK_CORESIGHT_AddAP(3, CORESIGHT_APB_AP);", + " JLINK_CORESIGHT_AddAP(4, CORESIGHT_AHB_AP);", + f" CPU = {cpu};", + f" CORESIGHT_IndexAHBAPToUse = {ap_index};", + ] + lines.extend(extras) + lines.append(" return 0;") + lines.append("}") + return "\n".join(lines) + + def _cpu_constant(self, core_name: str) -> str: + mapping = { + "Cortex-M33": "CORTEX_M33", + "Cortex-M23": "CORTEX_M23", + "Cortex-A32": "CORTEX_A32", + } + return mapping.get(core_name, "CORTEX_M33") + + def _resolve_swd_interface(self): + if pylink is None: + return None + try: + return pylink.enums.JLinkInterfaces.SWD + except Exception: + try: + return pylink.JLinkInterfaces.SWD + except Exception: + return None + + def attach_session(self, session: DebuggerSession): + if not self.is_pylink_available(): + session.set_unavailable("pylink not installed") + return + if not self.is_dll_available(): + messagebox.showerror("J-Link DLL Missing", "Copy pylink/JLinkARM.dll before connecting.") + session.set_unavailable("J-Link DLL missing") + return + device = self.get_selected_device() + if not device: + messagebox.showwarning("J-Link Not Found", "Select a connected J-Link before connecting.") + return + serial = device.get("serial") + speed = self.validate_speed() + if speed is None or serial is None: + return + ok, msg = self.controller.attach(session, serial, speed) + if not ok: + messagebox.showerror("Pylink Connection Failed", msg) + session.queue_message(f"[error] {msg}") + return + session.mark_connected(serial) + session.queue_message(f"Attached to emulator {serial} at {speed} kHz.") + + def detach_session(self, session: DebuggerSession): + session.mark_disconnected() + self.controller.detach(session) + + def run_session_command(self, session: DebuggerSession, command: str): + self.controller.execute(session, command) + + def run_gdb_script(self, session: DebuggerSession, path: str): + return self.controller.run_gdb_script(session, path) + + def set_shared_script(self, path: str, session: Optional[DebuggerSession] = None): + ok, msg = self.controller.set_script(path) + target_session = session or None + if target_session: + target_session.queue_message(f"[script] {msg}") + else: + self.queue_message(f"[script] {msg}") + return ok, msg + + def _set_status(self, text: str, color: str): + self.status_var.set(text) + if self.status_label: + try: + self.status_label.configure(foreground=color) + except tk.TclError: + pass + + def is_pylink_available(self) -> bool: + return self._pylink_available + + def is_dll_available(self) -> bool: + return bool(self.dll_path and os.path.exists(self.dll_path)) + + def get_selected_device(self): + label = (self.device_var.get() or "").strip() + return self._device_display.get(label) + + def validate_speed(self) -> Optional[int]: + try: + speed = int((self.speed_var.get() or "").strip()) + if speed <= 0: + raise ValueError + return speed + except ValueError: + messagebox.showwarning("Invalid Speed", "Enter a positive integer speed in kHz.") + return None + + def queue_message(self, text: str): + if not text: + return + for session in self.sessions: + session.queue_message(text) + + def show_help(self): + sections = [ + ( + "Memory Access", + [ + ("x ", "Read a 32-bit word at . Use x/COUNT{b|h|w|g} for other sizes/counts."), + ("set *=", "Write a 32-bit value to ."), + ], + ), + ( + "Execution Control", + [ + ("halt / h", "Stop the current core."), + ("go / g / resume / run", "Resume execution from the current PC."), + ("reset / r", "Reset and continue."), + ("reset_halt / rh", "Reset and halt immediately."), + ("step / s / si", "Single-step one instruction."), + ("next / n / ni", "Step over calls (uses software breakpoint when needed)."), + ("finish / fin / so", "Step out of the current function."), + ("i r", "Show current PC and general-purpose registers."), + ], + ), + ( + "Breakpoints", + [ + ("b ", "Set breakpoint at ."), + ("bc ", "Clear the breakpoint at ."), + ], + ), + ( + "J-Link Exec", + [ + ("Any other text", "Forwarded directly to J-Link 'exec' (ex: regs, mem32, log)."), + ], + ), + ( + "Scripts", + [ + ("Run Script", "Load .jlink or .gdb files for the selected tab."), + ("monitor ", "In .gdb files, these lines send directly to J-Link."), + ], + ), + ( + "Sessions", + [ + ("Per-tab isolation", "Each core tab owns its connection, history, and log."), + ], + ), + ] + lines = ["Debugger command reference", ""] + for title, entries in sections: + lines.append(f"{title}:") + for cmd, desc in entries: + lines.append(f" {cmd}") + lines.append(f" {desc}") + lines.append("") + text = "\n".join(lines).rstrip() + try: + messagebox.showinfo("Debugger Help", text) + except tk.TclError: + pass + + def poll_queue(self): + for session in self.sessions: + session.poll_queue() + + def shutdown(self): + self._cancel_device_refresh() + for session in list(self.sessions): + session.shutdown() + + def disconnect(self): + for session in list(self.sessions): + if session.connected: + self.detach_session(session) + + def has_active_connections(self) -> bool: + return any(session.connected for session in self.sessions) + + def refresh_devices(self): + entries = self._enumerate_emulators() + display = [] + mapping = {} + for entry in entries: + serial, detail = self._extract_emulator_metadata(entry) + if serial is None: + continue + label = self._format_device_label(serial, detail) + display.append(label) + mapping[label] = {"serial": serial, "detail": detail} + self._device_display = mapping + if self.device_combo: + self.device_combo["values"] = display + current = self.device_var.get() + if display and current not in mapping: + self.device_var.set(display[0]) + elif not display: + self.device_var.set("") + + def _schedule_device_refresh(self): + if self._device_refresh_after is not None: + return + try: + self._device_refresh_after = self.frame.after(1000, self._auto_refresh_devices) + except tk.TclError: + self._device_refresh_after = None + + def _auto_refresh_devices(self): + self._device_refresh_after = None + try: + if not self.frame.winfo_exists(): + return + except tk.TclError: + return + self.refresh_devices() + self._schedule_device_refresh() + + def _cancel_device_refresh(self): + if self._device_refresh_after is None: + return + try: + self.frame.after_cancel(self._device_refresh_after) + except tk.TclError: + pass + self._device_refresh_after = None + + def _enumerate_emulators(self): + if not self._pylink_available: + return [] + for provider in self._emulator_providers(): + try: + entries = provider() + except Exception as exc: + if not self._device_error_reported: + self.queue_message(f"[pylink] Unable to enumerate J-Link: {exc}") + self._device_error_reported = True + continue + if entries: + self._device_error_reported = False + return entries + return [] + + def _emulator_providers(self): + providers = [] + if hasattr(pylink, "JLink"): + def by_instance(): + link = pylink.JLink() + try: + if hasattr(link, "list_emulators"): + return link.list_emulators() + return link.connected_emulators() + finally: + try: + link.close() + except Exception: + pass + providers.append(by_instance) + dll_module = getattr(pylink, "JLinkARMDll", None) + dll_class = getattr(dll_module, "JLinkARMDll", None) if dll_module else None + if dll_class: + def by_dll(): + dll = dll_class() + try: + if hasattr(dll, "list_emulators"): + return dll.list_emulators() + finally: + try: + dll.close() + except Exception: + pass + providers.append(by_dll) + return providers + + def _extract_emulator_metadata(self, entry): + serial = self._extract_serial(entry) + if serial is None: + return None, "" + product = self._extract_attr(entry, ("product", "Product", "model")) + connection = self._extract_attr(entry, ("connection", "Connection", "interface")) + if hasattr(connection, "name"): + connection = connection.name + parts = [] + if product: + parts.append(str(product).strip()) + if connection: + parts.append(str(connection).strip()) + return serial, " / ".join(parts) + + def _extract_serial(self, entry): + candidates = [] + if isinstance(entry, dict): + candidates.extend([ + entry.get("serial_number"), + entry.get("SerialNumber"), + entry.get("serial"), + entry.get("Serial"), + ]) + else: + candidates.extend([ + getattr(entry, "serial_number", None), + getattr(entry, "SerialNumber", None), + getattr(entry, "serial", None), + getattr(entry, "Serial", None), + ]) + for value in candidates: + serial = safe_int(value) + if serial is not None: + return serial + return None + + def _extract_attr(self, entry, keys): + for key in keys: + if isinstance(entry, dict) and key in entry: + return entry.get(key) + if hasattr(entry, key): + return getattr(entry, key) + return None + + def _format_device_label(self, serial, detail): + serial_text = str(serial).strip() + info_parts = [] + normalized = self._normalize_device_detail(detail or "") + if normalized: + info_parts.append(normalized) + ap_overview = " / ".join(f"{ap['label']} ({ap['core']})" for ap in self.ap_definitions) + if ap_overview: + info_parts.append(ap_overview) + if info_parts: + return f"{serial_text} - {' | '.join(info_parts)}" + return serial_text + + def _normalize_device_detail(self, detail: str) -> str: + text = (detail or "").strip() + if text.endswith("- 1"): + text = text[:-3].rstrip() + return text + +class DualUARTApp: + def __init__(self, root: tk.Tk) -> None: + self.root = root + version_suffix = f"v{APP_VERSION}" if APP_VERSION not in ("", "unknown") else "version unknown" + self.root.title(f"AmebaPro3 Control Panel — Powered by yiekheng, {version_suffix}") + self._closing = False + self._poll_after_id = None + self.flash_thread = None + self._icon_image = self._create_robot_icon() + if self._icon_image: + self.root.iconphoto(False, self._icon_image) + + paned = ttk.Panedwindow(root, orient="horizontal") + paned.pack(fill="both", expand=True, padx=10, pady=10) + + left_container = ttk.Frame(paned) + right_container = ttk.Frame(paned) + paned.add(left_container, weight=2) + paned.add(right_container, weight=3) + + self.port_assignments: dict[str, str] = {} + + self.dev1 = DevicePanel( + left_container, + title="Control Device", + mode="arduino", + default_baud="9600", + line_ending="\n", + console_height=10, + on_connect=lambda port, key="dev1": self._panel_connected(key, port), + on_disconnect=lambda port, key="dev1": self._panel_disconnected(key, port), + exclude_ports_provider=lambda key="dev1": self._get_excluded_ports(key), + before_button_callback=self._before_control_button, + ) + self.dev1.frame.pack(fill="both", expand=True, padx=(0, 5)) + + self.debugger = PylinkDebuggerPanel(left_container, PYLINK_DLL_PATH) + self.debugger.frame.pack(fill="both", expand=True, padx=(0, 5), pady=(5, 0)) + + self.dev2 = DevicePanel( + right_container, + title="AmebaPro3", + mode="raw", + default_baud="1500000", + line_ending="\r\n", + flash_handler=self.flash_ameba, + flash_button_label="Flash Image", + on_connect=lambda port, key="dev2": self._panel_connected(key, port), + on_disconnect=lambda port, key="dev2": self._panel_disconnected(key, port), + exclude_ports_provider=lambda key="dev2": self._get_excluded_ports(key), + ) + self.dev2.frame.pack(fill="both", expand=True, padx=(5, 0)) + + self.poll_loop() + self.root.protocol("WM_DELETE_WINDOW", self.close) + + # ------------------------------------------------------------------ + def _create_robot_icon(self): + try: + icon = tk.PhotoImage(width=32, height=32) + except tk.TclError: + return None + icon.put("#1e1f2a", to=(0, 0, 32, 32)) + icon.put("#9ca5c9", to=(6, 6, 26, 18)) + icon.put("#3c3f58", to=(10, 18, 22, 28)) + icon.put("#f5f7ff", to=(10, 10, 14, 14)) + icon.put("#f5f7ff", to=(18, 10, 22, 14)) + icon.put("#1e1f2a", to=(12, 12, 13, 13)) + icon.put("#1e1f2a", to=(20, 12, 21, 13)) + icon.put("#9ca5c9", to=(14, 3, 18, 5)) + icon.put("#9ca5c9", to=(15, 5, 17, 6)) + icon.put("#6b708f", to=(6, 14, 8, 22)) + icon.put("#6b708f", to=(24, 14, 26, 22)) + return icon + + # ------------------------------------------------------------------ + def poll_loop(self) -> None: + if self._closing: + return + self.dev1.poll_queue() + self.dev2.poll_queue() + if self.debugger: + self.debugger.poll_queue() + try: + self._poll_after_id = self.root.after(10, self.poll_loop) + except tk.TclError: + self._closing = True + + def close(self) -> None: + if self._closing: + return + self._closing = True + if self._poll_after_id is not None: + try: + self.root.after_cancel(self._poll_after_id) + except tk.TclError: + pass + self._poll_after_id = None + self.dev1.disconnect_serial() + self.dev2.disconnect_serial() + if self.debugger: + self.debugger.shutdown() + self.debugger.disconnect() + self.root.destroy() + + # ------------------------------------------------------------------ + def _panel_connected(self, key: str, port: str) -> None: + self.port_assignments[key] = port + self._refresh_other_panels(key) + + def _panel_disconnected(self, key: str, _port: str) -> None: + self.port_assignments.pop(key, None) + self._refresh_other_panels(key) + + def _get_excluded_ports(self, key: str) -> List[str]: + return [port for panel_key, port in self.port_assignments.items() if panel_key != key] + + def _refresh_other_panels(self, key: str) -> None: + for panel_key, panel in ("dev1", self.dev1), ("dev2", self.dev2): + if panel_key != key: + panel.refresh_ports() + + def _before_control_button(self) -> None: + self._disconnect_debugger_for_reset("Arduino control command") + + def _disconnect_debugger_for_reset(self, reason: str) -> None: + if not self.debugger or not self.debugger.has_active_connections(): + return + note = f"Disconnecting debugger before {reason}." + for panel in (self.dev1, self.dev2): + panel.queue_message(f"[debugger] {note}") + try: + self.debugger.queue_message(f"[info] {note}") + except Exception: + pass + try: + self.debugger.disconnect() + except Exception: + pass + + def _log_flash_step(self, text: str) -> None: + message = f"[flash] {text}" + self.dev2.queue_message(message) + self.dev1.queue_message(message) + + def _prepare_firmware_images(self) -> bool: + image_dir = os.path.join(APP_ROOT, FLASH_IMAGE_DIR) + try: + os.makedirs(image_dir, exist_ok=True) + except Exception as exc: + msg = f"Unable to create firmware directory '{image_dir}': {exc}" + self._log_flash_step(msg) + messagebox.showerror("Firmware Directory Error", msg) + return False + missing = [src for src, _ in FIRMWARE_COPY_MAP if not os.path.exists(os.path.join(image_dir, src))] + if missing: + msg = f"Missing firmware file(s): {', '.join(missing)} in {image_dir}" + self._log_flash_step(msg) + messagebox.showerror("Missing Firmware", msg) + return False + for src, dest in FIRMWARE_COPY_MAP: + src_path = os.path.join(image_dir, src) + dest_path = os.path.join(image_dir, dest) + try: + shutil.copyfile(src_path, dest_path) + self._log_flash_step(f"Prepared '{dest}' from '{src}'.") + except Exception as exc: + err = f"Failed to copy '{src}' to '{dest}': {exc}" + self._log_flash_step(err) + messagebox.showerror("Firmware Copy Failed", err) + return False + return True + + def _send_arduino_pg_reset(self, value: int) -> bool: + self._disconnect_debugger_for_reset("Arduino PG/reset sequence") + if not self.dev1.is_connected(): + self._log_flash_step("Arduino is not connected; cannot toggle PG.") + return False + pg_cmd = f"pg {value}" + self._log_flash_step(f"Arduino command → '{pg_cmd}'") + self.dev1.send_uart(pg_cmd) + self._log_flash_step("Arduino command → 'reset'") + self.dev1.send_uart("reset") + return True + + def _set_flash_controls_state(self, enabled: bool) -> None: + state = "normal" if enabled else "disabled" + for widget in (self.dev2.flash_button, self.dev2.connect_button, self.dev1.connect_button): + if widget is not None: + widget.configure(state=state) + + def flash_ameba(self) -> None: + if self.flash_thread and self.flash_thread.is_alive(): + messagebox.showinfo("Flash in Progress", "Please wait for the current flash.py run to finish.") + return + self._disconnect_debugger_for_reset("flash.py run") + if not self.dev1.is_connected(): + messagebox.showwarning("Arduino Not Connected", "Connect Device 1 (Arduino) before flashing.") + return + if not self.dev2.is_connected(): + messagebox.showwarning("Not Connected", "Connect Device 2 (AmebaPro3) before flashing.") + return + port = self.dev2.get_selected_port() + if not port: + messagebox.showwarning("No Port", "Select a COM port for AmebaPro3 before flashing.") + return + if not self._prepare_firmware_images(): + return + self._log_flash_step(f"Starting flash.py on {port}.") + self._set_flash_controls_state(False) + if not self._send_arduino_pg_reset(1): + self._set_flash_controls_state(True) + messagebox.showerror("Arduino Error", "Unable to set PG=1 and reset through Arduino.") + return + self._log_flash_step("Disconnecting AmebaPro3 UART before flashing.") + self.dev2.disconnect_serial() + self.flash_thread = threading.Thread(target=self._flash_worker, args=(port,), daemon=True) + self.flash_thread.start() + + def _flash_worker(self, port: str) -> None: + command = self._build_flash_command(port) + self.dev2.queue_message(f"[flash] Executing: {' '.join(command)}") + try: + process = subprocess.Popen( + command, + cwd=APP_ROOT, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1, + ) + except Exception as exc: + self._flash_complete(False, f"Failed to start flash.py: {exc}") + return + if process.stdout: + timestamp_pattern = re.compile(r"\[\d{4}-\d{2}-\d{2} [^\]]+\]") + for line in process.stdout: + clean = line.rstrip() + if not clean: + continue + sanitized = timestamp_pattern.sub("", clean).strip() + if not sanitized: + continue + self.dev2.queue_message(f"[flash.py] {sanitized}") + ret = process.wait() + if process.stdout: + process.stdout.close() + success = ret == 0 + message = "flash.py completed successfully." if success else f"flash.py exited with code {ret}." + self._flash_complete(success, message) + + def _flash_complete(self, success: bool, message: str) -> None: + def finalize(): + self.dev2.queue_message(f"[flash] {message}") + self._set_flash_controls_state(True) + self.flash_thread = None + if not self._closing: + self._log_flash_step("Reconnecting AmebaPro3 UART...") + try: + self.dev2.connect_serial() + except Exception: + pass + if self.dev2.is_connected(): + self._log_flash_step("AmebaPro3 reconnected. Lowering PG and resetting.") + self._send_arduino_pg_reset(0) + else: + self._log_flash_step("Failed to reconnect AmebaPro3; PG reset skipped.") + if not success and not self._closing: + messagebox.showerror("Flash Failed", message) + try: + self.root.after(0, finalize) + except tk.TclError: + pass + + def _build_flash_command(self, port: str) -> List[str]: + args = [ + "-d", + "--profile", FLASH_PROFILE, + "--image-dir", FLASH_IMAGE_DIR, + "--baudrate", FLASH_BAUDRATE, + "--memory-type", FLASH_MEMORY_TYPE, + "--port", port, + ] + exe = self._locate_flash_executable() + if exe: + return [exe] + args + script = os.path.join(APP_ROOT, "flash.py") + return [sys.executable, script] + args + + def _locate_flash_executable(self) -> Optional[str]: + candidates = [] + if getattr(sys, "frozen", False): + candidates.append(os.path.dirname(sys.executable)) + candidates.append(APP_ROOT) + for directory in candidates: + exe = os.path.join(directory, "flash.exe") + if os.path.exists(exe): + return exe + return None + + +if __name__ == "__main__": + root = tk.Tk() + DualUARTApp(root) + root.mainloop() diff --git a/pro3_uart_v1.2.2.spec b/pro3_uart_v1.2.2.spec new file mode 100644 index 0000000..fa318ef --- /dev/null +++ b/pro3_uart_v1.2.2.spec @@ -0,0 +1,44 @@ +# -*- mode: python ; coding: utf-8 -*- + + +a = Analysis( + ['C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\pro3_uart.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='pro3_uart_v1.2.2', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) +coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='pro3_uart_v1.2.2', +) diff --git a/pylink/JLinkARM.dll b/pylink/JLinkARM.dll new file mode 100644 index 0000000..c0fe259 Binary files /dev/null and b/pylink/JLinkARM.dll differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2cb24d1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +// python3.11.4 +pyDes==2.0.1 +colorama==0.4.6 +pyserial==3.5 \ No newline at end of file diff --git a/version_info.py b/version_info.py new file mode 100644 index 0000000..1c73293 --- /dev/null +++ b/version_info.py @@ -0,0 +1,8 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Realtek Semiconductor Corp. +# SPDX-License-Identifier: Apache-2.0 + +version = "1.2.2.0" +display_version = "1.2.2"