365 lines
12 KiB
Bash
Executable File
365 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 [[ -f "$root/vendor/Hazard3/test/sim/common/init.S" && \
|
|
-f "$root/vendor/lab-runtime/memops.c" ]]; then
|
|
printf '%s\n' "$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"
|
|
if [[ "$task_number" -eq 3 ]]; then
|
|
"$root/tests/check_task03_elf.sh" "$elf" >&2
|
|
fi
|
|
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_reset_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
|