Gam­ma-Mess­ge­rät – 1st Release

Nach­dem der Pro­to­typ nun zuver­läs­sig funk­tio­niert, habe ich das Design des ersten Releases fer­tig­ge­stellt. Es über­nimmt die Funk­tio­nen des Pro­to­typs, ver­zich­tet aber auf nicht not­wen­di­ge Debug-Steck­ver­bin­der und ‑Schnitt­stel­len. Das Tang Pri­mer 25K Basis-Board wird nicht mehr unter­stützt, son­dern ledig­lich das Tang Pri­mer 25K Core-Board. Die­ses Core-Board muß dann extern auf einem Basis-Board pro­gram­miert und umge­steckt wer­den. Phy­si­ka­li­sche seri­el­le Schnitt­stel­len wie USB, Blue­tooth und RS485 sind ent­fal­len. Bei Bedarf müs­sen sie ander­wei­tig ange­schlos­sen wer­den. RxD und TxD wer­den als 3.3V-TTL-Signale zur Ver­fü­gung gestellt.

Der Feh­ler des Pro­to­typs mit dem Leck­strom in den Kom­pa­ra­to­ren wur­de mit einem Ope­ra­ti­ons­ver­stär­ker (hof­fent­lich) aus­ge­bü­gelt. Das Board geht in Kür­ze in Produktion.

Ansich­ten und Design-Dateien

Die 3D-Ansich­ten…

3D-Ansich­ten des ersten Releases

und hier der Schalt­plan und das KiCad-Projekt:

Kurz­be­schrei­bung

Die Dämp­fungs­glie­der am Ein­gang sind jetzt für eine maxi­ma­le Ein­gangs­lei­stung von 27 dBm (500 mW) aus­ge­legt. Dem nega­ti­ven Ein­gang der Kom­pa­ra­to­ren ist nun jeweils ein rausch­ar­mer Prä­zi­si­ons-OpAmp vom Typ OPA376 als Span­nungs­fol­ger vor­ge­schal­tet. Der soll­te die Span­nungs­ver­schie­bung durch den Ein­gangs­leck­strom der Kom­pa­ra­to­ren kom­pen­sie­ren. Für OpAmp und Kom­pa­ra­to­ren wären auch jeweils Vari­an­ten mit zwei Ein­hei­ten in einem Gehäu­se ver­füg­bar. Um eine best­mög­li­che Ent­kopp­lung der bei­den Kanä­le zu errei­chen, wur­de auf deren Ein­satz aber verzichtet.

J7 dient der Span­nungs­ver­sor­gung und der seri­el­len Kom­mu­ni­ka­ti­on mit der Außen­welt. Hier kann zu Test­zwecken ein USB-Seri­ell-Kon­ver­ter direkt ange­steckt wer­den. Um einen gewis­sen Schutz gegen Ver­po­lung zu errei­chen, sind in die Daten­lei­tun­gen seri­el­le Wider­stän­de von 330 Ω ein­ge­fügt. Die Span­nungs­ver­sor­gung ist mit einer Ther­mo­si­che­rung und einer TVS-Diode gesi­chert. J3 und J6 stel­len ein paar Signa­le des FPGAs zur Ver­fü­gung. Wenn alles auf Anhieb funk­tio­niert, sind sie unnötig.

Die Kanä­le wur­den jetzt mit Port1 und Port2 bezeich­net. Sie sind ja gleich­ar­tig auf­ge­baut und die Soft­ware ent­schei­det, wel­cher Port die vor­lau­fen­de und wel­cher die rück­lau­fen­de Span­nung aufnimmt.

Kopp­ler V1.7

Anders als ursprüng­lich geplant wur­de der Kopp­ler nicht auf die­ses Board inte­griert. Statt­des­sen habe ich einen pas­sen­den Kopp­ler ent­wor­fen, nun­mehr die Ver­si­on 1.7, der den pas­sen­den Port­ab­stand hat, so daß er direkt an das Mess­ge­rät ange­schraubt wer­den kann. Hier die 3D-Ansicht:

Anten­nen­kopp­ler V1.7

Für J3 und J4 wer­den tat­säch­lich männ­li­che SMA-Schraub­ver­bin­der mon­tiert, die aber hier man­gels 3D-Modell nicht dar­ge­stellt sind. Der Abstand ist pas­send zum Mess­ge­rät gewählt.

Für die Tra­fos wird hier RG400 Koax­ka­bel mit blau­em Außen­man­tel ein­ge­setzt. Es hat etwa den glei­chen Außen­durch­mes­ser wie RG58, nutzt aber PTFE als Iso­la­tor und einen tem­pe­ra­tur­fe­sten Außen­man­tel. Es brut­zelt daher beim Löten nicht gleich weg.

Erfah­run­gen zum Bau der Trafos

Zwi­schen­zeit­lich habe ich eini­ge wei­te­re Ver­suchs­auf­bau­ten mit den vor­he­ri­gen Kopp­ler­va­ri­an­ten gemacht. Die habe ich bis­her nicht hier doku­men­tiert. Es sieht im Moment so aus, daß die Vari­an­te mit FT 50–61 Ring­kern und nur 20 Win­dun­gen den besten Fre­quenz­gang von 160m bis in den UKW-Bereich hat. Die Kop­pel­dämp­fung beträgt 26 dB (1:20²) was für ein stär­ke­res Signal am Mess­ein­gang sorgt und gleich­zei­tig den Ver­lust gering hält (250 mW Ver­lust bei 100 Watt Sen­der­aus­gangs­lei­stung). Der ein­zi­ge klei­ne Nach­teil ist der, daß auf­grund der rela­tiv nied­ri­gen Induk­ti­vi­tät der induk­ti­ve Wider­stand der Spu­len bei 1,8 MHz grenz­wer­tig ist. Hier kann man wei­ter opti­mie­ren und ent­we­der ein paar weni­ge Win­dun­gen mehr auf­brin­gen (22~24) oder doch wie­der auf den FT 50–43 Ring­kern zurück­grei­fen. Höhe­re Win­dungs­zah­len wür­de ich in bei­den Fäl­len ver­mei­den. Sie trei­ben die Induk­ti­vi­tät hoch und ver­rin­gern die Selbst­re­so­nanz­fre­quenz. Bei­des ist für die 4‑m- und 6‑m-Bän­der ungünstig.

Hier die Meß­er­geb­nis­se ohne wei­te­ren Kommentar:

Gam­ma-Mess­ge­rät – Inbetriebnahme

Ein­lei­tung

Der im vori­gen Bei­trag bereits ange­kün­dig­te RTL-Code für das FPGA und die zuge­hö­ri­ge Firm­ware sind jetzt funk­ti­ons­fä­hig und soweit, daß man bei­des vor­zei­gen kann. Es ist noch der eine oder ande­re Test­code ent­hal­ten, daher ist bei­des noch nicht als „Release“ gekenn­zeich­net. Außer­dem soll es noch ein paar Ände­run­gen in der näch­sten Revi­si­on der Lei­ter­plat­te geben.

Nach ein paar grund­le­gen­den Bemer­kun­gen zum Mess­prin­zip wird nach­fol­gend zuerst der Ver­i­log-Code beschrie­ben, anschlie­ßend die Soft­ware und zum Abschluss wer­den ein paar Bei­spiel­mes­sun­gen mit einem Signal­ge­nera­tor gezeigt. Am Ende des Bei­trags fol­gen noch ein paar Anmer­kun­gen zum PicoRV32-Risc-V-Core.

Das ver­wen­de­te Tool zum Erstel­len des FPGAs ist der Gowin FPGA Desi­gner V1.9.11.01. Für die Soft­ware­er­stel­lung wird GMD (Gowin for Embedded C/C++ Deve­lo­pers), Ver­si­on: 2025.01 verwendet.

Als Refe­renz soll hier zunächst noch­mal das Block­dia­gramm aus dem vori­gen Bei­trag gezeigt wer­den, weil sich die nach­fol­gen­den Erklä­run­gen dar­auf beziehen:

Das Mess­prin­zip

