Forum: FPGA, VHDL & Co. Wired-Or-Bus statt TriState-Bus


von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Hallo,

habe derzeit ein Design mit Registern, die über einen rein internen 
Tristate bus gelesen werden.

Das sieht so aus:
(Din und Dout sind jeweils 32 Bit breit)
1
goutputbit: for i in Reg.lower to Reg.upper generate
2
  Dout(i) <= Din(i) when Reg.Adr = Adr else 'Z';
3
end generate;

Von diesen Registern kann es mehrere pro 32 Bit geben und insgesamt sind 
es mehrere hundert im gesamten Design, verteilt über viele Hierarchien.

Gebaut werden soll hier halt ein Wired-Or, das durchgängig entweder 0 
oder 1 liefert für nicht definierte Bits/Adressen.
Ob 0 oder 1 ist mir egal, soll nur konsistent sein.

Timing-technisch ist das gar kein Problem, für die Simulation und die 
Altera/Intel und Synplify Synthese auch nicht.

Vivado hingegen hat damit ein starkes Problem:
- befindet sich Dout in einem Record, dann baut er den Bus manchmal so 
das dieser immer nur 0 liefert, egal wo man liest
- ist der Bus auf dem Toplevel verfügbar, dann baut er den Bus manchmal 
so das dieser immer nur 0 liefert, egal wo man liest
- es gibt unzählige Warnungsmeldungen

Mit alle dem konnte ich noch irgendwie leben. Seit heute jedoch läuft 
die Synthese gar nicht mehr durch: Endlosschleife oder Crash.


Deshalb: eine neue Lösung muss her.

Derzeit fällt mir nur ein jedes Register von Hand pro Entity zu 
verodern.
Das ist so hässlich und fehleranfällig, das ich wohl lieber eine 
automatische Konvertierung der Sourcefiles extra für Vivado bauen würde, 
eine Art Präprozessor. Spaß macht das aber bestimmt auch nicht.

Hat jemand eine bessere Idee wie das funktionieren kann?
Wie löst ihr das?

: Bearbeitet durch User
von Christoph Z. (christophz)


Lesenswert?

Robert P. schrieb:
> Wie löst ihr das?

On-chip-bus. Da gibt es diverse zum Auswählen, die von verschiedenen 
Ecken/Firmen herkommen. Unsere Firma hat noch einen eigenen Bus 
erfunden, den wir verwenden "dürfen". Vielleicht können wir das in 
Zukunft ändern.

von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Naja, für sowas simples wie einzelne Register und wired-or will ich mir 
eigentlich nicht AXI oder ganz allgemein fremde IP quer durchs FPGAs 
legen.

Habe jetzt doch erstmal alles auf Wired-or händisch umgebaut. Bläht die 
Module nicht so immens auf und die Synthese funktioniert wieder ohne 
Warnungen.
Zwar crasht Vivado immer noch, was aber wohl doch an etwas anderem 
liegt.

Zumindest kann ich jetzt "Flatten Design" abschalten und damit die 
Designphase überspringen welche crasht, was mit Tristate-Bus nicht geht, 
weil es sonst harte multiple-drivers Fehler gibt.

von Klakx (Gast)


Lesenswert?

für einen internen Tristate-Bus sollte es schon einen ganz triftigen 
Grund geben. Falls dieser überhaupt implementierbar ist im FPGA, was ich 
stark bezweifle. Selbst mein alter Prof an der Uni hielt schon davon 
nichts. In der Regel eigentlich immer eine mittelgroße Katastrophe.

Wired-OR Bus kann man machen. Das kenne ich auch für kleine Designs. Der 
Spaß hört dann auf, wenn der Bus zu groß wird. Ein APB oder Wishbone 
sind ein netter Zwischenschritt. Damit sind deine Cores auch wesentlich 
wiederverwendbarer. Ein Hauptgrund für einen Standard.

von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Danke erstmal!

Ja, der interne TriStateBus ist nicht implementierbar, zumindest die 
Synthesewerkzeuge die ich bisher verwendet habe (Quartus und Synplify 
Pro) bauen daraus aber genau das was man haben will. Scheint wohl also 
nicht völlig abstrus zu sein.

Mein Hauptanliegen ist wirklich die Lesbarkeit und Wartbarkeit. Zudem 
möchte ich es gern kompakt haben.
Deswegen will ich da auch ungern Wishbone/AXI verwenden, das wird für 
diese Anzahl an Registern ja nur noch aufwändiger und umständlicher.

Soweit ich das weiß(kann mich irren, habe bisher nur AXI rudimentär 
verwendet) hätten die beide auch große Probleme mit Registern die auf 
der gleichen Adresse liegen, aber unterschiedliche Bits aus dem 32 Bit 
Datenbus bedienen und in völlig verschiedenen Entities zu finden sind.

Bevor Fragen aufkommen: die Registerbeschreibung ist fix vorgegeben, 
d.h. ich kann das nicht einfach "schöner" anordnen.


Meine aktuelle Beschreibung braucht:
- einen Eintrag in der Portliste: ein Inout Signal bei dem im Record 
alles drin ist: datain, dataout, adresse, byteenable, RnW, enable, done
- eine Zeile für jedes Register: eine Instanzierung der Registerentity 
mit Clk, dem Bus, und den 2 Datenports. Über ein Generic wird angegeben 
welches Register.

