summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt4
-rwxr-xr-xbuild-aux/lint-bin40
-rwxr-xr-xbuild-aux/stack.c.gen248
-rw-r--r--cmd/sbc_harness/CMakeLists.txt1
-rw-r--r--libcr_ipc/CMakeLists.txt4
-rw-r--r--libcr_ipc/chan.c40
-rw-r--r--libcr_ipc/include/libcr_ipc/chan.h36
-rw-r--r--libcr_ipc/include/libcr_ipc/select.h98
-rw-r--r--libcr_ipc/select.c102
9 files changed, 397 insertions, 176 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f13226b..386b13c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,7 +6,7 @@
cmake_minimum_required(VERSION 3.30)
if (NOT PICO_PLATFORM)
- message(FATAL_ERROR "PICO_PLATFORM must be specified; use the GNUmakefile to set this")
+ message(FATAL_ERROR "PICO_PLATFORM must be specified; use the GNUmakefile to set this")
endif()
set(PICO_SDK_PATH "${CMAKE_SOURCE_DIR}/3rd-party/pico-sdk")
@@ -32,7 +32,7 @@ if ("-DNDEBUG" IN_LIST _build_type_flags)
endif()
function(_suppress_tinyusb_warnings)
- __suppress_tinyusb_warnings()
+ __suppress_tinyusb_warnings()
set_source_files_properties(
${PICO_TINYUSB_PATH}/src/device/usbd.c
PROPERTIES
diff --git a/build-aux/lint-bin b/build-aux/lint-bin
index ffc2a12..0b955de 100755
--- a/build-aux/lint-bin
+++ b/build-aux/lint-bin
@@ -18,6 +18,15 @@ shopt -s extglob
# Textual info:
# - ${elf%.elf}.dis : `objdump --section-headers ${elf}; objdump --disassemble ${elf}; picotool coprodis --quiet ${elf}`
# - ${elf}.map : `ld --print-map` info
+# - ${elf%.elf}_stack.c : `stack.c.gen`
+
+RED=$(tput setaf 1)
+RESET=$(tput sgr0)
+
+err() {
+ printf "${RED}%s${RESET}: %s\n" "$1" "$2" >&2
+ #r=1
+}
# Input is `ld --print-map` format.
#
@@ -27,6 +36,14 @@ objdump_globals() {
sed -E -n '/^ \.t?(data|bss)\./{ / 0x/{ p; D; }; N; s/\n/ /; p; }' <"$1"
}
+readelf_funcs() {
+ local in_elffile
+ in_elffile=$1
+
+ readelf --syms --wide -- "$in_elffile" |
+ awk '$4 == "FUNC" { print $8 }'
+}
+
lint_globals() {
local in_mapfile
in_mapfile=$1
@@ -72,14 +89,37 @@ lint_globals() {
} | column -t
}
+lint_stack() {
+ local in_elffile
+ in_elffile=$1
+
+ IFS=''
+ while read -r line; do
+ func=${line#$'\t'}
+ if [[ $line == $'\t'* ]]; then
+ err "$in_elffile" "function in binary but not _stack.c: ${func}"
+ else
+ err "$in_elffile" "function in _stack.c but not binary: ${func}"
+ fi
+ done < <(
+ comm -3 \
+ <(sed -En 's/^included: (.*:)?//p' "${in_elffile%.elf}_stack.c" | sort -u) \
+ <(readelf_funcs "$in_elffile" | sed 's/\.part\.[0-9]*$//' | sort -u))
+}
+
main() {
+ r=0
+
local elf
for elf in "$@"; do
{
echo 'Global variables:'
lint_globals "${elf}.map" | sed 's/^/ /'
} > "${elf%.elf}.lint.globals"
+ lint_stack "$elf" &> "${elf%.elf}.lint.stack"
done
+
+ return $r
}
main "$@"
diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen
index 2acce60..edc7bae 100755
--- a/build-aux/stack.c.gen
+++ b/build-aux/stack.c.gen
@@ -142,6 +142,8 @@ class AnalyzeResult(typing.NamedTuple):
missing: set[str]
dynamic: set[str]
+ included_funcs: set[str]
+
class Application(typing.Protocol):
def extra_nodes(self) -> typing.Collection[Node]: ...
@@ -162,7 +164,7 @@ def analyze(
+ r"(?P<location>[^\n]+:[0-9]+:[0-9]+)\n"
+ r"(?P<nstatic>[0-9]+) bytes \((?P<usage_kind>static|dynamic|dynamic,bounded)\)\n"
+ r"(?P<ndynamic>[0-9]+) dynamic objects"
- + r"(?:\n.*)?",
+ + r"(?:\n.*)*",
flags=re.MULTILINE,
)
@@ -246,6 +248,7 @@ def analyze(
missing: set[str] = set()
dynamic: set[str] = set()
+ included_funcs: set[str] = set()
dbg = False
@@ -288,6 +291,7 @@ def analyze(
print(f"//dbg: {funcname}\t{node.nstatic}")
if node.usage_kind == "dynamic" or node.ndynamic > 0:
dynamic.add(app.location_xform(funcname))
+ included_funcs.add(funcname)
return node.nstatic + max(
[
0,
@@ -312,7 +316,9 @@ def analyze(
nsum += cnt * n
groups[grp_name] = AnalyzeResultGroup(rows=rows, nmax=nmax, nsum=nsum)
- return AnalyzeResult(groups=groups, missing=missing, dynamic=dynamic)
+ return AnalyzeResult(
+ groups=groups, missing=missing, dynamic=dynamic, included_funcs=included_funcs
+ )
################################################################################
@@ -345,6 +351,7 @@ re_call_other = re.compile(r"(?P<func>[^(]+)\(.*")
class Plugin(typing.Protocol):
+ def is_intrhandler(self, name: str) -> bool: ...
def extra_nodes(self) -> typing.Collection[Node]: ...
def indirect_callees(
self, loc: str, line: str
@@ -398,6 +405,14 @@ class PluginApplication:
class AppPlugin:
+ def is_intrhandler(self, name: str) -> bool:
+ return name in [
+ "rp2040_hwtimer_intrhandler",
+ "_cr_gdb_intrhandler",
+ "hostclock_handle_sig_alarm",
+ "hostnet_handle_sig_io",
+ ]
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
@@ -460,6 +475,9 @@ class LibObjPlugin:
objcalls[method_name].add(impl_name + "_" + method_name)
self.objcalls = objcalls
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
@@ -470,7 +488,7 @@ class LibObjPlugin:
return None
if m := re_call_objcall.fullmatch(line):
if m.group("meth") in self.objcalls:
- return sorted(self.objcalls[m.group("meth")]), True
+ return sorted(self.objcalls[m.group("meth")]), False
return [
f"__indirect_call:{m.group('obj')}.vtable->{m.group('meth')}"
], False
@@ -481,6 +499,14 @@ class LibObjPlugin:
class LibHWPlugin:
+ pico_platform: str
+
+ def __init__(self, arg_pico_platform: str) -> None:
+ self.pico_platform = arg_pico_platform
+
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
@@ -488,11 +514,15 @@ class LibHWPlugin:
if "/3rd-party/" in loc:
return None
if "trigger->cb(trigger->cb_arg)" in line:
- return [
+ ret = [
"alarmclock_sleep_intrhandler",
- "w5500_tcp_alarm_handler",
- "w5500_udp_alarm_handler",
- ], True
+ ]
+ if self.pico_platform == "rp2040":
+ ret += [
+ "w5500_tcp_alarm_handler",
+ "w5500_udp_alarm_handler",
+ ]
+ return ret, False
return None
def skip_call(self, chain: list[str], call: str) -> bool:
@@ -500,17 +530,20 @@ class LibHWPlugin:
class LibCRIPCPlugin:
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None:
if "/3rd-party/" in loc:
return None
- if "/chan.h:" in loc and "front->dequeue(" in line:
+ if "/chan.c:" in loc and "front->dequeue(" in line:
return [
"_cr_chan_dequeue",
"_cr_select_dequeue",
- ], True
+ ], False
return None
def skip_call(self, chain: list[str], call: str) -> bool:
@@ -597,6 +630,9 @@ class Lib9PPlugin:
return self._CONFIG_9P_NUM_SOCKS * self.CONFIG_9P_SRV_MAX_REQS
return 1
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
@@ -625,10 +661,23 @@ class Lib9PPlugin:
for c in chain[-self.CONFIG_9P_SRV_MAX_DEPTH :]
):
return True
+ re_msg_meth = re.compile(
+ r"^lib9p_(?P<grp>[TR])msg_(?P<meth>validate|unmarshal|marshal)$"
+ )
+ wrapper = next((c for c in chain if re_msg_meth.match(c)), None)
+ if wrapper:
+ m = re_msg_meth.match(wrapper)
+ assert m
+ deny = ":" + m.group("meth") + "_" + ("R" if m.group("grp") == "T" else "T")
+ if deny in call:
+ return True
return False
class LibMiscPlugin:
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
@@ -648,9 +697,57 @@ class LibMiscPlugin:
class PicoSDKPlugin:
app_gpio_handlers: typing.Collection[str]
+ app_init_array: typing.Collection[str]
+ app_preinit_array: typing.Collection[str]
- def __init__(self, app_gpio_handlers: typing.Collection[str]) -> None:
+ def __init__(
+ self,
+ *,
+ app_gpio_handlers: typing.Collection[str],
+ app_init_array: typing.Collection[str],
+ ) -> None:
self.app_gpio_handlers = app_gpio_handlers
+ self.app_init_array = app_init_array
+
+ # git grep '^PICO_RUNTIME_INIT_FUNC\S*('
+ self.app_preinit_array = [
+ # "runtime_init_mutex", # pico_mutex
+ # "runtime_init_default_alarm_pool", # pico_time
+ # "runtime_init_boot_locks_reset", # hardware_boot_lock
+ "runtime_init_per_core_irq_priorities", # hardware_irq
+ # "spinlock_set_extexclall", # hardware_sync_spin_lock
+ "__aeabi_bits_init", # pico_bit_ops
+ # "runtime_init_bootrom_locking_enable", # pico_bootrom, rp2350-only
+ # "runtime_init_pre_core_tls_setup", # pico_clib_interface, picolibc-only
+ # "__aeabi_double_init", # pico_double
+ # "__aeabi_float_init", # pico_float
+ "__aeabi_mem_init", # pico_mem_ops
+ "first_per_core_initializer", # pico_runtime
+ # pico_runtime_init
+ # "runtime_init_bootrom_reset", # rp2350-only
+ # "runtime_init_per_core_bootrom_reset", # rp2350-only
+ # "runtime_init_per_core_h3_irq_registers", # rp2350-only
+ "runtime_init_early_resets",
+ "runtime_init_usb_power_down",
+ # "runtime_init_per_core_enable_coprocessors", # PICO_RUNTIME_SKIP_INIT_PER_CORE_ENABLE_COPROCESSORS
+ "runtime_init_clocks",
+ "runtime_init_post_clock_resets",
+ "runtime_init_rp2040_gpio_ie_disable",
+ "runtime_init_spin_locks_reset",
+ "runtime_init_install_ram_vector_table",
+ ]
+
+ def is_intrhandler(self, name: str) -> bool:
+ return name in [
+ "gpio_default_irq_handler",
+ "isr_invalid",
+ "isr_nmi",
+ "isr_hardfault",
+ "isr_svcall",
+ "isr_pendsv",
+ "isr_systick",
+ *[f"isr_irq{n}" for n in range(32)],
+ ]
def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None:
if "/3rd-party/pico-sdk/" not in loc or "/3rd-party/pico-sdk/lib/" in loc:
@@ -672,7 +769,7 @@ class PicoSDKPlugin:
if "/flash.c:" in loc and "boot2_copyout" in line:
return ["_stage2_boot"], False
if "/gpio.c:" in loc and call == "callback":
- return sorted(self.app_gpio_handlers), True
+ return sorted(self.app_gpio_handlers), False
if "/printf.c:" in loc:
if call == "out":
return [
@@ -697,6 +794,11 @@ class PicoSDKPlugin:
return ["stdio_uart_out_flush"], False
case "in_chars":
return ["stdio_uart_in_chars"], False
+ if "/newlib_interface.c:" in loc:
+ if line == "*p)();":
+ return sorted(self.app_init_array), False
+ if "/pico_runtime/runtime.c:" in loc:
+ return sorted(self.app_preinit_array), False
return None
def skip_call(self, chain: list[str], call: str) -> bool:
@@ -717,18 +819,35 @@ class PicoSDKPlugin:
return False
def extra_nodes(self) -> typing.Collection[Node]:
+ ret = []
+
# src/rp2_common/hardware_divider/include/hardware/divider_helper.S
save_div_state_and_lr = 5 * 4
# src/rp2_common/pico_divider/divider_hardware.S
save_div_state_and_lr_64 = 5 * 4
- return [
+
+ # src/src/rp2_common/pico_crt0/crt0.S
+ for n in range(32):
+ ret += [synthetic_node(f"isr_irq{n}", 0, {"__unhandled_user_irq"})]
+ ret += [
+ synthetic_node("isr_invalid", 0, {"__unhandled_user_irq"}),
+ synthetic_node("isr_nmi", 0, {"__unhandled_user_irq"}),
+ synthetic_node("isr_hardfault", 0, {"__unhandled_user_irq"}),
+ synthetic_node("isr_svcall", 0, {"__unhandled_user_irq"}),
+ synthetic_node("isr_pendsv", 0, {"__unhandled_user_irq"}),
+ synthetic_node("isr_systick", 0, {"__unhandled_user_irq"}),
+ synthetic_node(f"__unhandled_user_irq", 0),
+ synthetic_node("_reset_handler", 0, {"runtime_init", "main", "exit"}),
+ ]
+
+ ret += [
# src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S
- synthetic_node("__aeabi_lmul", 4),
+ synthetic_node("__wrap___aeabi_lmul", 4),
# src/rp2_common/pico_divider/divider_hardware.S
# s32 aliases
synthetic_node("div_s32s32", 0, {"divmod_s32s32"}),
- synthetic_node("__aeabi_idiv", 0, {"divmod_s32s32"}),
- synthetic_node("__aeabi_idivmod", 0, {"divmod_s32s32"}),
+ synthetic_node("__wrap___aeabi_idiv", 0, {"divmod_s32s32"}),
+ synthetic_node("__wrap___aeabi_idivmod", 0, {"divmod_s32s32"}),
# s32 impl
synthetic_node("divmod_s32s32", 0, {"divmod_s32s32_savestate"}),
synthetic_node(
@@ -739,8 +858,8 @@ class PicoSDKPlugin:
synthetic_node("divmod_s32s32_unsafe", 2 * 4, {"__aeabi_idiv0"}),
# u32 aliases
synthetic_node("div_u32u32", 0, {"divmod_u32u32"}),
- synthetic_node("__aeabi_uidiv", 0, {"divmod_u32u32"}),
- synthetic_node("__aeabi_uidivmod", 0, {"divmod_u32u32"}),
+ synthetic_node("__wrap___aeabi_uidiv", 0, {"divmod_u32u32"}),
+ synthetic_node("__wrap___aeabi_uidivmod", 0, {"divmod_u32u32"}),
# u32 impl
synthetic_node("divmod_u32u32", 0, {"divmod_u32u32_savestate"}),
synthetic_node(
@@ -751,8 +870,8 @@ class PicoSDKPlugin:
synthetic_node("divmod_u32u32_unsafe", 2 * 4, {"__aeabi_idiv0"}),
# s64 aliases
synthetic_node("div_s64s64", 0, {"divmod_s64s64"}),
- synthetic_node("__aeabi_ldiv", 0, {"divmod_s64s64"}),
- synthetic_node("__aeabi_ldivmod", 0, {"divmod_s64s64"}),
+ synthetic_node("__wrap___aeabi_ldiv", 0, {"divmod_s64s64"}),
+ synthetic_node("__wrap___aeabi_ldivmod", 0, {"divmod_s64s64"}),
# s64 impl
synthetic_node("divmod_s64s64", 0, {"divmod_s64s64_savestate"}),
synthetic_node(
@@ -765,8 +884,7 @@ class PicoSDKPlugin:
),
# u64 aliases
synthetic_node("div_u64u64", 0, {"divmod_u64u64"}),
- synthetic_node("__aeabi_uldiv", 0, {"divmod_u64u64"}),
- synthetic_node("__aeabi_uldivmod", 0, {"divmod_u64u64"}),
+ synthetic_node("__wrap___aeabi_uldivmod", 0, {"divmod_u64u64"}),
# u64 impl
synthetic_node("divmod_u64u64", 0, {"divmod_u64u64_savestate"}),
synthetic_node(
@@ -780,6 +898,42 @@ class PicoSDKPlugin:
# *_rem
synthetic_node("divod_s64s64_rem", 2 * 4, {"divmod_s64s64"}),
synthetic_node("divod_u64u64_rem", 2 * 4, {"divmod_u64u64"}),
+ # src/rp2_common/pico_mem_ops/mem_ops_aeabi.S
+ synthetic_node("__aeabi_mem_init", 0, {"rom_funcs_lookup"}),
+ synthetic_node(
+ "__wrap___aeabi_memset", 0, {"rom_func_lookup(ROM_FUNC_MEMSET)"}
+ ),
+ synthetic_node("__wrap___aeabi_memset4", 0, {"__wrap___aeabi_memset8"}),
+ synthetic_node(
+ "__wrap___aeabi_memset8", 0, {"rom_func_lookup(ROM_FUNC_MEMSET4)"}
+ ),
+ synthetic_node("__wrap___aeabi_memcpy4", 0, {"__wrap___aeabi_memcpy8"}),
+ synthetic_node(
+ "__wrap___aeabi_memcpy7", 0, {"rom_func_lookup(ROM_FUNC_MEMCPY4)"}
+ ),
+ synthetic_node("__wrap_memset", 0, {"rom_func_lookup(ROM_FUNC_MEMSET)"}),
+ synthetic_node("__wrap___aeabi_memcpy", 0, {"__wrap_memcpy"}),
+ synthetic_node("__wrap_memcpy", 0, {"rom_func_lookup(ROM_FUNC_MEMCPY)"}),
+ # src/rp2_common/pico_bit_ops/bit_ops_aeabi.S
+ synthetic_node("__aeabi_bits_init", 0, {"rom_funcs_lookup"}),
+ synthetic_node("__wrap___clz", 0, {"__wrap___clzsi2"}),
+ synthetic_node("__wrap___clzl", 0, {"__wrap___clzsi2"}),
+ synthetic_node("__wrap___clzsi2", 0, {"rom_func_lookup(ROM_FUNC_CLZ32"}),
+ synthetic_node("__wrap___ctzsi2", 0, {"rom_func_lookup(ROM_FUNC_CTZ32"}),
+ synthetic_node(
+ "__wrap___popcountsi2", 0, {"rom_func_lookup(ROM_FUNC_POPCOUNT32"}
+ ),
+ synthetic_node("__wrap___clzll", 0, {"__wrap___clzdi2"}),
+ synthetic_node("__wrap___clzdi2", 4, {"rom_func_lookup(ROM_FUNC_CLZ32"}),
+ synthetic_node("__wrap___ctzdi2", 4, {"rom_func_lookup(ROM_FUNC_CTZ32"}),
+ synthetic_node(
+ "__wrap___popcountdi2", 3 * 4, {"rom_func_lookup(ROM_FUNC_POPCOUNT32"}
+ ),
+ synthetic_node("__rev", 0, {"reverse32"}),
+ synthetic_node("__revl", 0, {"reverse32"}),
+ synthetic_node("reverse32", 0, {"rom_func_lookup(ROM_FUNC_REVERSE32"}),
+ synthetic_node("__revll", 0, {"reverse64"}),
+ synthetic_node("reverse64", 3 * 4, {"rom_func_lookup(ROM_FUNC_REVERSE32"}),
# src/rp2040/boot_stage2/boot2_${name,,}.S for name=W25Q080,
# controlled by `#define PICO_BOOT_STAGE2_{name} 1` in
# src/boards/include/boards/pico.h
@@ -790,6 +944,7 @@ class PicoSDKPlugin:
# synthetic_node("rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE)", 0), # TODO
# synthetic_node("rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET)", 0), # TODO
]
+ return ret
class TinyUSBDevicePlugin:
@@ -856,6 +1011,9 @@ class TinyUSBDevicePlugin:
in_table = True
self.tud_drivers = tud_drivers
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
return []
@@ -884,6 +1042,9 @@ class TinyUSBDevicePlugin:
class NewlibPlugin:
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
# This is accurate to
# /usr/arm-none-eabi/lib/thumb/v6-m/nofp/libg.a as of
@@ -916,6 +1077,7 @@ class NewlibPlugin:
synthetic_node("__errno", 0),
synthetic_node("_getpid_r", 8, {"_getpid"}),
synthetic_node("random", 8),
+ synthetic_node("register_fini", 8, {"atexit"}),
]
def indirect_callees(self, loc: str, line: str) -> tuple[list[str], bool] | None:
@@ -926,6 +1088,9 @@ class NewlibPlugin:
class LibGCCPlugin:
+ def is_intrhandler(self, name: str) -> bool:
+ return False
+
def extra_nodes(self) -> typing.Collection[Node]:
# This is accurate to
# /usr/lib/gcc/arm-none-eabi/14.2.0/thumb/v6-m/nofp/libgcc.a
@@ -952,7 +1117,6 @@ def main(
) -> None:
plugins: list[Plugin] = []
- hooks_is_intrhandler: list[typing.Callable[[str], bool]] = []
# sbc-harness ####################################################
@@ -967,24 +1131,14 @@ def main(
if "9p" in name:
return lib9p_plugin.thread_count(name)
return 1
- if name == "main":
+ if name == ("_reset_handler" if arg_pico_platform == "rp2040" else "main"):
return 1
return 0
- def sbc_is_intrhandler(name: str) -> bool:
- return name in [
- "rp2040_hwtimer_intrhandler",
- "_cr_gdb_intrhandler",
- "hostclock_handle_sig_alarm",
- "hostnet_handle_sig_io",
- ]
-
- hooks_is_intrhandler += [sbc_is_intrhandler]
-
plugins += [
AppPlugin(),
LibObjPlugin(arg_c_fnames),
- LibHWPlugin(),
+ LibHWPlugin(arg_pico_platform),
LibCRIPCPlugin(),
lib9p_plugin,
LibMiscPlugin(),
@@ -993,16 +1147,11 @@ def main(
# pico-sdk #######################################################
if arg_pico_platform == "rp2040":
-
- def pico_is_intrhandler(name: str) -> bool:
- return name in [
- "gpio_default_irq_handler",
- ]
-
- hooks_is_intrhandler += [pico_is_intrhandler]
-
plugins += [
- PicoSDKPlugin(sbc_gpio_handlers),
+ PicoSDKPlugin(
+ app_gpio_handlers=sbc_gpio_handlers,
+ app_init_array=["register_fini"],
+ ),
TinyUSBDevicePlugin(arg_c_fnames),
NewlibPlugin(),
LibGCCPlugin(),
@@ -1015,8 +1164,8 @@ def main(
def intrhandler_filter(name: str) -> int:
name = name.rsplit(":", 1)[-1]
- for hook in hooks_is_intrhandler:
- if hook(name):
+ for plugin in plugins:
+ if plugin.is_intrhandler(name):
return 1
return 0
@@ -1055,6 +1204,8 @@ def main(
# Print.
print("= " + grp_name + " " + sep1[len(grp_name) + 3 :])
for name, num in sorted(grp.rows.items()):
+ if num == 0:
+ continue
print(f"{name.ljust(namelen)} {str(num).rjust(numlen)}")
print(sep2)
print(f"{'Total'.ljust(namelen)} {str(grp.nsum).rjust(numlen)}")
@@ -1078,7 +1229,7 @@ def main(
baselen = max(len(str(r[1])) for r in rows)
sizelen = max(len(str(r[2])) for r in rows)
for row in sorted(rows):
- if row[0] == "main":
+ if row[0] in ("main", "_reset_handler"):
continue
print("const size_t CONFIG_COROUTINE_STACK_SIZE_", end="")
print(f"{row[0].ljust(namelen)} =", end="")
@@ -1094,6 +1245,11 @@ def main(
print(f"warning: dynamic-stack-usage: {funcname}")
print("*/")
+ print("")
+ print("/*")
+ for funcname in sorted(result.included_funcs):
+ print(f"included: {funcname}")
+ print("*/")
if __name__ == "__main__":
diff --git a/cmd/sbc_harness/CMakeLists.txt b/cmd/sbc_harness/CMakeLists.txt
index 9025447..081a5fc 100644
--- a/cmd/sbc_harness/CMakeLists.txt
+++ b/cmd/sbc_harness/CMakeLists.txt
@@ -48,6 +48,7 @@ target_sources(sbc_harness PRIVATE
target_link_libraries(sbc_harness
pico_standard_link
)
+target_link_options(sbc_harness PRIVATE "$<TARGET_PROPERTY:sbc_harness_objs,LINK_OPTIONS>")
pico_add_extra_outputs(sbc_harness) # create .map/.bin/.hex/.uf2 files in addition to .elf
pico_set_program_url(sbc_harness "https://git.lukeshu.com/sbc-harness")
diff --git a/libcr_ipc/CMakeLists.txt b/libcr_ipc/CMakeLists.txt
index 0c3bd57..4590bdd 100644
--- a/libcr_ipc/CMakeLists.txt
+++ b/libcr_ipc/CMakeLists.txt
@@ -5,6 +5,10 @@
add_library(libcr_ipc INTERFACE)
target_include_directories(libcr_ipc SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
+target_sources(libcr_ipc INTERFACE
+ chan.c
+ select.c
+)
target_link_libraries(libcr_ipc INTERFACE
libcr
)
diff --git a/libcr_ipc/chan.c b/libcr_ipc/chan.c
new file mode 100644
index 0000000..7dd1132
--- /dev/null
+++ b/libcr_ipc/chan.c
@@ -0,0 +1,40 @@
+/* libcr_ipc/chan.c - Simple channels for libcr
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr_ipc/chan.h>
+
+void _cr_chan_dequeue(void *_ch, size_t) {
+ struct _cr_chan *ch = _ch;
+ _cr_ipc_dll_pop_from_front(&ch->waiters);
+}
+
+void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void *val_ptr, size_t val_size) {
+ assert(ch);
+ assert(val_ptr);
+
+ if (ch->waiters.front && ch->waiter_typ != self_typ) { /* non-blocking fast-path */
+ /* Copy. */
+ struct _cr_chan_waiter *front = _cr_ipc_dll_node_cast(struct _cr_chan_waiter, ch->waiters.front);
+ if (self_typ == _CR_CHAN_SENDER)
+ memcpy(front->val_ptr, val_ptr, val_size);
+ else
+ memcpy(val_ptr, front->val_ptr, val_size);
+ cr_unpause(front->cid);
+ front->dequeue(front->dequeue_arg1,
+ front->dequeue_arg2);
+ cr_yield();
+ } else { /* blocking slow-path */
+ struct _cr_chan_waiter self = {
+ .cid = cr_getcid(),
+ .val_ptr = val_ptr,
+ .dequeue = _cr_chan_dequeue,
+ .dequeue_arg1 = ch,
+ };
+ _cr_ipc_dll_push_to_rear(&ch->waiters, &self);
+ ch->waiter_typ = self_typ;
+ cr_pause_and_yield();
+ }
+}
diff --git a/libcr_ipc/include/libcr_ipc/chan.h b/libcr_ipc/include/libcr_ipc/chan.h
index da4f08e..5b1e583 100644
--- a/libcr_ipc/include/libcr_ipc/chan.h
+++ b/libcr_ipc/include/libcr_ipc/chan.h
@@ -1,6 +1,6 @@
/* libcr_ipc/chan.h - Simple channels for libcr
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -122,37 +122,7 @@ struct _cr_chan {
_cr_ipc_dll_root waiters;
};
-static void _cr_chan_dequeue(void *_ch, size_t) {
- struct _cr_chan *ch = _ch;
- _cr_ipc_dll_pop_from_front(&ch->waiters);
-}
-
-static inline void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void *val_ptr, size_t val_size) {
- assert(ch);
- assert(val_ptr);
-
- if (ch->waiters.front && ch->waiter_typ != self_typ) { /* non-blocking fast-path */
- /* Copy. */
- struct _cr_chan_waiter *front = _cr_ipc_dll_node_cast(struct _cr_chan_waiter, ch->waiters.front);
- if (self_typ == _CR_CHAN_SENDER)
- memcpy(front->val_ptr, val_ptr, val_size);
- else
- memcpy(val_ptr, front->val_ptr, val_size);
- cr_unpause(front->cid);
- front->dequeue(front->dequeue_arg1,
- front->dequeue_arg2);
- cr_yield();
- } else { /* blocking slow-path */
- struct _cr_chan_waiter self = {
- .cid = cr_getcid(),
- .val_ptr = val_ptr,
- .dequeue = _cr_chan_dequeue,
- .dequeue_arg1 = ch,
- };
- _cr_ipc_dll_push_to_rear(&ch->waiters, &self);
- ch->waiter_typ = self_typ;
- cr_pause_and_yield();
- }
-}
+void _cr_chan_dequeue(void *_ch, size_t);
+void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void *val_ptr, size_t val_size);
#endif /* _LIBCR_IPC_CHAN_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/select.h b/libcr_ipc/include/libcr_ipc/select.h
index ee49cca..b845082 100644
--- a/libcr_ipc/include/libcr_ipc/select.h
+++ b/libcr_ipc/include/libcr_ipc/select.h
@@ -1,6 +1,6 @@
/* libcr_ipc/select.h - Select between channels
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -69,106 +69,14 @@ enum _cr_select_class {
_CR_SELECT_CLASS_NONBLOCK,
};
-static inline enum _cr_select_class _cr_select_getclass(struct cr_select_arg arg) {
- switch (arg.op) {
- case _CR_SELECT_OP_RECV:
- if (arg.ch->waiters.front && arg.ch->waiter_typ == _CR_CHAN_SENDER)
- return _CR_SELECT_CLASS_NONBLOCK;
- else
- return _CR_SELECT_CLASS_BLOCKING;
- case _CR_SELECT_OP_SEND:
- if (arg.ch->waiters.front && arg.ch->waiter_typ == _CR_CHAN_RECVER)
- return _CR_SELECT_CLASS_NONBLOCK;
- else
- return _CR_SELECT_CLASS_BLOCKING;
- case _CR_SELECT_OP_DEFAULT:
- return _CR_SELECT_CLASS_DEFAULT;
- default:
- assert_notreached("invalid arg.op");
- }
-}
-
struct _cr_select_waiters {
size_t cnt;
struct cr_select_arg *args;
struct _cr_chan_waiter *nodes;
};
-static void _cr_select_dequeue(void *_waiters, size_t idx) {
- struct _cr_select_waiters *waiters = waiters;
- for (size_t i = 0; i < waiters->cnt; i++)
- _cr_ipc_dll_remove(&(waiters->args[i].ch->waiters),
- &(waiters->nodes[i]));
- waiters->cnt = idx;
-}
-
-static size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) {
- size_t cnt_blocking = 0;
- size_t cnt_nonblock = 0;
- size_t cnt_default = 0;
-
- assert(arg_cnt);
- assert(arg_vec);
- cr_assert_in_coroutine();
-
- for (size_t i = 0; i < arg_cnt; i++) {
- switch (_cr_select_getclass(arg_vec[i])) {
- case _CR_SELECT_CLASS_BLOCKING:
- cnt_blocking++;
- break;
- case _CR_SELECT_CLASS_NONBLOCK:
- cnt_nonblock++;
- break;
- case _CR_SELECT_CLASS_DEFAULT:
- cnt_default++;
- break;
- }
- }
-
- if (cnt_nonblock) {
- size_t choice = rand_uint63n(cnt_nonblock);
- for (size_t i = 0, seen = 0; i < arg_cnt; i++) {
- if (_cr_select_getclass(arg_vec[i]) == _CR_SELECT_CLASS_NONBLOCK) {
- if (seen == choice) {
- _cr_chan_xfer(arg_vec[i].op == _CR_SELECT_OP_RECV
- ? _CR_CHAN_RECVER
- : _CR_CHAN_SENDER,
- arg_vec[i].ch,
- arg_vec[i].val_ptr,
- arg_vec[i].val_siz);
- return i;
- }
- seen++;
- }
- }
- assert_notreached("should have returned from inside for() loop");
- }
-
- if (cnt_default) {
- for (size_t i = 0; i < arg_cnt; i++)
- if (_cr_select_getclass(arg_vec[i]) == _CR_SELECT_CLASS_DEFAULT)
- return i;
- assert_notreached("should have returned from inside for() loop");
- }
-
- struct _cr_select_waiters waiters = {
- .cnt = arg_cnt,
- .args = arg_vec,
- .nodes = alloca(sizeof(struct _cr_chan_waiter) * arg_cnt),
- };
- for (size_t i = 0; i < arg_cnt; i++) {
- waiters.nodes[i] = (struct _cr_chan_waiter){
- .cid = cr_getcid(),
- .val_ptr = arg_vec[i].val_ptr,
- .dequeue = _cr_select_dequeue,
- .dequeue_arg1 = &waiters,
- .dequeue_arg2 = i,
- };
- _cr_ipc_dll_push_to_rear(&arg_vec[i].ch->waiters, &waiters.nodes[i]);
- }
- cr_pause_and_yield();
- return waiters.cnt;
-}
+void _cr_select_dequeue(void *_waiters, size_t idx);
+size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]);
/* cr_select_l(arg1, arg2, arg3, ...) ******************************************/
diff --git a/libcr_ipc/select.c b/libcr_ipc/select.c
new file mode 100644
index 0000000..4bd9067
--- /dev/null
+++ b/libcr_ipc/select.c
@@ -0,0 +1,102 @@
+/* libcr_ipc/select.c - Select between channels
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr_ipc/select.h>
+
+static inline enum _cr_select_class _cr_select_getclass(struct cr_select_arg arg) {
+ switch (arg.op) {
+ case _CR_SELECT_OP_RECV:
+ if (arg.ch->waiters.front && arg.ch->waiter_typ == _CR_CHAN_SENDER)
+ return _CR_SELECT_CLASS_NONBLOCK;
+ else
+ return _CR_SELECT_CLASS_BLOCKING;
+ case _CR_SELECT_OP_SEND:
+ if (arg.ch->waiters.front && arg.ch->waiter_typ == _CR_CHAN_RECVER)
+ return _CR_SELECT_CLASS_NONBLOCK;
+ else
+ return _CR_SELECT_CLASS_BLOCKING;
+ case _CR_SELECT_OP_DEFAULT:
+ return _CR_SELECT_CLASS_DEFAULT;
+ default:
+ assert_notreached("invalid arg.op");
+ }
+}
+
+void _cr_select_dequeue(void *_waiters, size_t idx) {
+ struct _cr_select_waiters *waiters = _waiters;
+ for (size_t i = 0; i < waiters->cnt; i++)
+ _cr_ipc_dll_remove(&(waiters->args[i].ch->waiters),
+ &(waiters->nodes[i]));
+ waiters->cnt = idx;
+}
+
+size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) {
+ size_t cnt_blocking = 0;
+ size_t cnt_nonblock = 0;
+ size_t cnt_default = 0;
+
+ assert(arg_cnt);
+ assert(arg_vec);
+ cr_assert_in_coroutine();
+
+ for (size_t i = 0; i < arg_cnt; i++) {
+ switch (_cr_select_getclass(arg_vec[i])) {
+ case _CR_SELECT_CLASS_BLOCKING:
+ cnt_blocking++;
+ break;
+ case _CR_SELECT_CLASS_NONBLOCK:
+ cnt_nonblock++;
+ break;
+ case _CR_SELECT_CLASS_DEFAULT:
+ cnt_default++;
+ break;
+ }
+ }
+
+ if (cnt_nonblock) {
+ size_t choice = rand_uint63n(cnt_nonblock);
+ for (size_t i = 0, seen = 0; i < arg_cnt; i++) {
+ if (_cr_select_getclass(arg_vec[i]) == _CR_SELECT_CLASS_NONBLOCK) {
+ if (seen == choice) {
+ _cr_chan_xfer(arg_vec[i].op == _CR_SELECT_OP_RECV
+ ? _CR_CHAN_RECVER
+ : _CR_CHAN_SENDER,
+ arg_vec[i].ch,
+ arg_vec[i].val_ptr,
+ arg_vec[i].val_siz);
+ return i;
+ }
+ seen++;
+ }
+ }
+ assert_notreached("should have returned from inside for() loop");
+ }
+
+ if (cnt_default) {
+ for (size_t i = 0; i < arg_cnt; i++)
+ if (_cr_select_getclass(arg_vec[i]) == _CR_SELECT_CLASS_DEFAULT)
+ return i;
+ assert_notreached("should have returned from inside for() loop");
+ }
+
+ struct _cr_select_waiters waiters = {
+ .cnt = arg_cnt,
+ .args = arg_vec,
+ .nodes = alloca(sizeof(struct _cr_chan_waiter) * arg_cnt),
+ };
+ for (size_t i = 0; i < arg_cnt; i++) {
+ waiters.nodes[i] = (struct _cr_chan_waiter){
+ .cid = cr_getcid(),
+ .val_ptr = arg_vec[i].val_ptr,
+ .dequeue = _cr_select_dequeue,
+ .dequeue_arg1 = &waiters,
+ .dequeue_arg2 = i,
+ };
+ _cr_ipc_dll_push_to_rear(&arg_vec[i].ch->waiters, &waiters.nodes[i]);
+ }
+ cr_pause_and_yield();
+ return waiters.cnt;
+}