Die vor- und rück­lau­fen­den Ana­log­si­gna­le wer­den in den Kom­pa­ra­to­ren digi­ta­li­siert und anschlie­ßend im FPGA digi­tal aus­ge­wer­tet. Hier ist exem­pla­risch ein Timing­dia­gramm für ein 10-MHz-Signal und eine Abta­stung mit 200 MHz gezeigt:

Oben wird das vor­lau­fen­de Signal und dar­un­ter das reflek­tier­te und um 90° pha­sen­ver­scho­be­ne rück­lau­fen­de Signal gezeigt. Die drit­te und vier­te Kur­ve zei­gen jeweils das digi­ta­li­sier­te Signal und danach ist das exklu­siv-ver­o­der­te Pha­sen­dif­fe­renz­si­gnal dar­ge­stellt, aus dem die Pha­sen­ver­schie­bung bestimmt wird. Das Abtast­si­gnal hat eine deut­lich höhe­re Fre­quenz, in die­sem Fall 200 MHz.

Ein Mess­in­ter­vall dau­ert typi­scher­wei­se zwi­schen 10 und 100 ms. Wäh­rend die­ses Inter­valls wer­den ver­schie­de­ne Zäh­ler unter bestimm­ten Bedin­gun­gen mit dem Takt des Abtast­si­gnals hoch­ge­zählt und am Ende des Mess­in­ter­valls von der CPU aus­ge­le­sen und aus­ge­wer­tet. Beim Start des näch­sten Mess­in­ter­valls wer­den die­se Zäh­ler auto­ma­tisch wie­der gelöscht und star­ten bei null.

Pha­se Dif­fe­rence Coun­ter PDC

Alle Zäh­ler sind im Modul „Reflection_Measurement“ in der Datei „RefMeas.v“ defi­niert. Der Pha­se Dif­fe­rence Coun­ter PDC wird immer dann inkre­men­tiert, wenn das Pha­sen­dif­fe­renz­si­gnal auf high liegt, wenn also bei­de Ein­gangs­si­gna­le ver­schie­den sind. Am Ende des Mess­in­ter­valls zeigt das Ver­hält­nis des PDC-Zäh­ler­stan­des zur Gesamt­an­zahl der Tak­te wäh­rend des Inter­valls also die Pha­sen­ver­schie­bung zwi­schen den Ein­gangs­si­gna­len an.

Edge Coun­ter EC

Die Pha­sen­mes­sung mit dem Pha­se Dif­fe­rence Coun­ter lie­fert ein zwei­deu­ti­ges Ergeb­nis, denn eine nega­ti­ve Pha­sen­ver­schie­bung führt zu dem­sel­ben Zäh­ler­stand, wie eine posi­ti­ve Ver­schie­bung. Die­se Mehr­deu­tig­keit löst der Edge Coun­ter EC auf. Die­ser Zäh­ler wird dann inkre­men­tiert, wenn das rück­lau­fen­de Signal bei einer stei­gen­den Flan­ke des vor­lau­fen­den Signals low ist. Ein wei­te­rer gleich­ar­ti­ger Zäh­ler wird dann inkre­men­tiert, wenn das vor­lau­fen­de Signal bei einer stei­gen­den Flan­ke des rück­lau­fen­den Signals low ist. Am Ende der Mes­sung trifft die CPU eine Mehr­heits­ent­schei­dung und bestimmt so das Vor­zei­chen der Pha­sen­ver­schie­bung. Bei 0° und 180° kommt es hier zu Unsi­cher­hei­ten, die aber im Rah­men der Mess­ge­nau­ig­keit liegen.

Fre­quen­cy Coun­ter FC1 und FC2

Die bei­den Fre­quenz­zäh­ler FC1 und FC2 wer­den immer dann inkre­men­tiert, wenn eine stei­gen­de Flan­ke auf dem jewei­li­gen Ein­gangs­si­gnal fest­ge­stellt wur­de. Aus dem Zäh­ler­stand am Ende der Mes­sung bestimmt die CPU dann die jewei­li­ge Frequenz.

Die­se Fre­quen­zen sind natur­ge­mäß gleich, weil das rück­lau­fen­de Signal ja vom vor­lau­fen­den Signal abge­lei­tet ist. Bei nahe­zu idea­ler Anpas­sung geht aber der Pegel des reflek­tier­ten Signals immer wei­ter gegen null und die gemes­se­ne Fre­quenz genau­so. Der Ver­gleich die­ser Fre­quen­zen ist daher ein Maß für die Zuver­läs­sig­keit der Pha­sen­mes­sung. Wei­chen sie (deut­lich) von­ein­an­der ab, ist die Pha­sen­mes­sung mit den oben genann­ten Regi­stern ungültig.

High und Low Counter

Im unten vor­ge­stell­ten Design sind noch vier wei­te­re Zäh­ler imple­men­tiert, die im Block­dia­gramm nicht gezeigt sind und zukünf­tig ent­fal­len sol­len: F‑High, F‑Low, R‑High und R‑Low. Die High-Zäh­ler wer­den inkre­men­tiert, wenn das ent­spre­chen­de Ein­gangs­si­gnal bei stei­gen­der Flan­ke des Abtast­tak­tes high ist, die (red­un­dan­ten) Low-Zäh­ler dann, wenn das Signal low ist. Wie im Bei­trag „Gam­­ma-Mes­s­­ge­rät – Nach­trag zu den Mes­sun­gen“ beschrie­ben wur­de, gibt es beim Pro­to­typ einen Off­set in der Ver­gleichs­span­nung der Kom­pa­ra­to­ren. Das führt dazu, daß die Umschalt­pe­gel der Kom­pa­ra­to­ren vom Null­durch­gang der Ein­gangs­si­gna­le ver­scho­ben ist. Der Effekt ist nach­fol­gend etwas über­trie­ben dargestellt:

Als Fol­ge davon sind die erkann­ten posi­ti­ven Halb­wel­len kür­zer als die nega­ti­ven Halb­wel­len. Die­ser Feh­ler ist bei hohen Ein­gangs­pe­geln gering, wird aber immer höher, je wei­ter die Pegel sin­ken. Am Ende des Mess­in­ter­valls zei­gen die Zäh­ler­stän­de also die Dau­er der high- und low-Pha­sen an, also die Qua­li­tät des jewei­li­gen Signals.

Wei­te­re Funk­ti­ons­blöcke im FPGA

Inter­vall Timer T1

T1 ist ein ein­fa­cher Inter­vall Timer, der auto­ma­tisch nach einer ein­stell­ba­ren Zeit einen CPU-Inter­rupt gene­riert. Er wird im Bei­spiel­code auf 10 ms ein­ge­stellt. Anders als der in der CPU ein­ge­bau­te Timer, muß er nach dem Aus­lö­sen eines Inter­rupts nicht wie­der neu initia­li­siert wer­den. Damit wird also unab­hän­gig von der Inter­rupt-Ant­wort­zeit ein festes zeit­li­ches Raster erzeugt, zumin­dest solan­ge kei­ne Inter­rupts ver­lo­ren gehen.

Asyn­chro­ner Trans­mit­ter ATx

In der CPU kön­nen zwei ver­schie­de­ne UARTs ange­legt wer­den, ein „simp­le UART“ und ein „WB UART“. Kei­nes davon ist beson­ders gut doku­men­tiert, so daß es mir nicht auf Anhieb gelun­gen ist, einen Inter­rupt aus­zu­lö­sen, wenn der Sen­de­puf­fer leer ist. Da zum Sen­den der Mess­wer­te nur ein Trans­mit­ter nötig ist, schien es mir ein­fa­cher, einen eige­nen asyn­chro­nen Sen­der zu schrei­ben. Ich habe ihn ATx genannt, nicht UART, weil er weder uni­ver­sell ist noch einen Emp­fän­ger beinhal­tet. Immer­hin ist er gepuf­fert und löst einen Inter­rupt aus, sobald der Puf­fer ins Schie­be­re­gi­ster über­tra­gen wird. Die Baud­ra­te ist von der CPU ein­stell­bar. Es wird ein Stopp­bit gene­riert und Pari­ty ist nicht unterstützt.

