Projekte.PCI-Tutorial-TGEntwSingle (Struktur)


Single Transfer PCI Target mit 4 KByte RAM

Übersicht

Die für dieses Projekt modifizerten Dateien des LogiCore PCI Interfaces und die darüber hinaus benötigten Dateien sind [xc2s200fg456_32_33.ucf] [pcim_top.vhd] [bram_1k_32b.vhd] [cfg.vhd] [userapp.vhd] .

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.


Autor: gkemnitz, Letzte Änderung: 14.04.2011 15:09:59


 TU Clausthal 2020  Impressum