commit e69aaa6d61af39003186415f3581d519e7a290bd Author: mpabi Date: Sat May 2 00:53:25 2026 +0200 Add FreeRTOS heap4 intro card diff --git a/.gitea/workflows/render-pdf.yml b/.gitea/workflows/render-pdf.yml new file mode 100644 index 0000000..2362972 --- /dev/null +++ b/.gitea/workflows/render-pdf.yml @@ -0,0 +1,63 @@ +name: Render PDF + +on: + push: + branches: + - deploy + workflow_dispatch: + +jobs: + render-pdf: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install TeX + run: | + if ! command -v latexmk >/dev/null 2>&1; then + if ! command -v apt-get >/dev/null 2>&1; then + echo "latexmk is missing and apt-get is unavailable" >&2 + exit 1 + fi + if command -v sudo >/dev/null 2>&1; then + SUDO=sudo + else + SUDO= + fi + $SUDO apt-get update + $SUDO apt-get install -y --no-install-recommends \ + latexmk \ + texlive-fonts-recommended \ + texlive-lang-polish \ + texlive-latex-base \ + texlive-latex-extra \ + texlive-latex-recommended + fi + + - name: Render PDF with commit metadata + run: ./scripts/render_pdf.sh + + - name: Commit rendered PDF + run: | + set -eu + repo_name="${GITHUB_REPOSITORY#*/}" + pdf_file="$(find doc/pdf -maxdepth 1 -type f -name '*.pdf' -print -quit)" + if [ -z "$pdf_file" ]; then + echo "rendered PDF is missing" >&2 + exit 1 + fi + remote_sha="$(git ls-remote origin refs/heads/deploy | cut -f1)" + if [ "$remote_sha" != "$GITHUB_SHA" ]; then + echo "deploy advanced from $GITHUB_SHA to $remote_sha; skip publishing stale PDF" + exit 0 + fi + git config user.name "gitea-actions" + git config user.email "gitea-actions@noreply.local" + printf '%s\n' "$GITHUB_SHA" > doc/pdf/source-commit.txt + git add -u doc/pdf + git add -f "$pdf_file" + git add doc/pdf/source-commit.txt + git diff --cached --quiet && exit 0 + git commit -m "Render ${repo_name} PDF for ${GITHUB_SHA::12} [skip actions]" + git push origin HEAD:deploy diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eab3e99 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +*.aux +*.log +*.out +*.fls +*.fdb_latexmk +*.synctex.gz +*.pdf +build-meta.tex +.rv/ +.nvim/ +.vim/ +.build/ +.gdb_history +host-build/ +build/*/prog.bin +build/*/prog.elf +build/*/prog.lst +build/*/prog.map diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..accd3b8 --- /dev/null +++ b/Makefile @@ -0,0 +1,112 @@ +RISCV_PREFIX ?= riscv64-unknown-elf- + +CC := $(RISCV_PREFIX)gcc +OBJCOPY := $(RISCV_PREFIX)objcopy +OBJDUMP := $(RISCV_PREFIX)objdump +SIZE := $(RISCV_PREFIX)size +HOST_CC ?= cc + +DEBUG ?= 1 +ifeq ($(DEBUG),1) + DBGFLAGS := -g3 -ggdb +else + DBGFLAGS := +endif + +RV_ENV_ROOT ?= /opt/rv-env +SUPPORT_ROOT ?= $(RV_ENV_ROOT)/vendor/Hazard3 +H3_COMMON ?= $(SUPPORT_ROOT)/test/sim/common +H3_INIT ?= $(H3_COMMON)/init.S +LDSCRIPT ?= $(H3_COMMON)/link_hazard3.ld +LAB_RUNTIME_DIR ?= $(RV_ENV_ROOT)/vendor/lab-runtime +MEMOPS ?= $(LAB_RUNTIME_DIR)/memops.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 + +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) +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 + +.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) + +all: tasks +tasks: $(TASKS) +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 "$(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 + +$(BUILD_ROOT)/$(1)/prog.elf: crt0.S src/tasks/$(1).c src/tasks/heap4_common.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) + $(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 $$< > $$@ + +$(BUILD_ROOT)/$(1)/$(1).s: src/tasks/$(1).c src/tasks/heap4_common.h | check-support + 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 $$@ $$< +endef + +$(foreach task,$(TASKS),$(eval $(call task_rules,$(task)))) + +clean: + rm -rf $(BUILD_ROOT) $(HOST_BUILD_ROOT) diff --git a/README.md b/README.md new file mode 100644 index 0000000..4ed2f1e --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# rv32i-freertos / heap4 + +Karta pracy 1/? z serii `rv32i-freertos`. + +Temat: mechanika alokatora `heap_4.c` z FreeRTOS-a na małych przykładach +uruchamianych w środowisku RV32I. + +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ń. + +Kod w `src/tasks` jest dydaktycznym modelem zachowania `heap_4`, a nie kopią +pliku upstream FreeRTOS. + +## 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`. + +## Budowanie + +```bash +RV_ENV_ROOT=~/dev/workspace/rv/tools/rv32i-hazard3-student-env make tasks +make host +``` + +## Praca przez rvctl + +```bash +./rvctl series cards fetch inf heap4 +./rvctl card use inf heap4 +./rvctl tasks list +./rvctl debug rv32i 1 +``` diff --git a/build/task01_heap_map/task01_heap_map.s b/build/task01_heap_map/task01_heap_map.s new file mode 100644 index 0000000..dea13e1 --- /dev/null +++ b/build/task01_heap_map/task01_heap_map.s @@ -0,0 +1,53 @@ + .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 new file mode 100644 index 0000000..5dd5d99 --- /dev/null +++ b/build/task02_free_list/task02_free_list.s @@ -0,0 +1,73 @@ + .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 new file mode 100644 index 0000000..cffae9f --- /dev/null +++ b/build/task03_heap_init/task03_heap_init.s @@ -0,0 +1,74 @@ + .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 new file mode 100644 index 0000000..db04ead --- /dev/null +++ b/build/task04_alignment/task04_alignment.s @@ -0,0 +1,47 @@ + .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 new file mode 100644 index 0000000..34fc98e --- /dev/null +++ b/build/task05_malloc_first_fit/task05_malloc_first_fit.s @@ -0,0 +1,95 @@ + .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 new file mode 100644 index 0000000..610dfa4 --- /dev/null +++ b/build/task06_split_block/task06_split_block.s @@ -0,0 +1,117 @@ + .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 new file mode 100644 index 0000000..4dd5308 --- /dev/null +++ b/build/task07_free_insert/task07_free_insert.s @@ -0,0 +1,116 @@ + .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 new file mode 100644 index 0000000..a60e79e --- /dev/null +++ b/build/task08_coalescing/task08_coalescing.s @@ -0,0 +1,116 @@ + .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 new file mode 100644 index 0000000..de74cea --- /dev/null +++ b/build/task09_config_macros/task09_config_macros.s @@ -0,0 +1,44 @@ + .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 new file mode 100644 index 0000000..215bceb --- /dev/null +++ b/build/task10_heap4_demo/task10_heap4_demo.s @@ -0,0 +1,253 @@ + .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/crt0.S b/crt0.S new file mode 100644 index 0000000..86409d3 --- /dev/null +++ b/crt0.S @@ -0,0 +1,33 @@ + .text + .global _start + .type _start, @function + +_start: + # Initialize global pointer. +.option push +.option norelax + la gp, __global_pointer$ +.option pop + + # Stack pointer is intentionally not set here. + # In the Hazard3 testbench flow, common/init.S enters _start with sp + # already loaded from __stack_top and adjusted to leave the argc/argv + # area expected by newlib-style startup code. + + # Clear the .bss segment so debug globals start in a known state. + la a0, __bss_start + la a1, __bss_end + +clear_bss: + bgeu a0, a1, finish_bss + sb x0, 0(a0) + addi a0, a0, 1 + j clear_bss +finish_bss: + + call main + + # Exit the testbench with main() return code (Hazard3 init.S provides _exit). + tail _exit + + .size _start, .-_start diff --git a/doc/main.tex b/doc/main.tex new file mode 100644 index 0000000..df43bfe --- /dev/null +++ b/doc/main.tex @@ -0,0 +1,153 @@ +\documentclass[12pt]{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{enumitem} + +\definecolor{accent}{HTML}{16324A} +\definecolor{accentlight}{HTML}{EEF3F7} +\definecolor{rulegray}{HTML}{D7DEE5} +\hypersetup{colorlinks=true,linkcolor=blue,urlcolor=blue,citecolor=blue} +\IfFileExists{build-meta.tex}{\input{build-meta.tex}}{\newcommand{\BuildCommit}{local}} +\newcommand{\PublisherDomain}{mpabi} +\newcommand{\DocumentAuthor}{M. Pabiszczak} +\newcommand{\DocumentYear}{2026} +\newcommand{\CardArea}{inf} +\newcommand{\CardSeries}{rv32i-freertos} +\newcommand{\CardNumber}{01} +\newcommand{\CardCount}{??} +\newcommand{\CardSlug}{heap4} +\newcommand{\CardVersion}{v0.1} +\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}[4]{\section{Task #1: #3}#4\lstinputlisting[language=C,caption={Task #1: #3},label={lst:task#1-c}]{../src/tasks/#2.c}\lstinputlisting[basicstyle=\ttfamily\scriptsize,caption={ASM dla task #1},label={lst:task#1-asm}]{../build/#2/#2.s}} + +\pagestyle{fancy} +\fancyhf{} +\lhead{\textbf{FreeRTOS: heap\_4}} +\rhead{\small \DocumentAuthor, \DocumentYear} +\lfoot{\scriptsize commit \BuildCommit} +\cfoot{\scriptsize \thepage/\pageref{LastPage}} +\rfoot{\scriptsize uuid \DocumentUUID} +\renewcommand{\headrulewidth}{0.4pt} +\setlength{\headheight}{18pt} +\setlength{\footskip}{24pt} + +\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: & 02.05.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} + +\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}. + +\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{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 +\end{tabularx} + +\TaskSection{01}{task01_heap_map}{mapa \texttt{heap\_4}}{Listing~\ref{lst:task01-c} pokazuje nagłówek bloku, wyrównanie rozmiaru i bit oznaczający blok zajęty.} +\TaskSection{02}{task02_free_list}{lista wolnych bloków}{Listing~\ref{lst:task02-c} przechodzi po liście wolnych bloków i liczy sumę dostępnej pamięci.} +\TaskSection{03}{task03_heap_init}{inicjalizacja sterty}{Listing~\ref{lst:task03-c} tworzy pierwszy wolny blok oraz końcowy znacznik listy.} +\TaskSection{04}{task04_alignment}{wyrównanie}{Listing~\ref{lst:task04-c} pokazuje, dlaczego alokator zaokrągla rozmiary i adres początku sterty.} +\TaskSection{05}{task05_malloc_first_fit}{\texttt{first fit}}{Listing~\ref{lst:task05-c} wybiera pierwszy wolny blok wystarczająco duży dla żądania.} +\TaskSection{06}{task06_split_block}{podział bloku}{Listing~\ref{lst:task06-c} zostawia resztę dużego bloku na liście wolnych bloków.} +\TaskSection{07}{task07_free_insert}{\texttt{free} i wstawianie}{Listing~\ref{lst:task07-c} zwraca blok do listy tak, aby lista pozostała posortowana po adresach.} +\TaskSection{08}{task08_coalescing}{scalanie bloków}{Listing~\ref{lst:task08-c} scala blok z lewym i prawym sąsiadem, gdy adresy są ciągłe.} +\TaskSection{09}{task09_config_macros}{makra konfiguracyjne}{Listing~\ref{lst:task09-c} pokazuje małe odpowiedniki makr konfiguracyjnych FreeRTOS-a.} +\TaskSection{10}{task10_heap4_demo}{mini \texttt{heap\_4}}{Listing~\ref{lst:task10-c} łączy inicjalizację, alokację, zwalnianie i scalanie w jeden model.} + +\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}. +\end{enumerate} + +\end{document} diff --git a/doc/pdf/.gitkeep b/doc/pdf/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scripts/render_pdf.sh b/scripts/render_pdf.sh new file mode 100755 index 0000000..3aa9027 --- /dev/null +++ b/scripts/render_pdf.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env sh +set -eu + +DOC_DIR="doc" +TEX_FILE="main.tex" + +script_dir=$(CDPATH= cd "$(dirname "$0")" && pwd) +repo_root=$(CDPATH= cd "$script_dir/.." && pwd) +repo_name="${REPO_NAME:-$(basename "$repo_root")}" +tex_path="$repo_root/$DOC_DIR/$TEX_FILE" + +read_tex_command() { + sed -n "s/^\\\\newcommand{\\\\$1}{\\([^}]*\\)}$/\\1/p" "$tex_path" | head -n 1 +} + +publisher_domain=$(read_tex_command PublisherDomain) +card_area=$(read_tex_command CardArea) +card_series=$(read_tex_command CardSeries) +card_number=$(read_tex_command CardNumber) +card_slug=$(read_tex_command CardSlug) +card_version=$(read_tex_command CardVersion) +document_uuid=$(read_tex_command DocumentUUID) + +if [ -z "$publisher_domain" ] || [ -z "$card_area" ] || [ -z "$card_series" ] || \ + [ -z "$card_number" ] || [ -z "$card_slug" ] || [ -z "$card_version" ] || \ + [ -z "$document_uuid" ]; then + echo "missing card metadata in $tex_path" >&2 + exit 1 +fi + +commit="${GITEA_SHA:-${GITHUB_SHA:-}}" +if [ -z "$commit" ]; then + commit=$(git -C "$repo_root" rev-parse HEAD) +fi +short_commit=$(printf '%s' "$commit" | cut -c1-12) + +meta_file="$repo_root/$DOC_DIR/build-meta.tex" +tmp_meta="$meta_file.tmp.$$" +cleanup() { + rm -f "$meta_file" "$tmp_meta" +} +trap cleanup EXIT HUP INT TERM + +printf '\\newcommand{\\BuildCommit}{%s}\n' "$short_commit" > "$tmp_meta" +mv "$tmp_meta" "$meta_file" + +pdf_out_dir="${PDF_OUT_DIR:-$repo_root/doc/pdf}" +mkdir -p "$pdf_out_dir" +out_dir=$(CDPATH= cd "$pdf_out_dir" && pwd) +pdf_basename="$publisher_domain-$card_area-$card_series-$card_number-$card_slug-$card_version-$document_uuid.pdf" +find "$out_dir" -maxdepth 1 -type f -name '*.pdf' -delete + +( + cd "$repo_root/$DOC_DIR" + latexmk -pdf -interaction=nonstopmode -outdir="$out_dir" "$TEX_FILE" +) + +source_pdf="$out_dir/${TEX_FILE%.tex}.pdf" +target_pdf="$out_dir/$pdf_basename" +if [ "$source_pdf" != "$target_pdf" ]; then + mv "$source_pdf" "$target_pdf" +fi + +find "$out_dir" -maxdepth 1 -type f \( \ + -name '*.aux' -o \ + -name '*.log' -o \ + -name '*.out' -o \ + -name '*.fls' -o \ + -name '*.fdb_latexmk' \ +\) -delete diff --git a/src/tasks/heap4_common.h b/src/tasks/heap4_common.h new file mode 100644 index 0000000..3d79f9d --- /dev/null +++ b/src/tasks/heap4_common.h @@ -0,0 +1,61 @@ +#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/task01_heap_map.c b/src/tasks/task01_heap_map.c new file mode 100644 index 0000000..b7c52b7 --- /dev/null +++ b/src/tasks/task01_heap_map.c @@ -0,0 +1,29 @@ +#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_free_list.c b/src/tasks/task02_free_list.c new file mode 100644 index 0000000..eb32284 --- /dev/null +++ b/src/tasks/task02_free_list.c @@ -0,0 +1,64 @@ +#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_heap_init.c b/src/tasks/task03_heap_init.c new file mode 100644 index 0000000..eda51c7 --- /dev/null +++ b/src/tasks/task03_heap_init.c @@ -0,0 +1,49 @@ +#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 new file mode 100644 index 0000000..debb85e --- /dev/null +++ b/src/tasks/task04_alignment.c @@ -0,0 +1,26 @@ +#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 new file mode 100644 index 0000000..4303c95 --- /dev/null +++ b/src/tasks/task05_malloc_first_fit.c @@ -0,0 +1,73 @@ +#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 new file mode 100644 index 0000000..49c0d51 --- /dev/null +++ b/src/tasks/task06_split_block.c @@ -0,0 +1,93 @@ +#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 new file mode 100644 index 0000000..be428a8 --- /dev/null +++ b/src/tasks/task07_free_insert.c @@ -0,0 +1,88 @@ +#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 new file mode 100644 index 0000000..02b3f48 --- /dev/null +++ b/src/tasks/task08_coalescing.c @@ -0,0 +1,76 @@ +#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 new file mode 100644 index 0000000..cc64b13 --- /dev/null +++ b/src/tasks/task09_config_macros.c @@ -0,0 +1,34 @@ +#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 new file mode 100644 index 0000000..f16b408 --- /dev/null +++ b/src/tasks/task10_heap4_demo.c @@ -0,0 +1,175 @@ +#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; +}