Der RTL Code

Die oben im Block­dia­gramm gezeig­ten und anschlie­ßend erklär­ten Funk­tio­nen wur­den in Ver­i­log geschrie­ben und für das FPGA syn­the­ti­siert. Hier ist das gesam­te kom­pri­mier­te Gowin_V1.9.11.01 FPGA Desi­gner Pro­jekt inklu­si­ve der vom IP Core Gene­ra­tor erzeug­ten Modu­le. Mit wel­chen Para­me­tern die­se Modu­le erzeugt wur­den steht in src/readme.txt.

Das Top-Level Design fin­det sich in der Datei „Antennenkoppler.v“. Hier wer­den die FPGA-Pins defi­niert, die auf dem Pro­to­ty­pen­board ange­schlos­sen sind. Außer den gene­rier­ten Modu­len wer­den die Modu­le „Reflection_Measurement“, „ATx“ und „Inter­val­Ti­mer“ instan­zi­iert. Deren Quell­code fin­det sich in den ent­spre­chen­den Datei­en im sel­ben Verzeichnis.

Das Top-Level-Modul gene­riert die Reset-Signa­le für die CPU und die Refle­xi­ons­mes­sung, sowie die Signa­le für das Wish­bo­ne-Memo­ry-Inter­face. Es defi­niert auch die Signa­le für die Adress-Deco­die­rung. Wei­te­re Erläu­te­run­gen fin­den sich in den jewei­li­gen Quelltexten.

Außer­dem instan­zi­iert das Top-Level-Modul die PLL, die aus dem Ein­gangs­takt von 50 MHz den Abtast­takt von 200 MHz und den CPU-Takt von 20 MHz erzeugt.

Der IP Core Generator

Exem­pla­risch soll hier die Erzeu­gung des Gowin PicoRV32 CPU-Cores mit dem IP Core Gene­ra­tor gezeigt wer­den. Der IP Core Gene­ra­tor wird ent­we­der unter „Tools“-„IP Core Gene­ra­tor“ gestar­tet oder durch Anklicken des zuge­hö­ri­gen Icons mit den drei Wür­feln auf der Tools-Lei­ste. Er mel­det sich mit einem Ange­bot ver­schie­de­ner Hard- und Soft-Module:

Hier wählt man unter Soft IP-Core den Gowin_PicoRV32 mit einem Dop­pel­klick aus. Es wird eine Ein­ga­be­mas­ke zu Kon­fi­gu­ra­ti­on des gewünsch­ten Funk­ti­ons­um­fangs gestartet:

Durch Dop­pel­klick auf die jewei­li­ge Funk­ti­ons­grup­pe kann die­se aus­ge­wählt (grün hin­ter­legt) oder wie­der ent­fernt wer­den. Bei kon­fi­gu­rier­ba­ren Ele­men­ten öff­net sich ein ent­spre­chen­des Menü. Star­ten wir mit der Kon­fi­gu­ra­ti­on der CPU:

Gowin PicoRV32 CORE

In die­sem Dia­log wird der CPU-Core konfiguriert.

Man kann zunächst in engen Gren­zen den ver­wen­de­ten Befehls­satz aus­wäh­len. Immer aus­ge­wählt ist der RV32I Inte­ger Befehls­satz, optio­nal kann man die kom­pri­mier­ten Instruk­tio­nen (RV32C) und die Unter­stüt­zung für Inte­ger-Mul­ti­pli­ka­ti­on und Divi­si­on (RV32M) zu- oder abwäh­len. Dar­über­hin­aus sind eini­ge Fea­tures wähl­bar, die den Funk­ti­ons­um­fang und die Per­for­mance beein­flus­sen, was jeweils zugun­sten oder zuun­gun­sten der Anzahl ver­wen­de­ter Logik­gat­ter führt. Da es bei dem hier vor­ge­stell­ten System weder Ein­schrän­kun­gen bei den Logik­gat­tern noch bei der Per­for­mance gibt, las­sen wir alles auf den oben gezeig­ten Default-Einstellungen.

ITCM

Mit der etwas unge­wöhn­li­chen Abkür­zung ITCM wird der Befehls­spei­cher für den Pro­zes­sor kon­fi­gu­riert und sein Inhalt festgelegt.

Der Befehls­spei­cher kann 8 kB, 16 kB, 32 kB oder 64 kB groß sein. Für die­ses Pro­jekt belas­sen wir es bei dem Default-Wert von 32 kB.

Als näch­stes muß der Boot-Mode aus­ge­wählt wer­den. Bei den ersten bei­den Modi wird ein SPI-Inter­face gene­riert, über das das Pro­gramm beim Start aus dem exter­nen Flash in das ITCM gela­den wird. Damit ist die Soft­ware­ent­wick­lung qua­si unab­hän­gig von der Hard­ware­ent­wick­lung. Hier ist es im Moment aller­dings ein­fa­cher, die Soft­ware bereits in den FPGA-Bit­stream ein­zu­mas­sie­ren. Dazu wählt man „MCU boot and run in ITCM“ und trägt den Namen der zu ver­wen­den­ten Hex-Datei ein. Das hat aller­dings den Nach­teil, daß die Soft­ware bereits bei Gene­rie­rung des Bit­streams vor­han­den sein und die­ser bei jeder Soft­ware­än­de­rung neu gene­riert wer­den muß.

DTCM

Die Kon­fi­gu­ra­ti­on des Daten­spei­chers erfolgt ana­log zu der des Instruktionsspeichers.

Der Daten­spei­cher wird beim Star­ten durch die CPU initia­li­siert. Ein Hex-File wie beim Instruk­ti­ons­spei­cher ent­fällt daher.

WB UART

Das Wish­bo­ne UART wird tat­säch­lich in dem hier vor­ge­stell­ten Pro­jekt nicht mehr ver­wen­det, aber aus histo­ri­schen Grün­den sind die Signa­le noch defi­niert. Daher muß es enab­led wer­den, um Feh­ler­mel­dun­gen zu ver­mei­den. Es wird in einer zukünf­ti­gen Ver­si­on ent­we­der wie­der benutzt oder ganz entfernt.

WB SPI Master

Das SPI Master Inter­face wird zur Kom­mu­ni­ka­ti­on mit dem exter­nen AD-Wand­ler ADS1118 benö­tigt. Es wird enab­led und fol­gen­der­ma­ßen konfiguriert:

Das SPI-Inter­face wird mit dem CPU-Takt von 20 MHz betrie­ben und der AD-Wand­ler darf mit höch­stens 4 MHz getak­tet wer­den. Um genü­gend Mar­ge zu haben, wird er tat­säch­lich mit 2 MHz betrie­ben, was einem CLOCK_SEL=4 ent­spricht (SPICLK = CPUCLK/(2*(CLOCK_SEL+1))). Shift-Rich­tung, Clock Pha­se und Pola­ri­tät sind vom ADC vor­ge­ge­ben und müs­sen wie hier gezeigt gewählt wer­den, die Kom­mu­ni­ka­ti­on erfolgt mit einer Daten­län­ge von 32 Bit. Die ande­ren Para­me­ter blei­ben auf ihren Default-Werten.

Open WB Interface

Abschlie­ßend wird noch das Open Wish­bo­ne Inter­face enab­led. Es wird für das Lesen und Schrei­ben der Regi­ster in den oben genann­ten Funk­ti­ons­blöcken benötigt.

Ande­re CPU Funktionsblöcke

Simp­le UART, Open AHB Inter­face, WB I2C Master, WB SPI Slave, WB GPIO und ADV SPI-Flash wer­den hier nicht benutzt und dem­entspre­chend disabled.

Gene­rie­ren der kon­fi­gu­rier­ten CPU

Nach­dem nun alle Blöcke wie gewünscht kon­fi­gu­riert sind, kann nun die CPU gene­riert wer­den. Vor dem Klicken auf „ok“ soll­te aber noch der Haken bei „Use Gowin PicoRV32 as top modu­le“ abge­wählt wer­den, denn die CPU ist hier ein Sub-Modul.

