Ich habe Probleme, das Design meines FPGA 80-Computers auf ein Papilio Duo-Board zu bekommen, das ein Spartan 6 - xcs6slx9 ist. Das Problem rührt daher, dass RAM als verteilt statt als Block gefolgert wird.
Kurzversion: Ich verwende eine generische Entität, um die RAM-Blöcke abzuleiten (siehe unten), und stelle fest, dass alles bis zu einer Adressbreite von 11 verteilt zu sein scheint, eine Adressbreite von 12 oder mehr XST ist glücklich, es auszudrücken in Blöcke. Ich habe versucht, Attribute als Block zu markieren, aber das scheint nicht zu funktionieren.
Aktuelle Lösung: Erweitern Sie die Adressbreite einer Instanz, indem Sie das hohe Adressbit auf Null setzen ... jetzt passt das Design.
Lange Version :
Das Design erfordert drei Dual-Port-2048x8-Bit-RAM-Module. Ein Port benötigt Lese-/Schreibzugriff (CPU-Zugriff), der andere nur Lesezugriff (Videocontroller). Die Ports sind asynchron und laufen auf unterschiedlichen Taktdomänen.
Ursprünglich habe ich dieses Modul verwendet: RamDualPort dafür.
entity RamDualPort is
generic
(
ADDR_WIDTH : integer;
DATA_WIDTH : integer := 8
);
port
(
-- Port A
clock_a : in std_logic;
clken_a : in std_logic;
addr_a : in std_logic_vector(ADDR_WIDTH-1 downto 0);
din_a : in std_logic_vector(DATA_WIDTH-1 downto 0);
dout_a : out std_logic_vector(DATA_WIDTH-1 downto 0);
wr_a : in std_logic;
-- Port B
clock_b : in std_logic;
addr_b : in std_logic_vector(ADDR_WIDTH-1 downto 0);
dout_b : out std_logic_vector(DATA_WIDTH-1 downto 0)
);
end RamDualPort;
architecture behavior of RamDualPort is
constant MEM_DEPTH : integer := 2**ADDR_WIDTH;
type mem_type is array(0 to MEM_DEPTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
shared variable ram : mem_type;
begin
process (clock_a)
begin
if rising_edge(clock_a) then
if clken_a='1' then
if wr_a = '1' then
ram(to_integer(unsigned(addr_a))) := din_a;
end if;
dout_a <= ram(to_integer(unsigned(addr_a)));
end if;
end if;
end process;
process (clock_b)
begin
if rising_edge(clock_b) then
dout_b <= ram(to_integer(unsigned(addr_b)));
end if;
end process;
end;
Ein paar Probleme damit: 1) Abhängig von der Adressbreite werden einige als verteilt gefolgert (das Hauptproblem, nach dem ich frage), aber auch 2) diejenigen, die auf Block-RAMS gefolgert wurden, wurden als Read-First implementiert, wofür async clocks hat Probleme mit Spartan 6.
Die einzige Möglichkeit, das Read-First-Problem zu beheben, bestand darin, beide Ports mit einem neuen Modul „RamTrueDualPort“ wie folgt lesen/schreiben zu lassen:
entity RamTrueDualPort is
generic
(
ADDR_WIDTH : integer;
DATA_WIDTH : integer := 8
);
port
(
-- Port A
clock_a : in std_logic;
clken_a : in std_logic;
addr_a : in std_logic_vector(ADDR_WIDTH-1 downto 0);
din_a : in std_logic_vector(DATA_WIDTH-1 downto 0);
dout_a : out std_logic_vector(DATA_WIDTH-1 downto 0);
wr_a : in std_logic;
-- Port B
clock_b : in std_logic;
clken_b : in std_logic;
addr_b : in std_logic_vector(ADDR_WIDTH-1 downto 0);
din_b : in std_logic_vector(DATA_WIDTH-1 downto 0);
dout_b : out std_logic_vector(DATA_WIDTH-1 downto 0);
wr_b : in std_logic
);
end RamTrueDualPort;
architecture behavior of RamTrueDualPort is
constant MEM_DEPTH : integer := 2**ADDR_WIDTH;
type mem_type is array(0 to MEM_DEPTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
shared variable ram : mem_type;
begin
process (clock_a)
begin
if rising_edge(clock_a) then
if clken_a='1' then
if wr_a = '1' then
ram(to_integer(unsigned(addr_a))) := din_a;
end if;
dout_a <= ram(to_integer(unsigned(addr_a)));
end if;
end if;
end process;
process (clock_b)
begin
if rising_edge(clock_b) then
if clken_b='1' then
if wr_b = '1' then
ram(to_integer(unsigned(addr_b))) := din_b;
end if;
dout_b <= ram(to_integer(unsigned(addr_b)));
end if;
end if;
end process;
end;
Das hat also das Read-First-Problem behoben und die RAMs, die den RAM blockieren, sind jetzt als Write-First implementiert (NB: Ich interessiere mich eigentlich nicht für Read-First/Write-First, außer für das Spartan 6-beschädigte RAM-Read-First-Problem ).
Jetzt besteht das Problem darin, die kleineren 2k-Instanzen (addrWidth 11) auf Block-RAM zu bekommen. Wie bereits erwähnt, habe ich Attribute ausprobiert, aber es besteht immer noch darauf, es in den verteilten RAM zu stecken. Ich konnte keine Dokumentation zu ram_style für Variablen (im Gegensatz zu Signalen) finden, habe aber Folgendes vermutet: (Beachten Sie das Bit ram:variable
)
constant MEM_DEPTH : integer := 2**ADDR_WIDTH;
type mem_type is array(0 to MEM_DEPTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
shared variable ram : mem_type;
ATTRIBUTE ram_extract: string;
ATTRIBUTE ram_extract OF ram:variable is "yes";
ATTRIBUTE ram_style: string;
ATTRIBUTE ram_style OF ram:variable is "block";
Jetzt spuckt XST dies aus, was darauf hindeutet, dass die Attributsyntax verstanden wird: (Beachten Sie die Erwähnung von ram_extract
und ram_style
)
Synthesizing Unit <RamTrueDualPort_1>.
Related source file is "C:/Documents and Settings/Brad/Projects/fpgabee/Hardware/FPGABeeCore/RamTrueDualPort.vhd".
ADDR_WIDTH = 12
DATA_WIDTH = 8
Set property "ram_extract = yes" for signal <ram>.
Set property "ram_style = block" for signal <ram>.
Found 4096x8-bit dual-port RAM <Mram_ram> for signal <ram>.
Found 8-bit register for signal <dout_b>.
Found 8-bit register for signal <dout_a>.
Summary:
inferred 1 RAM(s).
inferred 16 D-type flip-flop(s).
inferred 1 Multiplexer(s).
Unit <RamTrueDualPort_1> synthesized.
Synthesizing Unit <RamTrueDualPort_2>.
Related source file is "C:/Documents and Settings/Brad/Projects/fpgabee/Hardware/FPGABeeCore/RamTrueDualPort.vhd".
ADDR_WIDTH = 11
DATA_WIDTH = 8
Set property "ram_extract = yes" for signal <ram>.
Set property "ram_style = block" for signal <ram>.
Found 2048x8-bit dual-port RAM <Mram_ram> for signal <ram>.
Found 8-bit register for signal <dout_b>.
Found 8-bit register for signal <dout_a>.
Summary:
inferred 1 RAM(s).
inferred 16 D-type flip-flop(s).
inferred 2 Multiplexer(s).
Unit <RamTrueDualPort_2> synthesized.
Die 2k-Blöcke werden jedoch immer noch verteilt:
2048x8-bit dual-port distributed RAM : 2
4096x8-bit dual-port block RAM : 1
Wenn ich die redundante Adresszeile herausnehme (also: zurück auf addrWidth=11 stelle), landen alle drei Instanzen verteilt und das Design passt nicht mehr:
2048x8-bit dual-port distributed RAM : 3
Was zu tun ist? Ich möchte wirklich nicht zurück zu Coregen dafür wechseln.
PS: Ich bin ein Amateur darin - sei sanft!.
Wenn Sie genau wissen, was Sie am Ende haben möchten, muss Xst nicht versuchen, es aus einem Verhaltensmodell abzuleiten.
Sie können ein Block-RAM-Objekt direkt im HDL-Code instanziieren. Einzelheiten zur geeigneten Syntax und den beteiligten Optionen finden Sie in Xilinx UG615: Spartan-6 Libraries Guide for HDL Designs , um Seite 274 ("RAMB16BWER"). Sie können auch das Makro BRAM_TDP_MACRO verwenden, das auf Seite 20 desselben Dokuments erläutert wird.
Sie müssen mit der Funktionsweise des Spartan-6-Block-RAM-Elements vertraut sein. Informationen dazu finden Sie in Xilinx UG383: Spartan-6 FPGA Block RAM Resources .
(Beachten Sie, dass das Block-RAM Standardbreiten von 9, 18 oder 36 Bit hat; Sie werden es wahrscheinlich im 9-Bit-Modus verwenden und das zusätzliche Bit einfach ignorieren wollen. Es ist für Designs da, die Paritätsbits benötigen.)
Erstens sind die unterstützten Codierungsstile im Synthese-Benutzerhandbuch dokumentiert. Für Xilinx ISE wäre dies das XST-Benutzerhandbuch . Ab Seite 200 wird erläutert, was unterstützt wird. Leider verwendet es die alte conv_integer
Funktion, aber Sie verwenden bereits die neueren numeric_std
Äquivalente, und diese funktionieren genauso mit XST. Wenn Sie eine ältere Version dieses Dokuments finden, enthält sie viel mehr Beispiele. Es gibt auch Beispiele in ISE; Gehen Sie zu Bearbeiten > Sprachvorlagen > VHDL > Synthesekonstrukte > Codierungsbeispiele > RAM. Dazu gehört ein spezielles „Write-First“-Beispiel unter Block RAM > Dual Port > Asymmetric Ports.
Nun zu deinen Problemen:
Je nach Adressbreite werden einige als verteilt gefolgert
Dies ist Absicht. Es ist in den meisten Fällen nicht effizient, einen Block-RAM zu verwenden, wenn die Adressbreite klein ist. Wie klein „klein“ ist, hängt von der verwendeten Gerätefamilie ab, aber im Allgemeinen, wenn eines der Datenbits unter Verwendung von LUT-Ressourcen implementiert werden kann und in einen kombinatorischen Logikblock (CLB) passt, dann wird auch ein verteilter RAM funktionieren wie ein Block-RAM hätte.
Sehen Sie sich für Ihr spezielles Gerät UG384 an, das einen Abschnitt über „Verteilter RAM“ enthält, der eine Tabelle enthält, die auflistet, welche RAM-Größen effizient in einem einzelnen CLB implementiert werden können. Das heißt nicht, dass größere Speicher kein verteiltes RAM verwenden können, nur dass ein größerer als dieser nicht mit einer so hohen Taktrate arbeitet wie ein Block-RAM.
Darüber hinaus kann verteiltes RAM keinen "echten" Dual-Port-Speicher implementieren. Wenn Sie also mehr als einen Port haben und entweder mehrere Leseadressen separat verwendet werden oder mehrere Schreibfreigaben separat verwendet werden, sollte XST auf Block-RAM schließen. Normalerweise verwenden Sie nur die Schreibschnittstelle eines Ports und die Leseschnittstelle eines anderen, daher ist dies meiner Erfahrung nach selten ein Faktor.
Sie sollten sich normalerweise keine Gedanken darüber machen, ob ein Speicher mit verteiltem oder Block-RAM implementiert wird. Betrachten Sie es als Vorteil; Da Sie den RAM abgeleitet haben, anstatt ein Primitiv zu instanziieren oder CoreGen zu verwenden, können die Tools auswählen, wie es implementiert werden soll, basierend auf den im Gerät verfügbaren Ressourcen, den von Ihnen eingerichteten Einschränkungen, den Syntheseeinstellungen usw.
Es scheint seltsam, dass, wenn Ihr Design nicht passt, es die verfügbaren Block-RAMs nicht nutzt. Ich glaube nicht, dass du etwas falsch machst. Wenn Sie wirklich einen Block-RAM erzwingen möchten, können Sie versuchen, mit der rechten Maustaste auf „Synthesize“ zu klicken, „Prozesseigenschaften“ auszuwählen und dann unter „HDL-Optionen“ den „RAM-Stil“ in „Block“ zu ändern. Beachten Sie, dass die Beschreibung für „Auto“ in der Hilfe lautet:
XST bestimmt die beste Implementierung für jedes Makro.
Im Allgemeinen wäre ich vorsichtig, dies zu ändern, ohne genau zu verstehen, warum das Design durch das Erzwingen von Block-RAM besser wird. Diese Option könnte auch sehr verschwenderisch sein, wenn Sie viele kleine Erinnerungen haben.
Sie können auch versuchen, die Synthese „Optimierungsziel“ auf „Bereich“ zu setzen. Dies kann den Schwellenwert ändern, wann ein Block-RAM über einen verteilten RAM abgeleitet wird.
Stellen Sie abschließend sicher, dass Sie die neueste Tool-Version (14.7) verwenden.
ram_style
Attribut nur zum Erzwingen von verteiltem RAM funktioniert und nicht umgekehrt. Ich spüre deinen Frust!Also ein kurzes Follow-up ... als Teil der Umstellung auf Coregen-Block-RAM habe ich meinen vorhandenen RamTrueDualPort in ein anderes Modul gepackt, das effektiv gerade durchgelaufen ist. (Die Absicht war, Generika zu verwenden, um die tatsächliche zugrunde liegende Implementierung zu wechseln).
Es stellte sich heraus, dass ich das nicht brauchte - einfach das ursprüngliche Modul in ein anderes zu packen, reichte für XST aus, um Block-Ram für die kleineren Blöcke abzuleiten.
Stelle dir das vor...
Paebbels
Brad Robinson
Paebbels