Forum: FPGA, VHDL & Co. VHDL: UART State Machine


von Hugo P. (portisch)


Lesenswert?

Hallo,

ich bin VHDL Anfänger und hab nun so einige Probleme eine UART State 
Machine zum Laufen zu bekommen. Das Empfangen/Senden von 8 Bits (1 Byte) 
funktioniert ohne Probleme - mit z.B. einem Echo Process.

Nun will ich aber ein Protokoll im FPGA implementieren. Ein Command vom 
PC zum FPGA sieht z.B. so aus:
AA 55 00 03 00 00
AA: Sync1
55: Sync2
00: FPGA Adresse (RS422)
03: Command (Set DAC)
00: 15..8 data of 16 Bit Unsigned
00: 7..0 data of 16 Bit Unsigned

Ich habe die UART von hier in Verwendung:
https://github.com/jakubcabal/uart-for-fpga

(Habe aber das Parity rausgeschmissen)

Ich habe das Cmod S6 Board das eine 8MHz und eine 1Hz Clock für den FPGA 
hat.
Aus dem 8MHz mache ich mir mit einem IP Core 100Mhz mit dieser der UART 
betrieben wird. Der Process(CLK_LFC) überprüft ob es einen Timeout gibt 
mit ~1-2s. Dann springt die UART State Machine zurück auf idle.

Nun funktioniert das ganze in der Simulation aber beim Erstellen des 
Bitstreams für den FPGA gibt es diese Warnung und deswegen funktioniert 
das auf dem FPGA nicht. Verwendet wird Xilinx ISE 14.7:
1
WARNING:PhysDesignRules:372 - Gated clock. Clock net state[3]_PWR_14_o_Mux_31_o
2
   is sourced by a combinatorial pin. This is not good design practice. Use the
3
   CE pin to control the loading of data into the flip-flop.
4
WARNING:PhysDesignRules:372 - Gated clock. Clock net state[3]_PWR_20_o_Mux_43_o
5
   is sourced by a combinatorial pin. This is not good design practice. Use the
6
   CE pin to control the loading of data into the flip-flop.
1
----------------------------------------------------------------------------------
2
-- Company: 
3
-- Engineer: 
4
-- 
5
-- Create Date:    13:20:04 06/06/2018 
6
-- Design Name: 
7
-- Module Name:    Spartan6_Uart - Behavioral 
8
-- Project Name: 
9
-- Target Devices: 
10
-- Tool versions: 
11
-- Description: 
12
--
13
-- Dependencies: 
14
--
15
-- Revision: 
16
-- Revision 0.01 - File Created
17
-- Additional Comments: 
18
--
19
----------------------------------------------------------------------------------
20
library IEEE;
21
use IEEE.STD_LOGIC_1164.ALL;
22
23
-- Uncomment the following library declaration if using
24
-- arithmetic functions with Signed or Unsigned values
25
use IEEE.NUMERIC_STD.ALL;
26
27
-- Uncomment the following library declaration if instantiating
28
-- any Xilinx primitives in this code.
29
--library UNISIM;
30
--use UNISIM.VComponents.all;
31
32
entity Spartan6_Uart is
33
    Generic (
34
        CLK_FREQ   : integer := 100e6;  -- set system clock frequency in Hz
35
        BAUD_RATE  : integer := 9600    -- baud rate value
36
    );
37
  Port ( 
38
    -- FPGA_GCLK, 8MHz
39
    CLK      : in STD_LOGIC;
40
41
    -- FPGA_LFC, 1 Hz
42
    CLK_LFC    : in STD_LOGIC;
43
44
    -- LED outputs
45
    LED_0      : out STD_LOGIC;
46
    LED_1      : out STD_LOGIC;
47
    LED_2      : out STD_LOGIC;
48
    LED_3      : out STD_LOGIC;
49
50
    -- BTN inputs
51
    BTN_0      : in STD_LOGIC;
52
    BTN_1      : in STD_LOGIC;    
53
54
    -- connected to physical I/O pad
55
    PORTA     : inout  STD_LOGIC_VECTOR (7 downto 0);
56
    PORTB     : inout  STD_LOGIC_VECTOR (7 downto 0);
57
    PORTC     : inout  STD_LOGIC_VECTOR (6 downto 0);
58
    PORTD     : inout  STD_LOGIC_VECTOR (7 downto 0);
59
    PORTE     : inout  STD_LOGIC_VECTOR (7 downto 0);  
60
    PORTF     : inout  STD_LOGIC_VECTOR (6 downto 0)
61
  );