Nach dem Klicken auf „ok“ wird noch nach­ge­fragt, ob das exi­stie­ren­de Ver­zeich­nis über­schrie­ben wer­den darf, falls es aus vor­he­ri­gen Durch­gän­gen bereits exi­stiert. Wir klicken auf „yes“ und nun hat er erst­mal 30 Sekun­den zu tun. Dann wird nach­ge­fragt, ob die neu gene­rier­ten Datei­en zum Pro­jekt hin­zu­ge­fügt wer­den sol­len. Das soll­te man beim ersten mal mit „yes“ beant­wor­ten, sonst muß man sie anschlie­ßend manu­ell hin­zu­fü­gen. Bei wei­te­ren Durch­gän­gen ist die Ant­wort egal, denn sie sind ja schon Teil des Projekts.

Syn­the­se, Pla­ce­ment & Routing

Jetzt ist das Pro­jekt bereit zur Syn­the­se. Dabei wer­den die Logik­funk­tio­nen auf­ge­löst und das gesam­te Pro­jekt in Logik­gat­ter umge­wan­delt. Wenn das feh­ler­frei durch­läuft, folgt anschlie­ßend das Pla­ce­ment & Rou­ting. Dabei wer­den die Pins und Gat­ter an die berech­ne­ten Posi­tio­nen plat­ziert und elek­trisch mit­ein­an­der verbunden.

Wenn die Syn­the­se für ein defi­nier­tes Pro­jekt ver­wen­det wer­den soll, dann muß man vor dem Pla­ce­ment zumin­dest die ange­schlos­se­nen Pins im Flo­or­plan­ner zuwei­sen. Nicht ange­schlos­se­ne Pins bekom­men eine Default-Kon­fi­gu­ra­ti­on (meist Input mit pull-up Wider­stän­den). Nicht im Flo­or­plan­ner zuge­wie­se­ne Signa­le wer­den irgend­wo­hin gelegt, wo es dem Pla­ce­ment genehm ist. Das kann man bei ersten Ver­su­chen machen, wenn es noch kei­ne Lei­ter­plat­te gibt. Das auto­ma­ti­sche Pla­ce­ment kann sogar eine Hil­fe bei der Pin­aus­wahl für das Lay­out sein.

Syn­the­se, Pla­ce­ment & Rou­ting benö­ti­gen für die­ses Pro­jekt auf mei­nem PC etwa 2 Minu­ten. Im Ver­lauf wer­den meh­re­re Berich­te erstellt, die man sich gele­gent­lich anschau­en soll­te. Sie kön­nen ins­be­son­de­re bei Feh­lern sehr hilf­reich sein. Auch die Timing Ana­ly­se soll­te man nicht igno­rie­ren. Hier wer­den die maxi­mal mög­li­chen Takt­fre­quen­zen ange­zeigt und zwar in rot, wenn die Vor­ga­ben nicht getrof­fen wer­den. Gera­de in der Ent­wick­lungs­pha­se kann man mal ein Auge zudrücken, wenn die Abwei­chun­gen gering sind und bei­spiels­wei­se nur 195 MHz statt 200 MHz erreicht wer­den, wir arbei­ten ja schließ­lich unter defi­nier­ten Bedin­gun­gen und bei Zim­mer­tem­pe­ra­tur. Sobald das Modul aber für den Außen­be­reich released wird und damit dem gesam­ten Tem­pe­ra­tur­be­reich und womög­lich Span­nungs­schwan­kun­gen aus­ge­lie­fert ist, wür­de ich kei­ne roten Mel­dun­gen mehr tole­rie­ren. Im ungün­stig­sten Moment fällt dann das Modul aus und man hat kei­nen Hin­weis, was schief­ge­lau­fen sein könnte.

Die Soft­ware

Hier zunächst das kom­plet­te Eclipse-Projekt:

Es müss­te direkt über­setz­bar sein, aber gege­be­nen­falls müs­sen Pfa­de ange­passt werden.

Das eigent­li­che Haupt­pro­gramm steht in der Datei „RiscV_Phasenmessung.c“. Im „libra­ry“ Ver­zeich­nis befin­den sich die Quell­codes, die von Gowin zur Ver­fü­gung gestellt wur­den, eini­ge von ihnen wur­den für die­ses Pro­jekt adap­tiert, ins­be­son­de­re die Inter­rupt-Funk­tio­nen in irq.c und irq.h.

Das Haupt­pro­gramm

Im Haupt­pro­gramm wer­den nach der Initia­li­sie­rung loka­ler Varia­blen als erstes die ver­wen­de­ten Peri­phe­rie­ele­men­te im FPGA initia­li­siert. Der asyn­chro­ne Sen­der wird auf 115,2 kBd ein­ge­stellt, der Inter­vall­timer auf 10 ms und ein Mess­zy­klus wird gestar­tet. Anschlie­ßend wer­den die Inter­rupts freigegeben.

Der Inter­vall­timer

Der Inter­vall­timer erzeugt alle 10 ms einen Inter­rupt, der die Funk­ti­on user1_int() auf­ruft. Dort wird im wesent­li­chen der näch­ste ADC Mess­zy­klus gestar­tet und das Ergeb­nis der vor­he­ri­gen Mes­sung gele­sen. Der SPI-Trans­fer zum ADC läuft auto­ma­tisch und die CPU muß nicht auf des­sen Ergeb­nis­se war­ten. Sie wer­den ein­fach bei der näch­sten Gele­gen­heit nach 10 ms abge­holt. Um die Aus­füh­rungs­zeit des Inter­rupt-Hand­lers kurz zu hal­ten, wer­den die Mess­ergeb­nis­se nicht kon­ver­tiert, son­dern als Roh­da­ten abgespeichert.

Die Pha­sen­mes­sung

Die Pha­sen­mes­sung erfolgt wie oben beschrie­ben im FPGA. Sie wird durch Schrei­ben des Con­trol­re­gi­sters gestar­tet und läuft dann auto­ma­tisch ab. Der Sta­tus der Mes­sung wird im Sta­tus­re­gi­ster ange­zeigt, das hier der Ein­fach­heit hal­ber gepollt wird. Ist die Mes­sung been­det, wer­den die Ergeb­nis­se aus­ge­le­sen, for­ma­tiert und die seri­el­le Über­tra­gung wird gestar­tet. Der Daten­trans­fer nach dem ersten Byte erfolgt inter­rupt­ge­steu­ert. Direkt nach dem Start der Über­tra­gung wird der näch­ste Mess­zy­klus gestartet.

Die For­ma­tie­rung der Ergeb­nis­se ist im Moment in les­ba­rer Form gehal­ten, damit man sie direkt in einem Ter­mi­nal­pro­gramm beob­ach­ten kann. Daher wur­de auch auf einen Zei­len­vor­schub ver­zich­tet, es wird also jedes­mal die vor­he­ri­ge Zei­le über­schrie­ben. Das wird in einem end­gül­ti­gen System sicher­lich adaptiert.

Bei­spiel­mes­sun­gen

Nach­fol­gend sol­len eini­ge Bei­spiel­mes­sun­gen z.Zt. noch mit rei­ner Text­aus­ga­be doku­men­tiert werden.

Mes­sung ohne Eingangssignale

Die­ser Block zeigt die gesam­te Text­aus­ga­be nach einem Reset. Die fol­gen­den Bei­spie­le zei­gen jeweils nur die Messung.

-------- Reflection Measurement, under development, date: Jan  8 2026, time: 12:39:23 --------
CPU clock: 20.000 MHz - Sampling clock: 200.000 MHz - Measurement interval: 25 ms
Ff: 0.000 MHz, Fr: 0.000 MHz, Delta_P: +0.00°, High_f: 0.00%, High_r: 0.00%, ADC0=2 mV, ADC1=3303 mV, ADC2=398 mV, ADC3=381 mV, T=+23,750 °C

ADC1 zeigt die Ver­sor­gungs­span­nung in Mil­li­volt, T die aktu­el­le Tem­pe­ra­tur des AD-Wand­lers. Es sind kei­ne Ein­gangs­si­gna­le ange­legt, daher ist die Fre­quenz 0 Hz. An den Aus­gän­gen der log­arith­mi­schen Ver­stär­ker lie­gen knapp 400 mV an.

