diff --git a/.gitignore b/.gitignore index eab3e99..7c8f381 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,9 @@ -*.aux -*.log -*.out -*.fls -*.fdb_latexmk -*.synctex.gz -*.pdf -build-meta.tex -.rv/ -.nvim/ -.vim/ -.build/ -.gdb_history +.stem/ host-build/ build/*/prog.bin build/*/prog.elf build/*/prog.lst build/*/prog.map +build/*/*.o +cmake-build-*/ +compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..247b644 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,114 @@ +cmake_minimum_required(VERSION 3.13) + +if(NOT PICO_SDK_PATH) + if(DEFINED ENV{PICO_SDK_PATH}) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + else() + message(FATAL_ERROR "Set PICO_SDK_PATH to a Raspberry Pi Pico SDK checkout") + endif() +endif() + +include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) + +project(freertos_heap4_rp2350 C CXX ASM) +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_CXX_STANDARD 17) + +# The card and allocator are C-only. Debian's compact RISC-V toolchain does +# not ship libstdc++ headers, so keep the SDK's optional new/delete source out +# while preserving the interface targets expected by pico_stdlib. +add_library(pico_cxx_options_headers INTERFACE) +target_include_directories(pico_cxx_options_headers SYSTEM INTERFACE + ${PICO_SDK_PATH}/src/rp2_common/pico_cxx_options/include) +add_library(pico_cxx_options INTERFACE) +target_link_libraries(pico_cxx_options INTERFACE pico_cxx_options_headers) + +pico_sdk_init() + +if(NOT PICO_PLATFORM STREQUAL "rp2350-riscv") + message(FATAL_ERROR "This card targets the RP2350 RISC-V cores") +endif() +if(NOT HEAP4_UPSTREAM_ROOT) + if(DEFINED ENV{HEAP4_UPSTREAM_ROOT}) + set(HEAP4_UPSTREAM_ROOT $ENV{HEAP4_UPSTREAM_ROOT}) + else() + set(HEAP4_UPSTREAM_ROOT /opt/FreeRTOS-Kernel) + endif() +endif() +if(NOT EXISTS ${HEAP4_UPSTREAM_ROOT}/portable/MemMang/heap_4.c) + message(FATAL_ERROR "Pinned FreeRTOS heap_4.c not found at ${HEAP4_UPSTREAM_ROOT}") +endif() +if(NOT FREERTOS_RP2350_PATH) + if(DEFINED ENV{FREERTOS_RP2350_PATH}) + set(FREERTOS_RP2350_PATH $ENV{FREERTOS_RP2350_PATH}) + else() + set(FREERTOS_RP2350_PATH /opt/FreeRTOS-RP2350) + endif() +endif() +if(NOT EXISTS ${FREERTOS_RP2350_PATH}/portable/ThirdParty/GCC/RP2350_RISC-V/portASM.S) + message(FATAL_ERROR "Compatible RP2350 FreeRTOS core/port not found at ${FREERTOS_RP2350_PATH}") +endif() + +set(FREERTOS_CONFIG_FILE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include) +# Raspberry Pi's port CMake uses FREERTOS_KERNEL_PATH for its matching core. +# Keep the stable allocator root separate and add its heap_4.c explicitly. +set(FREERTOS_KERNEL_PATH ${FREERTOS_RP2350_PATH}) +add_subdirectory( + ${FREERTOS_RP2350_PATH}/portable/ThirdParty/GCC/RP2350_RISC-V + ${CMAKE_BINARY_DIR}/freertos-kernel) + +set(HEAP4_PROFILE "observe" CACHE STRING "observe, debug or release") +set_property(CACHE HEAP4_PROFILE PROPERTY STRINGS observe debug release) +set(HEAP4_BINARY_TYPE "no_flash" CACHE STRING "Pico SDK binary type") +set_property(CACHE HEAP4_BINARY_TYPE PROPERTY STRINGS no_flash default) + +if(HEAP4_PROFILE STREQUAL "observe") + set(HEAP4_COMPILE_OPTIONS + -O0 -g3 -fno-omit-frame-pointer -fno-inline + -fno-optimize-sibling-calls) +elseif(HEAP4_PROFILE STREQUAL "debug") + set(HEAP4_COMPILE_OPTIONS + -Og -g3 -fno-omit-frame-pointer -fno-optimize-sibling-calls) +elseif(HEAP4_PROFILE STREQUAL "release") + set(HEAP4_COMPILE_OPTIONS -O2) +else() + message(FATAL_ERROR "HEAP4_PROFILE must be observe, debug or release") +endif() + +set(MODEL_TASKS + task01_first_fit_split + task02_address_order_coalesce) + +foreach(task IN LISTS MODEL_TASKS) + set(task_source src/tasks/${task}.c) + add_executable(${task} ${task_source}) + target_include_directories(${task} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/tasks) + set_property(SOURCE ${task_source} APPEND PROPERTY COMPILE_OPTIONS + ${HEAP4_COMPILE_OPTIONS} -Wall -Wextra -Wpedantic) + target_link_libraries(${task} pico_stdlib) + pico_enable_stdio_usb(${task} 0) + pico_enable_stdio_uart(${task} 0) + if(NOT HEAP4_BINARY_TYPE STREQUAL "default") + pico_set_binary_type(${task} ${HEAP4_BINARY_TYPE}) + endif() + pico_add_extra_outputs(${task}) +endforeach() + +set(heap4_task task03_freertos_heap4) +set(heap4_source src/tasks/${heap4_task}.c) +add_executable(${heap4_task} ${heap4_source}) +target_sources(${heap4_task} PRIVATE + ${HEAP4_UPSTREAM_ROOT}/portable/MemMang/heap_4.c) +target_include_directories(${heap4_task} PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/include + ${CMAKE_CURRENT_LIST_DIR}/src/tasks) +set_property(SOURCE ${heap4_source} APPEND PROPERTY COMPILE_OPTIONS + ${HEAP4_COMPILE_OPTIONS} -Wall -Wextra -Wpedantic) +target_link_libraries(${heap4_task} pico_stdlib FreeRTOS-Kernel) +pico_enable_stdio_usb(${heap4_task} 0) +pico_enable_stdio_uart(${heap4_task} 0) +if(NOT HEAP4_BINARY_TYPE STREQUAL "default") + pico_set_binary_type(${heap4_task} ${HEAP4_BINARY_TYPE}) +endif() +pico_add_extra_outputs(${heap4_task}) diff --git a/Makefile b/Makefile index accd3b8..29ab56a 100644 --- a/Makefile +++ b/Makefile @@ -6,11 +6,18 @@ OBJDUMP := $(RISCV_PREFIX)objdump SIZE := $(RISCV_PREFIX)size HOST_CC ?= cc -DEBUG ?= 1 -ifeq ($(DEBUG),1) +PROFILE ?= observe +ifeq ($(PROFILE),observe) + OPTFLAGS := -O0 -fno-omit-frame-pointer -fno-inline -fno-optimize-sibling-calls DBGFLAGS := -g3 -ggdb -else +else ifeq ($(PROFILE),debug) + OPTFLAGS := -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls + DBGFLAGS := -g3 -ggdb +else ifeq ($(PROFILE),release) + OPTFLAGS := -O2 DBGFLAGS := +else + $(error PROFILE must be observe, debug or release) endif RV_ENV_ROOT ?= /opt/rv-env @@ -21,36 +28,35 @@ LDSCRIPT ?= $(H3_COMMON)/link_hazard3.ld LAB_RUNTIME_DIR ?= $(RV_ENV_ROOT)/vendor/lab-runtime MEMOPS ?= $(LAB_RUNTIME_DIR)/memops.c +LOCAL_FREERTOS := $(abspath ../lab-rv32i-freertos-task-stack-vector/vendor/FreeRTOS-Kernel) +FREERTOS_KERNEL_PATH ?= $(if $(wildcard /opt/FreeRTOS-Kernel/include/FreeRTOS.h),/opt/FreeRTOS-Kernel,$(LOCAL_FREERTOS)) +FREERTOS_PORT := $(FREERTOS_KERNEL_PATH)/portable/GCC/RISC-V +HEAP4_SOURCE := $(FREERTOS_KERNEL_PATH)/portable/MemMang/heap_4.c + BUILD_ROOT ?= build HOST_BUILD_ROOT ?= host-build TASKS := \ - task01_heap_map \ - task02_free_list \ - task03_heap_init \ - task04_alignment \ - task05_malloc_first_fit \ - task06_split_block \ - task07_free_insert \ - task08_coalescing \ - task09_config_macros \ - task10_heap4_demo + task01_first_fit_split \ + task02_address_order_coalesce \ + task03_freertos_heap4 ARCH ?= rv32i_zicsr_zifencei ABI ?= ilp32 - -COMMON_FLAGS := -std=c99 -march=$(ARCH) -mabi=$(ABI) -nostdlib -nostartfiles -ffreestanding -fno-builtin -fno-stack-protector $(DBGFLAGS) -Wall -Wextra -Isrc/tasks -LINK_FLAGS := -Wl,--no-relax -Wl,-T,$(LDSCRIPT) +COMMON_FLAGS := -std=c11 -march=$(ARCH) -mabi=$(ABI) -nostdlib -nostartfiles \ + -ffreestanding -fno-builtin -fno-stack-protector -ffunction-sections \ + -fdata-sections $(OPTFLAGS) $(DBGFLAGS) -Wall -Wextra -Wpedantic +MODEL_CPPFLAGS := -Isrc/tasks +KERNEL_CPPFLAGS := -Iinclude/freestanding -Iinclude -Isrc/tasks \ + -I$(FREERTOS_KERNEL_PATH)/include -I$(FREERTOS_PORT) +LINK_FLAGS := -Wl,--no-relax -Wl,--gc-sections -T $(LDSCRIPT) LIBS := -lgcc -EMIT_FLAGS := -std=c99 -march=$(ARCH) -mabi=$(ABI) -O -ffreestanding -fno-builtin -fno-stack-protector -nostdlib -nostartfiles -Wall -Wextra -Isrc/tasks -CFLAGS_STEP := $(COMMON_FLAGS) -O -HOST_CFLAGS := -std=c99 -O0 -g -Wall -Wextra -DHOST_PRINTF=1 -Isrc/tasks +HOST_FLAGS := -std=c11 $(OPTFLAGS) $(DBGFLAGS) -Wall -Wextra -Wpedantic \ + -DHOST_PRINTF=1 -Isrc/tasks +HOST_HEAP4_FLAGS := $(HOST_FLAGS) -DHEAP4_HOST_ADAPTER=1 -Iinclude/host-shim -.PHONY: all tasks host \ - task1 task2 task3 task4 task5 task6 task7 task8 task9 task10 \ - t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 \ - host-task1 host-task2 host-task3 host-task4 host-task5 host-task6 host-task7 host-task8 host-task9 host-task10 \ - ht1 ht2 ht3 ht4 ht5 ht6 ht7 ht8 ht9 ht10 \ - clean check-support $(TASKS) +.PHONY: all tasks host task1 task2 task3 t1 t2 t3 \ + host-task1 host-task2 host-task3 ht1 ht2 ht3 clean check-support check-kernel \ + $(TASKS) all: tasks tasks: $(TASKS) @@ -59,54 +65,66 @@ host: $(addprefix $(HOST_BUILD_ROOT)/,$(addsuffix /prog,$(TASKS))) task1 t1: $(word 1,$(TASKS)) task2 t2: $(word 2,$(TASKS)) task3 t3: $(word 3,$(TASKS)) -task4 t4: $(word 4,$(TASKS)) -task5 t5: $(word 5,$(TASKS)) -task6 t6: $(word 6,$(TASKS)) -task7 t7: $(word 7,$(TASKS)) -task8 t8: $(word 8,$(TASKS)) -task9 t9: $(word 9,$(TASKS)) -task10 t10: $(word 10,$(TASKS)) host-task1 ht1: $(HOST_BUILD_ROOT)/$(word 1,$(TASKS))/prog host-task2 ht2: $(HOST_BUILD_ROOT)/$(word 2,$(TASKS))/prog host-task3 ht3: $(HOST_BUILD_ROOT)/$(word 3,$(TASKS))/prog -host-task4 ht4: $(HOST_BUILD_ROOT)/$(word 4,$(TASKS))/prog -host-task5 ht5: $(HOST_BUILD_ROOT)/$(word 5,$(TASKS))/prog -host-task6 ht6: $(HOST_BUILD_ROOT)/$(word 6,$(TASKS))/prog -host-task7 ht7: $(HOST_BUILD_ROOT)/$(word 7,$(TASKS))/prog -host-task8 ht8: $(HOST_BUILD_ROOT)/$(word 8,$(TASKS))/prog -host-task9 ht9: $(HOST_BUILD_ROOT)/$(word 9,$(TASKS))/prog -host-task10 ht10: $(HOST_BUILD_ROOT)/$(word 10,$(TASKS))/prog check-support: - @test -f "$(H3_INIT)" || { echo "Missing $(H3_INIT). Run inside rv-env or set RV_ENV_ROOT=/path/to/rv32i-hazard3-env" >&2; exit 1; } + @test -f "$(H3_INIT)" || { echo "Missing $(H3_INIT). Run inside hazard3-sim." >&2; exit 1; } @test -f "$(LDSCRIPT)" || { echo "Missing $(LDSCRIPT)." >&2; exit 1; } @test -f "$(MEMOPS)" || { echo "Missing $(MEMOPS)." >&2; exit 1; } -define task_rules -$(1): $(BUILD_ROOT)/$(1)/prog.bin $(BUILD_ROOT)/$(1)/prog.elf $(BUILD_ROOT)/$(1)/prog.lst $(BUILD_ROOT)/$(1)/$(1).s +check-kernel: + @test -f "$(HEAP4_SOURCE)" || { echo "Missing pinned FreeRTOS heap_4.c at $(HEAP4_SOURCE)." >&2; exit 1; } + @grep -F 'FreeRTOS Kernel V11.3.0' "$(FREERTOS_KERNEL_PATH)/include/FreeRTOS.h" >/dev/null || { echo "Expected FreeRTOS Kernel V11.3.0." >&2; exit 1; } -$(BUILD_ROOT)/$(1)/prog.elf: crt0.S src/tasks/$(1).c src/tasks/heap4_common.h $(H3_INIT) $(LDSCRIPT) $(MEMOPS) | check-support +define model_task_rules +$(1): $(BUILD_ROOT)/$(1)/prog.bin $(BUILD_ROOT)/$(1)/prog.elf $(BUILD_ROOT)/$(1)/prog.lst + +$(BUILD_ROOT)/$(1)/prog.elf: crt0.S src/tasks/$(1).c src/tasks/heap4_model.h $(H3_INIT) $(LDSCRIPT) $(MEMOPS) | check-support mkdir -p $$(@D) - $(CC) $(CFLAGS_STEP) $(LINK_FLAGS) -Wl,-Map,$(BUILD_ROOT)/$(1)/prog.map -o $$@ $(H3_INIT) crt0.S $(MEMOPS) src/tasks/$(1).c $(LIBS) + $(CC) $(COMMON_FLAGS) $(MODEL_CPPFLAGS) $(LINK_FLAGS) -Wl,-Map,$(BUILD_ROOT)/$(1)/prog.map -o $$@ $(H3_INIT) crt0.S $(MEMOPS) src/tasks/$(1).c $(LIBS) $(SIZE) -A -x $$@ $(BUILD_ROOT)/$(1)/prog.bin: $(BUILD_ROOT)/$(1)/prog.elf $(OBJCOPY) -O binary $$< $$@ $(BUILD_ROOT)/$(1)/prog.lst: $(BUILD_ROOT)/$(1)/prog.elf - $(OBJDUMP) -d -M no-aliases,numeric $$< > $$@ + $(OBJDUMP) -d -S -M no-aliases,numeric $$< > $$@ -$(BUILD_ROOT)/$(1)/$(1).s: src/tasks/$(1).c src/tasks/heap4_common.h | check-support +$(HOST_BUILD_ROOT)/$(1)/prog: src/tasks/$(1).c src/tasks/heap4_model.h mkdir -p $$(@D) - $(CC) $(EMIT_FLAGS) -S -o $$@ $$< - -$(HOST_BUILD_ROOT)/$(1)/prog: src/tasks/$(1).c src/tasks/heap4_common.h - mkdir -p $$(@D) - $(HOST_CC) $(HOST_CFLAGS) -o $$@ $$< + $(HOST_CC) $(HOST_FLAGS) -o $$@ $$< endef -$(foreach task,$(TASKS),$(eval $(call task_rules,$(task)))) +$(eval $(call model_task_rules,$(word 1,$(TASKS)))) +$(eval $(call model_task_rules,$(word 2,$(TASKS)))) + +TASK03 := $(word 3,$(TASKS)) +TASK03_KERNEL_SOURCES := \ + $(FREERTOS_KERNEL_PATH)/tasks.c \ + $(FREERTOS_KERNEL_PATH)/list.c \ + $(HEAP4_SOURCE) \ + $(FREERTOS_PORT)/port.c \ + $(FREERTOS_PORT)/portASM.S + +$(TASK03): $(BUILD_ROOT)/$(TASK03)/prog.bin $(BUILD_ROOT)/$(TASK03)/prog.elf $(BUILD_ROOT)/$(TASK03)/prog.lst + +$(BUILD_ROOT)/$(TASK03)/prog.elf: crt0.S src/tasks/$(TASK03).c include/FreeRTOSConfig.h $(TASK03_KERNEL_SOURCES) $(H3_INIT) $(LDSCRIPT) $(MEMOPS) | check-support check-kernel + mkdir -p $(@D) + $(CC) $(COMMON_FLAGS) $(KERNEL_CPPFLAGS) $(LINK_FLAGS) -Wl,-Map,$(BUILD_ROOT)/$(TASK03)/prog.map -o $@ $(H3_INIT) crt0.S $(MEMOPS) $(TASK03_KERNEL_SOURCES) src/tasks/$(TASK03).c $(LIBS) + $(SIZE) -A -x $@ + +$(BUILD_ROOT)/$(TASK03)/prog.bin: $(BUILD_ROOT)/$(TASK03)/prog.elf + $(OBJCOPY) -O binary $< $@ + +$(BUILD_ROOT)/$(TASK03)/prog.lst: $(BUILD_ROOT)/$(TASK03)/prog.elf + $(OBJDUMP) -d -S -M no-aliases,numeric $< > $@ + +$(HOST_BUILD_ROOT)/$(TASK03)/prog: src/tasks/$(TASK03).c include/host-shim/FreeRTOS.h include/host-shim/task.h $(HEAP4_SOURCE) | check-kernel + mkdir -p $(@D) + $(HOST_CC) $(HOST_HEAP4_FLAGS) -o $@ src/tasks/$(TASK03).c $(HEAP4_SOURCE) clean: rm -rf $(BUILD_ROOT) $(HOST_BUILD_ROOT) diff --git a/README.md b/README.md index bb10b1e..9cd0480 100644 --- a/README.md +++ b/README.md @@ -1,73 +1,69 @@ -# rv32i-freertos / heap4 +# Karta pracy: FreeRTOS `heap_4` -Karta pracy `01/14` z serii `rv32i-freertos`; jest opcjonalnym fundamentem L1. +Trzy krótkie zadania po kartach `pointers` i `structures`: -Temat: mechanika alokatora `heap_4.c` z FreeRTOS-a na małych przykładach -uruchamianych w środowisku RV32I. +1. first-fit + split na arenie 256 B; +2. lista adresowa + scalanie `A | B | C`; +3. niezmodyfikowany FreeRTOS-Kernel V11.3.0 `heap_4.c`. -Ta karta jest pomostem po kartach C: używa wskaźników, struktur, listy -jednokierunkowej, `static`, `typedef`, makr i arytmetyki adresów, ale nie -wymaga jeszcze schedulera, tasków ani przerwań. +Task 3 linkuje upstream `portable/MemMang/heap_4.c` z commita +`9b777ae5c5b8e9e456065a00294d1e5f5f9facf5`. -Kod w `src/tasks` jest dydaktycznym modelem zachowania `heap_4`, a nie kopią -pliku upstream FreeRTOS. +- AMD64: rzeczywisty algorytm + jednowątkowy adapter sekcji krytycznej. +- Hazard3: `tasks.c`, `list.c`, `heap_4.c` i oficjalny port RISC-V. +- RP2350: zgodny core+port z forka Raspberry Pi (`4f7299d6ea74`) oraz + podstawiony stabilny allocator V11.3.0. -## Taski - -- `task01_heap_map.c` - mapa pojęć: sterta, blok, nagłówek bloku. -- `task02_free_list.c` - lista wolnych bloków. -- `task03_heap_init.c` - inicjalizacja sterty i wartownik końca listy. -- `task04_alignment.c` - wyrównanie rozmiarów i adresów. -- `task05_malloc_first_fit.c` - pierwszy pasujący wolny blok. -- `task06_split_block.c` - podział większego bloku. -- `task07_free_insert.c` - zwracanie bloku na listę wolnych. -- `task08_coalescing.c` - scalanie sąsiednich wolnych bloków. -- `task09_config_macros.c` - makra konfiguracyjne i warunkowy `printf`. -- `task10_heap4_demo.c` - mini-demonstracja `malloc/free` bez pełnego RTOS-a. - -Hostowe `printf` jest dostępne tylko przez `-DHOST_PRINTF=1`; RV32I zapisuje -wyniki w globalnych zmiennych `volatile`. - -## Gałęzie zadań - -Gałąź `deploy` jest pełną publiczną wersją karty. Każde zadanie ma osobną -gałąź startową `tasks/*`, na przykład `tasks/05-malloc-first-fit`. Taka gałąź -nie jest wycinkiem repozytorium: zawiera dokumentację, treść karty, `Makefile`, -kod pomocniczy i pliki danego zadania. - -Uczeń nie commituje bezpośrednio do `tasks/*`. W repozytorium klasy tworzy -gałąź pochodną, na przykład `students/u01/tasks/05-malloc-first-fit`. - -## Powiązanie z FreeRTOS Book v1.1.0 - -To jest opcjonalna karta `K01` z -[mapy serii](../README.md). Jej źródłem pojęciowym jest rozdział 3 książki -*Mastering the FreeRTOS Real Time Kernel*, przede wszystkim sekcja 3.2.4 o -`heap_4` oraz rysunki 3.1–3.4. Rozdział o heapie nie ma numerowanego programu -z zestawu `Example001`–`Example025`, dlatego karta rozkłada mechanikę -alokatora na własne, krótsze eksperymenty: - -- Taski 01–03 odtwarzają mapę bloku, wolną listę i inicjalizację heapu; -- Taski 04–08 izolują wyrównanie, first-fit, podział, wstawianie i scalanie; -- Task 09 łączy konfigurację z sekcjami 3.1.3–3.1.5; -- Task 10 składa te elementy w jeden model zachowania `heap_4`. - -Karta nie jest kopią upstreamowego `heap_4.c` ani listingów książki. Jest -modelem dydaktycznym, po którym karta `K02` przechodzi na prawdziwy plik -`portable/MemMang/heap_4.c` z FreeRTOS V11.3.0. - -## Budowanie +## Jedno wejście: `stemctl` ```bash -RV_ENV_ROOT=~/dev/workspace/rv/tools/rv32i-hazard3-student-env make tasks -make host +stemctl series cards fetch freertos heap4 +stemctl test native-amd64 freertos heap4 1 +stemctl test hazard3-sim freertos heap4 2 +stemctl debug hazard3-sim freertos heap4 3 +stemctl test rp2350 freertos heap4 3 +stemctl deploy rp2350 freertos heap4 3 --device /dev/bus/usb/BBB/DDD +stemctl debug rp2350 freertos heap4 3 --device /dev/bus/usb/BBB/DDD ``` -## Praca przez rvctl +- `debug rp2350`: roboczy ELF `no_flash` ładowany do SRAM. +- `deploy rp2350`: trwały obraz w SPI flash, odczytany przez breakpointy. + +## Kontrakt zadań + +| Task | Sprawdza | Wynik kluczowy | +|---|---|---| +| 1 | alignment, header, split | `wanted + remainder == 256` | +| 2 | lista po adresie, coalescing | `2 bloki -> 1 blok`, `96 -> 144 B` | +| 3 | prawdziwe API FreeRTOS | free wraca do F0, minimum nie rośnie, OOM = NULL | + +## Debugowanie Task 3 + +Automatyczny start: `heap4_fragmented_checkpoint`. + +```gdb +p g_fragmented_stats +p g_final_stats +p xFreeBytesRemaining +p xMinimumEverFreeBytesRemaining +p xStart +p pxEnd +x/160bx ucHeap +b prvInsertBlockIntoFreeList +b pvPortMalloc +b vPortFree +``` + +W UI: Termdebug po lewej; źródło i listing po prawej; bash pod Neovimem. +`F5` continue, `F9` breakpoint, `F10` next, `F11` step, `Shift-F11` +finish, `F8` stepi, `:StudentStack`, `:StudentMemory`, `:StudentLst`. + +## Test repozytorium ```bash -./rvctl series cards fetch inf heap4 -./rvctl card use inf heap4 -./rvctl tasks list -./rvctl debug rv32i 1 +./tests/test_host.sh +bash -n tools/card-action.sh +./scripts/render_pdf.sh ``` + +Źródło i licencja: [UPSTREAM.md](UPSTREAM.md). diff --git a/UPSTREAM.md b/UPSTREAM.md new file mode 100644 index 0000000..4e6ae0c --- /dev/null +++ b/UPSTREAM.md @@ -0,0 +1,19 @@ +# Upstream contract + +- Project: FreeRTOS-Kernel +- Version: V11.3.0 +- Commit: `9b777ae5c5b8e9e456065a00294d1e5f5f9facf5` +- Source used by Task 3: `portable/MemMang/heap_4.c` +- License: MIT (as shipped in the pinned upstream checkout) + +RP2350 needs a port that is newer than the V11.3.0 tag: + +- Project: Raspberry Pi fork of FreeRTOS-Kernel +- Commit: `4f7299d6ea746b27a9dd19e87af568e34bd65b15` +- Files used on RP2350: matching kernel core plus + `portable/ThirdParty/GCC/RP2350_RISC-V` +- Allocator used on RP2350: still the stable V11.3.0 `heap_4.c` above, + compiled instead of the fork's development-branch allocator. + +The canonical containers provide `/opt/FreeRTOS-Kernel`; the RP2350 profile +also provides `/opt/FreeRTOS-RP2350`. The card carries no allocator copy. diff --git a/build/task01_heap_map/task01_heap_map.s b/build/task01_heap_map/task01_heap_map.s deleted file mode 100644 index dea13e1..0000000 --- a/build/task01_heap_map/task01_heap_map.s +++ /dev/null @@ -1,53 +0,0 @@ - .file "task01_heap_map.c" - .option nopic - .attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0" - .attribute unaligned_access, 0 - .attribute stack_align, 16 - .text - .align 2 - .globl main - .type main, @function -main: - li a5,8 - sw a5,g_header_size,a4 - sw a5,g_aligned_header_size,a4 - li a5,1 - sw a5,g_allocated_bit_set,a4 - lla a5,g_total_block_bytes - li a4,24 - sw a4,0(a5) - lw a5,0(a5) - addi a5,a5,-8 - sw a5,g_user_bytes,a4 - li a0,0 - ret - .size main, .-main - .globl g_total_block_bytes - .globl g_user_bytes - .globl g_allocated_bit_set - .globl g_aligned_header_size - .globl g_header_size - .section .sbss,"aw",@nobits - .align 2 - .type g_total_block_bytes, @object - .size g_total_block_bytes, 4 -g_total_block_bytes: - .zero 4 - .type g_user_bytes, @object - .size g_user_bytes, 4 -g_user_bytes: - .zero 4 - .type g_allocated_bit_set, @object - .size g_allocated_bit_set, 4 -g_allocated_bit_set: - .zero 4 - .type g_aligned_header_size, @object - .size g_aligned_header_size, 4 -g_aligned_header_size: - .zero 4 - .type g_header_size, @object - .size g_header_size, 4 -g_header_size: - .zero 4 - .ident "GCC: (15.2.0-23) 15.2.0" - .section .note.GNU-stack,"",@progbits diff --git a/build/task02_free_list/task02_free_list.s b/build/task02_free_list/task02_free_list.s deleted file mode 100644 index 5dd5d99..0000000 --- a/build/task02_free_list/task02_free_list.s +++ /dev/null @@ -1,73 +0,0 @@ - .file "task02_free_list.c" - .option nopic - .attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0" - .attribute unaligned_access, 0 - .attribute stack_align, 16 - .text - .align 2 - .globl main - .type main, @function -main: - addi sp,sp,-32 - addi a5,sp,16 - sw a5,24(sp) - li a5,32 - sw a5,28(sp) - addi a5,sp,8 - sw a5,16(sp) - li a5,80 - sw a5,20(sp) - sw zero,8(sp) - li a5,24 - sw a5,12(sp) - li a4,0 - add a5,sp,a5 -.L2: - lw a3,4(a5) - add a4,a4,a3 - lw a5,0(a5) - bne a5,zero,.L2 - sw a4,g_free_total,a5 - li a3,0 - addi a5,sp,24 - j .L4 -.L3: - lw a5,0(a5) - beq a5,zero,.L10 -.L4: - lw a4,4(a5) - bgeu a3,a4,.L3 - mv a3,a4 - j .L3 -.L10: - sw a3,g_largest_free,a5 - addi a5,sp,24 - li a4,0 -.L5: - addi a4,a4,1 - lw a5,0(a5) - bne a5,zero,.L5 - sw a4,g_node_count,a5 - li a0,0 - addi sp,sp,32 - jr ra - .size main, .-main - .globl g_node_count - .globl g_largest_free - .globl g_free_total - .section .sbss,"aw",@nobits - .align 2 - .type g_node_count, @object - .size g_node_count, 4 -g_node_count: - .zero 4 - .type g_largest_free, @object - .size g_largest_free, 4 -g_largest_free: - .zero 4 - .type g_free_total, @object - .size g_free_total, 4 -g_free_total: - .zero 4 - .ident "GCC: (15.2.0-23) 15.2.0" - .section .note.GNU-stack,"",@progbits diff --git a/build/task03_heap_init/task03_heap_init.s b/build/task03_heap_init/task03_heap_init.s deleted file mode 100644 index cffae9f..0000000 --- a/build/task03_heap_init/task03_heap_init.s +++ /dev/null @@ -1,74 +0,0 @@ - .file "task03_heap_init.c" - .option nopic - .attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0" - .attribute unaligned_access, 0 - .attribute stack_align, 16 - .text - .align 2 - .globl main - .type main, @function -main: - lla a2,.LANCHOR0 - andi a4,a2,7 - mv a5,a2 - beq a4,zero,.L2 - andi a5,a2,-8 - addi a5,a5,8 -.L2: - lla a3,.LANCHOR0+160 - sub a3,a3,a5 - addi a4,a5,-8 - andi a3,a3,-8 - add a4,a4,a3 - sw a4,end_marker,a3 - sw zero,0(a4) - sw zero,4(a4) - sw a4,0(a5) - sub a3,a4,a5 - sw a3,4(a5) - lla a1,start - sw a5,0(a1) - sw zero,4(a1) - sub a5,a5,a2 - sw a5,g_aligned_offset,a2 - sw a3,g_initial_free_size,a5 - lw a5,0(a4) - seqz a5,a5 - sw a5,g_end_is_last,a4 - li a0,0 - ret - .size main, .-main - .globl g_end_is_last - .globl g_aligned_offset - .globl g_initial_free_size - .bss - .align 2 - .set .LANCHOR0,. + 0 - .type heap_area, @object - .size heap_area, 168 -heap_area: - .zero 168 - .section .sbss,"aw",@nobits - .align 2 - .type g_end_is_last, @object - .size g_end_is_last, 4 -g_end_is_last: - .zero 4 - .type g_aligned_offset, @object - .size g_aligned_offset, 4 -g_aligned_offset: - .zero 4 - .type g_initial_free_size, @object - .size g_initial_free_size, 4 -g_initial_free_size: - .zero 4 - .type end_marker, @object - .size end_marker, 4 -end_marker: - .zero 4 - .type start, @object - .size start, 8 -start: - .zero 8 - .ident "GCC: (15.2.0-23) 15.2.0" - .section .note.GNU-stack,"",@progbits diff --git a/build/task04_alignment/task04_alignment.s b/build/task04_alignment/task04_alignment.s deleted file mode 100644 index db04ead..0000000 --- a/build/task04_alignment/task04_alignment.s +++ /dev/null @@ -1,47 +0,0 @@ - .file "task04_alignment.c" - .option nopic - .attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0" - .attribute unaligned_access, 0 - .attribute stack_align, 16 - .text - .align 2 - .globl main - .type main, @function -main: - addi sp,sp,-32 - li a5,16 - sw a5,g_request_1,a4 - li a5,24 - sw a5,g_request_9,a4 - li a5,32 - sw a5,g_request_17,a4 - li a5,7 - sw a5,g_address_delta,a4 - li a0,0 - addi sp,sp,32 - jr ra - .size main, .-main - .globl g_address_delta - .globl g_request_17 - .globl g_request_9 - .globl g_request_1 - .section .sbss,"aw",@nobits - .align 2 - .type g_address_delta, @object - .size g_address_delta, 4 -g_address_delta: - .zero 4 - .type g_request_17, @object - .size g_request_17, 4 -g_request_17: - .zero 4 - .type g_request_9, @object - .size g_request_9, 4 -g_request_9: - .zero 4 - .type g_request_1, @object - .size g_request_1, 4 -g_request_1: - .zero 4 - .ident "GCC: (15.2.0-23) 15.2.0" - .section .note.GNU-stack,"",@progbits diff --git a/build/task05_malloc_first_fit/task05_malloc_first_fit.s b/build/task05_malloc_first_fit/task05_malloc_first_fit.s deleted file mode 100644 index 34fc98e..0000000 --- a/build/task05_malloc_first_fit/task05_malloc_first_fit.s +++ /dev/null @@ -1,95 +0,0 @@ - .file "task05_malloc_first_fit.c" - .option nopic - .attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0" - .attribute unaligned_access, 0 - .attribute stack_align, 16 - .text - .align 2 - .globl main - .type main, @function -main: - lla a4,.LANCHOR0 - andi a5,a4,7 - beq a5,zero,.L2 - andi a4,a4,-8 - addi a4,a4,8 -.L2: - mv a5,a4 - addi a3,a4,120 - sw a3,end_marker,a2 - sw zero,120(a4) - sw zero,4(a3) - sw a3,0(a4) - li a2,120 - sw a2,4(a4) - lla a2,start - sw a4,0(a2) - sw zero,4(a2) - li a1,31 - j .L5 -.L6: - mv a5,a4 -.L5: - lw a4,4(a5) - bgtu a4,a1,.L7 - lw a4,0(a5) - mv a2,a5 - bne a4,a3,.L6 - li a5,0 - j .L4 -.L7: - lw a4,0(a5) - sw a4,0(a2) - sw zero,0(a5) - lw a4,4(a5) - li a3,-2147483648 - or a4,a4,a3 - sw a4,4(a5) - addi a5,a5,8 -.L4: - snez a4,a5 - sw a4,g_allocation_ok,a3 - lw a5,-4(a5) - slli a5,a5,1 - srli a5,a5,1 - sw a5,g_allocated_size,a4 - lw a5,start - lw a5,4(a5) - sw a5,g_remaining_free,a4 - li a0,0 - ret - .size main, .-main - .globl g_allocation_ok - .globl g_remaining_free - .globl g_allocated_size - .bss - .align 2 - .set .LANCHOR0,. + 0 - .type heap_area, @object - .size heap_area, 136 -heap_area: - .zero 136 - .section .sbss,"aw",@nobits - .align 2 - .type g_allocation_ok, @object - .size g_allocation_ok, 4 -g_allocation_ok: - .zero 4 - .type g_remaining_free, @object - .size g_remaining_free, 4 -g_remaining_free: - .zero 4 - .type g_allocated_size, @object - .size g_allocated_size, 4 -g_allocated_size: - .zero 4 - .type end_marker, @object - .size end_marker, 4 -end_marker: - .zero 4 - .type start, @object - .size start, 8 -start: - .zero 8 - .ident "GCC: (15.2.0-23) 15.2.0" - .section .note.GNU-stack,"",@progbits diff --git a/build/task06_split_block/task06_split_block.s b/build/task06_split_block/task06_split_block.s deleted file mode 100644 index 610dfa4..0000000 --- a/build/task06_split_block/task06_split_block.s +++ /dev/null @@ -1,117 +0,0 @@ - .file "task06_split_block.c" - .option nopic - .attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0" - .attribute unaligned_access, 0 - .attribute stack_align, 16 - .text - .align 2 - .globl main - .type main, @function -main: - lla a4,.LANCHOR0 - andi a5,a4,7 - beq a5,zero,.L2 - andi a4,a4,-8 - addi a4,a4,8 -.L2: - mv a5,a4 - addi a3,a4,176 - sw a3,end_marker,a2 - sw zero,176(a4) - sw zero,4(a3) - sw a3,0(a4) - li a2,176 - sw a2,4(a4) - lla a2,start - sw a4,0(a2) - sw zero,4(a2) - li a1,39 - j .L7 -.L10: - mv a5,a4 -.L7: - lw a4,4(a5) - bgtu a4,a1,.L13 - lw a4,0(a5) - mv a2,a5 - bne a4,a3,.L10 - li a5,0 - j .L6 -.L13: - addi a4,a4,-40 - li a1,16 - bleu a4,a1,.L4 - sw a4,44(a5) - lw a4,0(a5) - sw a4,40(a5) - addi a4,a5,40 - sw a4,0(a2) - li a4,40 - sw a4,4(a5) -.L5: - sw zero,0(a5) - lw a4,4(a5) - li a2,-2147483648 - or a4,a4,a2 - sw a4,4(a5) - addi a5,a5,8 -.L6: - lw a5,-4(a5) - slli a5,a5,1 - srli a5,a5,1 - sw a5,g_allocated_size,a4 - lw a5,start - lw a4,4(a5) - sw a4,g_split_free_size,a2 - beq a5,a3,.L11 - li a4,0 -.L9: - addi a4,a4,1 - lw a5,0(a5) - bne a5,a3,.L9 -.L8: - sw a4,g_free_nodes,a5 - li a0,0 - ret -.L4: - lw a4,0(a5) - sw a4,0(a2) - j .L5 -.L11: - li a4,0 - j .L8 - .size main, .-main - .globl g_free_nodes - .globl g_split_free_size - .globl g_allocated_size - .bss - .align 2 - .set .LANCHOR0,. + 0 - .type heap_area, @object - .size heap_area, 200 -heap_area: - .zero 200 - .section .sbss,"aw",@nobits - .align 2 - .type g_free_nodes, @object - .size g_free_nodes, 4 -g_free_nodes: - .zero 4 - .type g_split_free_size, @object - .size g_split_free_size, 4 -g_split_free_size: - .zero 4 - .type g_allocated_size, @object - .size g_allocated_size, 4 -g_allocated_size: - .zero 4 - .type end_marker, @object - .size end_marker, 4 -end_marker: - .zero 4 - .type start, @object - .size start, 8 -start: - .zero 8 - .ident "GCC: (15.2.0-23) 15.2.0" - .section .note.GNU-stack,"",@progbits diff --git a/build/task07_free_insert/task07_free_insert.s b/build/task07_free_insert/task07_free_insert.s deleted file mode 100644 index 4dd5308..0000000 --- a/build/task07_free_insert/task07_free_insert.s +++ /dev/null @@ -1,116 +0,0 @@ - .file "task07_free_insert.c" - .option nopic - .attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0" - .attribute unaligned_access, 0 - .attribute stack_align, 16 - .text - .align 2 - .globl main - .type main, @function -main: - lla a5,.LANCHOR0 - lla a4,.LANCHOR0+176 - sw a4,end_marker,a3 - sw a5,start,a3 - lla a3,.LANCHOR0+128 - sw a3,0(a5) - sw a4,128(a5) - sw zero,176(a5) - li a4,40 - sw a4,4(a5) - li a4,48 - sw a4,132(a5) - sw zero,180(a5) - li a4,32 - sw a4,68(a5) - lla a5,start - lla a3,.LANCHOR0+64 -.L2: - mv a4,a5 - lw a5,0(a5) - bltu a5,a3,.L2 - lla a3,.LANCHOR0 - sw a5,64(a3) - lla a5,.LANCHOR0+64 - sw a5,0(a4) - lw a5,start - beq a5,a3,.L16 - sw zero,g_order_ok,a4 - lla a4,.LANCHOR0+176 - beq a5,a4,.L5 -.L9: - mv a4,a5 - li a3,0 - lla a2,.LANCHOR0+176 -.L6: - addi a3,a3,1 - lw a4,0(a4) - bne a4,a2,.L6 - sw a3,g_free_count,a4 - li a4,0 - lla a2,.LANCHOR0+176 -.L7: - lw a3,4(a5) - add a4,a4,a3 - lw a5,0(a5) - bne a5,a2,.L7 -.L8: - sw a4,g_total_free,a5 - li a0,0 - ret -.L16: - lla a3,.LANCHOR0+64 - lw a2,.LANCHOR0 - li a4,0 - beq a2,a3,.L17 -.L4: - sw a4,g_order_ok,a3 - j .L9 -.L17: - lla a3,.LANCHOR0+128 - lw a2,.LANCHOR0+64 - bne a2,a3,.L4 - lla a3,.LANCHOR0+176 - lw a4,.LANCHOR0+128 - sub a4,a4,a3 - seqz a4,a4 - j .L4 -.L5: - sw zero,g_free_count,a5 - li a4,0 - j .L8 - .size main, .-main - .globl g_total_free - .globl g_free_count - .globl g_order_ok - .bss - .align 2 - .set .LANCHOR0,. + 0 - .type heap_area, @object - .size heap_area, 192 -heap_area: - .zero 192 - .section .sbss,"aw",@nobits - .align 2 - .type g_total_free, @object - .size g_total_free, 4 -g_total_free: - .zero 4 - .type g_free_count, @object - .size g_free_count, 4 -g_free_count: - .zero 4 - .type g_order_ok, @object - .size g_order_ok, 4 -g_order_ok: - .zero 4 - .type end_marker, @object - .size end_marker, 4 -end_marker: - .zero 4 - .type start, @object - .size start, 8 -start: - .zero 8 - .ident "GCC: (15.2.0-23) 15.2.0" - .section .note.GNU-stack,"",@progbits diff --git a/build/task08_coalescing/task08_coalescing.s b/build/task08_coalescing/task08_coalescing.s deleted file mode 100644 index a60e79e..0000000 --- a/build/task08_coalescing/task08_coalescing.s +++ /dev/null @@ -1,116 +0,0 @@ - .file "task08_coalescing.c" - .option nopic - .attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0" - .attribute unaligned_access, 0 - .attribute stack_align, 16 - .text - .align 2 - .globl main - .type main, @function -main: - lla a5,.LANCHOR0 - lla a4,.LANCHOR0+160 - sw a4,end_marker,a3 - sw a5,start,a3 - lla a3,.LANCHOR0+96 - sw a3,0(a5) - sw a4,96(a5) - sw zero,160(a5) - li a4,48 - sw a4,4(a5) - sw a4,52(a5) - li a4,64 - sw a4,100(a5) - sw zero,164(a5) - lla a5,start - lla a3,.LANCHOR0+48 -.L2: - mv a4,a5 - lw a5,0(a5) - bltu a5,a3,.L2 - lla a3,.LANCHOR0+96 - beq a5,a3,.L12 -.L3: - sw a5,.LANCHOR0+48,a3 - lla a5,start - lla a3,.LANCHOR0+48 - beq a4,a5,.L4 - lw a2,4(a4) - slli a5,a2,1 - srli a5,a5,1 - add a5,a4,a5 - beq a5,a3,.L13 -.L4: - sw a3,0(a4) - lw a2,start - lla a5,.LANCHOR0+160 - beq a2,a5,.L9 - mv a5,a2 - li a4,0 - lla a3,.LANCHOR0+160 -.L6: - addi a4,a4,1 - lw a5,0(a5) - bne a5,a3,.L6 -.L5: - sw a4,g_free_count,a5 - lw a5,4(a2) - sw a5,g_merged_size,a4 - lw a5,0(a2) - lla a4,.LANCHOR0+160 - sub a5,a5,a4 - seqz a5,a5 - sw a5,g_merged_with_right,a4 - li a0,0 - ret -.L12: - li a5,112 - sw a5,.LANCHOR0+52,a3 - lw a5,0(a4) - lw a5,0(a5) - j .L3 -.L13: - lla a3,.LANCHOR0 - lw a5,52(a3) - add a5,a5,a2 - sw a5,4(a4) - lw a3,48(a3) - j .L4 -.L9: - li a4,0 - j .L5 - .size main, .-main - .globl g_merged_with_right - .globl g_merged_size - .globl g_free_count - .bss - .align 2 - .set .LANCHOR0,. + 0 - .type heap_area, @object - .size heap_area, 192 -heap_area: - .zero 192 - .section .sbss,"aw",@nobits - .align 2 - .type g_merged_with_right, @object - .size g_merged_with_right, 4 -g_merged_with_right: - .zero 4 - .type g_merged_size, @object - .size g_merged_size, 4 -g_merged_size: - .zero 4 - .type g_free_count, @object - .size g_free_count, 4 -g_free_count: - .zero 4 - .type end_marker, @object - .size end_marker, 4 -end_marker: - .zero 4 - .type start, @object - .size start, 8 -start: - .zero 8 - .ident "GCC: (15.2.0-23) 15.2.0" - .section .note.GNU-stack,"",@progbits diff --git a/build/task09_config_macros/task09_config_macros.s b/build/task09_config_macros/task09_config_macros.s deleted file mode 100644 index de74cea..0000000 --- a/build/task09_config_macros/task09_config_macros.s +++ /dev/null @@ -1,44 +0,0 @@ - .file "task09_config_macros.c" - .option nopic - .attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0" - .attribute unaligned_access, 0 - .attribute stack_align, 16 - .text - .align 2 - .globl main - .type main, @function -main: - li a5,256 - sw a5,g_total_heap,a4 - li a5,248 - sw a5,g_adjusted_heap,a4 - li a5,1 - sw a5,g_dynamic_enabled,a4 - sw zero,g_trace_is_host_only,a5 - li a0,0 - ret - .size main, .-main - .globl g_trace_is_host_only - .globl g_dynamic_enabled - .globl g_adjusted_heap - .globl g_total_heap - .section .sbss,"aw",@nobits - .align 2 - .type g_trace_is_host_only, @object - .size g_trace_is_host_only, 4 -g_trace_is_host_only: - .zero 4 - .type g_dynamic_enabled, @object - .size g_dynamic_enabled, 4 -g_dynamic_enabled: - .zero 4 - .type g_adjusted_heap, @object - .size g_adjusted_heap, 4 -g_adjusted_heap: - .zero 4 - .type g_total_heap, @object - .size g_total_heap, 4 -g_total_heap: - .zero 4 - .ident "GCC: (15.2.0-23) 15.2.0" - .section .note.GNU-stack,"",@progbits diff --git a/build/task10_heap4_demo/task10_heap4_demo.s b/build/task10_heap4_demo/task10_heap4_demo.s deleted file mode 100644 index 215bceb..0000000 --- a/build/task10_heap4_demo/task10_heap4_demo.s +++ /dev/null @@ -1,253 +0,0 @@ - .file "task10_heap4_demo.c" - .option nopic - .attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0" - .attribute unaligned_access, 0 - .attribute stack_align, 16 - .text - .align 2 - .type heap_malloc_model, @function -heap_malloc_model: - beq a0,zero,.L1 - addi a4,a0,8 - andi a0,a0,7 - beq a0,zero,.L3 - andi a4,a4,-8 - addi a4,a4,8 -.L3: - lw a0,start - lw a3,end_marker - beq a0,a3,.L10 - lla a2,start - j .L8 -.L11: - mv a0,a5 -.L8: - lw a5,4(a0) - bgeu a5,a4,.L12 - lw a5,0(a0) - mv a2,a0 - bne a5,a3,.L11 - li a0,0 - ret -.L12: - sub a3,a5,a4 - li a1,16 - bleu a3,a1,.L5 - add a5,a0,a4 - sw a3,4(a5) - lw a3,0(a0) - sw a3,0(a5) - sw a5,0(a2) - sw a4,4(a0) -.L6: - sw zero,0(a0) - lw a5,4(a0) - li a3,-2147483648 - or a5,a5,a3 - sw a5,4(a0) - lla a3,free_bytes_remaining - lw a5,0(a3) - sub a5,a5,a4 - sw a5,0(a3) - lw a4,minimum_ever_free - bgeu a5,a4,.L7 - sw a5,minimum_ever_free,a4 -.L7: - addi a0,a0,8 - ret -.L5: - lw a4,0(a0) - sw a4,0(a2) - mv a4,a5 - j .L6 -.L10: - li a0,0 -.L1: - ret - .size heap_malloc_model, .-heap_malloc_model - .align 2 - .type heap_free_model, @function -heap_free_model: - beq a0,zero,.L13 - lw a5,-4(a0) - blt a5,zero,.L19 -.L13: - ret -.L19: - addi a3,a0,-8 - slli a5,a5,1 - srli a2,a5,1 - sw a2,-4(a0) - lla a4,free_bytes_remaining - lw a5,0(a4) - add a5,a5,a2 - sw a5,0(a4) - lla a5,start -.L15: - mv a4,a5 - lw a5,0(a5) - bgtu a3,a5,.L15 - add a1,a3,a2 - beq a5,a1,.L20 -.L16: - sw a5,-8(a0) - lla a5,start - beq a4,a5,.L17 - lw a2,4(a4) - slli a5,a2,1 - srli a5,a5,1 - add a5,a4,a5 - beq a3,a5,.L21 -.L17: - sw a3,0(a4) - ret -.L20: - lw a5,4(a5) - add a5,a5,a2 - sw a5,-4(a0) - lw a5,0(a4) - lw a5,0(a5) - j .L16 -.L21: - lw a5,-4(a0) - add a5,a5,a2 - sw a5,4(a4) - lw a5,-8(a0) - sw a5,0(a4) - ret - .size heap_free_model, .-heap_free_model - .align 2 - .globl main - .type main, @function -main: - addi sp,sp,-32 - sw ra,28(sp) - sw s0,24(sp) - sw s1,20(sp) - sw s2,16(sp) - sw s3,12(sp) - sw s4,8(sp) - lla a4,.LANCHOR0 - andi a3,a4,7 - mv a5,a4 - beq a3,zero,.L23 - andi a4,a4,-8 - addi a5,a4,8 -.L23: - lla s0,.LANCHOR0+256 - sub s0,s0,a5 - andi s0,s0,-8 - addi a4,a5,-8 - add s0,s0,a4 - sw s0,end_marker,a4 - sw zero,0(s0) - sw zero,4(s0) - sw s0,0(a5) - sub a4,s0,a5 - sw a4,4(a5) - lla s4,start - sw a5,0(s4) - sw zero,4(s4) - sw a4,free_bytes_remaining,a5 - sw a4,minimum_ever_free,a5 - li a0,24 - call heap_malloc_model - mv s2,a0 - li a0,40 - call heap_malloc_model - mv s3,a0 - mv a0,s2 - call heap_free_model - li a0,16 - call heap_malloc_model - mv s1,a0 - mv a0,s3 - call heap_free_model - mv a0,s1 - call heap_free_model - snez s2,s2 - snez s3,s3 - and s2,s2,s3 - snez s1,s1 - and s1,s1,s2 - sw s1,g_allocations_ok,a5 - lw a5,0(s4) - beq s0,a5,.L24 - mv a4,a5 - li a3,0 -.L25: - addi a3,a3,1 - lw a4,0(a4) - bne s0,a4,.L25 - sw a3,g_final_free_nodes,a2 - li a3,0 -.L26: - lw a2,4(a5) - add a3,a3,a2 - lw a5,0(a5) - bne a5,a4,.L26 -.L27: - sw a3,g_final_free_total,a5 - lw a5,minimum_ever_free - sw a5,g_minimum_ever_free,a4 - li a0,0 - lw ra,28(sp) - lw s0,24(sp) - lw s1,20(sp) - lw s2,16(sp) - lw s3,12(sp) - lw s4,8(sp) - addi sp,sp,32 - jr ra -.L24: - sw zero,g_final_free_nodes,a5 - li a3,0 - j .L27 - .size main, .-main - .globl g_minimum_ever_free - .globl g_final_free_total - .globl g_final_free_nodes - .globl g_allocations_ok - .bss - .align 2 - .set .LANCHOR0,. + 0 - .type heap_area, @object - .size heap_area, 264 -heap_area: - .zero 264 - .section .sbss,"aw",@nobits - .align 2 - .type minimum_ever_free, @object - .size minimum_ever_free, 4 -minimum_ever_free: - .zero 4 - .type free_bytes_remaining, @object - .size free_bytes_remaining, 4 -free_bytes_remaining: - .zero 4 - .type g_minimum_ever_free, @object - .size g_minimum_ever_free, 4 -g_minimum_ever_free: - .zero 4 - .type g_final_free_total, @object - .size g_final_free_total, 4 -g_final_free_total: - .zero 4 - .type g_final_free_nodes, @object - .size g_final_free_nodes, 4 -g_final_free_nodes: - .zero 4 - .type g_allocations_ok, @object - .size g_allocations_ok, 4 -g_allocations_ok: - .zero 4 - .type end_marker, @object - .size end_marker, 4 -end_marker: - .zero 4 - .type start, @object - .size start, 8 -start: - .zero 8 - .ident "GCC: (15.2.0-23) 15.2.0" - .section .note.GNU-stack,"",@progbits diff --git a/doc/main.tex b/doc/main.tex index 074d059..ccb668c 100644 --- a/doc/main.tex +++ b/doc/main.tex @@ -1,21 +1,18 @@ -\documentclass[12pt]{article} +\documentclass[11pt]{article} \usepackage[T1]{fontenc} \usepackage[utf8]{inputenc} \usepackage[polish]{babel} -\usepackage[a4paper,margin=2.0cm]{geometry} -\usepackage{array} -\usepackage{tabularx} -\usepackage{xcolor} -\usepackage{listings} -\usepackage{hyperref} -\usepackage{fancyhdr} -\usepackage{lastpage} +\usepackage[a4paper,margin=1.75cm]{geometry} +\usepackage{array,tabularx,booktabs} +\usepackage{amsmath,amssymb} +\usepackage{xcolor,listings} +\usepackage{hyperref,fancyhdr,lastpage} \usepackage{enumitem} \definecolor{accent}{HTML}{16324A} \definecolor{accentlight}{HTML}{EEF3F7} \definecolor{rulegray}{HTML}{D7DEE5} -\hypersetup{colorlinks=true,linkcolor=blue,urlcolor=blue,citecolor=blue} +\hypersetup{colorlinks=true,linkcolor=accent,urlcolor=blue} \IfFileExists{build-meta.tex}{\input{build-meta.tex}}{\newcommand{\BuildCommit}{local}} \newcommand{\PublisherDomain}{mpabi} \newcommand{\DocumentAuthor}{M. Pabiszczak} @@ -25,267 +22,290 @@ \newcommand{\CardNumber}{01} \newcommand{\CardCount}{14} \newcommand{\CardSlug}{heap4} -\newcommand{\CardVersion}{v0.1} +\newcommand{\CardVersion}{v0.2} \newcommand{\DocumentKey}{\PublisherDomain/\CardArea/\CardSeries/\CardNumber/\CardSlug/\CardVersion} \newcommand{\DocumentUUID}{4654524c-73f2-46df-83a3-d448ba6d6c5a} -\renewcommand{\lstlistingname}{Listing} -\renewcommand{\lstlistlistingname}{Lista listingów} - -\lstset{basicstyle=\ttfamily\small,columns=fullflexible,keepspaces=true,frame=single,breaklines=true,showstringspaces=false,numbers=left,numberstyle=\tiny\color{accent},stepnumber=1,numbersep=8pt,backgroundcolor=\color{accentlight},rulecolor=\color{rulegray},captionpos=b} -\newcommand{\TaskSection}[5]{\section{Task #1: #3}\subsection*{Algorytm i zakresy linii}#4\lstinputlisting[language=C,caption={Task #1: #3},label={lst:task#1-c}]{../src/tasks/#2.c}\subsection*{Co sprawdzić w ASM}#5\lstinputlisting[basicstyle=\ttfamily\scriptsize,caption={ASM dla task #1},label={lst:task#1-asm}]{../build/#2/#2.s}} +\lstset{ + language=C,basicstyle=\ttfamily\scriptsize,columns=fullflexible, + keepspaces=true,frame=single,breaklines=true,showstringspaces=false, + numbers=left,numberstyle=\tiny\color{accent},numbersep=6pt, + backgroundcolor=\color{accentlight},rulecolor=\color{rulegray} +} \pagestyle{fancy} \fancyhf{} \lhead{\textbf{FreeRTOS: heap\_4}} -\rhead{\small \DocumentAuthor, \DocumentYear} +\rhead{\small karta 01/14} \lfoot{\scriptsize commit \BuildCommit} \cfoot{\scriptsize \thepage/\pageref{LastPage}} -\rfoot{\scriptsize uuid \DocumentUUID} -\renewcommand{\headrulewidth}{0.4pt} -\setlength{\headheight}{18pt} -\setlength{\footskip}{24pt} +\rfoot{\scriptsize V11.3.0 / \CardVersion} +\setlength{\headheight}{15pt} +\setlength{\footskip}{22pt} +\setlist[itemize]{nosep,leftmargin=1.5em} +\setlist[enumerate]{itemsep=.25em,leftmargin=1.7em} +\newcommand{\checkline}{\(\square\)\;} \begin{document} \sloppy -\noindent -\begin{tabular}{@{}lp{0.72\textwidth}@{}} -Project: & FreeRTOS: \texttt{heap\_4} \\ -Subject: & Informatyka \\ -Level: & 1, Zakres Podstawowy, PP \\ -Topic: & dynamiczna alokacja pamięci, lista wolnych bloków, scalanie bloków \\ -Revision date: & 14.07.2026 \\ -Status: & Draft \\ -Card in series: & \small\texttt{\CardNumber/\CardCount} \\ -Card version: & \small\texttt{\CardVersion} \\ -Card key: & \small\texttt{\DocumentKey} \\ -UUID: & \small\texttt{\DocumentUUID} \\ -Commit: & \small\texttt{\BuildCommit} \\ -Scope: & pierwsza karta serii FreeRTOS; model dydaktyczny mechaniki \texttt{heap\_4.c} \\ -Files used: & \small -\begin{tabular}[t]{@{}l@{}} -Task01: \texttt{task01\_heap\_map.c} \\ -Task02: \texttt{task02\_free\_list.c} \\ -Task03: \texttt{task03\_heap\_init.c} \\ -Task04: \texttt{task04\_alignment.c} \\ -Task05: \texttt{task05\_malloc\_first\_fit.c} \\ -Task06: \texttt{task06\_split\_block.c} \\ -Task07: \texttt{task07\_free\_insert.c} \\ -Task08: \texttt{task08\_coalescing.c} \\ -Task09: \texttt{task09\_config\_macros.c} \\ -Task10: \texttt{task10\_heap4\_demo.c} \\ -Common: \texttt{heap4\_common.h} \\ -Runtime: \texttt{memops.c} \\ -Startup: \texttt{crt0.S} -\end{tabular} -\end{tabular} -\vspace{0.8em} -\noindent\rule{\textwidth}{0.5pt} +\begin{center} + {\LARGE\bfseries Karta pracy: FreeRTOS \texttt{heap\_4}}\par + \vspace{.4em} + {\large model $\rightarrow$ prawdziwy upstream $\rightarrow$ RP2350}\par +\end{center} -\section{Cel} -Ta karta zaczyna serię FreeRTOS od \texttt{heap\_4}, ponieważ ten plik jest -naturalnym pomostem po kartach z języka C. Wymaga wskaźników, tablic, -struktur, listy jednokierunkowej, \texttt{static}, \texttt{typedef}, makr oraz -arytmetyki adresów, ale nie wymaga jeszcze schedulera, przerwań ani -uruchamiania tasków. - -Przykłady są małym modelem edukacyjnym mechaniki \texttt{heap\_4.c}. Nie są -kopią pliku upstream FreeRTOS. Na RV32I wyniki są zapisywane w globalnych -zmiennych \texttt{volatile}; \texttt{printf} jest dostępny wyłącznie w wariancie -hostowym przez flagę \texttt{-DHOST\_PRINTF=1}. - -W tej karcie słowo „sterta” oznacza obszar pamięci, którym zarządza alokator. -W \texttt{heap\_4} taki obszar jest zwykle statyczną tablicą bajtów, więc linker -może umieścić go w sekcji \texttt{.bss}. Sama sekcja \texttt{.bss} nie jest jednak -alokacją dynamiczną: dopiero kod \texttt{pvPortMalloc}/\texttt{vPortFree} albo -model z tej karty dzieli bajty tego bufora na bloki, prowadzi listę wolnych -bloków i wykonuje scalanie. W Task 03 widać ten moment przejścia od zwykłego -statycznego bufora do sterty zarządzanej przez algorytm. - -Listingi C i ASM mają numerowane linie. Opisy w sekcjach tasków odnoszą się -do numerów widocznych po lewej stronie listingów C. - -\subsection{Powiązanie z FreeRTOS Book v1.1.0} - -Karta K01 odpowiada rozdziałowi 3 książki \emph{Mastering the FreeRTOS Real -Time Kernel}, przede wszystkim sekcji 3.2.4 o \texttt{heap\_4} oraz rysunkom -3.1--3.4. Ten rozdział nie ma programu z numerowanego zestawu -\texttt{Example001}--\texttt{Example025}. Dlatego Taski 01--08 są krótkimi, -własnymi eksperymentami izolującymi kolejne elementy algorytmu, a Task 10 -składa je w jeden model. - -Nie kopiujemy listingu upstream ani projektu Win32/MSVC. Ta karta służy do -zrozumienia mechaniki; karta K02 przechodzi na niezmodyfikowany -\texttt{portable/MemMang/heap\_4.c} z FreeRTOS V11.3.0. Pełna mapa książki, -lekcji i kart znajduje się w pliku \texttt{series/inf/README.md}. - -\lstinputlisting[language=C,caption={Wspólny nagłówek dla karty \texttt{heap\_4}},label={lst:common-h}]{../src/tasks/heap4_common.h} - -\subsection{\texttt{static inline} w nagłówku} -Helpery z Listingu~\ref{lst:common-h} są zapisane jako \texttt{static inline}, -bo każdy task jest osobnym programem i dołącza ten sam nagłówek. \texttt{static} -daje prywatną kopię funkcji w każdym pliku \texttt{.c}, a \texttt{inline} -pozwala kompilatorowi wstawić krótkie obliczenia, takie jak wyrównanie rozmiaru -lub sprawdzenie bitu alokacji, bez kosztu zwykłego wywołania funkcji. - -\section{Organizacja gałęzi} -Gałąź \texttt{deploy} jest pełną publiczną wersją karty. Każde zadanie ma -osobną gałąź startową \texttt{tasks/*}, na przykład -\texttt{tasks/05-malloc-first-fit}. Taka gałąź nie jest wycinkiem repozytorium: -zawiera dokumentację, treść karty, \texttt{Makefile}, kod pomocniczy i pliki -danego zadania. - -Uczeń nie commituje bezpośrednio do \texttt{tasks/*}. W repozytorium klasy -tworzy gałąź pochodną, na przykład -\texttt{students/u01/tasks/05-malloc-first-fit}. - -\section{Mapa karty} -\begin{tabularx}{\textwidth}{|p{2.1cm}|X|} -\hline -Task & Temat \\ -\hline -1 & Mapa pojęć: obszar sterty, nagłówek bloku i bit alokacji. \\ -\hline -2 & Lista wolnych bloków jako lista jednokierunkowa. \\ -\hline -3 & Inicjalizacja sterty, pierwszy wolny blok i wartownik końca listy. \\ -\hline -4 & Wyrównanie rozmiarów żądań i adresu początku sterty. \\ -\hline -5 & Pierwszy pasujący blok, czyli zasada \texttt{first fit}. \\ -\hline -6 & Podział dużego bloku na część przydzieloną i pozostały blok wolny. \\ -\hline -7 & Zwracanie bloku i wstawianie go do listy według adresu. \\ -\hline -8 & Scalanie sąsiednich wolnych bloków. \\ -\hline -9 & Makra konfiguracyjne oraz hostowy \texttt{TRACE\_PRINTF}. \\ -\hline -10 & Mini-demonstracja sekwencji \texttt{malloc/free}. \\ -\hline +\begin{tabularx}{\textwidth}{@{}lX@{}} +\toprule +Klucz & \texttt{\DocumentKey} \\ +Wersja / commit & \texttt{\CardVersion} / \texttt{\BuildCommit} \\ +Data & 14.07.2026 \\ +Allocator & FreeRTOS-Kernel V11.3.0, \texttt{9b777ae5c5b8} \\ +Port RP2350 & fork Raspberry Pi, \texttt{4f7299d6ea74} \\ +Źródło & \texttt{portable/MemMang/heap\_4.c} — bez modyfikacji \\ +Punkt wejścia & \texttt{stemctl} \\ +\bottomrule \end{tabularx} -\TaskSection{01}{task01_heap_map}{mapa \texttt{heap\_4}}{ -\begin{itemize}[leftmargin=1.6em] - \item Linie 3--7 definiują zmienne \texttt{volatile}, czyli obserwowalne wyniki taska. - \item Linie 11--16 tworzą pojedynczy blok i zapisują w jego polu \texttt{size} rozmiar z bitem alokacji. - \item Linie 18--22 rozbijają nagłówek bloku na wartości pomocnicze: rozmiar nagłówka, rozmiar wyrównany, bit zajętości, rozmiar całego bloku i część użytkownika. - \item Linie 24--27 wypisują te same wartości tylko w wariancie hostowym; na RV32I makro \texttt{TRACE\_PRINTF} znika. - \item Wynik sprawdzaj w \texttt{g\_header\_size}, \texttt{g\_allocated\_bit\_set}, \texttt{g\_total\_block\_bytes} i \texttt{g\_user\_bytes}. -\end{itemize} -}{ -W ASM szukaj operacji ustawiania najwyższego bitu rozmiaru oraz zapisów do symboli globalnych \texttt{g\_*}. Dostęp do pól \texttt{block.next} i \texttt{block.size} powinien być zwykłym zapisem pod adres bazowy stosu z przesunięciem. -} -\TaskSection{02}{task02_free_list}{lista wolnych bloków}{ -\begin{itemize}[leftmargin=1.6em] - \item Linie 7--16 implementują sumowanie rozmiarów bloków na liście. - \item Linie 18--28 przechodzą po tej samej liście i zapamiętują największy blok. - \item Linie 30--39 liczą liczbę węzłów listy. - \item Linie 43--55 budują ręcznie listę \texttt{start -> a -> b -> c -> NULL}. - \item Linie 57--59 uruchamiają trzy funkcje pomiarowe i zapisują wyniki w \texttt{volatile}. -\end{itemize} -}{ -W ASM zwróć uwagę na pętle po wskaźniku \texttt{block = block->next}. To jest podstawowy idiom przechodzenia po liście jednokierunkowej, widoczny jako ładowanie pola \texttt{next} i skok warunkowy. -} -\TaskSection{03}{task03_heap_init}{inicjalizacja sterty}{ -\begin{itemize}[leftmargin=1.6em] - \item Linie 3--7 definiują bufor sterty, węzeł startowy listy i wskaźnik końcowego wartownika. - \item Linie 20--23 wyrównują początek sterty i obcinają dostępną długość do wielokrotności wyrównania. - \item Linie 25--28 ustawiają adres i pola końcowego wartownika. - \item Linie 30--34 tworzą pierwszy wolny blok i podpinają go za \texttt{start}. - \item Linie 39--43 wykonują inicjalizację i zapisują trzy wartości kontrolne. -\end{itemize} -}{ -W ASM szukaj maskowania dolnych bitów adresu i rozmiaru. Warto też porównać zapisy do \texttt{first->next}, \texttt{first->size}, \texttt{start.next} i \texttt{start.size}; każdy z nich jest dostępem do pola struktury. -} -\TaskSection{04}{task04_alignment}{wyrównanie}{ -\begin{itemize}[leftmargin=1.6em] - \item Linie 3--6 definiują wyniki pokazujące wpływ wyrównania. - \item Linie 14--16 liczą rzeczywiste rozmiary bloków dla żądań 1, 9 i 17 bajtów, po dodaniu nagłówka. - \item Linie 18--20 biorą celowo nie-wyrównany adres \texttt{\&bytes[1]} i wyliczają przesunięcie do kolejnej granicy wyrównania. - \item Linie 22--24 są hostowym wypisem kontrolnym. -\end{itemize} -}{ -W ASM znajdź operacje \texttt{and} używane do testowania dolnych bitów oraz dodawanie brakującej liczby bajtów do wyrównania. To jest ten sam mechanizm, którego używa alokator dla rozmiaru bloku i początku sterty. -} -\TaskSection{05}{task05_malloc_first_fit}{\texttt{first fit}}{ -\begin{itemize}[leftmargin=1.6em] - \item Linie 13--30 inicjalizują stertę z jednym wolnym blokiem oraz wartownikiem końca. - \item Linie 38--40 zamieniają żądanie użytkownika na całkowity rozmiar bloku i ustawiają początek przeszukiwania listy. - \item Linie 42--51 realizują algorytm \texttt{first fit}: idą po liście do pierwszego bloku o wystarczającym rozmiarze. - \item Linie 44--47 zdejmują znaleziony blok z listy wolnych, oznaczają go jako zajęty i zwracają adres danych użytkownika. - \item Linie 61--67 wykonują alokację i zapisują, czy się udała, jaki rozmiar dostał blok i ile wolnego miejsca zostało na liście. -\end{itemize} -}{ -W ASM obserwuj porównanie \texttt{block->size >= total}, zmianę \texttt{previous->next} oraz dodanie \texttt{sizeof(HeapBlock)} do adresu bloku przed zwróceniem wskaźnika użytkownika. -} -\TaskSection{06}{task06_split_block}{podział bloku}{ -\begin{itemize}[leftmargin=1.6em] - \item Linie 3--12 definiują rozmiar sterty, minimalny sensowny rozmiar reszty i zmienne wynikowe. - \item Linie 14--28 tworzą jeden duży wolny blok. - \item Linie 38--44 szukają bloku pasującego do żądania i zapamiętują jego pierwotny rozmiar. - \item Linie 45--50 wykonują podział: nowy blok \texttt{remainder} zaczyna się pod adresem \texttt{block + total}. - \item Linie 51--56 obsługują przypadek bez podziału, oznaczają blok jako zajęty i zwracają adres użytkownika. - \item Linie 65--73 liczą pozostałe wolne bloki, a linie 81--87 zapisują wynik demonstracji. -\end{itemize} -}{ -W ASM szukaj arytmetyki adresów dla \texttt{remainder}: adres bloku plus \texttt{total}. To najważniejszy fragment pokazujący, że dzielenie bloku jest tylko przesunięciem wskaźnika i zapisaniem nowego nagłówka. -} -\TaskSection{07}{task07_free_insert}{\texttt{free} i wstawianie}{ -\begin{itemize}[leftmargin=1.6em] - \item Linie 11--20 wstawiają blok do listy w miejscu wynikającym z porządku adresów. - \item Linie 22--32 modelują \texttt{free}: z adresu użytkownika wracają do nagłówka bloku, czyszczą bit alokacji i wywołują wstawianie. - \item Linie 34--54 zawierają funkcje kontrolne liczące węzły oraz łączny rozmiar wolnej pamięci. - \item Linie 63--76 budują układ \texttt{a}, \texttt{b}, \texttt{c}, gdzie \texttt{b} jest zajętym blokiem między dwoma wolnymi. - \item Linie 78--83 zwalniają \texttt{b} i sprawdzają, czy lista ma kolejność \texttt{a -> b -> c -> end}. -\end{itemize} -}{ -W ASM zwróć uwagę na porównywanie adresów w pętli z linii 15. To porządkowanie po adresie jest przygotowaniem do scalania bloków w następnym tasku. -} -\TaskSection{08}{task08_coalescing}{scalanie bloków}{ -\begin{itemize}[leftmargin=1.6em] - \item Linie 11--17 znajdują miejsce w liście, w którym powinien znaleźć się zwracany blok. - \item Linie 19--25 sprawdzają sąsiedztwo z prawym blokiem; jeśli adres końca zwracanego bloku jest adresem następnego, rozmiary są łączone. - \item Linie 27--32 sprawdzają sąsiedztwo z lewym blokiem i ewentualnie dołączają scalony wynik do lewej strony. - \item Linie 35--44 liczą końcową liczbę wolnych bloków. - \item Linie 52--65 budują trzy sąsiednie bloki w pamięci, a linia 67 uruchamia scalanie środkowego bloku. - \item Linie 69--71 zapisują, czy po scaleniu został jeden blok i jaki ma rozmiar. -\end{itemize} -}{ -W ASM szukaj testu \texttt{(uint8\_t *)block + block->size == iterator->next}. To jest sedno scalania: alokator nie porównuje indeksów, tylko rzeczywiste adresy końca i początku bloków. -} -\TaskSection{09}{task09_config_macros}{makra konfiguracyjne}{ -\begin{itemize}[leftmargin=1.6em] - \item Linie 3--9 definiują wartości domyślne tylko wtedy, gdy nie zostały podane z zewnątrz przez kompilator. - \item Linia 11 tworzy wartość pochodną od konfiguracji i wyrównania. - \item Linie 20--22 zapisują wartości makr do zmiennych \texttt{volatile}, żeby były widoczne w debugerze. - \item Linie 24--28 pokazują kompilację warunkową: host ustawia \texttt{g\_trace\_is\_host\_only} na 1, RV32I na 0. - \item Linie 30--32 wypisują wynik tylko w wariancie hostowym. -\end{itemize} -}{ -W ASM dla RV32I nie powinno być wywołania \texttt{printf}. Sprawdź też, że makra konfiguracyjne nie są zmiennymi w pamięci, tylko stałymi wstawionymi do kodu przez preprocesor. -} -\TaskSection{10}{task10_heap4_demo}{mini \texttt{heap\_4}}{ -\begin{itemize}[leftmargin=1.6em] - \item Linie 18--38 to funkcje diagnostyczne przechodzące po liście wolnych bloków. - \item Linie 40--62 wstawiają blok i scalają go z prawym oraz lewym sąsiadem. - \item Linie 64--88 inicjalizują stertę: wyrównują początek, ustawiają wartownika, pierwszy wolny blok i liczniki wolnej pamięci. - \item Linie 90--131 implementują alokację: sprawdzają rozmiar żądania, szukają bloku, dzielą go lub zdejmują w całości z listy, oznaczają jako zajęty i aktualizują minimum wolnej pamięci. - \item Linie 133--149 implementują zwalnianie: wracają z adresu użytkownika do nagłówka, sprawdzają bit alokacji, przywracają rozmiar i scalają blok. - \item Linie 157--169 wykonują sekwencję \texttt{malloc/free} i zapisują stan końcowy alokatora. -\end{itemize} -}{ -W ASM porównaj trzy fragmenty: inicjalizację listy, pętlę szukania wolnego bloku oraz wywołanie \texttt{insert\_block\_merge}. To pokazuje, że pełny model składa się z prostych operacji wskaźnikowych poznanych w poprzednich taskach. -} - -\section{Polecenia} -\begin{enumerate}[leftmargin=1.6em] - \item Zbuduj kartę dla RV32I poleceniem \texttt{make tasks}. - \item Zbuduj wariant hostowy poleceniem \texttt{make host}; wypisywanie działa tylko przez \texttt{HOST\_PRINTF}. - \item W tasku 6 zmień próg \texttt{MIN\_SPLIT\_SIZE} i sprawdź, kiedy blok nie jest dzielony. - \item W tasku 8 zmień rozmiary bloków tak, aby scalanie działało tylko z prawym sąsiadem. - \item W tasku 10 dodaj większą alokację, która ma się nie udać, i zapisz status w nowej zmiennej \texttt{volatile}. +\section*{Tylko trzy kroki} +\begin{enumerate} + \item First-fit + split: skąd bierze się drugi nagłówek. + \item Free-list + coalescing: dlaczego lista jest uporządkowana po adresie. + \item API FreeRTOS: \texttt{pvPortMalloc}, \texttt{vPortFree}, stats, OOM. \end{enumerate} +\section*{Warunek zaliczenia} +\begin{itemize} + \item \checkline przewiduję rozmiary nagłówków i bloków na AMD64/RV32; + \item \checkline rozpoznaję fragmentację: suma wolnych bajtów $\neq$ największy blok; + \item \checkline pokazuję split i dwa kierunki scalania w Memory; + \item \checkline odczytuję \texttt{xStart}, \texttt{pxEnd}, minimum-ever i stack frame; + \item \checkline Task 3 daje \texttt{pass=1} na host, Hazard3 i RP2350. +\end{itemize} + +\newpage +\section{Start — polecenia i platformy} + +\begin{lstlisting}[language=bash,numbers=none] +stemctl test native-amd64 freertos heap4 1 +stemctl test hazard3-sim freertos heap4 2 +stemctl debug hazard3-sim freertos heap4 3 +stemctl test rp2350 freertos heap4 3 +stemctl deploy rp2350 freertos heap4 3 \ + --device /dev/bus/usb/BBB/DDD +\end{lstlisting} + +\begin{tabularx}{\textwidth}{@{}p{3.1cm}XXX@{}} +\toprule + & AMD64 & Hazard3 & RP2350 \\ +\midrule +Task 1--2 & model + sanitizery & model na RTL & build \\ +Task 3 & V11.3 + adapter 1-thread & pełny V11.3 + port RISC-V & zgodny core+port RP; allocator V11.3 \\ +Debug & GDB & GDB stub RTL & OpenOCD; SRAM \\ +Deploy & --- & --- & SPI flash + verify \\ +\bottomrule +\end{tabularx} + +\subsection*{Termdebug} +\begin{tabularx}{\textwidth}{@{}lXlX@{}} +\toprule +\texttt{F5} & continue & \texttt{F9} & breakpoint \\ +\texttt{F10} & next & \texttt{F11} & step \\ +\texttt{Shift-F11} & finish & \texttt{F8} & stepi \\ +\texttt{:StudentStack} & frame + stos & \texttt{:StudentLst} & listing ELF-a \\ +\bottomrule +\end{tabularx} + +\textbf{Strategia obserwacji}: breakpoint przed zmianą $\rightarrow$ Memory +$\rightarrow$ continue/finish $\rightarrow$ Memory. Nie opieramy karty na data +watchpointach. + +\newpage +\section{Task 1 — first-fit i split} + +\subsection*{Policz przed uruchomieniem} +\begin{tabular}{@{}lcc@{}} +\toprule + & AMD64 & RV32 \\ +\midrule +\texttt{sizeof(ModelBlock)} & \rule{1.6cm}{.2pt} & \rule{1.6cm}{.2pt} \\ +request & 13 B & 13 B \\ +\texttt{align8(header + request)} & \rule{1.6cm}{.2pt} & \rule{1.6cm}{.2pt} \\ +remainder z 256 B & \rule{1.6cm}{.2pt} & \rule{1.6cm}{.2pt} \\ +offset payloadu & \rule{1.6cm}{.2pt} & \rule{1.6cm}{.2pt} \\ +\bottomrule +\end{tabular} + +\subsection*{Do wykonania} +\begin{enumerate} + \item Wskaż nagłówek bloku przydzielonego i nagłówek remainder. + \item Sprawdź: \texttt{wanted + remainder == 256}. + \item Zmień request 13 na 17; przewidź wynik bez uruchamiania. + \item W listingu znajdź dodawanie bajtowego offsetu do adresu areny. +\end{enumerate} + +\lstinputlisting{../src/tasks/task01_first_fit_split.c} + +\newpage +\section{Task 2 — lista adresowa i coalescing} + +\subsection*{Eksperyment} +\begin{center} +\texttt{A:48 | B:48 | C:48}\quad +$\xrightarrow{\text{free A, free C}}$\quad +\texttt{A:48 -> C:48}\quad +$\xrightarrow{\text{free B}}$\quad +\texttt{A:144} +\end{center} + +\subsection*{Do wykonania} +\begin{enumerate} + \item Zatrzymaj program w \texttt{task02\_fragmented\_checkpoint}. + \item Zapisz adresy i rozmiary dwóch elementów free-list. + \item Wykonaj \texttt{F5}; zatrzymaj w \texttt{task02\_debug\_checkpoint}. + \item Wskaż pierwszy warunek scalania: z blokiem po prawej. + \item Wskaż drugi warunek scalania: z blokiem po lewej. + \item Wyjaśnij, czemu kolejność po adresie redukuje koszt szukania sąsiadów. +\end{enumerate} + +\subsection*{Tabela obserwacji} +\begin{tabular}{@{}lccc@{}} +\toprule +Stan & liczba bloków & suma & największy \\ +\midrule +przed B & \rule{1.4cm}{.2pt} & \rule{1.4cm}{.2pt} & \rule{1.4cm}{.2pt} \\ +po B & \rule{1.4cm}{.2pt} & \rule{1.4cm}{.2pt} & \rule{1.4cm}{.2pt} \\ +\bottomrule +\end{tabular} + +\lstinputlisting{../src/tasks/task02_address_order_coalesce.c} + +\newpage +\section{Task 3 — prawdziwy FreeRTOS \texttt{heap\_4.c}} + +\subsection*{Sekwencja testowa} +\begin{enumerate} + \item init przez pierwsze \texttt{pvPortMalloc(1)} i \texttt{vPortFree}; zapisz $F_0$; + \item malloc: 24 B, 40 B, 16 B; zapisz $F_1$; + \item free pierwszego i trzeciego; oczekuj 2 wolnych bloków; + \item free środkowego; oczekuj jednego bloku i $F_{final}=F_0$; + \item malloc większy niż heap; oczekuj \texttt{NULL} i jednego hooka. +\end{enumerate} + +\subsection*{Inwarianty — zaznacz po teście} +\begin{itemize} + \item \checkline $F_0 > F_1$; + \item \checkline $F_{final}=F_0$; + \item \checkline minimum-ever $\leq F_1$ i nie rośnie po free; + \item \checkline każdy payload ma adres podzielny przez 8; + \item \checkline OOM zwraca \texttt{NULL}; + \item \checkline końcowa free-list ma dokładnie jeden blok. +\end{itemize} + +\lstinputlisting{../src/tasks/task03_freertos_heap4.c} + +\newpage +\section{Czytanie upstream \texttt{heap\_4.c}} + +\subsection*{Znajdź w pliku — nie czytaj całego od góry} +\begin{lstlisting}[language=bash,numbers=none] +K=/opt/FreeRTOS-Kernel/portable/MemMang/heap_4.c +rg -n 'xHeapStructSize|pvPortMalloc|prvHeapInit' "$K" +rg -n 'prvInsertBlockIntoFreeList|vPortFree' "$K" +rg -n 'xFreeBytesRemaining|xMinimumEver' "$K" +rg -n 'heapADD_WILL_OVERFLOW|heapBLOCK_ALLOCATED' "$K" +\end{lstlisting} + +\begin{itemize}[leftmargin=*,itemsep=2pt] + \item \texttt{xHeapStructSize}: dlaczego jest wyrównany oddzielnie od + \texttt{sizeof}? + \item \texttt{heapBLOCK\_ALLOCATED\_BITMASK}: który bit rozmiaru oznacza + właściciela? + \item \texttt{pvPortMalloc}: gdzie są overflow, alignment, first-fit i split? + \item \texttt{vPortFree}: jak odzyskuje nagłówek z payloadu? + \item \texttt{prvInsertBlockIntoFreeList}: gdzie są dwa kierunki scalania? + \item \texttt{xMinimumEverFreeBytesRemaining}: dlaczego free go nie zwiększa? +\end{itemize} + +\subsection*{Breakpointy} +\begin{lstlisting}[numbers=none] +b pvPortMalloc +b vPortFree +b prvInsertBlockIntoFreeList +p xStart +p pxEnd +p xFreeBytesRemaining +p xMinimumEverFreeBytesRemaining +x/160bx ucHeap +\end{lstlisting} + +\newpage +\section{RP2350 — deploy i dowód sprzętowy} + +\subsection*{Polecenie} +\begin{lstlisting}[language=bash,numbers=none] +stemctl probe list +stemctl deploy rp2350 freertos heap4 3 \ + --device /dev/bus/usb/BBB/DDD +\end{lstlisting} + +\subsection*{Automatyczny test zatrzymuje się dwa razy} +\begin{enumerate} + \item \texttt{heap4\_fragmented\_checkpoint}: 2 wolne bloki; + \item \texttt{task03\_debug\_checkpoint}: heap odtworzony, OOM obsłużony. +\end{enumerate} + +\subsection*{Zapis ucznia} +\begin{tabular}{@{}ll@{}} +\toprule +Wielkość & Wartość \\ +\midrule +\texttt{initial free} & \rule{4cm}{.2pt} \\ +\texttt{after allocations} & \rule{4cm}{.2pt} \\ +\texttt{minimum ever} & \rule{4cm}{.2pt} \\ +\texttt{final free} & \rule{4cm}{.2pt} \\ +\texttt{sp / fp / pc} & \rule{7cm}{.2pt} \\ +\bottomrule +\end{tabular} + +\subsection*{Kontrakt} +\begin{itemize} + \item flash: write + verify; ELF: RISC-V; + \item breakpointy sprzętowe, nie data watchpoint; + \item \texttt{aligned=1 oom=1 hooks=1 asserts=0 pass=1}; + \item \texttt{sp \& 15 == 0}; + \item \texttt{rp2350-hardware-ok task=task03 heap\_4=upstream}. +\end{itemize} + +\textbf{Operacje są rozdzielone}: \texttt{debug} ładuje do SRAM; +\texttt{deploy} zapisuje trwały obraz do SPI flash. + +\newpage +\section{Podsumowanie i oddanie} + +\subsection*{Odpowiedz jednym zdaniem} +\begin{enumerate} + \item Dlaczego \texttt{heap\_4} scala, a nie tylko odkłada blok na listę?\\[.6em] + \rule{\textwidth}{.2pt} + \item Dlaczego „wolne razem” nie oznacza „można przydzielić jeden duży blok”?\\[.6em] + \rule{\textwidth}{.2pt} + \item Gdzie leży metadata bloku przydzielonego aplikacji?\\[.6em] + \rule{\textwidth}{.2pt} + \item Co mierzy minimum-ever-free, a czego nie mierzy?\\[.6em] + \rule{\textwidth}{.2pt} +\end{enumerate} + +\subsection*{Oddaj} +\begin{itemize} + \item \checkline wyniki 3 tasków na AMD64; + \item \checkline Hazard3: trzy kody wyjścia 0; + \item \checkline zrzut Task 2: przed i po scaleniu; + \item \checkline zrzut Task 3: source + listing + Memory + Stack; + \item \checkline wynik RP2350 \texttt{rp2350-hardware-ok}; + \item \checkline wypełnione inwarianty i cztery odpowiedzi. +\end{itemize} + +\vfill +\noindent\textbf{Dalej}: taski FreeRTOS, scheduler, osobne stosy, 1 kHz tick; +następnie cienki C++ header wrapper i użycie w projekcie hoveboard. + \end{document} diff --git a/include/FreeRTOSConfig.h b/include/FreeRTOSConfig.h new file mode 100644 index 0000000..32edcb2 --- /dev/null +++ b/include/FreeRTOSConfig.h @@ -0,0 +1,68 @@ +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +#define configCPU_CLOCK_HZ ( 150000000UL ) +#define configTICK_RATE_HZ ( 1000U ) +#define configMTIME_BASE_ADDRESS ( 0xC0000100UL ) +#define configMTIMECMP_BASE_ADDRESS ( 0xC0000108UL ) + +#define configUSE_PREEMPTION 1 +#define configUSE_TIME_SLICING 1 +#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 +#define configUSE_IDLE_HOOK 0 +#define configUSE_PASSIVE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configMAX_PRIORITIES 4 +#define configMINIMAL_STACK_SIZE 128 +#define configMAX_TASK_NAME_LEN 12 +#define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_32_BITS +#define configIDLE_SHOULD_YIELD 1 + +#define configNUMBER_OF_CORES 1 +#define configTICK_CORE 0 +#define configUSE_CORE_AFFINITY 0 +#define configRUN_MULTIPLE_PRIORITIES 0 +#define configSUPPORT_PICO_SYNC_INTEROP 0 + +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configSUPPORT_STATIC_ALLOCATION 0 +#define configTOTAL_HEAP_SIZE ( 4096U ) +#define configAPPLICATION_ALLOCATED_HEAP 1 +#define configUSE_MALLOC_FAILED_HOOK 1 +#define configHEAP_CLEAR_MEMORY_ON_FREE 0 +#define configENABLE_HEAP_PROTECTOR 0 + +#define configUSE_TIMERS 0 +#define configUSE_MUTEXES 0 +#define configUSE_RECURSIVE_MUTEXES 0 +#define configUSE_COUNTING_SEMAPHORES 0 +#define configUSE_TASK_NOTIFICATIONS 1 +#define configUSE_TRACE_FACILITY 1 +#define configUSE_STATS_FORMATTING_FUNCTIONS 0 +#define configGENERATE_RUN_TIME_STATS 0 +#define configUSE_CO_ROUTINES 0 +#define configUSE_NEWLIB_REENTRANT 0 +#define configUSE_POSIX_ERRNO 0 +#define configCHECK_FOR_STACK_OVERFLOW 0 + +#define INCLUDE_vTaskDelay 0 +#define INCLUDE_vTaskDelayUntil 0 +#define INCLUDE_vTaskDelete 0 +#define INCLUDE_vTaskSuspend 0 +#define INCLUDE_xTaskGetSchedulerState 0 + +#define configENABLE_FPU 0 +#define configENABLE_VPU 0 +#if !defined(PICO_RP2350) +#define configISR_STACK_SIZE_WORDS 128 +#endif + +#ifndef __ASSEMBLER__ +void heap4_lab_assert(const char *file, int line); +#define configASSERT(condition) \ + do { if (!(condition)) heap4_lab_assert(__FILE__, __LINE__); } while (0) +#else +#define configASSERT(condition) +#endif + +#endif diff --git a/include/freertos_risc_v_chip_specific_extensions.h b/include/freertos_risc_v_chip_specific_extensions.h new file mode 100644 index 0000000..5395250 --- /dev/null +++ b/include/freertos_risc_v_chip_specific_extensions.h @@ -0,0 +1,15 @@ +#ifndef FREERTOS_RISC_V_CHIP_SPECIFIC_EXTENSIONS_H +#define FREERTOS_RISC_V_CHIP_SPECIFIC_EXTENSIONS_H + +/* Hazard3 adds no architectural registers to the base RV32I context. */ +#define portasmHAS_MTIME 1 +#define portasmHAS_SIFIVE_CLINT 0 +#define portasmADDITIONAL_CONTEXT_SIZE 0 + +.macro portasmSAVE_ADDITIONAL_REGISTERS +.endm + +.macro portasmRESTORE_ADDITIONAL_REGISTERS +.endm + +#endif diff --git a/include/freestanding/stdlib.h b/include/freestanding/stdlib.h new file mode 100644 index 0000000..ada35b1 --- /dev/null +++ b/include/freestanding/stdlib.h @@ -0,0 +1,4 @@ +#ifndef HEAP4_FREESTANDING_STDLIB_H +#define HEAP4_FREESTANDING_STDLIB_H +#include +#endif diff --git a/include/freestanding/string.h b/include/freestanding/string.h new file mode 100644 index 0000000..1841781 --- /dev/null +++ b/include/freestanding/string.h @@ -0,0 +1,7 @@ +#ifndef HEAP4_FREESTANDING_STRING_H +#define HEAP4_FREESTANDING_STRING_H +#include +void *memcpy(void *destination, const void *source, size_t count); +void *memset(void *destination, int value, size_t count); +size_t strlen(const char *text); +#endif diff --git a/include/host-shim/FreeRTOS.h b/include/host-shim/FreeRTOS.h new file mode 100644 index 0000000..d4a223d --- /dev/null +++ b/include/host-shim/FreeRTOS.h @@ -0,0 +1,50 @@ +#ifndef HEAP4_HOST_FREERTOS_H +#define HEAP4_HOST_FREERTOS_H + +#include +#include + +typedef int BaseType_t; +typedef uintptr_t portPOINTER_SIZE_TYPE; + +typedef struct xHeapStats { + size_t xAvailableHeapSpaceInBytes; + size_t xSizeOfLargestFreeBlockInBytes; + size_t xSizeOfSmallestFreeBlockInBytes; + size_t xNumberOfFreeBlocks; + size_t xMinimumEverFreeBytesRemaining; + size_t xNumberOfSuccessfulAllocations; + size_t xNumberOfSuccessfulFrees; +} HeapStats_t; + +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configTOTAL_HEAP_SIZE ((size_t)4096U) +#define configAPPLICATION_ALLOCATED_HEAP 1 +#define configUSE_MALLOC_FAILED_HOOK 1 +#define configHEAP_CLEAR_MEMORY_ON_FREE 0 +#define configENABLE_HEAP_PROTECTOR 0 + +#define portBYTE_ALIGNMENT 8U +#define portBYTE_ALIGNMENT_MASK (portBYTE_ALIGNMENT - 1U) +#define portMAX_DELAY (~(size_t)0U) + +#define PRIVILEGED_DATA +#define PRIVILEGED_FUNCTION +#define mtCOVERAGE_TEST_MARKER() do { } while (0) +#define traceMALLOC(pointer, size) do { (void)(pointer); (void)(size); } while (0) +#define traceFREE(pointer, size) do { (void)(pointer); (void)(size); } while (0) +#define taskENTER_CRITICAL() do { } while (0) +#define taskEXIT_CRITICAL() do { } while (0) + +void heap4_lab_assert(const char *file, int line); +void vApplicationMallocFailedHook(void); +void *pvPortMalloc(size_t wanted_size); +void vPortFree(void *pointer); +size_t xPortGetFreeHeapSize(void); +size_t xPortGetMinimumEverFreeHeapSize(void); +void vPortGetHeapStats(HeapStats_t *stats); +void vPortHeapResetState(void); +#define configASSERT(condition) \ + do { if (!(condition)) heap4_lab_assert(__FILE__, __LINE__); } while (0) + +#endif diff --git a/include/host-shim/task.h b/include/host-shim/task.h new file mode 100644 index 0000000..54116c6 --- /dev/null +++ b/include/host-shim/task.h @@ -0,0 +1,9 @@ +#ifndef HEAP4_HOST_TASK_H +#define HEAP4_HOST_TASK_H + +#include "FreeRTOS.h" + +void vTaskSuspendAll(void); +BaseType_t xTaskResumeAll(void); + +#endif diff --git a/src/tasks/heap4_common.h b/src/tasks/heap4_common.h deleted file mode 100644 index 3d79f9d..0000000 --- a/src/tasks/heap4_common.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef HEAP4_COMMON_H -#define HEAP4_COMMON_H - -#include -#include - -#ifdef HOST_PRINTF -#include -#define TRACE_PRINTF(...) printf(__VA_ARGS__) -#else -#define TRACE_PRINTF(...) ((void)0) -#endif - -#define HEAP4_ARRAY_LEN(a) ((int)(sizeof(a) / sizeof((a)[0]))) -#define HEAP4_ALIGNMENT ((size_t)8) -#define HEAP4_ALIGNMENT_MASK (HEAP4_ALIGNMENT - 1U) -#define HEAP4_ALLOCATED_BIT ((size_t)1U << (sizeof(size_t) * 8U - 1U)) - -typedef struct HeapBlock { - struct HeapBlock *next; - size_t size; -} HeapBlock; - -static inline size_t heap4_align_up(size_t value) -{ - if ((value & HEAP4_ALIGNMENT_MASK) != 0U) - value += HEAP4_ALIGNMENT - (value & HEAP4_ALIGNMENT_MASK); - return value; -} - -static inline uintptr_t heap4_align_address(uintptr_t address) -{ - uintptr_t mask; - - mask = (uintptr_t)HEAP4_ALIGNMENT_MASK; - if ((address & mask) != 0U) - address += (uintptr_t)HEAP4_ALIGNMENT - (address & mask); - return address; -} - -static inline size_t heap4_clear_allocated(size_t size) -{ - return size & ~HEAP4_ALLOCATED_BIT; -} - -static inline size_t heap4_mark_allocated(size_t size) -{ - return size | HEAP4_ALLOCATED_BIT; -} - -static inline int heap4_is_allocated(size_t size) -{ - return (size & HEAP4_ALLOCATED_BIT) != 0U; -} - -static inline int heap4_blocks_touch(HeapBlock *left, HeapBlock *right) -{ - return (uint8_t *)left + heap4_clear_allocated(left->size) == (uint8_t *)right; -} - -#endif diff --git a/src/tasks/heap4_model.h b/src/tasks/heap4_model.h new file mode 100644 index 0000000..fb154d0 --- /dev/null +++ b/src/tasks/heap4_model.h @@ -0,0 +1,25 @@ +#ifndef HEAP4_MODEL_H +#define HEAP4_MODEL_H + +#include +#include + +#define MODEL_ALIGNMENT ((size_t)8U) + +typedef struct ModelBlock ModelBlock; + +struct ModelBlock { + ModelBlock *next; + size_t size; +}; + +static inline size_t model_align_up(size_t value) +{ + return (value + MODEL_ALIGNMENT - 1U) & ~(MODEL_ALIGNMENT - 1U); +} + +#if defined(HOST_PRINTF) +#include +#endif + +#endif diff --git a/src/tasks/task01_first_fit_split.c b/src/tasks/task01_first_fit_split.c new file mode 100644 index 0000000..eced284 --- /dev/null +++ b/src/tasks/task01_first_fit_split.c @@ -0,0 +1,72 @@ +#include "heap4_model.h" + +enum { + ARENA_BYTES = 256, + REQUEST_BYTES = 13, + WANTED_BYTES = (sizeof(ModelBlock) + REQUEST_BYTES + 7U) & ~7U +}; + +typedef struct { + ModelBlock allocated; + uint8_t payload[WANTED_BYTES - sizeof(ModelBlock)]; + ModelBlock remainder; + uint8_t free_payload[ARENA_BYTES - WANTED_BYTES - sizeof(ModelBlock)]; +} SplitArena; + +typedef union { + uint64_t force_alignment; + SplitArena blocks; +} AlignedSplitArena; + +AlignedSplitArena g_split_arena; +ModelBlock *g_free_head; +volatile size_t g_header_bytes; +volatile size_t g_wanted_bytes; +volatile size_t g_remainder_bytes; +volatile size_t g_payload_offset; +volatile int g_split_addresses_aligned; +volatile int g_task01_pass; + +__attribute__((noinline)) void task01_debug_checkpoint(void) +{ +#if defined(__GNUC__) + __asm__ volatile("" ::: "memory"); +#endif +} + +int main(void) +{ + SplitArena *arena = &g_split_arena.blocks; + uintptr_t base = (uintptr_t)&arena->allocated; + uintptr_t remainder = (uintptr_t)&arena->remainder; + + arena->allocated.next = NULL; + arena->allocated.size = WANTED_BYTES; + arena->remainder.next = NULL; + arena->remainder.size = ARENA_BYTES - WANTED_BYTES; + arena->payload[0] = (uint8_t)'A'; + g_free_head = &arena->remainder; + + g_header_bytes = sizeof(ModelBlock); + g_wanted_bytes = WANTED_BYTES; + g_remainder_bytes = arena->remainder.size; + g_payload_offset = (size_t)((uintptr_t)&arena->payload[0] - base); + g_split_addresses_aligned = + (base % MODEL_ALIGNMENT) == 0U && + (remainder % MODEL_ALIGNMENT) == 0U; + g_task01_pass = + g_wanted_bytes == model_align_up(sizeof(ModelBlock) + REQUEST_BYTES) && + (size_t)(remainder - base) == g_wanted_bytes && + g_remainder_bytes + g_wanted_bytes == ARENA_BYTES && + g_payload_offset == sizeof(ModelBlock) && + g_split_addresses_aligned; + + task01_debug_checkpoint(); + +#if defined(HOST_PRINTF) + printf("header=%zu wanted=%zu remainder=%zu payload=%zu aligned=%d pass=%d\n", + g_header_bytes, g_wanted_bytes, g_remainder_bytes, + g_payload_offset, g_split_addresses_aligned, g_task01_pass); +#endif + return g_task01_pass ? 0 : 1; +} diff --git a/src/tasks/task01_heap_map.c b/src/tasks/task01_heap_map.c deleted file mode 100644 index b7c52b7..0000000 --- a/src/tasks/task01_heap_map.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "heap4_common.h" - -volatile size_t g_header_size; -volatile size_t g_aligned_header_size; -volatile size_t g_allocated_bit_set; -volatile size_t g_user_bytes; -volatile size_t g_total_block_bytes; - -int main(void) -{ - HeapBlock block; - size_t requested; - - requested = 13U; - block.next = 0; - block.size = heap4_mark_allocated(heap4_align_up(requested + sizeof(HeapBlock))); - - g_header_size = sizeof(HeapBlock); - g_aligned_header_size = heap4_align_up(sizeof(HeapBlock)); - g_allocated_bit_set = heap4_is_allocated(block.size); - g_total_block_bytes = heap4_clear_allocated(block.size); - g_user_bytes = g_total_block_bytes - sizeof(HeapBlock); - - TRACE_PRINTF("header=%u aligned=%u allocated=%u total=%u user=%u\n", - (unsigned)g_header_size, (unsigned)g_aligned_header_size, - (unsigned)g_allocated_bit_set, (unsigned)g_total_block_bytes, - (unsigned)g_user_bytes); - return 0; -} diff --git a/src/tasks/task02_address_order_coalesce.c b/src/tasks/task02_address_order_coalesce.c new file mode 100644 index 0000000..0877b9f --- /dev/null +++ b/src/tasks/task02_address_order_coalesce.c @@ -0,0 +1,136 @@ +#include "heap4_model.h" + +enum { BLOCK_BYTES = 48 }; + +typedef struct { + ModelBlock first; + uint8_t first_payload[BLOCK_BYTES - sizeof(ModelBlock)]; + ModelBlock middle; + uint8_t middle_payload[BLOCK_BYTES - sizeof(ModelBlock)]; + ModelBlock last; + uint8_t last_payload[BLOCK_BYTES - sizeof(ModelBlock)]; +} CoalesceArena; + +typedef union { + uint64_t force_alignment; + CoalesceArena blocks; +} AlignedCoalesceArena; + +AlignedCoalesceArena g_coalesce_arena; +ModelBlock *g_free_head; +volatile size_t g_before_nodes; +volatile size_t g_before_total; +volatile size_t g_before_largest; +volatile size_t g_after_nodes; +volatile size_t g_after_total; +volatile size_t g_after_largest; +volatile int g_task02_pass; + +static void insert_and_coalesce(ModelBlock *block) +{ + ModelBlock *previous = NULL; + ModelBlock *current = g_free_head; + + while (current != NULL && (uintptr_t)current < (uintptr_t)block) + { + previous = current; + current = current->next; + } + + block->next = current; + if (current != NULL && + (uint8_t *)block + block->size == (uint8_t *)current) + { + block->size += current->size; + block->next = current->next; + } + + if (previous != NULL && + (uint8_t *)previous + previous->size == (uint8_t *)block) + { + previous->size += block->size; + previous->next = block->next; + } + else if (previous != NULL) + { + previous->next = block; + } + else + { + g_free_head = block; + } +} + +static void measure(size_t *nodes, size_t *total, size_t *largest) +{ + ModelBlock *block; + + *nodes = 0U; + *total = 0U; + *largest = 0U; + for (block = g_free_head; block != NULL; block = block->next) + { + (*nodes)++; + *total += block->size; + if (block->size > *largest) + { + *largest = block->size; + } + } +} + +__attribute__((noinline)) void task02_fragmented_checkpoint(void) +{ +#if defined(__GNUC__) + __asm__ volatile("" ::: "memory"); +#endif +} + +__attribute__((noinline)) void task02_debug_checkpoint(void) +{ +#if defined(__GNUC__) + __asm__ volatile("" ::: "memory"); +#endif +} + +int main(void) +{ + CoalesceArena *arena = &g_coalesce_arena.blocks; + size_t nodes; + size_t total; + size_t largest; + + arena->first.size = BLOCK_BYTES; + arena->middle.size = BLOCK_BYTES; + arena->last.size = BLOCK_BYTES; + g_free_head = NULL; + + insert_and_coalesce(&arena->first); + insert_and_coalesce(&arena->last); + measure(&nodes, &total, &largest); + g_before_nodes = nodes; + g_before_total = total; + g_before_largest = largest; + task02_fragmented_checkpoint(); + + insert_and_coalesce(&arena->middle); + measure(&nodes, &total, &largest); + g_after_nodes = nodes; + g_after_total = total; + g_after_largest = largest; + g_task02_pass = + g_before_nodes == 2U && g_before_total == 96U && + g_before_largest == 48U && g_after_nodes == 1U && + g_after_total == 144U && g_after_largest == 144U && + g_free_head == &arena->first && g_free_head->next == NULL; + + task02_debug_checkpoint(); + +#if defined(HOST_PRINTF) + printf("before_nodes=%zu before_total=%zu before_largest=%zu " + "after_nodes=%zu after_total=%zu after_largest=%zu pass=%d\n", + g_before_nodes, g_before_total, g_before_largest, + g_after_nodes, g_after_total, g_after_largest, g_task02_pass); +#endif + return g_task02_pass ? 0 : 1; +} diff --git a/src/tasks/task02_free_list.c b/src/tasks/task02_free_list.c deleted file mode 100644 index eb32284..0000000 --- a/src/tasks/task02_free_list.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "heap4_common.h" - -volatile size_t g_free_total; -volatile size_t g_largest_free; -volatile int g_node_count; - -static size_t list_total(HeapBlock *start) -{ - HeapBlock *block; - size_t total; - - total = 0U; - for (block = start->next; block != 0; block = block->next) - total += block->size; - return total; -} - -static size_t list_largest(HeapBlock *start) -{ - HeapBlock *block; - size_t largest; - - largest = 0U; - for (block = start->next; block != 0; block = block->next) - if (block->size > largest) - largest = block->size; - return largest; -} - -static int list_count(HeapBlock *start) -{ - HeapBlock *block; - int count; - - count = 0; - for (block = start->next; block != 0; block = block->next) - count++; - return count; -} - -int main(void) -{ - HeapBlock start; - HeapBlock a; - HeapBlock b; - HeapBlock c; - - start.next = &a; - start.size = 0U; - a.next = &b; - a.size = 32U; - b.next = &c; - b.size = 80U; - c.next = 0; - c.size = 24U; - - g_free_total = list_total(&start); - g_largest_free = list_largest(&start); - g_node_count = list_count(&start); - - TRACE_PRINTF("free=%u largest=%u count=%d\n", - (unsigned)g_free_total, (unsigned)g_largest_free, g_node_count); - return 0; -} diff --git a/src/tasks/task03_freertos_heap4.c b/src/tasks/task03_freertos_heap4.c new file mode 100644 index 0000000..a73eb93 --- /dev/null +++ b/src/tasks/task03_freertos_heap4.c @@ -0,0 +1,122 @@ +#include +#include + +#include "FreeRTOS.h" +#include "task.h" + +#if defined(HOST_PRINTF) +#include +#endif + +uint8_t ucHeap[configTOTAL_HEAP_SIZE] __attribute__((aligned(portBYTE_ALIGNMENT))); + +HeapStats_t g_fragmented_stats; +HeapStats_t g_final_stats; +volatile size_t g_initial_free; +volatile size_t g_after_allocations; +volatile size_t g_final_free; +volatile size_t g_minimum_ever_free; +volatile int g_allocations_aligned; +volatile int g_oom_is_null; +volatile int g_malloc_failed_hooks; +volatile int g_assert_failures; +volatile int g_task03_pass; + +void vApplicationMallocFailedHook(void) +{ + g_malloc_failed_hooks++; +} + +void heap4_lab_assert(const char *file, int line) +{ + (void)file; + (void)line; + g_assert_failures++; +} + +#if defined(HEAP4_HOST_ADAPTER) +void vTaskSuspendAll(void) +{ +} + +BaseType_t xTaskResumeAll(void) +{ + return 0; +} +#endif + +__attribute__((noinline)) void heap4_fragmented_checkpoint(void) +{ +#if defined(__GNUC__) + __asm__ volatile("" ::: "memory"); +#endif +} + +__attribute__((noinline)) void task03_debug_checkpoint(void) +{ +#if defined(__GNUC__) + __asm__ volatile("" ::: "memory"); +#endif +} + +int main(void) +{ + void *probe; + void *a; + void *b; + void *c; + void *too_large; + + vPortHeapResetState(); + probe = pvPortMalloc(1U); + if (probe != NULL) + { + vPortFree(probe); + } + g_initial_free = xPortGetFreeHeapSize(); + + a = pvPortMalloc(24U); + b = pvPortMalloc(40U); + c = pvPortMalloc(16U); + g_after_allocations = xPortGetFreeHeapSize(); + g_allocations_aligned = + a != NULL && b != NULL && c != NULL && + ((uintptr_t)a % portBYTE_ALIGNMENT) == 0U && + ((uintptr_t)b % portBYTE_ALIGNMENT) == 0U && + ((uintptr_t)c % portBYTE_ALIGNMENT) == 0U; + + vPortFree(a); + vPortFree(c); + vPortGetHeapStats(&g_fragmented_stats); + heap4_fragmented_checkpoint(); + + vPortFree(b); + g_final_free = xPortGetFreeHeapSize(); + g_minimum_ever_free = xPortGetMinimumEverFreeHeapSize(); + vPortGetHeapStats(&g_final_stats); + + too_large = pvPortMalloc(configTOTAL_HEAP_SIZE * 2U); + g_oom_is_null = too_large == NULL; + g_task03_pass = + probe != NULL && g_allocations_aligned && + g_initial_free > g_after_allocations && + g_final_free == g_initial_free && + g_minimum_ever_free <= g_after_allocations && + g_fragmented_stats.xNumberOfFreeBlocks == 2U && + g_final_stats.xNumberOfFreeBlocks == 1U && + g_final_stats.xSizeOfLargestFreeBlockInBytes == g_initial_free && + g_oom_is_null && g_malloc_failed_hooks == 1 && + g_assert_failures == 0; + + task03_debug_checkpoint(); + +#if defined(HOST_PRINTF) + printf("initial=%zu after=%zu fragmented=%zu final=%zu min=%zu " + "aligned=%d oom=%d hooks=%d asserts=%d pass=%d\n", + g_initial_free, g_after_allocations, + g_fragmented_stats.xNumberOfFreeBlocks, g_final_free, + g_minimum_ever_free, g_allocations_aligned, g_oom_is_null, + g_malloc_failed_hooks, g_assert_failures, g_task03_pass); +#endif + return g_task03_pass ? 0 : 1; +} diff --git a/src/tasks/task03_heap_init.c b/src/tasks/task03_heap_init.c deleted file mode 100644 index eda51c7..0000000 --- a/src/tasks/task03_heap_init.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "heap4_common.h" - -#define HEAP_BYTES 160U - -static uint8_t heap_area[HEAP_BYTES + HEAP4_ALIGNMENT]; -static HeapBlock start; -static HeapBlock *end_marker; - -volatile size_t g_initial_free_size; -volatile uintptr_t g_aligned_offset; -volatile int g_end_is_last; - -static void heap_init(void) -{ - uintptr_t raw; - uintptr_t aligned; - size_t available; - HeapBlock *first; - - raw = (uintptr_t)&heap_area[0]; - aligned = heap4_align_address(raw); - available = HEAP_BYTES - (size_t)(aligned - raw); - available &= ~HEAP4_ALIGNMENT_MASK; - - first = (HeapBlock *)aligned; - end_marker = (HeapBlock *)(aligned + available - sizeof(HeapBlock)); - end_marker->next = 0; - end_marker->size = 0U; - - first->next = end_marker; - first->size = (uint8_t *)end_marker - (uint8_t *)first; - - start.next = first; - start.size = 0U; -} - -int main(void) -{ - heap_init(); - - g_aligned_offset = (uintptr_t)start.next - (uintptr_t)&heap_area[0]; - g_initial_free_size = start.next->size; - g_end_is_last = (start.next->next == end_marker) && (end_marker->next == 0); - - TRACE_PRINTF("offset=%u free=%u end_last=%d\n", - (unsigned)g_aligned_offset, (unsigned)g_initial_free_size, - g_end_is_last); - return 0; -} diff --git a/src/tasks/task04_alignment.c b/src/tasks/task04_alignment.c deleted file mode 100644 index debb85e..0000000 --- a/src/tasks/task04_alignment.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "heap4_common.h" - -volatile size_t g_request_1; -volatile size_t g_request_9; -volatile size_t g_request_17; -volatile uintptr_t g_address_delta; - -int main(void) -{ - uint8_t bytes[32]; - uintptr_t raw; - uintptr_t aligned; - - g_request_1 = heap4_align_up(1U + sizeof(HeapBlock)); - g_request_9 = heap4_align_up(9U + sizeof(HeapBlock)); - g_request_17 = heap4_align_up(17U + sizeof(HeapBlock)); - - raw = (uintptr_t)&bytes[1]; - aligned = heap4_align_address(raw); - g_address_delta = aligned - raw; - - TRACE_PRINTF("r1=%u r9=%u r17=%u delta=%u\n", - (unsigned)g_request_1, (unsigned)g_request_9, - (unsigned)g_request_17, (unsigned)g_address_delta); - return 0; -} diff --git a/src/tasks/task05_malloc_first_fit.c b/src/tasks/task05_malloc_first_fit.c deleted file mode 100644 index 4303c95..0000000 --- a/src/tasks/task05_malloc_first_fit.c +++ /dev/null @@ -1,73 +0,0 @@ -#include "heap4_common.h" - -#define HEAP_BYTES 128U - -static uint8_t heap_area[HEAP_BYTES + HEAP4_ALIGNMENT]; -static HeapBlock start; -static HeapBlock *end_marker; - -volatile size_t g_allocated_size; -volatile size_t g_remaining_free; -volatile int g_allocation_ok; - -static void heap_init_one_block(void) -{ - uintptr_t raw; - uintptr_t aligned; - HeapBlock *first; - - raw = (uintptr_t)&heap_area[0]; - aligned = heap4_align_address(raw); - first = (HeapBlock *)aligned; - end_marker = (HeapBlock *)(aligned + 120U); - - end_marker->next = 0; - end_marker->size = 0U; - first->next = end_marker; - first->size = (uint8_t *)end_marker - (uint8_t *)first; - start.next = first; - start.size = 0U; -} - -static void *heap_malloc_whole_block(size_t wanted) -{ - HeapBlock *previous; - HeapBlock *block; - size_t total; - - total = heap4_align_up(wanted + sizeof(HeapBlock)); - previous = &start; - block = start.next; - - while (block != end_marker) { - if (block->size >= total) { - previous->next = block->next; - block->next = 0; - block->size = heap4_mark_allocated(block->size); - return (uint8_t *)block + sizeof(HeapBlock); - } - previous = block; - block = block->next; - } - - return 0; -} - -int main(void) -{ - void *p; - HeapBlock *allocated; - - heap_init_one_block(); - p = heap_malloc_whole_block(24U); - allocated = (HeapBlock *)((uint8_t *)p - sizeof(HeapBlock)); - - g_allocation_ok = p != 0; - g_allocated_size = heap4_clear_allocated(allocated->size); - g_remaining_free = start.next->size; - - TRACE_PRINTF("ok=%d allocated=%u remaining=%u\n", - g_allocation_ok, (unsigned)g_allocated_size, - (unsigned)g_remaining_free); - return 0; -} diff --git a/src/tasks/task06_split_block.c b/src/tasks/task06_split_block.c deleted file mode 100644 index 49c0d51..0000000 --- a/src/tasks/task06_split_block.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "heap4_common.h" - -#define HEAP_BYTES 192U -#define MIN_SPLIT_SIZE ((size_t)(sizeof(HeapBlock) * 2U)) - -static uint8_t heap_area[HEAP_BYTES + HEAP4_ALIGNMENT]; -static HeapBlock start; -static HeapBlock *end_marker; - -volatile size_t g_allocated_size; -volatile size_t g_split_free_size; -volatile int g_free_nodes; - -static void heap_init_one_block(void) -{ - uintptr_t aligned; - HeapBlock *first; - - aligned = heap4_align_address((uintptr_t)&heap_area[0]); - first = (HeapBlock *)aligned; - end_marker = (HeapBlock *)(aligned + 176U); - end_marker->next = 0; - end_marker->size = 0U; - first->next = end_marker; - first->size = (uint8_t *)end_marker - (uint8_t *)first; - start.next = first; - start.size = 0U; -} - -static void *heap_malloc_split(size_t wanted) -{ - HeapBlock *previous; - HeapBlock *block; - HeapBlock *remainder; - size_t total; - size_t original; - - total = heap4_align_up(wanted + sizeof(HeapBlock)); - previous = &start; - block = start.next; - - while (block != end_marker) { - if (block->size >= total) { - original = block->size; - if (original - total > MIN_SPLIT_SIZE) { - remainder = (HeapBlock *)((uint8_t *)block + total); - remainder->size = original - total; - remainder->next = block->next; - previous->next = remainder; - block->size = total; - } else { - previous->next = block->next; - } - block->next = 0; - block->size = heap4_mark_allocated(block->size); - return (uint8_t *)block + sizeof(HeapBlock); - } - previous = block; - block = block->next; - } - - return 0; -} - -static int count_free_nodes(void) -{ - HeapBlock *block; - int count; - - count = 0; - for (block = start.next; block != end_marker; block = block->next) - count++; - return count; -} - -int main(void) -{ - void *p; - HeapBlock *allocated; - - heap_init_one_block(); - p = heap_malloc_split(32U); - allocated = (HeapBlock *)((uint8_t *)p - sizeof(HeapBlock)); - - g_allocated_size = heap4_clear_allocated(allocated->size); - g_split_free_size = start.next->size; - g_free_nodes = count_free_nodes(); - - TRACE_PRINTF("allocated=%u split=%u nodes=%d\n", - (unsigned)g_allocated_size, (unsigned)g_split_free_size, - g_free_nodes); - return 0; -} diff --git a/src/tasks/task07_free_insert.c b/src/tasks/task07_free_insert.c deleted file mode 100644 index be428a8..0000000 --- a/src/tasks/task07_free_insert.c +++ /dev/null @@ -1,88 +0,0 @@ -#include "heap4_common.h" - -static uint8_t heap_area[192]; -static HeapBlock start; -static HeapBlock *end_marker; - -volatile int g_order_ok; -volatile int g_free_count; -volatile size_t g_total_free; - -static void insert_block(HeapBlock *block) -{ - HeapBlock *iterator; - - for (iterator = &start; iterator->next < block; iterator = iterator->next) - ; - - block->next = iterator->next; - iterator->next = block; -} - -static void heap_free_model(void *p) -{ - HeapBlock *block; - - if (p == 0) - return; - - block = (HeapBlock *)((uint8_t *)p - sizeof(HeapBlock)); - block->size = heap4_clear_allocated(block->size); - insert_block(block); -} - -static int count_free(void) -{ - HeapBlock *block; - int count; - - count = 0; - for (block = start.next; block != end_marker; block = block->next) - count++; - return count; -} - -static size_t total_free(void) -{ - HeapBlock *block; - size_t total; - - total = 0U; - for (block = start.next; block != end_marker; block = block->next) - total += block->size; - return total; -} - -int main(void) -{ - HeapBlock *a; - HeapBlock *b; - HeapBlock *c; - void *payload; - - a = (HeapBlock *)&heap_area[0]; - b = (HeapBlock *)&heap_area[64]; - c = (HeapBlock *)&heap_area[128]; - end_marker = (HeapBlock *)&heap_area[176]; - - start.next = a; - a->next = c; - c->next = end_marker; - end_marker->next = 0; - - a->size = 40U; - b->size = heap4_mark_allocated(32U); - c->size = 48U; - end_marker->size = 0U; - - payload = (uint8_t *)b + sizeof(HeapBlock); - heap_free_model(payload); - - g_order_ok = start.next == a && a->next == b && b->next == c && c->next == end_marker; - g_free_count = count_free(); - g_total_free = total_free(); - - TRACE_PRINTF("order=%d count=%d total=%u\n", - g_order_ok, g_free_count, (unsigned)g_total_free); - return 0; -} diff --git a/src/tasks/task08_coalescing.c b/src/tasks/task08_coalescing.c deleted file mode 100644 index 02b3f48..0000000 --- a/src/tasks/task08_coalescing.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "heap4_common.h" - -static uint8_t heap_area[192]; -static HeapBlock start; -static HeapBlock *end_marker; - -volatile int g_free_count; -volatile size_t g_merged_size; -volatile int g_merged_with_right; - -static void insert_block_merge(HeapBlock *block) -{ - HeapBlock *iterator; - uint8_t *block_end; - - for (iterator = &start; iterator->next < block; iterator = iterator->next) - ; - - block_end = (uint8_t *)block + block->size; - if (block_end == (uint8_t *)iterator->next) { - block->size += iterator->next->size; - block->next = iterator->next->next; - } else { - block->next = iterator->next; - } - - if (iterator != &start && heap4_blocks_touch(iterator, block)) { - iterator->size += block->size; - iterator->next = block->next; - } else { - iterator->next = block; - } -} - -static int count_free(void) -{ - HeapBlock *block; - int count; - - count = 0; - for (block = start.next; block != end_marker; block = block->next) - count++; - return count; -} - -int main(void) -{ - HeapBlock *left; - HeapBlock *middle; - HeapBlock *right; - - left = (HeapBlock *)&heap_area[0]; - middle = (HeapBlock *)&heap_area[48]; - right = (HeapBlock *)&heap_area[96]; - end_marker = (HeapBlock *)&heap_area[160]; - - start.next = left; - left->next = right; - right->next = end_marker; - end_marker->next = 0; - - left->size = 48U; - middle->size = 48U; - right->size = 64U; - end_marker->size = 0U; - - insert_block_merge(middle); - - g_free_count = count_free(); - g_merged_size = start.next->size; - g_merged_with_right = start.next->next == end_marker; - - TRACE_PRINTF("count=%d size=%u merged_right=%d\n", - g_free_count, (unsigned)g_merged_size, g_merged_with_right); - return 0; -} diff --git a/src/tasks/task09_config_macros.c b/src/tasks/task09_config_macros.c deleted file mode 100644 index cc64b13..0000000 --- a/src/tasks/task09_config_macros.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "heap4_common.h" - -#ifndef CONFIG_TOTAL_HEAP_SIZE -#define CONFIG_TOTAL_HEAP_SIZE 256U -#endif - -#ifndef CONFIG_SUPPORT_DYNAMIC_ALLOCATION -#define CONFIG_SUPPORT_DYNAMIC_ALLOCATION 1 -#endif - -#define CONFIG_ADJUSTED_HEAP_SIZE (CONFIG_TOTAL_HEAP_SIZE - HEAP4_ALIGNMENT) - -volatile size_t g_total_heap; -volatile size_t g_adjusted_heap; -volatile int g_dynamic_enabled; -volatile int g_trace_is_host_only; - -int main(void) -{ - g_total_heap = CONFIG_TOTAL_HEAP_SIZE; - g_adjusted_heap = CONFIG_ADJUSTED_HEAP_SIZE; - g_dynamic_enabled = CONFIG_SUPPORT_DYNAMIC_ALLOCATION; - -#ifdef HOST_PRINTF - g_trace_is_host_only = 1; -#else - g_trace_is_host_only = 0; -#endif - - TRACE_PRINTF("heap=%u adjusted=%u dynamic=%d host_trace=%d\n", - (unsigned)g_total_heap, (unsigned)g_adjusted_heap, - g_dynamic_enabled, g_trace_is_host_only); - return 0; -} diff --git a/src/tasks/task10_heap4_demo.c b/src/tasks/task10_heap4_demo.c deleted file mode 100644 index f16b408..0000000 --- a/src/tasks/task10_heap4_demo.c +++ /dev/null @@ -1,175 +0,0 @@ -#include "heap4_common.h" - -#define HEAP_BYTES 256U -#define MIN_SPLIT_SIZE ((size_t)(sizeof(HeapBlock) * 2U)) - -static uint8_t heap_area[HEAP_BYTES + HEAP4_ALIGNMENT]; -static HeapBlock start; -static HeapBlock *end_marker; - -volatile int g_allocations_ok; -volatile int g_final_free_nodes; -volatile size_t g_final_free_total; -volatile size_t g_minimum_ever_free; - -static size_t free_bytes_remaining; -static size_t minimum_ever_free; - -static size_t list_total(void) -{ - HeapBlock *block; - size_t total; - - total = 0U; - for (block = start.next; block != end_marker; block = block->next) - total += block->size; - return total; -} - -static int list_count(void) -{ - HeapBlock *block; - int count; - - count = 0; - for (block = start.next; block != end_marker; block = block->next) - count++; - return count; -} - -static void insert_block_merge(HeapBlock *block) -{ - HeapBlock *iterator; - uint8_t *block_end; - - for (iterator = &start; iterator->next < block; iterator = iterator->next) - ; - - block_end = (uint8_t *)block + block->size; - if (block_end == (uint8_t *)iterator->next) { - block->size += iterator->next->size; - block->next = iterator->next->next; - } else { - block->next = iterator->next; - } - - if (iterator != &start && heap4_blocks_touch(iterator, block)) { - iterator->size += block->size; - iterator->next = block->next; - } else { - iterator->next = block; - } -} - -static void heap_init(void) -{ - uintptr_t raw; - uintptr_t aligned; - size_t available; - HeapBlock *first; - - raw = (uintptr_t)&heap_area[0]; - aligned = heap4_align_address(raw); - available = HEAP_BYTES - (size_t)(aligned - raw); - available &= ~HEAP4_ALIGNMENT_MASK; - - first = (HeapBlock *)aligned; - end_marker = (HeapBlock *)(aligned + available - sizeof(HeapBlock)); - end_marker->next = 0; - end_marker->size = 0U; - - first->next = end_marker; - first->size = (uint8_t *)end_marker - (uint8_t *)first; - start.next = first; - start.size = 0U; - - free_bytes_remaining = first->size; - minimum_ever_free = free_bytes_remaining; -} - -static void *heap_malloc_model(size_t wanted) -{ - HeapBlock *previous; - HeapBlock *block; - HeapBlock *remainder; - size_t total; - size_t original; - - if (wanted == 0U) - return 0; - - total = heap4_align_up(wanted + sizeof(HeapBlock)); - previous = &start; - block = start.next; - - while (block != end_marker) { - if (block->size >= total) { - original = block->size; - if (original - total > MIN_SPLIT_SIZE) { - remainder = (HeapBlock *)((uint8_t *)block + total); - remainder->size = original - total; - remainder->next = block->next; - previous->next = remainder; - block->size = total; - } else { - previous->next = block->next; - total = original; - } - - block->next = 0; - block->size = heap4_mark_allocated(block->size); - free_bytes_remaining -= total; - if (free_bytes_remaining < minimum_ever_free) - minimum_ever_free = free_bytes_remaining; - return (uint8_t *)block + sizeof(HeapBlock); - } - previous = block; - block = block->next; - } - - return 0; -} - -static void heap_free_model(void *p) -{ - HeapBlock *block; - size_t size; - - if (p == 0) - return; - - block = (HeapBlock *)((uint8_t *)p - sizeof(HeapBlock)); - if (!heap4_is_allocated(block->size)) - return; - - size = heap4_clear_allocated(block->size); - block->size = size; - free_bytes_remaining += size; - insert_block_merge(block); -} - -int main(void) -{ - void *a; - void *b; - void *c; - - heap_init(); - - a = heap_malloc_model(24U); - b = heap_malloc_model(40U); - heap_free_model(a); - c = heap_malloc_model(16U); - heap_free_model(b); - heap_free_model(c); - - g_allocations_ok = a != 0 && b != 0 && c != 0; - g_final_free_nodes = list_count(); - g_final_free_total = list_total(); - g_minimum_ever_free = minimum_ever_free; - - TRACE_PRINTF("ok=%d nodes=%d free=%u min=%u\n", - g_allocations_ok, g_final_free_nodes, - (unsigned)g_final_free_total, (unsigned)g_minimum_ever_free); - return 0; -} diff --git a/stem-card.yaml b/stem-card.yaml new file mode 100644 index 0000000..f53c299 --- /dev/null +++ b/stem-card.yaml @@ -0,0 +1,19 @@ +schema: 1 +targets: + native: + profile: native-amd64 + actions: [build, test, run, debug] + hazard3-baremetal: + profile: hazard3-sim + actions: [build, test, run, debug] + rp2350-rv: + profile: rp2350 + actions: [build, test, deploy, debug] +actions: + build: [bash, tools/card-action.sh, build] + test: [bash, tools/card-action.sh, test] + run: [bash, tools/card-action.sh, run] + deploy: [bash, tools/card-action.sh, deploy] + debug: [bash, tools/card-action.sh, debug] +artifacts: + directory: .stem/artifacts diff --git a/tests/check_task03_elf.sh b/tests/check_task03_elf.sh new file mode 100755 index 0000000..304a752 --- /dev/null +++ b/tests/check_task03_elf.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -euo pipefail + +elf="${1:?usage: check_task03_elf.sh ELF [tool-prefix]}" +prefix="${2:-riscv64-unknown-elf-}" +nm="${prefix}nm" +objdump="${prefix}objdump" + +test -s "$elf" +for symbol in pvPortMalloc vPortFree prvInsertBlockIntoFreeList ucHeap \ + heap4_fragmented_checkpoint task03_debug_checkpoint g_task03_pass; do + "$nm" -a "$elf" | grep -E "[[:space:]]${symbol}$" >/dev/null +done +"$objdump" -d "$elf" | grep -E 'addi[[:space:]]+sp,sp,-' >/dev/null +printf 'PASS Task 3 links upstream heap_4 and observable checkpoints: %s\n' "$elf" diff --git a/tests/test_host.sh b/tests/test_host.sh new file mode 100755 index 0000000..322dc3b --- /dev/null +++ b/tests/test_host.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +make -C "$root" PROFILE=observe host >/dev/null + +check_output() { + local program="$1" expected="$2" actual + actual="$($program)" + if [[ "$actual" != "$expected" ]]; then + printf 'FAIL %s\nexpected: %s\nactual: %s\n' "$program" "$expected" "$actual" >&2 + return 1 + fi + printf 'PASS %s: %s\n' "$(basename -- "$(dirname -- "$program")")" "$actual" +} + +check_output "$root/host-build/task01_first_fit_split/prog" \ + 'header=16 wanted=32 remainder=224 payload=16 aligned=1 pass=1' +check_output "$root/host-build/task02_address_order_coalesce/prog" \ + 'before_nodes=2 before_total=96 before_largest=48 after_nodes=1 after_total=144 after_largest=144 pass=1' +check_output "$root/host-build/task03_freertos_heap4/prog" \ + 'initial=4080 after=3952 fragmented=2 final=4080 min=3952 aligned=1 oom=1 hooks=1 asserts=0 pass=1' diff --git a/tools/card-action.sh b/tools/card-action.sh new file mode 100755 index 0000000..1a99120 --- /dev/null +++ b/tools/card-action.sh @@ -0,0 +1,358 @@ +#!/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 '' "$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