Forum: FPGA, VHDL & Co. Entwicklung eines RAM mit Interface - Probleme mit der Synthese/Impl.


von Marcel (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich arbeite aktuell an einem Projekt für die Universität.
Ziel ist es ein Interface zwischen einem Mikrocontroller und einem FPGA 
in Betrieb zu nehmen. Das Interface basiert auf dem Ansatz einen RAM im 
FPGA anzulegen und diesen mit dem Memory Interface des Controllers 
anzusteuern.


Nun habe ich folgende Schritte gemacht:


RAM Modul entwickelt in VHDL
RAM Interface des µC in Betrieb genommen und mit dem Logic Analyzer 
alles inspiziert damit ich meine Testbench schreiben kann.
RAM in die Testbench geworfen (Sehr rudimentäre Testbench - Ich weiß 
ehrlich gesagt nicht wie ich das besser machen soll?)


Was ich jetzt für Probleme habe:


Ich bekomme folgende Fehler während der Synthese die mich stutzig 
machen:
(1)[Synth 8-3331] design main has unconnected port clk
(2)[Synth 8-327] inferring latch for variable 'ram_reg[0]' 
["C:/Users/rddlr/Documents/Bachelor/01_Vivado/memory_interface_test_sram 
/memory_interface_test_sram.srcs/sources_1/new/async_sram_tristate_new_n 
Bit_test.vhd":92]


Ich bekomme folgende Fehler in der Implementierung die mich stutzig 
machen:
(3)[DRC PLIO-3] Placement Constraints Check for IO constraints: 
Partially locked IO Bus is found. Following components of the IO Bus 
ram_addr[5:0] are not locked:  ram_addr[1]
(4)Bad Practice (534)


Ich würde gerne verstehen wie diese Fehler zu stande kommen und wie ich 
sie beheben kann.

von Christoph Z. (christophz)


Lesenswert?

Marcel schrieb:
> Ich bekomme folgende Fehler während der Synthese die mich stutzig
> machen:

Beides sind Warnungen. Wenn es Fehler wären, dann würde er abbrechen und 
du könntest gar keine Implementierung starten.

Trotzdem macht es Sinn, wie du es hier gerade machst, sie zu verstehen. 
In den Warnungen sind immer wieder sehr nützliche Informationen 
versteckt.

Marcel schrieb:
> (1)[Synth 8-3331] design main has unconnected port clk

Du deklarierst einen Port namens clk, der aber nicht angeschlossen ist. 
Bei einem Clock üblicherweise ganz schlecht. :-)

Wenn es gewünscht ist, einen Port nicht anzuschliessen, dann kannst du 
das mit dem Schlüsselwort "open" explizit hinschreiben. Dann ist die 
Warnung weg und jeder weiss beim lesen des Source Codes, dass das so 
sein soll.

Marcel schrieb:
> (2)[Synth 8-327] inferring latch for variable 'ram_reg[0]'
> ["C:/Users/rddlr/Documents/Bachelor/01_Vivado/memory_interface_test_sram
> /memory_interface_test_sram.srcs/sources_1/new/async_sram_tristate_new_n
> Bit_test.vhd":92]

In FPGAs gibt es eigentlich nur synchrone Designs. In einem synchronen 
Design soll es nur Register und sicher keine Latches geben. Darum 
meckert der Synthesizer jedes Latch an, das generiert wird.

Dein asynchrones RAM Interface ist asynchron implementiert und benötigt 
darum zu recht Latches. Aber da gibt es wohl noch andere, die hierzu 
diskutieren möchten.

Marcel schrieb:
> (3)[DRC PLIO-3] Placement Constraints Check for IO constraints:
> Partially locked IO Bus is found. Following components of the IO Bus
> ram_addr[5:0] are not locked:  ram_addr[1]

Tönt danach, als würde im Constraint File die Pinzuweisung für 
ram_addr[1] fehlen.

von Nick M. (Gast)


Lesenswert?

Wolltest du das wirklich so schreiben?
(2**(ADDR_WIDTH-1) - 1 downto 0)

von Tobias B. (Firma: www.elpra.de) (ttobsen) Benutzerseite


Lesenswert?

Christoph Z. schrieb:
> Wenn es gewünscht ist, einen Port nicht anzuschliessen, dann kannst du
> das mit dem Schlüsselwort "open" explizit hinschreiben. Dann ist die
> Warnung weg und jeder weiss beim lesen des Source Codes, dass das so
> sein soll.

Aber nur bei einem out Port. Inputs duerfen nicht open sein!

von Marcel (Gast)


Lesenswert?

Nick M. schrieb:
> Wolltest du das wirklich so schreiben?
> (2**(ADDR_WIDTH-1) - 1 downto 0)

Ja, ich denke schon. Es klingt so als würdest du da ein Problem erkennen 
das ich nicht sehe.

Der RAM wird unterteilt in zwei Teile - einer ist read only und einer 
write only.

Also dachhte ich mir einer geht von 0 bis 127 und der andere von 128 bis 
256.
127 wären doch dann 2**(n-1)-1 oder?


Christoph Z. schrieb:
> Dein asynchrones RAM Interface ist asynchron implementiert und benötigt
> darum zu recht Latches.

Das ist gut zu hören - ich hatte mir das bereits gedacht war mir aber 
nach dem Blogeintrag von Nandland über Latches nicht mehr sicher. Er 
schreibt das er in 30 Jahren kein Design entwickelt hat, das Latches 
benötigt.


Christoph Z. schrieb:
> Du deklarierst einen Port namens clk, der aber nicht angeschlossen ist.
> Bei einem Clock üblicherweise ganz schlecht. :-)

Genau das mache ich allerdings. Ich weise den Port in der main.vhd dem 
component sram zu und im sram benutzte ich die clk in meiner sensitivity 
list.

Christoph Z. schrieb:
> Tönt danach, als würde im Constraint File die Pinzuweisung für
> ram_addr[1] fehlen.

Alles ist zugewiesen (nach meiner Meinung).
Auszug:
1
 set_property -dict { PACKAGE_PIN V15   IOSTANDARD LVCMOS33 } [get_ports { ram_dat[0]  }]; #IO_L16P_T2_CSI_B_14          Sch=ck_io[0]
2
set_property -dict { PACKAGE_PIN U16   IOSTANDARD LVCMOS33 } [get_ports { ram_dat[1]  }]; #IO_L18P_T2_A12_D28_14        Sch=ck_io[1]
3
set_property -dict { PACKAGE_PIN P14   IOSTANDARD LVCMOS33 } [get_ports { ram_dat[2]  }]; #IO_L8N_T1_D12_14             Sch=ck_io[2]
4
set_property -dict { PACKAGE_PIN T11   IOSTANDARD LVCMOS33 } [get_ports { ram_dat[3]  }]; #IO_L19P_T3_A10_D26_14        Sch=ck_io[3]
5
set_property -dict { PACKAGE_PIN R12   IOSTANDARD LVCMOS33 } [get_ports { ram_dat[4]  }]; #IO_L5P_T0_D06_14             Sch=ck_io[4]
6
set_property -dict { PACKAGE_PIN T14   IOSTANDARD LVCMOS33 } [get_ports { ram_dat[5]  }]; #IO_L14P_T2_SRCC_14           Sch=ck_io[5]
7
set_property -dict { PACKAGE_PIN T15   IOSTANDARD LVCMOS33 } [get_ports { ram_dat[6]  }]; #IO_L14N_T2_SRCC_14           Sch=ck_io[6]

von Achim S. (Gast)


Lesenswert?

Marcel schrieb:
> Ich weise den Port in der main.vhd dem
> component sram zu und im sram benutzte ich die clk in meiner sensitivity
> list.

Aber nur in der sensitivity list, nicht in der synthetisierten 
Schaltung. (d.h. innerhalb des Prozesses fragst du nicht die Taktflanke 
von CLK ab).

Dementsprechend ist auch die Auflistung von clk in der sensitivity list 
irreführend bis falsch: du baust hier kein Design, das taktsynchon mit 
clk arbeitet.

Aber da der Name deiner Komponente mit "asynch_sram" beginnt, ist das 
wohl auch gar nicht deine Absicht, ein synchrones Design zu bauen. Dann 
solltest du aber auch das irreführende Signal clk weglassen und dich 
nicht über die Bildung von Latches wundern.

von Christoph Z. (christophz)


Lesenswert?

Marcel schrieb:
> Christoph Z. schrieb:
>> Du deklarierst einen Port namens clk, der aber nicht angeschlossen ist.
>> Bei einem Clock üblicherweise ganz schlecht. :-)
>
> Genau das mache ich allerdings. Ich weise den Port in der main.vhd dem
> component sram zu und im sram benutzte ich die clk in meiner sensitivity
> list.

