Forum: FPGA, VHDL & Co. VHDL-SPI implementierung


von VHDL-Frage (Gast)


Lesenswert?

Guten Abend,
ich habe eine Frage bezüglich diesem SPI-Master

http://www.lothar-miller.de/s9y/categories/45-SPI-Master
von Lothar Miller



1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
6
entity SPI_Master is
7
    Generic ( Quarz_Taktfrequenz : integer   := 50000000;  -- Hertz 
8
              SPI_Taktfrequenz   : integer   :=  1000000;  -- Hertz / zur Berechnung des Reload-Werts für Taktteiler
9
              Pre_Delay          : integer   :=   50;      -- us / Zeit nach Aktivieren von CS bis Beginn der Übertragung
10
              Post_Delay         : integer   :=   10;      -- us / Zeit nach Beenden der Übertragung bis Deaktivieren CS 
11
              Laenge             : integer   :=   32       -- Anzahl der zu übertragenden Bits
12
             ); 
13
    Port ( TX_Data  : in  STD_LOGIC_VECTOR (Laenge-1 downto 0); -- Sendedaten
14
           RX_Data  : out STD_LOGIC_VECTOR (Laenge-1 downto 0); -- Empfangsdaten
15
           CPHA     : STD_LOGIC;                                -- Clock Phase
16
           CPOL     : STD_LOGIC;                                -- Clock Polarity
17
           MOSI     : out STD_LOGIC;
18
           MISO     : in  STD_LOGIC;
19
           SCLK     : out STD_LOGIC;
20
           SS       : out STD_LOGIC;
21
           Start_TX : in  STD_LOGIC;
22
           TX_Done  : out STD_LOGIC;
23
           clk      : in  STD_LOGIC
24
         );
25
end SPI_Master;
26
27
architecture Behavioral of SPI_Master is
28
  signal   delay       : integer range 0 to (Quarz_Taktfrequenz/(2*SPI_Taktfrequenz));
29
  signal   delay_pre   : integer range 0 to (Pre_Delay*(Quarz_Taktfrequenz/1000))/1000;
30
  signal   delay_post  : integer range 0 to (Post_Delay*(Quarz_Taktfrequenz/1000))/1000;
31
  constant clock_delay : integer := (Quarz_Taktfrequenz/(2*SPI_Taktfrequenz))-1;
32
  
33
  type   spitx_states is (spi_stx,del_pre,spi_txactive,del_post,spi_etx);
34
  signal spitxstate    : spitx_states := spi_stx;
35
36
  type   spi_clkstates is (shift,sample);
37
  signal spiclkstate   : spi_clkstates;
38
  
39
  signal bitcounter    : integer range 0 to Laenge; -- wenn bitcounter = Laenge --> alle Bits uebertragen
40
  signal tx_reg        : std_logic_vector(Laenge-1 downto 0) := (others=>'0');
41
  signal rx_reg        : std_logic_vector(Laenge-1 downto 0) := (others=>'0');
42
43
begin
44
  ------ Verwaltung --------
45
  process begin 
46
     wait until rising_edge(CLK);
