Forum: FPGA, VHDL & Co. VHDL Signal Teilen und in die nächst höhere Adresse schreiben


von Markus G. (jim_bobby)


Lesenswert?

Hallo, ich bin ziemlich neu in VHDL.
Mein Problem: Ich muss den Inhalt von input (36 Bit) in einen RAM (18 
Bit) schreiben. Der untere Teil soll auf addr1 geschrieben werden, der 
obere Teil auf die nächst höhere Adresse.
Ich bekomme folgende Fehlermeldung: The data cannot be written to the 
memory correctly when only en_write is enabled, or the content of memory 
cannot be read.
Kann mir jemand sagen, wo mein Problem liegt?
Mein RAM
1
library IEEE;
2
use IEEE.std_logic_1164.all;
3
use IEEE.numeric_std.all;
4
5
entity RAM is
6
    port( Clk : in std_logic;
7
          addr1 : in std_logic_vector(9 downto 0);
8
      addr2 : in std_logic_vector(9 downto 0);
9
          en_read : in std_logic;
10
          en_write : in std_logic;
11
          input : in std_logic_vector(35 downto 0);
12
          output : out std_logic_vector(17 downto 0)
13
            );
14
end RAM;

Mein RAM_verhalten
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.all;
3
use IEEE.Numeric_Std.all;
4
5
architecture Behavioral of RAM is
6
7
signal outputsignal: std_logic_vector(17 downto 0) :="000000000000000000";
8
signal input_1: std_logic_vector(17 downto 0) :="000000000000000000";
9
signal input_2: std_logic_vector(17 downto 0):="000000000000000000";
10
subtype logic_vector is std_logic_vector(17 downto 0);
11
type ram_type is array (1023 downto 0) of logic_vector;
12
signal ram : ram_type := ((others=> (others=>'0')));
13
begin
14
process(Clk)
15
begin
16
if(en_read = '1') then 
17
    outputsignal <= ram(to_integer(unsigned(addr2)));
18
elsif(en_write = '1') then
19
    input_1 <= input(17 downto 0);
20
    input_2 <= input(35 downto 18);
21
    ram(to_integer(unsigned(addr1))) <= input_1;
22
    ram(to_integer(unsigned(addr1))+1) <= input_2;
23
    outputsignal <= "ZZZZZZZZZZZZZZZZZZ";
24
else
25
    outputsignal <= "ZZZZZZZZZZZZZZZZZZ";
26
end if;
27
end process;
28
output <= outputsignal;
29
end Behavioral;

: Bearbeitet durch User
von Nick M. (Gast)


Lesenswert?

Ich bezweifle, dass man auf 2 Adressen gleichzeitig schreiben kann. Kann 
aber auch kein VHDL.

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


Lesenswert?

Markus G. schrieb:
> Kann mir jemand sagen, wo mein Problem liegt?
Du "programmierst" das FPGA, so wie du einen Mikrocontroller 
programmierst. Stimmts?

> Kann mir jemand sagen, wo mein Problem liegt?
Stell dir mal vor, das wäre tatsächlich ein RAM mit einem Satz 
Adressleitungen und einem Satz Datenleitungen. Dann könntest du also 
immer 1 Adresse anlegen und 1 Datenwort schreiben. Deine Zuweisungen 
würden aber "gleichzeitig" stattfinden und ergäben so natürlich einen 
Buskonflikt.


Aber da sind noch ganz, ganz arg grundlegende Probleme in deiner 
Beschreibung. Wenn ich mir die so ansehe, dann könnte das bestenfalls 
noch ein Simulator "verdauen" und irgendwas Krudes vor sich 
hinsimulieren. Aber der Synthesizer wird sich an diesem Codebrocken herb 
"verschlucken" und niemals eine Hardware draus machen, die das tut, was 
der Simulator simuliert hat oder gar das, was du dir wünscht:
1
process(Clk)  -- der Prozess soll offenbar getaktet sein...
2
begin         -- warum taucht dann nachfolgend
3
              -- weder ein rising/falling_edge() noch ein 'event auf?
4
-- Defaultwert am Anfang des Prozesses zuweisen
5
outputsignal <= (others=>'Z');
6
7
if(en_read = '1') then
8
    -- letzte Zuweisung an ein Signal "gewinnt" --> Defaultwert überschreiben
9
    outputsignal <= ram(to_integer(unsigned(addr2)));
10
elsif(en_write = '1') then
11
    input_1 <= input(17 downto 0);
12
    input_2 <= input(35 downto 18);
13
    -- diese beiden Zuweisungen auf das RAM werden
14
    -- nicht "nacheinander" in Hardware implementiert!
15
    ram(to_integer(unsigned(addr1))) <= input_1;
16
    ram(to_integer(unsigned(addr1))+1) <= input_2;