Wie Achim schreibt, was du nicht brauchst, solltest du auch nicht in der 
Entity auflisten.

Marcel schrieb:
> Christoph Z. schrieb:
>> Dein asynchrones RAM Interface ist asynchron implementiert und benötigt
>> darum zu recht Latches.
>
> Das ist gut zu hören - ich hatte mir das bereits gedacht war mir aber
> nach dem Blogeintrag von Nandland über Latches nicht mehr sicher. Er
> schreibt das er in 30 Jahren kein Design entwickelt hat, das Latches
> benötigt.

Hehe, ja, im Prinzip sollte das auch dein Ziel sein, keine Designs für 
FPGAs zu bauen, die Latches benötigen. FPGAs und ihre Tools sind nicht 
wirklich dafür vorgesehen asynchrone Interfaces zu implementieren.

Habe ich aber auch schon gemacht, weil andere vorher das so ausgesucht 
hatten. Das muss mit Constraints sauber definiert werden, sonst gibt es 
Überraschungen.

Falls die Zugriffszeiten des CPU es erlauben, kannst du dein SRAM 
Interface  im FPGA auch synchron bauen (du musst es nur schaffen, 
innerhalb der erlaubten Zeit die Daten an den Ausgang zu legen).

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


Lesenswert?

Marcel schrieb:
> Christoph Z. schrieb:
>> Dein asynchrones RAM Interface ist asynchron implementiert und benötigt
>> darum zu recht Latches.
> Das ist gut zu hören
Das nennt sich auch "schönsaufen".
> - ich hatte mir das bereits gedacht war mir aber
> nach dem Blogeintrag von Nandland über Latches nicht mehr sicher. Er
> schreibt das er in 30 Jahren kein Design entwickelt hat, das Latches
> benötigt.
Wenn man sein Design aus Erfahrung von vorn herein so auslegt, dass 
man keine Latches benötigt, dann braucht man auch keine. Aber wenn ich 
das hier so sehe(**):
1
use ieee.numeric_std.all;
2
use IEEE.std_logic_unsigned.all;
3
use IEEE.std_logic_arith.all;
dann hast du dieses RAM nicht aus Erfahrung asynchron gemacht.

Denn das hier gibts im realen Leben nicht:
1
if(falling_edge(nWE) or falling_edge(nOE)) then
Alles, das mit _edge() oder 'event beschrieben wird, ergibt ein 
Flipflop.
Und das, was du da beschreibst, ist ein "Dual-Clock-Input-Flipflop". 
Sowas gibt es nicht.
Weil der Synthesizer das nicht kann, bastelt er irgendwie ein Latch 
daraus.