Beispiel:
1
regbus : inout proc_bus_type;                  
2
...
3
signal forcedBlank : std_logic_vector(RegForcedBlank.upper downto RegForcedBlank.lower);
4
...
5
iforcedBlank : entity work.eProcReg generic map (RegForcedBlank) port map(clk100, regbus, forcedBlank, forcedBlank);

Das finde ich halt persönlich sehr elegant und gut verständlich und 
wartbar.

Es scheint aber so das es wohl keine Alternative gibt, die eine ähnlich 
kompakte Schreibweise ermöglicht und nur "Basisfunktionen" von VHDL 
benutzt UND von allen Synthesetools beherrscht wird.

von Klakx (Gast)


Lesenswert?

Robert P. schrieb:
> Mein Hauptanliegen ist wirklich die Lesbarkeit und Wartbarkeit.

Dann würde ich mir keinen Tristate-Bus mit inout-Signalen antuen.

Zeig mal ein vollständiges Beispiel mit entity+architecture und wenn 
noch ein Submodul aufgerufen wird, dann das gleiche dazu.

Robert P. schrieb:
> Soweit ich das weiß(kann mich irren, habe bisher nur AXI rudimentär
> verwendet) hätten die beide auch große Probleme mit Registern die auf
> der gleichen Adresse liegen, aber unterschiedliche Bits aus dem 32 Bit
> Datenbus bedienen und in völlig verschiedenen Entities zu finden sind.

Am Ende willst du doch eine Registermap als Komponente. Die kann man für 
jeden x-beliebigen Bus schreiben. Die internen Interface-Signale können 
dann auch an andere Module gehen.
Klar geben viele Interfaces byte-Schreibzugriffe vor. Das hat aber den 
Vorteil, dass der der die Software schreibt noch durchsieht. Wenn mich 
das stört dann lege ich die zwei Register nicht auf die gleiche Adresse.

Richtig wartbar wird es, wenn dir ein Skript das VHDL-Modul schreibt 
(..und gleich den C-Header, die Doku und das Package für den Test). 
Elegant ist es auch, wenn die Tools dich nicht mit Warnings überhäufen.

von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Hier aus einem fertigen Projekt.

Busbeschreibung und generisches Registermodul:
https://github.com/MiSTer-devel/GBA_MiSTer/blob/master/rtl/proc_bus_gba.vhd

Registerbeschreibungs-Package:
https://github.com/MiSTer-devel/GBA_MiSTer/blob/master/rtl/reggba_keypad.vhd

Entity + Architecture in welchem diese 2 Register verwendet sind:
https://github.com/MiSTer-devel/GBA_MiSTer/blob/master/rtl/gba_joypad.vhd

Insgesamt sind ~300 solche Register im Design verteilt, gebaut von 
Quartus in einem Cyclone 4/5 und betrieben bei 100 Mhz ohne 
Delay/waitcycles.

Habe für diese Registerbeschreibungen bereits Skripte die mir aus der 
VHDL Registerbeschreibung dann Code für C++, C# und Lua erzeugen.


Nicht falsch verstehen, ich bin durchaus offen für Veränderung, deswegen 
schreibe ich hier ja.
Vielleicht fehlt mir einfach die Vorstellungskraft wie sowas mit 
Wishbone/Axi/etc aussehen könnte und trotzdem übersichtlich wird.
Kenne die beide aus allen Beispielen immer nur als recht lange Liste an 
Signalen/Ports die überall durchgeroutet werden müssen.

von Christoph Z. (christophz)


Lesenswert?

Robert P. schrieb:
> Kenne die beide aus allen Beispielen immer nur als recht lange Liste an
> Signalen/Ports die überall durchgeroutet werden müssen.

Weil die alle, aus mir völlig unverständlichen Gründen, offensichtlich 
noch nie was von Records gehört haben. Gerade ein Bus bietet sich ja 
sowas von an, in Records gepackt zu werden...

von S. R. (svenska)


Lesenswert?

Christoph Z. schrieb:
> Weil die alle, aus mir völlig unverständlichen Gründen,
> offensichtlich noch nie was von Records gehört haben.

Man braucht aber immer einen Record pro Richtung, oder irre ich mich? 
Oder kann ich alle Bussignale in einen Record packen (sowohl 
read/write+data und ack)?

von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Genau das habe ich ja in meiner Variante getan: alles in einem Record 
und an den Ports Inout.
VHDL gibt das her, weil intern einfach jedes Element seperat aufgelöst 
werden kann.

Klappt leider nicht mit Vivado, bzw. nicht immer. Bei dem oben 
verlinkten Design z.b. für genau 3(!) der über 300 Register nicht, da 
baut Vivado einfach mal das immer eine 0 zurückgelesen wird. Andere 
Register in der gleichen Entity hingegen sind zugreifbar. Trennt man die 
Signale nach In und Out auf klappt es.