62
end Spartan6_Uart;
63
64
architecture Behavioral of Spartan6_Uart is
65
-------------------------------------------------------
66
-- 100 MHz clock
67
-------------------------------------------------------
68
  component Clock_100Mhz
69
    port
70
    (  
71
      -- Clock in ports
72
      CLK_IN1           : in     std_logic;
73
      -- Clock out ports
74
      CLK_OUT1          : out    std_logic
75
    );
76
  end component;
77
  
78
  signal clk_100 : STD_LOGIC;
79
  
80
-------------------------------------------------------
81
-- UART
82
-------------------------------------------------------  
83
84
  signal data_in    : std_logic_vector(7 downto 0) := ( others => '0' );
85
  signal data_out   : std_logic_vector(7 downto 0) := ( others => '0' );
86
87
  signal valid_in: std_logic := '0';
88
  signal valid_out: std_logic := '0';
89
  
90
  signal BUSY   : std_logic;
91
  
92
  -- FSM states
93
  type statetype is (idle, sync_1, sync_2, address_match, cmd3_r, send_address, AKN, s_tx);
94
  signal state, next_state, next_tx_state : statetype := idle;  
95
  
96
  signal timeout_counter : integer range 0 to 1 := 0;
97
  signal timeout : std_logic := '0';
98
  
99
begin
100
101
  reset <= BTN_0;
102
  
103
  my_100MHz_clock_instance : Clock_100Mhz
104
  port map
105
  (  
106
    -- Clock in ports
107
    CLK_IN1 => CLK,
108
    -- Clock out ports
109
    CLK_OUT1 => clk_100
110
  );
111
  
112
  uart_i: entity work.UART
113
    generic map (
114
        CLK_FREQ    => CLK_FREQ,
115
        BAUD_RATE   => BAUD_RATE
116
    )
117
    port map (
118
        CLK         => clk_100,
119
        RST         => reset,
120
        -- UART INTERFACE
121
        UART_TXD    => PORTD(4),
122
        UART_RXD    => PORTD(5),
123
        -- USER DATA OUTPUT INTERFACE
124
        DATA_OUT    => data_in,
125
        DATA_VLD    => valid_in,
126
        -- USER DATA INPUT INTERFACE
127
        DATA_IN     => data_out,
128
        DATA_SEND   => valid_out,
129
        BUSY        => BUSY      
130
    );
131
   
132
   process (clk_100) is
133
   begin
134
    if rising_edge(clk_100) then
135
      if timeout = '0' then
136
        state <= next_state;
137
      else
138
        state <= idle;
139
      end if;
140
    end if;   
141
   end process;
142
   
143
   process (CLK_LFC) is
144
   begin
145
    if rising_edge(CLK_LFC) then    
146
      if state /= idle then
147
        timeout_counter <= timeout_counter + 1;
148
        
149
        if timeout_counter = 1 then
150
          timeout <= '1';
151
        else
152
          timeout <= '0';
153
        end if;
154
      else
155
        timeout_counter <= 0;
156
        timeout <= '0';
157
      end if;
158
    end if;
159
   end process;
160
   
161
  process(state, valid_in, BUSY)
162
    variable bits : integer range 0 to 48 := 0;
163
  begin  
164
      next_state <= state;    
165
      valid_out <= '0';
166
      
167
      case state is
168
        when idle =>
169
          next_state <= idle;    
170
          if valid_in = '1' then
171
            if data_in = x"AA" then
172
              next_state <= sync_1;              
173
            end if;
174
          end if;
175
          
176
        when sync_1 =>
177
          next_state <= sync_1;    
178
          if valid_in = '1' then
179
            if data_in = x"55" then
180
              next_state <= sync_2;              
181
            else
182
              next_state <= idle;
183
            end if;
184
          end if;  
185
186
        when sync_2 =>
187
          next_state <= sync_2;  
188
          if valid_in = '1' then
