Engineering KI

Wenn Embedded-Entwicklung zu langsam, teuer oder schwer beherrschbar wird

Moderne Embedded-Projekte scheitern selten daran, dass ein Team nicht programmieren kann.

Die Schwierigkeiten beginnen dort, wo Software auf die reale Plattform trifft:

Hardware ist teuer oder noch nicht verfügbar.
Tests benötigen Laboraufbauten und lassen sich nur begrenzt parallelisieren.
Produktionszyklen laufen über viele Jahre.
BSP, Betriebssystem, Interrupts, Speicherarchitektur und Hardwareabhängigkeiten sind oft nur teilweise verstanden oder dokumentiert.

Gleichzeitig sollen bestehende Systeme weiterentwickelt, auf neue Plattformen migriert und zunehmend mit KI-Unterstützung bearbeitet werden.

Genau an dieser Stelle unterstütze ich.

Hardware muss nicht der Flaschenhals sein

Für Firmwaretests ist reale Hardware häufig die knappste Ressource.

Ein Testsystem steht im Labor. Ein Fehler tritt nur unter bestimmten Zuständen auf. Mehrere Entwickler benötigen gleichzeitig Zugriff. CI/CD endet dort, wo physische Hardware erforderlich wird.

Ich entwickle virtuelle Embedded-Plattformen, auf denen reale Firmware und BSPs ausgeführt werden können.

Dabei geht es nicht nur um eine CPU-Simulation, sondern bei Bedarf um das für die Software relevante Gesamtsystem: Speicher, Interrupts, Peripherie, Busse, Kommunikationswege und externe Komponenten.

Eine solche virtuelle Plattform kann anschließend automatisiert gestartet, zurückgesetzt, manipuliert und getestet werden.

Damit werden Dinge möglich, die mit realer Hardware schwierig oder teuer sind:

  1. reproduzierbare Fehlerzustände,
  2. parallele Tests,
  3. automatisierte Regressionen,
  4. Fault Injection
  5. und CI/CD bis tief in die Firmware hinein.

Reale Hardware bleibt wichtig – aber sie muss nicht mehr für jeden Entwicklungsschritt zur Verfügung stehen.

Fault Injection can be seen as flows which are connected to a break point.

Fault Injection

Wenn das Problem unterhalb der Applikation liegt

Viele sehr gute Softwareteams arbeiten hervorragend auf Applikationsebene.

Schwierig wird es, wenn ein Problem tiefer sitzt:

im BSP, im RTOS, im Startup-Code, in der Speicherabbildung, im Interrupt-System, in der ABI, im Compiler oder in den Eigenschaften der Hardwareplattform.

Gerade bei Migrationen reicht es dann nicht, Quellcode von einem System auf ein anderes zu übertragen.

Man muss verstehen, welche Annahmen die bestehende Software über ihre Umgebung macht.

Wie wird Speicher initialisiert?
Welche Teile hängen vom Compiler oder der Runtime ab?
Welche Interrupt- und Scheduling-Semantik wird vorausgesetzt?
Welche Hardwarezustände erwartet die Software?
Welche Abhängigkeiten sind im BSP versteckt?

Ich kann diese Ebene analysieren, modellieren und in eine kontrollierte Testumgebung überführen.

Das ist besonders hilfreich, wenn ein Team fachlich stark ist, aber für eine bestimmte Migration oder Plattform nicht dauerhaft eigene Spezialisten für Compiler, Betriebssysteme und Low-Level-Architektur vorhalten möchte.

Migration ohne Blindflug

Bei langlebigen Embedded-Produkten besteht häufig ein unangenehmer Zielkonflikt:

Die bestehende Software funktioniert – aber Toolchain, Hardware oder Entwicklungsumgebung sollen ersetzt werden.

Eine komplette Neuentwicklung ist riskant. Eine einfache Portierung reicht oft nicht aus.

Ich unterstütze solche Migrationen, indem bestehendes Verhalten zunächst reproduzierbar gemacht wird.

Die alte und die neue Umgebung können dadurch gegeneinander getestet werden.

So wird aus einer Migration kein einmaliger großer Sprung, sondern ein überprüfbarer Prozess:

Verstehen → Modellieren → Migrieren → Ausführen → Vergleichen → Verifizieren