Bei sowas reagiere ich leider sehr allergisch. Ich mag nicht wenn die 
Synthese etwas anderes baut als das was die Simulation macht.
Deswegen bin ich jetzt auf wired-or und 2 Ports gegangen: einen 
In-Record für die Richtung Master->Register(Adr, RnW, Ena, ByteEna, Din) 
und einen 32Bit-Out-SLV für die Rückrichtung.

Die Schreibarbeit ist da nicht soooo viel Größer und ich habe den 
Rückweg so geschrieben wie Supachris hier bschrieben hat:
Beitrag "Ausgangssignale mehrerer Blöcke OR'en"

Beispiel:
1
                                                                                                                                                                                                                                                                                              
2
signal REG_IF_ALL : std_logic_vector(IF_ALL.upper downto IF_ALL.lower);
3
type t_reg_wired_or is array(0 to 2) of std_logic_vector(31 downto 0);
4
signal reg_wired_or : t_reg_wired_or;
5
...
6
iREG_IF_ALL : entity work.eProcReg_ds generic map (IF_ALL) port map  (clk100, ds_bus, reg_wired_or(2), IRPFLags(IF_ALL.upper downto 0), REG_IF_ALL , IF_written); 
7
...
8
process (reg_wired_or)
9
   variable wired_or : std_logic_vector(31 downto 0); 
10
begin
11
   wired_or := reg_wired_or(0);