Und wenn ich jetzt noch einen Schritt "nach aussen" gehe und das hier 
sehe:
1
    if (nCS = '0') then -- if nCS = '0' the adress is valid but it can change due one write cycle
2
        if(falling_edge(nWE) or falling_edge(nOE)) then
dann frage ich mich und dich: wo im Handbuch des Synthesizers steht, 
dass man es so machen soll oder kann?

Achim S. schrieb:
> du baust hier kein Design, das taktsynchon mit clk arbeitet.
Und das ist der erste Schritt in Richtung "Probleme!"

Marcel schrieb:
> Ich weise den Port in der main.vhd dem component sram zu und im sram
> benutzte ich die clk in meiner sensitivity list.
Die Sensitivliste ist dem Synthesizer schnurz. Sie wird nur vom 
Simulator benutzt, um anzuzeigen, bei Änderung welcher Signale der 
Prozess für die Simulation neu berechnet werden muss.
Der Synthesizer gibt bei einer falschen Sensitivliste bestenfalls eine 
Meldung, dass die Simulation nicht mehr zur Implementation passt.

Christoph Z. schrieb:
> Hehe, ja, im Prinzip sollte das auch dein Ziel sein, keine Designs für
> FPGAs zu bauen, die Latches benötigen.
Nur Anfänger und Profis bauen Latches in ihr Design ein. Erstere aus 
Versehen und zweitere nur dann, wenn es unbedingt nötig ist.

(**)
Never ever die numeric_std und die Synopsys-Libs gelcihzeitig. Du wirst 
sonst eingenartige Fehler und Effekte mit Doppeldefinitionen von 
Datentypen bekommen.

: Bearbeitet durch Moderator
von Marcel (Gast)


Lesenswert?

Achim S. schrieb:
> Dementsprechend ist auch die Auflistung von clk in der sensitivity list
> irreführend bis falsch: du baust hier kein Design, das taktsynchon mit
> clk arbeitet.

Aha, das hatte ich mich schon länger gefragt ob ich das zusätzlich 
machen muss.
Wie kann ich denn die ganze clk weglassen und dafür sorgen das die 
Latches verschwinden? Hast du da einen Tipp?

Christoph Z. schrieb:
> Falls die Zugriffszeiten des CPU es erlauben, kannst du dein SRAM
> Interface  im FPGA auch synchron bauen (du musst es nur schaffen,
> innerhalb der erlaubten Zeit die Daten an den Ausgang zu legen).

Die würden es erlauben. Allerdings würde ich mich erst einmal freuen 
wenn überhaupt etwas funktioniert.

Christoph Z. schrieb:
> Wie Achim schreibt, was du nicht brauchst, solltest du auch nicht in der
> Entity auflisten.

Könnte ich nur alle Signale die ich im if abfrage, in die Sensitivity 
List schreiben?

Mir ist außerdem nicht klar geworden ob mein Design so überhaupt 
funktioniert oder ich hier gerade viele falsche Wege eingeschlagen habe.

Desweiteren verstehe ich nicht ob der FPGA BRAM nutzt als Speicher oder 
LUTs - weiß einer von euch wo ich das rausfinden kann?

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


Lesenswert?

Marcel schrieb:
> Desweiteren verstehe ich nicht ob der FPGA BRAM nutzt als Speicher oder
> LUTs - weiß einer von euch wo ich das rausfinden kann?
Im Synthesebericht steht irgendwas von "inferring yxz RAM" und später 
taucht noch irgendwas zu "Ressources" auf.

Aber dein Design muss zwingend in LUTs abgebildet werden, weil du ja das 
gesamte Array als Ausgang deklariert hast und quasi parallel darauf 
zugreifen könntest:
1
                data_fpga_read   : out  ram_array_16(2**(ADDR_WIDTH-1) - 1 downto 0); 
2
                data_fpga_write  : in ram_array_16(2**(ADDR_WIDTH-1) - 1 downto 0)
Das kann dir ein Blockram nicht bieten. An beiden Ports kann das nur ein 
einziges Wort Schreiben und Lesen. Aber auch hierzu empfehle ich das 
Lesen des Syntehsizer User Guides und des Blockram User Guides.


Marcel schrieb:
> Wie kann ich denn die ganze clk weglassen und dafür sorgen das die
> Latches verschwinden? Hast du da einen Tipp?
Geht nicht. Wenn du was speichern willst, brauchst du entweder Flipflops 
oder Latches. Flipflops brauchen einen Takt:
1
-- nebenläufig
2
wert <= datain when rising_edge(CSn);
3
4
-- sequentiell
5
process (CSn)
6
begin
7
   if rising_edge(CSn) then
8
      wert <= datain;
9
   end if;
10
end process;
Ein Latch ist pegelgestuert:
1
-- nebenläufig
2
wert <= datain when CSn='0' else wert;
3
4
-- sequentiell
5
process (datain, CSn)
6
begin
7
   if CSn='0' then
8
      wert <= datain;
9
   else
10
      wert <= wert;
11
   end if;
12
end process;

BTW:
Du kannst Eingängen keinen Defaultwert geben:
1
                    nCS : in std_logic := '1';  -- low-active Chip-Enable of the SRAM device; defaults to '1' (inactive)
2
                    nOE : in std_logic := '1';  -- low-active Output-Enable of the SRAM device; defaults to '1' (inactive)
3
                    nWE : in std_logic := '1';  -- low-active Write-Enable of the SRAM device; defaults to '1' (inactive)