Das gilt für klassische C/C++-Systeme ebenso wie für Ada-basierte Embedded-Software und spezielle Compiler- oder Runtime-Umgebungen.

KI hilft erst dann wirklich, wenn sie das System versteht

Auch beim Einsatz von LLMs entsteht in technischen Projekten schnell ein neues Kostenproblem.

Ein Modell bekommt ein Reference Manual, einige Source-Dateien und Compiler-Dokumentation.

Bei der nächsten Aufgabe werden dieselben Dateien erneut gelesen.

Und bei der nächsten wieder.

Das kostet Kontext, Tokens und Zeit – und trotzdem fehlt häufig die Verbindung zwischen den Informationen.

Ich verwende deshalb Knowledge Graphs und GraphRAG.

Technisches Wissen wird einmal strukturiert extrahiert und miteinander verknüpft.

Zum Beispiel:

Source Code → Funktion → Register → Peripheral → Hardwareverhalten → Requirement → Test

oder:

Ada Source → Sprachsemantik → Compiler → Runtime → generierter Code → Test

Ein Modell muss dadurch nicht immer wieder hunderte oder tausende Seiten Dokumentation durchsuchen.

Es erhält gezielt den Teil des Wissens, der für die aktuelle Aufgabe relevant ist.

Das reduziert Tokenverbrauch und macht KI-Unterstützung gleichzeitig präziser und nachvollziehbarer.

Für große Dokumentations- und Codebestände betreibe ich dafür eine lokale Infrastruktur, mit der technische Informationen massiv parallel extrahiert und in strukturierte Wissensbasen überführt werden können.

KI bekommt nicht nur Dokumente – sondern Werkzeuge

Der nächste Schritt besteht darin, das Modell nicht nur lesen zu lassen.

Über MCP kann ein Modell kontrollierten Zugriff auf Entwicklungswerkzeuge erhalten.

Es kann beispielsweise:

  • Firmware analysieren,
  • Informationen aus dem Knowledge Graph abrufen,
  • Tests erzeugen oder starten,
  • einen Emulator bedienen,
  • Speicher- und Registerzustände untersuchen,
  • Compilerergebnisse vergleichen
  • und Fehler systematisch eingrenzen.

Damit verändert sich die Rolle der KI.

Sie produziert nicht einfach möglichst plausiblen Code.

Sie arbeitet innerhalb einer Engineering-Umgebung, in der ihre Ergebnisse überprüft werden können.

Von der KI-generierten Idee zum verifizierten Ergebnis

Für mich ist ein von einem Modell erzeugter Patch noch kein Ergebnis.

Erst wenn er kompiliert, ausgeführt und getestet wurde, wird daraus Engineering.

Deshalb verbinde ich KI-Unterstützung mit ausführbaren Systemmodellen und automatisierter Testtechnik.

Meine Testumgebung kann unter anderem Firmware auf virtuellen Zielsystemen ausführen, Speicher- und Hardwarezustände beeinflussen, Fehler injizieren, mehrere Geräte miteinander verbinden und Testergebnisse mit Requirements verknüpfen.

So entsteht ein geschlossener Prozess:

Dokumentation → strukturiertes Wissen → Analyse → Implementierung → Ausführung → Test → Verifikation

Wo ich sinnvoll unterstützen kann

Meine Arbeit ist besonders dann sinnvoll, wenn Sie vor einem Problem stehen, für das normale Applikationsentwicklung allein nicht mehr ausreicht.

Zum Beispiel:

Eine neue Embedded-Plattform soll entwickelt werden, während die Hardware noch nicht vollständig verfügbar ist.

Eine bestehende Firmware soll auf eine andere Hardware, Toolchain oder Entwicklungsumgebung migriert werden.

Ein Team benötigt Unterstützung bei BSP, RTOS, Speicherarchitektur, Interrupts oder anderen Low-Level-Themen.

Tests sind heute zu stark an physische Geräte und Laboraufbauten gebunden.

Ein komplexes Embedded-System soll in CI/CD integriert werden.

Ein großer Bestand technischer Dokumentation soll für KI effizient nutzbar gemacht werden.

LLMs verursachen hohe Kosten, weil dieselben technischen Quellen immer wieder in den Kontext geladen werden.

