You've already forked ivan-torvalds-GuitarPedal
forked from AllSpiceMirrors/torvalds-GuitarPedal
Drop copy_to_ram. It was always the lazy option - I didn't want to work out which functions actually needed to be in SRAM, so I put all of them there. Now that the audio core is marked, the linker script already does the right thing: it pulls '.time_critical*' out of flash, and everything else is cold enough to run from XIP. RAM before: 73132 text + 386972 bss = 460104 RAM after: 20504 data + 384652 bss = 405156 which is 54948 bytes back, and takes the headroom from 70K to 124K out of 520K. That matters more than it sounds: bss is already 384K, mostly the 64K analyzer ring and the 64K FFT buffer, so headroom is the thing that was going to run out first. Also add scripts/check-audio.py, run after every link. Everything in a '.time_critical.audio_*' section may only call into another one, and anything else fails the build. Right now there is nothing on the allowed list at all - the audio path doesn't call a single library function - which is the whole reason to add the check now rather than after it has rotted. The last one to go was the newlib 'lrintf()' a couple of commits ago. The one thing this cannot check is that the code still *works* from flash. cpu1 is unaffected by construction since all of it is in RAM, but cpu0 now takes XIP cache misses, and the tuner does an 8192-point FFT at 25Hz. I think the cache holds the inner loop fine. I have not proven it on hardware. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
79 lines
2.7 KiB
Python
Executable File
79 lines
2.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Check that nothing running on the audio core calls out of it.
|
|
#
|
|
# Everything marked with __audio_func() is compiled into its own
|
|
# '.time_critical.audio_<name>' section. Anything such a function calls
|
|
# has to be marked too - if it isn't, either it wants marking, or we have
|
|
# accidentally pulled a newlib or soft-float routine into the
|
|
# hard-realtime path.
|
|
#
|
|
# The linker merges all of those input sections into .data, so the
|
|
# section names only survive in the map file. That is fine: the macro
|
|
# builds the section name out of the function name, so the map is enough
|
|
# to recover the list.
|
|
#
|
|
# Called as: check-audio.py <map> <elf> [objdump]
|
|
#
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
# Calls allowed out of the audio sections. Keep this short, and say why.
|
|
ALLOWED = {
|
|
# (nothing yet - the audio path is entirely self-contained)
|
|
}
|
|
|
|
|
|
def main():
|
|
mapfile, elf = sys.argv[1], sys.argv[2]
|
|
objdump = (sys.argv[3] if len(sys.argv) > 3 and sys.argv[3]
|
|
else "arm-none-eabi-objdump")
|
|
|
|
with open(mapfile) as f:
|
|
audio = set(re.findall(r"^ \.time_critical\.audio_(\S+)$",
|
|
f.read(), re.M))
|
|
if not audio:
|
|
sys.exit("check-audio: no __audio_func() symbols in the map - "
|
|
"stale build?")
|
|
|
|
res = subprocess.run([objdump, "-d", "--no-show-raw-insn", elf],
|
|
capture_output=True, text=True)
|
|
if res.returncode:
|
|
sys.exit(f"check-audio: {objdump} failed:\n{res.stderr}")
|
|
|
|
func = re.compile(r"^[0-9a-f]+ <([^>]+)>:")
|
|
call = re.compile(r"\s(?:bl|blx)\s+[0-9a-f]+ <([^>+]+)")
|
|
cur, bad, seen = None, set(), set()
|
|
for line in res.stdout.splitlines():
|
|
m = func.match(line)
|
|
if m:
|
|
cur = m.group(1)
|
|
if cur in audio:
|
|
seen.add(cur)
|
|
continue
|
|
m = call.search(line)
|
|
if m and cur in audio:
|
|
callee = m.group(1)
|
|
if callee not in audio and callee not in ALLOWED:
|
|
bad.add((cur, callee))
|
|
|
|
# A marked function that vanished was fully inlined into its callers,
|
|
# which is fine - they are marked too.
|
|
if bad:
|
|
print(f"check-audio: {len(bad)} call(s) leaving the audio core:",
|
|
file=sys.stderr)
|
|
for caller, callee in sorted(bad):
|
|
print(f" {caller} -> {callee}", file=sys.stderr)
|
|
print("\nMark the callee with __audio_func(), or if it really has to "
|
|
"live\nout of line, add it to ALLOWED in this script with a "
|
|
"reason.", file=sys.stderr)
|
|
return 1
|
|
|
|
print(f"check-audio: {len(seen)} functions on the audio core, no calls out")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|