Wie sollte dieser Defaultwert denn hardwaremäßig implementiert werden?
Du kannst lediglich speichernden Signalen und Ausgängen einen 
Defaultwert geben. Und im Gunde auch nur dann, wenn das Signal oder der 
Ausgang ein Flipflop ist.

von Marcel (Gast)


Lesenswert?

Lothar M. schrieb:
> dann hast du dieses RAM nicht aus Erfahrung asynchron gemacht.

Da würde ich dir erstmal zustimmen.

Danke für dein Feedback. Kannst du mir vielleicht 2-3 Tipps geben wie 
ich besser werden kann? Ich weiß das dieses Design ein Griff ins Klo 
war. Ich finde es akutell schwer Feedback zu bekommen da ich niemanden 
kenne der sich wirklich auskennt. Mir bleibt häufig nur der Weg in 
Foren. Ich denke der ein oder andere kennt es vielleicht.

Falls mir jemand ein paar Hinweise geben könnte oder mir wenigstens 
sagen kann A ist falsch weil lies in B nach, wäre ich sehr sehr dankbar.

Deine Seite Lothar Miller besuche ich oft um mir die Artikel 
durchzulesen!

Vielen Dank!

von Marcel (Gast)


Lesenswert?

Lothar M. schrieb:
> Lesen des Syntehsizer User Guides und des Blockram User Guides.

Das werde ich gleich morgen machen!

Lothar M. schrieb:
> Geht nicht. Wenn du was speichern willst, brauchst du entweder Flipflops
> oder Latches. Flipflops brauchen einen Takt:

Guter Punkt das hatte ich so nicht betrachtet!

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


Lesenswert?

Marcel schrieb:
> Lothar M. schrieb:
>> Flipflops brauchen einen Takt:
> Guter Punkt das hatte ich so nicht betrachtet!
Dazu noch ein Wort: such mal nach "Postulate" hier im Forum. Eines davon 
lautet "nur 1 und der selbe Takt im ganzen Design". Das ist der 
einfachste Weg zur sicheren Funktion.

Insofern ist das mit dem rising_edge(CSn) natürlich nur exemplarisch 
gemeint und nicht zur Nachahmung empfohlen... ;-)

In meinem Design würde dort mit sehr hoher Wahrscheinlichkeit (>99%) ein 
rising_edge(clk) stehen. So wie in allen anderen getakteten Prozessen 
auch.

von Markus F. (mfro)


Lesenswert?

Marcel schrieb:
> Alles ist zugewiesen (nach meiner Meinung).

Du hast hier schön die Pin-Assignments für ram_dat aufgelistet.

Angemeckert wird aber ram_addr[1]?


Marcel schrieb:
> (1)[Synth 8-3331] design main has unconnected port clk

clk kommt zwar von irgendwo her und wird auch schön von oben nach unten 
durchgereicht, ist aber am Ziel (unten) nirgends 
verwendet/angeschlossen. Du hast also ein vollkommen asynchrones Design 
gebastelt.