Oder eine bestehende Codebasis ist so groß und spezialisiert, dass Entwickler und KI zunächst eine gemeinsame, strukturierte Wissensbasis benötigen.

Engineering-Unterstützung dort, wo Software auf das System trifft

Ich arbeite an der Grenze zwischen Software, Compiler, Betriebssystem und Hardware.

Dazu gehören moderne ARM-Systeme von Cortex-M/Thumb-2 bis Cortex-A ebenso wie PowerPC/VLE-basierte Plattformen.

Die konkrete Architektur ist dabei nicht das Produkt.

Entscheidend ist die Fähigkeit, ein reales technisches System so weit zu verstehen und zu modellieren, dass Software darauf entwickelt, migriert, getestet und mit KI-Unterstützung analysiert werden kann.

Wenn Hardware, Toolchain, Betriebssystem, Firmware und Dokumentation gemeinsam betrachtet werden müssen, kann ich genau an dieser Stelle unterstützen.

Wie gitlab im Docker installiert wird

Motivation

Einer meiner Kunden hatte kein Konzept für Code Reviews und einer modernen Versionierungslösung – gearbeitet wird mit IBMs ELM, welches seine Stärken hat, jedoch: Entwickler benötigen GitLab im Entwicklungsprozess, weil es eine integrierte Plattform für Quellcodeverwaltung, CI/CD und Teamzusammenarbeit bietet, die Agilität und Effizienz fördert. GitLab ermöglicht eine schnelle Iteration durch Funktionen wie Merge Requests und automatisierte Tests. IBM ELM hingegen konzentriert sich auf das Management des gesamten Engineering-Lebenszyklus, einschließlich Anforderungen, Tests und Änderungsmanagement, und ist ideal für komplexere Projekte, die strengen Compliance-Vorgaben unterliegen. Die beiden Tools schließen sich nicht aus; tatsächlich können sie gut zusammenarbeiten. Entwickler können GitLab für die Code-Entwicklung und -Bereitstellung nutzen, während ELM die Anforderungen und Tests verwaltet, wodurch eine nahtlose Integration und umfassende Rückverfolgbarkeit innerhalb des Projekts ermöglicht wird.

Voraussetzungen


Docker-Compose: Für die Verwaltung mehrerer Container (Datenbank, Redis, GitLab) ist Docker-Compose erforderlich:

Bash
sudo apt install -y docker-compose
  1. Docker: GitLab wird typischerweise in einem Docker-Container ausgeführt. Stelle sicher, dass Docker auf deinem System installiert ist. Wenn nicht, kannst du es mit den folgenden Befehlen installieren:
Bash
sudo apt update
sudo apt install -y docker.io
sudo systemctl start docker
sudo systemctl enable docker

Um GitLab in einem Linux-Container (z.B. mit Docker) in Betrieb zu nehmen, sind einige Schritte erforderlich. Hier ist eine schrittweise Anleitung zur Installation von GitLab sowie eine Übersicht der benötigten Drittprodukte:

Schritt-für-Schritt-Anleitung zur Installation von GitLab

GitLab-Docker-Image herunterladen: Das offizielle GitLab-Image kann vom Docker Hub heruntergeladen werden. Normalerweise verwenden die meisten Benutzer die gitlab/gitlab-ee oder gitlab/gitlab-ce Version, je nachdem, ob sie die kommerzielle oder Community Edition wünschen.

Bash
docker pull gitlab/gitlab-ce:latest

Docker-Container-Setup: Erstelle ein Verzeichnis für GitLab, das die Konfiguration, die Daten und die Logs speichert.

Bash
mkdir -p ~/gitlab/config ~/gitlab/logs ~/gitlab/data

Docker-Compose-Datei erstellen: Erstelle eine docker-compose.yml-Datei in deinem ~/gitlab-Verzeichnis.

YAML
version: '3'

services:
  gitlab:
    image: 'gitlab/gitlab-ce:latest'
    restart: always
    hostname: 'gitlab.example.com'  # Ersetze dies durch deinen Hostnamen
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://gitlab.example.com'  # Pfad zu deinem GitLab-Server
        gitlab_rails['gitlab_shell_ssh_port'] = 22  # Standard-SSH-Port
    ports:
      - '8080:80'
      - '8443:443'
      - '2222:22'
    volumes:
      - './config:/etc/gitlab'
      - './logs:/var/log/gitlab'
      - './data:/var/opt/gitlab'