189
            if unsigned(data_in) = unsigned(PORTF) then
190
              next_state <= address_match;              
191
            else
192
              next_state <= idle;
193
            end if;
194
          end if;
195
          
196
        when address_match =>
197
          next_state <= address_match;
198
          if valid_in = '1' then
199
            if unsigned(data_in) = x"03" then
200
              bits := 16;
201
              next_state <= cmd3_r;
202
            end if;          
203
          end if;
204
          
205
        when cmd3_r =>
206
          next_state <= cmd3_r;
207
          if valid_in = '1' then
208
            
209
            bits := bits - 8;
210
            --load 16 unsigned value
211
                      
212
            if bits = 0 then        
213
              next_tx_state <= AKN;
214
              next_state <= send_address;              
215
            end if;
216
          end if;
217
          
218
        when s_rx_wait_done =>
219
          if valid_in = '0' then
220
            next_state <= next_rx_state;
221
          end if;    
222
            
223
        when send_address =>
224
          data_out <= "0" & PORTF;
225
          next_state <= s_tx;
226
          
227
        when AKN =>
228
          data_out <= x"55";
229
          next_tx_state <= idle;
230
          next_state <= s_tx;        
231
          
232
        when s_tx =>
233
          next_state <= s_tx;
234
          if BUSY = '0' then            
235
            valid_out <= '1';
236
            next_state <= next_tx_state;
237
          end if;    
238
      end case;
239
  end process;
240
     
241
  
242
243
  -------------------------------------------------------
244
  -- LED/BTN Testing
245
  -------------------------------------------------------
246
  LED_3 <= CLK_LFC;
247
  LED_2 <= not CLK_LFC;
248
249
  LED_0 <= BTN_0;
250
  LED_1 <= timeout;
251
252
end Behavioral;

Das Warning über die Gated clock bekomme ich weg, wenn ich die State 
Machine zusätzlich um die clk_100 erweitere und ein "if 
rising_edge(clk_100) then" einbaue.

Gibt es irgendwo ein Beispiel wo so eine State Machine dargestellt ist?
Im Internet finde ich nur die UART State Machine selbst um die 8 bits zu 
empfangen/senden. Jedoch nichts dazu wenn mehrere Bytes empfangen werden 
sollen...

Oder wie löst man eine solche Protokoll Auswertung im FPGA besser!?

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


Lesenswert?

Hugo P. schrieb:
> Das Warning über die Gated clock bekomme ich weg, wenn ich die State
> Machine zusätzlich um die clk_100 erweitere und ein "if
> rising_edge(clk_100) then" einbaue.
Der beliebte Anfängerfehler aus der Zwei-Prozess-Fraktion: du hast mit 
"bits := bits - 8;" in einem kombinatorischen Prozess einen Zähler 
eingebaut.
Der Synthesizer führt dich in die Irre, denn eigentlich ist das eine 
kombinatorische Schleife, die aber über den state und valid_in 
unterbrochen (gegated) werden kann.
Hier die Grundlagen dazu:
http://www.lothar-miller.de/s9y/archives/42-Kombinatorische-Schleifen.html

Mit ein wenig Stöbern findest du dort auch noch eine RS232 
Schnittstelle. Die kannst du dir ja mal als Denkanstoß anschauen...

> Nun will ich aber ein Protokoll im FPGA implementieren. Ein Command vom
> PC zum FPGA sieht z.B. so aus:
> AA 55 00 03 00 00
> AA: Sync1
> 55: Sync2
> 00: FPGA Adresse (RS422)
> 03: Command (Set DAC)
> 00: 15..8 data of 16 Bit Unsigned
> 00: 7..0 data of 16 Bit Unsigned
Das ist ein recht wagemutiges Protokoll. Was, wenn da Daten AA 55 
übertragen werden sollen?

Aber prinzipiell läuft es schon so ab, dass du ein Byte nach dem anderen 
empfängst und dann entsprechend der Kommandos verzweigst. eben so, wie 
du es da schon implementiert hast. Nur eben ohne die kombinatorische 
Schleife...

> Ich habe die UART von hier in Verwendung:
Unschöne Sache, diese Taktgenerierung. Das sollte die Toolchain auch 
anmeckern...

