Forum: FPGA, VHDL & Co. Erzeugung von Tri - State Signalen?


von Stephan M. (stmiko)


Angehängte Dateien:

Lesenswert?

Hallo,

gemäß der im Anhang dargestellten Abbildung möchte ich gern den CPLD an 
den Bus ankoppeln. Wie ersichtlich ist, existiert ein NE1 
(Enable-Signal,low aktiv). Mittels diesen Signals möchte ich erst die 
Abarbeitung innerhalb des CPLD beginnen. Um Signalkollisionen zu 
vermeiden, möchte ich den Adress-, Daten- und Steuerbus hochohmig 
schalten.
Da ich erst begonnen habe mich mit VHDL zu beschäftigen, würde ich euch 
bitten über den Code zu schauen und mir zu sagen, ob das die richtige 
herangehensweise ist.
1
library ieee;
2
use ieee.std_logic_1164.ALL;
3
4
entity IO_EX is
5
        port (  NE1: in bit;  --Enable (low active)
6
                NINT3: out bit; --Interrupt (low active)
7
                
8
                STE:   in std_logic_vector(1 downto 0);    --Steuerbus 0-NWE 1-NOE
9
                ADDR:  inout std_logic_vector(5 downto 0); --Adresse Parallelbus
10
                DATA:  inout std_logic_vector(7 downto 0); --Datenbus - bidirektional
11
                
12
                PA: inout std_logic_vector(7 downto 0); --Port A
13
                PB: inout std_logic_vector(7 downto 0); --Port B
14
                PC: inout std_logic_vector(7 downto 0); --Port C
15
                PD: inout std_logic_vector(7 downto 0); --Port D
16
                
17
                ADDR_MUX: out std_logic_vector(2 downto 0)); --Adresse des Multiplexkanals
18
end IO_EX; 
19
         
20
21
architecture VERHALTEN of IO_EX is
22
23
signal DATA_BUF: std_logic_vector(7 downto 0);
24
  
25
begin
26
27
-- Hochohmiges Umschalten der am Bus angeschlossenen Pins    
28
TR: process
29
      begin
30
              --wait until falling_edge(NE1);
31
              if NE1 = '1' then
32
                --STE  <= (others => 'Z'); --Steuerleitungen hochohmig schalten
33
                ADDR <= (others => 'Z'); --Adressbus hochohmig schalten
34
                DATA <= (others => 'Z'); --Datenbus hochohmig schalten
35
              end if;
36
              
37
            --  if STE(1) = '0' and STE(0) = '1' then
38
            --  DATA <= DATA_BUF;  
39
            --ADDR <= (others => 'Z'); --Adressbus hochohmig schalten
40
            --DATA <= (others => 'Z'); --Datenbus hochohmig schalten
41
            --else if falling_edge(NE1) then
42
                                
43
      end process TR;
44
45
--weitere Logik...
46
47
  
48
  DATA <= DATA_BUF when STE(1) = '0' and STE(0) = '1'
Ist es überhaupt nötig alle Bussignale hochohmig zu schalten? Wie kann 
man die umgeschalteten Kanäle( 'Z') wieder als Eingang (STE und ADDR) 
bzw. als Ein- oder Ausgang (DATA) umschalten? Wäre eine Flankenabfrage 
mittels falling_edge() die bessere Variante?

Danke

Stephan

von Klaus F. (kfalser)


Lesenswert?

Üblicherweise werden die Adresssen und Steuerleitungen am Bus von einem 
Master getrieben, und diese werden nie hochohmig geschalten.
In deinem Fall nehme ich einmal an, dass nicht das CPLD der Master ist, 
sondern irgend ein Prozessor außen.
In diesem Fall sind die Adress- und Steuersignale am CPLD keine inouts, 
sondern reine Eingänge.
Bei den Daten ist es etwas anderes.
Diese werden wechselseitig jeweils vom Master oder von den einzelnen 
Slaves getrieben, wobei immer nur einer die Daten treibt, alle anderen 
sind hochohmig.

