<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://satyria.de/arm/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Satyria</id>
	<title>C und Assembler mit Raspberry - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="https://satyria.de/arm/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Satyria"/>
	<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Spezial:Beitr%C3%A4ge/Satyria"/>
	<updated>2026-06-24T00:20:56Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.42.1</generator>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1321</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1321"/>
		<updated>2026-06-05T13:55:09Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Zusammenfassung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 4 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 4 muss wissen, dass wir ein Bare-Metal-Programm via JTAG debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2711-rpi-4-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bootcode.bin&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;fixup4.dat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;start4.elf&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
=== Hardware verbinden ===&lt;br /&gt;
&lt;br /&gt;
==== Verdrahtung ====&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A72 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 4 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1320</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1320"/>
		<updated>2026-06-05T13:54:36Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Die Debugger-Ansichten nutzen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 4 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 4 muss wissen, dass wir ein Bare-Metal-Programm via JTAG debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2711-rpi-4-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bootcode.bin&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;fixup4.dat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;start4.elf&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
=== Hardware verbinden ===&lt;br /&gt;
&lt;br /&gt;
==== Verdrahtung ====&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A72 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1319</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1319"/>
		<updated>2026-06-05T13:53:15Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Hardware vorbereiten und Debugging in der GUI */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 4 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 4 muss wissen, dass wir ein Bare-Metal-Programm via JTAG debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2711-rpi-4-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bootcode.bin&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;fixup4.dat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;start4.elf&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
=== Hardware verbinden ===&lt;br /&gt;
&lt;br /&gt;
==== Verdrahtung ====&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1318</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1318"/>
		<updated>2026-06-05T13:52:07Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Hardware vorbereiten und SD-Karte einrichten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 4 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 4 muss wissen, dass wir ein Bare-Metal-Programm via JTAG debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2711-rpi-4-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bootcode.bin&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;fixup4.dat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;start4.elf&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden. &lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1317</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1317"/>
		<updated>2026-06-05T13:42:39Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Linkerscript und Makefile erstellen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden. &lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1316</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1316"/>
		<updated>2026-06-05T13:40:26Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Die .vscode Konfigurationsdateien anlegen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden. &lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1315</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1315"/>
		<updated>2026-06-05T13:39:49Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Die .vscode Konfigurationsdateien anlegen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden. &lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1314</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1314"/>
		<updated>2026-06-05T13:38:21Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden. &lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1313</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1313"/>
		<updated>2026-06-05T13:08:33Z</updated>

		<summary type="html">&lt;p&gt;Satyria: Die Seite wurde neu angelegt: „Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.  Um direkt mit dem Raspberry Pi 5 zu kommunizieren u…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Um direkt mit dem Raspberry Pi 5 zu kommunizieren und Programme zu debuggen, wird zusätzlich eine &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039; benötigt. Diese wird über USB mit einem Windows-Rechner und über die Debug-Pins mit dem Raspberry Pi 5 verbunden.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und der Debug Probe verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Überprüfen Sie anschließend, ob die Datei &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; existiert. Diese wird in der Regel standardmäßig mitgeliefert. Sollte sie fehlen, erstellen Sie diese Datei neu mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Da in älteren oder Standard-OpenOCD-Releases ein passendes Target-Profil für den neuen Broadcom-Chip BCM2712 des Raspberry Pi 5 fehlt, müssen wir dieses manuell anlegen. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP für direkten Zugriff&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# Diese Adressen wurden aus der ROM-Table via &#039;dap info 0&#039;-Befehl ausgelesen&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Standard-Target ist cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Überschreibt den standardmäßigen &amp;quot;reset halt&amp;quot; Befehl von VS-Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Befehle, die direkt nach dem Verbinden ausgeführt werden&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Zwingt den Prozessor direkt auf die Startadresse&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI5)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Bit 9 auf 0 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Bit 9 auf 1 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO-Definitionen für den Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; setzt die Startadresse im RAM fest, an die unser Kernel geladen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 5 in den JTAG-/SWD-Modus um. Dadurch wird die Kommunikation mit der Raspberry Pi Debug Probe überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 5.&lt;br /&gt;
* Verbinden Sie die Raspberry Pi Debug Probe über das mitgelieferte 3-Pol-UART/Debug-Kabel mit dem dedizierten Debug-Port des Raspberry Pi 5 (befindet sich zwischen den Micro-HDMI-Ports).&lt;br /&gt;
* Schließen Sie die Debug Probe per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 5 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 5 über die Debug Probe auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 5 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Hauptseite&amp;diff=1312</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Hauptseite&amp;diff=1312"/>
		<updated>2026-06-05T13:07:54Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Anhang */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== &amp;lt;strong&amp;gt;C und ARM-Assemblerprogrammierung mit dem Raspberry&amp;lt;/strong&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
