Forum: FPGA, VHDL & Co. Dual Port Ram als Block Ram


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Jens W. (jensw)


Lesenswert?

Hallo zusammen,

ich möchte in meinem Design ein Dual Port Ram als Block Ram verwenden.
Jetzt habe ich aber das Verhalten, dass das Block Ram durch Register 
ersetzt wird, sobald ich den zweiten Port schreibend hinzunehme.

So sieht die Implementierung aus:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity DualPort_CacheRam is
6
    Port ( clk : in  std_logic;
7
           PortA_Adr : in  std_logic_vector (7 downto 0);
8
        PortA_Din : in std_logic_vector(7 downto 0);
9
        PortA_Dout : out std_logic_vector(7 downto 0);
10
        PortA_we : in std_logic;
11
        PortA_en : in std_logic;
12
        PortB_Adr : in  std_logic_vector (5 downto 0);
13
        PortB_Din : in std_logic_vector(31 downto 0);
14
        PortB_Dout : out std_logic_vector(31 downto 0);
15
        PortB_we : in std_logic;
16
        PortB_en : in std_logic);
17
end DualPort_CacheRam;
18
19
architecture Behavioral of DualPort_CacheRam is
20
  type mem_type is array(0 to 255) of std_logic_vector(7 downto 0);
21
  signal memory : mem_type := (others => "00000000");
22
23
begin
24
25
  process   
26
  begin
27
    wait until rising_edge(clk);
28
    if(PortA_en = '1') then
29
      if (PortA_we='1') then
30
        memory(to_integer(unsigned(PortA_Adr))) <= PortA_Din;    --PortA write operation
31
      else
32
        PortA_Dout <= memory(to_integer(unsigned(PortA_Adr)));  --PortA read operation
33
      end if;
34
    end if;    
35
    
36
    if(PortB_en = '1') then
37
      if(PortB_we = '1') then
38
--        memory(to_integer(unsigned(PortB_Adr(5 downto 0)) & "00")) <= PortB_Din(7 downto 0);    
39
--        memory(to_integer(unsigned(PortB_Adr(5 downto 0)) & "01")) <= PortB_Din(15 downto 8);
40
--        memory(to_integer(unsigned(PortB_Adr(5 downto 0)) & "10")) <= PortB_Din(23 downto 16);
41
--        memory(to_integer(unsigned(PortB_Adr(5 downto 0)) & "11")) <= PortB_Din(31 downto 24);
42
      else
43
        PortB_Dout(7 downto 0) <= memory(to_integer(unsigned(PortB_Adr(5 downto 0)) & "00"));    
44
        PortB_Dout(15 downto 8) <= memory(to_integer(unsigned(PortB_Adr(5 downto 0)) & "01"));    
45
        PortB_Dout(23 downto 16) <= memory(to_integer(unsigned(PortB_Adr(5 downto 0)) & "10"));  
46
        PortB_Dout(31 downto 24) <= memory(to_integer(unsigned(PortB_Adr(5 downto 0)) & "11"));  
47
      end if;
48
    end if;
49
  end process;
50
51
end Behavioral;

Sobald ich den Teil unter "if(PortB_we = '1') then" auskommentiere ist 
nach der Synthese das FPGA komplett voll und er macht das nicht mehr als 
Block Ram.
Wenn ich es weglasse, dann macht die Synthese was sie soll.
Muss ich das noch als irgendeine Option mitgeben, dass ich ein Block Ram 
möchte?

Viele Grüße, Jens

von Vancouver (vancouver)


Lesenswert?

Um Blockrams zuverlässig zu inferieren, musst du dich mit deinem 
Coding-Style ziemlich akribisch an die Vorgaben des FPGA-Herstellers 
halten. Bei Xilinx stehen die z.B. Synthese-Guide UG901, im Kapitel "RAM 
HDL Coding Guidelines".

von Markus W. (mwww)


Lesenswert?

BRAM:

- aktualisiert den Leseausgang i.A. auch bei we=1
- schreibt undefiniert, wenn beide Ports auf dieselbe Adresse schreiben

Dein Code:

- aktualisiert den Leseausgang nur bei we=0
- schreibt die Daten von Port B, wenn beide auf dieselbe Adresse 
schreiben


Ersteres ist evtl. noch kein Problem für die Synthese, zweiteres zwingt 
die Synthese Register zu nehmen (wenn sie nicht so schlau ist, da noch 
zusätzliche address collision-Logik einzubauen).

Daher (mindestens) drei Möglichkeiten am Beispiel Xilinx:

- Inference wie in UG901
- Instanziierung über IP Memory Generator
- Instanziierung über ein Macro (xpm, 
https://docs.amd.com/r/2020.2-English/ug974-vivado-ultrascale-libraries/XPM_MEMORY_TDPRAM 
)

von Jens W. (jensw)


Lesenswert?

Hallo zusammen,

