From 3bfd204909b52c039229a2a91520f62eb916b778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Wed, 27 Apr 2022 18:11:20 +0200 Subject: [PATCH] [realtek-ambz] Add UART upload configuration --- boards/wr3.json | 9 +- builder/frameworks/realtek-ambz-sdk.py | 29 +- builder/main.py | 3 +- tools/rtltool.py | 506 +++++++++++++++++++++++++ 4 files changed, 544 insertions(+), 3 deletions(-) create mode 100644 tools/rtltool.py diff --git a/boards/wr3.json b/boards/wr3.json index 4748cee..ec9bdbb 100644 --- a/boards/wr3.json +++ b/boards/wr3.json @@ -37,8 +37,15 @@ "realtek-ambz-arduino" ], "upload": { + "flash_size": "2MB", "maximum_size": 806912, - "maximum_ram_size": 262144 + "maximum_ram_size": 262144, + "require_upload_port": true, + "speed": 1500000, + "protocol": "uart", + "protocols": [ + "uart" + ] }, "name": "WR3 Wi-Fi Module", "url": "https://developer.tuya.com/en/docs/iot/wr3-module-datasheet?id=K9g3ainzbj9z1", diff --git a/builder/frameworks/realtek-ambz-sdk.py b/builder/frameworks/realtek-ambz-sdk.py index 12bd094..4e074c3 100644 --- a/builder/frameworks/realtek-ambz-sdk.py +++ b/builder/frameworks/realtek-ambz-sdk.py @@ -1,6 +1,7 @@ +import sys from os.path import isdir, join -from SCons.Script import Builder, DefaultEnvironment +from SCons.Script import AlwaysBuild, Builder, DefaultEnvironment env = DefaultEnvironment() platform = env.PioPlatform() @@ -553,6 +554,31 @@ actions.append(env.VerboseAction(package_ota, "Packaging OTA image - $IMG_OTA")) actions.append(env.VerboseAction("true", f"- OTA1 flash offset: {flash_ota1_offset}")) actions.append(env.VerboseAction("true", f"- OTA2 flash offset: {flash_ota2_offset}")) +# Uploader +upload_protocol = env.subst("$UPLOAD_PROTOCOL") +upload_source = "" +upload_actions = [] +# from platform-espressif32/builder/main.py +if upload_protocol == "uart": + env.Replace( + UPLOADER=join(platform.get_dir(), "tools", "rtltool.py"), + UPLOADERFLAGS=[ + "--port", + "$UPLOAD_PORT", + "--go", # run firmware after uploading + "wf", # Write a binary file to Flash data + ], + UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS $FLASH_OTA1_OFFSET "$BUILD_DIR/$IMG_FW"', + ) + upload_actions = [ + env.VerboseAction(env.AutodetectUploadPort, "Looking for upload port..."), + env.VerboseAction("$UPLOADCMD", "Uploading $IMG_FW"), + ] +elif upload_protocol == "custom": + upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $IMG_FW")] +else: + sys.stderr.write("Warning! Unknown upload protocol %s\n" % upload_protocol) + # Clone env to ignore options from child projects envsdk = env.Clone() @@ -576,4 +602,5 @@ env.Append( BUILDERS=dict( DumpFirmwareBinary=Builder(action=actions), ), + UPLOAD_ACTIONS=upload_actions, ) diff --git a/builder/main.py b/builder/main.py index 908bf7c..8a1c4a6 100644 --- a/builder/main.py +++ b/builder/main.py @@ -37,5 +37,6 @@ if flash_layout: env.Replace(**defines) target_elf = env.BuildProgram() -target_fw = env.DumpFirmwareBinary("firmware.bin", target_elf) +target_fw = env.DumpFirmwareBinary("$IMG_FW", target_elf) +env.AddPlatformTarget("upload", target_fw, env["UPLOAD_ACTIONS"], "Upload") Default(target_fw) diff --git a/tools/rtltool.py b/tools/rtltool.py new file mode 100644 index 0000000..edc72fc --- /dev/null +++ b/tools/rtltool.py @@ -0,0 +1,506 @@ +#!/usr/bin/env python +# RTL871xBx ROM Bootloader Utility Ver 12.01.2018 +# Created on: 10.10.2017 +# Author: pvvx +# + +import argparse +import io +import os +import platform +import struct +import sys +import time + +import serial + +# Protocol bytes +SOH = b"\x01" +STX = b"\x02" +EOT = b"\x04" +ACK = b"\x06" +DLE = b"\x10" +NAK = b"\x15" +CAN = b"\x18" + +CMD_USB = b"\x05" # UART Set Baud +CMD_XMD = b"\x07" # Go xmodem mode (write RAM/Flash mode) +CMD_EFS = b"\x17" # Erase Flash Sectors +CMD_RBF = b"\x19" # Read Block Flash +CMD_ABRT = b"\x1B" # End xmodem mode (write RAM/Flash mode) +CMD_GFS = b"\x21" # FLASH Get Status +CMD_SFS = b"\x26" # FLASH Set Status + +# Protocol Mode +MODE_RTL = 0 # Rtl mode +MODE_XMD = 1 # xmodem mode +MODE_UNK1 = 3 # Unknown mode, test 1 +MODE_UNK2 = 4 # Unknown mode, test 2 + +# Default baudrate +RTL_ROM_BAUD = 1500000 + +RTL_READ_BLOCK_SIZE = 1024 +RTL_FLASH_SECTOR_SIZE = 4096 + + +class RTLXMD: + def __init__(self, port=0, baud=RTL_ROM_BAUD, timeout=1): + self.mode = MODE_UNK1 + try: + self._port = serial.Serial(port, baud) + self._port.timeout = timeout + except: + # raise Exception('Error open %s, %d baud' % (port, baud)) + print("Error: Open %s, %d baud!" % (port, baud)) + sys.exit(-1) + + def writecmd(self, cmd, ok=ACK): + if self._port.write(cmd): + char = self._port.read(1) + if char: + if char == ok: + return True + return False + + def WaitNAK(self): + chr_count = 128 + while 1: + char = self._port.read(1) + if char: + if char == NAK: + return True + else: + return None + chr_count -= 1 + if chr_count == 0: + return False + + # return False + + def sync(self, mode=MODE_RTL, flush=True, ready=7): + if flush: + self._port.flushOutput() + self._port.flushInput() + error_count = 0 + cancel = 0 + while True: + char = self._port.read(1) + if char: + if char == b"\x00": + continue + elif char == NAK: + # standard checksum requested (NAK) + if mode != self.mode: + if self.mode < MODE_UNK1: + if mode == MODE_RTL: + if self.writecmd(CMD_ABRT, CAN): + self.mode = MODE_RTL + # return True + break + elif mode == MODE_XMD: + if self.writecmd(CMD_XMD): + self.mode = MODE_XMD + break + else: + if mode == MODE_XMD: + if self.writecmd(CMD_XMD): + self.mode = MODE_XMD + break + self.mode = MODE_RTL + break + elif char == CAN: + # received CAN + if cancel: + # Transmission canceled: received 2xCAN at start-sequence + return False + else: + # Cancellation at start sequence + cancel = 1 + # else: + # send error: expected NAK, or CAN + # print 'Not NAK or CAN: %02x' % (ord(char)) + else: + if self.mode == MODE_UNK1: + if self.writecmd(CMD_XMD): + self.mode = MODE_XMD + if mode == MODE_XMD: + return True + if self.writecmd(CMD_ABRT, CAN): + self.mode = MODE_RTL + return True + self.mode = MODE_UNK2 + elif self.mode == MODE_UNK2: + if self.writecmd(CMD_ABRT, CAN): + self.mode = MODE_RTL + if mode == MODE_RTL: + return True + if self.writecmd(CMD_XMD): + self.mode = MODE_XMD + return True + self.mode = MODE_UNK1 + error_count += 1 + if error_count > ready: + if self.mode == MODE_XMD: + # send error: error_count reached 15, aborting. + self._port.write(CAN) + self._port.write(CAN) + return False + return True + + def ModeXmodem(self): + if self.sync(): + ret = self.writecmd(CMD_XMD) + if ret == True: + self.mode = 1 + return ret + return None + + def RtlMode(self): + if self.sync(): + ret = self.writecmd(CMD_ABRT, CAN) + if ret == True: + self.mode = 0 + return ret + return None + + def GetFlashStatus(self): + if self.sync(): + self._port.write(CMD_GFS) + return self._port.read(1) + return None + + def SetFlashStatus(self, status): + if self.sync(): + if self.writecmd([CMD_SFS, status]): + return self.GetFlashStatus() + return None + + def ReadBlockFlash(self, stream, offset=0, size=0x200000): + # Read sectors size: 4 block 1024 bytes, else not set ACK! + count = int((size + RTL_FLASH_SECTOR_SIZE - 1) / RTL_FLASH_SECTOR_SIZE) + offset &= 0xFFFFFF + if count > 0 and count < 0x10000 and offset >= 0: # 1 byte .. 16 Mbytes + ret = self.sync() + if ret: + ret = self._port.write( + struct.pack( + " RTL_READ_BLOCK_SIZE: + stream.write(data) + elif size > 0: + stream.write(data[:size]) + else: + return ret + else: + return False + size -= RTL_READ_BLOCK_SIZE + if size <= 0: + ret = self.sync() + else: + ret = False + return ret + + def connect(self): + # issue reset-to-bootloader: + # RTS = either RESET (both active low = chip in reset) + # DTR = GPIOA_30 (active low = boot to flasher) + self._port.setDTR(False) + self._port.setRTS(True) + time.sleep(0.05) + self._port.setDTR(True) + self._port.setRTS(False) + time.sleep(0.05) + self._port.setDTR(False) + return self.GetFlashStatus() + + def EraseSectorsFlash(self, offset=0, size=0x200000): + count = int((size + RTL_FLASH_SECTOR_SIZE - 1) / RTL_FLASH_SECTOR_SIZE) + offset &= 0xFFF000 + if count > 0 and count < 0x10000 and offset >= 0: # 1 byte .. 16 Mbytes + for i in range(count): + ret = self.sync() + if ret: + # print '\r%d' % i + ret = self.writecmd( + struct.pack( + "= ("3", "0", "0"): + return (sum(data) + checksum) % 256 + else: + return (sum(map(ord, data)) + checksum) % 256 + + def send_xmodem(self, stream, offset, size, retry=3): + ret = self.sync(MODE_XMD) + if ret: + sequence = 1 + while size > 0: + if size <= 128: + packet_size = 128 + cmd = SOH + else: + packet_size = 1024 + cmd = STX + rdsize = packet_size + if size < rdsize: + rdsize = size + data = stream.read(rdsize) + if not data: # end of stream + print("send: at EOF") + return False + data = data.ljust(packet_size, b"\xFF") + pkt = ( + struct.pack(" retry: + return False + + ret = self.writecmd(EOT) # if write SRAM -> (*0x10002000)() + self.mode = MODE_RTL + return ret + + def WriteBlockSRAM(self, stream, offset=0x10002000, size=0x1000, retry=3): + offset &= 0x00FFFFFF + offset |= 0x10000000 + return self.send_xmodem(stream, offset, size, retry) + + def WriteBlockFlash(self, stream, offset=0x10010000, size=0x1000, retry=3): + offset &= 0x00FFFFFF + offset |= 0x08000000 + return self.send_xmodem(stream, offset, size, retry) + + +def arg_auto_int(x): + return int(x, 0) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="RT871xBx ROM Bootloader Utility", prog="rtltool" + ) + + parser.add_argument("--port", "-p", help="Serial port device", required=True) + parser.add_argument( + "--go", "-g", action="store_true", help="Run after performing the operation" + ) + + subparsers = parser.add_subparsers( + dest="operation", + help="Run rtltool {command} -h for additional help", + required=True, + ) + + parser_read_flash = subparsers.add_parser( + "rf", help="Read Flash data to binary file" + ) + parser_read_flash.add_argument("address", help="Start address", type=arg_auto_int) + parser_read_flash.add_argument("size", help="Size of region", type=arg_auto_int) + parser_read_flash.add_argument("filename", help="Name of binary file") + + parser_write_flash = subparsers.add_parser( + "wf", help="Write a binary file to Flash data" + ) + parser_write_flash.add_argument("address", help="Start address", type=arg_auto_int) + parser_write_flash.add_argument("filename", help="Name of binary file") + + parser_write_mem = subparsers.add_parser( + "wm", help="Write a binary file to SRAM memory" + ) + parser_write_mem.add_argument("address", help="Start address", type=arg_auto_int) + # parser_write_mem.add_argument('size', help='Size of region', type=arg_auto_int) + parser_write_mem.add_argument("filename", help="Name of binary file") + + parser_erase_flash = subparsers.add_parser("es", help="Erase Sectors Flash") + parser_erase_flash.add_argument("address", help="Start address", type=arg_auto_int) + parser_erase_flash.add_argument("size", help="Size of region", type=arg_auto_int) + + parser_get_status_flash = subparsers.add_parser( + "gf", help="Get Flash Status register" + ) + + parser_set_status_flash = subparsers.add_parser( + "sf", help="Set Flash Status register" + ) + parser_boot_flash = subparsers.add_parser("bf", help="Start boot flash") + parser_set_status_flash = subparsers.add_parser("gm", help="Go ROM Monitor") + + args = parser.parse_args() + rtl = RTLXMD(args.port) + print("Connecting...") + if rtl.connect(): + if args.operation == "wf": + stream = open(args.filename, "rb") + size = os.path.getsize(args.filename) + if size < 1: + stream.close + print("Error: File size = 0!") + sys.exit(-1) + offset = args.address & 0x00FFFFFF + offset |= 0x08000000 + print( + "Write Flash data 0x%08x to 0x%08x from file: %s ..." + % (offset, offset + size, args.filename) + ) + if not rtl.WriteBlockFlash(stream, args.address, size): + stream.close + print("Error: Write Flash!") + sys.exit(-2) + stream.close + # print 'Done!' + # sys.exit(0) + + elif args.operation == "rf": + print( + "Read Flash data from 0x%08x to 0x%08x in file: %s ..." + % (args.address, args.address + args.size, args.filename) + ) + stream = open(args.filename, "wb") + if not rtl.ReadBlockFlash(stream, args.address, args.size): + stream.close + print("Error!") + sys.exit(-2) + stream.close + # print 'Done!' + # sys.exit(0) + + elif args.operation == "wm": + stream = open(args.filename, "rb") + size = os.path.getsize(args.filename) + if size < 1: + stream.close + print("Error: File size = 0!") + sys.exit(-1) + offset = args.address & 0x00FFFFFF + offset |= 0x10000000 + print( + "Write SRAM at 0x%08x to 0x%08x from file: %s ..." + % (args.address, args.address + size, args.filename) + ) + if not rtl.WriteBlockSRAM(stream, args.address, size): + stream.close + print("Error: Write Flash!") + sys.exit(-2) + stream.close + print("Done!") + sys.exit(0) + + elif args.operation == "es": + count = (args.size + RTL_FLASH_SECTOR_SIZE - 1) / RTL_FLASH_SECTOR_SIZE + size = count * RTL_FLASH_SECTOR_SIZE + offset = args.address & 0xFFF000 + print( + "Erase Flash %d sectors, data from 0x%08x to 0x%08x ..." + % (count, offset, offset + size) + ) + if rtl.EraseSectorsFlash(offset, size): + print("Done!") + sys.exit(0) + print("Error: Erase Flash sectors!") + sys.exit(-2) + + elif args.operation == "gf": + fsta = rtl.GetFlashStatus() + if fsta: + print("Flash Status value: 0x%02x" % (ord(fsta))) + sys.exit(0) + print("Error: Get Flash Status!") + sys.exit(-2) + + elif args.operation == "sf": + print("Set Flash Status value: 0x%02x" % (args.value & 0xFF)) + if rtl.SetFlashStatus(args.value & 0xFF): + sys.exit(0) + print("Error: Set Flash Status!") + sys.exit(-2) + + elif args.operation == "bf": + print("BOOT_ROM_FromFlash()...") # ROM-Call:00005404 + stream = io.BytesIO(b"\x05\x54\x00\x00") + if not rtl.WriteBlockSRAM( + stream, 0x10002000, 4 + ): # [0x10002000] = 0x00005405 + stream.close + print("Error!") + sys.exit(-2) + print("Done!") + rtl._port.close() + rtl._port.baudrate = 115200 + rtl._port.open() + rtl._port.timeout = 1 + sio = io.TextIOWrapper(io.BufferedRWPair(rtl._port, rtl._port)) + print( + sio.readline(), + sio.readline(), + sio.readline(), + sio.readline(), + sio.readline(), + ) + sys.exit(0) + + elif args.operation == "gm": + stream = io.BytesIO( + b"\x19\x20\x00\x10\x19\x20\x00\x10\x19\x20\x00\x10\x19\x20\x00\x10\x19\x20\x00\x10\x00\x00\x00\x00\x08\xb5\x02\x4c\x4f\xf4\x7a\x70\xa0\x47\xfb\xe7\x05\x22\x00\x00" + ) + if not rtl.WriteBlockSRAM(stream, 0x10002000, 40): # [0x10002000] = ... + stream.close + print("Error!") + sys.exit(-2) + print("Done!") + sys.exit(0) + else: + print("Failed to connect device on", args.port, "!") + sys.exit(-2) + + if args.go: + if not rtl.WaitNAK() or rtl.writecmd(CMD_GFS, 0) == None: + print("Error: Sync!") + sys.exit(-2) + print("BOOT FromFlash...") # ROM-Call:00005404 + stream = io.BytesIO(b"\x05\x54\x00\x00") + if not rtl.WriteBlockSRAM(stream, 0x10002000, 4): # [0x10002000] = 0x00005405 + stream.close + print("Error!") + sys.exit(-2) + print("Done!") + sys.exit(0)