12
   for i in 1 to (reg_wired_or'length - 1) loop
13
      wired_or := wired_or or reg_wired_or(i);
14
   end loop;
15
   ds_bus_data <= wired_or;
16
end process;

Damit habe ich in meiner Register Portliste nur einen Eintrag mehr: die 
Rückgabe in ein Array.
Das Array wird automatisch ver-odert ohne das ich den process jemals 
wieder anfassen muss.

Wenn ich ein neues Register einfüge muss ich damit nur das Array 1 
größer machen. Vergesse ich das oder weise die Daten dem gleichen 
Arrayindex zu gibt es einen harten Fehler, was gut ist, weil man so 
nichts übersehen kann.

Das einzige was hier noch marginal stört, ist das man die Zahl selbst 
hochzählen muss. Damit kann ich aber leben.

: Bearbeitet durch User
von Duke Scarring (Gast)


Lesenswert?

S. R. schrieb:
> Man braucht aber immer einen Record pro Richtung, oder irre ich mich?
Richtig. Ein Record pro Richtung.

Z.B.:
1
  type wishbone_bus_in is record
2
    adr      : std_logic_vector(31 downto 0); 
3
    sel      : std_logic_vector(3 downto 0); 
4
    we      : std_logic;
5
    dat      : std_logic_vector(31 downto 0);
6
    cyc      : std_logic; 
7
    stb      : std_logic;
8
  end record;
9
10
  type wishbone_bus_out is record
11
    dat    : std_logic_vector(31 downto 0);
12
    ack      : std_logic;
13
  end record;

Oder:
1
  -- AHB slave inputs
2
  type ahb_slv_in_type is record
3
    hsel        : std_logic_vector(0 to NAHBSLV-1);     -- slave select
4
    haddr       : std_logic_vector(31 downto 0);        -- address bus (byte)
5
    hwrite      : std_ulogic;                           -- read/write
6
    htrans      : std_logic_vector(1 downto 0);         -- transfer type
7
    ...
8
    testrst     : std_ulogic;                           -- scan test reset
9
    scanen      : std_ulogic;                           -- scan enable
10
    testoen     : std_ulogic;                           -- test output enable 
11
    testin      : std_logic_vector(NTESTINBITS-1 downto 0);         -- test vector for syncrams
12
  end record;
13
14
  -- AHB slave outputs
15
  type ahb_slv_out_type is record
16
    hready      : std_ulogic;                           -- transfer done
17
    hresp       : std_logic_vector(1 downto 0);         -- response type
18
    hrdata      : std_logic_vector(AHBDW-1 downto 0);   -- read data bus
19
    hsplit      : std_logic_vector(NAHBMST-1 downto 0); -- split completion
20
    hirq        : std_logic_vector(NAHBIRQ-1 downto 0); -- interrupt bus
21
    hconfig     : ahb_config_type;                      -- memory access reg.
22
    hindex      : integer range 0 to NAHBSLV-1;         -- diagnostic use only
23
  end record;

Xilinx hat es leider nicht geschafft, das geschickt ins Vivado 
einzubauen. Da darf man sich dann für AXI wieder mit fehlerträchtigen 
Listen rumschlagen...

: Bearbeitet durch Moderator
von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Das sieht doch für Wishbone schön übersichtlich aus.

Hast du eventuell ein Beispiel wie das dat und ack aus dem 
wishbone_bus_out dann für mehrere Teilnehmer verbunden wird?

von Weltbester FPGA-Pongo (Gast)


Lesenswert?

Robert P. schrieb:
> Bei sowas reagiere ich leider sehr allergisch. Ich mag nicht wenn die
> Synthese etwas anderes baut als das was die Simulation macht.

Dein Denkfehler ist hier, dass du in der Simulation ein Bauteilverhalten 
simulierst, welches zwar von echten Treibern geleistet werden kann, in 
FPGAs aber nur in den IO-Zellen, weil dort eben diese parallelen Input 
und Output Pfade existieren und physikalsich verbunden sind.

Daraus kann man nicht ableiten, dass es ein Problem ist, wenn das innen 
im FPGA nicht geht. Die Mehrfachverwendung von records, die BIDIR sind 
und sowohl Port- als auch IO-Port-Beschreibungen sein sollen, ist also 
das Problem. Definiere mal ein sauberes Package für das wired OR und es 
wird klappen.

Ich rate auch allgemein davon ab, solche Doppelkonstrukte zu verwenden, 
die dann der Compiler erst wieder in Machbares übersetzen soll. ES ist 
einfach unsinnig, nur wegen Beschreibungsfaulheit auch 
Sparbeschreibungen mit scheinbaren bidir-Signalen zu setzen (ob records 
oder nicht ist dabei auch egal) und dann zu hoffen, dass dort was 
rauskommen kann.

AXI und andere Busse sind SIGNALBUSSE und keine Physikalischen Busse. 
Die haben nichts bidirektionales an sich, weil eine Information immer 
nur in eine Richtung fliessen kann.

Xilinx ist aber selber Schuld, weil die hauptsächlich diejenigen sind, 
die Physik ins design schleppen, mit angeblichen lo-Aktiven Signalen für 
resets und Allemmöglichen.

von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Das hat mir Denkfehler nichts zu tun. Ich schrieb ja selbst das ich die 
Konstrukte loswerden will ohne Übersichtlichkeit aufzugeben und mögliche 
(menschliche) Fehlerquellen einzubauen.

Meiner Ansicht nach spricht nichts dagegen, das ich diese Dinge(Bidir 
Records und interne TriState Busse die automatisch zu wired-or 
degradieren) mittlerweile über 10 Jahre problemfrei in Altera und Xilinx 
Designs(mit Synplify als Synthesewerkzeug) verwendet habe.

Warum soll man etwas aufgeben, das alle Tools die man nutzt beherschen?

Dann kommt der Nächste und sagt "Initialwerte kann ja nicht jedes 
Tool/FPGA, nimm sowas nicht".

Wenn dann ein Tool es nicht beherscht muss man halt eine Lösung finden. 
Aber doch bitte mit vertretbaren Aufwand und in sinnvoller Weise.

Ich habe auch prinzipiell kein Problem damit eine Lösung zu benutzen die 
noch kompatibler ist, wenn der Mehraufwand und die Nachteile in einem 
vernünftigen Rahmen bleiben.
Ich sehe aber nicht, warum ich mich absichtlich geißeln muss, nur um den 
Tools zu gefallen. Das mache ich nur soweit, wie ich gezwungen bin.

Es ist ja nicht so das ich einen realen Vorteil davon habe, sondern im 
besten Fall wird mit der alternativen Beschreibung exakt die gleiche 
Logik gebaut.

Wishbone hätte ja zumindest noch den Vorteil geläufiger zu sein, das 
wäre mir zumindest noch etwas Wert.
Die händische Bschreibung als wired-or und Auftrennen in mehrere Record 
hingegen ist wirklich NUR für das Tool, für mich als denjenigen der die 
Logik beschreiben muss ergeben sich daraus nur Nachteile.

von Martin S. (strubi)


Lesenswert?

Robert P. schrieb:
> Hat jemand eine bessere Idee wie das funktionieren kann?
> Wie löst ihr das?

Ich generiere mir aus einer XML-Geraetebeschreibung die Decoder fuer die 
Memory-Mapped-Register (plus Header, Registertabellen usw, wie nach 
Methode 'Klakx' oben).
Mit wired-or erzeugst du nur eine Menge LUTs und hast ev. irgendwann 
einen Flaschenhals.
Machen die 'Grossen' mit IP-XACT aehnlich, nur von hinten durch die 
Brust, indem noch eine weitere Register-Language erfunden wurde..

Und inout in die Tiefen zu routen kann bei einigen FPGA-Tools echt ueble 
obskure Fehler provozieren. Nicht tun :-).

von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Das es weniger Flaschenhals als Wired-or haben soll, damit hast du mich 
jetzt neugierig gemacht.
Wie kommt das? 1 bis mehrere Takte Delay?

Hast du eventuell mal irgendein Beispiel wo man das sieht?
Mir geht es dabei vor allem um so einen generierten Decoder.

Das wäre echt nett mal etwas Code dazu zu sehen.

von Martin S. (strubi)


Lesenswert?

Robert P. schrieb:
> Das es weniger Flaschenhals als Wired-or haben soll, damit hast du mich
> jetzt neugierig gemacht.
> Wie kommt das? 1 bis mehrere Takte Delay?

Ja, beim Lesen kommst du daran wohl nicht vorbei, beim Schreiben kann 
man's notfalls (bei riesigen Maps) pipelinen. Der Flaschenhals tritt im 
Bus-Generator ebenfalls auf, wenn man den Delay abschaltet (dann hast du 
wieder Multiplexer-Verstopfung).

