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.