Hardware-Entwurf für Single Transfers
Die für dieses Projekt modifizerten Dateien des LogiCore PCI Interfaces und die darüber hinaus benötigten Dateien sind:
Dieser Entwurf implementiert ein PCI-Target ohne Burstunterstützung, das ein 4 KByte (1 K x 32 Bit) großes RAM über den PCI-Bus ansteuert. Der PCI Schnittstellenteil ist praktisch identisch mit dem "Board-Test"-Entwurf und wird daher an dieser Stelle nicht nochmals beschrieben.
Anpassung des LogiCore PCI Interfaces
Das LogiCore PCI Interface wird in der Datei cfg.vhd mit symbolischen Konstanten konfiguriert.
Das PCI-Target belegt einen 4 KByte umfassenden Speicherbereich. Außerdem erfüllt das RAM die Prefetch-Bedingungen. Ansonsten ist die Konfiguration identisch mit der des Projektes "Board-Test".
[...] -------------------------------------------------------------- -- Configure Base Address Registers -------------------------------------------------------------- -- BAR0 : 4 KByte prefetchable memory space cfg_int(0) <= ENABLE ; cfg_int(32 downto 1) <= SIZE4K ; cfg_int(33) <= PREFETCH ; -- Hier nun das Prefetch-Attribut gesetzt! cfg_int(35 downto 34) <= TYPE00 ; cfg_int(36) <= MEMORY ; [...]
Aufbau des RAMs
Der Speicher soll aus den Spartan-II internen Block RAMs aufgebaut werden, den gesamten geplanten Adressraum der PCI-Karte (4 KByte) abdecken und natürlich auf voller PCI-Busbreite (32 Bit) arbeiten. Also wird ein RAM-Modul mit 32 Bit Wortbeite und 1 K (10 Bit) Adressraum benötigt, da 1 K x 32 Bit = 4 KByte.
Das Block RAM Primitiv RAMB4_S4 der Xilinx-Bibliothek hat einen 10 Bit Adressraum und eine Wortbreite von 4 Bit:
-- Primitiv aus der Xilinx-Bibliothek entity RAMB4_S4 is [...] port ( ADDR : in std_logic_vector(9 downto 0); -- Adresse CLK : in std_logic; -- Takt DI : in std_logic_vector(4 downto 0); -- Dateneingang EN : in std_logic; -- RAM Enable (Chip Select) RST : in std_logic; -- Reset (Initialisierung des Speichers) WE : in std_logic; -- Write Enable DO : out std_logic_vector(4 downto 0) -- Datenausgang ); end RAMB4_S4;
Bei der Implementierung wird es auf genau eines der 14 Block RAMs im Spartan-II abgebildet. Ausführliche Erklärungen zu den Block RAMs des Spartan-II befinden sich in der Spartan-II Dokumentation (Functional Description). Hier nur die wesentlichen Stichpunkte:
- Das RAM arbeitet taktsynchron, d.h. Eingangssignale werden bei steigender Taktflanke ausgewertet, und die Ausgänge sind bei steigender Taktflanke gültig.
- EN = 1: Der Inhalt der Speicherzelle mit Adresse ADDR wird mit der nächsten steigenden Taktflanke auf DO ausgegeben.
- EN = 1: Zu schreibende Daten werden an DI angelegt und WE auf 1 gesetzt. Mit der nächsten steigenden Flanke erfolgt die Speicherung in die Speicherzelle mit Adresse ADDR.
- EN = 0: Ist EN (Chip Select) nicht gesetzt, so behält der Datenausgang DO seinen letzten Zustand bei. Außerdem kann nichts gespeichert werden.
Durch "Parallelschaltung" von 8 dieser RAMB4_S4 Instanzen (8 x 4 Bit = 32 Bit Wortbreite) erhält man die gewünschte RAM-Struktur bram_1k_32b. Die separaten Dateneingangs- und Datenausgangsbusse werden zum bidirektionalen Bus DIO zusammengefasst, siehe Abbildung.
Abb.: Aufbau des Moduls bram_1k_32b. Das Speicher-Schaltzeichen beinhaltet 8 zusammengefasste RAMB_S4 Instanzen wie im Text beschrieben.
Als VHDL-Code für das bram_1k_32b Modul ergibt sich damit (gekürzt):
library IEEE; use IEEE.STD_LOGIC_1164.ALL; -- Einbinden der Xilinx Bibliothek library UNISIM; use UNISIM.Vcomponents.ALL; entity bram_1k_32b is port ( ADDR : in std_logic_vector(9 downto 0); -- Adresse CLK : in std_logic; -- Takt DIO : inout std_logic_vector(31 downto 0); -- bidirektionaler Datenbus (Tristate) OE : in std_logic; -- Output Enable RST : in std_logic; -- Reset (Initialisierung des RAMs) WE : in std_logic -- Write Signal ); end bram_1k_32b; architecture rtl of bram_1k_32b is constant LOGIC_1 : std_logic := '1'; -- Datenausgaenge der Block RAMs signal dout : std_logic_vector(31 downto 0); begin -- Daten auf den Bus, wenn OE = '1' und WE = '0' (lesen) DIO <= dout when OE = '1' and WE = '0' else (others => 'Z'); -- Block RAM fuer die Datenbits 0 - 3 blockram_inst0 : RAMB4_S4 port map ( ADDR => ADDR(9 downto 0), CLK => CLK, DI => DIO(3 downto 0), EN => LOGIC_1, RST => RST, WE => WE, DO => dout(3 downto 0) ); -- Block RAM fuer die Datenbits 7 - 4 blockram_inst1 : RAMB4_S4 port map ( ADDR => ADDR(9 downto 0), CLK => CLK, DI => DIO(7 downto 4), EN => LOGIC_1, RST => RST, WE => WE, DO => dout(7 downto 4) ); [...] -- Block RAM fuer die Datenbits 31 - 28 blockram_inst7 : RAMB4_S4 port map ( ADDR => ADDR(9 downto 0), CLK => CLK, DI => DIO(31 downto 28), EN => LOGIC_1, RST => RST, WE => WE, DO => dout(31 downto 28) ); end rtl;
Die Datei bram_1k_32b.vhd wird dem Projekt hinzugefügt.
Der Hardware-Entwurf des PCI-Targets
Da außer dem PCI-Interface keine I/O-Pins verwendet werden, wird neben der Konfigurationsdatei cfg.vhd, siehe oben, nur die Datei userapp.vhd verändert. Das Modul bram_1k_32b wird als Komponente eingebunden und instanziiert. Der PCI-Teil des Entwurfs stimmt mit dem des Projektes "Board-Test" weitgehend überein. Er ist sogar noch einfacher, weil die gesamte Adressdekodierung RAM-intern geschieht. Deshalb wird an dieser Stelle auf eine nochmalige ausführliche Erläuterung verzichtet und direkt auf den VHDL-Code verwiesen.