21.104 MHz, F=0 dBm, R=0 dBm, ∆P=0°

Ff:  21.104 MHz, Fr:  21.104 MHz, Delta_P: +0.99°, High_f: 45.39%, High_r: 45.16%, ADC0=2 mV, ADC1=3303 mV, ADC2=1873 mV, ADC3=1864 mV, T=+25,844 °C

Hier wur­de absicht­lich eine krum­me Fre­quenz von 21.104 MHz gewählt, denn mit­un­ter erge­ben sich bei aus­ge­such­ten Ein­gangs­wer­ten zufäl­lig die gewünsch­ten kor­rek­ten Ergeb­nis­se. Die Fre­quen­zen wer­den kor­rekt gemes­sen und es zeigt sich eine Pha­sen­ver­schie­bung von +0.99°, was im Rah­men der Mess­ge­nau­ig­keit liegt. Die an den LogAmps gemes­se­nen Pegel lie­gen bei 1,87 V mit einer Abwei­chung von etwa 10 mV. High_f und High_r geben die rela­ti­ve High-Zeit des jewei­li­gen Signals wäh­rend der Mess­pe­ri­ode an. Sie lie­gen etwas unter 50%, wodurch die Mes­sung bei die­sem Ein­gangs­pe­gel schon unzu­ver­läs­sig wird.

21.104 MHz, F=20 dBm, R=0 dBm, ∆P=100°

Ff:  21.104 MHz, Fr:  21.104 MHz, Delta_P: +108.90°, High_f: 49.78%, High_r: 45.12%, ADC0=2 mV, ADC1=3303 mV, ADC2=2372 mV, ADC3=1862 mV, T=+26,813 °C 

Wir blei­ben bei der krum­men Fre­quenz und erhö­hen den Pegel des vor­lau­fen­den Signals auf +20 dBm und die Pha­sen­ver­schie­bung auf 100°. Die Fre­quenz­mes­sung bleibt kor­rekt und die Qua­li­tät der vor­lau­fen­den Signals ver­bes­sert sich auf 49.78%, also nahe­zu auf den Idealwert.

21.104 MHz, F=20 dBm, R=20 dBm, ∆P=100°

Ff: 21.104 MHz, Fr: 21.104 MHz, Delta_P: +101.99°, High_f: 49.77%, High_r: 49.65%, ADC0=2 mV, ADC1=3303 mV, ADC2=2371 mV, ADC3=2363 mV, T=+27,531 °C

Jetzt wird auch der Pegel des rück­lau­fen­den Signals auf +20 dBm erhöht. Die Qua­li­tät bei­der Signa­le nähert sich dem Ide­al­wert von 50% und die gemes­se­ne Pha­sen­ver­schie­bung liegt nun 2° neben dem tat­säch­li­chen Wert. Bei­de LogAmps zei­gen nun etwa 2,37 V Aus­gangs­span­nung an.

21.104 MHz, F=0 dBm, R=-15 dBm, ∆P=-100°

Ff:  21.104 MHz, Fr:  21.104 MHz, Delta_P: -103.13°, High_f: 45.24%, High_r: 29.50%, ADC0=0 mV, ADC1=3303 mV, ADC2=1871 mV, ADC3=1484 mV, T=+28,375 °C

Der Vor­lauf-Pegel liegt nun wie­der bei 0 dBm, der Rück­lauf-Pegel bei ‑15 dBm und die Pha­sen­ver­schie­bung wur­de am Signal­ge­nera­tor auf ‑100° ein­ge­stellt. Die Fre­quenz­mes­sun­gen sind nach wie vor kor­rekt, aber die Qua­li­tät des rück­lau­fen­den Signals sinkt auf unter 30%. Es wird also ein erheb­li­cher Teil der posi­ti­ven Halb­wel­le abge­schnit­ten. Trotz­dem weicht die gemes­se­ne Pha­sen­ver­schie­bung nur um gut 3° von der tat­säch­li­chen Ver­schie­bung ab.

21.104 MHz, F=0 dBm, R=-19 dBm, ∆P=-100°

Ff:  21.104 MHz, Fr:  21.104 MHz, Delta_P: -109.76°, High_f: 45.49%, High_r: 15.77%, ADC0=2 mV, ADC1=3303 mV, ADC2=1871 mV, ADC3=1385 mV, T=+27,500 °C

Der Rück­lauf-Pegel liegt jetzt bei ‑19 dBm. Die Fre­quenz­mes­sun­gen sind nach wie vor kor­rekt, aber die Qua­li­tät des rück­lau­fen­den Signals sinkt auf unter 16%.

21.104 MHz, F=0 dBm, R=-20 dBm, ∆P=-100°

Ff:  21.104 MHz, Fr:   0.000 MHz, Delta_P: -81.88°, High_f: 45.30%, High_r: 0.00%, ADC0=2 mV, ADC1=3303 mV, ADC2=1871 mV, ADC3=1358 mV, T=+27,500 °C

Der Rück­lauf-Pegel liegt jetzt bei ‑20 dBm, nur 1 dB weni­ger als bei der vori­gen Mes­sung. Das Signal wird nicht mehr erkannt, die Fre­quenz sinkt auf 0 Hz und die Qua­li­tät auf 0%. Kanal 3 des ADC zeigt noch 1,358 V, es liegt also noch ein hin­rei­chend star­kes Signal an.

Das ist genau das Pro­blem, das einer Nach­bes­se­rung bedarf und im näch­sten Rede­sign beho­ben wer­den soll.

Anmer­kun­gen zur PicoRV32 CPU

Im FPGA wird eine PicoRV32 CPU syn­the­ti­siert. Das ist ein 32-bit-Mikro­pro­zes­sor mit einer Risc-V-Archi­tek­tur. Zum Erstel­len des RTL-Codes wird der „IP Core Gene­ra­tor“ des Gowin FPGA Desi­gners benutzt. Alter­na­tiv kann der gesam­te Ver­i­log-Code auch von Git­Hub her­un­ter­ge­la­den wer­den. Damit hat man alle Frei­hei­ten, inklu­si­ve der Adap­ti­on des Codes, aber eben auch alle Last der kor­rek­ten Kon­fi­gu­ra­ti­on. Wei­te­re Infor­ma­tio­nen zur Risc-V-Archi­tek­tur fin­det man auf deren Web­site.

PicoRV32 Design­zie­le

PicoRV32 ist für die Syn­the­se für ein FPGA opti­miert. Dazu soll der Res­sour­cen­ver­brauch gering sein, die Rechen­lei­stung ist von unter­ge­ord­ne­ter Bedeu­tung. Im hier ver­wen­de­ten FPGA wird bei der Stan­dard­kon­fi­gu­ra­ti­on der CPU unge­fähr ein drit­tel der vor­han­de­nen Logik­gat­ter benötigt.

PicoRV32 Per­for­mance

Fälsch­li­cher­wei­se wird der Begriff RISC oft mit „eine Instruk­ti­on pro Takt“ gleich­setzt, was zwar oft stimmt, aber nicht die eigent­li­che Bedeu­tung ist. RISC bedeu­tet nur, daß der Befehls­satz auf sol­che Ope­ra­tio­nen redu­ziert ist, die ohne zeit­lich unbe­stimm­te Pipe­line-Locks aus­ge­führt wer­den kön­nen. Die Pipe­line darf durch­aus meh­re­re Tak­te für eine Instruk­ti­on benö­ti­gen, sie darf aber nicht bei­spiels­wei­se durch Wait-Sta­tes beim Zugriff auf ande­re Res­sour­cen auf unbe­stimm­te Zeit blockiert wer­den. Es sind nur sol­che Instruk­tio­nen erlaubt, die das sicher­stel­len. Damit fal­len ins­be­son­de­re die Befeh­le weg, die Daten im Spei­cher direkt modi­fi­zie­ren, wie es bei CISC-Pro­zes­so­ren üblich ist. Für Ope­ra­tio­nen die­ser Art wird in einem RISC Pro­zes­sor zunächst der Spei­cher­wert in ein Regi­ster gele­sen, dort modi­fi­ziert und anschlie­ßend zurück­ge­schrie­ben (Loa­d/­Sto­re-Archi­tek­tur). Wäh­rend des Lese- oder Schreib­zu­griffs kann die CPU prin­zi­pi­ell wei­ter­ar­bei­ten, was eine höhe­re Rechen­lei­stung ermög­licht. In einer ein­fach gehal­te­nen CPU darf man Opti­mie­run­gen die­ser Art aller­dings nicht erwar­ten. Auch die etwas kom­ple­xe­ren mathe­ma­ti­schen Ope­ra­tio­nen Mul­ti­pli­ka­ti­on und Divi­si­on benö­ti­gen eini­ge Dut­zend Taktzyklen.

