CommunityCodierung & Entwicklunggithub.com

Verilog → draw.io:架构图 / 结构框图 / 状态机 / 控制结构

Verilog / SystemVerilog-Code (auch Paper, Patente, mündliche Beschreibungen) in editierbare draw.io-Diagramme umwandeln – mit vier Perspektiven: Architekturdiagramm, Strukturblockdiagramm, Zustandsmaschine und Gesamtsteuerungsstruktur.

Was ist 结构框图 / 状态机 / 控制结构?

Dieses Skill konvertiert HDL-Code, insbesondere Verilog/SystemVerilog, sowie technische Beschreibungen aus Papieren oder Patenten automatisch in vier verschiedene Diagrammtypen im draw.io-Format. Es ist für Entwickler:innen und KI-Agenten gedacht, die Hardware-Designs visualisieren möchten. Der primäre Anwendungsfall ist das Architekturdiagramm (Stil einer Paper-Abbildung), das die Kernidee des Designs hervorhebt. Daneben gibt es ein detailliertes Blockdiagramm auf Gatterebene (aus Yosys-Netlisten), eine Zustandsmaschine (für FSM mit `case(state)`-Struktur) und ein Gesamtsteuerungsdiagramm, das nur Steuersignale zeigt. Der Workflow ist: Der Agent liest den Quellcode (oder die Beschreibung), extrahiert die wesentlichen Komponenten, schreibt eine YAML-Beschreibung, und ein Skript rendert diese deterministisch als draw.io-Diagramm (optional als PNG/SVG). Das Skill analysiert Pipeline-Strukturen, Feedback-Pfade und Steuerungssignale auf intelligente Weise. Es erfordert Yosys, sv2v und Graphviz für einige Diagrammtypen, und eine installierte drawio-Exportfunktion. Die Ausgabe erfolgt als editierbare `.drawio`-Datei, die in Obsidian, Markdown oder direkt in draw.io weiterverarbeitet werden kann.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/BeenLi/claude-skills/tree/HEAD/skills/verilog-diagram

Installed? Explore more Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Verilog → draw.io:Architekturdiagramm / Strukturblockdiagramm / Zustandsmaschine / Steuerungsstruktur

Vier Perspektiven, je nach Frage auswählen: Designgedanke (für Präsentationen, Paper/Reports) → arch2drawio.py (Abschnitt Null, Standard); Gatterebene (Datenpfad pro Einheit) → rtl2drawio.py; Ablauf (einzelner FSM) → fsm2drawio.py fsm; Wer steuert wen (gesamte Steuerungsstruktur) → fsm2drawio.py ctrl. Bei steuerungsorientierten Modulen (FSM, Decoder, Arbitrierungs-Controller) wird das Blockdiagramm unübersichtlich; dann lieber die letzten beiden verwenden.

Abhängigkeiten: PyYAML; Export nach PNG/SVG benötigt drawio (Suchreihenfolge $DRAWIO_BIN → PATH → macOS App → ~/.local/opt/drawio; unter Linux ohne $DISPLAY wird automatisch xvfb-run verwendet, für Server ohne Root-Rechte gibt es tools/install-drawio-linux.sh im Repository). Ohne Export-Tool werden alle Befehle normal .drawio-Dateien erzeugen, nur ohne PNG, mit einem Hinweis in stderr. Blockdiagramm/Steuerungsstruktur benötigen zusätzlich yosys, sv2v (für SystemVerilog), dot (Graphviz) sowie das installierte drawio-skill (standardmäßig ~/.claude/skills/drawio-skill, überschreibbar mit Umgebungsvariable DRAWIO_SKILL_DIR; es verwendet nur dessen autolayout.py, ohne es zu modifizieren). Alle drei Generierungsskripte akzeptieren --png / --svg / --scale N (Standard-Scale für PNG ist 3, ca. 1 Model Unit = 3 px; $DRAWIO_SCALE kann den Standard ändern), auch separater Export möglich: python3 $S/scripts/drawio_export.py X.drawio --png --svg. Für Markdown/Obsidian bevorzugt SVG, da nicht unscharf. $S bezeichnet das Skill-Verzeichnis. examples/ enthält zwei Vergleichsbeispiele: wrr_arbiter.v (einstufig + Zustandsregister), load_pipe.v (mehrstufige Pipeline mit Replay).