Den ganzen Code-Moloch dazu gibt's hier: 
https://github.com/hackfin/MaSoCist
Achtung, Linux-lastig.
Die Peripherie musst du allerdings erzeugen, d.h. das Ding im Container 
(siehe Readme) einmal bauen. Dann liegen in 
`~/src/masocist-opensource/gen` o.ae. die erzeugten VHDL-Decoder. Die 
Gesamtstruktur laesst sich dann am besten aus der Wave-Trace der 
Simulation per GTKWave rauslesen.

Um die XML-Maps zu editieren macht der 'xxe' als grafischer Editor Sinn, 
siehe auch Screenshot hier: 
https://hackaday.io/project/162259-netpp-node (das ist die 'reale' 
Hardware-Umsetzung von obigen SoC).

von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Habe mir mal das anhand des generierten "decode_uart" angesehen und über 
die Instanzen verfolgt.
Verstehe damit auch warum man das dann erzeugen muss. Diese Struktur 
lässt sich fehlerfrei kaum noch selbst hinschreiben.

Ehrlich gesagt ist mir das aber viel zu viel Code, weil mein 
vordergründiges Problem, mehrere mögliche Signale auf möglichst 
kompaktem Weg durch das System zu bekommen, damit nicht erfüllt wird:

Alle Multiplexer sind in ihrer Komplettheit abgebildet und 
ausgeschrieben und jedes Register steht ausgeschrieben mit dem 
kompletten Buszugriff im Code.

Klar, kann man ja alles generieren und verschwindet irgendwo in 
niedrigeren Hierarchieebenen, ist mir am Ende aber zu umständlich für 
das was es leisten soll.

Nicht falsch verstehen, das funktioniert für deinen Anwendungsfall 
sicher gut, für mich passt es leider nicht.
Auf jeden Fall vielen Dank, es war sehr hilfreich mal einen anderen 
Lösungsvorschlag zu sehen!

von Martin S. (strubi)


Lesenswert?

Nja, ich habe noch keine kompaktere (explizite/debugbare/portable) 
Variante gesehen, die auf allen mir bekannten Tools von A/I/L/X/S und 
yosys genau das macht, was sie soll und einem nicht um die Ohren fliegt. 
Kompakter geht es nur noch in Python/MyHDL, intern wird aber wieder 
dasselbe generiert.

Also, der Apfel bleibt sauer :-) Irgend einen Busdecoder wirst du 
stricken muessen, den die Tools richtig umsetzen. Tristate-Hacks mit 
komplizierter Treiberstruktur sind aus einer langen Liste von Gruenden 
no-go und viel Schreibarbeit wird irgendwann zum Verwaltungsmoloch. Da 
spare ich mir lieber Zeit mit einem 'make all'.

von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Am Ende führen viele Wege wohl zum Ergebnis.

Die oben erwähnte Variante mit wired-or dürfte jetzt wohl auch auf allen 
Tools funktionieren, da ist ja nichts ungewöhnliches mehr drin (kein 
Bidir, kein TriState)
Probiert habe ich es bisher aber erst mit Vivado und Modelsim.

Mir gings ja hier im Thread auch weniger um den Weg von der 
Registerbeschreibung zur Dekodierung, sondern um den Rückweg der Daten 
von mehreren Registern zum Master.

Scheinbar gibt es aber keinen Weg in VHDL der so kompakt wie Tristate 
ist, jedoch von allen Tools akzeptiert wird. Das war ja übersprünglich 
meine Hoffnung.

Das "multiple drivers" Problem bei der Nutzung von nur einer Leitung 
lässt sich wohl einfach nicht umgehen.

von S. R. (svenska)


Lesenswert?

Robert P. schrieb:
> Scheinbar gibt es aber keinen Weg in VHDL der so kompakt wie
> Tristate ist, jedoch von allen Tools akzeptiert wird. Das war
> ja übersprünglich meine Hoffnung.

Da bin ich vor einigen Jahren auch auf die Nase gefallen mit der 
Hoffnung.

Das Argument "dann generiert man das halt extern" ist ... naja, 
eigentlich ein gutes Zeichen, dass die Sprache doch irgendwo ein 
bisschen schlecht riecht.

von Martin S. (strubi)


Lesenswert?

Robert P. schrieb:
> Am Ende führen viele Wege wohl zum Ergebnis.
>

Siehe nochmal oben, Kommentar von Klakx. IMHO gibt es fuer Busdekoder 
nicht viele praktikable Wege, die skalierbar und vernuenftig debugbar, 
geschweige verifizierbar bleiben.
Wired-OR kann man bei einfachen Controllern o.ae. machen, aber es 
skaliert schlecht. 300 Register willst du so implementieren und dann 
allenfalls debuggen?

> Scheinbar gibt es aber keinen Weg in VHDL der so kompakt wie Tristate
> ist, jedoch von allen Tools akzeptiert wird. Das war ja übersprünglich
> meine Hoffnung.
>

Es gibt einen ziemlich triftigen Hauptgrund, warum das mit Tristate 
Murks ist:
Man kann recht leicht kombinatorische Logik erzeugen, die nicht nach 
Treiber und Empfaenger sauber aufloesbar ist ('resolving').
Der Simulator arbeitet ereignisbasiert, die klassischen Synthesizer 
nicht: sie analysieren die Code-Syntax und versuchen, die Konstrukte 
aufzuloesen.
Da gibt es uneindeutige Szenarien, die deswegen zu Unterschieden bei 
Sim/Synth fuehren koennen..Dauerklassiker seit Isim, Season two mit 
Vivado..
GHDL wuerde dir einiges, was bei Isim 'funktioniert' mit einem 'X' 
(undefiniert) markieren, und du kannst dann in der Trace die 
Fehlerquelle suchen.