Dhry­s­tone Bench­mark Ergebnisse

Die PicoR­V32-CPU benö­tigt beim Dhry­s­tone Bench­mark 4,1 Tak­te pro Instruk­ti­on, was 0,516 DMIPS/MHz ent­spricht. Das ist für RISC-Ver­hält­nis­se nicht über­wäl­ti­gend, aber in Anbe­tracht des gerin­gen Res­sour­cen­ver­brauch doch erstaun­lich gut. Auf jeden Fall ist es für die typi­schen embedded-Anwen­dun­gen mehr als aus­rei­chend. Eine Takt­fre­quenz von bis zu 50 MHz soll mög­lich sein, hier sind 20 MHz ein­ge­stellt und völ­lig ausreichend.

Inter­rupt Handling

Ein 32-bit RISC-Pro­zes­sor hat oft (aber nicht immer) 32 Regi­ster zur all­ge­mei­nen Ver­wen­dung. Das gilt auch für den PicoRV32. Das ist für vie­le Anwen­dungs­fäl­le groß­zü­gig und bequem, hat aber für Inter­rupt-Hand­ler den Nach­teil, daß beim Start des Hand­lers alle Regi­ster außer R0 gesi­chert und am Ende wie­der­her­ge­stellt wer­den müs­sen. Das dau­ert sei­ne Zeit. Für die hier ver­wen­de­te Vari­an­te RV32IMC wur­den typi­scher­wei­se 700 Tak­te zwi­schen dem Inter­rupt-Request und dem Inter­rupt-Ack­now­ledge gemes­sen. In die­ser Zeit ist der Inter­rupt-Hand­ler gestar­tet, hat die Regi­ster gesi­chert und springt zum Inter­rupt-Code des Anwen­ders. Des­sen Aus­füh­rungs­zeit, das Wie­der­her­stel­len der Regi­ster und der Rück­sprung in das unter­bro­che­ne Pro­gramm kommt zur gesam­ten Aus­füh­rungs­zeit noch dazu.

Es gibt eine RV32E-Ver­si­on, die die­ses Pro­blem ver­rin­gert, indem sie nur 16 Regi­ster unter­stützt. Sie wird aller­dings vom „IP Core Gene­ra­tor“ nicht angeboten.

Der Betrieb mit einem 10-MHz-Takt konn­te für den asyn­chro­nen seri­el­len Sen­der ATx inter­rupt­ge­steu­ert kei­nen kon­ti­nu­ier­li­chen Daten­strom bei 115 kBd errei­chen. Erst durch Takt­er­hö­hung auf 20 MHz wur­de das mög­lich. Das liegt natür­lich auch dar­an, daß in jedem Inter­rupt nur ein Byte in den Sen­der geschrie­ben wird. Mit einem Fifo aus meh­re­ren Bytes lie­ße sich das beheben.

Pro­ble­me

Einen schwer­wie­gen­den Feh­ler im Inter­rupt-Hand­ling konn­te ich loka­li­sie­ren, wenn­gleich noch nicht behe­ben. Zu Beginn der sprintf-Funk­ti­on und ihren Deri­va­ten wird die varia­bel lan­ge Para­me­ter­li­ste aus­ge­wer­tet. Das wird mit va_start() bewerk­stel­ligt und ist unge­wöhn­lich, weil bei fast allen ande­ren C‑Funktionen die Anzahl der Para­me­ter beim Auf­ruf bekannt ist. Wenn zwi­schen dem Funk­ti­ons­auf­ruf und va_start() ein Inter­rupt akzep­tiert wird (z.B. der Timer-Inter­rupt), dann pas­sie­ren schlim­me Din­ge. Als Work­around wer­den daher vor dem Auf­ruf alle Inter­rupts dis­ab­led und nach der Aus­füh­rung von va_start() wie­der enab­led. Das Pro­blem muß natür­lich zügig ana­ly­siert und beho­ben wer­den, aber für den Moment muß der Work­around reichen.

Nach­trag (07.02.2026)

Die Ursa­che des hier beschrie­be­nen Pro­blems ist gefun­den und beho­ben. Es ist ein Feh­ler in dem von Gowin ange­pass­ten Start­up code in der Datei start.S und betrifft den Inter­rupt Hand­ler. Vor dem Rück­sprung in das unter­bro­che­ne Pro­gramm wird der Inhalt des x31 (=t6) Regi­sters über­schrie­ben. Das ist ein tem­po­rä­res Regi­ster, des­sen Inhalt in vie­len Fäl­len nicht rele­vant ist, daher geht es meist gut. Es hat auch nichts mit der printf-Funk­ti­on zu tun, außer daß es da schnell auffällt.

Original-Code:
picorv32_getq_insn(x1, q1)
picorv32_getq_insn(x2, q2)

jal t6, return_from_irq

neuer korrekter Code:
picorv32_getq_insn(x1, q1)
picorv32_getq_insn(x2, q2)

picorv32_retirq_insn()

jal t6… springt unnö­ti­ger­wei­se in eine C‑Funktion und spei­chert die Rück­sprung­adres­se in t6. Ein Rück­sprung erfolgt aber nicht, denn es ist kein Funk­ti­ons­auf­ruf son­dern die Rück­kehr zum unter­bro­che­nen Pro­gramm. Das erle­digt die retirq Instruktion.

Sehr hilf­reich bei der Feh­ler­su­che war der Ori­gi­nal­code des PicoRV32 und das dazu­ge­hö­ri­ge readme.md-File.

Next Steps…

Ich wer­de in den näch­sten Wochen ein Rede­sign der Lei­ter­plat­te ange­hen. Dabei soll das Pro­blem der Digi­ta­li­sie­rung der Ein­gangs­si­gna­le beho­ben und die Lei­ter­plat­te deut­lich ver­klei­nert wer­den, denn die vie­len Test­mög­lich­kei­ten des Pro­to­ty­pen sind dann nicht mehr nötig.

Bis die Lei­ter­plat­te dann auf dem Tisch liegt, soll die PC-Soft­ware so ange­passt wer­den, daß der Daten­strom ana­ly­siert und das Mess­ergeb­nis im Smith-Dia­gramm dar­ge­stellt wird.

Sipeed Tang FPGA Series

Anläss­lich mei­ner Über­le­gun­gen zur Mes­sung des kom­ple­xen Refle­xi­ons­fak­tors oder all­ge­mei­ner eines Gam­ma-Mess­ge­rä­tes hat­te ich mich auf die Suche nach aktu­el­len CPLDs oder FPGAs bege­ben, die heut­zu­ta­ge für Ama­teur­bud­gets in Ein­zel­stück­zah­len erhält­lich sind. Da man dafür sowie­so nicht beson­ders vie­le Pins braucht, wäre ein wei­te­rer Wunsch, ein klei­nes QFP-Gehäu­se, das man mög­lichst noch von Hand löten kann. Ein 44- oder 64-Pin QFP mit 0,65 mm Pin-Pitch, not­falls 0,5 mm, wäre also bevor­zugt. Am näch­sten kam dem ein 5M160ZE64 von Alte­ra, die inzwi­schen wie­der von Intel geschie­den sind. Es hat aber 0,4 mm Pin­ab­stand und das traue ich mir nicht mehr zu, von Hand zuver­läs­sig zu löten.