UG901 hat ein bisschen Licht ins Dunkel gebracht.
Da muss man deutlich mehr machen, damit das funktioniert. Da muss man 
mit Funktionen arbeiten und so weiter.
Das was ich brauche steht 1:1 drin. Hab ich übernommen und jetzt meckert 
er, dass da das Ausgangssignal mehrere Treiber hat.
Da könnte man sicher den Fehler suchen, aber das artet gleich wieder 
aus.
Ich gehe zurück auf den IP Generator, damit funktioniert es tadellos.

Danke euch!

Grüße, Jens

von Martin S. (strubi)


Lesenswert?

Du findest sonst unzaehlige portable TDP-RAM-HDL-Implementierungen im 
Netz, die mit dem VHDL93-Standard zumindest mit A, L, X und inzwischen 
auch yosys-unterstuetzten Architekturen korrekt uebersetzen. Bei VHDL08 
wirds lustig, da gab es Diskrepanzen bei den Tools, bis zu im Detail 
inkorrekten Simulationen. Aeussert sich allerdings dann bei Kollisionen 
und Transparenz-Glitches, aber das wirst du wohl fuer den Anfang nicht 
brauchen. Wenn du nur mit einem Clock arbeitest und nicht auf beiden 
Ports schreibst, tut es auch eine einfache 
Simple-Dualport-Implementierung.

von Jens W. (jensw)


Lesenswert?

Hi Martin,

ich schreibe schon auf beiden Ports, aber nicht gleichzeitig.
Über das Ram übergebe ich Daten zwischen µC und FGPA in beide 
Richtungen.
Die AD-Werte füttert der µC rein und das FPGA rechnet damit den Rest. 
Das Ergebnis, gibt es dann auch wieder über das Ram zurück.

Für den Rest schau ich mich mal um.

Danke dir!

Grüße

von Rick D. (rickdangerus)


Lesenswert?

Jens W. schrieb:
> ich schreibe schon auf beiden Ports, aber nicht gleichzeitig.
Schreiben auf beiden Ports (True-Dual-Port-RAM) bieten die wenigsten 
FPGA-Hersteller.
Wenn das Schreiben sowieso nicht gleichzeitig stattfindet, bietet es 
sich an eine passende State-Machine/Multiplexer/Arbiter vor den 
Schreibport zu setzen.

von Rolf (audiorolf)


Lesenswert?

Dazu hätte ich eine Frage:

Was passiert bei einem true dual port ram, wenn beide Seiten schreiben?

Hier gibt es ein Beispiel von Altera, das so klingt, als sei das intern 
gemanged. Das kann aber irgendwie nicht sein.

https://www.intel.de/content/www/de/de/support/programmable/support-resources/design-examples/horizontal/vhd-true-dual-port-ram-sclk.html

Was mich daran stört ist die Bezeichung dual port, wo es aber nur einen 
Takt hat. Nach meiner Erinnerung (ich habe länger nichts mehr mit Altera 
gemacht) war es aber so, dass das ein true dual port RAM mit zwei 
unterschiedlichen Takten klarkommt und das oben dargestellte ein 
"normales" dual port ram ist.

von Martin S. (strubi)


Lesenswert?

Bei einem synchronen statischem TDP-RAM (mit zwei unterschiedlichen 
Clks) lassen sich zwei 'gleichzeitige' (TBD) Writes auf dieselbe Adresse 
ganz unten in der harten BRAM-Logik abfangen, gibt auch in manchen 
Architekturen die Option das zu aktivieren. Geht nur auf die 
Performance. Ansonsten ist der Inhalt der Speicherzelle beliebig 
korrumpiert, je nachdem wie nahe die Writes zeitlich beeinander sind. 
Ist allerdings gar nicht so einfach, in der HW etwas deterministisches 
wie in der Simulation mit exakt verstimmten Clocks zu provozieren.
Ansonsten kann man bei einem TDP-RAM natuerlich gleichzeitig auf 
verschiedene Adressen schreiben. Entweder stellt man mit Gray-Logik 
sicher, dass die Adressen nie gleich sein koennen, oder nimmt 
grundsaetzlich eine Pingpong-Buffer-Logik her, wenn die Daten in 
synchronen Paketen kommen/gehen.

von Jonas B. (jibi)


Lesenswert?

Rolf schrieb:
> Was passiert bei einem true dual port ram, wenn beide Seiten schreiben?

Naja eine Seite gewinnt. Welche verrät das Handbuch.

von Bradward B. (Firma: Starfleet) (ltjg_boimler)


Angehängte Dateien:

Lesenswert?

>> Was passiert bei einem true dual port ram, wenn beide Seiten schreiben?
>
> Naja eine Seite gewinnt. Welche verrät das Handbuch.

Im Handbuch steht "unknown", was letzlich bedeudet, das der 
FPGA-Designer dafür zu sorgen hat, das dergleichen nicht auftritt, wenn 
er an einem deterministischen Verhalten interessiert ist.

So wie der Ampel-Programmierer Sorge trägt, das nicht alle an einer 
Kreuzung "Grün" bekommen.

(Anhang aus 
http://web.mit.edu/neboat/Public/6.111_final_project/code/blk_mem_gen_ds512.pdf)

: Bearbeitet durch User
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.