Die VHDL Beschreibung für das hochohmig-schalten ist ganz einfach:
1
DATA <= DATA_BUF when NE1 = '0' else (others => 'Z';

DATA_BUF sind die errechneten Daten, die an den Bus gehen.

von Stephan M. (stmiko)


Lesenswert?

Ja,
ein uC bildet den Master am Bus. Somit stelle ich die Adress- und 
Steuerleitungen auf reine Eingänge um.

Mit der von Ihnen beschrieben Befehlszeile findet also eine 
Signalumschaltung statt.


Danke

von Klaus F. (kfalser)


Lesenswert?

Stephan M. schrieb:
> Mit der von Ihnen beschrieben Befehlszeile findet also eine
> Signalumschaltung statt.

Nein, als Umschaltung kann man es nicht bezeichnen, da hochohmig kein 
Signalwert ist.
Es ist einfach nur die VHDL Beschreibung, mit der man erreicht, dass das 
inout-Port DATA entweder hochohmig ist (NE = 1) oder das Signal DATA_BUF 
ausgegeben wird.

Du kannst DATA übrigends jederzeit lesend verwenden, dabei liest du die 
Daten, die gerade außen am Bus liegen.

von Stephan M. (stmiko)


Lesenswert?

Danke! Jetzt kann ich das nachvollziehen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Zum Hintergrund:
Wir kommen vom Beitrag "CPLD - I/O Erweiterung"

> STE:   in  std_logic_vector(1 downto 0);    --Steuerbus 0-NWE 1-NOE
Teil das besser in 2 Einzelsignale auf, dann liest es sich leichter...

Hier mal mein Ansatz:
(Das Tristate-Zeugs kommt bei den Ports natürlich auch zum Zuge)
1
library ieee;
2
use ieee.std_logic_1164.ALL;
3
4
entity IO_EX is
5
        port (  NE1:   in  std_logic; -- ! nimm nur STD_LOGIC !  --Enable (low active)    NINT3: out std_logic; --Interrupt (low active)
6
                NWE:   in  std_logic;   
7
                NOE:   in  std_logic;   
8
9
                ADDR:  in std_logic_vector(5 downto 0); --Adresse Parallelbus
10
                DATA:  inout std_logic_vector(7 downto 0); --Datenbus - bidirektional
11
                
12
                PA: inout std_logic_vector(7 downto 0); --Port A
13
                PB: inout std_logic_vector(7 downto 0); --Port B
14
                PC: inout std_logic_vector(7 downto 0); --Port C
15
                PD: inout std_logic_vector(7 downto 0); --Port D
16
                
17
                ADDR_MUX: out std_logic_vector(2 downto 0)); --Adresse des Multiplexkanals
18
end IO_EX; 
19
         
20
21
architecture VERHALTEN of IO_EX is
22
23
signal DATA_BUF: std_logic_vector(7 downto 0);
24
signal PortA: std_logic_vector(7 downto 0);
25
signal PortB: std_logic_vector(7 downto 0);
26
signal DirA:  std_logic_vector(7 downto 0); -- '1' = Ausgang, '0' = Eingang
27
signal DirB:  std_logic_vector(7 downto 0);
28
  
29
begin
30
31
-- Richtungsumschaltung Port A
32
  process (PortA, DirA) begin
33
     for i in 0 to 7 loop
34
        PA(i) <= PortA(i) when DirA(i)='1' else 'Z';
35
     end loop;
36
  end process;
37
38
-- Richtungsumschaltung Port B
39
  process (PortB, DirB) begin
40
     for i in 0 to 7 loop
41
        PB(i) <= PortB(i) when DirB(i)='1' else 'Z';
42
     end loop;
43
  end process;
44
:
45
:
46
47
-- Schreiben
48
  process begin
49
     wait until rising_edge(NWE); -- steigende Flanke an WR --> Daten und Adressen sind stabil
50
     if    (NE1='1') then NULL; -- nichts tun, wenn disabled
51
     if    (A="000000") then PortA <= DATA;
52
     elsif (A="000001") then PortB <= DATA;
53
     :
54
     elsif (A="000100") then DirA <= DATA;
55
     elsif (A="000101") then DirB <= DATA;
56
     :
57
     elsif (A="001000") then ADDR_MUX <= D(2 downto 0);
58
  end process;
59
60
-- Lesen
61
  DATA_BUF <= PA when (ADDR="000000") else
62
              PB when (ADDR="000001") else
63
              PC when (ADDR="000010") else
64
              PD when (ADDR="000011") else
65
              (others => '0');
66
  
67
   DATA <= DATA_BUF when NE1='0' and NOE='0' else (others => 'Z');
68
69
end VERHALTEN;

von Stephan M. (stmiko)


Lesenswert?

Danke Lothar,

ich möchte eine "Don´t care" Auswertung mit einbringen.
Ich habe mir das in folgender Form vorgestellt:
1
process begin
2
     wait until rising_edge(NWE); -- steigende Flanke an WR --> Daten und Adressen sind stabil
3
     if    (NE1='1') then NULL; -- nichts tun, wenn disabled
4
5
     if    (ADDR(2 downto 0)="000") then PortA <= DATA;
6
     --bei den anderen Abfragen wäre das ja gleich...
7
8
9
     if    (A="000000") then PortA <= DATA;
10
     elsif (A="000001") then PortB <= DATA;
11
     :
12
     elsif (A="000100") then DirA <= DATA;
13
     elsif (A="000101") then DirB <= DATA;
14
     :
15
     elsif (A="001000") then ADDR_MUX <= D(2 downto 0);
16
  end process;

Da alle Signale low - active sind, muss man dann auf eine fallende 
Flanke triggern.

Stephan

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Stephan M. schrieb:
> Da alle Signale low - active sind, muss man dann auf eine fallende
> Flanke triggern.
Sieh dir an, wo bei deinem WE-Signal die Daten und Adressen stabil sind.

Ich habe da einen kleinen Bock geschossen...
Das sollte besser heißen:
1
     :
2
     if    (NE1='1')    then NULL; -- nichts tun, wenn disabled
3
     elsif (A="000000") then PortA <= DATA;
4
     elsif (A="000001") then PortB <= DATA;
5
     :
6
     elsif (A="000100") then DirA <= DATA;
7
     elsif (A="000101") then DirB <= DATA;
8
     :
9
     elsif (A="001000") then ADDR_MUX <= D(2 downto 0);
10
     end if;
11
     :


Stephan M. schrieb:
> ich möchte eine "Don´t care" Auswertung mit einbringen.
Also sowas:
1
     :
2
     if    (NE1='1') then NULL; -- nichts tun, wenn disabled
3
     elsif (ADDR(5 downto 3)="000") then
4
        if    (ADDR(2 downto 0)="000") then PortA <= DATA;
5
        elsif (ADDR(2 downto 0)="001") then PortB <= DATA;
6
        :
7
        elsif (ADDR(2 downto 0)="101") then DirA <= DATA;
8
        elsif (ADDR(2 downto 0)="101") then DirB <= DATA;
9
        end if;
10
     elsif (A="001000") then ADDR_MUX <= D(2 downto 0);
11
     end if;
12
     :
Das ist unnötig, der Synthesizer macht das schon richtig, und ausserdem 
ist das miserabel lesbar...

von Stephan M. (stmiko)


Lesenswert?

Ja, Danke für den Hinweis!

Bei dem Prozess muss es doch heißen?:
1
-- Richtungsumschaltung Port A
2
  process (PortA, DirA) begin
3
     for i in 0 to 7 loop
4
        if DirA(i) <= '1' then
5
        PA(i) <= PortA(i);
6
        else
7
        PA(i) <= 'Z';
8
        end if;
9
     end loop;
10
  end process;

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Stephan M. schrieb:
> Bei dem Prozess muss es doch heißen?:
Ja, das mit dem when geht da wieder mal nicht ;-)