17
18
--        besser   <= (others=>'Z');         denn das passt automatisch
19
--    outputsignal <= "ZZZZZZZZZZZZZZZZZZ";
20
-- else
21
--    und noch besser wäre es, den Defaultwert vor die if-Abfrage zu setzen
22
--    outputsignal <= "ZZZZZZZZZZZZZZZZZZ"; 
23
end if;
24
end process;
Sieh dir dringend im Handbuch/Userguide deines Synthesizers an wie ein 
getakteter Prozess beschrieben werden muss und auch wie ein RAM codiert 
werden muss, dass der Synthesizer letztlich ein RAM macht.

Nick M. schrieb:
> Ich bezweifle, dass man auf 2 Adressen gleichzeitig schreiben kann.
Man "kann" das schon, wenn man weiß was man macht und wie man es macht. 
Dann bekommt man aber kein RAM, sondern einen Satz Register und einen 
Riesenmonstermultipexer.

: Bearbeitet durch Moderator
von Gustl B. (-gb-)


Lesenswert?

Du willst ja eigentlich nur ein RAM mit einem 2^^n Schreibinterface und 
einem 2^^(n-1) Leseinterface. Und das gibt es natürlich.

Das kann man auf verschiedene Arten bauen:

1. Die Adressbreite vom Leseinterface ist 1 größer als die vom 
Schreibinterface.
Beispiel, 9*1024 (= 18*512 oder 36*256) Bit Speicher mit 36 Bit 
Schreibinterface und 18 Bit Leseinterface:

Der Speicher benötigt 256 Adressen = 8 Bits als Adresse um den gesamten 
Speicher mit 36 Bit langen Einträgen zu adressieren. Das 
Schreibinterface bekommt 8 Bits.

Der Speicher benötigt 512 Adressen = 9 Bits als Adresse um den gesamten 
Speicher mit 18 Bit langen Einträgen zu adressieren. Das Leseinterface 
bekommt 9 Bits.

2. Man macht zusätzliche Signale für Upper- und Lowerbyte. Damit ist man 
maximal flexibel und genau solche Speicher gibt es auch in echter 
Hardware zu kaufen. Das Upper- Lowerbyte Signal ist am Ende nur das LSB 
der Adresse (in 1. Näherung):

Bei dem Speicher nimmst du also für das Schreib- und das Leseinterface 
jeweils die gleiche Breite. Hier 36 Bits. Beim Schreiben werden immer 
volle 36 Bits geschrieben. Aber am Leseinterface kannst du noch mit dem 
Zusatzsignal auswählen ob du die obere oder die untere Hälfte der Daten 
haben möchtest. (Das geht auch beim Schreibinterface damit man auch nur 
18 Bits schreiben kann wenn man das möchte)

Wenn man hier schön flexibel sein möchte, dann kann man einfach eine Art 
Maske vorsehen. Bei 36 Bits Breite würde ich 4 zusätzliche Leitungen 
nehmen an Schreibe- und Leseinterface um einzelne 9 Bit Wörter schreiben 
und lesen zu können. Dann schreibst du deine RAM Komponente einmal und 
kannst sie unterschiedlich verwenden. Mal mit 36 Bit Bussen, mal mit 36 
Bit und 18 Bit oder 9 Bit oder ...

Edit: Und hier ein Paar Datenblätter:

Hier das 
http://pdf.datasheetcatalog.com/datasheet2/7/0ylsf0p5ucw0x6qd5ia5tticyo7y.pdf 
hat 32 Bit Breite und 4 Byte writes Signale um die 4 einzelnen Bytes zu 
adressieren.

Das http://www.issi.com/WW/pdf/61VF_LF25636A_51218A.pdf hat auch Byte 
writes.

Das hier https://www.cypress.com/file/42696/download hat 16 Bit Breite 
und ein Byte High Enable und ein Byte Low Enable.

Edit2:

Lothar M. schrieb:
> Stell dir mal vor, das wäre tatsächlich ein RAM mit einem Satz
> Adressleitungen und einem Satz Datenleitungen. Dann könntest du also
> immer 1 Adresse anlegen und 1 Datenwort schreiben. Deine Zuweisungen
> würden aber "gleichzeitig" stattfinden und ergäben so natürlich einen
> Buskonflikt.

Er schreibt ja nicht an irgendwelche Adressen, sondern:

Markus G. schrieb:
> ram(to_integer(unsigned(addr1))) <= input_1;
> ram(to_integer(unsigned(addr1))+1) <= input_2;

Eigentlich könnte da der Synthesizer erkennen (habe ich nicht probiert, 
wird er vermutlich nicht machen), dass das schlicht ein doppelt so 
breiter Bus ist und das genau so bauen wie das der TO hier wollte.

: Bearbeitet durch User
von Markus G. (jim_bobby)


Lesenswert?

Danke für die Antworten. Ich stehe leider nach wie vor ziemlich auf der 
Leitung. Ich habe die CLK miteinbezogen und den Standard Output 
verändert. Jedoch weiß ich nicht wie ich das Problem mit dem 
gleichzeitigen schreiben am besten lösen soll.
Mein Ansatz wäre gewesen ein wait for einzubauen. Dies funktioniert aber 
nicht.
1
begin
2
process(Clk)  
3
begin         
4
outputsignal <= (others=>'Z');
5
6
if(rising_edge(Clk) and en_read = '1') then
7
    outputsignal <= ram(to_integer(unsigned(addr2)));