Das mag vielleicht funktionieren (hab' ich noch nie probiert), 
Timing-Tools sind dabei aber vollkommen aufgeschmissen (schliesslich 
gibt es ja kein Timing) und können dir nicht helfen, wenn es das nicht 
tut.

von Tim (Gast)


Lesenswert?

Dieses Thema erinnert mich auch an eine meiner ersten Aufgaben, wo ich 
noch wenig Ahnung hatte. Leider auch die Aufgabensteller.

Auch wenn man vom µC den Memory-Interface-Controller verwendet, dann 
sollte dies nicht asynchron geschehen. Es geht, aber das Constraining 
ist furchtbar (man sieht ja jetzt schon die Code-Verrenkungen) und da du 
keinen Takt hast, kann der RAM nur Register-frei angesteuert werden. Das 
macht das Resultat sehr langsam...glitch-anfällig ist es auch noch.

Wenn das Interface einen synchronen Takt dazu hat, wird es schon besser. 
Ansonsten rate ich das Interface mit Oversampling zu betreiben. Das 
schafft der FPGA locker und alles wird einfach.

Fazit: neues Konzept wäre besser

von Marcel (Gast)


Angehängte Dateien:

Lesenswert?

Lothar M. schrieb:
> In meinem Design würde dort mit sehr hoher Wahrscheinlichkeit (>99%) ein
> rising_edge(clk) stehen. So wie in allen anderen getakteten Prozessen
> auch.

Hierzu eine Frage: Kann ich das nicht in jeden Prozess einbinden um 
alles Taktsynchron zu halten. Denn ich muss ja nur genug Zeit haben 
damit die clk des FPGA schnell genug den Prozess triggert und der 
Controller mit Daten beliefert wird.

Frage: Wie mache ich das mit dem ein und ausgeben der Daten die vom FPGA 
kommen. Ist es richtig beides in einen Prozess zu packen oder muss ich 
da etwas beachten?
1
    write_ram : process(nWE, nOE, clk)
2
    begin    
3
        if(rising_edge(clk)) then
4
            if (falling_edge(nWE)) then
5
                if (addr_buffer < ADRR_HALF) then
6
                    ram(addr_buffer) <= ram_dat;    
7
                end if;
8
            end if;
9
--------------------------- SO RICHTIG? ------------------------------
10
            ram(2**(ADDR_WIDTH-1) to (2**ADDR_WIDTH)-1) <= data_fpga_write;
11
            data_fpga_read <= ram(0 to (2**(ADDR_WIDTH-1)- 1));
12
----------------------------------------------------------------------
13
        end if;
14
    end process;

Frage: Ich baue am Ende eine kleine Platine mit dem FPGA und dem 
Mikrocontroller. Das ich den Code vorher entwerfe ist auch meiner 
mangelnden Erfahrung geschuldet. Wird das Interface an beliebigen Pins 
funkionieren? Ich würde alle Pins auf eine Bank legen und die clk Quelle 
ebenfalls an einen Pin in dieser Bank.

Verbesserung:

Lothar M. schrieb:
> In meinem Design würde dort mit sehr hoher Wahrscheinlichkeit (>99%) ein
> rising_edge(clk) stehen. So wie in allen anderen getakteten Prozessen
> auch.

Ich habe mein Design verbessert und noch eine spezifische Frage:
Soll ich nun lieber die Flanke abfragen?
1
     read_ram : process (nWE, nOE, clk)
2
    begin
3
        if(rising_edge(clk)) then
4
-------------------------------------------------------------
5
            if (falling_edge(nOE)) then -- WICHTIGE STELLE
6
-------------------------------------------------------------
7
                if (addr_buffer > ADRR_HALF) then
8
                    ram_output_buffer <= ram(addr_buffer); 
9
                end if;                   
10
            end if;
11
        end if;
12
    end process;
Oder auf den festen Wert setzen?
1
     read_ram : process (nWE, nOE, clk)
2
    begin
3
        if(rising_edge(clk)) then
4
-------------------------------------------------------------
5
            if (nOE = 0) then -- WICHTIGE STELLE
6
-------------------------------------------------------------
7
                if (addr_buffer > ADRR_HALF) then 
8
                    ram_output_buffer <= ram(addr_buffer); 
9
                end if;                   
10
            end if;
11
        end if;
12
    end process;

Verbesserung: Anbei mein verbessertes VHDL File mit ersterem Beispiel.

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


Lesenswert?

Marcel schrieb:
> if(rising_edge(clk)) then
>    if (falling_edge(nWE)) then
Zur realen Hardware: es gibt nach wie vor keine 
"Dual-Clock-Input-Flipflops".
Und zur Theorie: weil eine Flanke in der Theorie 0,0000...0ps lang ist, 
werden niemals zwei Flanken gleichzeitig kommen. Ergo wird das zweite 
if nie erfüllt.

Und zu dem Codeschnipsel hier ein paar Worte:
1
    write_ram : process(nWE, nOE, clk)
2
    begin    
3
        if(rising_edge(clk)) then
4
            if (falling_edge(nWE)) then
5
                if (addr_buffer < ADRR_HALF) then
6
                    ram(addr_buffer) <= ram_dat;    
7
                end if;
8
            end if;
9
--------------------------- SO RICHTIG? ------------------------------
10
            ram(2**(ADDR_WIDTH-1) to (2**ADDR_WIDTH)-1) <= data_fpga_write;
11
            data_fpga_read <= ram(0 to (2**(ADDR_WIDTH-1)- 1));
12
----------------------------------------------------------------------
13
        end if;
14
    end process;
Abgesehen von der falschen Sensitivliste mit unnötigem nWE und nicht 
verwendetem nOE sowie fehlenden data_fpga_write und ram vermischst du da 
im Prinzip eine getaktete Beschreibung und mit einer kombinatorischen 
Beschreibung dahinter.
Das ist prinzipiell schlechter Stil, weil keiner hinter/in einem 
getakteten Prozess eine zusätzliche Kombinatorik erwartet.

Im Grunde ist deine Beschreibung das Selbe wie das hier:
1
    write_ram : process(nWE, nOE, clk)
2
    begin    
3
        if(rising_edge(clk)) then
4
            if (falling_edge(nWE)) then
5
                if (addr_buffer < ADRR_HALF) then
6
                    ram(addr_buffer) <= ram_dat;    
7
                end if;
8
            end if;
9
        end if;
10
    end process; 
11
12
    ram(2**(ADDR_WIDTH-1) to (2**ADDR_WIDTH)-1) <= data_fpga_write;
13
    data_fpga_read <= ram(0 to (2**(ADDR_WIDTH-1)- 1));

Marcel schrieb:
> Soll ich nun lieber die Flanke abfragen?
Du kannst da nichts "abfragen".
VHDL ist eine Hardwarebeschreibungssprache. Du musst also erstmal ein 
klares Bild davon haben, wie die Hardware deines Businterfaces mit Logik 
und Flipflops aussieht und beschreibst dann diesen Aufbau mit VHDL.
Hinterher kannst du im RTL-Schaltplan nachschauen, ob der Synthesizer 
deine Beschreibung richtig verstanden hat.

Auch wenn ein Prozess nach dem Lehrbuch "eigentlich" Zeile für Zeile 
"abgearbeitet" wird, so hat er doch eine "Durchlaufzeit" von 0. Denn in 
der realen Hardware wird der Prozess eben nicht abgearbeitet, sondern in 
einer parallelen Logik abgebildet. Da ist dann nichts mehr mit 
"nacheinander".

Marcel schrieb:
> Frage: Ich baue am Ende eine kleine Platine mit dem FPGA und dem
> Mikrocontroller.
Und du willst dafür ein altbekanntes asynchrones Interface verwenden, 
wie es im letzten Jahrtausend zuletzt eingesetzt wurde?

> if(rising_edge(clk)) then
> -------------------------------------------------------------
>             if (nOE = 0) then -- WICHTIGE STELLE
Wenn schon, dann nur so: ein und der selbe Takt im ganzen Design, der 
Rest wird nach Einsynchroniserung der asynchronen Signale taktsynchron 
abgearbeitet.

Aber packen wir den Spieß doch mal am Griff: was hast du für einen µC? 
Hat der so ein asynchrones Adress-/Daten-/Steuerbus-Interface? Und wenns 
doch unbedingt ein Adress-/Daten-/Steuerbus-Interface mit 
biderektionalen Daten sein muss: wie schnell soll das denn laufen? Wie 
schnell ist da ein Buszyklus? Kannst du im Buscontroller des µC 
Watistates konfigurieren?

Und nicht zuletzt: was spricht gegen ein synchrones serielles Interface 
wie SPI?

: Bearbeitet durch Moderator
von Marcel (Gast)


Angehängte Dateien:

Lesenswert?

Lothar M. schrieb:
> Zur realen Hardware: es gibt nach wie vor keine
> "Dual-Clock-Input-Flipflops".

Gut ich habe wohl wieder übersehen das es das genauso ist als würde ich 
beide Abfragen in ein if schreiben.

Lothar M. schrieb:
> Im Grunde ist deine Beschreibung das Selbe wie das hier:

Also kann ich Statements problemlos aus dem Process lösen?

Lothar M. schrieb:
> Und du willst dafür ein altbekanntes asynchrones Interface verwenden,
> wie es im letzten Jahrtausend zuletzt eingesetzt wurde?

Ich habe das Interface vorgeschlagen bekommen. Der Betreuer sagte das es 
am einfachsten ist und vergleichsweise wenig Zeit in Anspruch nimmt.
Was wäre die Alternative?

Lothar M. schrieb:
> Aber packen wir den Spieß doch mal am Griff: was hast du für einen µC?
> Hat der so ein asynchrones Adress-/Daten-/Steuerbus-Interface? Und wenns
> doch unbedingt ein Adress-/Daten-/Steuerbus-Interface mit
> biderektionalen Daten sein muss: wie schnell soll das denn laufen? Wie
> schnell ist da ein Buszyklus? Kannst du im Buscontroller des µC
> Watistates konfigurieren?

Es ist ein C2000 der Marke TI. Die Zugriffszeiten (SETUP, STROBE, HOLD) 
kann man konfigurieren und es gibt sogar die Möglichkeit ein wait Pin zu 
nutzen mit dem man den Buszugriff weiter verzögern kann.
Anbei eine valide Konfiguration mit nahezu maximaler Verzögerung in 
jedem State.

Lothar M. schrieb:
> Und nicht zuletzt: was spricht gegen ein synchrones serielles Interface
> wie SPI?

Ich denke das der gewünschte Datendurchsatz nicht erreicht werden kann. 
Desweiteren wird dieses Interface hier schon genutzt.

von Christoph Z. (christophz)


Lesenswert?

Marcel schrieb:
> Lothar M. schrieb:
>> Und nicht zuletzt: was spricht gegen ein synchrones serielles Interface
>> wie SPI?
>
> Ich denke das der gewünschte Datendurchsatz nicht erreicht werden kann.
> Desweiteren wird dieses Interface hier schon genutzt.

SPI kann mehrere Slaves bedienen. Dafür gibt es Chip Select (CS) 
Signale. Der C2000 kann das sogar ziemlich schnell und mit DMA per 
McBSP. Je nach Typ hast du mehrere McBSP.

Marcel schrieb:
> Es ist ein C2000 der Marke TI. Die Zugriffszeiten (SETUP, STROBE, HOLD)
> kann man konfigurieren und es gibt sogar die Möglichkeit ein wait Pin zu
> nutzen mit dem man den Buszugriff weiter verzögern kann.
> Anbei eine valide Konfiguration mit nahezu maximaler Verzögerung in
> jedem State.

Kommt mir bekannt vor.

Variante 1 ist, dass du das Interface als asynchron betrachtest aber im 
FPGA trotzdem ein synchrones Design baust. Das gibt zwei Taktzyklen 
Verzögerung auf den CS, OEn und WEn Eingängen, da diese zuerst in deine 
lokale FPGA Takdomäne einsynchronisiert werden müssen.

Variante 2 ist, dass der FPGA mit dem Clock vom C2000 betrieben wird 
(der hat einen passenden Ausgang), der ja intern auch genutzt wird um 
das parallele Interface zu bedienen. Wenn das Layout für die 
Taktfrequenz soweit OK ist, kannst du alle Signale zwischen C2000 und 
FPGA als synchron betrachten. Kein Einsynchronisieren nötig, keine 
Latches, wenige übersichtliche Constraints, nur ein einzelner 
Quarzoszillator auf der Leiterplatte.

Marcel schrieb:
> Lothar M. schrieb:
>> Zur realen Hardware: es gibt nach wie vor keine
>> "Dual-Clock-Input-Flipflops".
>
> Gut ich habe wohl wieder übersehen das es das genauso ist als würde ich
> beide Abfragen in ein if schreiben.
>
> Lothar M. schrieb:
>> Im Grunde ist deine Beschreibung das Selbe wie das hier:
>
> Also kann ich Statements problemlos aus dem Process lösen?

Aber mir scheint, dass hier zuerst mal Grundlagenwissen aufgebaut werden 
sollte.

Um aus der Softwaredenkweise herauszukommen und um zu verstehen, was man 
mit VHDL eigentlich (be-)schreibt war für mich das Buch "VHDL Synthese" 
von  Reichhard und Schwarz sehr erhellend (Vermutlich gibt es das in 
eurer Uni Bibliothek).

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


Lesenswert?

Marcel schrieb:
> Ich denke das der gewünschte Datendurchsatz nicht erreicht werden kann.
Das kann man ganz ohne Denken ermitteln: einfach so einen Buszyklus 
aufzeichnen und schauen, wie schnell das Ganze sein muss (und zwar 
getrennt für Lesen und Schreiben).

> Ich denke das der gewünschte Datendurchsatz nicht erreicht werden kann.
Hast du Zahlen zum gewünschten Durchsatz? Denn dazu wird scih der 
Betreuer hoffentlich Gedanken gemacht haben, wenn er schon diesen Bus 
vorschlägt...

von Tim (Gast)


Lesenswert?

Marcel schrieb:
> Ich habe das Interface vorgeschlagen bekommen. Der Betreuer sagte das es
> am einfachsten ist und vergleichsweise wenig Zeit in Anspruch nimmt.

Genau wie ich es vermutet habe. Die Alternativen habe ich und andere 
bereits dir hier hingeschrieben.

Man sieht, dass das asynchrone interface doch mehr Zeit zum designen 
beansprucht. Schnell wird es auch nicht werden. Soll es das überhaupt? 
SPI/mcbsp mit DMA ist schon recht flott für den uC und ich finde das 
einen guten Ansatz. Dafür ist es auch konzipiert.

Dein Betreuer hat keine Ahnung von dem Thema.

von Markus F. (mfro)


Lesenswert?

Tim schrieb:
> Dein Betreuer hat keine Ahnung von dem Thema.

Na ja, so streng wäre ich da erst mal nicht. Das Interface ist 
tatsächlich das simpelste - hat ja keiner gesagt, dass es auf FPGA-Seite 
unbedingt asynchron implementiert werden muss (nein, ich bin nicht der 
Betreuer).

Tim schrieb:
> SPI/mcbsp mit DMA ist schon recht flott für den uC und ich finde das
> einen guten Ansatz. Dafür ist es auch konzipiert.

Wir kennen die Anwendung nicht, aber ein Blick ins Datenblatt verrät, 
dass das asynchrone RAM-Interface beachtliche 60+ MB/s Datentransfer im 
Random Access erreicht - das dürfte mit SPI (da scheint mir auf den 
ersten Blick bei 50 MHz Schluss zu sein) nicht erreichbar sein.

von Tim (Gast)


Lesenswert?

Markus F. schrieb:
> Wir kennen die Anwendung nicht, aber ein Blick ins Datenblatt verrät,
> dass das asynchrone RAM-Interface beachtliche 60+ MB/s Datentransfer im
> Random Access erreicht - das dürfte mit SPI (da scheint mir auf den
> ersten Blick bei 50 MHz Schluss zu sein) nicht erreichbar sein.

Man sollte da nicht so optimistisch sein. Wir reden hier vom C2000 EMIF 
im 16bit ASRAM-Betrieb.

Hier hat das netterweise jemand theoretisch durchgerechnet:
http://e2e.ti.com/support/microcontrollers/c2000/f/171/t/704812?TMS320F28379D-Theoretical-maximum-read-and-write-speed-of-the-EMIF-connected-to-ASRAM

Die Quelle kommt auf theoretisch, maximal 51,3 MB/s für Lesen. Ließt man 
weiter, kommt noch der Einwurf, dass die DMA-Anbindung gar nicht so 
effektiv arbeitet. Auch das kann ich von ähnlichen Controllern 
bestätigen.

Aus Erfahrung würde ich 20-30 MByte/s realistisch sehen, wenn man das 
Interface optimal betreibt UND die DMA große Bursts überträgt. Nagut, 
das ist schon schneller als SPI. Vielleicht ist damit der geheimnisvolle 
Durchsatz erreicht ;)

