[realtek-ambz] Add UART upload configuration

This commit is contained in:
Kuba Szczodrzyński
2022-04-27 18:11:20 +02:00
parent fef022192c
commit 3bfd204909
4 changed files with 544 additions and 3 deletions

View File

@@ -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",

View File

@@ -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,
)

View File

@@ -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)

506
tools/rtltool.py Normal file
View File

@@ -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(
"<BHBH",
ord(CMD_RBF),
offset & 0xFFFF,
int(offset / 0x10000) & 0xFF,
count,
)
)
count *= 4
if ret:
for _ in range(count):
data = self._port.read(RTL_READ_BLOCK_SIZE)
if data:
ret = self._port.write(ACK)
if ret:
if size > 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(
"<BHBH",
ord(CMD_EFS),
offset & 0xFFFF,
int(offset / 0x10000) & 0xFF,
1,
)
)
if not ret:
return ret
offset += RTL_FLASH_SECTOR_SIZE
ret = self.sync()
else:
ret = False
return ret
def calc_checksum(self, data, checksum=0):
if platform.python_version_tuple() >= ("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("<BBBI", ord(cmd), sequence, 0xFF - sequence, offset)
+ data
)
crc = self.calc_checksum(pkt[3:])
pkt += struct.pack("<B", crc)
error_count = 0
while True:
ret = self.writecmd(pkt)
if ret:
sequence = (sequence + 1) % 0x100
offset += packet_size
size -= rdsize
break
else:
error_count += 1
if error_count > 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)