Null、Architekturdiagramm im Paper-Stil (arch2drawio.py) — Standard

Eingabe ist Code (oder Paper/Patent/Benutzerbeschreibung). Yosys-Netlisten sind zu detailliert für Architekturdiagramme, daher wird hier nicht synthetisiert: Das Skript gibt eine Quellcodegliederung aus, und Sie (der Agent) lesen den Code, extrahieren das Wesentliche und schreiben eine Diagrammbeschreibung, die das Skript dann deterministisch in ein Diagramm layoutet. Der Benutzer muss keine Beschreibungsdatei schreiben; er sieht nur das Diagramm, es sei denn, er möchte die Beschreibung selbst ändern.

python3 $S/scripts/arch2drawio.py outline src/*.sv --top gpu [--module core]   # Quellcode-Gliederung (ohne Synthese)
python3 $S/scripts/arch2drawio.py render  fig.yaml -o diagrams/arch_core.drawio --png

outline listet: Hierarchiebaum (generate-Vervielfältigungen ×N), Ports, Speicher-Arrays (Tiefe × Breite, Felder von packed structs, Arrays gleicher Tiefe als Hinweis auf „möglicherweise gleiche Tabelle“), nach Stufen benannte sequentielle Register (s1_ / stage2_ / if_id_ …), Zustandsmaschinen, mutmaßliche Rückkopplungssignale (replay/kill/flush/stall/forward/refill…), Datenflüsse zwischen Instanzen (über gemeinsame Signale, bereits ohne clk/reset/Schleifenvariablen). Bei Paper/Patent/Beschreibungseingabe wird dieser Schritt übersprungen.

Zeichenregeln (beim Schreiben der Beschreibung beachten)

  1. Nur das Wesentliche zeichnen: Zuerst den Kerngedanken des Designs in ein bis zwei Sätzen beschreiben, nur die Komponenten erhalten, die ihn widerspiegeln; Klebelogik, clk/reset, Debug-Schnittstellen, vereinzelte Steuerung weglassen. In der Regel 8–20 Kästchen; bei mehr auf zwei Blätter aufteilen (ein Top-Level-Blatt + ein Blatt für die Schlüsseleinheit).
  2. Schwerpunkte setzen: 1–3 wichtige Designpunkte mit hl: true (Kontrastfarbe: dunkler Hintergrund, weiße Schrift) oder bold: true (fett); auch Leitungen auf kritischen Pfaden können mit | hl markiert werden. Wenn ein Bereich hervorgehoben werden soll, groups + hl: true (heller Hintergrundrahmen) verwenden.
  3. Nicht jede Komponente nummerieren: Nur bei Kernkomponenten, die einer textuellen Erläuterung bedürfen, num: N setzen und in callouts erklären.
  4. Kästchenbeschriftungen kurz und prägnant: In das Kästchen nur den Namen schreiben (≤ 2 Zeilen), die Bedeutung wird durch die Form ausgedrückt: Rechenoperationen mit adder / mult / cmp / op (Kreissymbole) oder alu, Auswahl mit mux (Auswahl-Eingang x.sel von unten), Speicher mit table, Warteschlangen mit fifo, Register mit reg, Gatter mit and/or/xor/not.
  5. SRAM-Tabelle: Felder eines einzelnen Eintrags ausschreiben: Bei table mit fields die Bedeutung jedes Felds angeben (z. B. [V, Tag (20b), Data (32b)]), mehrwege-assoziativ mit stack: 4 (gestapelt + ×4); parallele Arrays gleicher Tiefe und Adresse zu einer Tabelle zusammenfassen, jedes Array als ein Feld.
  6. Pipeline: stages definieren die Stufen (id + Funktion sub, z. B. {id: S1, sub: Treffererkennung}), das Skript zeichnet gestrichelte Trennlinien und Stufennamen; Zwischenstufenregister / FIFOs mit between: [S0, S1] angeben, das Skript platziert sie auf der Trennlinie; Rückkopplungen (Ziel in einer früheren Stufe) werden automatisch über einen unteren Korridor zurückgeführt, auch mit | fb oder | fb via=top (von oben). Mehrere Pipelines mit lanes angeben, die Lanes teilen sich die Spalten der Stufen, zeitlich korrespondierende Stufen werden automatisch ausgerichtet.
  7. Für blockweise Diagramme ohne Stufen (Top-Level, SoC) kein stages schreiben; vervielfältigte Einheiten mit stack: N, Ressourcen pro Thread/Kern mit groups umrahmen.

Beschreibungsformat (YAML)

title: dcache_pipe:Direct-Mapped D-Cache Lookup Pipeline
stages: [{id: S0, sub: Auswahl / Array-Lesen}, {id: S1, sub: Treffererkennung}, {id: S2, sub: Fehlzugriff / Wiederholung}]
lanes:  [{id: ld, name: Load-Pipeline}, {id: st, name: Store-Pipeline}]     # optional, bei mehreren Pipelines
nodes:
  - {id: arr, type: table, label: "Cache-Array · 64 Einträge", fields: [V, "Tag (20b)", "Data (32b)"], stage: S0}
  - {id: r1, type: reg, label: addr, between: [S0, S1]}              # liegt auf der Trennlinie S0|S1
  - {id: tcmp, type: cmp, label: Tag-Vergleich, stage: S1, hl: true}      # Kreissymbol "=", Erläuterung darunter
  - {id: rmux, type: mux, stage: S0, ports: 2}
edges:
  - "rmux => arr : idx | w=6"            # -> Daten  => Bus (dick)  ~> Steuerung (gestrichelt);w=N Bitbreite
  - "rpl -> rmux.sel : replay | fb hl"   # .sel/.en von unten, .top von oben;fb Rückkopplung, hl Hervorhebung, via=top, pos=src|dst
groups: [{label: "Pro Thread ×4", members: [rf, alu, lsu], hl: true}]
legend: {reg: Pipeline-Register, fb: Rückkopplung, ctrl: Steuerung, hl: Schlüsseldesign}      # mögliche Schlüssel: data bus ctrl fb hl hl_edge hl_region reg
callouts: {1: "Nur nummerieren, wenn eine erläuternde Beschreibung nötig ist"}

Knotentypen: block reg fifo table(fields, rows, stack) array(dims, cell) mux(ports) adder mult cmp/op(sym) alu and or xor nand nor xnor not cloud mem xbar in out text dot. Allgemeine Attribute: stage / lane / between, hl bold num stack, Feintuning mit row / col (Zeile / Spalte innerhalb der Stufe, auch negativ), dx / dy, w / h. Sonstiges: array unterstützt cell_labels (Zellen-Beschriftungen, z. B. T0..T3) und cell_h (rechteckige Zellen, für Thread-Kanäle); groups unterstützt stack: N (gesamter Bereich gestapelt, z. B. „Core ×2“ mit sichtbarem Inneren) und label_align: right;lanes mit leerem name = nur Spur, keine Beschriftung; Kanten mit | both zeichnen bidirektionale Pfeile (Anfrage/Antwort); Rückkopplung zwischen zwei übereinanderliegenden Einheiten derselben Spalte als enge kurze vertikale Linie (z. B. Dispatcher ⇅ Core).

Ablauf

  1. outline + Lesen des relevanten Quellcodes (oder Lesen von Paper/Patent), Kerngedanke notieren → entscheiden, welche Ebene gezeichnet wird, was die Schwerpunkte sind.
  2. Beschreibung in ein temporäres Verzeichnis (Scratchpad) schreiben, jedem Knoten stage zuweisen (wenn vorhanden). Schleifen automatisch auflösen: Knoten, deren Namen wie Controller klingen (sched / ctrl / fsm / arbiter / dispatch / 调度 / 控制 …, gefolgt von Knoten mit vielen ausgehenden Steuerleitungen), beginnen; ihre Ausgänge gehen nach rechts, Kanten, die zu ihnen zurückführen, werden als Rückkopplung gezeichnet; bei falscher Erkennung für diese Kante explizit | fb angeben. Optional: Layout-Referenz (gpt-image-bridge, erfordert Benutzerbestätigung pro Bild) – der Grid-Layouter stellt korrekte Verbindungen sicher, aber er „komponiert“ kein Layout. Bei Modulen ≥ 10 oder mit offensichtlichen Partitionen (Steuerungsebene/Datenebene/Speicherhierarchie) kann zuerst eine draw.io-Baseline erstellt werden und dann die aus dem Code gewonnene Modulliste und Verbindungen in den Prompt geschrieben werden (Paper-Abbildungsstil, weißer Hintergrund, einzelne Akzentfarbe, Drei-Bereichs-Layout, extrem wenig Text, englische Kurzbeschriftungen), um mit gpt-image-bridge 1 Skizze zu generieren und zu lesen. Nur Layout und visuelle Hierarchie übernehmen (Bereiche, wer über wem, Formation, Weißraum, Position der Schwerpunkte); in lane / row / col / groups umrechnen; Module, Leitungen und Namen müssen ausschließlich dem Code entsprechen – häufige Fehler in Skizzen: falsche Quellen verbunden, zu viele/wenige Module gezeichnet, dunkler Hintergrund. Vor der Generierung gemäß dieser Skill-Regel die Bilder (Zweck, Pfad, Größe, Prompt-Zusammenfassung) auflisten und auf explizite Zustimmung des Benutzers warten; Skizze als diagrams/sketch_*.png speichern, in der Beschreibungsdatei im Kopfkommentar darauf verweisen.
  3. render --png → validate.py (error muss 0 sein; Warnungen zu durch Stufentrennlinien/Registerüberlappungen sind beabsichtigt) → PNG lesen und visuell prüfen, mit row / col maximal zwei Runden Feinjustierung (üblich: Hauptdatenpfad in row 0 gerade durchziehen, Nebenpfad/Steuerung in die nächste Zeile).
  4. Ausliefern .drawio + .png; Beschreibungsdatei im Scratchpad belassen, nur herausgeben, wenn der Benutzer sie selbst ändern möchte.

Beispiele: tiny-gpu Top-Level ~/hardware-project/tiny-gpu/diagrams/arch_gpu.* (Layout angeleitet durch Skizze sketch_gpu_top.png, Vergleich mit arch_gpu_baseline.png ohne Skizze); examples/dcache_pipe.v → dcache_pipe.arch.yaml (aus Code geschriebene Beschreibung) → .png; arch_dual_issue.yaml (Zwei-Pipeline-Ausrichtung + Vorwärtspfad + nummerierte Anmerkungen), arch_mac.yaml (Schaltkreis-Ebene: Multiplikations-/Additionssymbole, Akkumulationsrückkopplung, MUX-Auswahl-Eingang). Blockdiagramm des tiny-gpu Cores siehe ~/hardware-project/tiny-gpu/diagrams/arch_core.*.

Eins、Strukturblockdiagramm (rtl2drawio.py, Gatterebene Details)

python3 $S/scripts/rtl2drawio.py src/*.sv --top gpu --list                     # Module und deren Einheiten
python3 $S/scripts/rtl2drawio.py src/*.sv --top gpu --module alu -o diagrams/alu.drawio --png

Ablauf: sv2v (automatisch bei .sv) → Yosys proc; opt; opt_dff; wreduce; memory -nomap → JSON-Netlist → Einheiten-Pipeline-Analyse → Vereinfachung → Spalten-basiertes Layout. Nach der Generierung python3 ~/.claude/skills/drawio-skill/scripts/validate.py X.drawio ausführen (error muss 0 sein; Warnungen zu durch Stufentrennlinien/Registerüberlappungen sind beabsichtigt), dann PNG lesen und visuell prüfen.

Layout-Regeln (--layout pipeline; Standard auto: Hierarchische Module mit überwiegend Submodul-Instanzen verwenden freies Layout, sonst Pipeline-Stufen-Layout)

  • Eingänge links, Ausgänge rechts, Logik von links nach rechts nach Kombinationstiefe spalten; mehrstufige Pipelines von S0 → Sn links nach rechts, gestrichelte Trennlinien, Spaltenkopf mit Stufennummer.
  • Zwei Arten von Registern:
    • Pipeline-Register (schmaler vertikaler Balken) auf die Stufentrennlinie setzen; die letzte Reihe, die nur Ausgangsports treibt, ist das Ausgangsregister (zählt nicht als Stufe, keine Trennlinie).
    • Zustandsregister (FSM / Zähler / Zeiger: Ausgang geht nur über kombinatorische Logik zurück zu sich selbst) innerhalb der Stufe zeichnen, Ausgang als Rückkopplung.
    • Wenn Registernamen eine Stufennummer enthalten (s2_x, x_s2, stage2_x, if_id_/id_ex_/ex_mem_/mem_wb_, und mindestens zwei verschiedene) gilt der Name – ein Valid-Bit mit Kill, selbst mit kombinatorischer Schleife, ist dennoch ein Pipeline-Register. Wenn das Design nicht nach Stufen benennt, kann nur durch Struktur inferiert werden; die Ankunftszeit der Eingangsports ist unbekannt, die Stufennummer kann zu früh geschätzt werden.
  • Stufenschätzung: Ausgehend von Registern mit Stufennummer, die Stufe einer Einheit = spätestes ankommendes Register-Operand (Maximum); nur von Eingangsports getriebene Logik wird der Stufe zugeordnet, die sie abtastet (ALAP), der zugehörige Eingangsport wird ebenfalls an den Anfang dieser Stufe gesetzt (z. B. TLB-Antwort in S1).
  • Rückkopplung: Signale, die in die Eingangskonus eines Registers mit Stufennummer eintreten, aber nur von späteren Stufen kommen können (replay→S0, kill→S1), sowie Rückkopplungen von Zustandsregistern, werden als rote Linie entlang des unteren Außenkorridors zurückgeführt, weit auseinanderliegende außen; bei mehr als 3 Rückkopplungen zwischen demselben Stufenpaar nur eine dicke Linie als Bündel zeichnen (Beschriftung Rückkopplung ×N: …).
  • Bündelung: Wenn mehr als 4 Pipeline-Register auf derselben Trennlinie liegen, zu einer Registergruppe zusammenfassen (vertikaler Balken mit Beschriftung S1→S2 Register ×N und Gesamtbitbreite).
  • Vereinfachung: Gleichnamige Register-Slices zusammenführen; MUX-Ketten aus verschachtelten if/case zu einem N:1 MUX zusammenfassen; kleine Dekodier-Logik, die nur einen MUX-Auswahleingang / Register-Enable treibt, integrieren und als Auswahl-/Enable-Leitung zeichnen; bis zu 3 Gatter als UND/ODER/NICHT/XOR-Gatter-Symbole zeichnen, größere zu einer „Kombinatorik“-Wolke zusammenfalten (wird an Recheneinheiten aufgeschnitten, um falsche Schleifen zu vermeiden); generate-Schleifen entfaltete gleichartige (gleiche Quellcode-Position) Einheiten und threads[i].x-Instanzen zu „×N“ zusammenfalten (mit Schatten). Alle Zusammenfassungen nur innerhalb derselben Stufe. clk/reset standardmäßig versteckt.
  • Leitungen: durchgezogen = Daten (dick = Mehrfachbus, /N = Bitbreite); lila gestrichelt = MUX-Auswahl; blau gepunktet = Enable; rot = Rückkopplung.

Parameter

ParameterFunktion
--listModule und deren Einheiten auflisten und beenden
--module MWelches Modul zeichnen (parametrisierte Varianten zeichnen die erste, mit Hinweis)
--layout auto|pipeline|freeStandard auto: Bei Modulen mit Submodul-Instanzen und nicht mehr kombinatorischen Knoten als Instanzen (Top-Level-Klebemodul) free (bidirektionale Kommunikation zwischen Blöcken ist keine Pipeline-Rückkopplung), sonst pipeline
--show-clockclk/reset zeichnen
--no-foldgenerate-Instanzen / gleichartige Einheiten nicht falten
--no-collapseSteuerungs-/Kombinatorik-Logik nicht falten
--sv2v auto|always|neverSV-Frontend (nicht zusammengefasste Array-Ports usw. benötigen sv2v)
--keep-json PATHYosys JSON-Netlist behalten

Zwei、Zustandsmaschinendiagramm (fsm2drawio.py fsm)

python3 $S/scripts/fsm2drawio.py list src/*.sv
python3 $S/scripts/fsm2drawio.py fsm  src/*.sv [--module M] -d diagrams --png [--no-actions]
  • Direktes Parsen des Quellcodes, Übergangsbedingungen bleiben als Originalausdrücke erhalten. Erkennt case (S)-Zweige, die S (oder die nächste Zustandsvariable N mit S <= N) zuweisen; Zustandsnamen aus localparam; Konstantenvergleiche mit anderen FSM-Zustandsvariablen in den entsprechenden Zustandsnamen übersetzen (core_state == 3'b001 → core_state == FETCH).
  • Übergangsbedingung = die if/case/for, die direkt die Zustandszuweisung umschließt; else if schreibt nur seine eigene Bedingung; for-Bedingungen als ∃j: …; äußeres for i, das die gesamte case umschließt, bedeutet Vervielfältigung nach i; Guards außerhalb des case (enable, Lese-/Schreibzweige) kommen in einen Kommentarkasten; in Schleifen gesetzte lokale boolesche Variablen werden im Kommentarkasten definiert.
  • Zeichenweise: Jeder Zustand ein gleich großer Kreis (Name bei _ umbrechen, darunter klein Codierung und ⟳), Endzustand Doppelkreis, schwarzer Punkt = Reset-Einsprung. Wenn der Hauptablauf die meisten Zustände abdeckt, als U-förmige Schleife anordnen (obere Reihe links→rechts, runter, untere Reihe rechts→links); verzweigende Typen nach Abstand vom Initialzustand gleichmäßig auf Spalten verteilen. Benachbarte Zustände direkt verbinden; Sprünge über andere Zustände hinweg oben/unten überwölben, Beschriftung im Bogenscheitel; A↔B Hin und Rück je zur Seite gebogen; explizite Selbstschleife oberhalb des Kreises zeichnen. Spaltenbreite automatisch an längste Beschriftung angepasst. Nach / die Zuweisungen beim Übergang (maximal 2 anzeigen).
  • Nicht erkannt: Steuerungen ohne case(state)-Struktur (Zähler-artig, rein kombinatorische Dekodierung) – erscheinen nicht in list, ist beabsichtigt.

Drei、Gesamtsteuerungsstruktur (fsm2drawio.py ctrl)

python3 $S/scripts/fsm2drawio.py ctrl src/*.sv --top gpu --module core -o diagrams/ctrl_core.drawio --png
  • Zeichnet Submodul-Instanzen innerhalb des Moduls L (generate-Instanzen als ×N falten), nur Steuersignale behalten: orange dicke Linie = FSM-Zustandsbroadcast; blau = valid/ready Handshake; lila gestrichelt = start/done/Enable/Auswahl/Reset usw.; Datenbusse weglassen. Instanzenkästchen mit FSM listen die Zustandssequenz auf, Unterebenen mit FSM markieren „enthält FSM“.
  • Steuersignale maximal 3 Ebenen von Basiseinheiten (Komparator, Gatter, Register) zurückverfolgen. Klassifizierung anhand des Signalnamens; bei nicht standardkonformer Benennung können Signale fehlen/überflüssig sein.
  • Empfehlung von oben nach unten: Zuerst Top-Level (wer startet wen, wer handshakt mit Speicher) → nächste Ebene (wie der Hauptsteuer-FSM dispatchen) → Zustandsmaschinendiagramm des kritischen FSM.

Einschränkungen

  • Das Layout wird von Graphviz automatisch nach Spaltenbeschränkungen erstellt; Kreuzungen können nicht vollständig vermieden werden; Feinschliff durch Ziehen in draw.io (alle Objekte sind nativ editierbar).
  • Große steuerungsorientierte Module (hunderte Einheiten) bleiben im Blockdiagramm unübersichtlich; gemäß obiger Anleitung Zustandsmaschinen-/Steuerungsstrukturdiagramm verwenden oder einzeln als Submodule zeichnen.
  • Kreise/Bögen in Zustandsmaschinendiagrammen werden direkt anhand von Koordinaten gezeichnet; beim Ziehen eines Zustands folgen die Bogen-Stützpunkte nicht automatisch, sie müssen nachgezogen werden.

Verwandte Skills