Container starten: Mit Docker-Compose kannst du den Container starten und GitLab in Betrieb nehmen.

Bash
cd ~/gitlab
sudo docker-compose up -d

Der -d-Schalter startet die Container im Hintergrund.

Warten auf die Initialisierung: GitLab benötigt einige Minuten zum Starten und zur Initialisierung. Du kannst die Logs überprüfen, um sicherzustellen, dass alles korrekt funktioniert.

Bash
sudo docker-compose logs -f

Zugriff auf GitLab: Nachdem der Container läuft, kannst du GitLab über deinen Webbrowser erreichen. Gib die URL ein, die du in der docker-compose.yml-Datei konfiguriert hast (z.B. http://gitlab.example.com:8080).

Erste Anmeldung: Bei der ersten Anmeldung kannst du den Admin-Benutzernamen und das Passwort festlegen. Das Standardpasswort für den Benutzer root wird in den Logs angezeigt. In meiner Installation ist kein password in den Logs, deswegen zeige ich hier, wie ich das root Passwort geändert habe, funktioniert immer, auch wenn es mal vergessen wurde.

Bash
    docker ps
    docker exec -it gitlab_gitlab_1 bash # that is my container name in step 1
    gitlab-rails console -e production # wait, minutes for another prompt to come
    user = User.where(id: 1).first
    user.password = ‘secret_pass’ # use your favorite password
    user.password_confirmation = 'secret_pass' # again
    user.save!  # save user

Eine Test-EMail kann dann ganz einfach über die Rails-Konsole verschickt werden:

Bash
docker exec -it gitlab_gitlab_1 bash # we are within the docker container 

gitlab-rails console -e production # starting the rails console
Ruby
Notify.test_email('destination_email@address.com', 'Message Subject', 'Message Body').deliver_now # executing a notify test email and immediate delivery

eMail setup

Um Mails zu senden, braucht gitlab_rails die Details über den SMTP-Port/-Servers. Hierzu wird der GITLAB_OMNIBUS_CONFIG erweitert, nicht ersetzt, durch:

environment:
   GITLAB_OMNIBUS_CONFIG: |
        gitlab_rails['smtp_enable'] = true 
        gitlab_rails['smtp_address'] = "***"
        gitlab_rails['smtp_port'] = 587    
        gitlab_rails['smtp_user_name'] = "***"
        gitlab_rails['smtp_password'] = "***"
        gitlab_rails['smtp_enable_starttls_auto'] = true
        gitlab_rails['smtp_tls'] = true    
        gitlab_rails['smtp_openssl_verify_mode'] = 'peer'

Die *** müssen selbstverständlich durch die aktuellen Server Details ersetzt werden.

Notwendige Drittprodukte

  1. Docker: Grundlegende Container-Technologie, die für die Ausführung von GitLab im Container erforderlich ist.
  2. Docker-Compose: Eine hilfreiche Software, um mehrere Docker-Container zu verwalten. Damit kann die Konfiguration und Verwaltung von GitLab und seinen Abhängigkeiten erleichtert werden.
  3. PostgreSQL (optional): GitLab verwendet standardmäßig PostgreSQL als Datenbank. Diese ist im offiziellen GitLab-Image bereits integriert, daher ist keine separate Installation erforderlich.
  4. Redis (optional): Redis wird in GitLab für Caching und als Background-Jobs-Queue verwendet und ist ebenfalls im GitLab-Image enthalten.
  5. Nginx (optional): Wenn du GitLab über HTTPS bereitstellen möchtest, wäre es vorteilhaft, einen Nginx-Server für die SSL/TLS-Konfiguration und das Reverse-Proxy-Setup einzusetzen. Dies kann entweder innerhalb von GitLab oder als separater Container erfolgen.

Fazit

Das Einrichten von GitLab in einem Docker-Container unter Linux ist durch Docker und Docker-Compose relativ unkompliziert. Mit minimalen Anforderungen an Drittprodukte kannst du GitLab effizient in Betrieb nehmen, um als Versionskontrollsystem und CI/CD-Plattform zu fungieren. Achte auf die Konfiguration von Domänen und Portnummern, um einen reibungslosen Zugriff auf deine GitLab-Instanz zu gewährleisten.