Bei die­ser Suche stieß ich auf FPGAs der chi­ne­si­schen Fir­ma Gowin, die mir sehr inter­es­sant erschie­nen. Die eben­falls chi­ne­si­sche Fir­ma Sipeed setzt die­se FPGA auf ihren Tang-Boards ein, die zu sehr gün­sti­gen Prei­sen über die bekann­ten Platt­for­men gekauft wer­den können.

Die benö­tig­ten Ent­wick­lungs­werk­zeu­ge für die Soft­ware- und FPGA-Ent­wick­lung sind kosten­los von der Gowin-Web­site her­un­ter­zu­la­den. Sowohl die­se Werk­zeu­ge wie auch die Daten­blät­ter der Bau­tei­le sind abso­lut „Sta­te-of-the-Art“, geschrie­ben oder gegen­ge­le­sen und kor­ri­giert von eng­li­schen Mut­ter­sprach­lern. Kein Ver­gleich zu dem, was vor 40 Jah­ren aus Japan kam. Die Soft­ware-Tools basie­ren auf Eclip­se und die FPGA-Tools sind selbst geschrie­ben. Wer schon­mal mit Quar­tus gear­bei­tet hat, wird sich schnell zurechtfinden.

Die SW-Tools set­zen auf den GNU-Tools auf und unter­stüt­zen sowohl den ARM Cor­tex als auch die RISC‑V Archi­tek­tur. Die FPGA-Tools unter­stüt­zen Ver­i­log 95, Ver­i­log 2001, System-Ver­i­log 2017 und VHDL.

Ich habe mir die genann­ten Ent­wick­lungs­werk­zeu­ge instal­liert und zwei Tang Nano 4K-Boards, ein Tang Pri­mer 25K Dock Base Board (ent­hält ein Core Board) und dazu ein wei­te­res Core Board besorgt und damit ein paar Wochen herumgespielt.

Zum Ein­stieg will ich nach­fol­gend kurz mei­ne Erfah­run­gen beschrei­ben. Wer sel­ber ein­stei­gen will, wird nicht an der Ori­gi­nal-Doku­men­ta­ti­on der oben ver­link­ten Sei­ten vor­bei­kom­men. Zur Ein­ar­bei­tung wird auch je nach Vor­kennt­nis­sen ein gewis­ser Zeit­auf­wand not­wen­dig sein, den ich nie­man­dem abneh­men kann.

Tang Nano 4K

Tang Nano 4K ist ein Board der Grö­ße 60 mm x 22,86 mm, das als zen­tra­len Chip den GW1NSR-LV4C einsetzt.

Das Board ist offen­sicht­lich ursprüng­lich für Gra­fik­an­wen­dun­gen ent­wickelt wor­den, denn es hat sowohl einen HDMI-Anschluß für einen Moni­tor als auch ein DVP-Inter­face zum Anschluß einer Kame­ra. Außer­dem hat es einen 32 MBit Flash-Spei­cher und ein USB-Inter­face sowie zwei Taster und eine LED. Vie­le Signa­le des GW1NSR-LV4C sind auf eine der bei­den 22-poli­gen Stift­lei­sten her­aus­ge­führt, man muß aber stets dar­auf ach­ten, daß sie nicht schon ander­wei­tig für die Kame­ra oder den Moni­tor benutzt sind. Außer­dem haben sie unter­schied­li­che Signal­pe­gel, mal 1,8 V, mal 2,5 V und mal 3,3 V. Das schränkt die Anwen­dungs­fäl­le erheb­lich ein, aber als Star­ter­board zum Ein­ar­bei­ten in die Ent­wick­lungs­werk­zeu­ge ist es den­noch sehr hilfreich.

Tang Nano 4K ist ohne Kame­ra für etwa 15 Euro und inklu­si­ve HDMI Kame­ra für etwa 20 Euro über Ali­ex­press zu bezie­hen. Wem das zu teu­er ist, der kann sich auch den Tang Nano 1K mal anschau­en, der etwa 8 Euro kostet.

GW1NSR-LV4C

Der GW1NSR-LV4C ist der zen­tra­le Chip auf dem Tang Nano 4K-Board. Er hat als Hard-Core einen ARM Cor­tex-M3 an Bord (80 MHz max), sowie ein FPGA mit 4608 LUT4 Zel­len. Das sind „look-up Tabel­len“ mit jeweils vier Ein­gän­gen und einem Aus­gang. Sie kön­nen vier Ein­gangs­si­gna­le in belie­bi­ger Wei­se ver­knüp­fen und dar­aus das Aus­gangs­si­gnal erzeu­gen. Für wei­te­re Details der Archi­tek­tur kon­sul­tie­re man das Daten­blatt. Aller­dings ist die inter­ne Archi­tek­tur für den Anwen­der trans­pa­rent. Man muß nicht unbe­dingt wis­sen, wie der Chip intern funk­tio­niert, die Tools über­neh­men das sozu­sa­gen auf Knopfdruck.

Wei­te­re inter­es­san­te Bau­grup­pen auf dem Chip sind 180 kBit SRAM, 256 kBit Flash-Spei­cher, 64 MBit PSRAM, 64 MBit Hyper­RAM und 32 MBit NOR Flash. Außer­dem hat er 16 18 bit x 18 bit Mul­ti­pli­zie­rer, zwei PLLs (clkout 480 MHz max) und zwei DLLs ein­ge­baut, sowie einen Oszil­la­tor (125 MHz typ.) mit +/-5% Tole­ranz. Die vier I/O‑Bänke kön­nen jeweils mit Span­nun­gen zwi­schen 1,2 V und 3,3 V betrie­ben werden.

Hier ein Foto mit ange­schlos­se­nem Logik­ana­ly­sa­tor für Testzwecke.

Die Kom­mu­ni­ka­ti­on mit dem PC erfolgt über die USB-Buch­se auf der rech­ten Sei­te, die gleich­zei­tig die Span­nungs­ver­sor­gung des Boards bereitstellt.

Tang Pri­mer 25K

Viel inter­es­san­ter als der Tang Nano ist für mich der Tang Pri­mer 25K. Dabei han­delt es sich um zwei sepa­ra­te Lei­ter­plat­ten, das „Dock Base Board“ und das auf­ge­steck­te „Core Board“. Das Core Board ent­hält das eigent­li­che FPGA vom Typ GW5A-LV25MG121NC1/I0, einen 50 MHz Quarz­os­zil­la­tor, die Span­nungs­ver­sor­gung und ein 64 MBit NOR-Flash zum Spei­chern des FPGA-Inhalts inklu­si­ve gege­be­nen­falls des Codes für eine im FPGA imple­men­tier­te Soft­co­re-CPU. Auch die Tang Pri­mer Boards sind über Ali­ex­press bestell­bar. Der aktu­el­le Preis (2025) liegt bei etwa 20 Euro für das Core-Board und bei etwa 30 Euro für das Basis­board inklu­si­ve einem Core-Board.

Tang Pri­mer 25K Core-Board

Das Core-Board hat eine Grö­ße von nur 23 mm x 18 mm. Hier ein Grö­ßen­ver­gleich mit einer SMA-Buchse:

Die Ver­bin­dung mit dem Basis­board oder einem selbst zu bau­en­den Appli­ka­ti­ons­board erfolgt über zwei jeweils 60-poli­ge Sub­minia­tur Steck­ver­bin­der aus der DF40-Serie von Hiro­se. Sie haben einen Pin­ab­stand von 0,4 mm und damit ist ein zuver­läs­si­ges „Sel­ber­lö­ten“ nicht mehr mög­lich. Man wird also auf einen Bestücker zurück­grei­fen müs­sen und glück­li­cher­wei­se hat JLCPCB die pas­sen­den Sockel in sei­nem Lagerbestand.