[[Datei:EinhornOrg.png|frameless|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Matthias Steiner&lt;br /&gt;
&lt;br /&gt;
Satyria Press © by 2020-2025&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vorwort ===&lt;br /&gt;
&lt;br /&gt;
Herzlich willkommen auf unserer Seite zur Bare-Metal-Programmierung für den Raspberry Pi 4 und Pi 5! Diese Webseite richtet sich sowohl an Anfänger als auch an fortgeschrittene Programmierer, die tief in die Welt der hardwarenahen Programmierung eintauchen möchten.&lt;br /&gt;
&lt;br /&gt;
Ursprünglich sollte diese Seite sich ausschließlich mit Assembler beschäftigen, wurde jedoch im Laufe der Zeit um C erweitert. Daher finden Sie hier zunächst grundlegende Informationen zu Assembler. Wenn Ihr Interesse in der C-Programmierung liegt und Sie mit Windows arbeiten, empfehlen wir Ihnen mit dem Kapitel &#039;&#039;&#039;[[Programmierumgebung erstellen (Konsole)]]&#039;&#039;&#039; zu beginnen. Für Linux-Nutzer ist das Kapitel &#039;&#039;&#039;[[Programmierumgebung erstellen unter Linux]]&#039;&#039;&#039; der richtige Startpunkt.&lt;br /&gt;
&lt;br /&gt;
Im Kapitel &#039;&#039;&#039;Raspberry Pi 4 Assembler 32-Bit&#039;&#039;&#039; beschreiben wir die Assembler-Programmierung für den Raspberry Pi 4. Dieses Kapitel hat zwar bereits einige Jahre auf dem Buckel, bleibt aber weiterhin relevant und wertvoll. Hier finden Sie detaillierte Erklärungen der ARM-32-Bit-Befehle mit anschaulichen Beschreibungen und Beispielen, was besonders Anfängern den Einstieg erleichtern soll.&lt;br /&gt;
&lt;br /&gt;
Das Kapitel &#039;&#039;&#039;Raspberry Pi 4 C (64-Bit)&#039;&#039;&#039; entstand im Laufe unserer Arbeit am Raspberry Pi 5 und bietet eine umfassende Beschreibung der 64-Bit-C-Programmierung für den Raspberry Pi 4. Dies wird auch später die USB-Programmierung einschließen, eine Funktion, die wir später auf den Raspberry Pi 5 übertragen werden. So haben alle Projekte einen gemeinsamen Ausgangspunkt.&lt;br /&gt;
&lt;br /&gt;
Das Kapitel &#039;&#039;&#039;Raspberry Pi 5&#039;&#039;&#039; ist der ideale Startpunkt für die Programmierung des Raspberry Pi 5 in Assembler und C. Es führt Sie von der Installation des Hostsystems (Windows/Linux) bis zur abschließenden Programmierung in C und Assembler.&lt;br /&gt;
&lt;br /&gt;
Im Anhang finden Sie viele nützliche Informationen, wie eine Beschreibung der &#039;&#039;&#039;GNU Compiler Collection&#039;&#039;&#039;, eine Auflistung des &#039;&#039;&#039;ARM-Befehlssatzes&#039;&#039;&#039; (im Aufbau), verschiedene &#039;&#039;&#039;Raspberry Pi Modelle&#039;&#039;&#039; und einige hilfreiche &#039;&#039;&#039;Links&#039;&#039;&#039; zu anderen Projekten. Ein aktuelles Projekt zum Thema &#039;&#039;&#039;Debugging&#039;&#039;&#039; ist ebenfalls dort zu finden.&lt;br /&gt;
&lt;br /&gt;
Wir hoffen, dass Sie auf unserer Seite viel Freude haben und sich gut zurechtfinden! Bei Fragen oder Kommentaren schicken Sie uns gerne eine E-Mail an assem@satyria.de. Wir freuen uns über Ihre Rückmeldungen und Anregungen.&lt;br /&gt;
&lt;br /&gt;
Viel Spaß beim Programmieren!&lt;br /&gt;
&lt;br /&gt;
== Sponsor ==&lt;br /&gt;
Wenn ihr uns etwas unterstützen möchtet, schaut einfach mal hier vorbei:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Logo hexenlaedle.png|frameless|200x200px|link=https://www.hexenlaedle.de]]&lt;br /&gt;
&lt;br /&gt;
== Inhalt ==&lt;br /&gt;
*[[Vorwort]]&lt;br /&gt;
*[[Grundlegendes zu Assembler]]&lt;br /&gt;
**[[Grundlegendes zu Assembler#Der ARM-Assembler|Der ARM-Assembler]]&lt;br /&gt;
*[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 ==&lt;br /&gt;
*[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
*[[Einführung in C- und Assemblerprogrammierung mit dem Raspberry Pi 5]]&lt;br /&gt;
**[[Einführung in C- und Assemblerprogrammierung mit dem Raspberry Pi 5#Warum C und Assembler?|Warum C und Assembler?]]&lt;br /&gt;
*[[Programm auf dem Raspberry Pi 5 ausführen]]&lt;br /&gt;
*[[Arbeiten mit Make und Linker-Script]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 Assembler==&lt;br /&gt;
*[[Unser erstes Programm (PI5)]]&lt;br /&gt;
*[[Lass die LED leuchten (PI5)]]&lt;br /&gt;
*[[Fehlerbehandlung]]&lt;br /&gt;
*[[Grafik (PI5)]]&lt;br /&gt;
*[[Chars (PI5)]]&lt;br /&gt;
*Das Terminal (PI5)&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 C ==&lt;br /&gt;
*[[Unser erstes Programm in C (PI5)]]&lt;br /&gt;
*[[Lass die LED leuchten in C (PI5)]]&lt;br /&gt;
*[[Fehlerbehandlung in C (PI5)]]&lt;br /&gt;
*[[Grafik in C (PI5)]]&lt;br /&gt;
*[[Chars in C (PI5)]]&lt;br /&gt;
*[[Das Terminal in C (PI5)]]&lt;br /&gt;
*[[printf in BareMetal (PI5)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 Assembler 32-Bit ==&lt;br /&gt;
*[[Programmierumgebung erstellen (32-Bit)]]&lt;br /&gt;
*[[Systemprogrammierung / Bare Metal]]&lt;br /&gt;
*[[Das erste Programm]]&lt;br /&gt;
*[[General Purpose I/O]]&lt;br /&gt;
*[[System Timer]]&lt;br /&gt;
*[[UART]]&lt;br /&gt;
*[[Die Anzeige]]&lt;br /&gt;
*[[Zeichenfunktionen]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 C (64-Bit) ==&lt;br /&gt;
*[[Unser erstes Programm in C (PI4)]]&lt;br /&gt;
*[[Lass die LED leuchten in C (PI4)]]&lt;br /&gt;
*[[Fehlerbehandlung in C (PI4)]]&lt;br /&gt;
*[[Grafik in C (PI4)]]&lt;br /&gt;
*[[Chars in C (PI4)]]&lt;br /&gt;
*[[Das Terminal in C (PI4)]]&lt;br /&gt;
*[[Printf in BareMetal (PI4)]]&lt;br /&gt;
*[[Systeminformationen (PI4)]] (noch kein Inhalt)&lt;br /&gt;
*[[Interrupts (PI4)]]&lt;br /&gt;
**[[Beispiel Timer-Interrupt (PI4)]]&lt;br /&gt;
*[[Interrupt Teil 2 (PI4)]]&lt;br /&gt;
&lt;br /&gt;
== ARM64 Assembler ==&lt;br /&gt;
*[[Programmieren mit ARM64 Assembler]]&lt;br /&gt;
&lt;br /&gt;
== Zusätzliches Material ==&lt;br /&gt;
*[[Links der verwendeten Software]]&lt;br /&gt;
&lt;br /&gt;
== Anhang ==&lt;br /&gt;
*[[GNU Compiler Collection]]&lt;br /&gt;
*[[ARM-Befehlssatz]]&lt;br /&gt;
**[[Grundlegende Befehle]]&lt;br /&gt;
&amp;lt;!--**[[ARMv8-Übersicht]]--&amp;gt;&lt;br /&gt;
*[[Raspberry PI Modelle]]&lt;br /&gt;
**[[Basisadressen der Modelle]]&lt;br /&gt;
*[[LINKS]]&lt;br /&gt;
* Debugging&lt;br /&gt;
**[[Bare-Metal Debugging (JTAG, RPI 4)]]&lt;br /&gt;
&amp;lt;!--*[[Bare-Metal Debugging]]--&amp;gt;&lt;br /&gt;
**[[Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4]]&lt;br /&gt;
**[[Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5]]&lt;br /&gt;
----&lt;br /&gt;
*[[Die wichtigsten Linux-Terminal-Befehle]]&lt;br /&gt;
&lt;br /&gt;
== Translations/Traducciones ==&lt;br /&gt;
*[[English]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1311</id>
		<title>Bare-Metal Debugging (JTAG, RPI 4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1311"/>
		<updated>2026-06-05T13:06:17Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Konfigurationsdatei erstellen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Bei der Entwicklung von Software wird oft ein Debugger benötigt, um Fehler zu finden und zu beheben. Ein gängiger Debugger ist GDB. Solange das Programm aus einer Umgebung wie Linux oder Windows startbar ist, kann dieser Debugger direkt verwendet werden. Bei der Bare-Metal-Entwicklung sind die Dinge jedoch etwas schwieriger, da hier die gewohnte Betriebssystemumgebung fehlt. In solchen Fällen sind Emulatoren oder spezielle Hardwarelösungen nötig.&lt;br /&gt;
&lt;br /&gt;
Da Emulatoren meist nicht zu 100 % die Hardware widerspiegeln können, verwenden wir das Hostsystem, welches direkt mit dem Raspberry Pi 4 (RPI4) kommuniziert. Das Programm wird auf dem Raspberry Pi direkt ausgeführt. Über das Hostsystem können wir jedoch den Code direkt manipulieren und sehen das Ergebnis unmittelbar auf der Hardware.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten ==&lt;br /&gt;
Das FT232H Modul wurde zwar mit Stiftleisten geliefert, aber diese mussten zunächst auf das Modul aufgelötet werden. Anschließend konnte mit Kabelbrücken die Verbindung mit dem Raspberry Pi 4 hergestellt werden.&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Windows ==&lt;br /&gt;
Unter Windows verwenden wir die MSYS2-Umgebung. Diese bietet eine Unix-ähnliche Umgebung unter Windows und ist nützlich für viele Entwicklungsaufgaben.&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Falls noch nicht geschehen, installieren wir MSYS2 nach folgender Anleitung [[Programmierumgebung erstellen (Konsole)]] und richten die Programmierumgebung ein, indem wir die folgenden Befehle ausführen:&lt;br /&gt;
&lt;br /&gt;
* Starte MSYS2 MSYS.&lt;br /&gt;
* Gib die folgenden Befehle ein, um OpenOCD zu installieren und die Umgebungsvariablen zu setzen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
pacman -S mingw64/mingw-w64-x86_64-openocd&lt;br /&gt;
echo &#039;export PATH=&amp;quot;/mingw64/bin:$PATH&amp;quot;&#039; &amp;gt;&amp;gt; ~/.bashrc&lt;br /&gt;
source ~/.bashrc&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;C:\msys64\mingw64\share\openocd\scripts\interface\ftdi&amp;quot; ab, wenn MSYS2 nach den Vorgaben installiert wurde.&lt;br /&gt;
&lt;br /&gt;
=== Treiberinstallation ===&lt;br /&gt;
OpenOCD möchte auf das Gerät über libusb zugreifen. In der Regel bietet Windows diesen Treiber nicht, sodass wir hier noch einen Treiber installieren müssen. Dazu verwenden wir &amp;quot;Zadig&amp;quot;, welches von https://zadig.akeo.ie/ heruntergeladen werden kann.&lt;br /&gt;
&lt;br /&gt;
# Installiere Zadig.&lt;br /&gt;
# Starte Zadig und aktiviere im Menü &amp;quot;Options&amp;quot; die Option &amp;quot;List All Devices&amp;quot;.&lt;br /&gt;
# Wähle &amp;quot;Single RS232-HS&amp;quot; aus der Geräteliste.&lt;br /&gt;
# Als Treiber wähle &amp;quot;libusbK&amp;quot; aus und klicke auf &amp;quot;Replace Driver&amp;quot;.&lt;br /&gt;
Damit ist Windows bereit, mit dem Modul über OpenOCD zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Linux ==&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Zunächst benötigen wir OpenOCD:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install openocd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;/usr/share/openocd/scripts/interface/ftdi&amp;quot; ab.&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi vorbereiten ==&lt;br /&gt;
Nachdem unser Betriebssystem auf dem Raspberry Pi eingerichtet ist, müssen wir den Raspberry Pi 4 für Bare-Metal-Programmierung vorbereiten. Dazu schreiben wir ein kleines Programm, das nur eine Endlosschleife erzeugt, aber den Raspberry Pi startet:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The boot program for RPI4&lt;br /&gt;
// 07.03.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  b _start          // endless loop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir verwenden ein Makefile, um das Programm zu kompilieren und zu linken (siehe [[Unser erstes Programm in C (PI4)#Kompilieren des Programms mit Make]]). Den erzeugten &amp;quot;kernel8.img&amp;quot; kopieren wir auf die SD-Karte. Beachte, dass auch folgende Dateien im Root-Verzeichnis der SD-Karte liegen müssen:&lt;br /&gt;
* bcm2711-rpi-4-b.dtb&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* fixup4.dat&lt;br /&gt;
* start4.elf&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir dem Raspberry Pi auch mitteilen, dass er auf JTAG reagiert. Dazu erstellen wir eine config.txt mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Datei legen wir ebenfalls in das Root-Verzeichnis der SD-Karte.&lt;br /&gt;
&lt;br /&gt;
== Starten von OpenOCD und Verwendung von GDB == &lt;br /&gt;
=== Verbindung herstellen ===&lt;br /&gt;
Verbinde zunächst das FT232H-Modul per USB mit dem Hostsystem. Auf dem Modul sollte nun eine LED leuchten, welche anzeigt, dass das Gerät betriebsbereit ist. Starte den Raspberry Pi. Wenn du ihn an einem Monitor angeschlossen hast, wird auf dem Bildschirm das Regenbogenbild angezeigt, was bedeutet, dass das System gestartet wurde.&lt;br /&gt;
&lt;br /&gt;
Starte nun &amp;quot;MSYS2 MSYS&amp;quot; oder unter Linux ein Terminal:&lt;br /&gt;
[[Datei:OpenOcd.png|mini]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
openocd -f interface/ftdi/ft232h-jtag.cfg -f target/bcm2711.cfg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn alles korrekt eingerichtet ist, sollte OpenOCD alle vier CPUs des Raspberry Pi anzeigen. Es zeigt außerdem, dass die Ports 3333, 3334, 3335 und 3336 für die GDB-Kommunikation bereitgestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== Kernel-Upload und Debugging ===&lt;br /&gt;
Um sicherzugehen, öffnen wir ein weiteres &amp;quot;MSYS2 MSYS&amp;quot;-Fenster oder zweites Terminal unter Linux. Hier wechseln wir in das Verzeichnis, welches den zu testenden Code enthält. Falls der Code noch nicht kompiliert ist, muss dies jetzt erledigt werden.&lt;br /&gt;
&lt;br /&gt;
Verwende für diese Versuche den kernel8.elf:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies stellt eine Verbindung zur CPU0 her.&lt;br /&gt;
&lt;br /&gt;
Lade nun den Kernel auf den Raspberry Pi hoch:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
load kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Kernel nun startet, verwenden wir die folgenden Befehle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
set $pc = 0x80000&lt;br /&gt;
continue&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ohne Kernel-Upload ===&lt;br /&gt;
&lt;br /&gt;
Wenn bereits auf dem Raspberry PI 4 der Kernel läuft, verwende folgendes:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Debugger dann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1310</id>
		<title>Bare-Metal Debugging (JTAG, RPI 4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1310"/>
		<updated>2026-06-05T13:04:55Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Konfigurationsdatei erstellen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Bei der Entwicklung von Software wird oft ein Debugger benötigt, um Fehler zu finden und zu beheben. Ein gängiger Debugger ist GDB. Solange das Programm aus einer Umgebung wie Linux oder Windows startbar ist, kann dieser Debugger direkt verwendet werden. Bei der Bare-Metal-Entwicklung sind die Dinge jedoch etwas schwieriger, da hier die gewohnte Betriebssystemumgebung fehlt. In solchen Fällen sind Emulatoren oder spezielle Hardwarelösungen nötig.&lt;br /&gt;
&lt;br /&gt;
Da Emulatoren meist nicht zu 100 % die Hardware widerspiegeln können, verwenden wir das Hostsystem, welches direkt mit dem Raspberry Pi 4 (RPI4) kommuniziert. Das Programm wird auf dem Raspberry Pi direkt ausgeführt. Über das Hostsystem können wir jedoch den Code direkt manipulieren und sehen das Ergebnis unmittelbar auf der Hardware.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten ==&lt;br /&gt;
Das FT232H Modul wurde zwar mit Stiftleisten geliefert, aber diese mussten zunächst auf das Modul aufgelötet werden. Anschließend konnte mit Kabelbrücken die Verbindung mit dem Raspberry Pi 4 hergestellt werden.&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Windows ==&lt;br /&gt;
Unter Windows verwenden wir die MSYS2-Umgebung. Diese bietet eine Unix-ähnliche Umgebung unter Windows und ist nützlich für viele Entwicklungsaufgaben.&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Falls noch nicht geschehen, installieren wir MSYS2 nach folgender Anleitung [[Programmierumgebung erstellen (Konsole)]] und richten die Programmierumgebung ein, indem wir die folgenden Befehle ausführen:&lt;br /&gt;
&lt;br /&gt;
* Starte MSYS2 MSYS.&lt;br /&gt;
* Gib die folgenden Befehle ein, um OpenOCD zu installieren und die Umgebungsvariablen zu setzen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
pacman -S mingw64/mingw-w64-x86_64-openocd&lt;br /&gt;
echo &#039;export PATH=&amp;quot;/mingw64/bin:$PATH&amp;quot;&#039; &amp;gt;&amp;gt; ~/.bashrc&lt;br /&gt;
source ~/.bashrc&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;C:\msys64\mingw64\share\openocd\scripts\interface\ftdi&amp;quot; ab, wenn MSYS2 nach den Vorgaben installiert wurde.&lt;br /&gt;
&lt;br /&gt;
=== Treiberinstallation ===&lt;br /&gt;
OpenOCD möchte auf das Gerät über libusb zugreifen. In der Regel bietet Windows diesen Treiber nicht, sodass wir hier noch einen Treiber installieren müssen. Dazu verwenden wir &amp;quot;Zadig&amp;quot;, welches von https://zadig.akeo.ie/ heruntergeladen werden kann.&lt;br /&gt;
&lt;br /&gt;
# Installiere Zadig.&lt;br /&gt;
# Starte Zadig und aktiviere im Menü &amp;quot;Options&amp;quot; die Option &amp;quot;List All Devices&amp;quot;.&lt;br /&gt;
# Wähle &amp;quot;Single RS232-HS&amp;quot; aus der Geräteliste.&lt;br /&gt;
# Als Treiber wähle &amp;quot;libusbK&amp;quot; aus und klicke auf &amp;quot;Replace Driver&amp;quot;.&lt;br /&gt;
Damit ist Windows bereit, mit dem Modul über OpenOCD zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Linux ==&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Zunächst benötigen wir OpenOCD:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install openocd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
&lt;br /&gt;
# Setze das Layout für JTAG-Signale (TDI, TDO, TMS, TCK, nTRST, nSRST)&lt;br /&gt;
# Diese Pins sind für JTAG erforderlich&lt;br /&gt;
ftdi layout_init 0x0000 0x000b&lt;br /&gt;
ftdi layout_signal nTRST -data 0x0020 -oe 0x0020&lt;br /&gt;
ftdi layout_signal nSRST -data 0x0010 -oe 0x0010&lt;br /&gt;
ftdi layout_signal TDI -data 0x0011 -oe 0x0011&lt;br /&gt;
ftdi layout_signal TDO -data 0x0012 -oe 0x0012&lt;br /&gt;
ftdi layout_signal TMS -data 0x0013 -oe 0x0013&lt;br /&gt;
ftdi layout_signal TCK -data 0x0014 -oe 0x0014&lt;br /&gt;
&lt;br /&gt;
# Wählen Sie den JTAG-Modus aus&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;/usr/share/openocd/scripts/interface/ftdi&amp;quot; ab.&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi vorbereiten ==&lt;br /&gt;
Nachdem unser Betriebssystem auf dem Raspberry Pi eingerichtet ist, müssen wir den Raspberry Pi 4 für Bare-Metal-Programmierung vorbereiten. Dazu schreiben wir ein kleines Programm, das nur eine Endlosschleife erzeugt, aber den Raspberry Pi startet:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The boot program for RPI4&lt;br /&gt;
// 07.03.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  b _start          // endless loop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir verwenden ein Makefile, um das Programm zu kompilieren und zu linken (siehe [[Unser erstes Programm in C (PI4)#Kompilieren des Programms mit Make]]). Den erzeugten &amp;quot;kernel8.img&amp;quot; kopieren wir auf die SD-Karte. Beachte, dass auch folgende Dateien im Root-Verzeichnis der SD-Karte liegen müssen:&lt;br /&gt;
* bcm2711-rpi-4-b.dtb&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* fixup4.dat&lt;br /&gt;
* start4.elf&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir dem Raspberry Pi auch mitteilen, dass er auf JTAG reagiert. Dazu erstellen wir eine config.txt mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Datei legen wir ebenfalls in das Root-Verzeichnis der SD-Karte.&lt;br /&gt;
&lt;br /&gt;
== Starten von OpenOCD und Verwendung von GDB == &lt;br /&gt;
=== Verbindung herstellen ===&lt;br /&gt;
Verbinde zunächst das FT232H-Modul per USB mit dem Hostsystem. Auf dem Modul sollte nun eine LED leuchten, welche anzeigt, dass das Gerät betriebsbereit ist. Starte den Raspberry Pi. Wenn du ihn an einem Monitor angeschlossen hast, wird auf dem Bildschirm das Regenbogenbild angezeigt, was bedeutet, dass das System gestartet wurde.&lt;br /&gt;
&lt;br /&gt;
Starte nun &amp;quot;MSYS2 MSYS&amp;quot; oder unter Linux ein Terminal:&lt;br /&gt;
[[Datei:OpenOcd.png|mini]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
openocd -f interface/ftdi/ft232h-jtag.cfg -f target/bcm2711.cfg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn alles korrekt eingerichtet ist, sollte OpenOCD alle vier CPUs des Raspberry Pi anzeigen. Es zeigt außerdem, dass die Ports 3333, 3334, 3335 und 3336 für die GDB-Kommunikation bereitgestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== Kernel-Upload und Debugging ===&lt;br /&gt;
Um sicherzugehen, öffnen wir ein weiteres &amp;quot;MSYS2 MSYS&amp;quot;-Fenster oder zweites Terminal unter Linux. Hier wechseln wir in das Verzeichnis, welches den zu testenden Code enthält. Falls der Code noch nicht kompiliert ist, muss dies jetzt erledigt werden.&lt;br /&gt;
&lt;br /&gt;
Verwende für diese Versuche den kernel8.elf:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies stellt eine Verbindung zur CPU0 her.&lt;br /&gt;
&lt;br /&gt;
Lade nun den Kernel auf den Raspberry Pi hoch:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
load kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Kernel nun startet, verwenden wir die folgenden Befehle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
set $pc = 0x80000&lt;br /&gt;
continue&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ohne Kernel-Upload ===&lt;br /&gt;
&lt;br /&gt;
Wenn bereits auf dem Raspberry PI 4 der Kernel läuft, verwende folgendes:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Debugger dann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1309</id>
		<title>Bare-Metal Debugging (JTAG, RPI 4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1309"/>
		<updated>2026-06-05T13:00:48Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Verdrahtung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Bei der Entwicklung von Software wird oft ein Debugger benötigt, um Fehler zu finden und zu beheben. Ein gängiger Debugger ist GDB. Solange das Programm aus einer Umgebung wie Linux oder Windows startbar ist, kann dieser Debugger direkt verwendet werden. Bei der Bare-Metal-Entwicklung sind die Dinge jedoch etwas schwieriger, da hier die gewohnte Betriebssystemumgebung fehlt. In solchen Fällen sind Emulatoren oder spezielle Hardwarelösungen nötig.&lt;br /&gt;
&lt;br /&gt;
Da Emulatoren meist nicht zu 100 % die Hardware widerspiegeln können, verwenden wir das Hostsystem, welches direkt mit dem Raspberry Pi 4 (RPI4) kommuniziert. Das Programm wird auf dem Raspberry Pi direkt ausgeführt. Über das Hostsystem können wir jedoch den Code direkt manipulieren und sehen das Ergebnis unmittelbar auf der Hardware.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten ==&lt;br /&gt;
Das FT232H Modul wurde zwar mit Stiftleisten geliefert, aber diese mussten zunächst auf das Modul aufgelötet werden. Anschließend konnte mit Kabelbrücken die Verbindung mit dem Raspberry Pi 4 hergestellt werden.&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Windows ==&lt;br /&gt;
Unter Windows verwenden wir die MSYS2-Umgebung. Diese bietet eine Unix-ähnliche Umgebung unter Windows und ist nützlich für viele Entwicklungsaufgaben.&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Falls noch nicht geschehen, installieren wir MSYS2 nach folgender Anleitung [[Programmierumgebung erstellen (Konsole)]] und richten die Programmierumgebung ein, indem wir die folgenden Befehle ausführen:&lt;br /&gt;
&lt;br /&gt;
* Starte MSYS2 MSYS.&lt;br /&gt;
* Gib die folgenden Befehle ein, um OpenOCD zu installieren und die Umgebungsvariablen zu setzen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
pacman -S mingw64/mingw-w64-x86_64-openocd&lt;br /&gt;
echo &#039;export PATH=&amp;quot;/mingw64/bin:$PATH&amp;quot;&#039; &amp;gt;&amp;gt; ~/.bashrc&lt;br /&gt;
source ~/.bashrc&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
&lt;br /&gt;
# Setze das Layout für JTAG-Signale (TDI, TDO, TMS, TCK, nTRST, nSRST)&lt;br /&gt;
# Diese Pins sind für JTAG erforderlich&lt;br /&gt;
ftdi layout_init 0x0000 0x000b&lt;br /&gt;
ftdi layout_signal nTRST -data 0x0020 -oe 0x0020&lt;br /&gt;
ftdi layout_signal nSRST -data 0x0010 -oe 0x0010&lt;br /&gt;
ftdi layout_signal TDI -data 0x0011 -oe 0x0011&lt;br /&gt;
ftdi layout_signal TDO -data 0x0012 -oe 0x0012&lt;br /&gt;
ftdi layout_signal TMS -data 0x0013 -oe 0x0013&lt;br /&gt;
ftdi layout_signal TCK -data 0x0014 -oe 0x0014&lt;br /&gt;
&lt;br /&gt;
# Wählen Sie den JTAG-Modus aus&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;C:\msys64\mingw64\share\openocd\scripts\interface\ftdi&amp;quot; ab, wenn MSYS2 nach den Vorgaben installiert wurde.&lt;br /&gt;
&lt;br /&gt;
=== Treiberinstallation ===&lt;br /&gt;
OpenOCD möchte auf das Gerät über libusb zugreifen. In der Regel bietet Windows diesen Treiber nicht, sodass wir hier noch einen Treiber installieren müssen. Dazu verwenden wir &amp;quot;Zadig&amp;quot;, welches von https://zadig.akeo.ie/ heruntergeladen werden kann.&lt;br /&gt;
&lt;br /&gt;
# Installiere Zadig.&lt;br /&gt;
# Starte Zadig und aktiviere im Menü &amp;quot;Options&amp;quot; die Option &amp;quot;List All Devices&amp;quot;.&lt;br /&gt;
# Wähle &amp;quot;Single RS232-HS&amp;quot; aus der Geräteliste.&lt;br /&gt;
# Als Treiber wähle &amp;quot;libusbK&amp;quot; aus und klicke auf &amp;quot;Replace Driver&amp;quot;.&lt;br /&gt;
Damit ist Windows bereit, mit dem Modul über OpenOCD zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Linux ==&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Zunächst benötigen wir OpenOCD:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install openocd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
&lt;br /&gt;
# Setze das Layout für JTAG-Signale (TDI, TDO, TMS, TCK, nTRST, nSRST)&lt;br /&gt;
# Diese Pins sind für JTAG erforderlich&lt;br /&gt;
ftdi layout_init 0x0000 0x000b&lt;br /&gt;
ftdi layout_signal nTRST -data 0x0020 -oe 0x0020&lt;br /&gt;
ftdi layout_signal nSRST -data 0x0010 -oe 0x0010&lt;br /&gt;
ftdi layout_signal TDI -data 0x0011 -oe 0x0011&lt;br /&gt;
ftdi layout_signal TDO -data 0x0012 -oe 0x0012&lt;br /&gt;
ftdi layout_signal TMS -data 0x0013 -oe 0x0013&lt;br /&gt;
ftdi layout_signal TCK -data 0x0014 -oe 0x0014&lt;br /&gt;
&lt;br /&gt;
# Wählen Sie den JTAG-Modus aus&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;/usr/share/openocd/scripts/interface/ftdi&amp;quot; ab.&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi vorbereiten ==&lt;br /&gt;
Nachdem unser Betriebssystem auf dem Raspberry Pi eingerichtet ist, müssen wir den Raspberry Pi 4 für Bare-Metal-Programmierung vorbereiten. Dazu schreiben wir ein kleines Programm, das nur eine Endlosschleife erzeugt, aber den Raspberry Pi startet:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The boot program for RPI4&lt;br /&gt;
// 07.03.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  b _start          // endless loop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir verwenden ein Makefile, um das Programm zu kompilieren und zu linken (siehe [[Unser erstes Programm in C (PI4)#Kompilieren des Programms mit Make]]). Den erzeugten &amp;quot;kernel8.img&amp;quot; kopieren wir auf die SD-Karte. Beachte, dass auch folgende Dateien im Root-Verzeichnis der SD-Karte liegen müssen:&lt;br /&gt;
* bcm2711-rpi-4-b.dtb&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* fixup4.dat&lt;br /&gt;
* start4.elf&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir dem Raspberry Pi auch mitteilen, dass er auf JTAG reagiert. Dazu erstellen wir eine config.txt mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Datei legen wir ebenfalls in das Root-Verzeichnis der SD-Karte.&lt;br /&gt;
&lt;br /&gt;
== Starten von OpenOCD und Verwendung von GDB == &lt;br /&gt;
=== Verbindung herstellen ===&lt;br /&gt;
Verbinde zunächst das FT232H-Modul per USB mit dem Hostsystem. Auf dem Modul sollte nun eine LED leuchten, welche anzeigt, dass das Gerät betriebsbereit ist. Starte den Raspberry Pi. Wenn du ihn an einem Monitor angeschlossen hast, wird auf dem Bildschirm das Regenbogenbild angezeigt, was bedeutet, dass das System gestartet wurde.&lt;br /&gt;
&lt;br /&gt;
Starte nun &amp;quot;MSYS2 MSYS&amp;quot; oder unter Linux ein Terminal:&lt;br /&gt;
[[Datei:OpenOcd.png|mini]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
openocd -f interface/ftdi/ft232h-jtag.cfg -f target/bcm2711.cfg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn alles korrekt eingerichtet ist, sollte OpenOCD alle vier CPUs des Raspberry Pi anzeigen. Es zeigt außerdem, dass die Ports 3333, 3334, 3335 und 3336 für die GDB-Kommunikation bereitgestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== Kernel-Upload und Debugging ===&lt;br /&gt;
Um sicherzugehen, öffnen wir ein weiteres &amp;quot;MSYS2 MSYS&amp;quot;-Fenster oder zweites Terminal unter Linux. Hier wechseln wir in das Verzeichnis, welches den zu testenden Code enthält. Falls der Code noch nicht kompiliert ist, muss dies jetzt erledigt werden.&lt;br /&gt;
&lt;br /&gt;
Verwende für diese Versuche den kernel8.elf:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies stellt eine Verbindung zur CPU0 her.&lt;br /&gt;
&lt;br /&gt;
Lade nun den Kernel auf den Raspberry Pi hoch:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
load kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Kernel nun startet, verwenden wir die folgenden Befehle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
set $pc = 0x80000&lt;br /&gt;
continue&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ohne Kernel-Upload ===&lt;br /&gt;
&lt;br /&gt;
Wenn bereits auf dem Raspberry PI 4 der Kernel läuft, verwende folgendes:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Debugger dann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1308</id>
		<title>Bare-Metal Debugging (JTAG, RPI 4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1308"/>
		<updated>2026-06-05T12:58:25Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Bei der Entwicklung von Software wird oft ein Debugger benötigt, um Fehler zu finden und zu beheben. Ein gängiger Debugger ist GDB. Solange das Programm aus einer Umgebung wie Linux oder Windows startbar ist, kann dieser Debugger direkt verwendet werden. Bei der Bare-Metal-Entwicklung sind die Dinge jedoch etwas schwieriger, da hier die gewohnte Betriebssystemumgebung fehlt. In solchen Fällen sind Emulatoren oder spezielle Hardwarelösungen nötig.&lt;br /&gt;
&lt;br /&gt;
Da Emulatoren meist nicht zu 100 % die Hardware widerspiegeln können, verwenden wir das Hostsystem, welches direkt mit dem Raspberry Pi 4 (RPI4) kommuniziert. Das Programm wird auf dem Raspberry Pi direkt ausgeführt. Über das Hostsystem können wir jedoch den Code direkt manipulieren und sehen das Ergebnis unmittelbar auf der Hardware.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten ==&lt;br /&gt;
Das FT232H Modul wurde zwar mit Stiftleisten geliefert, aber diese mussten zunächst auf das Modul aufgelötet werden. Anschließend konnte mit Kabelbrücken die Verbindung mit dem Raspberry Pi 4 hergestellt werden.&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO27 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO27 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Windows ==&lt;br /&gt;
Unter Windows verwenden wir die MSYS2-Umgebung. Diese bietet eine Unix-ähnliche Umgebung unter Windows und ist nützlich für viele Entwicklungsaufgaben.&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Falls noch nicht geschehen, installieren wir MSYS2 nach folgender Anleitung [[Programmierumgebung erstellen (Konsole)]] und richten die Programmierumgebung ein, indem wir die folgenden Befehle ausführen:&lt;br /&gt;
&lt;br /&gt;
* Starte MSYS2 MSYS.&lt;br /&gt;
* Gib die folgenden Befehle ein, um OpenOCD zu installieren und die Umgebungsvariablen zu setzen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
pacman -S mingw64/mingw-w64-x86_64-openocd&lt;br /&gt;
echo &#039;export PATH=&amp;quot;/mingw64/bin:$PATH&amp;quot;&#039; &amp;gt;&amp;gt; ~/.bashrc&lt;br /&gt;
source ~/.bashrc&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
&lt;br /&gt;
# Setze das Layout für JTAG-Signale (TDI, TDO, TMS, TCK, nTRST, nSRST)&lt;br /&gt;
# Diese Pins sind für JTAG erforderlich&lt;br /&gt;
ftdi layout_init 0x0000 0x000b&lt;br /&gt;
ftdi layout_signal nTRST -data 0x0020 -oe 0x0020&lt;br /&gt;
ftdi layout_signal nSRST -data 0x0010 -oe 0x0010&lt;br /&gt;
ftdi layout_signal TDI -data 0x0011 -oe 0x0011&lt;br /&gt;
ftdi layout_signal TDO -data 0x0012 -oe 0x0012&lt;br /&gt;
ftdi layout_signal TMS -data 0x0013 -oe 0x0013&lt;br /&gt;
ftdi layout_signal TCK -data 0x0014 -oe 0x0014&lt;br /&gt;
&lt;br /&gt;
# Wählen Sie den JTAG-Modus aus&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;C:\msys64\mingw64\share\openocd\scripts\interface\ftdi&amp;quot; ab, wenn MSYS2 nach den Vorgaben installiert wurde.&lt;br /&gt;
&lt;br /&gt;
=== Treiberinstallation ===&lt;br /&gt;
OpenOCD möchte auf das Gerät über libusb zugreifen. In der Regel bietet Windows diesen Treiber nicht, sodass wir hier noch einen Treiber installieren müssen. Dazu verwenden wir &amp;quot;Zadig&amp;quot;, welches von https://zadig.akeo.ie/ heruntergeladen werden kann.&lt;br /&gt;
&lt;br /&gt;
# Installiere Zadig.&lt;br /&gt;
# Starte Zadig und aktiviere im Menü &amp;quot;Options&amp;quot; die Option &amp;quot;List All Devices&amp;quot;.&lt;br /&gt;
# Wähle &amp;quot;Single RS232-HS&amp;quot; aus der Geräteliste.&lt;br /&gt;
# Als Treiber wähle &amp;quot;libusbK&amp;quot; aus und klicke auf &amp;quot;Replace Driver&amp;quot;.&lt;br /&gt;
Damit ist Windows bereit, mit dem Modul über OpenOCD zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Linux ==&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Zunächst benötigen wir OpenOCD:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install openocd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
&lt;br /&gt;
# Setze das Layout für JTAG-Signale (TDI, TDO, TMS, TCK, nTRST, nSRST)&lt;br /&gt;
# Diese Pins sind für JTAG erforderlich&lt;br /&gt;
ftdi layout_init 0x0000 0x000b&lt;br /&gt;
ftdi layout_signal nTRST -data 0x0020 -oe 0x0020&lt;br /&gt;
ftdi layout_signal nSRST -data 0x0010 -oe 0x0010&lt;br /&gt;
ftdi layout_signal TDI -data 0x0011 -oe 0x0011&lt;br /&gt;
ftdi layout_signal TDO -data 0x0012 -oe 0x0012&lt;br /&gt;
ftdi layout_signal TMS -data 0x0013 -oe 0x0013&lt;br /&gt;
ftdi layout_signal TCK -data 0x0014 -oe 0x0014&lt;br /&gt;
&lt;br /&gt;
# Wählen Sie den JTAG-Modus aus&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;/usr/share/openocd/scripts/interface/ftdi&amp;quot; ab.&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi vorbereiten ==&lt;br /&gt;
Nachdem unser Betriebssystem auf dem Raspberry Pi eingerichtet ist, müssen wir den Raspberry Pi 4 für Bare-Metal-Programmierung vorbereiten. Dazu schreiben wir ein kleines Programm, das nur eine Endlosschleife erzeugt, aber den Raspberry Pi startet:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The boot program for RPI4&lt;br /&gt;
// 07.03.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  b _start          // endless loop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir verwenden ein Makefile, um das Programm zu kompilieren und zu linken (siehe [[Unser erstes Programm in C (PI4)#Kompilieren des Programms mit Make]]). Den erzeugten &amp;quot;kernel8.img&amp;quot; kopieren wir auf die SD-Karte. Beachte, dass auch folgende Dateien im Root-Verzeichnis der SD-Karte liegen müssen:&lt;br /&gt;
* bcm2711-rpi-4-b.dtb&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* fixup4.dat&lt;br /&gt;
* start4.elf&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir dem Raspberry Pi auch mitteilen, dass er auf JTAG reagiert. Dazu erstellen wir eine config.txt mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Datei legen wir ebenfalls in das Root-Verzeichnis der SD-Karte.&lt;br /&gt;
&lt;br /&gt;
== Starten von OpenOCD und Verwendung von GDB == &lt;br /&gt;
=== Verbindung herstellen ===&lt;br /&gt;
Verbinde zunächst das FT232H-Modul per USB mit dem Hostsystem. Auf dem Modul sollte nun eine LED leuchten, welche anzeigt, dass das Gerät betriebsbereit ist. Starte den Raspberry Pi. Wenn du ihn an einem Monitor angeschlossen hast, wird auf dem Bildschirm das Regenbogenbild angezeigt, was bedeutet, dass das System gestartet wurde.&lt;br /&gt;
&lt;br /&gt;
Starte nun &amp;quot;MSYS2 MSYS&amp;quot; oder unter Linux ein Terminal:&lt;br /&gt;
[[Datei:OpenOcd.png|mini]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
openocd -f interface/ftdi/ft232h-jtag.cfg -f target/bcm2711.cfg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn alles korrekt eingerichtet ist, sollte OpenOCD alle vier CPUs des Raspberry Pi anzeigen. Es zeigt außerdem, dass die Ports 3333, 3334, 3335 und 3336 für die GDB-Kommunikation bereitgestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== Kernel-Upload und Debugging ===&lt;br /&gt;
Um sicherzugehen, öffnen wir ein weiteres &amp;quot;MSYS2 MSYS&amp;quot;-Fenster oder zweites Terminal unter Linux. Hier wechseln wir in das Verzeichnis, welches den zu testenden Code enthält. Falls der Code noch nicht kompiliert ist, muss dies jetzt erledigt werden.&lt;br /&gt;
&lt;br /&gt;
Verwende für diese Versuche den kernel8.elf:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies stellt eine Verbindung zur CPU0 her.&lt;br /&gt;
&lt;br /&gt;
Lade nun den Kernel auf den Raspberry Pi hoch:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
load kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Kernel nun startet, verwenden wir die folgenden Befehle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
set $pc = 0x80000&lt;br /&gt;
continue&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ohne Kernel-Upload ===&lt;br /&gt;
&lt;br /&gt;
Wenn bereits auf dem Raspberry PI 4 der Kernel läuft, verwende folgendes:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Debugger dann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1307</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1307"/>
		<updated>2026-06-05T10:53:39Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Hardware vorbereiten und SD-Karte einrichten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Um direkt mit dem Raspberry Pi 5 zu kommunizieren und Programme zu debuggen, wird zusätzlich eine &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039; benötigt. Diese wird über USB mit einem Windows-Rechner und über die Debug-Pins mit dem Raspberry Pi 5 verbunden.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und der Debug Probe verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Überprüfen Sie anschließend, ob die Datei &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; existiert. Diese wird in der Regel standardmäßig mitgeliefert. Sollte sie fehlen, erstellen Sie diese Datei neu mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Da in älteren oder Standard-OpenOCD-Releases ein passendes Target-Profil für den neuen Broadcom-Chip BCM2712 des Raspberry Pi 5 fehlt, müssen wir dieses manuell anlegen. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP für direkten Zugriff&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# Diese Adressen wurden aus der ROM-Table via &#039;dap info 0&#039;-Befehl ausgelesen&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Standard-Target ist cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Überschreibt den standardmäßigen &amp;quot;reset halt&amp;quot; Befehl von VS-Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Befehle, die direkt nach dem Verbinden ausgeführt werden&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Zwingt den Prozessor direkt auf die Startadresse&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI5)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Bit 9 auf 0 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Bit 9 auf 1 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO-Definitionen für den Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; setzt die Startadresse im RAM fest, an die unser Kernel geladen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 5 in den JTAG-/SWD-Modus um. Dadurch wird die Kommunikation mit der Raspberry Pi Debug Probe überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 5.&lt;br /&gt;
* Verbinden Sie die Raspberry Pi Debug Probe über das mitgelieferte 3-Pol-UART/Debug-Kabel mit dem dedizierten Debug-Port des Raspberry Pi 5 (befindet sich zwischen den Micro-HDMI-Ports).&lt;br /&gt;
* Schließen Sie die Debug Probe per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 5 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 5 über die Debug Probe auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 5 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1306</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1306"/>
		<updated>2026-06-05T10:30:47Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Hardware vorbereiten und SD-Karte einrichten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Um direkt mit dem Raspberry Pi 5 zu kommunizieren und Programme zu debuggen, wird zusätzlich eine &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039; benötigt. Diese wird über USB mit einem Windows-Rechner und über die Debug-Pins mit dem Raspberry Pi 5 verbunden.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und der Debug Probe verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Überprüfen Sie anschließend, ob die Datei &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; existiert. Diese wird in der Regel standardmäßig mitgeliefert. Sollte sie fehlen, erstellen Sie diese Datei neu mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Da in älteren oder Standard-OpenOCD-Releases ein passendes Target-Profil für den neuen Broadcom-Chip BCM2712 des Raspberry Pi 5 fehlt, müssen wir dieses manuell anlegen. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP für direkten Zugriff&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# Diese Adressen wurden aus der ROM-Table via &#039;dap info 0&#039;-Befehl ausgelesen&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Standard-Target ist cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Überschreibt den standardmäßigen &amp;quot;reset halt&amp;quot; Befehl von VS-Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Befehle, die direkt nach dem Verbinden ausgeführt werden&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Zwingt den Prozessor direkt auf die Startadresse&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI5)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Bit 9 auf 0 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Bit 9 auf 1 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO-Definitionen für den Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; setzt die Startadresse im RAM fest, an die unser Kernel geladen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 5 in den JTAG-/SWD-Modus um. Dadurch wird die Kommunikation mit der Raspberry Pi Debug Probe überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 5.&lt;br /&gt;
* Verbinden Sie die Raspberry Pi Debug Probe über das mitgelieferte 3-Pol-UART/Debug-Kabel mit dem dedizierten Debug-Port des Raspberry Pi 5 (befindet sich zwischen den Micro-HDMI-Ports).&lt;br /&gt;
* Schließen Sie die Debug Probe per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 5 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 5 über die Debug Probe auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 5 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1305</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1305"/>
		<updated>2026-06-05T10:29:30Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Hardware vorbereiten und SD-Karte einrichten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Um direkt mit dem Raspberry Pi 5 zu kommunizieren und Programme zu debuggen, wird zusätzlich eine &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039; benötigt. Diese wird über USB mit einem Windows-Rechner und über die Debug-Pins mit dem Raspberry Pi 5 verbunden.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und der Debug Probe verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Überprüfen Sie anschließend, ob die Datei &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; existiert. Diese wird in der Regel standardmäßig mitgeliefert. Sollte sie fehlen, erstellen Sie diese Datei neu mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Da in älteren oder Standard-OpenOCD-Releases ein passendes Target-Profil für den neuen Broadcom-Chip BCM2712 des Raspberry Pi 5 fehlt, müssen wir dieses manuell anlegen. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP für direkten Zugriff&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# Diese Adressen wurden aus der ROM-Table via &#039;dap info 0&#039;-Befehl ausgelesen&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Standard-Target ist cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Überschreibt den standardmäßigen &amp;quot;reset halt&amp;quot; Befehl von VS-Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Befehle, die direkt nach dem Verbinden ausgeführt werden&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Zwingt den Prozessor direkt auf die Startadresse&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI5)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Bit 9 auf 0 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Bit 9 auf 1 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO-Definitionen für den Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; setzt die Startadresse im RAM fest, an die unser Kernel geladen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 5 in den JTAG-/SWD-Modus um. Dadurch wird die Kommunikation mit der Raspberry Pi Debug Probe überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen:  &lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 5.&lt;br /&gt;
* Verbinden Sie die Raspberry Pi Debug Probe über das mitgelieferte 3-Pol-UART/Debug-Kabel mit dem dedizierten Debug-Port des Raspberry Pi 5 (befindet sich zwischen den Micro-HDMI-Ports).&lt;br /&gt;
* Schließen Sie die Debug Probe per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 5 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 5 über die Debug Probe auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 5 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1304</id>
		<title>Unser erstes Programm in C (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1304"/>
		<updated>2026-06-05T10:25:36Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Kompilieren und Ausführen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
In diesem Kurs werden wir die Grundlagen der Programmierung des Raspberry Pi 4 erlernen. Ähnlich wie bei der Programmierung des Raspberry Pi 5, gibt es einige Unterschiede, hauptsächlich bedingt durch die Unterstützung von USB und die umfangreichere Dokumentation für den Raspberry Pi 4. Für diesen Kurs habe ich Material vom Raspberry Pi 5 angepasst, um die Programmierung des Raspberry Pi 4 zu erläutern.&lt;br /&gt;
&lt;br /&gt;
Ein Terminal wurde programmiert, das sich sehr gut zum Debuggen eignet. Dies werden wir in unserem Kurs ebenfalls verwenden.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
== Ziel unseres ersten Programms ==&lt;br /&gt;
Unser erstes Programm wird eine einfache Endlosschleife enthalten. Dies dient als Basis für weitere Experimente und Versuche. Ich werde erklären, wie ein solches Programm erstellt, kompiliert und ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
== Erstellung des Sourcecodes ==&lt;br /&gt;
Leider kommen wir bei Bare-Metal-Programmierung in C nicht um Assembler herum. Auch wenn das Assemblerprogramm zunächst nicht viel tut, wird es später beispielsweise für die Interrupt-Programmierung benötigt. Erstelle zunächst das Verzeichnis &amp;quot;src&amp;quot;, in der wir unseren Sourcecode ablegen werden.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Code (boot.S) ===&lt;br /&gt;
&lt;br /&gt;
Öffnen Sie ein Textprogramm und schreiben Sie folgendes erstes Assemblerprogramm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  mov sp, #0x80000  // Create a stack of 512KB (524288 bytes)&lt;br /&gt;
  b main            // Branch to &amp;quot;main&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie die Datei im Standardverzeichnis unter Windows: C:\msys64\home\xxx (wobei xxx in der Regel Ihr Benutzername ist). Unter Linux können Sie die Datei im Home-Verzeichnis speichern. Geben Sie der Datei den Namen boot.S. Die Endung .S kennzeichnet die Datei als Assembler-Sourcecode.&lt;br /&gt;
&lt;br /&gt;
=== C-Code (main.c) ===&lt;br /&gt;
&lt;br /&gt;
Nun erstellen wir eine main.c Datei für unser erstes Programm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// main.c&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
  while (1){}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier wird eine einfache Endlosschleife erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren des Programms mit Make ==&lt;br /&gt;
Um unser Programm zu kompilieren, muss zunächst die Programmierumgebung eingerichtet werden. Verwenden Sie hierzu die entsprechenden Anleitungen. Diese funktionieren auch für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie dann ein Makefile, welches im Root-Verzeichnis des Projektes abgelegt wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen Makefile.&lt;br /&gt;
&lt;br /&gt;
Wenn Sie mehr über den Inhalt erfahren möchten, schauen Sie unter &amp;quot;[[Arbeiten mit Make und Linker-Script]]&amp;quot; nach.&lt;br /&gt;
&lt;br /&gt;
== Linker-Script (linker.ld) ==&lt;br /&gt;
Erstellen Sie das folgende Linker-Script, welches wir unter [[Arbeiten mit Make und Linker-Script]] beschrieben haben. Dieses wird, wie das Makefile in das Root-Verzeichnis abgelegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data*)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss*)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen linker.ld.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren und Ausführen ==&lt;br /&gt;
Bevor wir kompilieren, prüfe deine Verzeichnisstruktur:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
C:.&lt;br /&gt;
|   linker.ld&lt;br /&gt;
|   makefile&lt;br /&gt;
|       &lt;br /&gt;
\---src&lt;br /&gt;
        boot.S&lt;br /&gt;
        main.c &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Nun können wir unser erstes Programm kompilieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit unser Kernel funktionsfähig ist, muss der Kernel auf eine SD-Karte, die in FAT32 formatiert ist, kopiert werden. Zusätzlich benötigt der Raspberry Pi 4 folgende Dateien auf der SD-Karte:&lt;br /&gt;
&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* start.elf&lt;br /&gt;
* config.txt&lt;br /&gt;
* kernel8.img (Ihr kompiliertes Programm)&lt;br /&gt;
&lt;br /&gt;
Den Source-Code können Sie als ZIP-Datei [https://www.satyria.de/arm/sources/RPI4/C/1.zip hier] herunterladen.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | &lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Lass die LED leuchten in C (PI4)|Weiter (Lass die LED leuchten in C (PI4)) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1303</id>
		<title>Unser erstes Programm in C (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1303"/>
		<updated>2026-06-05T10:21:01Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Linker-Script (linker.ld) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
In diesem Kurs werden wir die Grundlagen der Programmierung des Raspberry Pi 4 erlernen. Ähnlich wie bei der Programmierung des Raspberry Pi 5, gibt es einige Unterschiede, hauptsächlich bedingt durch die Unterstützung von USB und die umfangreichere Dokumentation für den Raspberry Pi 4. Für diesen Kurs habe ich Material vom Raspberry Pi 5 angepasst, um die Programmierung des Raspberry Pi 4 zu erläutern.&lt;br /&gt;
&lt;br /&gt;
Ein Terminal wurde programmiert, das sich sehr gut zum Debuggen eignet. Dies werden wir in unserem Kurs ebenfalls verwenden.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
== Ziel unseres ersten Programms ==&lt;br /&gt;
Unser erstes Programm wird eine einfache Endlosschleife enthalten. Dies dient als Basis für weitere Experimente und Versuche. Ich werde erklären, wie ein solches Programm erstellt, kompiliert und ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
== Erstellung des Sourcecodes ==&lt;br /&gt;
Leider kommen wir bei Bare-Metal-Programmierung in C nicht um Assembler herum. Auch wenn das Assemblerprogramm zunächst nicht viel tut, wird es später beispielsweise für die Interrupt-Programmierung benötigt. Erstelle zunächst das Verzeichnis &amp;quot;src&amp;quot;, in der wir unseren Sourcecode ablegen werden.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Code (boot.S) ===&lt;br /&gt;
&lt;br /&gt;
Öffnen Sie ein Textprogramm und schreiben Sie folgendes erstes Assemblerprogramm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  mov sp, #0x80000  // Create a stack of 512KB (524288 bytes)&lt;br /&gt;
  b main            // Branch to &amp;quot;main&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie die Datei im Standardverzeichnis unter Windows: C:\msys64\home\xxx (wobei xxx in der Regel Ihr Benutzername ist). Unter Linux können Sie die Datei im Home-Verzeichnis speichern. Geben Sie der Datei den Namen boot.S. Die Endung .S kennzeichnet die Datei als Assembler-Sourcecode.&lt;br /&gt;
&lt;br /&gt;
=== C-Code (main.c) ===&lt;br /&gt;
&lt;br /&gt;
Nun erstellen wir eine main.c Datei für unser erstes Programm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// main.c&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
  while (1){}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier wird eine einfache Endlosschleife erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren des Programms mit Make ==&lt;br /&gt;
Um unser Programm zu kompilieren, muss zunächst die Programmierumgebung eingerichtet werden. Verwenden Sie hierzu die entsprechenden Anleitungen. Diese funktionieren auch für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie dann ein Makefile, welches im Root-Verzeichnis des Projektes abgelegt wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen Makefile.&lt;br /&gt;
&lt;br /&gt;
Wenn Sie mehr über den Inhalt erfahren möchten, schauen Sie unter &amp;quot;[[Arbeiten mit Make und Linker-Script]]&amp;quot; nach.&lt;br /&gt;
&lt;br /&gt;
== Linker-Script (linker.ld) ==&lt;br /&gt;
Erstellen Sie das folgende Linker-Script, welches wir unter [[Arbeiten mit Make und Linker-Script]] beschrieben haben. Dieses wird, wie das Makefile in das Root-Verzeichnis abgelegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data*)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss*)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen linker.ld.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren und Ausführen ==&lt;br /&gt;
Nun können wir unser erstes Programm kompilieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit unser Kernel funktionsfähig ist, muss der Kernel auf eine SD-Karte, die in FAT32 formatiert ist, kopiert werden. Zusätzlich benötigt der Raspberry Pi 4 folgende Dateien auf der SD-Karte:&lt;br /&gt;
&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* start.elf&lt;br /&gt;
* config.txt&lt;br /&gt;
* kernel8.img (Ihr kompiliertes Programm)&lt;br /&gt;
&lt;br /&gt;
Den Source-Code können Sie als ZIP-Datei [https://www.satyria.de/arm/sources/RPI4/C/1.zip hier] herunterladen.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | &lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Lass die LED leuchten in C (PI4)|Weiter (Lass die LED leuchten in C (PI4)) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1302</id>
		<title>Unser erstes Programm in C (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1302"/>
		<updated>2026-06-05T10:19:55Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Kompilieren des Programms mit Make */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
In diesem Kurs werden wir die Grundlagen der Programmierung des Raspberry Pi 4 erlernen. Ähnlich wie bei der Programmierung des Raspberry Pi 5, gibt es einige Unterschiede, hauptsächlich bedingt durch die Unterstützung von USB und die umfangreichere Dokumentation für den Raspberry Pi 4. Für diesen Kurs habe ich Material vom Raspberry Pi 5 angepasst, um die Programmierung des Raspberry Pi 4 zu erläutern.&lt;br /&gt;
&lt;br /&gt;
Ein Terminal wurde programmiert, das sich sehr gut zum Debuggen eignet. Dies werden wir in unserem Kurs ebenfalls verwenden.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
== Ziel unseres ersten Programms ==&lt;br /&gt;
Unser erstes Programm wird eine einfache Endlosschleife enthalten. Dies dient als Basis für weitere Experimente und Versuche. Ich werde erklären, wie ein solches Programm erstellt, kompiliert und ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
== Erstellung des Sourcecodes ==&lt;br /&gt;
Leider kommen wir bei Bare-Metal-Programmierung in C nicht um Assembler herum. Auch wenn das Assemblerprogramm zunächst nicht viel tut, wird es später beispielsweise für die Interrupt-Programmierung benötigt. Erstelle zunächst das Verzeichnis &amp;quot;src&amp;quot;, in der wir unseren Sourcecode ablegen werden.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Code (boot.S) ===&lt;br /&gt;
&lt;br /&gt;
Öffnen Sie ein Textprogramm und schreiben Sie folgendes erstes Assemblerprogramm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  mov sp, #0x80000  // Create a stack of 512KB (524288 bytes)&lt;br /&gt;
  b main            // Branch to &amp;quot;main&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie die Datei im Standardverzeichnis unter Windows: C:\msys64\home\xxx (wobei xxx in der Regel Ihr Benutzername ist). Unter Linux können Sie die Datei im Home-Verzeichnis speichern. Geben Sie der Datei den Namen boot.S. Die Endung .S kennzeichnet die Datei als Assembler-Sourcecode.&lt;br /&gt;
&lt;br /&gt;
=== C-Code (main.c) ===&lt;br /&gt;
&lt;br /&gt;
Nun erstellen wir eine main.c Datei für unser erstes Programm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// main.c&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
  while (1){}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier wird eine einfache Endlosschleife erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren des Programms mit Make ==&lt;br /&gt;
Um unser Programm zu kompilieren, muss zunächst die Programmierumgebung eingerichtet werden. Verwenden Sie hierzu die entsprechenden Anleitungen. Diese funktionieren auch für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie dann ein Makefile, welches im Root-Verzeichnis des Projektes abgelegt wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen Makefile.&lt;br /&gt;
&lt;br /&gt;
Wenn Sie mehr über den Inhalt erfahren möchten, schauen Sie unter &amp;quot;[[Arbeiten mit Make und Linker-Script]]&amp;quot; nach.&lt;br /&gt;
&lt;br /&gt;
== Linker-Script (linker.ld) ==&lt;br /&gt;
Erstellen Sie das folgende Linker-Script, welches wir unter [[Arbeiten mit Make und Linker-Script]] beschrieben haben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data*)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss*)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen linker.ld.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren und Ausführen ==&lt;br /&gt;
Nun können wir unser erstes Programm kompilieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit unser Kernel funktionsfähig ist, muss der Kernel auf eine SD-Karte, die in FAT32 formatiert ist, kopiert werden. Zusätzlich benötigt der Raspberry Pi 4 folgende Dateien auf der SD-Karte:&lt;br /&gt;
&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* start.elf&lt;br /&gt;
* config.txt&lt;br /&gt;
* kernel8.img (Ihr kompiliertes Programm)&lt;br /&gt;
&lt;br /&gt;
Den Source-Code können Sie als ZIP-Datei [https://www.satyria.de/arm/sources/RPI4/C/1.zip hier] herunterladen.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | &lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Lass die LED leuchten in C (PI4)|Weiter (Lass die LED leuchten in C (PI4)) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1301</id>
		<title>Unser erstes Programm in C (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1301"/>
		<updated>2026-06-05T10:18:34Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Erstellung des Sourcecodes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
In diesem Kurs werden wir die Grundlagen der Programmierung des Raspberry Pi 4 erlernen. Ähnlich wie bei der Programmierung des Raspberry Pi 5, gibt es einige Unterschiede, hauptsächlich bedingt durch die Unterstützung von USB und die umfangreichere Dokumentation für den Raspberry Pi 4. Für diesen Kurs habe ich Material vom Raspberry Pi 5 angepasst, um die Programmierung des Raspberry Pi 4 zu erläutern.&lt;br /&gt;
&lt;br /&gt;
Ein Terminal wurde programmiert, das sich sehr gut zum Debuggen eignet. Dies werden wir in unserem Kurs ebenfalls verwenden.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
== Ziel unseres ersten Programms ==&lt;br /&gt;
Unser erstes Programm wird eine einfache Endlosschleife enthalten. Dies dient als Basis für weitere Experimente und Versuche. Ich werde erklären, wie ein solches Programm erstellt, kompiliert und ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
== Erstellung des Sourcecodes ==&lt;br /&gt;
Leider kommen wir bei Bare-Metal-Programmierung in C nicht um Assembler herum. Auch wenn das Assemblerprogramm zunächst nicht viel tut, wird es später beispielsweise für die Interrupt-Programmierung benötigt. Erstelle zunächst das Verzeichnis &amp;quot;src&amp;quot;, in der wir unseren Sourcecode ablegen werden.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Code (boot.S) ===&lt;br /&gt;
&lt;br /&gt;
Öffnen Sie ein Textprogramm und schreiben Sie folgendes erstes Assemblerprogramm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  mov sp, #0x80000  // Create a stack of 512KB (524288 bytes)&lt;br /&gt;
  b main            // Branch to &amp;quot;main&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie die Datei im Standardverzeichnis unter Windows: C:\msys64\home\xxx (wobei xxx in der Regel Ihr Benutzername ist). Unter Linux können Sie die Datei im Home-Verzeichnis speichern. Geben Sie der Datei den Namen boot.S. Die Endung .S kennzeichnet die Datei als Assembler-Sourcecode.&lt;br /&gt;
&lt;br /&gt;
=== C-Code (main.c) ===&lt;br /&gt;
&lt;br /&gt;
Nun erstellen wir eine main.c Datei für unser erstes Programm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// main.c&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
  while (1){}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier wird eine einfache Endlosschleife erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren des Programms mit Make ==&lt;br /&gt;
Um unser Programm zu kompilieren, muss zunächst die Programmierumgebung eingerichtet werden. Verwenden Sie hierzu die entsprechenden Anleitungen. Diese funktionieren auch für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie dann ein Makefile, angepasst für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen Makefile.&lt;br /&gt;
&lt;br /&gt;
Wenn Sie mehr über den Inhalt erfahren möchten, schauen Sie unter &amp;quot;[[Arbeiten mit Make und Linker-Script]]&amp;quot; nach.&lt;br /&gt;
&lt;br /&gt;
== Linker-Script (linker.ld) ==&lt;br /&gt;
Erstellen Sie das folgende Linker-Script, welches wir unter [[Arbeiten mit Make und Linker-Script]] beschrieben haben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data*)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss*)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen linker.ld.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren und Ausführen ==&lt;br /&gt;
Nun können wir unser erstes Programm kompilieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit unser Kernel funktionsfähig ist, muss der Kernel auf eine SD-Karte, die in FAT32 formatiert ist, kopiert werden. Zusätzlich benötigt der Raspberry Pi 4 folgende Dateien auf der SD-Karte:&lt;br /&gt;
&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* start.elf&lt;br /&gt;
* config.txt&lt;br /&gt;
* kernel8.img (Ihr kompiliertes Programm)&lt;br /&gt;
&lt;br /&gt;
Den Source-Code können Sie als ZIP-Datei [https://www.satyria.de/arm/sources/RPI4/C/1.zip hier] herunterladen.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | &lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Lass die LED leuchten in C (PI4)|Weiter (Lass die LED leuchten in C (PI4)) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1300</id>
		<title>Unser erstes Programm in C (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1300"/>
		<updated>2026-06-05T10:17:03Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Kompilieren des Programms mit Make */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
In diesem Kurs werden wir die Grundlagen der Programmierung des Raspberry Pi 4 erlernen. Ähnlich wie bei der Programmierung des Raspberry Pi 5, gibt es einige Unterschiede, hauptsächlich bedingt durch die Unterstützung von USB und die umfangreichere Dokumentation für den Raspberry Pi 4. Für diesen Kurs habe ich Material vom Raspberry Pi 5 angepasst, um die Programmierung des Raspberry Pi 4 zu erläutern.&lt;br /&gt;
&lt;br /&gt;
Ein Terminal wurde programmiert, das sich sehr gut zum Debuggen eignet. Dies werden wir in unserem Kurs ebenfalls verwenden.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
== Ziel unseres ersten Programms ==&lt;br /&gt;
Unser erstes Programm wird eine einfache Endlosschleife enthalten. Dies dient als Basis für weitere Experimente und Versuche. Ich werde erklären, wie ein solches Programm erstellt, kompiliert und ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
== Erstellung des Sourcecodes ==&lt;br /&gt;
Leider kommen wir bei Bare-Metal-Programmierung in C nicht um Assembler herum. Auch wenn das Assemblerprogramm zunächst nicht viel tut, wird es später beispielsweise für die Interrupt-Programmierung benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Code (boot.S) ===&lt;br /&gt;
&lt;br /&gt;
Öffnen Sie ein Textprogramm und schreiben Sie folgendes erstes Assemblerprogramm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;GAS&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  mov sp, #0x80000  // Create a stack of 512KB (524288 bytes)&lt;br /&gt;
  b main            // Branch to &amp;quot;main&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie die Datei im Standardverzeichnis unter Windows: C:\msys64\home\xxx (wobei xxx in der Regel Ihr Benutzername ist). Unter Linux können Sie die Datei im Home-Verzeichnis speichern. Geben Sie der Datei den Namen boot.S. Die Endung .S kennzeichnet die Datei als Assembler-Sourcecode.&lt;br /&gt;
&lt;br /&gt;
=== C-Code (main.c) ===&lt;br /&gt;
&lt;br /&gt;
Nun erstellen wir eine main.c Datei für unser erstes Programm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// main.c&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
  while (1){}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier wird eine einfache Endlosschleife erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren des Programms mit Make ==&lt;br /&gt;
Um unser Programm zu kompilieren, muss zunächst die Programmierumgebung eingerichtet werden. Verwenden Sie hierzu die entsprechenden Anleitungen. Diese funktionieren auch für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie dann ein Makefile, angepasst für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen Makefile.&lt;br /&gt;
&lt;br /&gt;
Wenn Sie mehr über den Inhalt erfahren möchten, schauen Sie unter &amp;quot;[[Arbeiten mit Make und Linker-Script]]&amp;quot; nach.&lt;br /&gt;
&lt;br /&gt;
== Linker-Script (linker.ld) ==&lt;br /&gt;
Erstellen Sie das folgende Linker-Script, welches wir unter [[Arbeiten mit Make und Linker-Script]] beschrieben haben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data*)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss*)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen linker.ld.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren und Ausführen ==&lt;br /&gt;
Nun können wir unser erstes Programm kompilieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit unser Kernel funktionsfähig ist, muss der Kernel auf eine SD-Karte, die in FAT32 formatiert ist, kopiert werden. Zusätzlich benötigt der Raspberry Pi 4 folgende Dateien auf der SD-Karte:&lt;br /&gt;
&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* start.elf&lt;br /&gt;
* config.txt&lt;br /&gt;
* kernel8.img (Ihr kompiliertes Programm)&lt;br /&gt;
&lt;br /&gt;
Den Source-Code können Sie als ZIP-Datei [https://www.satyria.de/arm/sources/RPI4/C/1.zip hier] herunterladen.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | &lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Lass die LED leuchten in C (PI4)|Weiter (Lass die LED leuchten in C (PI4)) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1299</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1299"/>
		<updated>2026-06-03T16:33:55Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Preparing the Hardware and Setting Up the SD Card */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Creating the Linker Script and Makefile ===&lt;br /&gt;
&lt;br /&gt;
In order to build an executable bare-metal image for the Raspberry Pi 5 from the source code, we need a &#039;&#039;linker script&#039;&#039; and the control file for &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Both files are created directly in the main directory (root) of your project.&lt;br /&gt;
&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
This script defines the exact layout of the code segments in the Raspberry Pi 5&#039;s memory.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
The &#039;&#039;&#039;Makefile&#039;&#039;&#039; automates the invocation of the compiler and linker. Note that it explicitly targets the architecture of the Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note&#039;&#039;&#039; regarding Makefiles: Do not use spaces for indentation. &#039;&#039;&#039;make&#039;&#039;&#039; will not accept this. Always use a &#039;&#039;&#039;TAB&#039;&#039;&#039; character instead.&lt;br /&gt;
&lt;br /&gt;
Finally, do not forget to save all open files via &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the VS Code menu. The project is now fully configured and ready for its first build and debugging run!&lt;br /&gt;
&lt;br /&gt;
== Preparing the Hardware and Debugging in the GUI ==&lt;br /&gt;
=== Preparing the Hardware and Setting Up the SD Card ===&lt;br /&gt;
Since we are testing the code directly on the real hardware, we need to prepare the Raspberry Pi 5&#039;s SD card. The operating system (EEPROM/firmware) of the Pi 5 needs to know that we want to debug a bare-metal program via JTAG/SWD.&lt;br /&gt;
&lt;br /&gt;
Format a MicroSD card as FAT32 and copy the following three files into the root directory of the card:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (This file will be generated during the first compilation).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (The original device tree blob from the official Raspberry Pi firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (The configuration file for the firmware).&lt;br /&gt;
&lt;br /&gt;
Create the &#039;&#039;&#039;config.txt&#039;&#039;&#039; with the exact following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 What does this configuration do?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; sets the start address in RAM where our kernel will be loaded.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; switches the Raspberry Pi 5&#039;s GPIO pins into JTAG/SWD mode. This is what makes communication with the Raspberry Pi Debug Probe possible in the first place.&lt;br /&gt;
&lt;br /&gt;
Connecting the hardware:&lt;br /&gt;
* Insert the prepared SD card into the Raspberry Pi 5.&lt;br /&gt;
* Connect the Raspberry Pi Debug Probe to the dedicated debug port of the Raspberry Pi 5 (located between the Micro-HDMI ports) using the supplied 3-pin UART/debug cable.&lt;br /&gt;
* Connect the Debug Probe to your Windows PC via a USB cable.&lt;br /&gt;
* Power on the Raspberry Pi 5 (connect the power supply).&lt;br /&gt;
&lt;br /&gt;
With the JTAG interface enabled, the processor now waits at the start address for the debugger to connect and issue commands.&lt;br /&gt;
&lt;br /&gt;
=== Compiling the Program and Starting the Debugger ===&lt;br /&gt;
Thanks to our preparations in Visual Studio Code, we can control the entire build and flash process using keyboard shortcuts.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Compile&#039;&#039;: Press the keyboard shortcut &#039;&#039;&#039;CTRL + SHIFT + B&#039;&#039;&#039;. VS Code will now run the Makefile (&#039;&#039;&#039;make all&#039;&#039;&#039;) in the background. This will generate the files &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (for the debugger, including symbols) and &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (the raw binary format) in the project directory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Start Debugger&#039;&#039;: Press the &#039;&#039;&#039;F5&#039;&#039;&#039; key. &lt;br /&gt;
&lt;br /&gt;
The following now happens fully automatically:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD establishes the connection to the Raspberry Pi 5 via the Debug Probe.&lt;br /&gt;
* The GDB debugger is started.&lt;br /&gt;
* The newly compiled program is loaded directly into the Raspberry Pi 5&#039;s RAM (load).&lt;br /&gt;
* The processor&#039;s program counter is forced to the start address 0x80000.&lt;br /&gt;
&lt;br /&gt;
Since we specified in the &#039;&#039;&#039;launch.json&#039;&#039;&#039; that the debugger should halt on startup, execution stops exactly at the first instruction. In our source code, the cursor jumps directly to the &#039;&#039;&#039;boot.S&#039;&#039;&#039; file at the &#039;&#039;&#039;_start:&#039;&#039;&#039; label.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Operating the Debugger in VS Code ===&lt;br /&gt;
As soon as the debugger is active, the VS Code user interface changes. A floating debug control bar appears at the top of the screen.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The GUI controls:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Controls&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Action !! Keyboard Shortcut !! Description&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Immediately halts the running program at the current location.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Continue || F5 || Resumes normal program execution (until the next breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Step Over || F10 || Executes the current line. Does not step into functions.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Step Into || F11 || Steps directly into a function to examine it line by line.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Step Out || SHIFT + F11 || Executes the rest of the current function and stops immediately after returning.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Restart || CTRL + SHIFT + F5 || Reloads the program onto the Pi and starts the debugging process from the beginning.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stop || SHIFT + F5 || Ends the debug session and closes the connection to the Pi 5.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Using the Debugger Views ====&lt;br /&gt;
The biggest advantage of a professional GUI over the GDB command line is the visual presentation of all processor information on the left side of the screen:&lt;br /&gt;
&lt;br /&gt;
* Variables Window: Local and global variables are automatically displayed here. You can immediately see their current values. You can even manipulate these values with a double-click while halted to simulate test cases!&lt;br /&gt;
&lt;br /&gt;
* Watch Window: If you want to keep a permanent eye on specific variables or register addresses, you can add them here.&lt;br /&gt;
&lt;br /&gt;
* Call Stack: Shows you exactly which functions the program has gone through to reach the current point.&lt;br /&gt;
&lt;br /&gt;
* Registers Window (Cortex-Debug): A highlight for bare-metal developers. Here you can see the CPU registers of the ARM Cortex-A76 core (X0 to X30, SP, PC, etc.) with real-time access. If a register value changes after a single step, it is highlighted in color.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Setting Breakpoints with a Mouse Click ====&lt;br /&gt;
Typing memory addresses into GDB is a thing of the past. In VS Code, simply move your mouse to the left of the line numbers in the source code (e.g., in kernel.c on the line &#039;&#039;LED_on();&#039;&#039;). A faint red dot will appear. With a simple left-click, you activate the breakpoint (it turns solid red). If you now press &#039;&#039;&#039;F5&#039;&#039;&#039; (Continue), the program runs until it reaches exactly this line and freezes the CPU. Another click on the dot removes the breakpoint again.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
You have now set up a fully-fledged, professional development environment with hardware debugging for the Raspberry Pi 5!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1298</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1298"/>
		<updated>2026-06-03T16:32:44Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Creating the Linker Script and Makefile ===&lt;br /&gt;
&lt;br /&gt;
In order to build an executable bare-metal image for the Raspberry Pi 5 from the source code, we need a &#039;&#039;linker script&#039;&#039; and the control file for &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Both files are created directly in the main directory (root) of your project.&lt;br /&gt;
&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
This script defines the exact layout of the code segments in the Raspberry Pi 5&#039;s memory.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
The &#039;&#039;&#039;Makefile&#039;&#039;&#039; automates the invocation of the compiler and linker. Note that it explicitly targets the architecture of the Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note&#039;&#039;&#039; regarding Makefiles: Do not use spaces for indentation. &#039;&#039;&#039;make&#039;&#039;&#039; will not accept this. Always use a &#039;&#039;&#039;TAB&#039;&#039;&#039; character instead.&lt;br /&gt;
&lt;br /&gt;
Finally, do not forget to save all open files via &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the VS Code menu. The project is now fully configured and ready for its first build and debugging run!&lt;br /&gt;
&lt;br /&gt;
== Preparing the Hardware and Debugging in the GUI ==&lt;br /&gt;
=== Preparing the Hardware and Setting Up the SD Card ===&lt;br /&gt;
Since we are testing the code directly on the real hardware, we need to prepare the Raspberry Pi 5&#039;s SD card. The operating system (EEPROM/firmware) of the Pi 5 needs to know that we want to debug a bare-metal program via JTAG/SWD.&lt;br /&gt;
&lt;br /&gt;
Format a MicroSD card as FAT32 and copy the following three files into the root directory of the card:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (This file will be generated during the first compilation).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (The original device tree blob from the official Raspberry Pi firmware)&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (The configuration file for the firmware).&lt;br /&gt;
&lt;br /&gt;
Create the &#039;&#039;&#039;config.txt&#039;&#039;&#039; with the exact following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 What does this configuration do?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; sets the start address in RAM where our kernel will be loaded.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; switches the Raspberry Pi 5&#039;s GPIO pins into JTAG/SWD mode. This is what makes communication with the Raspberry Pi Debug Probe possible in the first place.&lt;br /&gt;
&lt;br /&gt;
Connecting the hardware:&lt;br /&gt;
* Insert the prepared SD card into the Raspberry Pi 5.&lt;br /&gt;
* Connect the Raspberry Pi Debug Probe to the dedicated debug port of the Raspberry Pi 5 (located between the Micro-HDMI ports) using the supplied 3-pin UART/debug cable.&lt;br /&gt;
* Connect the Debug Probe to your Windows PC via a USB cable.&lt;br /&gt;
* Power on the Raspberry Pi 5 (connect the power supply).&lt;br /&gt;
&lt;br /&gt;
With the JTAG interface enabled, the processor now waits at the start address for the debugger to connect and issue commands.&lt;br /&gt;
&lt;br /&gt;
=== Compiling the Program and Starting the Debugger ===&lt;br /&gt;
Thanks to our preparations in Visual Studio Code, we can control the entire build and flash process using keyboard shortcuts.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Compile&#039;&#039;: Press the keyboard shortcut &#039;&#039;&#039;CTRL + SHIFT + B&#039;&#039;&#039;. VS Code will now run the Makefile (&#039;&#039;&#039;make all&#039;&#039;&#039;) in the background. This will generate the files &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (for the debugger, including symbols) and &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (the raw binary format) in the project directory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Start Debugger&#039;&#039;: Press the &#039;&#039;&#039;F5&#039;&#039;&#039; key. &lt;br /&gt;
&lt;br /&gt;
The following now happens fully automatically:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD establishes the connection to the Raspberry Pi 5 via the Debug Probe.&lt;br /&gt;
* The GDB debugger is started.&lt;br /&gt;
* The newly compiled program is loaded directly into the Raspberry Pi 5&#039;s RAM (load).&lt;br /&gt;
* The processor&#039;s program counter is forced to the start address 0x80000.&lt;br /&gt;
&lt;br /&gt;
Since we specified in the &#039;&#039;&#039;launch.json&#039;&#039;&#039; that the debugger should halt on startup, execution stops exactly at the first instruction. In our source code, the cursor jumps directly to the &#039;&#039;&#039;boot.S&#039;&#039;&#039; file at the &#039;&#039;&#039;_start:&#039;&#039;&#039; label.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Operating the Debugger in VS Code ===&lt;br /&gt;
As soon as the debugger is active, the VS Code user interface changes. A floating debug control bar appears at the top of the screen.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The GUI controls:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Controls&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Action !! Keyboard Shortcut !! Description&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Immediately halts the running program at the current location.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Continue || F5 || Resumes normal program execution (until the next breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Step Over || F10 || Executes the current line. Does not step into functions.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Step Into || F11 || Steps directly into a function to examine it line by line.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Step Out || SHIFT + F11 || Executes the rest of the current function and stops immediately after returning.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Restart || CTRL + SHIFT + F5 || Reloads the program onto the Pi and starts the debugging process from the beginning.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stop || SHIFT + F5 || Ends the debug session and closes the connection to the Pi 5.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Using the Debugger Views ====&lt;br /&gt;
The biggest advantage of a professional GUI over the GDB command line is the visual presentation of all processor information on the left side of the screen:&lt;br /&gt;
&lt;br /&gt;
* Variables Window: Local and global variables are automatically displayed here. You can immediately see their current values. You can even manipulate these values with a double-click while halted to simulate test cases!&lt;br /&gt;
&lt;br /&gt;
* Watch Window: If you want to keep a permanent eye on specific variables or register addresses, you can add them here.&lt;br /&gt;
&lt;br /&gt;
* Call Stack: Shows you exactly which functions the program has gone through to reach the current point.&lt;br /&gt;
&lt;br /&gt;
* Registers Window (Cortex-Debug): A highlight for bare-metal developers. Here you can see the CPU registers of the ARM Cortex-A76 core (X0 to X30, SP, PC, etc.) with real-time access. If a register value changes after a single step, it is highlighted in color.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Setting Breakpoints with a Mouse Click ====&lt;br /&gt;
Typing memory addresses into GDB is a thing of the past. In VS Code, simply move your mouse to the left of the line numbers in the source code (e.g., in kernel.c on the line &#039;&#039;LED_on();&#039;&#039;). A faint red dot will appear. With a simple left-click, you activate the breakpoint (it turns solid red). If you now press &#039;&#039;&#039;F5&#039;&#039;&#039; (Continue), the program runs until it reaches exactly this line and freezes the CPU. Another click on the dot removes the breakpoint again.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
You have now set up a fully-fledged, professional development environment with hardware debugging for the Raspberry Pi 5!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1297</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1297"/>
		<updated>2026-06-03T16:28:29Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Creating the Linker Script and Makefile ===&lt;br /&gt;
&lt;br /&gt;
In order to build an executable bare-metal image for the Raspberry Pi 5 from the source code, we need a &#039;&#039;linker script&#039;&#039; and the control file for &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Both files are created directly in the main directory (root) of your project.&lt;br /&gt;
&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
This script defines the exact layout of the code segments in the Raspberry Pi 5&#039;s memory.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
The &#039;&#039;&#039;Makefile&#039;&#039;&#039; automates the invocation of the compiler and linker. Note that it explicitly targets the architecture of the Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note&#039;&#039;&#039; regarding Makefiles: Do not use spaces for indentation. &#039;&#039;&#039;make&#039;&#039;&#039; will not accept this. Always use a &#039;&#039;&#039;TAB&#039;&#039;&#039; character instead.&lt;br /&gt;
&lt;br /&gt;
Finally, do not forget to save all open files via &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the VS Code menu. The project is now fully configured and ready for its first build and debugging run!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1296</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1296"/>
		<updated>2026-06-03T16:25:31Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Source Code, Linker Script, and Makefile */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1295</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1295"/>
		<updated>2026-06-03T16:23:19Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* The Include Directory (include/) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
#define RPI_BASE 0x107C000000UL&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0 (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
#define MEM_KERNEL_START 0x80000 // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
typedef unsigned char u8;&lt;br /&gt;
typedef unsigned short u16;&lt;br /&gt;
typedef unsigned int u32;&lt;br /&gt;
typedef signed char s8;&lt;br /&gt;
typedef signed short s16;&lt;br /&gt;
typedef signed int s32;&lt;br /&gt;
typedef unsigned long u64;&lt;br /&gt;
typedef signed long s64;&lt;br /&gt;
typedef long intptr;&lt;br /&gt;
typedef unsigned long uintptr;&lt;br /&gt;
typedef unsigned long size_t;&lt;br /&gt;
typedef long ssize_t;&lt;br /&gt;
typedef char boolean;&lt;br /&gt;
#define ALIGN(n) attribute((aligned (n)))&lt;br /&gt;
#define FALSE 0&lt;br /&gt;
#define TRUE 1&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1294</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1294"/>
		<updated>2026-06-03T16:18:23Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Important: Note on Apparent Error Messages */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1293</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1293"/>
		<updated>2026-06-03T16:17:32Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Important: Note on Apparent Error Messages */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1292</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1292"/>
		<updated>2026-06-03T16:12:49Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1291</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1291"/>
		<updated>2026-06-03T16:06:26Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1290</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1290"/>
		<updated>2026-06-03T16:04:04Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1289</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1289"/>
		<updated>2026-06-03T15:59:42Z</updated>

		<summary type="html">&lt;p&gt;Satyria: Die Seite wurde neu angelegt: „Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.  To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &amp;#039;&amp;#039;&amp;#039;Raspbe…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=English&amp;diff=1288</id>
		<title>English</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=English&amp;diff=1288"/>
		<updated>2026-06-03T15:58:06Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Appendix */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== &amp;lt;strong&amp;gt;ARM Assembly Programming with the Raspberry Pi&amp;lt;/strong&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
[[File:EinhornOrg.png|frameless|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Matthias Steiner&lt;br /&gt;
&lt;br /&gt;
Satyria Press © 2020-2025&lt;br /&gt;
&lt;br /&gt;
== Content ==&lt;br /&gt;
*[[Preface]]&lt;br /&gt;
*[[Basics of Assembly]]&lt;br /&gt;
**[[Basics of Assembly#The ARM Assembler|The ARM Assembler]]&lt;br /&gt;
*[[Setting up the Programming Environment (Console)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 ==&lt;br /&gt;
*[[Setting up the Programming Environment (64-Bit)]]&lt;br /&gt;
*[[Introduction to C and Assembly Programming with the Raspberry Pi 5]]&lt;br /&gt;
**[[Introduction to C and Assembly Programming with the Raspberry Pi 5#Why C and Assembly?|Why C and Assembly?]]&lt;br /&gt;
*[[Running the Program on the Raspberry Pi 5]]&lt;br /&gt;
*[[Working with Make and Linker Script]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 Assembler==&lt;br /&gt;
*[[Our First Program (PI5)]]&lt;br /&gt;
*[[Making the LED Blink (PI5)]]&lt;br /&gt;
*[[Error Handling]]&lt;br /&gt;
*[[Graphics (PI5)]]&lt;br /&gt;
*[[Chars (PI5)e]]&lt;br /&gt;
*The Terminal&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 C==&lt;br /&gt;
*[[Our First Program in C (PI5)]]&lt;br /&gt;
*[[Making the LED Blink in C (PI5)]]&lt;br /&gt;
*[[Error Handling in C (PI5)]]&lt;br /&gt;
*[[Graphics in C (PI5)]]&lt;br /&gt;
*[[Chars in C (PI5)e]]&lt;br /&gt;
*[[The Terminal in C (PI5)]]&lt;br /&gt;
*[[Using printf in C (PI5)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 Assembler 32-Bit ==&lt;br /&gt;
*[[Setting up the Programming Environment (32-Bit)]]&lt;br /&gt;
*[[System Programming / Bare Metal]]&lt;br /&gt;
*[[The First Program]]&lt;br /&gt;
*[[General Purpose I/O(e)]]&lt;br /&gt;
&lt;br /&gt;
== Additional Material ==&lt;br /&gt;
== Appendix ==&lt;br /&gt;
* Debugging&lt;br /&gt;
**[[Bare-Metal Debugging with JTAG and RPI 4]]&lt;br /&gt;
**[[Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5]]&lt;br /&gt;
&lt;br /&gt;
== Translations/Traducciones ==&lt;br /&gt;
*[[Hauptseite|German]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=English&amp;diff=1287</id>
		<title>English</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=English&amp;diff=1287"/>
		<updated>2026-06-03T15:53:45Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Appendix */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== &amp;lt;strong&amp;gt;ARM Assembly Programming with the Raspberry Pi&amp;lt;/strong&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
[[File:EinhornOrg.png|frameless|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Matthias Steiner&lt;br /&gt;
&lt;br /&gt;
Satyria Press © 2020-2025&lt;br /&gt;
&lt;br /&gt;
== Content ==&lt;br /&gt;
*[[Preface]]&lt;br /&gt;
*[[Basics of Assembly]]&lt;br /&gt;
**[[Basics of Assembly#The ARM Assembler|The ARM Assembler]]&lt;br /&gt;
*[[Setting up the Programming Environment (Console)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 ==&lt;br /&gt;
*[[Setting up the Programming Environment (64-Bit)]]&lt;br /&gt;
*[[Introduction to C and Assembly Programming with the Raspberry Pi 5]]&lt;br /&gt;
**[[Introduction to C and Assembly Programming with the Raspberry Pi 5#Why C and Assembly?|Why C and Assembly?]]&lt;br /&gt;
*[[Running the Program on the Raspberry Pi 5]]&lt;br /&gt;
*[[Working with Make and Linker Script]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 Assembler==&lt;br /&gt;
*[[Our First Program (PI5)]]&lt;br /&gt;
*[[Making the LED Blink (PI5)]]&lt;br /&gt;
*[[Error Handling]]&lt;br /&gt;
*[[Graphics (PI5)]]&lt;br /&gt;
*[[Chars (PI5)e]]&lt;br /&gt;
*The Terminal&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 C==&lt;br /&gt;
*[[Our First Program in C (PI5)]]&lt;br /&gt;
*[[Making the LED Blink in C (PI5)]]&lt;br /&gt;
*[[Error Handling in C (PI5)]]&lt;br /&gt;
*[[Graphics in C (PI5)]]&lt;br /&gt;
*[[Chars in C (PI5)e]]&lt;br /&gt;
*[[The Terminal in C (PI5)]]&lt;br /&gt;
*[[Using printf in C (PI5)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 Assembler 32-Bit ==&lt;br /&gt;
*[[Setting up the Programming Environment (32-Bit)]]&lt;br /&gt;
*[[System Programming / Bare Metal]]&lt;br /&gt;
*[[The First Program]]&lt;br /&gt;
*[[General Purpose I/O(e)]]&lt;br /&gt;
&lt;br /&gt;
== Additional Material ==&lt;br /&gt;
== Appendix ==&lt;br /&gt;
* Debugging&lt;br /&gt;
**[[Bare-Metal Debugging with JTAG and RPI 4]]&lt;br /&gt;
&lt;br /&gt;
== Translations/Traducciones ==&lt;br /&gt;
*[[Hauptseite|German]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=English&amp;diff=1286</id>
		<title>English</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=English&amp;diff=1286"/>
		<updated>2026-06-03T15:53:35Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Appendix */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== &amp;lt;strong&amp;gt;ARM Assembly Programming with the Raspberry Pi&amp;lt;/strong&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
[[File:EinhornOrg.png|frameless|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Matthias Steiner&lt;br /&gt;
&lt;br /&gt;
Satyria Press © 2020-2025&lt;br /&gt;
&lt;br /&gt;
== Content ==&lt;br /&gt;
*[[Preface]]&lt;br /&gt;
*[[Basics of Assembly]]&lt;br /&gt;
**[[Basics of Assembly#The ARM Assembler|The ARM Assembler]]&lt;br /&gt;
*[[Setting up the Programming Environment (Console)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 ==&lt;br /&gt;
*[[Setting up the Programming Environment (64-Bit)]]&lt;br /&gt;
*[[Introduction to C and Assembly Programming with the Raspberry Pi 5]]&lt;br /&gt;
**[[Introduction to C and Assembly Programming with the Raspberry Pi 5#Why C and Assembly?|Why C and Assembly?]]&lt;br /&gt;
*[[Running the Program on the Raspberry Pi 5]]&lt;br /&gt;
*[[Working with Make and Linker Script]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 Assembler==&lt;br /&gt;
*[[Our First Program (PI5)]]&lt;br /&gt;
*[[Making the LED Blink (PI5)]]&lt;br /&gt;
*[[Error Handling]]&lt;br /&gt;
*[[Graphics (PI5)]]&lt;br /&gt;
*[[Chars (PI5)e]]&lt;br /&gt;
*The Terminal&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 C==&lt;br /&gt;
*[[Our First Program in C (PI5)]]&lt;br /&gt;
*[[Making the LED Blink in C (PI5)]]&lt;br /&gt;
*[[Error Handling in C (PI5)]]&lt;br /&gt;
*[[Graphics in C (PI5)]]&lt;br /&gt;
*[[Chars in C (PI5)e]]&lt;br /&gt;
*[[The Terminal in C (PI5)]]&lt;br /&gt;
*[[Using printf in C (PI5)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 Assembler 32-Bit ==&lt;br /&gt;
*[[Setting up the Programming Environment (32-Bit)]]&lt;br /&gt;
*[[System Programming / Bare Metal]]&lt;br /&gt;
*[[The First Program]]&lt;br /&gt;
*[[General Purpose I/O(e)]]&lt;br /&gt;
&lt;br /&gt;
== Additional Material ==&lt;br /&gt;
== Appendix ==&lt;br /&gt;
* Debugging&lt;br /&gt;
*[[Bare-Metal Debugging with JTAG and RPI 4]]&lt;br /&gt;
&lt;br /&gt;
== Translations/Traducciones ==&lt;br /&gt;
*[[Hauptseite|German]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Hauptseite&amp;diff=1285</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Hauptseite&amp;diff=1285"/>
		<updated>2026-06-03T15:52:15Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Anhang */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== &amp;lt;strong&amp;gt;C und ARM-Assemblerprogrammierung mit dem Raspberry&amp;lt;/strong&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
[[Datei:EinhornOrg.png|frameless|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Matthias Steiner&lt;br /&gt;
&lt;br /&gt;
Satyria Press © by 2020-2025&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vorwort ===&lt;br /&gt;
&lt;br /&gt;
Herzlich willkommen auf unserer Seite zur Bare-Metal-Programmierung für den Raspberry Pi 4 und Pi 5! Diese Webseite richtet sich sowohl an Anfänger als auch an fortgeschrittene Programmierer, die tief in die Welt der hardwarenahen Programmierung eintauchen möchten.&lt;br /&gt;
&lt;br /&gt;
Ursprünglich sollte diese Seite sich ausschließlich mit Assembler beschäftigen, wurde jedoch im Laufe der Zeit um C erweitert. Daher finden Sie hier zunächst grundlegende Informationen zu Assembler. Wenn Ihr Interesse in der C-Programmierung liegt und Sie mit Windows arbeiten, empfehlen wir Ihnen mit dem Kapitel &#039;&#039;&#039;[[Programmierumgebung erstellen (Konsole)]]&#039;&#039;&#039; zu beginnen. Für Linux-Nutzer ist das Kapitel &#039;&#039;&#039;[[Programmierumgebung erstellen unter Linux]]&#039;&#039;&#039; der richtige Startpunkt.&lt;br /&gt;
&lt;br /&gt;
Im Kapitel &#039;&#039;&#039;Raspberry Pi 4 Assembler 32-Bit&#039;&#039;&#039; beschreiben wir die Assembler-Programmierung für den Raspberry Pi 4. Dieses Kapitel hat zwar bereits einige Jahre auf dem Buckel, bleibt aber weiterhin relevant und wertvoll. Hier finden Sie detaillierte Erklärungen der ARM-32-Bit-Befehle mit anschaulichen Beschreibungen und Beispielen, was besonders Anfängern den Einstieg erleichtern soll.&lt;br /&gt;
&lt;br /&gt;
Das Kapitel &#039;&#039;&#039;Raspberry Pi 4 C (64-Bit)&#039;&#039;&#039; entstand im Laufe unserer Arbeit am Raspberry Pi 5 und bietet eine umfassende Beschreibung der 64-Bit-C-Programmierung für den Raspberry Pi 4. Dies wird auch später die USB-Programmierung einschließen, eine Funktion, die wir später auf den Raspberry Pi 5 übertragen werden. So haben alle Projekte einen gemeinsamen Ausgangspunkt.&lt;br /&gt;
&lt;br /&gt;
Das Kapitel &#039;&#039;&#039;Raspberry Pi 5&#039;&#039;&#039; ist der ideale Startpunkt für die Programmierung des Raspberry Pi 5 in Assembler und C. Es führt Sie von der Installation des Hostsystems (Windows/Linux) bis zur abschließenden Programmierung in C und Assembler.&lt;br /&gt;
&lt;br /&gt;
Im Anhang finden Sie viele nützliche Informationen, wie eine Beschreibung der &#039;&#039;&#039;GNU Compiler Collection&#039;&#039;&#039;, eine Auflistung des &#039;&#039;&#039;ARM-Befehlssatzes&#039;&#039;&#039; (im Aufbau), verschiedene &#039;&#039;&#039;Raspberry Pi Modelle&#039;&#039;&#039; und einige hilfreiche &#039;&#039;&#039;Links&#039;&#039;&#039; zu anderen Projekten. Ein aktuelles Projekt zum Thema &#039;&#039;&#039;Debugging&#039;&#039;&#039; ist ebenfalls dort zu finden.&lt;br /&gt;
&lt;br /&gt;
Wir hoffen, dass Sie auf unserer Seite viel Freude haben und sich gut zurechtfinden! Bei Fragen oder Kommentaren schicken Sie uns gerne eine E-Mail an assem@satyria.de. Wir freuen uns über Ihre Rückmeldungen und Anregungen.&lt;br /&gt;
&lt;br /&gt;
Viel Spaß beim Programmieren!&lt;br /&gt;
&lt;br /&gt;
== Sponsor ==&lt;br /&gt;
Wenn ihr uns etwas unterstützen möchtet, schaut einfach mal hier vorbei:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Logo hexenlaedle.png|frameless|200x200px|link=https://www.hexenlaedle.de]]&lt;br /&gt;
&lt;br /&gt;
== Inhalt ==&lt;br /&gt;
*[[Vorwort]]&lt;br /&gt;
*[[Grundlegendes zu Assembler]]&lt;br /&gt;
**[[Grundlegendes zu Assembler#Der ARM-Assembler|Der ARM-Assembler]]&lt;br /&gt;
*[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 ==&lt;br /&gt;
*[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
*[[Einführung in C- und Assemblerprogrammierung mit dem Raspberry Pi 5]]&lt;br /&gt;
**[[Einführung in C- und Assemblerprogrammierung mit dem Raspberry Pi 5#Warum C und Assembler?|Warum C und Assembler?]]&lt;br /&gt;
*[[Programm auf dem Raspberry Pi 5 ausführen]]&lt;br /&gt;
*[[Arbeiten mit Make und Linker-Script]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 Assembler==&lt;br /&gt;
*[[Unser erstes Programm (PI5)]]&lt;br /&gt;
*[[Lass die LED leuchten (PI5)]]&lt;br /&gt;
*[[Fehlerbehandlung]]&lt;br /&gt;
*[[Grafik (PI5)]]&lt;br /&gt;
*[[Chars (PI5)]]&lt;br /&gt;
*Das Terminal (PI5)&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 C ==&lt;br /&gt;
*[[Unser erstes Programm in C (PI5)]]&lt;br /&gt;
*[[Lass die LED leuchten in C (PI5)]]&lt;br /&gt;
*[[Fehlerbehandlung in C (PI5)]]&lt;br /&gt;
*[[Grafik in C (PI5)]]&lt;br /&gt;
*[[Chars in C (PI5)]]&lt;br /&gt;
*[[Das Terminal in C (PI5)]]&lt;br /&gt;
*[[printf in BareMetal (PI5)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 Assembler 32-Bit ==&lt;br /&gt;
*[[Programmierumgebung erstellen (32-Bit)]]&lt;br /&gt;
*[[Systemprogrammierung / Bare Metal]]&lt;br /&gt;
*[[Das erste Programm]]&lt;br /&gt;
*[[General Purpose I/O]]&lt;br /&gt;
*[[System Timer]]&lt;br /&gt;
*[[UART]]&lt;br /&gt;
*[[Die Anzeige]]&lt;br /&gt;
*[[Zeichenfunktionen]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 C (64-Bit) ==&lt;br /&gt;
*[[Unser erstes Programm in C (PI4)]]&lt;br /&gt;
*[[Lass die LED leuchten in C (PI4)]]&lt;br /&gt;
*[[Fehlerbehandlung in C (PI4)]]&lt;br /&gt;
*[[Grafik in C (PI4)]]&lt;br /&gt;
*[[Chars in C (PI4)]]&lt;br /&gt;
*[[Das Terminal in C (PI4)]]&lt;br /&gt;
*[[Printf in BareMetal (PI4)]]&lt;br /&gt;
*[[Systeminformationen (PI4)]] (noch kein Inhalt)&lt;br /&gt;
*[[Interrupts (PI4)]]&lt;br /&gt;
**[[Beispiel Timer-Interrupt (PI4)]]&lt;br /&gt;
*[[Interrupt Teil 2 (PI4)]]&lt;br /&gt;
&lt;br /&gt;
== ARM64 Assembler ==&lt;br /&gt;
*[[Programmieren mit ARM64 Assembler]]&lt;br /&gt;
&lt;br /&gt;
== Zusätzliches Material ==&lt;br /&gt;
*[[Links der verwendeten Software]]&lt;br /&gt;
&lt;br /&gt;
== Anhang ==&lt;br /&gt;
*[[GNU Compiler Collection]]&lt;br /&gt;
*[[ARM-Befehlssatz]]&lt;br /&gt;
**[[Grundlegende Befehle]]&lt;br /&gt;
&amp;lt;!--**[[ARMv8-Übersicht]]--&amp;gt;&lt;br /&gt;
*[[Raspberry PI Modelle]]&lt;br /&gt;
**[[Basisadressen der Modelle]]&lt;br /&gt;
*[[LINKS]]&lt;br /&gt;
* Debugging&lt;br /&gt;
**[[Bare-Metal Debugging (JTAG, RPI 4)]]&lt;br /&gt;
&amp;lt;!--*[[Bare-Metal Debugging]]--&amp;gt;&lt;br /&gt;
**[[Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5]]&lt;br /&gt;
----&lt;br /&gt;
*[[Die wichtigsten Linux-Terminal-Befehle]]&lt;br /&gt;
&lt;br /&gt;
== Translations/Traducciones ==&lt;br /&gt;
*[[English]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1284</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1284"/>
		<updated>2026-06-03T15:49:42Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Die .vscode Konfigurationsdateien anlegen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Um direkt mit dem Raspberry Pi 5 zu kommunizieren und Programme zu debuggen, wird zusätzlich eine &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039; benötigt. Diese wird über USB mit einem Windows-Rechner und über die Debug-Pins mit dem Raspberry Pi 5 verbunden.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und der Debug Probe verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Überprüfen Sie anschließend, ob die Datei &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; existiert. Diese wird in der Regel standardmäßig mitgeliefert. Sollte sie fehlen, erstellen Sie diese Datei neu mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Da in älteren oder Standard-OpenOCD-Releases ein passendes Target-Profil für den neuen Broadcom-Chip BCM2712 des Raspberry Pi 5 fehlt, müssen wir dieses manuell anlegen. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP für direkten Zugriff&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# Diese Adressen wurden aus der ROM-Table via &#039;dap info 0&#039;-Befehl ausgelesen&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Standard-Target ist cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Überschreibt den standardmäßigen &amp;quot;reset halt&amp;quot; Befehl von VS-Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Befehle, die direkt nach dem Verbinden ausgeführt werden&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Zwingt den Prozessor direkt auf die Startadresse&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI5)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Bit 9 auf 0 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Bit 9 auf 1 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO-Definitionen für den Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; setzt die Startadresse im RAM fest, an die unser Kernel geladen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 5 in den JTAG-/SWD-Modus um. Dadurch wird die Kommunikation mit der Raspberry Pi Debug Probe überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 5.&lt;br /&gt;
* Verbinden Sie die Raspberry Pi Debug Probe über das mitgelieferte 3-Pol-UART/Debug-Kabel mit dem dedizierten Debug-Port des Raspberry Pi 5 (befindet sich zwischen den Micro-HDMI-Ports).&lt;br /&gt;
* Schließen Sie die Debug Probe per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 5 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 5 über die Debug Probe auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 5 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1283</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1283"/>
		<updated>2026-06-03T15:47:33Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Linkerscript und Makefile erstellen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Um direkt mit dem Raspberry Pi 5 zu kommunizieren und Programme zu debuggen, wird zusätzlich eine &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039; benötigt. Diese wird über USB mit einem Windows-Rechner und über die Debug-Pins mit dem Raspberry Pi 5 verbunden.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und der Debug Probe verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Überprüfen Sie anschließend, ob die Datei &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; existiert. Diese wird in der Regel standardmäßig mitgeliefert. Sollte sie fehlen, erstellen Sie diese Datei neu mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Da in älteren oder Standard-OpenOCD-Releases ein passendes Target-Profil für den neuen Broadcom-Chip BCM2712 des Raspberry Pi 5 fehlt, müssen wir dieses manuell anlegen. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP für direkten Zugriff&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# Diese Adressen wurden aus der ROM-Table via &#039;dap info 0&#039;-Befehl ausgelesen&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Standard-Target ist cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Überschreibt den standardmäßigen &amp;quot;reset halt&amp;quot; Befehl von VS-Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Befehle, die direkt nach dem Verbinden ausgeführt werden&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Zwingt den Prozessor direkt auf die Startadresse&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI5)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Bit 9 auf 0 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Bit 9 auf 1 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO-Definitionen für den Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; setzt die Startadresse im RAM fest, an die unser Kernel geladen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 5 in den JTAG-/SWD-Modus um. Dadurch wird die Kommunikation mit der Raspberry Pi Debug Probe überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 5.&lt;br /&gt;
* Verbinden Sie die Raspberry Pi Debug Probe über das mitgelieferte 3-Pol-UART/Debug-Kabel mit dem dedizierten Debug-Port des Raspberry Pi 5 (befindet sich zwischen den Micro-HDMI-Ports).&lt;br /&gt;
* Schließen Sie die Debug Probe per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 5 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 5 über die Debug Probe auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 5 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1282</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1282"/>
		<updated>2026-06-03T15:43:44Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Die Debugger-Ansichten nutzen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Um direkt mit dem Raspberry Pi 5 zu kommunizieren und Programme zu debuggen, wird zusätzlich eine &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039; benötigt. Diese wird über USB mit einem Windows-Rechner und über die Debug-Pins mit dem Raspberry Pi 5 verbunden.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und der Debug Probe verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Überprüfen Sie anschließend, ob die Datei &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; existiert. Diese wird in der Regel standardmäßig mitgeliefert. Sollte sie fehlen, erstellen Sie diese Datei neu mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Da in älteren oder Standard-OpenOCD-Releases ein passendes Target-Profil für den neuen Broadcom-Chip BCM2712 des Raspberry Pi 5 fehlt, müssen wir dieses manuell anlegen. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP für direkten Zugriff&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# Diese Adressen wurden aus der ROM-Table via &#039;dap info 0&#039;-Befehl ausgelesen&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Standard-Target ist cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Überschreibt den standardmäßigen &amp;quot;reset halt&amp;quot; Befehl von VS-Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Befehle, die direkt nach dem Verbinden ausgeführt werden&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Zwingt den Prozessor direkt auf die Startadresse&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI5)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Bit 9 auf 0 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Bit 9 auf 1 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO-Definitionen für den Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; setzt die Startadresse im RAM fest, an die unser Kernel geladen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 5 in den JTAG-/SWD-Modus um. Dadurch wird die Kommunikation mit der Raspberry Pi Debug Probe überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 5.&lt;br /&gt;
* Verbinden Sie die Raspberry Pi Debug Probe über das mitgelieferte 3-Pol-UART/Debug-Kabel mit dem dedizierten Debug-Port des Raspberry Pi 5 (befindet sich zwischen den Micro-HDMI-Ports).&lt;br /&gt;
* Schließen Sie die Debug Probe per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 5 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 5 über die Debug Probe auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 5 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Datei:Debuger_Ansicht.png&amp;diff=1281</id>
		<title>Datei:Debuger Ansicht.png</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Datei:Debuger_Ansicht.png&amp;diff=1281"/>
		<updated>2026-06-03T15:43:33Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Debuger Ansicht&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1280</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1280"/>
		<updated>2026-06-03T15:42:37Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Breakpoints (Haltepunkte) per Mausklick setzen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Um direkt mit dem Raspberry Pi 5 zu kommunizieren und Programme zu debuggen, wird zusätzlich eine &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039; benötigt. Diese wird über USB mit einem Windows-Rechner und über die Debug-Pins mit dem Raspberry Pi 5 verbunden.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und der Debug Probe verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Überprüfen Sie anschließend, ob die Datei &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; existiert. Diese wird in der Regel standardmäßig mitgeliefert. Sollte sie fehlen, erstellen Sie diese Datei neu mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Da in älteren oder Standard-OpenOCD-Releases ein passendes Target-Profil für den neuen Broadcom-Chip BCM2712 des Raspberry Pi 5 fehlt, müssen wir dieses manuell anlegen. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP für direkten Zugriff&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# Diese Adressen wurden aus der ROM-Table via &#039;dap info 0&#039;-Befehl ausgelesen&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Standard-Target ist cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Überschreibt den standardmäßigen &amp;quot;reset halt&amp;quot; Befehl von VS-Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Befehle, die direkt nach dem Verbinden ausgeführt werden&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Zwingt den Prozessor direkt auf die Startadresse&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI5)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Bit 9 auf 0 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Bit 9 auf 1 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO-Definitionen für den Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; setzt die Startadresse im RAM fest, an die unser Kernel geladen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 5 in den JTAG-/SWD-Modus um. Dadurch wird die Kommunikation mit der Raspberry Pi Debug Probe überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 5.&lt;br /&gt;
* Verbinden Sie die Raspberry Pi Debug Probe über das mitgelieferte 3-Pol-UART/Debug-Kabel mit dem dedizierten Debug-Port des Raspberry Pi 5 (befindet sich zwischen den Micro-HDMI-Ports).&lt;br /&gt;
* Schließen Sie die Debug Probe per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 5 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 5 über die Debug Probe auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 5 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Datei:Breakpoint2.png&amp;diff=1279</id>
		<title>Datei:Breakpoint2.png</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Datei:Breakpoint2.png&amp;diff=1279"/>
		<updated>2026-06-03T15:42:20Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Breakpoint2&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Datei:Breakpoint1.png&amp;diff=1278</id>
		<title>Datei:Breakpoint1.png</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Datei:Breakpoint1.png&amp;diff=1278"/>
		<updated>2026-06-03T15:42:01Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Breakpoint1&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1277</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1277"/>
		<updated>2026-06-03T15:40:20Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Bedienung des Debuggers in VS Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Um direkt mit dem Raspberry Pi 5 zu kommunizieren und Programme zu debuggen, wird zusätzlich eine &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039; benötigt. Diese wird über USB mit einem Windows-Rechner und über die Debug-Pins mit dem Raspberry Pi 5 verbunden.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und der Debug Probe verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Überprüfen Sie anschließend, ob die Datei &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; existiert. Diese wird in der Regel standardmäßig mitgeliefert. Sollte sie fehlen, erstellen Sie diese Datei neu mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Da in älteren oder Standard-OpenOCD-Releases ein passendes Target-Profil für den neuen Broadcom-Chip BCM2712 des Raspberry Pi 5 fehlt, müssen wir dieses manuell anlegen. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP für direkten Zugriff&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# Diese Adressen wurden aus der ROM-Table via &#039;dap info 0&#039;-Befehl ausgelesen&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Standard-Target ist cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Überschreibt den standardmäßigen &amp;quot;reset halt&amp;quot; Befehl von VS-Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Befehle, die direkt nach dem Verbinden ausgeführt werden&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Zwingt den Prozessor direkt auf die Startadresse&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI5)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Bit 9 auf 0 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Bit 9 auf 1 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO-Definitionen für den Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; setzt die Startadresse im RAM fest, an die unser Kernel geladen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 5 in den JTAG-/SWD-Modus um. Dadurch wird die Kommunikation mit der Raspberry Pi Debug Probe überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 5.&lt;br /&gt;
* Verbinden Sie die Raspberry Pi Debug Probe über das mitgelieferte 3-Pol-UART/Debug-Kabel mit dem dedizierten Debug-Port des Raspberry Pi 5 (befindet sich zwischen den Micro-HDMI-Ports).&lt;br /&gt;
* Schließen Sie die Debug Probe per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 5 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 5 über die Debug Probe auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 5 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Datei:Debug_Stop.png&amp;diff=1276</id>
		<title>Datei:Debug Stop.png</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Datei:Debug_Stop.png&amp;diff=1276"/>
		<updated>2026-06-03T15:39:59Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Debug Stop&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Datei:Debug_Neustart.png&amp;diff=1275</id>
		<title>Datei:Debug Neustart.png</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Datei:Debug_Neustart.png&amp;diff=1275"/>
		<updated>2026-06-03T15:39:22Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Debug Neustart&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Datei:Debug_Herausspringen.png&amp;diff=1274</id>
		<title>Datei:Debug Herausspringen.png</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Datei:Debug_Herausspringen.png&amp;diff=1274"/>
		<updated>2026-06-03T15:38:42Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Debug Herausspringen&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Datei:Debug_EinzelschrittStepInto.png&amp;diff=1273</id>
		<title>Datei:Debug EinzelschrittStepInto.png</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Datei:Debug_EinzelschrittStepInto.png&amp;diff=1273"/>
		<updated>2026-06-03T15:38:03Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Debug EinzelschrittStepInto&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Datei:Debug_EinzelschrittStepOver.png&amp;diff=1272</id>
		<title>Datei:Debug EinzelschrittStepOver.png</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Datei:Debug_EinzelschrittStepOver.png&amp;diff=1272"/>
		<updated>2026-06-03T15:37:16Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Debug EinzelschrittStepOver&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
</feed>