Und das noch ausbessern, dann is jut:
1
 if DirA(i)='1' then

Und dann kannst du noch so schreiben:
1
     for i in PA'range loop
Dann sieht das ein wenig generischer aus... ;-)

von Stephan M. (stmiko)


Lesenswert?

Oh, stimmt mit dem Vergleich!

Bis jetzt schreibe und simuliere ich den Prozess in ModelSim.
Bei der Umsetzung auf einen CPLD von z.B Xlinx und deren 
Entwicklungsumgebung, besteht da in der Software die Möglich den 
definierten Ports z.B. PA, die Pins zuzuordnen?
Konnte die WebPack noch nicht runterladen...

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Stephan M. schrieb:
> besteht da in der Software die Möglich den
> definierten Ports z.B. PA, die Pins zuzuordnen?
Ja. Diese Einschränkungen nennen sich Constraints (Einschränkungen 
deshalb, weil die Toolchain nicht x-beliebig die Pins zuordnen darf) und 
werden in einer UCF-Datei (user constraints file) vorgenommen.

von Stephan M. (stmiko)


Angehängte Dateien:

Lesenswert?

Hallo,

nun habe ich es erreichen können, dass die Ports A bis D sowie die 
Multiplexer 1 und 2 die über den Datenbus gesendeten Daten korrekt 
ausgeben, aber sobald ich denn "letzen" Prozess in den Code einbinde, 
kommt in der Simulation, dass die Signale undefiniert sind.
Dieser Prozess soll dazu dienen, die anliegenden Level an den Ports über 
die Datenleitung zu senden...
Kann mir vielleicht jemand verraten, was ich übersehen habe?
Die Ports bleiben dann hochohmig, also 'Z' laut Simulator.