Das Core-Board benö­tigt eine Ver­sor­gungs­span­nung von 3,6 V bis 5,5 V und erzeugt dar­aus mit meh­re­ren Schalt­reg­lern alle benö­tig­ten wei­te­ren Span­nun­gen: 0,9 V, 1,2 V, 1,8 V, 2,5 V und 3,3 V. Die 0,9 V und die 1,2 V wer­den nur zur Ver­sor­gung des FPGAs benö­tigt und die 1,8/2,5/3,3V wer­den auf die Steck­ver­bin­der geführt. Auch die I/O‑Spannungsversorgungen der sechs GPIO-Bän­ke wer­den auf die Steck­ver­bin­der geführt, so daß das Basis­board die­se Bän­ke über Lei­ter­bahn­brücken mit der benö­tig­ten Span­nung ver­sor­gen kann, ohne sie selbst erzeu­gen zu müssen.

Tang Pri­mer 25K Base-Board

Das Tang Pri­mer Base-Board stellt eine hand­hab­ba­re Hard­ware-Ent­wick­lungs­um­ge­bung zur Verfügung.

Über die USB-Buch­se auf der rech­ten Sei­te wird das Board mit einem Host-PC ver­bun­den und mit der Betriebs­span­nung ver­sorgt. Über die­se USB-Buch­se ist das FPGA direkt pro­gram­mier­bar und alter­na­tiv kann der Bit­stream in den Flash-Spei­cher gela­den wer­den, aus dem das FPGA sich nach dem Ein­schal­ten selbst konfiguriert.

Außer­dem sind vie­le Signa­le des FPGAs auf 100-mil-Stift- und Buch­sen­lei­sten her­aus­ge­führt. Damit sind Test­auf­bau­ten wie die­ser hier leicht möglich.

Hier ist ein Steck­board mit zwei auf­ge­steck­ten SMA-Buch­sen gezeigt, das jeweils einen 50 Ω Abschluß­wi­der­stand ent­hält und dann mit mög­lichst kur­zen Dräh­ten an die Stift­lei­ste ver­bun­den wird. Auf die SMA-Buch­sen sind BNC-Adap­ter auf­ge­schraubt, an die ein Signal­ge­nera­tor ange­schlos­sen wird. Auch der oben schon gezeig­te Logik­ana­ly­sa­tor wird hier wie­der ver­wen­det. Das Kabel, das nach rechts oben abgeht, führt zu einem USB-RS232-Kon­ver­ter. Er ist einer der simp­len Art, der nur RxD, TxD und +5V zur Ver­fü­gung stellt. Er funk­tio­niert gleich­wohl her­vor­ra­gend und wird regel­mä­ßig pro­blem­los mit 115 kBd betrieben.

Das GW5A-LV25MG121NC1/I0-FPGA

Das hier ver­wen­de­te Gowin-FPGA vom Typ GW5A hat 23040 logi­sche Ein­hei­ten, bestehend aus jeweils einer Look­up-Tabel­le mit vier Ein­gän­gen und einem Flip­flop. Außer­dem sind 56 SRAM-Blöcke mit ins­ge­samt 1008 KBit und zusätz­lich noch 180 KBit ver­teil­tes SRAM imple­men­tiert. Dar­über­hin­aus gibt es 28 DSP-Blöcke, jeweils bestehend aus meh­re­ren Hard­ware-Mul­ti­pli­zie­rern und ‑Addie­rern sowie einer 48-bit-ALU. Sechs PLLs sor­gen für ver­schie­de­ne benö­tig­te Takt­ra­ten. Die Refe­renz­ein­gangs­fre­quenz muß zwi­schen 19 MHz und 800 MHz lie­gen und die VCO-Fre­quenz liegt zwi­schen 800 MHz und 1600 MHz. Aus der ein­ge­stell­ten VCO-Fre­quenz wird durch Fre­quenz­tei­ler die benö­tig­te Aus­gangs­fre­quenz erzeugt.

FPGA-Ent­wick­lung

Als Soft­ware-Ent­wick­lungs­um­ge­bung wird das weit­ver­brei­te­te Eclip­se mit GNU-Tools ein­ge­setzt. Das soll hier nicht wei­ter bespro­chen wer­den, detail­lier­te Hil­fe gibt es im Inter­net. Wer, so wie ich, bis­her noch nicht damit gear­bei­tet hat, wird sich schnell zurecht­fin­den, soll­te aber „ein paar Tage“ zur Ein­ar­bei­tung ver­an­schla­gen. Die Gowin-Doku­men­ta­ti­on ist auch hier sehr hilf­reich, ins­be­son­de­re wenn es um die Kon­fi­gu­ra­ti­on für die ver­wen­de­te Ziel­ar­chi­tek­tur geht, also ARM Cor­tex oder RiscV.

Der Gowin FPGA-Desi­gner unter­stützt Ver­i­log 95, Ver­i­log 2001, System-Ver­i­log 2017 und VHDL. Die Logik­syn­the­se läuft sehr schnell, Pla­ce­ment und Rou­ting (P&R) braucht natur­ge­mäß etwas län­ger. Ein kom­plet­ter Durch­lauf für ein Design mit einem PicoR­V32-Soft­co­re, UART und Wish­bo­ne-Bus, einer PLL und eini­ger 32-bit Regi­ster und Glue-Logik dau­ert etwa 1 Minu­te. Ein Simu­la­tor ist nicht vor­han­den, aber man kann auf kosten­lo­se Simu­la­to­ren ande­rer Ent­wick­lungs­werk­zeu­ge aus­wei­chen. In mei­nem Fall ist das Model­sim, das bei der Intel/Altera Ent­wick­lungs­um­ge­bung dabei ist.

Der IP Core Gene­ra­tor soll noch kurz erwähnt wer­den. Er ist Teil des Gowin FPGA Desi­gners und gestat­tet die Kon­fi­gu­ra­ti­on eini­ger vor­de­fi­nier­ter Bau­stei­ne. Damit wird bei­spiels­wei­se der RiscV-Soft­co­re, die PLLs oder die DSPs kon­fi­gu­riert. Hier die Kon­fi­gu­ra­ti­on des CPU-Cores:

Für den RiscV-Core wählt man außer dem Befehls­satz auch die anzu­schlie­ßen­den Spei­cher und das Bus-Inter­face (AHB oder Wish­bo­ne). Außer­dem sind eini­ge für Micro­con­trol­ler typi­sche Peri­pherals wähl­bar: GPI­Os, UART, I2C-Master, SPI Master oder Slave oder SPI-Flash-Inter­face. Das sieht dann so aus:

Ich habe damit jetzt eini­ge Wochen her­um­ge­spielt und bin außer­or­dent­lich zufrie­den. Die Tools arbei­ten sehr sta­bil und zuver­läs­sig, kei­ner­lei uner­klär­li­ches Fehl­ver­hal­ten, das dann nach Neu­start des PCs genau­so uner­klär­lich wie­der weg ist. Das bin ich bei ande­ren Embedded Ent­wick­lungs­um­ge­bun­gen anders gewohnt, irgend­was ist ja immer. Auch das Her­un­ter­la­den des Bit­streams zum FPGA funk­tio­niert zuver­läs­sig und ohne Mur­ren. Wenn etwas schief­ging, wuss­te ich bis­her immer, wor­an es lag, mei­stens an mir.

Mit dem oben schon gezeig­ten Test­auf­bau habe ich einen digi­ta­len Pha­sen­ver­glei­cher gebaut, der mit den zuge­ge­ben sau­be­ren Signa­le eines Test­ge­ne­ra­tors Pha­sen­ver­schie­bun­gen zwi­schen zwei Signa­len von 0,1° auf­lö­sen kann. Die Abwei­chung vom tat­säch­li­chen Wert lag nach mei­nen Beob­ach­tun­gen zwi­schen 1° und 2°, was an dem pro­vi­so­ri­schen Auf­bau lie­gen kann. Eine Anwen­dung für die­sen Pha­sen­mes­ser könn­te ein Gam­ma-Meß­ge­rät sein, des­sen Pro­to­typ ich hier schon­mal vor­ge­stellt, aber bis­her nicht ver­wirk­licht habe.

Ich wer­de noch etwas opti­mie­ren, sowohl Ver­i­log- als auch C‑Code noch etwas berei­ni­gen und dann die Ergeb­nis­se hier vorstellen.