Marcel schrieb:
> Wird das Interface an beliebigen Pins
> funkionieren?

Hier solltest du nochmal genau nachschauen. Es kann sein, dass dafür nur 
bestimmte Pins explizit vorgesehen sind.

von Markus F. (mfro)


Lesenswert?

Tim schrieb:
> Die Quelle kommt auf theoretisch, maximal 51,3 MB/s für Lesen. Ließt man
> weiter, kommt noch der Einwurf, dass die DMA-Anbindung gar nicht so
> effektiv arbeitet. Auch das kann ich von ähnlichen Controllern
> bestätigen.

Hier: http://www.ti.com/lit/an/sprac96a/sprac96a.pdf (S. 11) stehen 
etwas unter 50 MB/s für Reads und etwa 80 MB/s für Writes (CPU, DMA 
scheint etwas langsamer zu sein).

Die Diagramm-Fussnoten implizieren, dass das keine theoretischen, 
sondern (sicherlich unter absolut optimalen Bedingungen, sonst wär's ja 
nicht von TI) gemessene Werte seien.

von Duke Scarring (Gast)


Lesenswert?

Auf einem älteren ARM9 haben wir mal reichlich 140 MBit/s (~14 MByte/s) 
über das Extended Memory Interface (EMI, 16Bit WIMRE) realisiert.
Man brauch auf beiden Seiten relativ viele Pins. Statt einem größen FPGA 
kann man dann gleich zum Zynq greifen.

