feat: add interactive FC01 heap4 card

This commit is contained in:
user
2026-07-20 12:04:14 +02:00
parent bd34c7ec1e
commit 48eccb5e24
39 changed files with 7744 additions and 210 deletions
+203 -208
View File
@@ -1,8 +1,8 @@
\documentclass[11pt]{article}
\documentclass[10pt]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[polish]{babel}
\usepackage[a4paper,margin=1.75cm]{geometry}
\usepackage[a4paper,margin=1.55cm]{geometry}
\usepackage{array,tabularx,booktabs}
\usepackage{amsmath,amssymb}
\usepackage{xcolor,listings}
@@ -12,6 +12,7 @@
\definecolor{accent}{HTML}{16324A}
\definecolor{accentlight}{HTML}{EEF3F7}
\definecolor{rulegray}{HTML}{D7DEE5}
\definecolor{success}{HTML}{236B45}
\hypersetup{colorlinks=true,linkcolor=accent,urlcolor=blue}
\IfFileExists{build-meta.tex}{\input{build-meta.tex}}{\newcommand{\BuildCommit}{local}}
\newcommand{\PublisherDomain}{mpabi}
@@ -20,292 +21,286 @@
\newcommand{\CardArea}{inf}
\newcommand{\CardSeries}{rv32i-freertos}
\newcommand{\CardNumber}{01}
\newcommand{\CardCount}{14}
\newcommand{\CardCount}{15}
\newcommand{\CardSlug}{heap4}
\newcommand{\CardVersion}{v0.2}
\newcommand{\CardVersion}{v0.3}
\newcommand{\DocumentKey}{\PublisherDomain/\CardArea/\CardSeries/\CardNumber/\CardSlug/\CardVersion}
\newcommand{\DocumentUUID}{4654524c-73f2-46df-83a3-d448ba6d6c5a}
\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}
numbers=none,backgroundcolor=\color{accentlight},rulecolor=\color{rulegray}
}
\pagestyle{fancy}
\fancyhf{}
\lhead{\textbf{FreeRTOS: heap\_4}}
\rhead{\small karta 01/14}
\lhead{\textbf{FC01 · FreeRTOS \texttt{heap\_4}}}
\rhead{\small 30 minut · Pico 2 W / RP2350}
\lfoot{\scriptsize commit \BuildCommit}
\cfoot{\scriptsize \thepage/\pageref{LastPage}}
\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}
\setlength{\headheight}{14pt}
\setlength{\footskip}{19pt}
\setlist[itemize]{nosep,leftmargin=1.45em}
\setlist[enumerate]{nosep,leftmargin=1.65em}
\newcommand{\checkline}{\(\square\)\;}
\newcommand{\blank}[1]{\rule{#1}{.2pt}}
\begin{document}
\sloppy
\begin{center}
{\LARGE\bfseries Karta pracy: FreeRTOS \texttt{heap\_4}}\par
\vspace{.4em}
{\large model $\rightarrow$ prawdziwy upstream $\rightarrow$ RP2350}\par
{\LARGE\bfseries FreeRTOS \texttt{heap\_4}: od kursora do wolnych bloków}\par
\vspace{.25em}
{\large bezpośrednia kontynuacja projektu K\&R 5.4}\par
\end{center}
\begin{tabularx}{\textwidth}{@{}lX@{}}
\noindent\begin{tabularx}{\textwidth}{@{}p{1.45cm}Xp{1.4cm}X@{}}
\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} \\
Karta & FC01 / \CardCount & Czas & 30 minut \\
Płytka & Raspberry Pi Pico 2 W & Układ & RP2350, rdzeń RISC-V \\
Allocator & FreeRTOS-Kernel V11.3.0 & Źródło & upstream \texttt{heap\_4.c} \\
Wersja & \CardVersion & Data & 19.07.2026 \\
\bottomrule
\end{tabularx}
\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*{Punkt startowy — tego już nie powtarzamy}
\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}
Z poprzedniej karty działa projekt:
\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
\begin{lstlisting}
allocbuf[64]; allocp = allocbuf;
alloc_local(5) -> offset 0
alloc_local(7) -> offset 5
allocp -> offset 12
alloc_local(0) i OOM -> NULL; kursor pozostaje na 12
\end{lstlisting}
\begin{tabularx}{\textwidth}{@{}p{3.1cm}XXX@{}}
\noindent\fcolorbox{accent}{accentlight}{%
\begin{minipage}{.94\textwidth}
\textbf{Pytanie projektowe.} Jak zwolnić obszar 5 B spod offsetu 0, zachować
późniejszy obszar 7 B spod offsetu 5 i ponownie wykorzystać powstałą dziurę?
W naszym projekcie nie istnieje \texttt{free}; reset odzyskuje całą arenę.
Samo cofnięcie kursora zniszczyłoby również późniejsze przydziały.
\end{minipage}}
\section*{Cel lekcji}
Potrafię wskazać trzy elementy dodane przez \texttt{heap\_4} do alokatora
liniowego --- \textbf{nagłówek bloku, listę wolnych bloków i scalanie} --- oraz
potwierdzić ich działanie na prawdziwym \texttt{heap\_4.c}.
\section*{Plan 30 minut}
\begin{tabularx}{\textwidth}{@{}p{1.55cm}p{3.0cm}X@{}}
\toprule
& AMD64 & Hazard3 & RP2350 \\
Czas & Tryb & Wynik \\
\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 \\
0--3 & problem K\&R & nazwać, dlaczego jeden kursor nie zwolni obszaru ze środka \\
3--4 & kontekst & FreeRTOS linkuje jedną implementację; tutaj pracujemy tylko z \texttt{heap\_4} \\
4--9 & Demo 1 & przewidzieć nagłówek, first-fit i split \\
9--16 & Demo 2 & przewidzieć listę adresową i scalenie \texttt{2 -> 1} \\
16--27 & Task 3 RUN & dwa checkpointy prawdziwego \texttt{heap\_4.c} na Pico 2 W \\
27--30 & wyjście & odróżnić total free, largest free i minimum-ever \\
\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.
\vfill
\noindent\textbf{Jedna minuta kontekstu:} \texttt{heap\_1} przypomina rosnący
kursor bez \texttt{free}; \texttt{heap\_2} nie scala; \texttt{heap\_3} używa
alokatora biblioteki C; \texttt{heap\_5} rozszerza mechanikę \texttt{heap\_4}
na wiele regionów. Nie są to tematy tej lekcji.
\newpage
\section{Task 1 — first-fit i split}
\section{Most: od K\&R 5.4 do \texttt{heap\_4}}
\subsection*{Policz przed uruchomieniem}
\begin{tabular}{@{}lcc@{}}
\begin{tabularx}{\textwidth}{@{}p{3.5cm}p{4.8cm}X@{}}
\toprule
& AMD64 & RV32 \\
K\&R 5.4 — co mamy & Ograniczenie & Co dodaje \texttt{heap\_4} \\
\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} \\
\texttt{allocbuf[64]}, \texttt{allocp} & istnieje tylko wolny ogon areny & arena podzielona na opisane bloki \\
wynik to surowy adres & później nie znamy rozmiaru obszaru & nagłówek \texttt{BlockLink\_t} przed payloadem \\
przydział przesuwa kursor & nie wykorzystamy dziury & lista wolnych bloków i first-fit \\
reset całej areny & brak zwolnienia jednego obszaru & \texttt{vPortFree()} wskazanego bloku \\
brak relacji między dziurami & sąsiednie dziury są rozdzielone & porządek adresowy i coalescing \\
sukces albo \texttt{NULL} & brak miary fragmentacji & free, largest, block count, minimum-ever \\
\bottomrule
\end{tabular}
\end{tabularx}
\subsection*{Nagłówek realnego bloku}
\begin{lstlisting}
typedef struct A_BLOCK_LINK {
struct A_BLOCK_LINK *pxNextFreeBlock;
size_t xBlockSize; /* MSB oznacza blok przydzielony */
} BlockLink_t;
[ BlockLink_t | payload zwracany przez pvPortMalloc() ]
^ metadata ^ adres widziany przez aplikacje
\end{lstlisting}
\textbf{Granica modelu:} Task 1--2 używają uproszczonego nagłówka
\texttt{\{next,size\}}. Bit allocated i pełne zabezpieczenia oglądamy dopiero
w upstream \texttt{heap\_4.c}.
\section{Demo 1 — pierwszy pasujący blok i split (5 minut)}
Na RP2350/RV32 nagłówek modelu ma 8 B. Dla żądania 13 B:
\[
\texttt{wanted}=\operatorname{align8}(8+13)=24\ \mathrm{B}
\]
\begin{lstlisting}
free list przed: [ block 16 B ] -> [ block 256 B ] -> END
request po naglowku i align8: 24 B
\end{lstlisting}
\textbf{Predykcja — wpisz przed odsłonięciem:}
\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.
\item Pierwszy pasujący blok ma \blank{1.5cm} B.
\item Blok przydzielony ma \blank{1.5cm} B, a remainder \blank{1.5cm} B.
\item Równość zachowania areny: \blank{2cm} + \blank{2cm} = 256 B.
\end{enumerate}
\lstinputlisting{../src/tasks/task01_first_fit_split.c}
\noindent\textbf{Sprawdzenie po predykcji:}
\texttt{16 B} nie mieści żądania, więc first-fit wybiera \texttt{256 B}; split
daje \texttt{24 B + 232 B}. Realny \texttt{heap\_4} dzieli blok tylko wtedy,
gdy reszta jest większa od minimalnego rozmiaru bloku.
\noindent\textbf{Nie uruchamiamy osobnego debugowania Task 1.} Model służy do
policzenia geometrii; centralny RUN jest w Task 3.
\newpage
\section{Task 2 — lista adresowa i coalescing}
\section{Demo 2 — lista adresowa i coalescing (7 minut)}
\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}
\texttt{[ A:48 FREE ][ B:48 USED ][ C:48 FREE ]}
\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}
\begin{lstlisting}
pamiec: A -------- B -------- C
free list: A ------------------> C -> END
\end{lstlisting}
\textbf{Predykcja:} po \texttt{vPortFree(B)} narysuj listę i uzupełnij:
\subsection*{Tabela obserwacji}
\begin{tabular}{@{}lccc@{}}
\toprule
Stan & liczba bloków & suma & największy \\
Stan & liczba wolnych bloków & suma wolna & największy blok \\
\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} \\
przed zwolnieniem B & \blank{1.2cm} & \blank{1.2cm} & \blank{1.2cm} \\
po zwolnieniu B & \blank{1.2cm} & \blank{1.2cm} & \blank{1.2cm} \\
\bottomrule
\end{tabular}
\lstinputlisting{../src/tasks/task02_address_order_coalesce.c}
\subsection*{Odsłoń po zapisaniu predykcji}
\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.
\item Wstaw B między A i C według adresu.
\item \texttt{end(B) == address(C)}: połącz B z C.
\item \texttt{end(A) == address(BC)}: połącz A z BC.
\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"
\begin{lstlisting}
przed: A:48 -> C:48 -> END blocks=2 total=96 largest=48
po: A:144 -> END blocks=1 total=144 largest=144
\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}
Porządek po adresie sprawia, że fizycznych sąsiadów można sprawdzić podczas
jednego wstawienia. To jest mechanizm funkcji
\texttt{prvInsertBlockIntoFreeList()} z \texttt{heap\_4.c}.
\subsection*{Breakpointy}
\begin{lstlisting}[numbers=none]
b pvPortMalloc
b vPortFree
b prvInsertBlockIntoFreeList
p xStart
p pxEnd
\section{Task 3 — jedyny centralny RUN (11 minut)}
Obraz i sesja muszą być przygotowane przed lekcją. Nie kompilujemy ani nie
flashujemy w czasie tych 30 minut.
\begin{lstlisting}[language=bash]
# wykonane przed lekcja:
stemctl debug rp2350 freertos heap4 3 --device /dev/bus/usb/BBB/DDD
\end{lstlisting}
\begin{lstlisting}
RESET/INIT
-> ALLOC A(24) -> ALLOC B(40) -> ALLOC C(16)
-> FREE A -> FREE C
-> heap4_fragmented_checkpoint # STOP 1
-> FREE B -> OOM request
-> task03_debug_checkpoint # STOP 2
\end{lstlisting}
\textbf{STOP 1:} zapisz liczbę bloków, sumę i largest. Przed \texttt{F5}
przewidź, co zmieni \texttt{FREE B}.
\textbf{STOP 2:} sprawdź odzyskanie areny, minimum-ever, OOM i hook.
\newpage
\section{Dowód na Pico 2 W / RP2350}
\textbf{Ważne:} \texttt{heap\_4} jest plikiem wybranym i linkowanym przez
projekt. Nie jest sprzętowym heapem ani peryferium RP2350. Pico 2 W dostarcza
realną pamięć SRAM, w której obserwujemy ten sam algorytm.
\subsection*{Minimalny widok GDB}
\begin{lstlisting}
p g_fragmented_stats
p g_final_stats
p xFreeBytesRemaining
p xMinimumEverFreeBytesRemaining
x/160bx ucHeap
p xStart
p pxEnd
x/96bx ucHeap
\end{lstlisting}
\newpage
\section{RP2350 — deploy i dowód sprzętowy}
\subsection*{Tabela obserwacji}
\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@{}}
\begin{tabularx}{\textwidth}{@{}p{4.2cm}p{3.0cm}X@{}}
\toprule
Wielkość & Wartość \\
Wielkość & Odczyt & Warunek \\
\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} \\
\texttt{initial\_free} & \blank{2.3cm} & punkt odniesienia \\
\texttt{after\_allocations} & \blank{2.3cm} & mniejsze niż initial \\
STOP 1: free blocks & \blank{2.3cm} & równe 2 \\
STOP 1: total / largest & \blank{2.3cm} & largest mniejsze niż total \\
STOP 2: final free & \blank{2.3cm} & równe initial \\
STOP 2: free blocks & \blank{2.3cm} & równe 1 \\
minimum-ever & \blank{2.3cm} & nie rośnie po free \\
OOM / hook / asserts / pass & \blank{2.3cm} & \texttt{1 / 1 / 0 / 1} \\
\bottomrule
\end{tabular}
\end{tabularx}
\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}
\section*{Wyjście — trzy minuty}
\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}
\item Dlaczego jeden \texttt{allocp} nie wystarcza do zwolnienia obszaru
spod offsetu 0 przy zachowaniu obszaru spod offsetu 5?\\[.35em]
\blank{.96\linewidth}
\item Jak \texttt{vPortFree()} znajduje rozmiar payloadu, skoro dostaje tylko
jego adres?\\[.35em]
\blank{.96\linewidth}
\item Dlaczego 96 wolnych bajtów w dwóch blokach nie pozwala przydzielić
jednego bloku 80 B?\\[.35em]
\blank{.96\linewidth}
\end{enumerate}
\subsection*{Oddaj}
\subsection*{Zaliczenie}
\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.
\item \checkline wymieniam: header, free list, coalescing;
\item \checkline przewiduję \texttt{2 bloki -> 1 blok};
\item \checkline odróżniam payload, metadata allocatora i stos \texttt{sp};
\item \checkline odróżniam current free, largest free i minimum-ever;
\item \checkline Task 3 kończy się
\texttt{aligned=1 oom=1 hooks=1 asserts=0 pass=1}.
\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.
\noindent\textbf{Poza lekcją:} pełne czytanie upstream \texttt{heap\_4.c},
osobne uruchomienie Task 1--2 na AMD64/Hazard3, samodzielny deploy RP2350 oraz
porównanie z \texttt{heap\_1}, \texttt{heap\_2}, \texttt{heap\_3} i
\texttt{heap\_5}.
\end{document}