Fix .bin, .hex and .uf2 with new linker sections
Also, format perfbench output in table with reference timing from the host.
This commit is contained in:
parent
5bb8a7a7c6
commit
bdf592089a
@ -182,18 +182,20 @@ $(BUILD)/firmware.elf: $(OBJ) $(LD_FILES)
|
||||
$(STEPECHO) "LINK $@"
|
||||
$(Q)$(CC) -o $@ $(LDFLAGS) $(filter-out %.ld, $^) -Wl,--print-memory-usage -Wl,--start-group $(LIBS) -Wl,--end-group
|
||||
|
||||
# -R excludes sections from the output files.
|
||||
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
|
||||
$(STEPECHO) "Create $@"
|
||||
$(Q)$(OBJCOPY) -O binary -j .flash_config -j .ivt -j .text -j .ARM.exidx -j .data -j .itcm -j .dtcm_data $^ $@
|
||||
$(Q)$(OBJCOPY) -O binary -R .stack -R .dtcm_bss $^ $@
|
||||
|
||||
$(BUILD)/firmware.uf2: $(BUILD)/firmware.elf
|
||||
$(STEPECHO) "Create $@"
|
||||
$(Q)$(OBJCOPY) -O binary -j .text -j .ARM.exidx -j .data -j .itcm -j .dtcm_data $^ $@-binpart
|
||||
$(Q)$(OBJCOPY) -O binary -R .stack -R .dtcm_bss -R .ivt -R .flash_config $^ $@-binpart
|
||||
$(Q)$(PYTHON) $(TOP)/tools/uf2/utils/uf2conv.py -b $(BOOTLOADER_SIZE) -f MIMXRT10XX -c -o $@ $@-binpart
|
||||
$(Q)rm $@-binpart
|
||||
|
||||
# $(Q)rm $@-binpart
|
||||
|
||||
$(BUILD)/firmware.hex: $(BUILD)/firmware.elf
|
||||
$(Q)$(OBJCOPY) -O ihex -j .flash_config -j .ivt -j .text -j .ARM.exidx -j .data -j .itcm -j .dtcm_data $< $@
|
||||
$(Q)$(OBJCOPY) -O ihex -R .stack -R .dtcm_bss $< $@
|
||||
|
||||
include $(TOP)/py/mkrules.mk
|
||||
|
||||
|
@ -6,7 +6,7 @@ Boards can setup reserved flash with _ld_reserved_flash_size in board.ld. */
|
||||
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
code_size = 2M;
|
||||
code_size = _ld_flash_size >= 4M ? 2M : 1M;
|
||||
_ld_default_stack_size = 20K;
|
||||
|
||||
/* Default reserved flash to nothing. */
|
||||
@ -52,6 +52,20 @@ SECTIONS
|
||||
. = ALIGN(4);
|
||||
} > FLASH_IVT
|
||||
|
||||
/* Align for 256 ISR entries and place first in flash. Otherwise the UF2
|
||||
bootloader can't find it because it uses its own flash_config and ivt. */
|
||||
.isr_vector : ALIGN(4 * 256)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(4);
|
||||
} > ITCM AT> FLASH_FIRMWARE
|
||||
_ld_isr_destination = ADDR(.isr_vector);
|
||||
_ld_isr_flash_copy = LOADADDR(.isr_vector);
|
||||
_ld_isr_size = SIZEOF(.isr_vector);
|
||||
/* Used by the bootloader to start user code. */
|
||||
__VECTOR_TABLE = LOADADDR(.isr_vector);
|
||||
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
@ -146,19 +160,6 @@ SECTIONS
|
||||
_ld_itcm_flash_copy = LOADADDR(.itcm);
|
||||
_ld_itcm_size = SIZEOF(.itcm);
|
||||
|
||||
/* Align for 256 ISR entries */
|
||||
.isr_vector : ALIGN(4 * 256)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(4);
|
||||
} > ITCM AT> FLASH_FIRMWARE
|
||||
_ld_isr_destination = ADDR(.isr_vector);
|
||||
_ld_isr_flash_copy = LOADADDR(.isr_vector);
|
||||
_ld_isr_size = SIZEOF(.isr_vector);
|
||||
/* Used by the bootloader to start user code. */
|
||||
__VECTOR_TABLE = LOADADDR(.isr_vector);
|
||||
|
||||
.dtcm_data :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
|
@ -33,7 +33,7 @@
|
||||
#define PLACE_IN_DTCM_DATA(name) name __attribute__((section(".dtcm_data." #name)))
|
||||
#define PLACE_IN_DTCM_BSS(name) name __attribute__((section(".dtcm_bss." #name)))
|
||||
// Don't inline ITCM functions because that may pull them out of ITCM into other sections.
|
||||
#define PLACE_IN_ITCM(name) __attribute__((section(".itcm." #name),noinline)) name
|
||||
#define PLACE_IN_ITCM(name) __attribute__((section(".itcm." #name),noinline,aligned(4))) name
|
||||
#else
|
||||
#define PLACE_IN_DTCM_DATA(name) name
|
||||
#define PLACE_IN_DTCM_BSS(name) name
|
||||
|
@ -4,7 +4,7 @@ def bm_run(N, M):
|
||||
except ImportError:
|
||||
import time
|
||||
|
||||
ticks_us = lambda: int(time.monotonic_ns() / 1000)
|
||||
ticks_us = lambda: int(time.monotonic_ns() // 1000)
|
||||
ticks_diff = lambda a, b: a - b
|
||||
|
||||
# Pick sensible parameters given N, M
|
||||
|
@ -7,8 +7,12 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import argparse
|
||||
from glob import glob
|
||||
from rich.live import Live
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
sys.path.append("../tools")
|
||||
import pyboard
|
||||
@ -37,7 +41,7 @@ def compute_stats(lst):
|
||||
return avg, var**0.5
|
||||
|
||||
|
||||
def run_script_on_target(target, script):
|
||||
def run_script_on_target(target, script, run_command=None):
|
||||
output = b""
|
||||
err = None
|
||||
|
||||
@ -45,50 +49,72 @@ def run_script_on_target(target, script):
|
||||
# Run via pyboard interface
|
||||
try:
|
||||
target.enter_raw_repl()
|
||||
start_ts = time.monotonic_ns()
|
||||
output = target.exec_(script)
|
||||
if run_command:
|
||||
start_ts = time.monotonic_ns()
|
||||
output = target.exec_(run_command)
|
||||
end_ts = time.monotonic_ns()
|
||||
except pyboard.PyboardError as er:
|
||||
end_ts = time.monotonic_ns()
|
||||
err = er
|
||||
finally:
|
||||
target.exit_raw_repl()
|
||||
else:
|
||||
# Run local executable
|
||||
try:
|
||||
if run_command:
|
||||
script += run_command
|
||||
start_ts = time.monotonic_ns()
|
||||
p = subprocess.run(
|
||||
target, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, input=script
|
||||
)
|
||||
end_ts = time.monotonic_ns()
|
||||
output = p.stdout
|
||||
except subprocess.CalledProcessError as er:
|
||||
end_ts = time.monotonic_ns()
|
||||
err = er
|
||||
|
||||
return str(output.strip(), "ascii"), err
|
||||
return str(output.strip(), "ascii"), err, (end_ts - start_ts) // 1000
|
||||
|
||||
|
||||
def run_feature_test(target, test):
|
||||
with open("feature_check/" + test + ".py", "rb") as f:
|
||||
script = f.read()
|
||||
output, err = run_script_on_target(target, script)
|
||||
output, err, _ = run_script_on_target(target, script)
|
||||
if err is None:
|
||||
return output
|
||||
else:
|
||||
return "CRASH: %r" % err
|
||||
|
||||
|
||||
def run_benchmark_on_target(target, script):
|
||||
output, err = run_script_on_target(target, script)
|
||||
def run_benchmark_on_target(target, script, run_command=None):
|
||||
output, err, runtime_us = run_script_on_target(target, script, run_command)
|
||||
if err is None:
|
||||
time, norm, result = output.split(None, 2)
|
||||
try:
|
||||
return int(time), int(norm), result
|
||||
return int(time), int(norm), result, runtime_us
|
||||
except ValueError:
|
||||
return -1, -1, "CRASH: %r" % output
|
||||
return -1, -1, "CRASH: %r" % output, runtime_us
|
||||
else:
|
||||
return -1, -1, "CRASH: %r" % err
|
||||
return -1, -1, "CRASH: %r" % err, runtime_us
|
||||
|
||||
|
||||
def run_benchmarks(target, param_n, param_m, n_average, test_list):
|
||||
def run_benchmarks(console, target, param_n, param_m, n_average, test_list):
|
||||
skip_complex = run_feature_test(target, "complex") != "complex"
|
||||
skip_native = run_feature_test(target, "native_check") != "native"
|
||||
|
||||
table = Table(show_header=True)
|
||||
table.add_column("Test")
|
||||
table.add_column("Time", justify="right")
|
||||
table.add_column("Score", justify="right")
|
||||
table.add_column("Ref Time", justify="right")
|
||||
|
||||
live = Live(table, console=console)
|
||||
live.start()
|
||||
|
||||
for test_file in sorted(test_list):
|
||||
print(test_file + ": ", end="")
|
||||
# print(test_file + ": ", end="")
|
||||
|
||||
# Check if test should be skipped
|
||||
skip = (
|
||||
@ -99,6 +125,7 @@ def run_benchmarks(target, param_n, param_m, n_average, test_list):
|
||||
)
|
||||
if skip:
|
||||
print("skip")
|
||||
table.add_row(test_file, *(["skip"] * 6))
|
||||
continue
|
||||
|
||||
# Create test script
|
||||
@ -106,7 +133,7 @@ def run_benchmarks(target, param_n, param_m, n_average, test_list):
|
||||
test_script = f.read()
|
||||
with open(BENCH_SCRIPT_DIR + "benchrun.py", "rb") as f:
|
||||
test_script += f.read()
|
||||
test_script += b"bm_run(%u, %u)\n" % (param_n, param_m)
|
||||
bm_run = b"bm_run(%u, %u)\n" % (param_n, param_m)
|
||||
|
||||
# Write full test script if needed
|
||||
if 0:
|
||||
@ -115,12 +142,15 @@ def run_benchmarks(target, param_n, param_m, n_average, test_list):
|
||||
|
||||
# Run MicroPython a given number of times
|
||||
times = []
|
||||
runtimes = []
|
||||
scores = []
|
||||
error = None
|
||||
result_out = None
|
||||
for _ in range(n_average):
|
||||
time, norm, result = run_benchmark_on_target(target, test_script)
|
||||
if time < 0 or norm < 0:
|
||||
self_time, norm, result, runtime_us = run_benchmark_on_target(
|
||||
target, test_script, bm_run
|
||||
)
|
||||
if self_time < 0 or norm < 0:
|
||||
error = result
|
||||
break
|
||||
if result_out is None:
|
||||
@ -128,30 +158,43 @@ def run_benchmarks(target, param_n, param_m, n_average, test_list):
|
||||
elif result != result_out:
|
||||
error = "FAIL self"
|
||||
break
|
||||
times.append(time)
|
||||
scores.append(1e6 * norm / time)
|
||||
times.append(self_time)
|
||||
runtimes.append(runtime_us)
|
||||
scores.append(1e6 * norm / self_time)
|
||||
|
||||
# Check result against truth if needed
|
||||
if error is None and result_out != "None":
|
||||
_, _, result_exp = run_benchmark_on_target(PYTHON_TRUTH, test_script)
|
||||
_, _, result_exp, _ = run_benchmark_on_target(PYTHON_TRUTH, test_script, bm_run)
|
||||
if result_out != result_exp:
|
||||
error = "FAIL truth"
|
||||
|
||||
if error is not None:
|
||||
print(error)
|
||||
print(test_file, error)
|
||||
if error == "no matching params":
|
||||
table.add_row(test_file, *([None] * 3))
|
||||
else:
|
||||
table.add_row(test_file, *(["error"] * 3))
|
||||
else:
|
||||
t_avg, t_sd = compute_stats(times)
|
||||
r_avg, r_sd = compute_stats(runtimes)
|
||||
s_avg, s_sd = compute_stats(scores)
|
||||
print(
|
||||
"{:.2f} {:.4f} {:.2f} {:.4f}".format(
|
||||
t_avg, 100 * t_sd / t_avg, s_avg, 100 * s_sd / s_avg
|
||||
)
|
||||
# print(
|
||||
# "{:.2f} {:.4f} {:.2f} {:.4f} {:.2f} {:.4f}".format(
|
||||
# t_avg, 100 * t_sd / t_avg, s_avg, 100 * s_sd / s_avg, r_avg, 100 * r_sd / r_avg
|
||||
# )
|
||||
# )
|
||||
table.add_row(
|
||||
test_file,
|
||||
f"{t_avg:.2f}±{100 * t_sd / t_avg:.1f}%",
|
||||
f"{s_avg:.2f}±{100 * s_sd / s_avg:.1f}%",
|
||||
f"{r_avg:.2f}±{100 * r_sd / r_avg:.1f}%",
|
||||
)
|
||||
if 0:
|
||||
print(" times: ", times)
|
||||
print(" scores:", scores)
|
||||
|
||||
sys.stdout.flush()
|
||||
live.update(table, refresh=True)
|
||||
live.stop()
|
||||
|
||||
|
||||
def parse_output(filename):
|
||||
@ -268,9 +311,10 @@ def main():
|
||||
else:
|
||||
tests = sorted(args.files)
|
||||
|
||||
console = Console()
|
||||
print("N={} M={} n_average={}".format(N, M, n_average))
|
||||
|
||||
run_benchmarks(target, N, M, n_average, tests)
|
||||
run_benchmarks(console, target, N, M, n_average, tests)
|
||||
|
||||
if isinstance(target, pyboard.Pyboard):
|
||||
target.exit_raw_repl()
|
||||
|
@ -81,6 +81,7 @@ class REPL:
|
||||
else:
|
||||
timeout_count += 1
|
||||
if timeout is not None and timeout_count >= 100 * timeout:
|
||||
print("timeout")
|
||||
raise TimeoutError(110, "timeout waiting for", ending)
|
||||
time.sleep(0.01)
|
||||
return data
|
||||
@ -164,7 +165,10 @@ class Disk:
|
||||
self._path = mount[0][1]
|
||||
else:
|
||||
name = os.path.basename(dev)
|
||||
sh.pmount("-tvfat", dev, name, _timeout=10)
|
||||
try:
|
||||
sh.pmount("-tvfat", dev, name, _timeout=10)
|
||||
except sh.CommandNotFound:
|
||||
raise ValueError()
|
||||
self.mountpoint = "/media/" + name
|
||||
self._path = self.mountpoint
|
||||
|
||||
@ -516,7 +520,10 @@ class CPboard:
|
||||
if not part:
|
||||
return None
|
||||
|
||||
return Disk(part[0])
|
||||
try:
|
||||
return Disk(part[0])
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def firmware(self):
|
||||
@ -555,14 +562,17 @@ PyboardError = CPboardError
|
||||
class Pyboard:
|
||||
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 = self.board.disk
|
||||
if disk:
|
||||
with disk as open_disk:
|
||||
open_disk.copy("skip_if.py")
|
||||
|
||||
def close(self):
|
||||
self.board.close()
|
||||
|
||||
def enter_raw_repl(self):
|
||||
self.board.open()
|
||||
self.board.repl.reset()
|
||||
|
||||
def exit_raw_repl(self):
|
||||
self.close()
|
||||
@ -571,7 +581,12 @@ class Pyboard:
|
||||
return self.board.execfile(filename)
|
||||
|
||||
def exec_(self, command, data_consumer=None):
|
||||
output = self.board.exec(command, timeout=20000)
|
||||
try:
|
||||
output, error = self.board.repl.execute(command, timeout=20000, wait_for_response=True)
|
||||
except OSError as e:
|
||||
raise CPboardError("timeout", e)
|
||||
if error:
|
||||
raise CPboardError("exception", output, error)
|
||||
return output
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user