[realtek-ambz] Add UART upload configuration
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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
506
tools/rtltool.py
Normal 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)
|
||||
Reference in New Issue
Block a user