: Bearbeitet durch Moderator
von Hugo P. (portisch)


Lesenswert?

Danke! Das Hilft doch gleich mal weiter!
Ein Problem ist das "bits" und ein weiteres das "data_out".

Werde mir die Methode "Ein-Prozess-Darstellung" für die FSM zu Herzen 
nehmen und einmal versuchen diese so umzubauen. (Ist mir wegen der 
Übersicht auch lieber...)

von Hugo P. (portisch)


Lesenswert?

Das geht doch gleich viel besser!!

Habe hier mal nur den Empfang der Daten AA 55 00 03 00 00 eingebaut:
1
   process (CLK_LFC) is
2
   begin
3
    if rising_edge(CLK_LFC) then    
4
      if state /= idle then
5
        timeout_counter <= timeout_counter + 1;
6
        
7
        if timeout_counter = 1 then
8
          timeout <= '1';
9
        else
10
          timeout <= '0';
11
        end if;
12
      else
13
        timeout_counter <= 0;
14
        timeout <= '0';
15
      end if;
16
    end if;
17
   end process;   
18
   
19
   process(clk_100) is
20
    variable bits : integer range 0 to 48 := 0;
21
   begin
22
    if rising_edge(clk_100) then  
23
      
24
      if timeout = '1' then
25
        state <= idle;
26
      end if;
27
      
28
      case state is
29
        when idle =>
30
          if valid_in = '1' then
31
            if data_in = x"AA" then
32
              state <= sync_1;
33
            else
34
              state <= idle;
35
            end if;
36
          end if;
37
          
38
        when sync_1 =>
39
          if valid_in = '1' then
40
            if data_in = x"55" then
41
              state <= sync_2;
42
            else
43
              state <= idle;
44
            end if;
45
          end if;  
46
47
        when sync_2 =>
48
          if valid_in = '1' then
49
            if unsigned(data_in) = unsigned(PORTF) then
50
              state <= address_match;
51
            else
52
              state <= idle;
53
            end if;
54
          end if;
55
          
56
        when address_match =>
57
          if valid_in = '1' then
58
            if unsigned(data_in) = x"03" then
59
              bits := 16;
60
              state <= cmd_03;
61
            else
62
              state <= idle;
63
            end if;          
64
          end if;
65
          
66
        when cmd_03 =>
67
          if valid_in = '1' then
68
            bits := bits - 8;
69
            
70
            if bits = 0 then
71
              next_tx_state <= AKN;
72
              state <= send_address;              
73
            end if;
74
          end if;
75
          
76
        when send_address =>
77
          if BUSY = '0' then
78
            data_out <= "0" & PORTF;
79
            valid_out <= '1';
80
            state <= s_tx_done;
81
          end if;          
82
          
83
        when AKN =>
84
          if BUSY = '0' then
85
            data_out <= x"55";
86
            valid_out <= '1';
87
            next_tx_state <= idle;
88
            state <= s_tx_done;
89
          end if;  
90
91
        when s_tx_done =>
92
          if BUSY = '0' then
93
            valid_out <= '0';
94
            state <= next_tx_state;
95
          end if;
96
          
97
        when others =>
98
          state <= idle;
99
      end case;
100
    end if;
101
   end process;

Lothar M. schrieb:
> Das ist ein recht wagemutiges Protokoll. Was, wenn da Daten AA 55
> übertragen werden sollen?

Wie sollte man das sicherer Lösen? Einer Checksum über alle Daten 
zusätzlich?

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


Lesenswert?

Hugo P. schrieb:
> Wie sollte man das sicherer Lösen?
Entweder mit der Prüfsumme, die aber im Grunde auch nur das Verwerfen 
solch versetzter Telegramme erleichtert.
Mit einem langen Timeout zwischen den Telegrammen ginge das auch. Den 
Counter dafür hast du ja schon.

Ich übertrage die Nutzdaten nicht binär sondern als ASCII-Zeichen z.B. 
als Hex-Zahlen. Dann habe ich zwar mehr Traffic, kann aber mit den 
üblichen seriellen Steuerzeichen wie STX, ETX, CR, LF usw. arbeiten. Und 
das Zeug mit jedem beliebigen Terminal zeilenweise mitlesen.

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.