8
elsif(rising_edge(Clk) and en_write = '1') then
9
    input_1 <= input(17 downto 0);
10
    input_2 <= input(35 downto 18);
11
    ram(to_integer(unsigned(addr1))) <= input_1;
12
    wait for 10ns;
13
    ram(to_integer(unsigned(addr1))+1) <= input_2;
14
15
16
end if;

Edit: Wäre es besser die Originalfrage zu editieren?

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


Lesenswert?

Markus G. schrieb:
> Ich habe die CLK miteinbezogen
Generell wäre so die Sensitivliste unvollständig.

> if(rising_edge(Clk) and en_read = '1') then
>     outputsignal <= ram(to_integer(unsigned(addr2)));
> elsif(rising_edge(Clk) and en_write = '1') then
Steht das so im Userguide? Oder in einer Anleitung? Oder in einem Buch?



Als kleiner Denkanstoß: VHDL kann 100%. Der Simulator kann von den damit 
möglichen Konstrukten 95% simulieren. Und der Synthesizer kann davon 
bestenfalls 5% in Hardware abbilden.

Siehe z.B. das da:
http://www.lothar-miller.de/s9y/archives/80-Hello-World!.html
Schon an den einfachsten Dingen wie einem simplen "after" scheitert der 
Synthesizer.

: Bearbeitet durch Moderator
von Markus G. (jim_bobby)


Lesenswert?

1
> if(rising_edge(Clk) and en_read = '1') then
2
>     outputsignal <= ram(to_integer(unsigned(addr2)));
3
> elsif(rising_edge(Clk) and en_write = '1') then
4
Steht das so im Userguide? Oder in einer Anleitung? Oder in einem Buch?


Habe ich von einem Referenzbeispiel

von Gustl B. (-gb-)


Lesenswert?

Es reicht die steigende Flanke einmal abzufragen und den Rest davon 
abhängig zu machen.

Entweder
1
process (clock) begin
2
   if rising_edge(clock) then
3
   -- hier kommt deine Beschreibung rein.
4
   end if;
5
end process;

oder
1
process begin
2
   wait until rising_edge(clock);
3
   -- hier kommt deine Beschreibung rein.
4
end process;

von Nick M. (Gast)


Lesenswert?

Markus G. schrieb:
> wait for 10ns;

Weisst du, wie die Hardware dafür aussieht?

Du musst zuerst die Schaltung aufzeichnen, dann mit VHDL beschreiben. 
Das ist das 'D' im VHDL.

von Markus G. (jim_bobby)


Lesenswert?

>> wait for 10ns;
>
> Weisst du, wie die Hardware dafür aussieht?
>
Nein, war nur ein Versuch. Hat leider nicht geklappt.

von Markus G. (jim_bobby)


Lesenswert?

Hab es gelöst.
Wenn man auf input_1 und input_2 verzichtet und diese wie folgt schreibt 
funktioniert das Ganze.
1
ram(to_integer(unsigned(addr1))) <= input(17 downto 0);
2
ram(to_integer(unsigned(addr1))+1) <= input(35 downto 18);




Danke an alle die sich zeit genommen haben zu antworten.

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


Lesenswert?

Markus G. schrieb:
> funktioniert das Ganze.
Alerdings: wenn du im Syhtesereport nachschaust, dann siehst du mit 
hoher Wahrscheinlichkeit, dass du keinen RAM-Block instantiiert hast, 
sondern ein RAM aus LUTs und Flipflops zusammengewerkelt hast.

> Wenn man auf input_1 und input_2 verzichtet
Das ergäbe zusammen mit dem Takt nur ein weiteres klitzekleines 
Randphänomen namens "Latency". Mach dich mal schlau, wie und wann 
Signale in Prozessen verarbeitet werden und wann die an sie zugewiesenen 
Daten übernommen werden. Das ist essentiell. Denn dadurch kannst du 
diese prozedurale "top-down Programmierung" wie in C oder Java oder 
Python vergessen. Dieser von dort bekannte "zeilenweise von oben nach 
unten abarbeiten"-Programmierstil funktioniert bei VHDL nicht.

Markus G. schrieb:
> Mein Ansatz wäre gewesen ein wait for einzubauen. Dies funktioniert
> aber nicht.
Ja, im Ernst: du frickelst dich vogelwild durch die Gegend, ohne zu 
wissen, was du da tust oder was herauskommt. Weiter so, wenn das zum 
Bestehen der Klausur reicht und du es hinterher nicht mehr brauchst.

von Nick M. (Gast)


Lesenswert?

Lothar M. schrieb:
> du frickelst dich vogelwild durch die Gegend, ohne zu
> wissen, was du da tust oder was herauskommt.

Vor Jahrzehnten sagte ein Kollege immer nach dem ersten Compile-Lauf der 
ohne Murren durchging:
"Formal ist es schon mal richtig."
Den Spruch werde ich nie vergessen.

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.