Forum: FPGA, VHDL & Co. Kommunikations FPGA<-PIC: Fehlerhafte Erkennung der Daten


von Full W. (realjey)


Angehängte Dateien:

Lesenswert?

Hallo,

ich bin gerade dabei mich in VHDL einzuarbeiten und möchte eine 
vermutlich "einfache" Aufgabe lösen, komme aber nicht weiter :/.

Ich müchte Daten vom PIC an den FPGA senden. Dafür habe ich den 
PIC18F2450 mit einem Spartan3A-DSP-Board über 11 Leitungen verbunden.

Der FPGA arbeitet mit 100MHz.
Der PIC mit 20MHz.

Dabei sind 8 Leitungen mit Port-d des PIC verbunden (8Bit-Datenbus).

Zusätzlich verwende ich zwei Bits des Port-b (RB0 und RB1) als 
Statusbits welche Art von Daten gesendet werden (Adresse, NWT, HWT). Der 
PIC setzt diese zwei Pins auf die Kombinationen "10"=>Adresse, 
"11"=>NWT, "01"=>HWT.

Der letzte Kanal ist dann RC2 und ist für den "shake" des FPGAs 
vorgesehen.

Ein Blockschaltbild des Prinzips habe ich angehängt.

Nun möchte ich im FPGA den Status der zwei Statusbits korrekt erkennen. 
Ich synchronisiere diese beiden Bits wie von http://www.lothar-miller.de 
beschrieben ein und frage die einsynchronisierten Signale dann per 
IF-Anweisung ab.
Dies scheint soweit auch einwandfrei zu funktionieren. Zur Überprüfung 
setze ich jeweils eine von vier LED's (LED_test1,...) bei korrekter 
Erkennung auf '1'.

Dazu habe ich folgenden vhdl-Code geschrieben (Der Code ist jetzt nur 
für die Übergabe einer Adresse abgebildet (also RB0=1 und RB1=0) und die 
Deklarationen der LED_test's habe ich mal weggelassen, der 
übersichtshalber):
1
Library IEEE;
2
Use IEEE.Std_Logic_1164.All;
3
Use IEEE.Numeric_Std.All;
4
use IEEE.STD_LOGIC_ARITH.ALL;
5
use IEEE.STD_LOGIC_UNSIGNED.ALL;
6
7
entity uCread_topModule is
8
  Port (  clock     : In Std_Logic;   
9
    shake     : out std_logic;   
10
                hand_uC   : in std_logic;
11
    hand2_uC  : in std_logic;
12
    databus_uC: in std_logic_vector(7 downto 0);); 
13
end entity uCread_topModule;
14
15
architecture RTL of uCread_topModule is
16
17
-----------------------------------------------------------------
18
signal sync_hand_uC, sync_hand2_uC: std_logic_vector(1 downto 0);
19
signal LED_test1, LED_test2, LED_test3, LED_test4: std_logic:='0';
20
signal uCData:  std_logic_vector(31 downto 0);
21
-----------------------------------------------------------------
22
23
-----\
24
begin 
25
-----/
26
27
-- Einsynchronisieren
28
process begin
29
   wait until rising_edge(clock);
30
   -- Schieberegister
31
   sync_hand_uC <= sync_hand_uC(0) & hand_uC;
32
   sync_hand2_uC <= sync_hand2_uC(0) & hand2_uC;
33
end process;
34
35
process begin 
36
   wait until rising_edge(clock);
37
  if (sync_hand_uC(1) & sync_hand2_uC(1)= "10") then
38
          shake<='1';
39
    LED_test1<='1';
40
    uCData(31 downto 24)<=databus_uC;
41
    if (uCData(31 downto 24)="00000001") then
42
      LED_test2<='1';
43
    else
44
      LED_test2<='0';
45
    end if;
46
  else
47
    shake<='0';
48
    LED_test1<='0';
49
    LED_test2<='0';
50
    uCData<=(others=>'0');
51
  end if;
52
end process;
53
  
54
end RTL;

Der Ablauf ist also folgender: Haben die einsynchronisierten Bits den 
Status "10", wird LED_test1=grün, der FPGA sendet sein shake-Signal, was 
dem PIC signalisieren soll, das er jetzt Daten auf den Datenbus legen 
darf. Um dann zu testen ob die Daten auch korrekt sind, sende ich zzt. 
vom PIC nur eine definierte Adresse (00000001) und überprüfe im FPGA ob 
er diese auch erkannt hat. Falls ja, schaltet er die zweite LED=grün.

Der PIC-Code dazu ist folgender (Reiner Testcode, die eingebauten 
delay's sind nur dazu da, um die LED's noch in einem Takt blinken zu 
lassen den ich mit blossem Auge auch erkennen kann ;)
1
while(1)
2
{    
3
  portd=0x0;
4
  portb=0x0;
5
  portb=0x01;  //Setze Bits auf Kombination für send:Adresse
6
  delay_ms(500);
7
      
8
  if(portc.2==1) //Wenn shake=1, sende Daten
9
  {
10
    portd=0x01;  //Sende Daten
11
    portb=0x0;  //setze Hand-Signale wieder auf 0
12
  }
13
    delay_ms(500);
14
}