Duke

von Marcel (Gast)



Lesenswert?

Vielen Dank an alle die mir geholfen haben!
Ich habe derweil meinen Code verbessert und versuche nun die letzten 
Fehler ausfindig zu machen.

Das es durchaus bessere Interfaces gibt ist mir derweil auch 
aufgefallen. Nichts desto trotz möchte ich meine Arbeit nicht wegwerfen, 
denn ich habe unglaublich viel gelernt bis hier hin!
--------------------------------------------------------------

Tim schrieb:
> Hier solltest du nochmal genau nachschauen. Es kann sein, dass dafür nur
> bestimmte Pins explizit vorgesehen sind.

Ich meinte am FPGA. Am Controller sind sie sehr statisch vorgegeben.

Christoph Z. schrieb:
> Um aus der Softwaredenkweise herauszukommen und um zu verstehen, was man
> mit VHDL eigentlich (be-)schreibt war für mich das Buch "VHDL Synthese"
> von  Reichhard und Schwarz sehr erhellend (Vermutlich gibt es das in
> eurer Uni Bibliothek).

Vielen Dank für diesen Tipp!

---------------------------------------------------------------

Ich habe das Interface unterteilt in einen Read only und Write only Part 
und schiebe die Daten dann von einem Teil in den anderen damit ich sie 
am Mikrocontroller wieder auslesen kann.

