Forum: FPGA, VHDL & Co. SPI Master 1. Übertragung wird verschluckt


von Benjamin (Gast)


Lesenswert?

Hallo zusammen,

ich lerne seit ein paar Wochen intensiv VHDL und bin grad dran, ein DAC 
über SPI mittels Spartan3 anzusteuern. Der DAC besitzt 2 Kanäle und die 
Reihenfolge der zu übertragenden Daten sollte so sein (der DAC verlangt 
16 Bits):

1. Übertragung: 0x9002 (Referenzspannung wählen)
2. Übertragung: 0x1XX0 (Wert Kanal A)
3. Übertragung: 0x8XX0 (Wert Kanal B)

Es sind also 3x je 16 Bits zu übertragen. Als SPI Master hab ich den von 
Lothar Miller so ziemlich exakt übernommen (paar Änderungen musst ich 
machen).

Hier mal der SPI Code:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity spi is
6
  Generic ( crystal_frequency  : integer; -- in Hz
7
         spi_frequency      : integer; -- in Hz
8
         data_length      : integer -- Anzahl der zu übertragenden Bits
9
        );
10
  PORT ( TX_Data    : in  STD_LOGIC_VECTOR (data_length-1 downto 0); -- Sendedaten
11
       Data_out  : out STD_LOGIC;
12
       CS      : out STD_LOGIC;
13
       SCLK      : out STD_LOGIC;
14
       TX_Start  : in  STD_LOGIC;
15
       TX_Done    : out STD_LOGIC;
16
       clk      : in  STD_LOGIC
17
      );
18
end spi;
19
20
architecture Behavioral of spi is
21
  constant frequency_counter_upper_limit : integer := (crystal_frequency/(2*spi_frequency))-1;
22
  signal frequency_counter : integer range 0 to (crystal_frequency/(2*spi_frequency))  := frequency_counter_upper_limit;
23
24
  type states is (idle, active, finished); -- FSM Zustände
25
  signal spi_state   : states  := idle;
26
  
27
  signal spi_clk    : STD_LOGIC;
28
  signal spi_clk_old  : STD_LOGIC;
29
  
30
  signal bit_counter  : integer range 0 to data_length; -- Counter, der die schon rausgeschobenen Bits zählt
31
  
32
  signal tx_reg  : STD_LOGIC_VECTOR (data_length-1 downto 0)  := (others=>'0');
33
begin
34
  ------ FSM ------
35
  process (clk)
36
  begin -- SPI Mode 1: CLK Ruhe Pegel 1, Daten werden bei steigender Flanke ausgegeben und bei fallender eingelesen
37
    if clk'event and clk = '1' then
38
      
39
      if frequency_counter > 0 then -- Clock Enable für den SPI Takt erzeugen, doppelt so schneller Trigger, weil spi_clk jedes Mal nur invertiert wird
40
        frequency_counter <= frequency_counter - 1;
41
      else
42
        frequency_counter <= frequency_counter_upper_limit;
43
      end if;
44
      
45
      spi_clk_old <= spi_clk;
46
      
47
      case spi_state is
48
        when idle =>
49
          CS       <= '1'; -- inaktiv
50
          TX_Done     <= '0';
51
          bit_counter <= data_length;
52
          spi_clk     <= '1';
53
          if TX_Start = '1' then
54
            spi_state   <= active; -- Zustandsübergang
55
            CS        <= '0';
56
            frequency_counter <= frequency_counter_upper_limit;
57
          end if;
58
          
59
        when active =>
60
          if frequency_counter = 0 then
61
            spi_clk <= not spi_clk;
62
            if bit_counter = 0 then -- alle Daten rausgeschoben
63
              spi_clk <= '1';
64
              spi_state <= finished; -- Zustandsübergang
65
            end if;
66
            if spi_clk = '0' then
67
              bit_counter <= bit_counter - 1;
68
            end if;
69
          end if;
70
          
71
        when finished =>
72
          CS      <= '1'; -- inaktiv
73
          TX_Done  <= '1';
74
          if TX_Start = '0' then -- warten bis Eingangsflag gelöscht wurde
