summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorScott Shawcroft <scott@adafruit.com>2020-07-02 13:56:09 -0700
committerGitHub <noreply@github.com>2020-07-02 13:56:09 -0700
commitc33542f978cc61f0466ff60ea769e3067baca95f (patch)
tree071e8f8a86971f7e947053c3b4fb2ff7da95d223 /tools
parentf572b723060382bbc9ca7a3edc88cb7c30fdcc30 (diff)
parenteec42d4cb53536a75e88f7f9321515bc6dcfeaa7 (diff)
Merge branch 'main' into patch-1
Diffstat (limited to 'tools')
-rw-r--r--tools/build_board_info.py24
-rw-r--r--tools/build_memory_info.py18
-rwxr-xr-xtools/codestats.sh12
-rw-r--r--tools/convert_release_notes.py3
-rw-r--r--tools/cpboard.py357
-rw-r--r--[-rwxr-xr-x]tools/dfu.py84
-rw-r--r--tools/extract_pyi.py83
-rw-r--r--tools/hid_report_descriptors.py4
-rw-r--r--tools/join_bins.py20
-rwxr-xr-xtools/mksdiodata.py40
-rwxr-xr-xtools/pyboard.py2
11 files changed, 419 insertions, 228 deletions
diff --git a/tools/build_board_info.py b/tools/build_board_info.py
index 3c8f44f74..20a5866b7 100644
--- a/tools/build_board_info.py
+++ b/tools/build_board_info.py
@@ -9,10 +9,13 @@ import base64
from datetime import date
from sh.contrib import git
+sys.path.append("../docs")
+import shared_bindings_matrix
+
sys.path.append("adabot")
import adabot.github_requests as github
-SUPPORTED_PORTS = ["nrf", "atmel-samd", "stm32f4", "cxd56", "mimxrt10xx"]
+SUPPORTED_PORTS = ["atmel-samd", "cxd56", "esp32s2", "litex", "mimxrt10xx", "nrf", "stm"]
BIN = ('bin',)
UF2 = ('uf2',)
@@ -20,6 +23,8 @@ BIN_UF2 = ('bin', 'uf2')
HEX = ('hex',)
HEX_UF2 = ('hex', 'uf2')
SPK = ('spk',)
+DFU = ('dfu',)
+BIN_DFU = ('bin', 'dfu')
# Example:
# https://downloads.circuitpython.org/bin/trinket_m0/en_US/adafruit-circuitpython-trinket_m0-en_US-5.0.0-rc.0.uf2
@@ -29,9 +34,11 @@ DOWNLOAD_BASE_URL = "https://downloads.circuitpython.org/bin"
extension_by_port = {
"nrf": UF2,
"atmel-samd": UF2,
- "stm32f4": BIN,
+ "stm": BIN,
"cxd56": SPK,
"mimxrt10xx": HEX_UF2,
+ "litex": DFU,
+ "esp32s2": BIN
}
# Per board overrides
@@ -45,13 +52,17 @@ extension_by_board = {
"feather_m0_basic": BIN_UF2,
"feather_m0_rfm69": BIN_UF2,
"feather_m0_rfm9x": BIN_UF2,
+ "uchip": BIN_UF2,
# nRF52840 dev kits that may not have UF2 bootloaders,
"makerdiary_nrf52840_mdk": HEX,
- "makerdiary_nrf52840_mdk_usb_dongle": HEX,
+ "makerdiary_nrf52840_mdk_usb_dongle": HEX_UF2,
"pca10056": BIN_UF2,
"pca10059": BIN_UF2,
- "electronut_labs_blip": HEX
+ "electronut_labs_blip": HEX,
+
+ # stm32
+ "meowbit_v121": UF2
}
aliases_by_board = {
@@ -238,6 +249,10 @@ def generate_download_info():
languages = get_languages()
+ support_matrix = shared_bindings_matrix.support_matrix_by_board(
+ use_branded_name=False
+ )
+
new_stable = "-" not in new_tag
previous_releases = set()
@@ -275,6 +290,7 @@ def generate_download_info():
new_version = {
"stable": new_stable,
"version": new_tag,
+ "modules": support_matrix.get(alias, "[]"),
"files": {}
}
for language in languages:
diff --git a/tools/build_memory_info.py b/tools/build_memory_info.py
index 93b4c1e82..808b70bd2 100644
--- a/tools/build_memory_info.py
+++ b/tools/build_memory_info.py
@@ -52,7 +52,7 @@ regions = {}
with open(sys.argv[1], "r") as f:
for line in f:
line = line.strip()
- if line.startswith(("FLASH_FIRMWARE", "FLASH", "RAM")):
+ if line.startswith(("FLASH_FIRMWARE", "RAM")):
regions[line.split()[0]] = line.split("=")[-1]
for region in regions:
@@ -63,17 +63,15 @@ for region in regions:
space = M_PATTERN.sub(M_REPLACE, space)
regions[region] = int(eval(space))
-# TODO Remove check for both FLASH_FIRMWARE and FLASH after all ports are converted to use FLASH_FIRMWARE.
-try:
- firmware_region = regions["FLASH_FIRMWARE"]
-except KeyError:
- firmware_region = regions["FLASH"]
+firmware_region = regions["FLASH_FIRMWARE"]
ram_region = regions["RAM"]
-free_flash = firmware_region - text - data
-free_ram = ram_region - data - bss
-print("{} bytes free in flash firmware space out of {} bytes ({}kB).".format(free_flash, firmware_region, firmware_region / 1024))
-print("{} bytes free in ram for stack and heap out of {} bytes ({}kB).".format(free_ram, ram_region, ram_region / 1024))
+used_flash = data + text
+free_flash = firmware_region - used_flash
+used_ram = data + bss
+free_ram = ram_region - used_ram
+print("{} bytes used, {} bytes free in flash firmware space out of {} bytes ({}kB).".format(used_flash, free_flash, firmware_region, firmware_region / 1024))
+print("{} bytes used, {} bytes free in ram for stack and heap out of {} bytes ({}kB).".format(used_ram, free_ram, ram_region, ram_region / 1024))
print()
# Check that we have free flash space. GCC doesn't fail when the text + data
diff --git a/tools/codestats.sh b/tools/codestats.sh
index 09284a30d..5f7625c45 100755
--- a/tools/codestats.sh
+++ b/tools/codestats.sh
@@ -90,9 +90,9 @@ else
last_rev="v1.0"
fi
-# get a list of hashes between last revision (exclusive) and master
-hashes=$(git log --format=format:"%H" --reverse ${last_rev}..master)
-#hashes=$(git log --format=format:"%H" --reverse ${last_rev}..master | $AWK '{if (NR % 10 == 0) print $0}') # do every 10th one
+# get a list of hashes between last revision (exclusive) and main
+hashes=$(git log --format=format:"%H" --reverse ${last_rev}..main)
+#hashes=$(git log --format=format:"%H" --reverse ${last_rev}..main | $AWK '{if (NR % 10 == 0) print $0}') # do every 10th one
for hash in $hashes; do
@@ -116,7 +116,7 @@ index 77d2945..dae0644 100644
@@ -55,10 +55,8 @@ void msec_sleep_tv(struct timeval *tv) {
#define MP_CLOCKS_PER_SEC CLOCKS_PER_SEC
#endif
-
+
-#if defined(MP_CLOCKS_PER_SEC) && (MP_CLOCKS_PER_SEC == 1000000) // POSIX
-#define CLOCK_DIV 1000.0
-#elif defined(MP_CLOCKS_PER_SEC) && (MP_CLOCKS_PER_SEC == 1000) // WIN32
@@ -182,6 +182,6 @@ EOF
done
-# checkout master and cleanup
-git checkout master
+# checkout main and cleanup
+git checkout main
$RM $pystoneavg
diff --git a/tools/convert_release_notes.py b/tools/convert_release_notes.py
index 87b7e444b..649184102 100644
--- a/tools/convert_release_notes.py
+++ b/tools/convert_release_notes.py
@@ -52,6 +52,9 @@ class AdafruitBBCodeRenderer:
def double_emphasis(self, text):
return "[b]{}[/b]".format(text)
+ def emphasis(self, text):
+ return "[b]{}[/b]".format(text)
+
bbcode = mistune.Markdown(renderer=AdafruitBBCodeRenderer())
print()
diff --git a/tools/cpboard.py b/tools/cpboard.py
index 0b9c43c61..7769cb4f4 100644
--- a/tools/cpboard.py
+++ b/tools/cpboard.py
@@ -39,27 +39,34 @@ import serial.tools.list_ports
import sh
import shutil
+
class CPboardError(BaseException):
pass
+
# supervisor/messages/default.h:
MSG_NEWLINE = b"\r\n"
MSG_SAFE_MODE_CRASH = b"Looks like our core CircuitPython code crashed hard. Whoops!"
-MSG_SAFE_MODE_BROWN_OUT_LINE_1 = b"The microcontroller's power dipped. Please make sure your power supply provides"
-MSG_SAFE_MODE_BROWN_OUT_LINE_2 = b"enough power for the whole circuit and press reset (after ejecting CIRCUITPY)."
+MSG_SAFE_MODE_BROWN_OUT_LINE_1 = (
+ b"The microcontroller's power dipped. Please make sure your power supply provides"
+)
+MSG_SAFE_MODE_BROWN_OUT_LINE_2 = (
+ b"enough power for the whole circuit and press reset (after ejecting CIRCUITPY)."
+)
MSG_WAIT_BEFORE_REPL = b"Press any key to enter the REPL. Use CTRL-D to reload."
+
class REPL:
- CHAR_CTRL_A = b'\x01'
- CHAR_CTRL_B = b'\x02'
- CHAR_CTRL_C = b'\x03'
- CHAR_CTRL_D = b'\x04'
+ CHAR_CTRL_A = b"\x01"
+ CHAR_CTRL_B = b"\x02"
+ CHAR_CTRL_C = b"\x03"
+ CHAR_CTRL_D = b"\x04"
def __init__(self, board):
self.board = board
self.write_chunk_size = 32
self.safe_mode = False
- self.session = b''
+ self.session = b""
def __enter__(self):
self.reset()
@@ -73,20 +80,20 @@ class REPL:
return self.board.serial
def read(self):
- if self.serial.inWaiting():
- data = self.serial.read(self.serial.inWaiting())
+ if self.serial.in_waiting:
+ data = self.serial.read(self.serial.in_waiting)
else:
- data = b''
+ data = b""
self.session += data
return data
def read_until(self, ending, timeout=10):
- data = b''
+ data = b""
timeout_count = 0
while True:
if data.endswith(ending):
break
- elif self.serial.inWaiting() > 0:
+ elif self.serial.in_waiting > 0:
new_data = self.serial.read(1)
data += new_data
self.session += new_data
@@ -102,10 +109,10 @@ class REPL:
if chunk_size is None:
chunk_size = self.write_chunk_size
if not isinstance(data, bytes):
- data = bytes(data, encoding='utf8')
+ data = bytes(data, encoding="utf8")
for i in range(0, len(data), chunk_size):
- chunk = data[i:min(i + chunk_size, len(data))]
+ chunk = data[i : min(i + chunk_size, len(data))]
self.session += chunk
self.serial.write(chunk)
time.sleep(0.01)
@@ -113,28 +120,30 @@ class REPL:
def reset(self):
# Use read() since serial.reset_input_buffer() fails with termios.error now and then
self.read()
- self.session = b''
- self.write(b'\r' + REPL.CHAR_CTRL_C + REPL.CHAR_CTRL_C) # interrupt any running program
- self.write(b'\r' + REPL.CHAR_CTRL_B) # enter or reset friendly repl
- data = self.read_until(b'>>> ')
+ self.session = b""
+ self.write(
+ b"\r" + REPL.CHAR_CTRL_C + REPL.CHAR_CTRL_C
+ ) # interrupt any running program
+ self.write(b"\r" + REPL.CHAR_CTRL_B) # enter or reset friendly repl
+ data = self.read_until(b">>> ")
- def execute(self, code, timeout=10, async=False):
- self.read() # Throw away
+ def execute(self, code, timeout=10, wait_for_response=True):
+ self.read() # Throw away
self.write(REPL.CHAR_CTRL_A)
- self.read_until(b'\r\n>')
+ self.read_until(b"\r\n>")
self.write(code)
self.write(REPL.CHAR_CTRL_D)
- if async:
- return b'', b''
- self.read_until(b'OK')
+ if not wait_for_response:
+ return b"", b""
+ self.read_until(b"OK")
- output = self.read_until(b'\x04', timeout=timeout)
+ output = self.read_until(b"\x04", timeout=timeout)
output = output[:-1]
- error = self.read_until(b'\x04')
+ error = self.read_until(b"\x04")
error = error[:-1]
return output, error
@@ -146,8 +155,8 @@ class REPL:
self.reset()
self.write(REPL.CHAR_CTRL_D)
- data = self.read_until(b' output:\r\n')
- if b'Running in safe mode' in data:
+ data = self.read_until(b" output:\r\n")
+ if b"Running in safe mode" in data:
self.safe_mode = True
raise RuntimeError("Can't run in safe mode")
@@ -159,7 +168,7 @@ class REPL:
data = data.split(marker)[0]
# Haven't found out why we have to strip off this...
- if data.endswith(b'\r\n\r\n'):
+ if data.endswith(b"\r\n\r\n"):
data = data[:-4]
return data
@@ -168,9 +177,13 @@ class Disk:
def __init__(self, dev):
self.dev = os.path.realpath(dev)
self.mountpoint = None
- with open('/etc/mtab', 'r') as f:
+ with open("/etc/mtab", "r") as f:
mtab = f.read()
- mount = [mount.split(' ') for mount in mtab.splitlines() if mount.startswith(self.dev)]
+ mount = [
+ mount.split(" ")
+ for mount in mtab.splitlines()
+ if mount.startswith(self.dev)
+ ]
if mount:
self._path = mount[0][1]
else:
@@ -245,22 +258,22 @@ class Firmware:
@property
def info(self):
with self.disk as disk:
- fname = os.path.join(disk.path, 'INFO_UF2.TXT')
- with open(fname, 'r') as f:
+ fname = os.path.join(disk.path, "INFO_UF2.TXT")
+ with open(fname, "r") as f:
info = f.read()
lines = info.splitlines()
res = {}
- res['header'] = lines[0]
+ res["header"] = lines[0]
for line in lines[1:]:
- k, _, v = line.partition(':')
- res[k.replace(':', '')] = v.strip()
+ k, _, v = line.partition(":")
+ res[k.replace(":", "")] = v.strip()
return res
def upload(self, fw):
- with open(fw, 'rb') as f:
+ with open(fw, "rb") as f:
header = f.read(32)
- if header[0:4] != b'UF2\n':
- raise ValueError('Only UF2 files are supported')
+ if header[0:4] != b"UF2\n":
+ raise ValueError("Only UF2 files are supported")
self.board.close()
with self.disk as disk:
disk.copy(fw, sync=False)
@@ -274,35 +287,37 @@ class CPboard:
except ValueError:
pass
- vendor, _, product = name.partition(':')
+ vendor, _, product = name.partition(":")
if vendor and product:
- return CPboard.from_usb(**kwargs, idVendor=int(vendor, 16), idProduct=int(product, 16))
+ return CPboard.from_usb(
+ **kwargs, idVendor=int(vendor, 16), idProduct=int(product, 16)
+ )
return CPboard(name, **kwargs)
@classmethod
def from_build_name(cls, name, **kwargs):
boards = {
- #'arduino_zero'
- 'circuitplayground_express' : (0x239a, 0x8019),
- #'feather_m0_adalogger' : (0x239a, ),
- #'feather_m0_basic' : (0x239a, ),
- 'feather_m0_express' : (0x239a, 0x8023),
- #'feather_m0_rfm69' : (0x239a, ),
- #'feather_m0_rfm9x' : (0x239a, ),
- #'feather_m0_supersized' : (0x239a, ),
- #'feather_m4_express' : (0x239a, ),
- #'gemma_m0' : (0x239a, ),
- #'itsybitsy_m0_express' : (0x239a, ),
- #'itsybitsy_m4_express' : (0x239a, ),
- 'metro_m0_express' : (0x239a, 0x8014),
- 'metro_m4_express' : (0x239a, 0x8021),
- #'metro_m4_express_revb' : (0x239a, ),
- #'pirkey_m0' : (0x239a, ),
- #'trinket_m0' : (0x239a, ),
- #'trinket_m0_haxpress' : (0x239a, ),
- #'ugame10'
- }
+ #'arduino_zero'
+ "circuitplayground_express": (0x239A, 0x8019),
+ #'feather_m0_adalogger' : (0x239a, ),
+ #'feather_m0_basic' : (0x239a, ),
+ "feather_m0_express": (0x239A, 0x8023),
+ #'feather_m0_rfm69' : (0x239a, ),
+ #'feather_m0_rfm9x' : (0x239a, ),
+ #'feather_m0_supersized' : (0x239a, ),
+ #'feather_m4_express' : (0x239a, ),
+ #'gemma_m0' : (0x239a, ),
+ #'itsybitsy_m0_express' : (0x239a, ),
+ "itsybitsy_m4_express": (0x239A, 0x802C),
+ "metro_m0_express": (0x239A, 0x8014),
+ "metro_m4_express": (0x239A, 0x8021),
+ #'metro_m4_express_revb' : (0x239a, ),
+ #'pirkey_m0' : (0x239a, ),
+ #'trinket_m0' : (0x239a, ),
+ #'trinket_m0_haxpress' : (0x239a, ),
+ #'ugame10'
+ }
try:
vendor, product = boards[name]
@@ -314,26 +329,26 @@ class CPboard:
@classmethod
def from_build_name_bootloader(cls, name, **kwargs):
boards = {
- #'arduino_zero'
- #'circuitplayground_express' : (0x239a, ),
- #'feather_m0_adalogger' : (0x239a, ),
- #'feather_m0_basic' : (0x239a, ),
- 'feather_m0_express' : (0x239a, 0x001b),
- #'feather_m0_rfm69' : (0x239a, ),
- #'feather_m0_rfm9x' : (0x239a, ),
- #'feather_m0_supersized' : (0x239a, ),
- #'feather_m4_express' : (0x239a, ),
- #'gemma_m0' : (0x239a, ),
- #'itsybitsy_m0_express' : (0x239a, ),
- #'itsybitsy_m4_express' : (0x239a, ),
- #'metro_m0_express' : (0x239a, 0x8014),
- 'metro_m4_express' : (0x239a, 0x0021),
- #'metro_m4_express_revb' : (0x239a, ),
- #'pirkey_m0' : (0x239a, ),
- #'trinket_m0' : (0x239a, ),
- #'trinket_m0_haxpress' : (0x239a, ),
- #'ugame10'
- }
+ #'arduino_zero'
+ #'circuitplayground_express' : (0x239a, ),
+ #'feather_m0_adalogger' : (0x239a, ),
+ #'feather_m0_basic' : (0x239a, ),
+ "feather_m0_express": (0x239A, 0x001B),
+ #'feather_m0_rfm69' : (0x239a, ),
+ #'feather_m0_rfm9x' : (0x239a, ),
+ #'feather_m0_supersized' : (0x239a, ),
+ #'feather_m4_express' : (0x239a, ),
+ #'gemma_m0' : (0x239a, ),
+ #'itsybitsy_m0_express' : (0x239a, ),
+ "itsybitsy_m4_express": (0x239A, 0x002B),
+ #'metro_m0_express' : (0x239a, 0x8014),
+ "metro_m4_express": (0x239A, 0x0021),
+ #'metro_m4_express_revb' : (0x239a, ),
+ #'pirkey_m0' : (0x239a, ),
+ #'trinket_m0' : (0x239a, ),
+ #'trinket_m0_haxpress' : (0x239a, ),
+ #'ugame10'
+ }
try:
vendor, product = boards[name]
@@ -347,16 +362,17 @@ class CPboard:
@classmethod
def from_usb(cls, baudrate=115200, wait=0, timeout=10, **kwargs):
import usb.core
+
dev = usb.core.find(**kwargs)
if not dev:
s = "Can't find USB device: "
args = []
for x in kwargs.items():
try:
- args.append('%s=0x%x' % x)
+ args.append("%s=0x%x" % x)
except:
- args.append('%s = %s' % x)
- raise RuntimeError("Can't find USB device: " + ', '.join(args))
+ args.append("%s = %s" % x)
+ raise RuntimeError("Can't find USB device: " + ", ".join(args))
return cls(dev, baudrate=baudrate, wait=wait, timeout=timeout)
def __init__(self, device, baudrate=115200, wait=0, timeout=10):
@@ -364,11 +380,15 @@ class CPboard:
self.usb_dev = None
try:
# Is it a usb.core.Device?
- portstr = ':' + '.'.join(map(str, device.port_numbers)) + ':'
+ portstr = ":" + ".".join(map(str, device.port_numbers)) + ":"
except:
pass
else:
- serials = [serial for serial in os.listdir("/dev/serial/by-path") if portstr in serial]
+ serials = [
+ serial
+ for serial in os.listdir("/dev/serial/by-path")
+ if portstr in serial
+ ]
if len(serials) != 1:
raise RuntimeError("Can't find excatly one matching usb serial device")
self.device = os.path.realpath("/dev/serial/by-path/" + serials[0])
@@ -401,62 +421,75 @@ class CPboard:
delayed = False
for attempt in range(wait + 1):
try:
- self.serial = serial.Serial(self.device, baudrate=self.baudrate, timeout=self.timeout, write_timeout=self.timeout, interCharTimeout=1)
+ self.serial = serial.Serial(
+ self.device,
+ baudrate=self.baudrate,
+ timeout=self.timeout,
+ inter_byte_timeout=10,
+ write_timeout=self.timeout,
+ )
break
- except (OSError, IOError): # Py2 and Py3 have different errors
+ except (OSError, IOError): # Py2 and Py3 have different errors
if wait == 0:
continue
if attempt == 0:
- sys.stdout.write('Waiting {} seconds for board '.format(wait))
+ sys.stdout.write("Waiting {} seconds for board ".format(wait))
delayed = True
time.sleep(1)
- sys.stdout.write('.')
+ sys.stdout.write(".")
sys.stdout.flush()
else:
if delayed:
- print('')
- raise CPboardError('failed to access ' + self.device)
+ print("")
+ raise CPboardError("failed to access " + self.device)
if delayed:
- print('')
+ print("")
def close(self):
if self.serial:
self.serial.close()
self.serial = None
- def exec(self, command, timeout=10, async=False):
+ def exec(self, command, timeout=10, wait_for_response=True):
with self.repl as repl:
try:
- output, error = repl.execute(command, timeout=timeout, async=async)
+ output, error = repl.execute(
+ command, timeout=timeout, wait_for_response=wait_for_response
+ )
except OSError as e:
if self.debug:
- print('exec: session: ', self.repl.session)
- raise CPboardError('timeout', e)
+ print("exec: session: ", self.repl.session)
+ raise CPboardError("timeout", e)
if error:
- raise CPboardError('exception', output, error)
+ raise CPboardError("exception", output, error)
return output
def eval(self, expression, timeout=10):
- command = 'print({})'.format(expression)
+ command = "print({})".format(expression)
with self.repl as repl:
output, error = repl.execute(command, timeout=timeout)
if error:
- raise CPboardError('exception', output, error)
+ raise CPboardError("exception", output, error)
try:
- res = eval(str(output, encoding='utf8'))
+ res = eval(str(output, encoding="utf8"))
except:
- raise CPboardError('failed to eval: %s' % output)
+ raise CPboardError("failed to eval: %s" % output)
return res
- def _reset(self, mode='NORMAL'):
- self.exec("import microcontroller;microcontroller.on_next_reset(microcontroller.RunMode.%s)" % mode)
+ def _reset(self, mode="NORMAL"):
+ self.exec(
+ "import microcontroller;microcontroller.on_next_reset(microcontroller.RunMode.%s)"
+ % mode
+ )
try:
- self.exec("import microcontroller;microcontroller.reset()", async=True)
+ self.exec(
+ "import microcontroller;microcontroller.reset()", wait_for_response=False
+ )
except OSError:
pass
def reset(self, safe_mode=False, delay=5, wait=10):
- self._reset('SAFE_MODE' if safe_mode else 'NORMAL')
+ self._reset("SAFE_MODE" if safe_mode else "NORMAL")
self.close()
time.sleep(delay)
self.open(wait)
@@ -464,7 +497,7 @@ class CPboard:
def reset_to_bootloader(self, repl=False):
if repl:
- self._reset('BOOTLOADER')
+ self._reset("BOOTLOADER")
self.close()
else:
self.close()
@@ -481,7 +514,7 @@ class CPboard:
@property
def serial_number(self):
- try: # Permissions are needed to read the value
+ try: # Permissions are needed to read the value
return self.usb_dev.serial_number
except:
pass
@@ -490,18 +523,26 @@ class CPboard:
def get_disks(self):
if self.usb_dev:
- portstr = ':' + '.'.join(map(str, self.usb_dev.port_numbers)) + ':'
- return ["/dev/disk/by-path/" + disk for disk in os.listdir("/dev/disk/by-path") if portstr in disk]
+ portstr = ":" + ".".join(map(str, self.usb_dev.port_numbers)) + ":"
+ return [
+ "/dev/disk/by-path/" + disk
+ for disk in os.listdir("/dev/disk/by-path")
+ if portstr in disk
+ ]
serial = self.serial_number
if not serial:
raise RuntimeError("Serial number not found for: " + self.device)
- return ["/dev/disk/by-id/" + disk for disk in os.listdir("/dev/disk/by-id") if serial in disk]
+ return [
+ "/dev/disk/by-id/" + disk
+ for disk in os.listdir("/dev/disk/by-id")
+ if serial in disk
+ ]
@property
def disk(self):
disks = self.get_disks()
- part = [part for part in disks if 'part1' in part]
+ part = [part for part in disks if "part1" in part]
if not part:
raise RuntimeError("Disk not found for: " + self.device)
@@ -513,13 +554,13 @@ class CPboard:
def execfile_disk(self, filename):
with self.disk as disk:
- disk.copy(filename, 'code.py')
+ disk.copy(filename, "code.py")
with self.repl as repl:
try:
output = repl.run()
except OSError as e:
- raise CPboardError('timeout', e)
+ raise CPboardError("timeout", e)
except RuntimeError:
if self.repl.safe_mode:
raise PyboardError("Can't run in safe mode")
@@ -529,10 +570,10 @@ class CPboard:
return output
def execfile(self, filename, timeout=10):
- if os.environ.get('CPBOARD_EXEC_MODE') == 'disk':
+ if os.environ.get("CPBOARD_EXEC_MODE") == "disk":
return self.execfile_disk(filename)
else:
- with open(filename, 'rb') as f:
+ with open(filename, "rb") as f:
pyfile = f.read()
return self.exec(pyfile, timeout=timeout)
@@ -540,11 +581,14 @@ class CPboard:
# Implement just enough to make tests/run-tests work
PyboardError = CPboardError
+
class Pyboard:
- def __init__(self, device, baudrate=115200, user='micro', password='python', wait=0):
+ def __init__(
+ self, device, baudrate=115200, user="micro", password="python", wait=0
+ ):
self.board = CPboard.from_try_all(device, baudrate=baudrate, wait=wait)
with self.board.disk as disk:
- disk.copy('skip_if.py')
+ disk.copy("skip_if.py")
def close(self):
self.board.close()
@@ -558,12 +602,13 @@ class Pyboard:
def eval_namedtuple(board, command):
from collections import namedtuple
+
s = board.exec("print(%s)" % command)
s = s.decode().strip()
- items = [key.split('=') for key in s[1:-1].split(', ')]
+ items = [key.split("=") for key in s[1:-1].split(", ")]
keys = [item[0] for item in items]
vals = [item[1] for item in items]
- nt = namedtuple('eval', keys)
+ nt = namedtuple("eval", keys)
res = nt(*[eval(val) for val in vals])
return res
@@ -571,14 +616,16 @@ def eval_namedtuple(board, command):
def os_uname(board):
return eval_namedtuple(board, "__import__('os').uname()")
+
def print_verbose(cargs, *args, **kwargs):
if cargs.verbose:
print(*args, flush=True, **kwargs)
+
def upload(args):
try:
board = CPboard.from_build_name_bootloader(args.board)
- print_verbose(args, 'Board is already in the bootloader')
+ print_verbose(args, "Board is already in the bootloader")
except (ValueError, RuntimeError):
board = CPboard.from_try_all(args.board)
@@ -586,29 +633,32 @@ def upload(args):
if not (args.quiet or board.bootloader):
board.open()
- print('Current version:', os_uname(board).version, flush=True)
+ print("Current version:", os_uname(board).version, flush=True)
if not board.bootloader:
- print_verbose(args, 'Reset to bootloader...', end='')
- board.reset_to_bootloader(repl=True) # Feather M0 Express doesn't respond to 1200 baud
+ print_verbose(args, "Reset to bootloader...", end="")
+ board.reset_to_bootloader(
+ repl=True
+ ) # Feather M0 Express doesn't respond to 1200 baud
time.sleep(5)
- print_verbose(args, 'done')
+ print_verbose(args, "done")
- print_verbose(args, 'Bootloader:', board.firmware.info)
+ print_verbose(args, "Bootloader:", board.firmware.info)
- print_verbose(args, 'Upload firmware...', end='')
+ print_verbose(args, "Upload firmware...", end="")
board.firmware.upload(args.firmware)
- print_verbose(args, 'done')
+ print_verbose(args, "done")
- print_verbose(args, 'Wait for board...', end='')
+ print_verbose(args, "Wait for board...", end="")
time.sleep(5)
- print_verbose(args, 'done')
+ print_verbose(args, "done")
if not args.quiet:
if board.bootloader:
board = CPboard.from_try_all(args.board)
board.open(wait=10)
- print('New version:', os_uname(board).version, flush=True)
+ print("New version:", os_uname(board).version, flush=True)
+
def print_error_exit(args, e):
if args.debug:
@@ -617,16 +667,20 @@ def print_error_exit(args, e):
print(e, file=sys.stderr)
sys.exit(1)
+
def main():
import argparse
- cmd_parser = argparse.ArgumentParser(description='Circuit Python Board Tool')
- cmd_parser.add_argument('board', help='build_name, vid:pid or /dev/tty')
- cmd_parser.add_argument('-f', '--firmware', help='upload UF2 firmware file')
- cmd_parser.add_argument('-c', '--command', help='program passed in as string')
- cmd_parser.add_argument('--tty', action='store_true', help='print tty')
- cmd_parser.add_argument('--verbose', '-v', action='count', default=0, help='be verbose')
- cmd_parser.add_argument('-q', '--quiet', action='store_true', help='be quiet')
- cmd_parser.add_argument('--debug', action='store_true', help='raise exceptions')
+
+ cmd_parser = argparse.ArgumentParser(description="Circuit Python Board Tool")
+ cmd_parser.add_argument("board", help="build_name, vid:pid or /dev/tty")
+ cmd_parser.add_argument("-f", "--firmware", help="upload UF2 firmware file")
+ cmd_parser.add_argument("-c", "--command", help="program passed in as string")
+ cmd_parser.add_argument("--tty", action="store_true", help="print tty")
+ cmd_parser.add_argument(
+ "--verbose", "-v", action="count", default=0, help="be verbose"
+ )
+ cmd_parser.add_argument("-q", "--quiet", action="store_true", help="be quiet")
+ cmd_parser.add_argument("--debug", action="store_true", help="raise exceptions")
args = cmd_parser.parse_args()
if args.quiet:
@@ -648,9 +702,9 @@ def main():
raise
if args.verbose:
- exec_mode = os.environ.get('CPBOARD_EXEC_MODE')
+ exec_mode = os.environ.get("CPBOARD_EXEC_MODE")
if exec_mode:
- print('CPBOARD_EXEC_MODE =', exec_mode)
+ print("CPBOARD_EXEC_MODE =", exec_mode)
# Make sure we can open serial
try:
@@ -667,18 +721,21 @@ def main():
print(b.eval(args.command))
else:
with board as b:
- print('Device: ', end='')
+ print("Device: ", end="")
if b.usb_dev:
- print('%04x:%04x on ' % (b.usb_dev.idVendor, b.usb_dev.idProduct), end='')
+ print(
+ "%04x:%04x on " % (b.usb_dev.idVendor, b.usb_dev.idProduct), end=""
+ )
print(b.device)
- print('Serial number:', b.serial_number)
+ print("Serial number:", b.serial_number)
uname = os_uname(b)
- print('os.uname:')
- print(' sysname:', uname.sysname)
- print(' nodename:', uname.nodename)
- print(' release:', uname.release)
- print(' version:', uname.version)
- print(' machine:', uname.machine)
+ print("os.uname:")
+ print(" sysname:", uname.sysname)
+ print(" nodename:", uname.nodename)
+ print(" release:", uname.release)
+ print(" version:", uname.version)
+ print(" machine:", uname.machine)
+
if __name__ == "__main__":
main()
diff --git a/tools/dfu.py b/tools/dfu.py
index 54b602438..dd6019235 100755..100644
--- a/tools/dfu.py
+++ b/tools/dfu.py
@@ -1,6 +1,7 @@
#!/usr/bin/python
# Written by Antonio Galea - 2010/11/18
+# Updated for DFU 1.1 by Sean Cross - 2020/03/31
# Distributed under Gnu LGPL 3.0
# see http://www.gnu.org/licenses/lgpl-3.0.txt
@@ -23,30 +24,7 @@ def parse(file,dump_images=False):
print ('File: "%s"' % file)
data = open(file,'rb').read()
crc = compute_crc(data[:-4])
- prefix, data = consume('<5sBIB',data,'signature version size targets')
- print ('%(signature)s v%(version)d, image size: %(size)d, targets: %(targets)d' % prefix)
- for t in range(prefix['targets']):
- tprefix, data = consume('<6sBI255s2I',data,'signature altsetting named name size elements')
- tprefix['num'] = t
- if tprefix['named']:
- tprefix['name'] = cstring(tprefix['name'])
- else:
- tprefix['name'] = ''
- print ('%(signature)s %(num)d, alt setting: %(altsetting)s, name: "%(name)s", size: %(size)d, elements: %(elements)d' % tprefix)
- tsize = tprefix['size']
- target, data = data[:tsize], data[tsize:]
- for e in range(tprefix['elements']):
- eprefix, target = consume('<2I',target,'address size')
- eprefix['num'] = e
- print (' %(num)d, address: 0x%(address)08x, size: %(size)d' % eprefix)
- esize = eprefix['size']
- image, target = target[:esize], target[esize:]
- if dump_images:
- out = '%s.target%d.image%d.bin' % (file,t,e)
- open(out,'wb').write(image)
- print (' DUMPED IMAGE TO "%s"' % out)
- if len(target):
- print ("target %d: PARSE ERROR" % t)
+ data = data[len(data)-16:]
suffix = named(struct.unpack('<4H3sBI',data[:16]),'device product vendor dfu ufd len crc')
print ('usb: %(vendor)04x:%(product)04x, device: 0x%(device)04x, dfu: 0x%(dfu)04x, %(ufd)s, %(len)d, 0x%(crc)08x' % suffix)
if crc != suffix['crc']:
@@ -55,53 +33,49 @@ def parse(file,dump_images=False):
if data:
print ("PARSE ERROR")
-def build(file,targets,device=DEFAULT_DEVICE):
- data = b''
- for t,target in enumerate(targets):
- tdata = b''
- for image in target:
- tdata += struct.pack('<2I',image['address'],len(image['data']))+image['data']
- tdata = struct.pack('<6sBI255s2I',b'Target',0,1, b'ST...',len(tdata),len(target)) + tdata
- data += tdata
- data = struct.pack('<5sBIB',b'DfuSe',1,len(data)+11,len(targets)) + data
+def build(file,data,device=DEFAULT_DEVICE):
+ # Parse the VID and PID from the `device` argument
v,d=map(lambda x: int(x,0) & 0xFFFF, device.split(':',1))
- data += struct.pack('<4H3sB',0,d,v,0x011a,b'UFD',16)
+
+ # Generate the DFU suffix, consisting of these fields:
+ # Field name | Length | Description
+ # ================+=========+================================
+ # bcdDevice | 2 | The release number of this firmware (0xffff - don't care)
+ # idProduct | 2 | PID of this device
+ # idVendor | 2 | VID of this device
+ # bcdDFU | 2 | Version of this DFU spec (0x01 0x00)
+ # ucDfuSignature | 3 | The characters 'DFU', printed in reverse order
+ # bLength | 1 | The length of this suffix (16 bytes)
+ # dwCRC | 4 | A CRC32 of the data, including this suffix
+ data += struct.pack('<4H3sB',0xffff,d,v,0x0100,b'UFD',16)
crc = compute_crc(data)
+ # Append the CRC32 of the entire block
data += struct.pack('<I',crc)
open(file,'wb').write(data)
if __name__=="__main__":
usage = """
%prog [-d|--dump] infile.dfu
-%prog {-b|--build} address:file.bin [-b address:file.bin ...] [{-D|--device}=vendor:device] outfile.dfu"""
+%prog {-b|--build} file.bin [{-D|--device}=vendor:device] outfile.dfu"""
parser = OptionParser(usage=usage)
- parser.add_option("-b", "--build", action="append", dest="binfiles",
- help="build a DFU file from given BINFILES", metavar="BINFILES")
+ parser.add_option("-b", "--build", action="store", dest="binfile",
+ help="build a DFU file from given BINFILE", metavar="BINFILE")
parser.add_option("-D", "--device", action="store", dest="device",
help="build for DEVICE, defaults to %s" % DEFAULT_DEVICE, metavar="DEVICE")
parser.add_option("-d", "--dump", action="store_true", dest="dump_images",
default=False, help="dump contained images to current directory")
(options, args) = parser.parse_args()
- if options.binfiles and len(args)==1:
- target = []
- for arg in options.binfiles:
- try:
- address,binfile = arg.split(':',1)
- except ValueError:
- print ("Address:file couple '%s' invalid." % arg)
- sys.exit(1)
- try:
- address = int(address,0) & 0xFFFFFFFF
- except ValueError:
- print ("Address %s invalid." % address)
- sys.exit(1)
- if not os.path.isfile(binfile):
- print ("Unreadable file '%s'." % binfile)
- sys.exit(1)
- target.append({ 'address': address, 'data': open(binfile,'rb').read() })
+ if options.binfile and len(args)==1:
+ binfile = options.binfile
+ if not os.path.isfile(binfile):
+ print ("Unreadable file '%s'." % binfile)
+ sys.exit(1)
+ target = open(binfile,'rb').read()
outfile = args[0]
device = DEFAULT_DEVICE
+ # If a device is specified, parse the pair into a VID:PID pair
+ # in order to validate them.
if options.device:
device=options.device
try:
@@ -109,7 +83,7 @@ if __name__=="__main__":
except:
print ("Invalid device '%s'." % device)
sys.exit(1)
- build(outfile,[target],device)
+ build(outfile,target,device)
elif len(args)==1:
infile = args[0]
if not os.path.isfile(infile):
diff --git a/tools/extract_pyi.py b/tools/extract_pyi.py
new file mode 100644
index 000000000..d749d202b
--- /dev/null
+++ b/tools/extract_pyi.py
@@ -0,0 +1,83 @@
+import os
+import sys
+import astroid
+import traceback
+
+top_level = sys.argv[1].strip("/")
+stub_directory = sys.argv[2]
+
+def convert_folder(top_level, stub_directory):
+ ok = 0
+ total = 0
+ filenames = sorted(os.listdir(top_level))
+ pyi_lines = []
+ for filename in filenames:
+ full_path = os.path.join(top_level, filename)
+ file_lines = []
+ if os.path.isdir(full_path):
+ mok, mtotal = convert_folder(full_path, os.path.join(stub_directory, filename))
+ ok += mok
+ total += mtotal
+ elif filename.endswith(".c"):
+ with open(full_path, "r") as f:
+ for line in f:
+ if line.startswith("//|"):
+ if line[3] == " ":
+ line = line[4:]
+ elif line[3] == "\n":
+ line = line[3:]
+ else:
+ continue
+ file_lines.append(line)
+ elif filename.endswith(".pyi"):
+ with open(full_path, "r") as f:
+ file_lines.extend(f.readlines())
+
+ # Always put the contents from an __init__ first.
+ if filename.startswith("__init__."):
+ pyi_lines = file_lines + pyi_lines
+ else:
+ pyi_lines.extend(file_lines)
+
+ if not pyi_lines:
+ return ok, total
+
+ stub_filename = os.path.join(stub_directory, "__init__.pyi")
+ print(stub_filename)
+ stub_contents = "".join(pyi_lines)
+ os.makedirs(stub_directory, exist_ok=True)
+ with open(stub_filename, "w") as f:
+ f.write(stub_contents)
+
+ # Validate that the module is a parseable stub.
+ total += 1
+ try:
+ tree = astroid.parse(stub_contents)
+ for i in tree.body:
+ if 'name' in i.__dict__:
+ print(i.__dict__['name'])
+ for j in i.body:
+ if isinstance(j, astroid.scoped_nodes.FunctionDef):
+ if None in j.args.__dict__['annotations']:
+ print(f"Missing parameter type: {j.__dict__['name']} on line {j.__dict__['lineno']}\n")
+ if j.returns:
+ if 'Any' in j.returns.__dict__.values():
+ print(f"Missing return type: {j.__dict__['name']} on line {j.__dict__['lineno']}")
+ elif isinstance(j, astroid.node_classes.AnnAssign):
+ if 'name' in j.__dict__['annotation'].__dict__:
+ if j.__dict__['annotation'].__dict__['name'] == 'Any':
+ print(f"missing attribute type on line {j.__dict__['lineno']}")
+
+ ok += 1
+ except astroid.exceptions.AstroidSyntaxError as e:
+ e = e.__cause__
+ traceback.print_exception(type(e), e, e.__traceback__)
+ print()
+ return ok, total
+
+ok, total = convert_folder(top_level, stub_directory)
+
+print(f"{ok} ok out of {total}")
+
+if ok != total:
+ sys.exit(total - ok)
diff --git a/tools/hid_report_descriptors.py b/tools/hid_report_descriptors.py
index 92c962d59..07ed26744 100644
--- a/tools/hid_report_descriptors.py
+++ b/tools/hid_report_descriptors.py
@@ -68,9 +68,9 @@ def keyboard_hid_descriptor(report_id):
0x81, 0x02, # Input (Data, Variable, Absolute)
0x81, 0x01, # Input (Constant)
0x19, 0x00, # Usage Minimum (0)
- 0x29, 101, # Usage Maximum (101)
+ 0x29, 0xDD, # Usage Maximum (221)
0x15, 0x00, # Logical Minimum (0)
- 0x25, 101, # Logical Maximum (101)
+ 0x25, 0xDD, # Logical Maximum (221)
0x75, 0x08, # Report Size (8)
0x95, 0x06, # Report Count (6)
0x81, 0x00, # Input (Data, Array)
diff --git a/tools/join_bins.py b/tools/join_bins.py
new file mode 100644
index 000000000..cb73aaabb
--- /dev/null
+++ b/tools/join_bins.py
@@ -0,0 +1,20 @@
+import sys
+
+output_filename = sys.argv[1]
+input_filenames = {}
+i = 2
+while i < len(sys.argv):
+ offset = int(sys.argv[i], 16)
+ filename = sys.argv[i + 1]
+ input_filenames[offset] = filename
+ i += 2
+
+with open(output_filename, "wb") as output_file:
+ offsets = sorted(input_filenames.keys())
+ for offset in offsets:
+ input_filename = input_filenames[offset]
+ # Fill with ones to save NOR flash.
+ while output_file.tell() < offset:
+ output_file.write(b"\xff")
+ with open(input_filename, "rb") as input_file:
+ output_file.write(input_file.read())
diff --git a/tools/mksdiodata.py b/tools/mksdiodata.py
new file mode 100755
index 000000000..0cce3819f
--- /dev/null
+++ b/tools/mksdiodata.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python3
+
+def defines(name, function):
+ print(f'pin_function_t {name} [] = {{')
+ for instance in (0, 1):
+ for port in 'ABCD':
+ for idx in range(32):
+ pin = f'P{port}{idx:02d}'
+ pinmux = f'PINMUX_{pin}I_SDHC{instance}_{function}'
+ print(f'''\
+#if defined({pinmux}) && ! defined(IGNORE_PIN_{pin})
+ {{&pin_{pin}, {instance}, PIN_{pin}, {pinmux} & 0xffff}},
+#endif''')
+ print(f'{{NULL, 0, 0}}')
+ print(f'}};')
+ print()
+
+print('''\
+#include <stdint.h>
+#include "py/obj.h"
+#include "sam.h"
+#include "samd/pins.h"
+#include "mpconfigport.h"
+#include "atmel_start_pins.h"
+#include "hal/include/hal_gpio.h"
+
+typedef struct {
+ const mcu_pin_obj_t *obj;
+ uint8_t instance;
+ uint8_t pin;
+ uint16_t function;
+} pin_function_t;
+''')
+
+defines('sdio_ck', 'SDCK')
+defines('sdio_cmd', 'SDCMD')
+defines('sdio_dat0', 'SDDAT0')
+defines('sdio_dat1', 'SDDAT1')
+defines('sdio_dat2', 'SDDAT2')
+defines('sdio_dat3', 'SDDAT3')
diff --git a/tools/pyboard.py b/tools/pyboard.py
index 16ee41f70..bb5642bd7 100755
--- a/tools/pyboard.py
+++ b/tools/pyboard.py
@@ -191,7 +191,7 @@ class ProcessToSerial:
class ProcessPtyToTerminal:
- """Execute a process which creates a PTY and prints slave PTY as
+ """Execute a process which creates a PTY and prints secondary PTY as
first line of its output, and emulate serial connection using
this PTY."""