Deswegen sind in vielen Design-Regeln solche Konstrukte komplett 
verboten. Man schiesst sich damit sinnlos ins Knie, und kann das Design, 
wenn's mal soweit kommt, nicht verifizieren.
Drum fuehren manche HDL ein explizites `TristateSignal` ein, die einen 
klar definierten 'Besitzer' (Master) haben, intern darf kein anderer 
Teilhaber treiben. Und auch dann muss man's sparsam verwenden, sonst 
wird die Simulation fuerchterlich lahm (aufgrund vieler 
Ereignisquellen).

Aber nuja, ausprobieren.

von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Martin S. schrieb:
> 300 Register willst du so implementieren und dann
> allenfalls debuggen

Habe ich ja schon gemacht und es ist mMn sehr übersichtlich, Design ist 
oben verlinkt und läuft.
Die Register sind übersichtlich im Package definiert und werden in einem 
Einzeiler instantiiert.
Weiß auch nicht was daran zu debuggen wäre...kann jedes Register in 
Simulation und auf Hardware per GUI oder Script zum testen lesen und 
schreiben. Was will man da noch debuggen? Mehr sollen die doch gar nicht 
können...

Ist dort noch als Tristate beschrieben, sieht aber mit Wired-or nicht 
groß anders aus, kommt hauptsächlich der Process für das OR einmal pro 
Entity dazu.

Martin S. schrieb:
> GHDL wuerde dir einiges, was bei Isim 'funktioniert' mit einem 'X'
> (undefiniert) markieren, und du kannst dann in der Trace die
> Fehlerquelle suchen.

Kann ich nicht sagen, arbeite aktuell weder mit Isim noch GHDL, nur 
Modelsim.
Wenn ich dort ein X sehe, dann frage ich nach "drivers signalname"
und bekomme gelistet was da gleichzeitig treibt.

Bei dem Tristate Bus den ich bisher benutzt habe kann das aber nicht 
passieren, weil alle Zugriffe auf den Inhalt des Busses den generischen 
Entities überlassen sind. Niemand sonst fasst das Dout an. Damit sind 
doppelte Treiber auf 2 Register mit gleicher Adresse beschränkt.
Dafür gibts ein Script das einen Sanity Check für die Register macht.

Ich will ja auch gar nicht bei Tristate bleiben und mir ist bewusst das 
es ungünstig ist, sonst gäbe es den Thread hier ja gar nicht.

Was mir aber immer noch nicht gefällt sind die "Krücken" um genau das 
gleiche Ergebnis zu bekommen.
Im Prinzip ist das alles nur umständliche Syntax um es der Synthese 
irgendwie klar zu machen.

Zum Vergleich: bei der gleichen Implementierung in C schreibe ich 
einfach von x-beliebigen Positionen im Code in ein globales Array per 
Adresse.

Ein vergleichbares Konzept gibt VHDL leider nur schwer her, obwohl die 
Darstellung aller Register als großen, in Register implementierten, 
Speicherbereich mit einem Port pro FF ganz gut passen würde.

Sowas würde man ja höchstens als Signalarray in einem Package schaffen 
und das macht bei der Synthese sicher keine Freude mehr...

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Robert P. schrieb:
> kommt hauptsächlich der Process für das OR einmal pro Entity dazu.
Wozu ein Prozess? Das ist doch Kombinatorik, das geht Concurrent... ;-)

Martin S. schrieb:
>> Scheinbar gibt es aber keinen Weg in VHDL der so kompakt wie Tristate
>> ist, jedoch von allen Tools akzeptiert wird. Das war ja übersprünglich
>> meine Hoffnung.
> Es gibt einen ziemlich triftigen Hauptgrund, warum das mit Tristate
> Murks ist:
Es gibt kein Tristate im FPGA. Das ist ganz einfach so. Also kann 
der Synthesizer die Tristate-Beschreibung nicht so wie beschrieben 
umsetzen. Er warnt völig zu Recht, denn er weiß ja nicht, dass das der 
gewollte Beschreibungsstil ist. Deshalb ist es extrem unsauber (und im 
Prinzip ledigleich der seit 10 Jahren angewöhnten Schreibfaulheit 
entgegenkommend), den Synthesizer den Busmultiplexer implizit aus den 
lokalen Adressdekodern, die den Tristate steuern, herausklamüsern zu 
lassen und die entsprechenden Warnungen in den Wind zu schlagen.

Robert P. schrieb:
> Ist dort noch als Tristate beschrieben, sieht aber mit Wired-or nicht
> groß anders aus
Es gibt auch kein Wired-Or im altbekannten Sinne, dass alle Treiber von 
der HIGH-Side auf eine einzige Leitung treiben, und der LOW-Pegel durch 
einen Pulldown erreicht wird:
https://de.wikipedia.org/wiki/Wired-OR-Verkn%C3%BCpfung
Letztlich ist so ein Wired-Or auch nur eine Verknüpfung von 'Z' und '1'. 
Und das Thema 'Z' im FPGA ist ein paar Zeilen weiter oben beschrieben.

