SD-Karte als Code-Speicher für RISC-V YRV auf dem Tang-Nano-9K-FPGA
Auf kostengünstigen FPGA-Boards wie dem Tang Nano 9K sind die Syntheseressourcen begrenzt – insbesondere der On-Chip-Speicher (Block RAM) reicht nicht aus, um kompletten Programmcode zu speichern. Lediglich 16–20 KB stehen für Stack und Daten zur Verfügung: ausreichend für Basis-Tests, aber nicht für vollständige Firmware. Dieser Artikel stellt eine Lösung vor, bei der eine SD-Karte als externer, nur lesbarer Speicher (ROM) dient – mit sektorbasierter Lesezugriff auf jeweils 512 Byte. Funktionell ähnelt das dem eXecute-In-Place-Verfahren (XIP) von QSPI-Flash, ist jedoch an die Einschränkungen des SPI-Protokolls angepasst.
Der YRV-Core kommuniziert über einen 16-Bit-Datenbus mit dem Speicher. Die Lese-/Schreiblogik ist in Verilog implementiert:
always @ (posedge clk) begin
if (mem_trans[0]) begin
mem_rdata[31:24] <= mcu_mem_bank3 [mem_addr[13:2]];
mem_rdata[23:16] <= mcu_mem_bank2 [mem_addr[13:2]];
mem_rdata[15:8] <= mcu_mem_bank1 [mem_addr[13:2]];
mem_rdata[7:0] <= mcu_mem_bank0 [mem_addr[13:2]];
end
if (mem_wr_byte[3]) mcu_mem_bank3 [mem_addr_reg[13:2]] <= mem_wdata[31:24];
if (mem_wr_byte[2]) mcu_mem_bank2 [mem_addr_reg[13:2]] <= mem_wdata[23:16];
if (mem_wr_byte[1]) mcu_mem_bank1 [mem_addr_reg[13:2]] <= mem_wdata[15:8];
if (mem_wr_byte[0]) mcu_mem_bank0 [mem_addr_reg[13:2]] <= mem_wdata[7:0];
end
Zustandsautomat für SD-Sektor-Caching
Der RAM wird auf genau 512 Byte reduziert – also genau einen Sektor. Wenn die CPU auf eine Adresse innerhalb des aktuell zwischengespeicherten Sektors zugreift, läuft die Ausführung unmittelbar weiter, wobei mem_ready gesetzt wird (analog zu HREADY im AHB-Lite-Protokoll). Bei einem Cache-Miss – also beim Zugriff außerhalb des aktuellen Sektors – löst der endliche Zustandsautomat (FSM) einen neuen Sektor-Lesevorgang von der SD-Karte aus. Ein Zähler byte_cnt verfolgt den Fortschritt des Lesevorgangs.
Der FSM umfasst drei Zustände:
- IDLE: Wartet auf eine Speicherzugriffsanforderung.
- READ: Liest den angeforderten 512-Byte-Sektor von der SD-Karte.
- START: Initiiert den Lesebefehl (sendet
rd).
Der SD-Controller basiert auf Open-Source-Projekten wie FPGA-SDcard-Reader und dem MIT 6.111-Kurs – und wurde anschließend für die SPI-Schnittstelle des Tang Nano 9K adaptiert (die Leitungen DAT1/DAT2 bleiben unverbunden). Die Initialisierung erfolgt mit 400 kHz für maximale Robustheit – ein Neustart per Stromzyklus ist nicht erforderlich.
Integration in das YRV-Plus-Projekt
Implementiert im Pfad labs/99_experimental/99_03_yrv_sd, aufbauend auf der Basis basics-graphics-music. Die Konfiguration tang_nano_9k_tm1638_sd legt die SD-Pins auf 3,3 V fest. Die Ausgabe nutzt den TM1638-Treiber: port0 steuert die BCD-Segmente; port1 wählt die Stelle des 7-Segment-Anzeigers.
Beispiel-Assemblercode zum Anzeigen von „HELO“ (jedes Zeichen liegt in einem eigenen Sektor):
li s0, 0xFFFF0000 # port0 — Segmentdaten
li s1, 0xFFFF0002 # port1 — Stellenauswahl
main_loop:
# 'H' (Stelle 3)
li t1, 0x8
sh t1, 0(s1)
.rept 100
nop
.endr
li t2, 0b00110111
sh t2, 0(s0)
.rept 1000
nop
.endr
# Analog für E, L, O wiederholen
Die Firmware wird sektorweise mit HxD geschrieben.
Testergebnisse:
- Zwischen den Zeichen werden ca. 1.000 NOPs ausgeführt (repräsentiert realistische Rechenlast).
- „HELO“ wird klar auf vier 7-Segment-Anzeigern dargestellt.
- Zuverlässiger Reset und Start – kein Stromzyklus nötig.
- Entfernen der SD-Karte führt zu sauberem Abbruch der Ausführung.
Kernpunkte
- SD-Karten im SPI-Modus ermöglichen XIP-ähnliche Ausführung, wenn sie mit einem 512-Byte-Sektor-Cache kombiniert werden.
- Der Zustandsautomat minimiert Latenz;
mem_readyhält die CPU während Lesevorgängen synchron. - Ideal für Anwendungen mit geringer Bandbreite, bei denen Signal-Zeitverzögerungen im Millisekundenbereich liegen.
- Für flüssigere Performance empfiehlt sich eine Cache-Erweiterung auf 1–2 KB.
- Vollständig kompatibel mit YRV-Cores auf Gowin-FPGAs – sogar ohne externen SRAM.
Optimierungsmöglichkeiten
Die aktuelle Implementierung benötigt 6+ Taktzyklen pro Sektor-Lesevorgang (im Vergleich zu ebenfalls 6 bei QSPI). Eine Erweiterung des Caches auf 2–4 Sektoren würde Cache-Misses deutlich reduzieren. Auch ein Mehrbank-Buffering – analog zur ursprünglichen Speicherlogik des YRV – ist technisch machbar. Für fortgeschrittene Entwickler: Integration über Wishbone- oder AXI-Adapter ermöglicht den Einsatz dieser Lösung in größeren SoC-Architekturen.
— Editorial Team
Noch keine Kommentare.