Bug fix: unconsistent changes of match signal caused twitches im motor movement....
[fpga/rpi-motor-control.git] / pmsm-control / rpi_pmsm_control.vhdl
1 --
2 -- * Raspberry Pi BLDC/PMSM motor control design for RPi-MI-1 board *
3 -- The toplevel component file
4 --
5 -- (c) 2015 Martin Prudek <prudemar@fel.cvut.cz>
6 -- Czech Technical University in Prague
7 --
8 -- Project supervision and original project idea
9 -- idea by Pavel Pisa <pisa@cmp.felk.cvut.cz>
10 --
11 -- Related RPi-MI-1 hardware is designed by Petr Porazil,
12 -- PiKRON Ltd  <http://www.pikron.com>
13 --
14 -- VHDL design reuses some components and concepts from
15 -- LXPWR motion power stage board and LX_RoCoN system
16 -- developed at PiKRON Ltd with base code implemented
17 -- by Marek Peca <hefaistos@gmail.com>
18 --
19 -- license: GNU LGPL and GPLv3+
20 --
21
22 library ieee;
23 use ieee.std_logic_1164.all;
24 use ieee.numeric_std.all;
25 use work.util.all;
26
27 entity rpi_pmsm_control is
28 generic(
29         pwm_width : natural:=11
30         );
31 port (
32         gpio2: in std_logic; -- SDA
33         gpio3: in std_logic; -- SCL
34         gpio4: in std_logic; -- CLK
35         gpio14: in std_logic; -- Tx
36         gpio15: in std_logic; -- Rx
37         gpio17: in std_logic; -- RTS
38         gpio18: in std_logic; -- PWM0/PCMCLK
39         gpio27: in std_logic; -- SD1DAT3
40         gpio22: in std_logic; -- SD1CLK
41         gpio23: in std_logic; -- SD1CMD
42         gpio24: in std_logic; -- SD1DAT0
43         gpio10: in std_logic; -- SPI0MOSI
44         gpio9: out std_logic; -- SPI0MISO
45         gpio25: in std_logic; -- SD1DAT1
46         gpio11: in std_logic; -- SPI0SCLK
47         gpio8: in std_logic; -- SPI0CE0
48         gpio7: in std_logic; -- SPI0CE1
49         gpio5: in std_logic; -- GPCLK1
50         gpio6: in std_logic; -- GPCLK2
51         gpio12: in std_logic; -- PWM0
52         gpio13: in std_logic; -- PWM1
53         gpio19: in std_logic; -- PWM1/SPI1MISO/PCMFS
54         gpio16: in std_logic; -- SPI1CE2
55         gpio26: in std_logic; -- SD1DAT2
56         gpio20: in std_logic; -- SPI1MOSI/PCMDIN/GPCLK0
57         gpio21: in std_logic; -- SPI1SCLK/PCMDOUT/GPCLK1
58         --
59         -- PWM
60         -- Each PWM signal has cooresponding shutdown
61         pwm: out std_logic_vector (1 to 3);
62         shdn: out std_logic_vector (1 to 3);
63         -- Fault/power stage status
64         stat: in std_logic_vector (1 to 3);
65         -- HAL inputs
66         hal_in: in std_logic_vector (1 to 3);
67         -- IRC inputs
68         irc_a: in std_logic;
69         irc_b: in std_logic;
70         irc_i: in std_logic;
71         -- Power status
72         power_stat: in std_logic;
73         -- ADC for current
74         adc_miso: in std_logic;
75         adc_mosi: out std_logic;
76         adc_sclk: out std_logic;
77         adc_scs: out std_logic;
78         -- Extarnal SPI
79         ext_miso: in std_logic; --master in slave out
80         ext_mosi: in std_logic; --master out slave in
81         ext_sclk: in std_logic;
82         ext_scs0: in std_logic;
83         ext_scs1: in std_logic;
84         ext_scs2: in std_logic;
85         -- RS-485 Transceiver
86         rs485_rxd: in std_logic;
87         rs485_txd: out std_logic;
88         rs485_dir: out std_logic;
89         -- CAN Transceiver
90         can_rx: in std_logic;
91         can_tx: in std_logic;
92         -- DIP switch
93         dip_sw: in std_logic_vector (1 to 3); --na desce je prohozene cislovanni
94         -- Unused terminal to keep design tools silent
95         dummy_unused : out std_logic
96 );
97 end rpi_pmsm_control;
98
99
100 architecture behavioral of rpi_pmsm_control is
101         attribute syn_noprune :boolean;
102         attribute syn_preserve :boolean;
103         attribute syn_keep :boolean;
104         attribute syn_hier :boolean;
105
106         -- Actel lib
107         component pll50to200
108                 port (
109                         powerdown, clka: in std_logic;
110                         lock, gla: out std_logic
111                 );
112         end component;
113         
114         component CLKINT
115                 port (A: in std_logic; Y: out std_logic);
116         end component;
117         
118         component qcounter
119         port (
120                 clock: in std_logic;
121                 reset: in std_logic;
122                 a0, b0: in std_logic;
123                 qcount: out std_logic_vector (31 downto 0);
124                 a_rise, a_fall, b_rise, b_fall, ab_event: out std_logic;
125                 ab_error: out std_logic
126         );
127         end component;
128
129         component mcpwm is
130         generic (
131                 pwm_width: natural
132         );
133         port (
134                 clock: in std_logic;
135                 sync: in std_logic;                             --flag that counter "restarts-overflows"
136                 data_valid:in std_logic;                        --indicates data is consistent
137                 failsafe: in std_logic;                         --turn off both transistors
138                 en_p, en_n: in std_logic;                       --enable positive & enable shutdown
139                 match: in std_logic_vector (pwm_width-1 downto 0); --posion of counter when we swap output logic
140                 count: in std_logic_vector (pwm_width-1 downto 0); --we use an external counter
141                 out_p, out_n: out std_logic                     --pwm outputs: positive & shutdown
142                 --TODO add the rest of pwm signals, swap match to pwm_word
143         );
144         end component;
145         
146         --frequency division by 12
147         component divider is
148         port (
149                 clk_in: in std_logic;
150                 div12: out std_logic
151         );
152         end component;
153         
154         component adc_reader is
155         port (
156                 clk: in std_logic;                                      --input clk
157                 divided_clk : in std_logic;                             --divided clk - value suitable to sourcing voltage
158                 adc_reset: in std_logic;
159                 adc_miso: in std_logic;                                 --spi master in slave out
160                 adc_channels: out std_logic_vector (35 downto 0);       --consistent data of 3 channels
161                 adc_sclk: out std_logic;                                --spi clk
162                 adc_scs: out std_logic;                                 --spi slave select
163                 adc_mosi: out std_logic;                                --spi master out slave in
164                 measur_count: out std_logic_vector(8 downto 0)          --number of accumulated measurments
165         
166         );
167         end component;
168         
169         
170         signal adc_channels: std_logic_vector(71 downto 0);
171         signal adc_m_count: std_logic_vector(8 downto 0);
172
173         --clock signals for logic and master fail monitoring
174         signal gpio_clk: std_logic;
175         signal pll_clkin, pll_clkout, pll_lock: std_logic;
176         signal clkmon_dly1, clkmon_dly2: std_logic;
177         signal clkmon_fail, clkmon_fail_next: std_logic;
178         signal clkmon_wdg: integer range 0 to 6;
179         signal reset_sync, reset_async: std_logic;
180         signal failsafe, next_failsafe: std_logic;
181
182         --RPi SPI interface signals named aliases
183         signal spi_clk, spi_ce, spi_mosi, spi_miso : std_logic;
184         signal spiclk_old: std_logic_vector(1 downto 0); --pro detekci hrany SPI hodin
185
186         --signal pwm_in, pwm_dir_in: std_logic;
187         signal dat_reg : STD_LOGIC_VECTOR (127 downto 0); --shift register for spi
188         signal position: std_logic_vector(31 downto 0); --pozice z qcounteru
189         signal index_position: std_logic_vector(11 downto 0);           --pozice irc_i
190         signal ce0_old: std_logic_vector(1 downto 0);
191         
192         --pwm signals
193         constant pwm_n: natural := 3;                                   --number of pwm outputs
194         --number of ticks per pwm cycle, 2^11=2048
195         constant pwm_period : std_logic_vector (pwm_width-1 downto 0) := (others=>'1'); 
196         type pwm_res_type is array(1 to 3) of std_logic_vector (pwm_width-1 downto 0);
197         
198         signal pwm_match: pwm_res_type;                                 --point of reversion of pwm output, 0 to 2047
199         signal pwm_count: std_logic_vector (pwm_width-1 downto 0);      --counter, 0 to 2047
200         signal pwm_sync_at_next: std_logic;
201         signal pwm_sync: std_logic;
202         signal pwm_en_p: std_logic_vector(1 to 3);
203         signal pwm_en_n: std_logic_vector(1 to 3);
204         signal pwm_sig: std_logic_vector(1 to 3);
205         
206         signal income_data_valid: std_logic;
207         
208         signal clk_4M17: std_logic;
209
210         -- irc signals processing
211         signal irc_i_prev: std_logic;
212         
213         --  attribute syn_noprune of gpio2 : signal is true;
214         --  attribute syn_preserve of gpio2 : signal is true;
215         --  attribute syn_keep of gpio2 : signal is true;
216         --  attribute syn_hier of gpio2 : signal is true;
217
218 begin
219         -- PLL as a reset generator
220         
221         --zesileni signalu GPIO CLK
222         copyclk2: CLKINT
223         port map (
224                 a => gpio4,
225                 y => gpio_clk
226         );
227         
228         pll: pll50to200
229         port map (
230                 powerdown => '1',
231                 clka => pll_clkin,
232                 gla => pll_clkout,
233                 lock => pll_lock);
234
235         -- the failasfe signal from communication block if CRC is used
236         next_failsafe <= '0';
237
238         reset_async <= not pll_lock or clkmon_fail;
239
240         pll_clkin <= gpio_clk;
241         
242         qcount: qcounter
243         port map (
244                 clock => gpio_clk,
245                 reset => '0',
246                 a0 => irc_a,
247                 b0 => irc_b,
248                 qcount => position,
249                 a_rise => open,
250                 a_fall => open,
251                 b_rise => open,
252                 b_fall => open,
253                 ab_event => open,
254                 ab_error => open
255         );
256         
257         pwm_block: for i in pwm_n downto 1 generate
258                 pwm_map: mcpwm
259                 generic map (
260                         pwm_width => pwm_width
261                 )
262                 port map (
263                         clock => gpio_clk,                              --50 Mhz clk from gpclk on raspberry
264                         sync => pwm_sync,                               --counter restarts
265                         data_valid => pwm_sync_at_next,                 
266                         failsafe => failsafe,
267                         --
268                         -- pwm config bits & match word
269                         --
270                         en_n => pwm_en_n(i),                            --enable positive pwm
271                         en_p => pwm_en_p(i),                            --enable "negative" ->activate shutdown
272                         match => pwm_match(i),
273                         count => pwm_count,
274                         -- outputs
275                         out_p => pwm_sig(i),                            --positive signal
276                         out_n => shdn(i)                                --reverse signal is in shutdown mode
277                 );
278         end generate;
279         
280         
281         div12_map: divider
282         port map(
283                 --reset => income_data_valid,
284                 clk_in => gpio_clk,
285                 div12 => clk_4M17
286         );
287         
288         -- ADC needs 3.2 MHz clk when powered from +5V Vcc
289         --           2.0 MHz clk when +2.7V Vcc
290         -- on the input is 4.17Mhz,but this frequency is divided inside adc_reader by 2 to 2.08 Mhz,
291         --        while we use +3.3V Vcc     
292         adc_reader_map: adc_reader 
293         port map(
294                 clk => gpio_clk,
295                 divided_clk => clk_4M17,
296                 adc_reset => income_data_valid, --reset at each SPI cycle,TODO: replace with PLL reset
297                 adc_miso => adc_miso,
298                 adc_channels => adc_channels,
299                 adc_sclk => adc_sclk,
300                 adc_scs => adc_scs,
301                 adc_mosi => adc_mosi,
302                 measur_count => adc_m_count
303                 
304         );
305
306         dummy_unused <= gpio2 and gpio3 and
307                 gpio5 and gpio6 and
308                 gpio12 and gpio13 and gpio14 and
309                 gpio15 and gpio16 and gpio19 and
310                 gpio20 and gpio21 and gpio26 and
311                 stat(1) and stat(2) and stat(3) and
312                 hal_in(1) and hal_in(2) and hal_in(3) and
313                 irc_i and power_stat and 
314                 adc_miso and 
315                 rs485_rxd and
316                 can_rx and can_tx and
317                 dip_sw(1) and dip_sw(2) and dip_sw(3) and
318                 irc_a and irc_b and
319                 gpio17 and gpio18 and gpio27 and gpio22 and gpio23 and gpio24 and gpio25 and
320                 gpio8  and
321                 ext_scs1 and ext_scs2 and ext_miso and ext_mosi and ext_sclk and ext_scs0;
322                         
323         rs485_txd <= '1';
324         rs485_dir <= '0';
325
326         spi_clk <= gpio11;
327         spi_ce <= gpio7;
328         spi_mosi <= gpio10;
329         gpio9 <= spi_miso;
330
331         pwm(1) <= pwm_sig(1) and dip_sw(1);
332         pwm(2) <= pwm_sig(2) and dip_sw(2);
333         pwm(3) <= pwm_sig(3) and dip_sw(3);
334         
335                 
336         process
337         begin
338                 wait until (gpio_clk'event and gpio_clk='1');
339                 if irc_i_prev = '0' and irc_i = '1' then
340                         index_position(11 downto 0)<=position(11 downto 0);
341                 end if;
342                 irc_i_prev<=irc_i;
343         end process;
344         
345         process
346         begin
347                 wait until (gpio_clk'event and gpio_clk='1');
348                 IF pwm_count = std_logic_vector(unsigned(pwm_period) - 1) THEN                          
349                         --end of period nearly reached
350                         --fetch new pwm match data
351                         pwm_sync_at_next <= '1';
352                 else
353                         pwm_sync_at_next <= '0';
354                 end if;
355                 
356                 if pwm_sync_at_next='1' then
357                         --end of period reached
358                         pwm_count <= (others=>'0');      --reset counter
359                         pwm_sync <= '1';                                -- inform PWM logic about new period start
360                 ELSE                                                    --end of period not reached
361                         pwm_count <= std_logic_vector(unsigned(pwm_count)+1);           --increment counter
362                         pwm_sync <= '0';
363                 END IF;
364         end process;
365         
366         process
367         begin
368                 --position is obtained on rising edge -> we should write it on next cycle
369                 wait until (gpio_clk'event and gpio_clk='1');
370                 
371                 --SCLK edge detection
372                 spiclk_old(0)<=spi_clk;
373                 spiclk_old(1)<=spiclk_old(0);
374                 
375                 --SS edge detection
376                 ce0_old(0)<=spi_ce;
377                 ce0_old(1)<=ce0_old(0);
378                 
379                 if (spiclk_old="01") then --rising edge, faze cteni
380                         if (spi_ce = '0') then             -- SPI CS must be selected
381                                 -- shift serial data into dat_reg on each rising edge
382                                 -- of SCK, MSB first
383                                 dat_reg(127 downto 0) <= dat_reg(126 downto 0) & spi_mosi;
384                                 end if;
385                 elsif (spiclk_old="10" ) then --falling edge, faze zapisu
386                         if (spi_ce = '0') then
387                                 spi_miso <= dat_reg(127); --zapisujeme nejdriv MSB
388                         end if;
389                 end if;
390                 
391                         
392                 --sestupna hrana SS, pripravime data pro prenos
393                 if (ce0_old = "10" ) then 
394                         income_data_valid<='0';
395                         dat_reg(127 downto 96) <= position(31 downto 0); --pozice
396                         dat_reg(95 downto 93) <= hal_in(1 to 3); --halovy sondy
397                         dat_reg(92 downto 81) <= index_position(11 downto 0);   --position of irc_i
398                         dat_reg(80 downto 72) <=adc_m_count(8 downto 0);        --count of measurments
399                         --data order schould be: ch2 downto ch0 downto ch1
400                         dat_reg(71 downto 0) <= adc_channels(71 downto 0);      --current mesurments
401                         spi_miso <= position(31);               --prepare the first bit on SE activation
402                 elsif (ce0_old = "01") then --rising edge of SS, we should read the data
403                         pwm_en_p(1 to 3)<=dat_reg(126 downto 124);
404                         pwm_en_n(1 to 3)<=dat_reg(123 downto 121);
405                         --usable for up to 16-bit PWM duty cycle resolution (pwm_width):
406                         pwm_match(1)(pwm_width-1 downto 0)<=dat_reg(pwm_width+31 downto 32);
407                         pwm_match(2)(pwm_width-1 downto 0)<=dat_reg(pwm_width+15 downto 16);
408                         pwm_match(3)(pwm_width-1 downto 0)<=dat_reg(pwm_width-1 downto 0);
409                         income_data_valid<='1';
410                 end if;
411         end process;
412
413         clock_monitor: process (pll_clkout, gpio_clk, clkmon_dly1, clkmon_wdg, clkmon_fail_next)
414         begin
415                 if pll_clkout'event and pll_clkout = '1' then
416                         clkmon_dly1 <= gpio_clk;
417                         clkmon_dly2 <= clkmon_dly1;
418                         if clkmon_dly1 = '0' and clkmon_dly2 = '1' then
419                                 clkmon_wdg <= 6;
420                                 clkmon_fail_next <= '0';
421                         elsif clkmon_wdg > 0 then
422                                 clkmon_wdg <= clkmon_wdg - 1;
423                                 clkmon_fail_next <= '0';
424                         else
425                                 clkmon_wdg <= 0;
426                                 clkmon_fail_next <= '1';
427                         end if;
428                         clkmon_fail <= clkmon_fail_next;
429                 end if;
430         end process;
431
432         async_rst: process (gpio_clk, reset_async, reset_sync)
433         begin
434                 if reset_async = '1' then
435                         failsafe <= '1';
436                 elsif gpio_clk'event and gpio_clk = '1' then
437                         failsafe <= next_failsafe or reset_sync;
438                 end if;
439         end process;
440
441         sync_rst: process (gpio_clk, reset_async)
442         begin
443                 if gpio_clk'event and gpio_clk = '1' then
444                         reset_sync <= reset_async;
445                 end if;
446         end process;
447
448 end behavioral;
449