Das Problem was ich jetzt habe ist: Der FPGA erkennt zwar immer korrekt 
die hand-Signale (RB0, RB1-Kombinationen), allerdings nie die auf dem 
Bus anliegenden Daten. Das Problem wird ja höchstwahscheinlich die 5fach 
höhere Taktrate des FPGA's sein. Bis der PIC dazu kommt die Daten auf 
den 8bit-Bus zu legen ist der FPGA schon wieder einige Schritte weiter. 
Ich hoffe mir kann jemand einen Tip/Ansatz zur Problemlösung geben und 
ich habe nicht zuviel rumgetextet. Sollte ich den Thread noch einmal 
überarbeiten weil er zu undurchsichtig ist, bitte einfach bescheid geben 
:)

THX for RE!

von Klaus F. (kfalser)


Lesenswert?

Zuerst die Daten anlegen und dann die Handshake-Signale?

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


Lesenswert?

Jey Soon schrieb:
> Das Problem wird ja höchstwahscheinlich die 5fach
> höhere Taktrate des FPGA's sein.
Eher nicht. Das Problem liegt woanders:
Du mußt zuerst gültige Daten anlegen.
Und dann darfst du dem FPGA signalisieren, dass da gültige Daten da 
sind.


> Bis der PIC dazu kommt die Daten auf
> en 8bit-Bus zu legen ist der FPGA schon wieder einige Schritte weiter.
Wohin soll der dann gegangen sein?
Da einzige, was passiert, ist, dass etliche Taktzyklen lang die falschen 
Daten eingelesen werden...

Ich würde sagen: die LED geht schon an.
Aber nur für ein paar us. Und dann schaltest du sie wieder aus.
Porbier mal das
1
while(1)
2
{    
3
  portd=0x0;
4
  portb=0x0;
5
  portb=0x01;  //Setze Bits auf Kombination für send:Adresse
6
  delay_ms(500);
7
      
8
  if(portc.2==1) //Wenn shake=1, sende Daten
9
  {
10
    portd=0x01;  //Sende Daten
11
    delay_ms(500);
12
    portb=0x0;  //setze Hand-Signale wieder auf 0
13
  }
14
}

EDIT:
Nur, dass du mich nicht falsch verstehst:
das ist nicht die Lösung deines Problems!!!
Es ist nur eine Visualisierung des Problems...

BTW:
Ich hoffe, dir ist bewusst, dass du hier einen Takt Latency hast:
1
    uCData(31 downto 24)<=databus_uC;
2
    if (uCData(31 downto 24)="00000001") then
3
      LED_test2<='1';
4
    else
5
      LED_test2<='0';
6
    end if;
Du könntest das ohne jegliche Funktionsänderung auch so schreiben:
1
    if (uCData(31 downto 24)="00000001") then
2
      LED_test2<='1';
3
    else
4
      LED_test2<='0';
5
    end if;
6
    uCData(31 downto 24)<=databus_uC;

von Full W. (realjey)


Lesenswert?

Erstmal Danke für die Tipps. Ich habe den PIC und den FPGA jetzt so 
umgeschrieben, das zuerst die Daten auf den Bus gelegt werden und dann 
die Handshakes ausgetauscht werden. Das sieht dann folgendermaßen aus:
1
portd=0x0; //Definierter Anfangszustand
2
portb=0x0; //Definierter Anfangszustand
3
delay_us(10);
4
portd=0x01; //Lege Daten auf Datenbus
5
portb|=(1<<0)|(0<<1); //Setze Bits auf Kombination für send:Adresse "01" (Hand-Signal)
6
7
delay_ms(500);
8
9
if(portc.2==1) //Wenn shake=1, setzte Datenbus zurück und lösche die 
10
               //Hand-Signale
11
{
12
   portd=0x0;  
13
   portb&=~((1<<0)|(1<<1));  //setze Hand-Signale wieder auf 0
14
   delay_s(1);
15
}
1
process begin -- Einsynchronisieren
2
   wait until rising_edge(clock);
3
   sync_hand_uC <= sync_hand_uC(0) & hand_uC;
4
   sync_hand2_uC <= sync_hand2_uC(0) & hand2_uC;
5
end process;
6
7
process begin 
8
   wait until rising_edge(clock);
9
  if (sync_hand_uC(1) & sync_hand2_uC(1)= "10") then
10
    LED_test1<='1';
11
    if (uCData(31 downto 24)="00000001") then
12
      LED_test2<='1';
13
    else
14
      LED_test1<='0';
15
    end if;
16
    uCData(31 downto 24)<=databus_uC;
17
    shake<='1';
18
         else
19
                shake<='0';
20
    LED_test1<='0';
21
    LED_test2<='0';
