In diesem Kapitel wird der Entwurf eines möglichst einfachen PCI-Targets ohne Burstunterstützung beschrieben. Das Target implementiert drei 32 Bit Register:
Bit 31 - Bit 1 | Bit 0 |
nicht benutzt | USER ("USER_LED") |
Bit 31 - Bit 8 | Bit 7 - Bit 4 | Bit 3 - Bit 0 |
nicht benutzt | DD2 ("TEN_DIGIT") | DD1 ("ONE_DIGIT") |
Bit 31 - Bit 8 | Bit 7 - Bit 0 |
nicht benutzt | DIP-Schalter ("DIP_SWITCHES") |
Für das PCI-Target wird ein 3 x 32 Bit = 12 Byte umfassender Adressraum benötigt. Wegen der "Mindestens 4 KByte"-Empfehlung wird ein 4 KByte großer Block im Basisadressregister angefordert.
-------------------------------------------------------------- -- Configure Base Address Registers -------------------------------------------------------------- -- BAR0 : 4 KByte prefetchable memory space cfg_int(0) <= ENABLE ; cfg_int(32 downto 1) <= SIZE4K ; cfg_int(33) <= NOFETCH ; cfg_int(35 downto 34) <= TYPE00 ; cfg_int(36) <= MEMORY ; -- BAR1 : disabled cfg_int(37) <= DISABLE ; cfg_int(69 downto 38) <= SIZE2G ; cfg_int(70) <= NOFETCH ; cfg_int(72 downto 71) <= TYPE00 ; cfg_int(73) <= MEMORY ; -- BAR2 : disabled cfg_int(74) <= DISABLE ; cfg_int(106 downto 75) <= SIZE2G ; cfg_int(107) <= NOFETCH ; cfg_int(109 downto 108) <= TYPE00 ; cfg_int(110) <= MEMORY ;Alle anderen Optionen behalten ihre Voreinstellungen bei bzw. bleiben deaktiviert.
-- Device ID and Vendor ID cfg_int(151 downto 120) <= X"030010ee"; -- Class Code and Revision ID cfg_int(183 downto 152) <= X"0b400000"; -- Subsystem ID and SubVendor ID cfg_int(215 downto 184) <= X"00000000" ; -- External Subsystem ID and Subvendor ID cfg_int(114) <= DISABLE ; -- MAX_LAT and MIN_GNT cfg_int(231 downto 224) <= X"00" ; cfg_int(223 downto 216) <= X"00" ; -- Latency Timer Enable cfg_int(112) <= DISABLE ; -- Interrupt Enable cfg_int(113) <= DISABLE ; -- Capability List Enable cfg_int(116) <= DISABLE ; -- Capability List Pointer cfg_int(239 downto 232) <= X"00" ; -- User Config Space Enable cfg_int(118) <= DISABLE ; -- Interrupt Acknowledge cfg_int(240) <= DISABLE ;
ADDR (31 downto 0) (PCI → Userapp.) |
ADDR enthält die in der Adressphase zu Beginn einer PCI-Transaktion übermittelte Adresse. Die Adresse bleibt für die gesamte Transaktion gültig. In einfachen Designs kann man also direkt mit dem Signal ADDR arbeiten und davon ausgehen, dass es gültig ist, wenn man den Beginn einer Transaktion festgestellt hat. |
BASE_HIT (7 downto 0) (PCI → Userapp.) |
Dieses Signal ist der erste Indikator dafür, dass die aktuelle PCI-Transaktion in einen der Adressräme des Targets zielt. Da das LogiCore PCI Interface nur max. drei Adressräume implementiert, sind auch nur die unteren drei Bits BASE_HIT(2) bis BASE_HIT(0) relevant (One-Hot-Codierung). Gültigkeit: genau ein Takt. |
ADIO (31 downto 0) (PCI ↔ Userapp.) |
Über diesen bidirektionalen Bus werden Daten- und Adressentransfer durchgeführt. |
S_WRDN (PCI → Userapp.) |
S_WRDN gibt die Richtung der aktuellen Target-Transaktion an.
|
S_DATA_VLD (PCI → Userapp.) |
S_DATA_VLD hat zwei Bedeutungen, die von der Richtung des Datentransfers abhängen:
|
S_DATA (PCI → Userapp.) |
S_DATA signalisiert, dass sich der Target-Automat des LogiCore PCI Interfaces im Datentranfer-Zustand befindet: Die Adresse wurde zuvor dekodiert und erfolgreich mit einem der Basisadressregister abgeglichen. Das Target hat die Anfrage akzeptiert und wird nun reagieren. |
S_READY (Userapp. → PCI) |
Mit S_READY wird dem LogiCore PCI Interface die Bereitschaft zum Datentransfer angezeigt. Dieses Signal kann dazu benutzt werden, Wartezyklen in den Transfer einzufügen. |
S_TERM (Userapp. → PCI) |
Der Datentransfer soll beendet werden. |
S_ABORT (Userapp. → PCI) |
S_ABORT dient der Signalisierung schwerer Fehler (Target Abort auf dem PCI-Bus). |
Die Kombination von S_READY und S_TERM realisiert die verschiedenen Möglichkeiten eines Targets, den Transfer "geordnet" zu beenden oder zu steuern. Sie spielen insbesondere bei Burst-Transfers (also nicht in diesem Beispiel) eine Rolle.
Bedingung | S_TERM | S_READY | Erläuterung |
Wait | 0 | 0 | Einfügen von Wartezyklen zu Beginn einer PCI-Transaktion (Verzögerung der ersten Datenphase) |
Normal | 0 | 1 | Die Datenphase(n) werden normal, d.h. ohne zusätzliche Wartezyklen und Transferabbruch durch das Target ausgeführt. |
Disconnect Without Data (Retry) | 1 | 0 | Beendigung der aktuellen PCI-Bus Transaktion ohne einen Datentransfer in der letzten Datenphase. Der Initiator des Transfers ("Master") muß den Datentransfer später wiederholen. |
Disconnect With Data | 1 | 1 | Der Datentransfer in der letzten Datenphase wird zuendegeführt, bevor die Transaktion terminiert. |
S_ABORT wird in diesem Beispiel nicht aktiv verwendet.
library ieee; use ieee.std_logic_1164.all; entity displayrom is port ( ADDR : in std_logic_vector(3 downto 0); DATA : out std_logic_vector(6 downto 0) ); end displayrom; architecture rtl of displayrom is begin with ADDR select -- Segment-Folge: gfedcba DATA <= "0111111" when x"0", "0000110" when x"1", "1011011" when x"2", "1001111" when x"3", "1100110" when x"4", "1101101" when x"5", "1111101" when x"6", "0000111" when x"7", "1111111" when x"8", "1101111" when x"9", "1110111" when x"A", "1111100" when x"B", "1011000" when x"C", "1011110" when x"D", "1111001" when x"E", "1110001" when x"F", "0000001" when others; end rtl;
[...] entity pcim_top is port ( [...] USER_LED : out std_logic; ONE_DIGIT : out std_logic_vector(6 downto 0); TEN_DIGIT : out std_logic_vector(6 downto 0); DIP_SWITCHES : in std_logic_vector(7 downto 0) ); end pcim_top; architecture rtl of pcim_top is [...] component userapp port ( [...] USER_LED : out std_logic; ONE_DIGIT : out std_logic_vector(6 downto 0); TEN_DIGIT : out std_logic_vector(6 downto 0); DIP_SWITCHES : in std_logic_vector(7 downto 0) ); end component; [...] USER_APP : userapp port map ( [...] USER_LED => USER_LED, ONE_DIGIT => ONE_DIGIT, TEN_DIGIT => TEN_DIGIT, DIP_SWITCHES => DIP_SWITCHES ); end rtl;In der Datei userapp.vhd muß die Portbeschreibung entsprechend erweitert werden:
[...] entity userapp is port ( [...] USER_LED : out std_logic; ONE_DIGIT : out std_logic_vector(6 downto 0); TEN_DIGIT : out std_logic_vector(6 downto 0); DIP_SWITCHES : in std_logic_vector(7 downto 0) ); end userapp; [...]Nun fehlen noch die zugehörigen Pin-Vereinbarungen am Ende der User Constraint Datei xc2s200fg456_32_33.ucf:
[...] NET "USER_LED" LOC = "A10"; NET "TEN_DIGIT<0>" LOC = "D6" ; //DISPLAY.2A NET "TEN_DIGIT<1>" LOC = "C5" ; //DISPLAY.2B NET "TEN_DIGIT<2>" LOC = "D5" ; //DISPLAY.2C NET "TEN_DIGIT<3>" LOC = "C7" ; //DISPLAY.2D NET "TEN_DIGIT<4>" LOC = "D7" ; //DISPLAY.2E NET "TEN_DIGIT<5>" LOC = "C6" ; //DISPLAY.2F NET "TEN_DIGIT<6>" LOC = "A8" ; //DISPLAY.2G NET "ONE_DIGIT<0>" LOC = "E10" ; //DISPLAY.1A NET "ONE_DIGIT<1>" LOC = "E9" ; //DISPLAY.1B NET "ONE_DIGIT<2>" LOC = "E8" ; //DISPLAY.1C NET "ONE_DIGIT<3>" LOC = "E6" ; //DISPLAY.1D NET "ONE_DIGIT<4>" LOC = "E7" ; //DISPLAY.1E NET "ONE_DIGIT<5>" LOC = "F11" ; //DISPLAY.1F NET "ONE_DIGIT<6>" LOC = "E11" ; //DISPLAY.1G NET "DIP_SWITCHES<7>" LOC = "D2" ; //DIP7 NET "DIP_SWITCHES<6>" LOC = "C1" ; //DIP6 NET "DIP_SWITCHES<5>" LOC = "F4" ; //DIP5 NET "DIP_SWITCHES<4>" LOC = "G5" ; //DIP4 NET "DIP_SWITCHES<3>" LOC = "F5" ; //DIP3 NET "DIP_SWITCHES<2>" LOC = "E3" ; //DIP2 NET "DIP_SWITCHES<1>" LOC = "F3" ; //DIP1 NET "DIP_SWITCHES<0>" LOC = "E4" ; //DIP0 NET "DIP_SWITCHES<7>" PULLUP; NET "DIP_SWITCHES<6>" PULLUP; NET "DIP_SWITCHES<5>" PULLUP; NET "DIP_SWITCHES<4>" PULLUP; NET "DIP_SWITCHES<3>" PULLUP; NET "DIP_SWITCHES<2>" PULLUP; NET "DIP_SWITCHES<1>" PULLUP; NET "DIP_SWITCHES<0>" PULLUP;
architecture rtl of userapp is [...] component displayrom port ( ADDR : in std_logic_vector(3 downto 0); DATA : out std_logic_vector(6 downto 0) ); end component; -- Steuersignale signal bar_0_rd, bar_0_wr : std_logic; signal led_select, display_select, switches_select : std_logic; signal load_led_reg, load_display_reg : std_logic; signal oe_led_reg, oe_display_reg, oe_switches_reg, oe_dummy : std_logic; -- Register signal led_reg : std_logic; signal display_reg : std_logic_vector(7 downto 0); begin displayrom_inst2 : displayrom port map ( ADDR => display_reg (3 downto 0), DATA => ONE_DIGIT ); displayrom_inst1 : displayrom port map ( ADDR => display_reg (7 downto 4), DATA => TEN_DIGIT ); USER_LED <= led_reg;
Das niederwertigste Bit des BASE_HIT Vektors zeigt an, dass der aktuelle PCI-Transfer in den Adressraum des Basisadressregisters 0 zielt. Der Prozess decode_hit reagiert auf BASE_HIT(0), das nur für einen Takt gesetzt ist, indem er die Signale bar_0_rd bzw. bar_0_wr erzeugt, je nachdem, ob ein Lese- oder ein Schreibtransfer stattfindet. Die Signale bar_0_rd bzw. bar_0_wr behalten ihren Wert für die gesamte Datenphase der Transaktion (S_DATA = '1') bei und werden erst dann wieder zurückgesetzt.
decode_hit : process (CLK, RST) begin if RST = '1' then bar_0_rd <= '0'; bar_0_wr <= '0'; elsif CLK'event and CLK = '1' then if BASE_HIT(0) = '1' then bar_0_rd <= not S_WRDN; bar_0_wr <= S_WRDN; elsif S_DATA = '0' then bar_0_rd <= '0'; bar_0_wr <= '0'; end if; end if; end process;
led_select <= '1' when ADDR(11 downto 2) = x"00" & "00" else '0'; display_select <= '1' when ADDR(11 downto 2) = x"00" & "01" else '0'; switches_select <= '1' when ADDR(11 downto 2) = x"00" & "10" else '0';PCI Speicherzugriffe erfolgen immer 32 Bit ausgerichtet auf voller Busbreite. Bei der Implementierung von I/O-Ports ist es mithilfe der Bits ADDR(1 downto 0) und des hier nicht weiter besprochenen Signals S_CBE(3 downto 0) möglich, auch einzelne Bytes zu adressieren bzw. den Datenbus byteweise zu maskieren.
Das entscheidende kritische Signal für die Datenübernahme in die implementierten Datenregister ist S_DATA_VLD. Ist es gesetzt, sind die Daten auf dem ADIO-Bus gültig.
load_led_reg <= bar_0_wr and S_DATA_VLD and led_select; load_display_reg <= bar_0_wr and S_DATA_VLD and display_select;Der Inhalt des adressierten Datenregisters soll auf den ADIO-Bus ausgegeben werden, wenn sich der Target-Automat des LogiCore PCI Interfaces im Datentransfer-Zustand befindet und ein Lesetransfer stattfindet.
oe_led_reg <= bar_0_rd and S_DATA and led_select; oe_display_reg <= bar_0_rd and S_DATA and display_select; oe_switches_reg <= bar_0_rd and S_DATA and switches_select; oe_dummy <= bar_0_rd and S_DATA and not (led_select or display_select or switches_select);Jedes Datenregister wird durch einen separaten Prozess beschrieben. Ist das entsprechende Datenübernahme-Signal gesetzt, werden die Daten vom ADIO-Bus übernommen. Die DIP-Schalter werden direkt gelesen.
write_led_reg : process (CLK, RST) begin if RST = '1' then led_reg <= '0'; elsif CLK'event and CLK = '1' then if load_led_reg = '1' then led_reg <= ADIO(0); end if; end if; end process; write_display_reg : process (CLK, RST) begin if RST = '1' then display_reg <= x"00"; elsif CLK'event and CLK = '1' then if load_display_reg = '1' then display_reg <= ADIO(7 downto 0); end if; end if; end process;Die Ausgabe auf den ADIO-Bus wird mit kombinatorischer Logik realisiert. Bei jedem Lesezugriff sollen sich definierte Werte auf dem gesamten Bus befinden.
ADIO <= x"0000000" & "000" & led_reg when oe_led_reg = '1' else x"000000" & display_reg when oe_display_reg = '1' else x"000000" & DIP_SWITCHES when oe_switches_reg = '1' else x"00000000" when oe_dummy = '1' else (others => 'Z');
Es ist aus Timing-Gründen (Optimierungsauswirkungen, siehe nächster Abschnitt) nicht erlaubt, die Signale S_READY und S_TERM statisch auf feste Pegel zu legen! Deshalb werden die Signale über D-Flipflops gesetzt, die nicht wegoptimiert werden können.
term_control: process (RST, CLK) begin if RST = '1' then -- hier die invertierten Werte S_READY <= '0'; S_TERM <= '0'; elsif CLK'event and CLK = '1' then -- hier die richtigen Werte S_READY <= '1'; s_TERM <= '1'; end if; end process;
-- Card Bus CIS Pointer Daten / Subsystem ID Daten SUB_DATA <= x"FFFFFFFF" ; -- ADIO-Bus immer mit LogiCore PCI Interface verbunden KEEPOUT <= '0'; -- ADIO-Bus immer enabled -- Steuersignale für Konfigurationstransaktionen C_READY <= '1'; C_TERM <= '1'; -- Initiator Steuersignale REQUEST <= '0'; REQUESTHOLD <= '0'; CFG_SELF <= '0'; static_config: process (CLK, RST) begin if RST = '1' then -- hier die invertierten Default-Werte, Kommentare siehe unten S_ABORT <= '1'; COMPLETE <= '0'; M_WRDN <= '1'; M_READY <= '0'; M_CBE <= "1001"; INTR_N <= '0'; elsif (CLK'event and CLK='1') then -- Signalisierung von schweren Fehlern nicht vorgesehen S_ABORT <= '0'; -- ordnungsgemaesses Abschalten der Initiator-Funktionen COMPLETE <= '1'; M_WRDN <= '0'; M_READY <= '1'; M_CBE <= "0110"; -- keine Interrupt-Funktion INTR_N <= '1'; end if; end process;
Autor: gkemnitz, Letzte Änderung: 14.04.2011 15:09:59