pro3_control_panel/base/rom_handler.py
2025-12-15 09:23:52 +08:00

323 lines
10 KiB
Python

#! /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