: Bearbeitet durch Moderator
von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Ok, dann gibt es den Namen wohl auch für was anderes, war mir nicht 
bekannt.
Ich verbinde hier nur '0' und '1' und kein 'z', von daher 
unproblematisch.

Und ganz ehrlich, wenn es nur nach Synthesewarnungen geht muss ich noch 
viel mehr umbauen.

Z.b. warnt mich Vivado bei jedem inferierten Dualport-Ram ich solle doch 
bitte mit 2 Prozessen schreiben, sonst würde er wahrscheinlich kein 
Blockram daraus machen.
Macht er aber trotzdem und auf 2 Prozesse baue ich sicher nicht um, 
sonst kann ich das Modul nicht mehr simulieren, weil das Signal aus 2 
Prozessen getrieben wird.

Derjenige, der ein komplexes Design ohne Vivado/Quartus Warnungen gebaut 
bekommt, der werfe den ersten Stein...

Tut mir leid, aber der Umstieg von Quartus auf Vivado nervt echt. 
Wahrscheinlich ist es umgedreht genauso schlimm, aber aufgrund der 
Richtung muss jetzt Xilinx herhalten :)


Lothar, wenn du einen Vorschlag für Concurrent hast der ähnlich knapp 
ist wie die Lösung von Supachris(siehe oben), nur her damit, das tausche 
ich gerne aus.
Mir ist da noch nichts eingefallen.

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Robert P. schrieb:
> Derjenige, der ein komplexes Design ohne Vivado/Quartus Warnungen gebaut
> bekommt, der werfe den ersten Stein...
Warst nicht du derjenige, der sich an diesen Warnungen stört und sie 
loswerden will? Ich meinte, im ersten Post sowas gelesen zu haben... 
;-)

> Lothar, wenn du einen Vorschlag für Concurrent hast der ähnlich knapp
> ist wie die Lösung von Supachris(siehe oben), nur her damit, das tausche
> ich gerne aus.
Sieh dir mal die Funktion or_reduce() aus der std_logic_misc an. Die 
macht, was du dir unter einem Wired-Or vorstellst.
Soiehe dazu auch dort: 
https://stackoverflow.com/questions/28973387/or-reduce-an-array-of-vectors

: Bearbeitet durch Moderator
von Martin S. (strubi)


Lesenswert?