75
            spi_state  <= idle; -- Zustandsübergang
76
          end if;
77
      end case;
78
      
79
    end if;
80
  end process;
81
  ------ Sendeschieberegister ------
82
  process (clk)
83
  begin
84
    if clk'event and clk = '1' then
85
    
86
      if spi_state = idle then -- im IDLE Zustand die angelegten Daten übernehmen
87
        tx_reg <= TX_Data;
88
      end if;
89
      if spi_state = active and spi_clk = '1' and spi_clk_old = '0' then -- zur steigenden Flanke die Daten rausschieben
90
        tx_reg <= tx_reg(tx_reg'left-1 downto 0) & tx_reg(0); -- & Operator hängt STD_LOGIC_VECTOR und STD_LOGIC einfach hintereinander, linksschieben
91
      end if;
92
      
93
    end if;
94
  end process;
95
  
96
  SCLK     <= spi_clk;
97
  Data_out <= tx_reg(tx_reg'left);
98
  
99
end Behavioral;

Und der Code der FSM, die die 3 Übertragungen abarbeiten soll:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity threshold is
6
    Port ( ValueA : in  STD_LOGIC_VECTOR (7 downto 0);
7
        ValueB : in  STD_LOGIC_VECTOR (7 downto 0);
8
           WR : in  STD_LOGIC;
9
        WR_Done  : out STD_LOGIC  := '0';
10
        Data_out  : out STD_LOGIC;
11
        CS  : out STD_LOGIC;
12
        SCLK  : out STD_LOGIC;
13
           clk : in  STD_LOGIC);
14
end threshold;
15
16
architecture Behavioral of threshold is
17
  -- Component SPI Deklaration
18
  component spi is
19
    Generic ( crystal_frequency  : integer; -- in Hz
20
           spi_frequency      : integer; -- in Hz
21
           data_length      : integer -- Anzahl der zu übertragenden Bits
22
           );
23
    PORT ( TX_Data    : in  STD_LOGIC_VECTOR (data_length-1 downto 0); -- Sendedaten
24
         Data_out  : out STD_LOGIC;
25
         CS      : out STD_LOGIC;
26
         SCLK      : out STD_LOGIC;
27
         TX_Start  : in  STD_LOGIC;
28
         TX_Done    : out STD_LOGIC;
29
         clk      : in  STD_LOGIC
30
         );
31
  end component spi;
32
  
33
  signal TX_Data    : STD_LOGIC_VECTOR (15 downto 0)  := (others => '0');
34
  signal TX_Start  : STD_LOGIC;
35
  signal TX_Done    : STD_LOGIC;
36
  
37
  signal state  : integer range 0 to 10  := 0;
38
  signal init_state  : integer range 0 to 5  := 0;
39
  
40
  signal delay_counter  : integer range 0 to 4095  := 4095;
41
42
begin
43
  -- Component SPI Instanziierung
44
  spi_instance: component spi
45
      Generic MAP (crystal_frequency => 50000000, spi_frequency => 1000000, data_length => 16)
46
      PORT MAP ( TX_Data => TX_Data,
47
             Data_out => Data_out,
48
             CS => CS,
49
             SCLK => SCLK,
50
             TX_Start => TX_Start,
51
             TX_Done => TX_Done,
52
             clk => clk
53
            );
54
  -- FSM für die Initialisierung der Referenz-Spannung des DAC + 2 Bytes, die nacheinander über SPI rausgeschickt werden müssen
55
  process (clk)
56
  begin
57
    if clk'event and clk = '1' then
58
        case state is -- Initialisierungs-FSM
59
          when 0 =>
60
            case init_state is
61
              when 0 =>
62
                if delay_counter > 0 then
63
                  delay_counter <= delay_counter - 1;
64
                else
65
                  delay_counter <= 100;
66
                  init_state <= 1;
67
                end if;
68
        
69
              when 1 =>
70
                if WR = '1' then
71
                  TX_Data <= x"9002";
72
                  init_state <= 2;
73
                end if;
74
                
75
              when 2 =>
76
                if delay_counter > 0 then
77
                  delay_counter <= delay_counter - 1;
78
                else
79
                  delay_counter <= 100;
80
                  init_state <= 3;
81
                end if;
82
                
83
              when 3 =>
84
                TX_Start <= '1';
85
                init_state <= 4;
86
                
87
              when 4 =>
88
                if TX_Done = '1' then
89
                  TX_Start <= '0';
90
                  init_state <= 5;
91
                end if;
92
            
93
              when 5 =>
94
                if delay_counter > 0 then
95
                  delay_counter <= delay_counter - 1;
96
                else
97
                  delay_counter <= 100;
98
                  init_state <= 0;
99
                  state <= 1;
100
                end if;
101
                
102
            end case;
103
                
104
          when 1 =>
105
            -- if WR = '1' then
106
              -- TX_Data <= x"9002";
107
              TX_Data <= x"1" & ValueA & x"0"; 
108
              state <= 2;
109
            -- end if;
110
            
111
          when 2 =>
112
            if delay_counter > 0 then
113
              delay_counter <= delay_counter - 1;
114
            else
115
              delay_counter <= 100;
116
              state <= 3;
117
            end if;
118
            
119
          when 3 =>
120
            TX_Start <= '1';
121
            state <= 4;
122
            
123
          when 4 =>
124
            if TX_Done = '1' then
125
              TX_Start <= '0';
126
              state <= 5;
127
            end if;
128
            
129
          when 5 =>
130
            if delay_counter > 0 then
131
              delay_counter <= delay_counter - 1;
132
            else
133
              delay_counter <= 100;
134
              state <= 6;
135
            end if;
136
            
137
          when 6 =>
138
            TX_Data <= x"8" & ValueB & x"0"; -- 2. Byte mit ValueB
139
            state <= 7;
140
                        
141
          when 7 =>
142
            if delay_counter > 0 then
143
              delay_counter <= delay_counter - 1;
144
            else
145
              delay_counter <= 100;
146
              state <= 8;
147
            end if;
148
            
149
          when 8 =>
150
            TX_Start <= '1';
151
            state <= 9;
152
            
153
          when 9 =>
154
            if TX_Done = '1' then
155
              TX_Start <= '0';
156
              state <= 10;
157
            end if;
158
            
159
          when 10 =>
160
            if delay_counter > 0 then
161
              delay_counter <= delay_counter - 1;
162
            else
163
              WR_Done <= '1';
164
              if WR = '0' then
165
                delay_counter <= 100;
166
                WR_Done <= '0';
167
                state <= 0;
168
              end if;
169
            end if;
170
        
171
        end case;
172
    end if;
173
  end process;
174
175
end Behavioral;

Getriggert wird der Ablauf durch das WR-Signal. Die zu schickenden Werte 
für den DAC sind ValueA und ValueB. Die ersten 16. Bit haben eine eigene 
FSM, da ich diese mal als Initialiserung später ausgliedern will (muss 
nicht jedes Mal geschickt werden, wenn getriggert wird).

Kommen wir nur zum eigentlichen Problem:

Wird nach dem Anschließen der Versorgungsspannung die FSM das erste Mal 
getriggert, scheint der die Übertragung der ersten 16 Bits einfach zu 
übergehen. Die Referenzspannung wird nicht eingestellt beim DAC. Man 
kann auf dem DSO schön die Übertragung der anderen zwei 16 Bit Folgen 
sehen, die erste Übertragung wird jedoch einfach nicht gesendet. Trigger 
ich die FSM ein zweites oder drittes Mal, dann werden jeweils alle 3 
Übertragungen durchgeführt und funktioniert auch.

Bei Bedarf kann ich auch mal Oszi Bilder von den Übertragungen anhängen.


Ich weiß einfach nicht mehr weiter. Ich hoffe, ihr könnt mir helfen.

Viele Grüße,
Benjamin

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


Lesenswert?

Benjamin schrieb:
> Wird nach dem Anschließen der Versorgungsspannung die FSM das erste Mal
> getriggert, scheint der die Übertragung der ersten 16 Bits einfach zu
> übergehen.
Und was sagt die Simulation/Testbench zu diesem Thema?

Benjamin schrieb:
1
  signal state  : integer range 0 to 10  := 0;
2
:
3
  case state is -- Initialisierungs-FSM
4
     when 0 =>
Warum lässt du nicht den Synhtesizer die States abhandeln? Wenn du 
symbolische Namen vergibst, hat er eine Chance zum Optimieren und du 
kannst deinen Code wesentlich besser lesen...
So wie z.B. bei meinen SPI-Mastern:
http://www.lothar-miller.de/s9y/categories/45-SPI-Master


Dir ist schon klar, dass du im realen Leben mit asynchronen Eingängen 
nicht direkt in einer FSM herumfroschen sollt:
1
entity threshold is
2
    Port ( ValueA : in  STD_LOGIC_VECTOR (7 downto 0);
3
           ValueB : in  STD_LOGIC_VECTOR (7 downto 0);
4
           WR : in  STD_LOGIC;
5
:
6
    if clk'event and clk = '1' then
7
        case state is -- Initialisierungs-FSM
8
          when 0 =>
9
            case init_state is
10
              when 1 =>
11
                if WR = '1' then  -- aber Holla: ein asynchroner Eingang tief in einer FSM
Du wirst dir damit diesen Effekt einfangen:
http://www.lothar-miller.de/s9y/archives/64-State-Machine-mit-asynchronem-Eingang.html
Im Überblick:
http://www.lothar-miller.de/s9y/categories/35-Einsynchronisieren

von Benjamin (Gast)


Angehängte Dateien:

Lesenswert?

Danke für deine Antwort. Die Simulation zeigt genau das, was ich auch 
aufgrund des VHDL Codes erwarten würde und was passieren soll. Ich habe 
ein Screenshot der Simulation angehängt.

Wegen den States werd ich mir angewöhnen, diese als type zu deklarieren. 
Ich dachte nur in diesem Falle, dass ich für so viele Zustände keine 
Wirklichen aussagekräftigen Namen/Bezeichnungen finden würde.

Tut mir leid für die fehlende Information, aber WR ist ein synchrones 
Signal, was von einer Empfangs FSM (FTDI Chip auslesen) erzeugt wird. 
Sollte also auch kein Problem geben.

Im Prinzip sollte es ja auch funktionieren. Was ist das schon für ein 
Unterschied, ob ich die FSM das erste Mal trigger oder ein zweites? Beim 
Übergehen der ersten 16 Bits wird übrigens auch kein SPI Takt 
ausgegeben.

Noch ein komisches Phenomen:
Lasse ich die eingeliederte erste FSM für die Initialisierung selber 
anlaufen nach paar µs, dann läuft zwar der Takt an, aber die geshifteten 
Daten sind alle "0". Ich werde morgen mal ein paar Bilder vom Oszi 
anhängen.

Ich weiß echt nicht mehr weiter. Welche Phenomene kann es denn geben, 
die den Übergang von Simulation zu Realität erschweren?

von Benjamin (Gast)


Lesenswert?

Ich hab nun ein globalen Power On Reset eingebaut und jetzt wird die 
erste Übertragung nicht mehr übersprungen. Jetzt tuts, was es soll!

von Duke Scarring (Gast)


Lesenswert?

Benjamin schrieb:
> Ich hab nun ein globalen Power On Reset eingebaut und jetzt wird die
> erste Übertragung nicht mehr übersprungen.
Kannst Du das kurz skizzieren?
Die Xilinx-FPGAs haben ja eignetlich schon einen GSR.

Duke

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


Lesenswert?

Würde mich auch interessieren...  :-/

Bestenfalls hier könnte der Synthesizer noch was herumtricksen:
1
  signal TX_Start  : STD_LOGIC;
2
  signal TX_Done    : STD_LOGIC;
Werden diese beiden Signale mit dem zusätzlichen Reset auch 
zurückgesetzt?

Was passiert im Originalprojekt, wenn du da schreibst:
... STD_LOGIC :='0';

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.