47
     MOSI <= tx_reg(tx_reg'left);
48
     delay_post  <= (Post_Delay*(Quarz_Taktfrequenz/1000))/1000; -- POST-Delay wg OPTO-Koppler 
49
     delay_pre   <= (Pre_Delay *(Quarz_Taktfrequenz/1000))/1000; -- Initial-Delay wg OPTO-Koppler 
50
     if(delay>0) then delay <= delay-1;
51
     else             delay <= clock_delay;  
52
     end if;
53
     case spitxstate is
54
       when spi_stx =>
55
             SS          <= '1'; -- slave select disabled
56
             TX_Done     <= '0';
57
             bitcounter  <= Laenge;
58
             SCLK        <= CPOL;
59
             if(Start_TX = '1') then spitxstate <= del_pre; end if;
60
61
       when del_pre =>                       -- SS aktivieren und Zeit fuer Optokoppler abwarten 
62
             SS          <= '0';
63
             SCLK        <= CPOL;
64
             if (CPHA='0') then spiclkstate <= sample;  -- sample at odd SCLK-edge  (1st, 3rd, 5th...)
65
             else               spiclkstate <= shift; -- sample at even SCLK-edge (2nd, 4th, 6th...)
66
             end if;
67
             delay       <= 0;
68
             if (delay_pre>0) then 
69
                delay_pre  <= delay_pre-1;
70
             else
71
                spitxstate <= spi_txactive;
72
             end if;
73
74
       when spi_txactive =>  -- Daten aus tx_reg uebertragen
75
---------------------------------------- SPI-Takt generieren -----------------------------
76
             case spiclkstate is
77
               when sample =>
78
                       SCLK <= (CPOL xor CPHA); 
79
                       if (delay=0) then -- sample                     
80
                         spiclkstate <= shift;
81
                         if(CPHA='1') then bitcounter <= bitcounter-1;  end if;
82
                       end if;
83
                       
84
               when shift =>
85
                       SCLK <= not (CPOL xor CPHA); 
86
                       if (delay=0) then -- shift
87
                         spiclkstate <= sample; 
88
                         if(CPHA='0') then  bitcounter <= bitcounter-1;  end if;
89
                       end if;         
90
               end case;
91
               if (delay=0 and bitcounter=0) then -- alle Bits uebertragen -> deselektieren
92
                 SCLK       <= CPOL;
93
                 spitxstate <= del_post;
94
               end if;
95
---------------------------------------- SPI-Takt fertig  -----------------------------
96
       when del_post =>
97
             SS <= '1'; -- disable Slave Select 
98
             if (delay_post>0) then
99
               delay_post <= delay_post-1;
100
             else 
101
               spitxstate <= spi_etx;
102
             end if;
103
104
       when spi_etx =>
105
             TX_Done <= '1';
106
             if(Start_TX = '0') then -- Handshake: warten, bis Start-Flag geloescht
107
               spitxstate <= spi_stx;
108
             end if;
109
     end case;
110
  end process;   
111
  
112
  ---- Schieberegister in eigenem Prozess ist ressourcensparend -------
113
  process begin
114
     wait until rising_edge(clk);
115
--     if (spitxstate=del_pre) then  -- Initialisierung weglassen spart Ressourcen: 1 Mux = 10 Slices
116
--        rx_reg <= (others=>'0');
117
--     end if;
118
     if(spitxstate=spi_txactive and spiclkstate=sample and delay=0 and bitcounter/=0) then
119
        rx_reg <= rx_reg(rx_reg'left-1 downto 0) & MISO;
120
     end if;
121
     
122
     if (spitxstate=spi_stx) then
123
        tx_reg <= TX_Data;
124
     end if;
125
     if(spitxstate=spi_txactive and spiclkstate=shift and delay=0 and (cpha='0' or bitcounter/=Laenge)) then
126
        tx_reg <= tx_reg(tx_reg'left-1 downto 0) & tx_reg(0);
127
     end if;
128
  end process;   
129
  
130
  RX_Data    <= rx_reg;
131
  
132
end Behavioral;


Ich würde gerne diesen Drucksensor(ein BMP280)

https://www.amazon.de/Precision-Ultra-low-Leistungsaufnahme-barometrische-Drucksensor-Brett-Modul/dp/B07KXMTYNY/ref=sr_1_2?ie=UTF8&qid=1549722967&sr=8-2&keywords=drucksensor+spi+3%2C3v

via SPI auslesen. Ich nutze ein DE0-Nano und VHDL. Ich bin mir aber 
nicht ganz im klaren, was für Veränderungen ich an dem Code ändern muss. 
Könnte mir vielleicht jemand weiterhelfen?

Vielen Dank und ein schönes Wochenende!

von Gustl B. (-gb-)


Lesenswert?

Nichts. Das ist doch das Schöne wenn eine Komponente mit Generics 
geschrieben wurde. Dieses Stück Code ist so zu verstehen wie ein 
Elektronikbaustein, ein IC, der ist fertig und muss jetzt nurnoch 
angeschlossen werden. Die zusätzlichen "Anschlüsse" der Generics können 
diesen Baustein parametrisieren.

von VHDL-Frage (Gast)


Lesenswert?

Okay, das ist ja gut. Wenn ich den Code jetzt als Untermodul 
implementiere, muss ich dann alle Signale aus Generic und Port in der 
component meiner toplevel definieren?

von VHDL-Frage (Gast)


Lesenswert?

Ich bin der Meinung, dass nur die port-signale in die component müssen. 
Ist das richtig?

von Gustl B. (-gb-)


Lesenswert?

Also verbinde immer Port und Generic. Aber vielleicht kann man 
tatsächlich Generic weglassen, dann ist aber der hier im Code 
zugewiesene Wert gültig. Ich würde den Generic verbinden weil du das 
dann schön anpassen kannst.

von Achim S. (Gast)


Lesenswert?

Gustl B. schrieb:
> Aber vielleicht kann man
> tatsächlich Generic weglassen, dann ist aber der hier im Code
> zugewiesene Wert gültig.

Im Generic Map kann man diejenigen Werte weglassen, bei denen der 
Default-Wert des Generics passt. (In diesem Code ist ja für jedes 
Generic ein Default-Wert vorgesehen.)

Beim Port Map müssen alle Eingänge verbunden werden. (Auch für die 
Inputs im Port könnten im Prinzip Default-Werte definiert werden; in 
diesem Code ist das allerdings nicht geschehen, und das wird imho auch 
eher selten genutzt.) Ausgänge kann man bei der Instanziierung auch 
offen lassen (wenn man den entsprechenden Ausgang im Design nicht 
benötigt).

VHDL-Frage schrieb im Beitrag #5737698:
> Ich bin mir aber
> nicht ganz im klaren, was für Veränderungen ich an dem Code ändern muss.

Du musst wohl bei den Signalen CPHA und CPOL noch ein "in" ergänzen, das 
sie als Eingänge festlegt. Das wurde anscheinend vergessen.

Ansonsten gilt wie von Gustl schon gesagt: ändern musst du nichts, nur 
passend parametrisieren.

Und dann natürlich noch eine State Machine drumrumbauen, die die 
diversen Konfigurationsregister deines Sensors mit den gewünschten 
Inhalten beschreibt (außer der Reset-State der Register entspricht 
deinen Wünschen) und dann die Status- und Ergebnisregister ausliest.

von VHDL-Frage (Gast)


Lesenswert?

Erst einmal danke für die Antworten.

Achim S. schrieb:
> Und dann natürlich noch eine State Machine drumrumbauen, die die
> diversen Konfigurationsregister deines Sensors mit den gewünschten
> Inhalten beschreibt (außer der Reset-State der Register entspricht
> deinen Wünschen) und dann die Status- und Ergebnisregister ausliest.

Woher kann ich die Information  nehmen, welchen Inhalt der Sensor 
benötigt bzw. wie viele Konfigurationsregister ich brauche?

von VHDL-Frage (Gast)


Lesenswert?

Achim S. schrieb:
> Beim Port Map müssen alle Eingänge verbunden werden. (Auch für die
> Inputs im Port könnten im Prinzip Default-Werte definiert werden; in
> diesem Code ist das allerdings nicht geschehen, und das wird imho auch
> eher selten genutzt.) Ausgänge kann man bei der Instanziierung auch
> offen lassen (wenn man den entsprechenden Ausgang im Design nicht
> benötigt).

Also müssten in diesem Fall nur die Port-Signale bzw. die 
Eingangssignale von Port in die component, richtig?

von Achim S. (Gast)


Lesenswert?

VHDL-Frage schrieb im Beitrag #5738107:
> Woher kann ich die Information  nehmen, welchen Inhalt der Sensor
> benötigt bzw. wie viele Konfigurationsregister ich brauche?

Aus dem Datenblatt des Sensors. Und aus deinen eigenen Anforderungen an 
die Messung.


VHDL-Frage schrieb im Beitrag #5738111:
> Also müssten in diesem Fall nur die Port-Signale bzw. die
> Eingangssignale von Port in die component, richtig?

Dem Synthesetool würde das ausreichen.

Aber wenn du tatsächlich den Sensor ans FPGA anschließen willst, dann 
musst du die entsprechenden Ausgänge der Komponente an die Ausgänge des 
FPGAs durchreichen (also SCK und SS).

Und wenn du nicht nur den Sensor ansteuern willst sondern auch am 
Ergebnis des Sensors interessiert bist, dann musst du auch diese 
Ausgänge sinnvoll anschließen (als RX_Data).

Den Ausgang TX_Done könntest du ggf. tatsächlich offen lassen. 
Wahrscheinlich brauchst du ihn nicht unbedingt, aber er erleichtert dir 
die Gestaltung der Statmachine, die den SPI-Core benutzt.

von Achim S. (Gast)


Lesenswert?

Achim S. schrieb:
> Dem Synthesetool würde das ausreichen.

Das war vielleicht etwas knapper beschrieben als es sein sollte. Deshalb 
diesen Satz noch etwas ausführlicher: dem Synthesetool würde es 
tatsächlich reichen, wenn du nur die Eingänge der Komponente bedienst. 
Aber wenn du alle Ausgänge ignorierst erkennt das Synthesetool, dass die 
Komponente überhaupt nicht benutzt wird und optimiert sie einfach weg.

von VHDL-Frage (Gast)


Lesenswert?

Achim S. schrieb:
> Das war vielleicht etwas knapper beschrieben als es sein sollte. Deshalb
> diesen Satz noch etwas ausführlicher: dem Synthesetool würde es
> tatsächlich reichen, wenn du nur die Eingänge der Komponente bedienst.
> Aber wenn du alle Ausgänge ignorierst erkennt das Synthesetool, dass die
> Komponente überhaupt nicht benutzt wird und optimiert sie einfach weg.

Vielen Dank für deine Erklärung. Ich denke ich habe es jetzt verstanden. 
Doch eine Frage brennt mir noch auf der Seele:
Ich würde gerne etwas mit den ausgelesenen Daten des Sensors machen. 
Sobald der Sensor meldet, dass die Höhe niedriger wird soll ein gpio(1) 
<= '1'; angesteuert werden, um eine LED einzuschalten. Muss ich die von 
MISO empfangene Daten noch in Dezimalsystem umwandeln, oder gehe ich da 
anders ran?

von Achim S. (Gast)


Lesenswert?

VHDL-Frage schrieb im Beitrag #5738415:
> Muss ich die von
> MISO empfangene Daten noch in Dezimalsystem umwandeln, oder gehe ich da
> anders ran?

Du musst wissen, ob die Daten vorzeichenbehaftet sind oder immer positiv 
(ob also der std_logic_vector als signed oder unsigned zu behandeln 
ist). Diese Typanpassung musst du durchführen. Ansonsten musst du dich 
nicht um die Zahlendarstellung kümmern.

Auch wenn du den Vergleichswert im VHDL-Code im Dezimalsystem eingibst 
wird er in der generierten Logik binär dargestellt.

von Markus F. (mfro)


Lesenswert?

Bei diesem Sensor sollte man den gesamten Registersatz (mindestens aber 
6 Bytes für Druck und Temperatur) "auf einen Rutsch" (Burst Read) 
auslesen und nicht Byte für Byte.

Sonst kommt es zu Fehlern, wenn der Sensor während des Messens 
"erwischt" wird.

von VHDL-Frage (Gast)


Lesenswert?

Markus F. schrieb:
> Bei diesem Sensor sollte man den gesamten Registersatz (mindestens aber
> 6 Bytes für Druck und Temperatur) "auf einen Rutsch" (Burst Read)
> auslesen und nicht Byte für Byte.
>
> Sonst kommt es zu Fehlern, wenn der Sensor während des Messens
> "erwischt" wird.

Okay. Weißt du wie es bei diesem Sensor aussieht?

https://www.bluedot.space/sensor-boards/bmp388/

Ist ein BMP388 von BlueDot.

von Weltbester FPGA-Pongo (Gast)


Lesenswert?

Markus F. schrieb:
> Bei diesem Sensor sollte man den gesamten Registersatz (mindestens aber
> 6 Bytes für Druck und Temperatur) "auf einen Rutsch" (Burst Read)
> auslesen und nicht Byte für Byte.

Ist das eine Annahme von Dir, oder eine Empfehlung oder kommt das von 
diesem Sensor?

Ich habe die Erfahrung gemacht, dass es zweckmässig ist, die Störungen 
rauszurechnen oder den digitalen Teil gänzlich still zu legen

von Markus F. (mfro)


Lesenswert?

Weltbester FPGA-Pongo schrieb im Beitrag #5739021:
> Ist das eine Annahme von Dir, oder eine Empfehlung oder kommt das von
> diesem Sensor?

Nö. Das steht so im Datenblatt.

von VHDL-Frage (Gast)


Lesenswert?

VHDL-Frage schrieb im Beitrag #5737698:
> when del_pre =>                       -- SS aktivieren und Zeit
> fuer Optokoppler abwarten

Was ich auch noch nicht ganz verstanden habe ist, wofür ich einen 
Optokoppler benötige. Auf der Website steht, dass man den Optokoppler 
zur Potentialtrennung verwendet.

Doch ich verstehe nicht ganz, wozu das nötig ist. Außerdem wird noch 
gesagt, dass auch MISO und MOSI über Optokoppler ans FPGA geführt 
werden.

von VHDL-Frage (Gast)


Lesenswert?

Achim S. schrieb:
> Und dann natürlich noch eine State Machine drumrumbauen, die die
> diversen Konfigurationsregister deines Sensors mit den gewünschten
> Inhalten beschreibt (außer der Reset-State der Register entspricht
> deinen Wünschen) und dann die Status- und Ergebnisregister ausliest.

Aber wo implementiere ich meine State Machine? In einem eignene Prozess 
oder?
Soll ich die State Machine dann am besten mit einer Case-Anweisung 
bauen?

von Duke Scarring (Gast)


Lesenswert?

VHDL-Frage schrieb im Beitrag #5746865:
> Aber wo implementiere ich meine State Machine? In einem eignene Prozess
> oder?
Das ist zumindest eine Variante, die recht übersichtlich sein dürfte.

> Soll ich die State Machine dann am besten mit einer Case-Anweisung
> bauen?
Ja, das bietet sich hier geradezu an:
1
:
2
:
3
  type state_t is (IDLE, CALC, ..., WRITE, READY);
4
:
5
  signal state : state_t;
6
:
7
:
8
begin
9
:
10
:
11
  proces
12
  begin
13
    wait until rising_edge(clk);
14
    
15
    case state is
16
      
17
      when IDLE =>
18
        if ... then
19
          :
20
          :
21
          state <= CALC;
22
        end if;
23
24
      when CALC =>
25
26
      :
27
      :
28
29
      when READY =>
30
        state <= IDLE;
31
32
    end case;
33
34
  end process;
35
:
36
:

Duke

von VHDL-Frage (Gast)


Lesenswert?

Hallo Duke, vielen Dank für deine Antwort.

Duke Scarring schrieb:
> type state_t is (IDLE, CALC, ..., WRITE, READY);

Wo steht aber, welche States ich für meine state machine brauche?

von Achim S. (Gast)


Lesenswert?

VHDL-Frage schrieb im Beitrag #5750960:
> Wo steht aber, welche States ich für meine state machine brauche?

Das hängt primär davon ab, was du mit deinen Messwerten machen willst. 
Wie häufig liest du sie aus, machst du dazwischen Pausen, wartest du auf 
einen Trigger, wie werden die Daten nach dem Auslesen weiterverarbeitet, 
....

von VHDL-Frage (Gast)


Lesenswert?

Ich würde den Sensor alle 0,5s auslesen, die Messwerte im SDRAM 
speichern und nach 30s alle Sensorwerte auf einer SD-Karte 
speichern(aber das dann in einem anderen Untermodul). Für die pausen 
benötige ich dann ja einen Counter.
Muss ich dann in den States die Konfigurationsregister beschreiben?

von Achim S. (Gast)


Lesenswert?

Hm: ich gehe mal davon aus, dass es dir primär um die Beschäftigung mit 
FPGAs und VHDL geht. Denn bisher macht keine deiner Anforderungen den 
Einsatz eines FPGAs auch nur annähernd notwendig, so ziemlich jeder 
kleine µController könnte mit wenig Aufwand deine Messaufgaben 
erledigen.

VHDL-Frage schrieb im Beitrag #5751023:
> Ich würde den Sensor alle 0,5s auslesen, die Messwerte im SDRAM
> speichern

Das machst du, weil du das SDRAM-Interface ausprobieren willst, richtig? 
Denn um 60Messwerte zwischenzuspeicher bräuchtest du kein 32MByte SDRAM, 
da würde der FPGA-interne Speicher lockerst ausreichen.

VHDL-Frage schrieb im Beitrag #5751023:
> Für die pausen
> benötige ich dann ja einen Counter.

Ja: im wesentlichen wird dein FPGA-Desing aus Countern bestehen, die die 
Pausen abwarten. Und dann zwischendurch mal kurz den Sensor auslesen. 
Und wenn der Sensorwert ausgelesen wurde, ihn ins SDRAM speichern. Und 
wenn 60 Messwerte ins SDRAM gespeichert wurden, die Werte von dort 
abholen und auf das andere Modul übertragen.

Die letzten Zeilen wären so in etwas die Prosa-Beschreibung für den 
Entwurf deiner Statemachine.

VHDL-Frage schrieb im Beitrag #5751023:
> Muss ich dann in den States die Konfigurationsregister beschreiben?

Irgendwo in der Statemachine wird es wahrscheinlich einen State "init" 
geben (oder eine Folge von solchen States), in denen die notwendigen 
Konfigurationsregister deines Sensors beschrieben werden.

von VHDL-Frage (Gast)


Lesenswert?

Achim S. schrieb:
> Das machst du, weil du das SDRAM-Interface ausprobieren willst, richtig?

Die 0,5s sind erstmal nur zum Testzweck. Wenn ich später mein System 
synthetisiert und in Hardware getestet habe, werde ich die 
Zeitintervalle auf 0,1s reduzieren. Wenn ich das System dann länger 
laufen lasse, würde das direkte abspeichern auf die Sd-Karte diese ja 
stärker abnutzen.

von VHDL-Frage (Gast)


Lesenswert?

Achim S. schrieb:
> da würde der FPGA-interne Speicher lockerst ausreichen.

Den könnte ich dann natürlich auch benutzen, insofern dies Ressourcen 
spart.

von Achim S. (Gast)


Lesenswert?

VHDL-Frage schrieb im Beitrag #5751124:
> Den könnte ich dann natürlich auch benutzen, insofern dies Ressourcen
> spart.

Mit allem, was du bisher beschrieben hast, verwendest du geschätzt weit 
unter 0,1% der Ressourcen, die du zur Verfügung hast - der Rest der 
Ressourcen wird also vergeudet. Daher nochmal der Hinweis: wenn du das 
mit dem FPGA machst, weil du es genau damit machen willst, kannst du es 
natürlich verwenden (und gerne auch das 32MByte SDRAM zu weit unter 0,1% 
nutzen).

Wenn es dir nicht um die Beschäftigung mit dem FPGA geht sondern um das 
Auslesen und Abspeichern von 10 Messwerten pro Sekunde, dann könntest du 
mit einem Mikrocontroller eine wesentlich einfachere, billigere und 
ressourcenschonendere Lösung umsetzen.

von VHDL-Frage (Gast)


Lesenswert?

Da hast du natürlich recht. Ich werde diesen Code aber nur als 
Untermodul in einem System nutzen. Ich habe schon Module, welche einen 
Algorithmus in Hardware berechnen, eins was Servo Motoren steuert, 
Beschleunigungssensor, Counter etc. Das würde sich am Ende dann 
vielleicht mehr lohnen.

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.