Lothar M. schrieb:
> Es gibt kein Tristate im FPGA. Das ist ganz einfach so. Also kann
> der Synthesizer die Tristate-Beschreibung nicht so wie beschrieben
> umsetzen. Er warnt völig zu Recht, denn er weiß ja nicht, dass das der
> gewollte Beschreibungsstil ist. Deshalb ist es extrem unsauber (und im

Dass der obige 'interne' Tristate emuliert wird, ist allen bereits klar. 
Las sich vermutlich missverstaendlich.
Nebenbei: es gibt durchaus physikalisch-internen Tristate in einigen 
gehaerteten Architekturen (z.B. die, die Rekonfiguration im laufenden 
Betrieb machen). Aber verhaelt sich halt wie ein eigener Block.
Ansonsten gilt fuer Xilinx wohl, dass in der deren Synth-Engine (xst) 
noch eine Menge haesslicher 'legacy' rumliegt (aus den Zeiten, wo 
interne Tristate noch 'kompakter' waren als die heutigen Zellen). 
Deswegen auch die Mismatches zwischen Simulation und der 
Hardware-Emulation bei Isim/Vivado und dem Synthese-Ergebnis. Andere 
Syns spucken Warnungen oder sogar Fehler aus.
GHDL setzt da streng die VHDL-Standards um, Isim hat Xilinx-spezifische 
'Toleranzen'. Deswegen Stolperfalle. Wenn GHDL ein 'X' ausgibt, Isim 
aber nicht, darf man GHDL glauben.

Ansonsten: die syn-engines verhalten sich bei sowas hoechst 
unterschiedlich, xst kann gewisse loops eben wie genannt nicht aufloesen 
und es kommt Mist hintenraus, die andere Toolchain generiert sinnlos 
viele Muxer, um den Tristate komplett zu emulieren. In der 
Post-synthesis-Verifikation gibt es natuerlich 'Z' dann nicht, das muss 
man sich dann aus zwei Signalen wieder rekonstruieren.

Robert P. schrieb:
> Derjenige, der ein komplexes Design ohne Vivado/Quartus Warnungen gebaut
> bekommt, der werfe den ersten Stein...

Ich hatte mal so eine Anforderung fuer XST. Ja, macht keinen Spass. Aber 
wie gesagt, in einen sauren Apfel musst du beissen. Entweder schreibst 
du dir die Finger in VHDL wund (entweder konform nach Standard, oder 
nach dem Willen deines Tools) oder generierst den Kram nach den 
Designregeln (deines Tools). Mit MyHDL geht sowas einigermassen 
portabel, oder du schreibst fuer jede Architektur in VHDL eine Variante. 
Such dir's aus.

von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Lothar M. schrieb:
> Sieh dir mal die Funktion or_reduce() aus der std_logic_misc an.

Das sieht gut aus, damit werde ich die Prozesse los und habe das 
Verodern des kompletten Arrays in einer Zeile.

War davon ausgegangen du meinst eine "echte" concurrent Zuweisung, aber 
so ist ja noch viel besser.


Ich denke mal ich werde dabei auch erstmal bleiben. Tristate weg, Bidir 
weg und nicht zuviel Mehrarbeit. Das sollte so robust funktionieren.

Vielen Dank nochmal an alle Diskussionsteilnehmer!

von Markus F. (mfro)


Lesenswert?

Robert P. schrieb:
> Lothar M. schrieb:
>> Sieh dir mal die Funktion or_reduce() aus der std_logic_misc an.

VHDL 2008 hat or_reduce() übrigens als unären Operator im Sprachstandard 
(und braucht kein std_logic_misc mehr dazu):
1
x <= or_reduce(y)

VHDL 2008:
1
x <= or y;

nur leider, wie so oft, ist die Unterstützung dafür bei den 
einschlägigen Tools meist - wenn überhaupt - höchst rudimentär. Der 
Standard ist ja schliesslich erst zwölf Jahre alt, wie kann man da schon 
Support erwarten?

von Christoph Z. (christophz)


Lesenswert?

Robert P. schrieb:
> Zum Vergleich: bei der gleichen Implementierung in C schreibe ich
> einfach von x-beliebigen Positionen im Code in ein globales Array per
> Adresse.
>
> Ein vergleichbares Konzept gibt VHDL leider nur schwer her, obwohl die
> Darstellung aller Register als großen, in Register implementierten,
> Speicherbereich mit einem Port pro FF ganz gut passen würde.

Willkommen in der schönen neuen Zeit. Das was du hier gerade mit VHDL 
lernst, wird dir auch in C zugute kommen, da nämlich dein C Beispiel 
funktioniert, solange du kein Multithreading machst. Da aber alle 
modernen Plattformen (Fernseher, Handy, PC, Frequenzumrichter,...) ihre 
Leistung nur dank Multi-core Prozessoren erreichen, muss auch in C das 
"multiple-drivers" Problem adressiert werden (Typischerweise mit 
Semaphoren).

Das Äquivalent zu unserer Hardwaredenkweise hier (in so unabhängigen 
Blöcken mit internen Daten und Zuständen) wird in der Software "Actor 
based" Parallelisierung genannt.

Ach ja, hier geht es ja um Retrocomputing, da kommt das Problem erst 
beim Nachbau einer Transputer-Workstation oder Cray zum Tragen ;-)

von S. R. (svenska)


Lesenswert?

Christoph Z. schrieb:
> da nämlich dein C Beispiel
> funktioniert, solange du kein Multithreading machst

Korrektur: "solange du kein Multithreading auf diesem Array machst".

von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Solange nur ein Teilnehmer eine Zelle schreiben kann und beliebig viele 
nur Lesen, sollte das klappen.(Außer vielleicht Cache Kohärenz, aber das 
ist ein anderes Thema)

Bei den verwendeten Registern habe ich ja genau das:

Ein Readwrite Register kann nur vom Controller/Prozessor geschrieben 
werden, jedoch sowohl vom verwendenden Modul und vom Prozessor gelesen.
Ein Readonly Register kann nur das verwendende Modul Schreiben.

Der "Computer" den ich aktuell nachbaue hat sogar 2 Prozessoren, ist 
aber auch nicht mehr so ganz Retro(2004).
Dort wird es auch so gelöst: jedes Register hat genau einen Teilnehmer 
der es beschreiben kann.

Einzige Außnahme sind spezielle IPC Register. Aber auch die sind nur 
scheinbar von 2 Seiten zu schreiben, in Wirklichkeit gibt es diese 
doppelt vorhanden und Schreib/Leseseite ist vertauscht.

von Weltbester FPGA-Pongo (Gast)


Lesenswert?

Robert P. schrieb:
> Einzige Außnahme sind spezielle IPC Register. Aber auch die sind nur
> scheinbar von 2 Seiten zu schreiben, in Wirklichkeit gibt es diese
> doppelt vorhanden und Schreib/Leseseite ist vertauscht.

Und die Konsistenz wird dann manuell hergestellt? Warum ist das kein 
DP-BRAM?

von Robert P. (fpgazumspass) Benutzerseite


Lesenswert?

Nicht manuell, die Logik stellt das sehr schön zur Verfügung.

1.Register: Prozessor A darf schreiben und sein Geschriebenes Lesen, 
Prozessor B darf nur lesen.
2. Register: Prozessor B darf schreiben und sein Geschriebenes Lesen, 
Prozessor A darf nur lesen.

Das ganze gibts dann auch nochmal als zwei 16 DWord große Fifos(Einer 
darf nur Schreiben, einer nur Lesen) inklusive 2 aktivierbaren 
Interrupts wenn Daten angekommen sind und/oder wenn die Gegenseite alles 
abgeholt hat.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.