Hallo, ich bekomme alle x ns zwei STD_LOGIC_VECTOREN als Eingangssignal mit jeweils einer Größe von y Bit und möchte diese zeitgleich (parallel) in einen Speicher oder Register schreiben? Nach z Taktzyklen ist der Speichervorgang vorbei und beginnt nach einigen us erneut. Welche Form von Speicher ist für eine solche Prozedur am geeignetesten? Register / Blockram? Der Speicher müsste auch wieder parallel ausgelesen werden, um Beispielsweise das erste 8 Bit Speicherelement mit dem Zweiten 8 Bit Speicherelement im ersten Takt und das dritte Element mit dem 4 Element im 2 Takt etc. pp zu vergleichen? Gruß,
Da Du für x, y und z keine konkreten Zahlen nennst, kann man Dir auch nicht wirklich helfen. Tom schrieb: > Der Speicher müsste auch wieder parallel ausgelesen > werden, um Beispielsweise das erste 8 Bit Speicherelement mit dem > Zweiten 8 Bit Speicherelement im ersten Takt und das dritte Element mit > dem 4 Element im 2 Takt etc. pp zu vergleichen? Vielleicht helfen Dir dabei zwei Speicher, die abwechselnd beschrieben werden? Duke
Hi Duke, in einem Takt bekomme ich zwei mal 8 Bit Signale des Typs STD_LOGIC_VECTOR. Nach 1000 Takten also 2000 Signalwerte. Diese 2000 Werte bilden meine Gesamtinformation. Ich möchte also mit jedem Takt die zwei 8 Bit Signale in einem Speicher / Register speichern, sodass mir nach 1000 Takten die kompletten Informationen in meinem Speicher vorliegen. Also irgendwie in der Art: "schreibe die 8 Bit des signal's A im Takt 1 an adresse "00000000" in und schreibe die 8 Bit des signal's B im Takt 1 an adresse "00000001" schreibe die 8 Bit des signal's A im Takt 2 an adresse "00000010" in und schreibe die 8 Bit des signal's B im Takt 2 an adresse "00000011" . . .
Tom schrieb: > Welche Form von Speicher ist für eine solche Prozedur am geeignetesten? Tom schrieb: > Diese 2000 > Werte bilden meine Gesamtinformation. Also 16 kBit. Dafür reicht der interne Speicher im FPGA, bei Xilinx BRAM genannt. Duke
Wenn ich mit Hilfe des Core Generators von Xilinx die Block RAM Funktion erstelle, kommt für diese Aufgabe doch nur der True Dual Port Ram in Frage, oder? Beide Eingänge benötigen einen gemeinsamen Takt (Common Clock). Was ich noch nicht so richtig verstanden habe, ist, wie das mit dem Write Enable funktionert. (Use Byte Write Enable mit ByteSize 8 und 9 Bits). Als Algorithmus würde ich "Minumum Area" nehmen. Sollte als erstes geschrieben oder gelesen werden? Da vorher noch nichts im Ram drin steht, würde ich schreiben sagen. Und ein enable Pin macht in diesem Fall ja auch Sinn. Fragen über Fragen ;-) Vielen Dank und schönes Wochenende, Tom
> Sollte als erstes geschrieben oder gelesen werden?
Was denkst du denn was drinne steht wenn du noch nichts reingeschrieben
hast ? Macht das Sinn für dich ? Erst ein Blatt Papier lesen und dann
erst was draufschreiben ?
Uwe schrieb: > Was denkst du denn was drinne steht wenn du noch nichts reingeschrieben > hast ? Macht das Sinn für dich ? Erst ein Blatt Papier lesen und dann > erst was draufschreiben ? klar, da gebe ich Dir recht, wie ich es auch schon geschrieben hatte. Jedoch wusste ich nicht, ob evtl. noch mehr dahinter steckt.
Mach den Speicher 16 bit breit, da brauchst du keinen Dualport RAM, ausser du möchtest schreiben und lesen entkoppeln.
Lattice User schrieb: > Mach den Speicher 16 bit breit, da brauchst du keinen Dualport RAM, > ausser du möchtest schreiben und lesen entkoppeln. Und ich kann trotzdem zwei Adressen gleichzeitig beschreiben und beim Lesen zwei Adressen gleichenzeitig auslesen? Denn wenn ich im CoreGen nur SinglePort Ram auswähle, habe ich auch nur einen Eingang und einen Ausgang. Meinst Du damit, dass ich dann die 2 * 8 Bit Werte mit dem '&' Operator verknüpfe und diese dann als 1 Signal in den Ram schreibe? Später sollte es evtl. auf eine Größe von 4096 * 12 Bit Werten vergrößtert werden. Lässt sich das alles mit einem Takt realisieren, denn in der nächsten aktiven Taktflanke liegen zwei neue Signale an A und B an? Welchen Nachteil hat DualPort Ram für diese Anwendung gegenüber dem SinglePort? Deutlich mehr Ressourcenverbrauch? Empfiehlt es sich, den Ram mit Hilfe des CoreGen zu realisieren oder beispielsweise mit so einer einfachen Implementrierung? http://www.doulos.com/knowhow/vhdl_designers_guide/models/simple_ram_model/ Was den Coregenerator betrifft, bin ich dann an die jeweils vorher definierte Hardware gebunden? Was würde ich nur ohne das super Forum machen ;-) Vielen Dank, Tom
Tom schrieb: > Lattice User schrieb: >> Mach den Speicher 16 bit breit, da brauchst du keinen Dualport RAM, >> ausser du möchtest schreiben und lesen entkoppeln. > > Und ich kann trotzdem zwei Adressen gleichzeitig beschreiben und beim > Lesen zwei Adressen gleichenzeitig auslesen? Klar, wenn Deine Daten auf der gleichen Adresse liegen. > Denn wenn ich im CoreGen > nur SinglePort Ram auswähle, habe ich auch nur einen Eingang und einen > Ausgang. Logisch. > Meinst Du damit, dass ich dann die 2 * 8 Bit Werte mit dem '&' Operator > verknüpfe und diese dann als 1 Signal in den Ram schreibe? Ganz genau. > Später sollte es evtl. auf eine Größe von 4096 * 12 Bit Werten > vergrößtert werden. Schau ins Datenblatt, welche Speicheranordnungen unterstützt werden. > Lässt sich das alles mit einem Takt realisieren, denn in der nächsten > aktiven Taktflanke liegen zwei neue Signale an A und B an? Ja. > Welchen Nachteil hat DualPort Ram für diese Anwendung gegenüber dem > SinglePort? Deutlich mehr Ressourcenverbrauch? Nein. Kein Nachteil. Die zwei Ports sind sowieso vorhanden. Aber wenn man nur Singleport braucht, sieht der Code mit Singleport einfacher aus, als mit Dualport-BRAM. > Empfiehlt es sich, den Ram mit Hilfe des CoreGen zu realisieren oder > beispielsweise mit so einer einfachen Implementrierung? CoreGen hab ich für BRAMs noch nie verwendet. Der macht die Sache m.E. nur unnötig kompliziert und unportabel. > http://www.doulos.com/knowhow/vhdl_designers_guide... Ja, das ist gut. Das steht (leider mit veralteten Synopsis-Libs) auch im XST USer Guide. > Was den Coregenerator betrifft, bin ich dann an die jeweils vorher > definierte Hardware gebunden? Jain. Duke
Du brauchst doch gar kein solches Modul einbinden, mMn nur umständlich. (ohne Gewähr)
1 | type t_bram is array(0 to 4095) of std_logic_vector(23 downto 0); |
2 | signal bram : t_bram; |
3 | |
4 | signal write_address : integer range 0 to 4095 := 0; |
5 | ...
|
6 | process (clk) |
7 | begin
|
8 | if rising_edge(clk) then |
9 | |
10 | if write_enable='1' then |
11 | write_address <= write_address + 1; |
12 | bram(write_address) <= Data_in1 & Data_in2 |
13 | end if; |
14 | ...
|
15 | data_out1 <= bram(read_adress1/2)(11 downto 0); |
16 | data_out2 <= bram(read_adress2/2)(23 downto 12); |
17 | |
18 | end if; |
19 | end process; |
Nur so als Beispiel. Du kannst mit dieser Beischreibungsform beliebig viele Leseports an ein solches Ram hängen, die Synthese findet selbst raus wie sie das sinnvoll in HW abbilden kann.(z.b. Daten mehrfach speichern) Klar sollte man vor Augen haben was dabei passiert, aber das muss man so oder so. Coregen für solche Sachen ist absoluter Overkill.
Hey, vielen Dank für die super Hilfe. Das werde ich ausprobieren. Hoffentlich werde ich eines Tages auch mal so viel Helfen können ;-) Gruß, Tom
Eine (vielleicht dumme) Frage habe ich bezüglich des BRAMS noch. BRAM steht doch für Block RAM. Bezieht sich das BRAM auf einen im FPGA bezogenen Speicher (gibt es auch FPGA's ohne BRAM?) oder wird nur der Aufbau des Speichers in Form von Blöcken so bezeichnet und BRAM würde sich immer implementieren lassen?
In dem Beispiel bezieht hab ich einfach irgenteinen Namen gegeben. Typischerweise versteht man unter BRAM schon einen konkreten Speichertyp im FPGA(Blockram). Wer der Synthese vertraut, wird aber feststellen das bei halbwegs geeineter Schreibweise die Tools anhand von gewählter Speichertiefe und Breite schon von alleine feststellen welcher Speicher sinnvoll zu verwenden ist. Andere Typen selbst auswählen kann man aber auch ganz einfach im Code über attribute (unterscheiden sich ggf je nach Hersteller). Macht aber fast nur dann Sinn wenn die Ressourcen(BRAMs oder LUTs oder FFs) knapp werden.
Vergessen: Bei dem Beispiel oben ist es ein 24 Bit mal 4096 Ram. Das würde in Blockram bei den meisten neuen FPGAs genau ein BRAM ergeben bzw eins pro frei adressierbarem Leseport. In FFs wären das 98304FF, das baut die Synthese nur wenns ganz übel hingeschrieben ist. Als distributed Ram (in LUTs, falls unterstützt) sind das je nach Lutgröße 1500 bis 6000 Luts, was auch schon sehr viel ist. Deshalb lege ich jetzt mal meine Hand dafür ins Feuer das die aktuellen Tools von Synopsys, Xilinx und Altera dort allesamt selbst die Entscheidung treffen werden daraus Blockram zu bauen solange welcher Verfügbar ist. Nimmst du die Speichertiefe z.b. auf 16 runter, dann wird in den allermeisten Fällen hingegen Distributed Ram rauskommen. Probiers einfach mal, du siehst im Report ja was er draus macht.
Super, vielen Dank für die tolle Erklärung. Der RAM sieht bei mir jetzt folgendermaßen aus:
1 | library IEEE; |
2 | use IEEE.STD_LOGIC_1164.ALL; |
3 | use ieee.numeric_std.all; |
4 | |
5 | |
6 | entity BRAM is |
7 | port( |
8 | clk : IN std_logic; |
9 | data_a : IN std_logic_vector(11 downto 0); |
10 | data_b : IN std_logic_vector(11 downto 0); |
11 | write_enable : IN std_logic; |
12 | data_out_a: OUT std_logic_vector(11 downto 0); |
13 | data_out_b: OUT std_logic_vector(11 downto 0) |
14 | );
|
15 | end BRAM; |
16 | |
17 | |
18 | architecture Behavioral of BRAM is |
19 | type t_bram is array(0 to 4095) of std_logic_vector(23 downto 0); |
20 | signal bram : t_bram; |
21 | |
22 | signal write_address : integer range 0 to 4095 := 0; |
23 | signal read_address : integer range 0 to 4095 := 0; |
24 | |
25 | |
26 | begin
|
27 | |
28 | |
29 | |
30 | process (clk) |
31 | begin
|
32 | if rising_edge(clk) then |
33 | |
34 | if write_enable='1' then |
35 | write_address <= write_address + 1; |
36 | bram(write_address) <= data_a & data_b; |
37 | end if; |
38 | |
39 | |
40 | data_out_a <= bram(write_address)(11 downto 0); |
41 | data_out_b <= bram(write_address)(23 downto 12); |
42 | |
43 | |
44 | end if; |
45 | end process; |
46 | |
47 | |
48 | |
49 | end Behavioral; |
Wenn write_enable jetzt nun 1 ist, wird mit jeden Takt der Wert von data_a und data_b hintereinander gehangen in den Speicher an n-ter(write_address) Zeile gespeichert. Sobald write_enable wieder null wird, sind somit alle Werte im Ram gespeichert, wenn ich das richtig verstanden habe. Was ich noch nicht ganz verstanden habe ist, was genau mit
1 | data_out1 <= bram(read_adress1/2)(11 downto 0); |
2 | data_out2 <= bram(read_adress2/2)(23 downto 12); |
Muss read_adress nicht bei data_out1 und data_out2 nicht dieselbe sein? Ich habe mir nun mal eine TB geschrieben, um den Eingängen mal ein paar Werte zuzuweisen.
1 | LIBRARY ieee; |
2 | USE ieee.std_logic_1164.ALL; |
3 | Use ieee.numeric_std.all; |
4 | |
5 | |
6 | |
7 | ENTITY tb_BRAM_24x4096 IS |
8 | END tb_BRAM_24x4096; |
9 | |
10 | ARCHITECTURE behavior OF tb_BRAM_24x4096 IS |
11 | |
12 | -- Component Declaration for the Unit Under Test (UUT)
|
13 | |
14 | COMPONENT BRAM |
15 | PORT( |
16 | clk : IN std_logic; |
17 | data_a : IN std_logic_vector(11 downto 0); |
18 | data_b : IN std_logic_vector(11 downto 0); |
19 | write_enable : IN std_logic; |
20 | data_out_a : OUT std_logic_vector(11 downto 0); |
21 | data_out_b : OUT std_logic_vector(11 downto 0) |
22 | );
|
23 | END COMPONENT; |
24 | |
25 | |
26 | --Inputs
|
27 | signal clk : std_logic := '0'; |
28 | signal data_a : std_logic_vector(11 downto 0) := (others => '0'); |
29 | signal data_b : std_logic_vector(11 downto 0) := (others => '0'); |
30 | signal write_enable : std_logic := '0'; |
31 | |
32 | --Outputs
|
33 | signal data_out_a : std_logic_vector(11 downto 0); |
34 | signal data_out_b : std_logic_vector(11 downto 0); |
35 | |
36 | -- Clock period definitions
|
37 | constant clk_period : time := 25 ns; |
38 | |
39 | BEGIN
|
40 | |
41 | -- Instantiate the Unit Under Test (UUT)
|
42 | uut: BRAM PORT MAP ( |
43 | clk => clk, |
44 | data_a => data_a, |
45 | data_b => data_b, |
46 | write_enable => write_enable, |
47 | data_out_a => data_out_a, |
48 | data_out_b => data_out_b |
49 | );
|
50 | |
51 | -- Clock process definitions
|
52 | clk_process :process |
53 | begin
|
54 | clk <= '0'; |
55 | wait for clk_period/2; |
56 | clk <= '1'; |
57 | wait for clk_period/2; |
58 | end process; |
59 | |
60 | |
61 | |
62 | stim_proc: process |
63 | begin
|
64 | |
65 | |
66 | |
67 | |
68 | data_a <= std_logic_vector(to_unsigned(268,12)) after 200 ns, |
69 | std_logic_vector(to_unsigned(578,12)) after 225 ns, |
70 | std_logic_vector(to_unsigned(898,12)) after 250 ns, |
71 | std_logic_vector(to_unsigned(0,12)) after 275 ns; |
72 | |
73 | |
74 | data_b <= std_logic_vector(to_unsigned(268,12)) after 200 ns, |
75 | std_logic_vector(to_unsigned(578,12)) after 225 ns, |
76 | std_logic_vector(to_unsigned(898,12)) after 250 ns, |
77 | std_logic_vector(to_unsigned(0,12)) after 275 ns; |
78 | |
79 | |
80 | |
81 | |
82 | write_enable <= '0', '1' after 200 ns, '0' after 275 ns; |
83 | |
84 | |
85 | |
86 | |
87 | |
88 | |
89 | |
90 | |
91 | wait; |
92 | end process; |
93 | |
94 | END; |
Leider waren die Werte für data_out_a und data_out b null. Liegt das daran, dass ich hier versuche gleichzeitig zu schreiben und zu lesen? Wie kann ich nun sehen, was in den Speicher geschrieben wurde? Muss ich dann nachdem der Speicher beschrieben wurde und der write_enable wieder null auslesen? Wenn ich von aussen, also von einer übergeordneten Ebene den Speicher auslesen möchte, benötige ich dafür nicht auch ein read_enable? Sorry für die vielen Fragen und Danke nochmal für die tolle Hilfe!
Ich habe es nun mit einem zusätzlichen enable Eingang gelöst. Wenn enable und write enable = '1' sind, dann wird geschrieben, wenn enable 1 und write enable 0 ist, wird gelesen.
Sind die Adressen bei Xilinx BRAM'S immer vom Typ Integer? Ist die 1. Adresse eines Xilinx Block RAM's mit Null definiert, oder mit einer 1?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.