Files

359 lines
12 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
action="${1:?action is required}"
root="${STEM_REPO:-$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)}"
profile="${STEM_PROFILE:-native-amd64}"
target="${STEM_TARGET:-native}"
selector="${STEM_TASK:-task3}"
state="${STEM_STATE_DIR:-$root/.stem/instances/local}"
tasks=(
task01_first_fit_split
task02_address_order_coalesce
task03_freertos_heap4
)
resolve_task() {
local value="${1,,}" digits number stem
for stem in "${tasks[@]}"; do
if [[ "$value" == "$stem" ]]; then
printf '%s\n' "$stem"
return
fi
done
if [[ "$value" =~ ^(task|t)?[-_]?0*([1-3])($|[-_].*) ]]; then
digits="${BASH_REMATCH[2]}"
number=$((10#$digits))
printf '%s\n' "${tasks[number - 1]}"
return
fi
printf 'Unknown task selector: %s\n' "$1" >&2
exit 2
}
task_stem="$(resolve_task "$selector")"
task_digits="${task_stem#task}"
task_digits="${task_digits%%_*}"
task_number=$((10#$task_digits))
task_key="$(printf 'task%02d' "$task_number")"
source_file="$root/src/tasks/$task_stem.c"
artifact_dir="$root/.stem/artifacts/$profile/$target/$task_key"
mkdir -p "$state" "$artifact_dir"
freertos_kernel_path() {
if [[ -n "${FREERTOS_KERNEL_PATH:-}" ]]; then
printf '%s\n' "$FREERTOS_KERNEL_PATH"
elif [[ -f /opt/FreeRTOS-Kernel/portable/MemMang/heap_4.c ]]; then
printf '%s\n' /opt/FreeRTOS-Kernel
else
printf '%s\n' "$root/../lab-rv32i-freertos-task-stack-vector/vendor/FreeRTOS-Kernel"
fi
}
verify_kernel() {
local kernel="$1"
test -f "$kernel/portable/MemMang/heap_4.c"
grep -F 'FreeRTOS Kernel V11.3.0' "$kernel/include/FreeRTOS.h" >/dev/null
}
freertos_rp2350_path() {
if [[ -n "${FREERTOS_RP2350_PATH:-}" ]]; then
printf '%s\n' "$FREERTOS_RP2350_PATH"
else
printf '%s\n' /opt/FreeRTOS-RP2350
fi
}
verify_rp2350_kernel() {
local kernel="$1"
test -f "$kernel/portable/ThirdParty/GCC/RP2350_RISC-V/portASM.S"
grep -F '<DEVELOPMENT BRANCH>' "$kernel/include/FreeRTOS.h" >/dev/null
}
copy_artifact() {
local path="$1"
[[ -f "$path" ]] && cp -f "$path" "$artifact_dir/"
}
build_native() {
local mode="${1:-normal}" kernel build_dir output cc="${CC:-cc}"
local flags=(-std=c11 -O0 -g3 -fno-omit-frame-pointer -fno-inline
-fno-optimize-sibling-calls -Wall -Wextra -Wpedantic
-DHOST_PRINTF=1 -I"$root/src/tasks")
build_dir="$state/build/native/$task_stem"
output="$build_dir/prog"
if [[ "$mode" == sanitizer ]]; then
flags+=(-fsanitize=address,undefined)
fi
mkdir -p "$build_dir"
if [[ "$task_number" -eq 3 ]]; then
kernel="$(freertos_kernel_path)"
verify_kernel "$kernel"
flags+=(-DHEAP4_HOST_ADAPTER=1 -I"$root/include/host-shim")
"$cc" "${flags[@]}" "$source_file" \
"$kernel/portable/MemMang/heap_4.c" -o "$output"
else
"$cc" "${flags[@]}" "$source_file" -o "$output"
fi
objdump -dS "$output" > "$build_dir/prog.lst"
cp -f "$output" "$artifact_dir/prog"
copy_artifact "$build_dir/prog.lst"
printf '%s\n' "$output"
}
rv_env_root() {
if [[ -n "${RV_ENV_ROOT:-}" ]]; then
printf '%s\n' "$RV_ENV_ROOT"
elif [[ -d /opt/rv-env ]]; then
printf '%s\n' /opt/rv-env
else
printf '%s\n' /opt/stem
fi
}
build_hazard3() {
local env_root kernel build_dir elf
env_root="$(rv_env_root)"
kernel="$(freertos_kernel_path)"
verify_kernel "$kernel"
build_dir="$state/build/hazard3"
make -B -C "$root" "task$task_number" \
"BUILD_ROOT=$build_dir" "RV_ENV_ROOT=$env_root" \
"FREERTOS_KERNEL_PATH=$kernel" PROFILE=observe >&2 || return
elf="$build_dir/$task_stem/prog.elf"
test -s "$elf"
copy_artifact "$elf"
copy_artifact "${elf%.elf}.bin"
copy_artifact "${elf%.elf}.lst"
copy_artifact "${elf%.elf}.map"
printf '%s\n' "$elf"
}
hazard3_tb() {
local env_root tb
env_root="$(rv_env_root)"
tb="$env_root/vendor/Hazard3/test/sim/tb_verilator/tb"
if [[ ! -x "$tb" ]]; then
make -C "$(dirname -- "$tb")" tb >&2
fi
printf '%s\n' "$tb"
}
build_rp2350() {
local binary_type="${1:-no_flash}" build_kind build_dir elf kernel rp_kernel
local cmake_args
case "$binary_type" in
no_flash) build_kind=ram ;;
default) build_kind=flash ;;
*) printf 'Unsupported RP2350 binary type: %s\n' "$binary_type" >&2; return 2 ;;
esac
kernel="$(freertos_kernel_path)"
verify_kernel "$kernel"
rp_kernel="$(freertos_rp2350_path)"
verify_rp2350_kernel "$rp_kernel"
build_dir="$state/build/rp2350-rv-$build_kind"
cmake_args=(
-S "$root" -B "$build_dir"
-DPICO_PLATFORM=rp2350-riscv
-DPICO_BOARD="${PICO_BOARD:-pico2_w}"
-DPICO_GCC_TRIPLE=riscv32-pico-elf
-DHEAP4_UPSTREAM_ROOT="$kernel"
-DFREERTOS_RP2350_PATH="$rp_kernel"
-DHEAP4_PROFILE=observe
-DHEAP4_BINARY_TYPE="$binary_type"
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
)
if command -v ninja >/dev/null 2>&1; then
cmake_args+=(-G Ninja)
else
cmake_args+=(-G "Unix Makefiles")
fi
if [[ -d /opt/picotool/picotool ]]; then
cmake_args+=(-Dpicotool_DIR=/opt/picotool/picotool)
fi
cmake "${cmake_args[@]}" >&2
cmake --build "$build_dir" --target "$task_stem" >&2
elf="$build_dir/$task_stem.elf"
test -s "$elf"
riscv64-unknown-elf-objdump -dS -M no-aliases,numeric "$elf" \
> "$build_dir/$task_stem.lst"
copy_artifact "$elf"
copy_artifact "$build_dir/$task_stem.bin"
copy_artifact "$build_dir/$task_stem.hex"
copy_artifact "$build_dir/$task_stem.uf2"
copy_artifact "$build_dir/$task_stem.dis"
copy_artifact "$build_dir/$task_stem.lst"
copy_artifact "$build_dir/$task_stem.elf.map"
printf '%s\n' "$elf"
}
openocd_target="${STEM_OPENOCD_TARGET:-target/rp2350.cfg}"
gdb_port="${GDB_PORT:-3333}"
gdb_target="${GDB_TARGET:-127.0.0.1:$gdb_port}"
openocd_pid=""
stop_owned_openocd() {
if [[ -n "$openocd_pid" ]]; then
kill "$openocd_pid" 2>/dev/null || true
wait "$openocd_pid" 2>/dev/null || true
fi
}
start_openocd() {
local log="$state/openocd-rp2350.log" attempt
if nc -z 127.0.0.1 "$gdb_port" 2>/dev/null; then
return
fi
openocd -f interface/cmsis-dap.cfg \
-c "adapter speed ${STEM_ADAPTER_KHZ:-5000}" \
-c 'set USE_CORE { rv0 }' -c 'set USE_SMP 0' \
-c "set FLASHSIZE ${STEM_RP2350_FLASH_SIZE:-0x400000}" \
-f "$openocd_target" >"$log" 2>&1 &
openocd_pid=$!
for attempt in $(seq 1 80); do
if nc -z 127.0.0.1 "$gdb_port" 2>/dev/null; then
return
fi
if ! kill -0 "$openocd_pid" 2>/dev/null; then
sed -n '1,240p' "$log" >&2
return 1
fi
sleep 0.1
done
printf 'OpenOCD did not open GDB port %s\n' "$gdb_port" >&2
return 1
}
flash_rp2350() {
local elf="$1" log="$state/openocd-rp2350-flash.log"
if nc -z 127.0.0.1 "$gdb_port" 2>/dev/null; then
printf 'GDB port %s is busy; stop the current debug session before deploy.\n' "$gdb_port" >&2
return 1
fi
if ! openocd -f interface/cmsis-dap.cfg \
-c "adapter speed ${STEM_ADAPTER_KHZ:-5000}" \
-c 'set USE_CORE { cm0 rv0 }' -c 'set USE_SMP 0' \
-c "set FLASHSIZE ${STEM_RP2350_FLASH_SIZE:-0x400000}" \
-f "$openocd_target" -c init \
-c 'if {[target_present rp2350.rv0]} { targets rp2350.rv0 } else { targets rp2350.cm0 }' \
-c 'reset halt' -c "flash write_image erase $elf" \
-c "verify_image $elf" -c 'reset run' -c shutdown >"$log" 2>&1; then
sed -n '1,260p' "$log" >&2
return 1
fi
grep -E 'wrote [0-9]+ bytes|verified [0-9]+ bytes' "$log" || true
printf 'rp2350-flash-ok image=%s verify=passed architecture=RISC-V\n' "$elf"
}
hardware_test_task03() {
local elf="$1" output
start_openocd
trap stop_owned_openocd EXIT
output="$(gdb-multiarch -nx -q -batch "$elf" \
-ex 'set architecture riscv:rv32' \
-ex "target extended-remote $gdb_target" \
-ex 'monitor reset halt' \
-ex 'hbreak heap4_fragmented_checkpoint' \
-ex 'hbreak task03_debug_checkpoint' \
-ex continue \
-ex 'printf "fragmented blocks=%lu free=%lu largest=%lu\n", g_fragmented_stats.xNumberOfFreeBlocks, g_fragmented_stats.xAvailableHeapSpaceInBytes, g_fragmented_stats.xSizeOfLargestFreeBlockInBytes' \
-ex continue \
-ex 'printf "task03 initial=%lu after=%lu final=%lu min=%lu aligned=%d oom=%d hooks=%d asserts=%d pass=%d\n", g_initial_free, g_after_allocations, g_final_free, g_minimum_ever_free, g_allocations_aligned, g_oom_is_null, g_malloc_failed_hooks, g_assert_failures, g_task03_pass' \
-ex 'printf "frame sp=0x%08lx fp=0x%08lx pc=0x%08lx align=%lu\n", (unsigned long)$sp, (unsigned long)$s0, (unsigned long)$pc, ((unsigned long)$sp & 15)' \
-ex 'x/96bx ucHeap' \
-ex 'delete breakpoints' -ex 'monitor halt' -ex disconnect)"
printf '%s\n' "$output"
grep -E '^fragmented blocks=2 ' <<<"$output" >/dev/null
grep -E '^task03 .* aligned=1 oom=1 hooks=1 asserts=0 pass=1$' <<<"$output" >/dev/null
grep -E '^frame .* align=0$' <<<"$output" >/dev/null
printf 'rp2350-hardware-ok task=task03 heap_4=upstream load=flash\n'
}
configure_debug_view() {
export STEM_DEBUG_WINDOW_NAME=heap4
case "$task_number" in
1)
export STEM_DEBUG_INITIAL_BREAK=task01_debug_checkpoint
export STEM_DEBUG_MEMORY_EXPR='&g_split_arena' STEM_DEBUG_MEMORY_LENGTH=288
;;
2)
export STEM_DEBUG_INITIAL_BREAK=task02_fragmented_checkpoint
export STEM_DEBUG_MEMORY_EXPR='&g_coalesce_arena' STEM_DEBUG_MEMORY_LENGTH=160
;;
3)
export STEM_DEBUG_INITIAL_BREAK=heap4_fragmented_checkpoint
export STEM_DEBUG_MEMORY_EXPR='ucHeap' STEM_DEBUG_MEMORY_LENGTH=160
;;
esac
}
case "$profile:$target:$action" in
native-amd64:native:build)
build_native normal >/dev/null
;;
native-amd64:native:test)
output="$(build_native sanitizer)"
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=halt_on_error=1 "$output"
;;
native-amd64:native:run)
output="$(build_native normal)"
exec "$output"
;;
native-amd64:native:debug)
output="$(build_native normal)"
export RV_REPO="$root" RV_EDITOR="${STEM_EDITOR:-nvim}"
export RV_HOST_ELF="$output" RV_HOST_SRC="$source_file"
export RV_HOST_LST="$(dirname -- "$output")/prog.lst"
exec /opt/stem/scripts/host-entry.sh debug "task$task_number"
;;
hazard3-sim:hazard3-baremetal:build)
build_hazard3 >/dev/null
;;
hazard3-sim:hazard3-baremetal:test|hazard3-sim:hazard3-baremetal:run)
elf="$(build_hazard3)"
tb="$(hazard3_tb)"
exec "$tb" --bin "${elf%.elf}.bin" --cpuret
;;
hazard3-sim:hazard3-baremetal:debug)
elf="$(build_hazard3)"
configure_debug_view
export STEM_DEBUG_BACKEND=hazard3 STEM_DEBUG_ELF="$elf"
export STEM_DEBUG_BIN="${elf%.elf}.bin" STEM_DEBUG_SRC="$source_file"
export STEM_DEBUG_LST="${elf%.elf}.lst"
exec /opt/stem/scripts/card-debug-ui.sh
;;
rp2350:rp2350-rv:build)
build_rp2350 >/dev/null
;;
rp2350:rp2350-rv:test)
elf="$(build_rp2350)"
riscv64-unknown-elf-readelf -h "$elf" | grep -F 'Machine:' | grep -F 'RISC-V'
if [[ "$task_number" -eq 3 ]]; then
"$root/tests/check_task03_elf.sh" "$elf"
fi
printf 'rp2350-build-ok task=%s binary_type=no_flash\n' "$task_key"
;;
rp2350:rp2350-rv:deploy)
elf="$(build_rp2350 default)"
flash_rp2350 "$elf"
if [[ "$task_number" -eq 3 ]]; then
hardware_test_task03 "$elf"
else
printf 'rp2350-deploy-ok task=%s load=flash persistent=1\n' "$task_key"
fi
;;
rp2350:rp2350-rv:debug)
elf="$(build_rp2350)"
configure_debug_view
export STEM_DEBUG_BACKEND=rp2350 STEM_DEBUG_ELF="$elf"
export STEM_DEBUG_SRC="$source_file" STEM_DEBUG_LST="${elf%.elf}.lst"
exec /opt/stem/scripts/card-debug-ui.sh
;;
*)
printf 'Unsupported profile/target/action: %s/%s/%s\n' "$profile" "$target" "$action" >&2
exit 2
;;
esac