22
    LED_test3<='0';
23
    LED_test4<='0';
24
    uCData<=(others=>'0');
25
end process;

Sowohl in Modelsim als auch auf dem Board scheint es jetzt einwandfrei 
zu funktionieren. Ich habe alle verschiedenen Hand-Signale ["01", "10", 
"11"] mit verschiedensten Daten auf dem Datenbus getestet und die 
Erkennung lief einwandfrei.

Ist dieser Code den generell OK ok muss ich hier befürchten das doch 
auch metastabile Zustände auftauchen können, da ich irgendeine 
Grundlegende Designregel nicht beachtet habe?

@Lothar Noch einmal zu deiner Anmerkung mit der Takt-Latency: Ich habe 
es so verstanden, das die Befehle innerhalb einer IF-Schleife 
sequentiell abgearbeitet werden, in der Geschwindigkeit des 
Systemtaktes.

Das müsste doch bedeuten, das wenn ich schreibe:
1
process begin
2
   wait until rising_edge(clock);
3
   shake<='1';
4
   if(...) then
5
   .
6
   .
7
   end if;
8
end process;

das das shake-statement parallel zur Bedingungs-Abfrage in der 
IF-Schleife ausgeführt wird, richtig?

Das wiederum müsste doch bedeuten das wenn ich schreibe:
1
process begin
2
   wait until rising_edge(clock);
3
   if(...) then
4
   .
5
   .
6
   end if;
7
   shake<='1';
8
end process;

die statements in genau der selben Taktfolge abgearbeitet werden. Wo 
liegt dann der Unterschied?

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


Lesenswert?

Jey Soon schrieb:
> Wo liegt dann der Unterschied?
Es gibt keinen Unterschied in diesen beiden Beispielen.

>> portb|=(1<<0)|(0<<1);
Sieh dir unbedingt nochmal das Kapitel "Bitoperationen in C" an.
Es bringt nichts, eine '0' irgendwohinzischieben, wenn die dann 
anschliessend verodert wird. Ein dort gesetztes Bit wird so nicht 
gelöscht!!!


> Sowohl in Modelsim als auch auf dem Board scheint es jetzt
> einwandfrei zu funktionieren.
Da sind noch einige Lücken im Ablauf...
Z.B. gehen die Daten am PortD weg, bevor die Handshakesignale weggehen.

Das was du willst ist offenbar ganz einfach:
Mit 2 Kommandoleitungen irgendwelche Daten an das FPGA senden.

Das ist eigentlich nichts anderes, als z.B. ein asynchrones RAM zu 
beschreiben. Nur sieht dort die Lösung anders aus: erst werden die 
Adresse (=dein Kommando) und die Daten an das RAM angelegt, dann wird 
mit einer Steuerleitung die entsprechende Aktion ausgelöst.
Da gibt es keinen "Rückkanal" vom RAM...

Und so ähnlich kannst du das doch auch machen:
1. Leg dein Kommando an.
2. Leg die Daten an.
3. Validiere die angelegten Signale.

Auf der uC-Seite also ganz ohne Handshake:
1
portd=0x01;   // Lege Daten auf Datenbus
2
portb=0x01;   // Setze Bits auf Kombination für send:Adresse "01" (Hand-Signal)
3
4
portc.2 = 1;  // Datenübergabe
5
delay_us(10);
6
portc.2 = 1;

Und auf der FPGA-Seite:
1
Use IEEE.Std_Logic_1164.All;
2
Use IEEE.Numeric_Std.All;
3
4
entity uCread_topModule is
5
  Port (  clock     : in Std_Logic;   
6
          shake     : in std_logic;   
7
          addr_uC   : in std_logic_vector(1 downto 0);  -- Kommando
8
          databus_uC   : in std_logic_vector(7 downto 0)); 
9
end entity uCread_topModule;
10
:
11
:
12
process begin -- Einsynchronisieren
13
   wait until rising_edge(clock);
14
   sync_shake <= sync_shake (0) & shake;
15
end process;
16
17
process begin 
18
 wait until rising_edge(clock);
19
 if (sync_shake = "01") then  -- steigende Flanke --> Daten sind stabil
20
   if (addr_uC="10") then     -- kein sync nötig, weil hier garantiert stabil
21
     LED_test2<='0';
22
     LED_test1<='0';
23
     if (uCData(31 downto 24)="00000001") then
24
       LED_test2<='1';
25
     else
26
       LED_test1<='1';
27
     end if;
28
     uCData(31 downto 24)<=databus_uC;
29
   end if;
30
 end if;
31
end process;
1
Use IEEE.Std_Logic_1164.All;
2
Use IEEE.Numeric_Std.All;
3
use IEEE.STD_LOGIC_ARITH.ALL;
4
use IEEE.STD_LOGIC_UNSIGNED.ALL;
Niemals die numeric_std zusammen mit den synopsis-Libs verwenden!!
Da gibt es sonst einige Definitionen doppelt... :-o

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.