Nun habe ich festgestellt das meine Simulation (Post Implementation) 
einen XXXX state erzeugt beim lesen der gewünschten Daten aus dem RAM.

Vielleicht kann mir jemand ein paar Tipps zur Fehlersuche geben.

von Marcel (Gast)


Angehängte Dateien:

Lesenswert?

....
Grundsätzlich sieht in der Simulation aber alles gut aus. Das Signal das 
den X State erzeugt ist das Signal das aus dem Tristate Block die Daten 
in den RAM schreiben soll. Ich weiß leider nicht warum es zu diesem 
State kommt und wie ich das Problem weiter debuggen soll.

Vielleicht habe ich auch in der Simulation einen Schnitzer.

Im Anhang ein Screenshot der die o.g. Leseoperation abbildet. Wie man 
sieht schreibt das Modul die gewünschten Daten an den Port ram_dat. 
Allerdings ist in der Zwischenzeit der Zustand XXXX.

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


Lesenswert?

Marcel schrieb:
> Allerdings ist in der Zwischenzeit der Zustand XXXX.
Eine Testbench hat keinen Port nach aussen. Mach den mal weg und 
deklariere das Signal (wie schon geplant) in der Testbench.

von Marcel (Gast)


Lesenswert?

Lothar M. schrieb:
> Eine Testbench hat keinen Port nach aussen. Mach den mal weg und
> deklariere das Signal (wie schon geplant) in der Testbench.

Habe ich jetzt wieder zurück geändert. Das ändert erstmal nichts an der 
Simulation.

Das letzte mal lag der Fehler im Test Skript. Gehe ich richtig in der 
Annahme das wenn ich den Datenpins "ZZZZ" zuweise (in der Testbench), 
das dann dieser Zustand überschrieben wird und keinen "XXXX" Zustand 
erzeugen kann?

von Christoph Z. (christophz)


Lesenswert?

Marcel schrieb:
> Gehe ich richtig in der
> Annahme das wenn ich den Datenpins "ZZZZ" zuweise (in der Testbench),
> das dann dieser Zustand überschrieben wird und keinen "XXXX" Zustand
> erzeugen kann?

Ja, einer sagt "z" der andere sagt "was anderes" dann ist der Wert 
deines Signals "was anderes".

X bedeutet, dass zwei (oder sogar mehr) Treiber auf das Signal schreiben 
und zwar etwas anderes als "z".

In VHDL ist dafür die "resolution function" zuständig die für den Typ 
std_logic definiert ist und ausgeführt wird, wenn zwei Treiber auf ein 
std_logic Signal schreiben. Die Tabelle dazu ist z. B. hier:
https://vhdlwhiz.com/std_logic/

von Marcel (Gast)


Lesenswert?

Christoph Z. schrieb:
> X bedeutet, dass zwei (oder sogar mehr) Treiber auf das Signal schreiben
> und zwar etwas anderes als "z".

În dem Link den du mir geschickt hast steht allerdings das alle anderen 
Zustände "Z" überschreiben und somit "Z" rezessiv ist. Da ich auch X und 
- nicht benutze muss ich irgendwo eigentlich eine 1 und 0  gleichzeitig 
senden.

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


Angehängte Dateien:

Lesenswert?

Das ist eine schlechte Idee:
use IEEE.std_logic_unsigned.all;
use IEEE.numeric_std.all;
Du solltest dich entscheiden: entweder die alte std_logic_arith (zu der 
die std_logic_unsigned gehört), oder besser nur die numeric_std. Wenn du 
beide verwendest kannst du dir kuriose Effekte wegen doppelt definierten 
Datentypen einfangen...

Marcel schrieb:
> Allerdings ist in der Zwischenzeit der Zustand XXXX.
Die Verhaltensimulation mit ISE und dem ram_dat als lokales Signal sieht 
aber gut aus. Was simulierst du da?

von Marcel (Gast)


Angehängte Dateien:

Lesenswert?

Lothar M. schrieb:
> Was simulierst du da?

Ich schreibe den RAM einmal voll mit "random" Daten und lese sie danach 
wieder aus. Die Daten für die Simulation habe ich mit dem Logic Analyzer 
aufgenommen. Die Daten stammen direkt aus dem TI C2000.

Nun habe ich das Problem das sobald ich das Interface auf der Hardware 
laufen lasse, es nicht so funktioniert wie ich das gerne hätte.

Ich habe schon im .xdc File nach Fehlern gesucht und andere Fehler in 
meinem Code oder der Hardware selbst in Betracht gezogen.

Im Anhang einmal der Lesevorgang. Dieser passiert nach dem ich die Daten 
in den RAM geschrieben habe.

von Marcel (Gast)


Lesenswert?

Lothar M. schrieb:
> Die Verhaltensimulation mit ISE und dem ram_dat als lokales Signal sieht
> aber gut aus. Was simulierst du da?

Ich habe glaube ich deine Aussage Fehlinterpretiert.
Ich mache eine Post Implementation Simulation da ich die Vermutung hatte 
das bei der Synthese/Impl. etwas nicht so gemacht wird wie ich es gerne 
hätte.

Das Signal das ich mir anschaue ist der IN Teil vom Port ram_dat. Dieser 
wird über einen Treiber zurück zum RAM geführt um auch Daten einlesen zu 
können.

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.