Danke Stephan

von Klaus F. (kfalser)


Lesenswert?

Du denkst immer noch zu kompliziert und Dein Design funktioniert sicher 
auch nicht korrekt so wie Du glaubst.

Vielleicht beschreibst Du noch einmal, was Du erreichen willst:
Sind PA usw. Ports, die der Microprozessor als Eingang oder Ausgang 
konfigurieren soll?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Erstmal vorab:
Das Lesen bei dir ist ein rein kombinatorischer Vorgang, bei dem nichts 
irgendwo gespeichert werden muß. Es ist also unnötig, da irgendwas mit 
einer Flanke abzufragen...

Stephan M. schrieb:
> dass die Signale undefiniert sind.
Welche Signale?

Wie auch immmer: dein Problem liegt darin, dass zwei Prozesse die 
Richtungssignel treiben: der Lese- (mit '0') und der Schreibprozess (mit 
'1'). Das gibt im DIRA..D ein 'X' und damit kommt beim Porttreiber etwas 
/='1' und folglich ein 'Z' heraus.
So geht das nicht, du kannst nicht einfach zum Lesen die Richtung eines 
Ports umschalten wollen...
Hast du mein Beispiel nachvollzogen? Hast du mal gegengeprüft, wie uCs 
das machen mit der Richtungsumschaltung?

von Stephan M. (stmiko)


Lesenswert?

Ich habe mir eine Adresstabelle erstellt, wo das oberste Bit ADDR3 
entweder eine 0 für CPLD Port einlesen oder eine 1 für CPLD Port 
schreiben beinhaltet. Abhängig von diesem Bit, soll der jeweilige Port 
des CPLD die Daten vom Datenbus ausgeben bzw. auf den Datenbus 
"schreiben".
Bei einer fallenden Flanke an NE1 und NWE sendet der uC Daten an den 
CPLD und bei fallender Flanke an NE1 und NOE soll der uC die Daten des 
Ports abrufen.
Die Simulation des Schreibens mit den Flankenwechseln funktioniert auch 
soweit.
Die Umschaltung der I/Os innerhalb eines uC habe ich immer über die 
zugehörigen Librarys bzw. Register gemacht.

von Klaus F. (kfalser)


Lesenswert?

Stephan M. schrieb:
> Abhängig von diesem Bit, soll der jeweilige Port
> des CPLD die Daten vom Datenbus ausgeben bzw. auf den Datenbus
> "schreiben".

Das kann aber so nicht funktionieren.
Angenommen, du schreibst zuerst auf PA, dann wird das Port auf Ausgang 
geschalten, die Daten liegen danach aber weiterhin an.
Wenn Du jetzt etwas einlesen willst und Du dort von außen ein Signal 
anlegt, dann gibt es eine Kollision, weil die Pins schon einen Wert 
treiben.
Du musst also eine Methode finden, die Pins von PA zuerst auf Hochohmig 
zurückzuschalten.
Da waren deine vorherigen Lösungen sinnvoller.

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
Noch kein Account? Hier anmelden.