-- -- * LXPWR slave part * -- common sioreg & common counter for several ADC&PWM blocks -- -- part of LXPWR motion control board (c) PiKRON Ltd -- idea by Pavel Pisa PiKRON Ltd -- code by Marek Peca -- 01/2013 -- -- license: GNU GPLv3 -- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.util.all; entity rpi_mc_simple_dc is generic( pwm_width : natural:=11 ); port ( gpio2: in std_logic; -- SDA gpio3: in std_logic; -- SCL gpio4: in std_logic; -- CLK gpio14: in std_logic; -- Tx gpio15: in std_logic; -- Rx gpio17: in std_logic; -- RTS gpio18: in std_logic; -- PWM0/PCMCLK gpio27: in std_logic; -- SD1DAT3 gpio22: in std_logic; -- SD1CLK gpio23: in std_logic; -- SD1CMD gpio24: in std_logic; -- SD1DAT0 gpio10: in std_logic; -- SPI0MOSI gpio9: out std_logic; -- SPI0MISO gpio25: in std_logic; -- SD1DAT1 gpio11: in std_logic; -- SPI0SCLK gpio8: in std_logic; -- SPI0CE0 gpio7: in std_logic; -- SPI0CE1 gpio5: in std_logic; -- GPCLK1 gpio6: in std_logic; -- GPCLK2 gpio12: in std_logic; -- PWM0 gpio13: in std_logic; -- PWM1 gpio19: in std_logic; -- PWM1/SPI1MISO/PCMFS gpio16: in std_logic; -- SPI1CE2 gpio26: in std_logic; -- SD1DAT2 gpio20: in std_logic; -- SPI1MOSI/PCMDIN/GPCLK0 gpio21: in std_logic; -- SPI1SCLK/PCMDOUT/GPCLK1 -- -- PWM -- Each PWM signal has cooresponding shutdown pwm: out std_logic_vector (1 to 3); shdn: out std_logic_vector (1 to 3); -- Fault/power stage status stat: in std_logic_vector (1 to 3); -- HAL inputs hal_in: in std_logic_vector (1 to 3); -- IRC inputs irc_a: in std_logic; irc_b: in std_logic; irc_i: in std_logic; -- Power status power_stat: in std_logic; -- ADC for current adc_miso: in std_logic; adc_mosi: out std_logic; adc_sclk: out std_logic; adc_scs: out std_logic; -- Extarnal SPI ext_miso: in std_logic; --master in slave out ext_mosi: in std_logic; --master out slave in ext_sclk: in std_logic; ext_scs0: in std_logic; ext_scs1: in std_logic; ext_scs2: in std_logic; -- RS-485 Transceiver rs485_rxd: in std_logic; rs485_txd: out std_logic; rs485_dir: out std_logic; -- CAN Transceiver can_rx: in std_logic; can_tx: in std_logic; -- DIP switch dip_sw: in std_logic_vector (1 to 3); --na desce je prohozene cislovanni -- Unused terminal to keep design tools silent dummy_unused : out std_logic ); end rpi_mc_simple_dc; architecture behavioral of rpi_mc_simple_dc is attribute syn_noprune :boolean; attribute syn_preserve :boolean; attribute syn_keep :boolean; attribute syn_hier :boolean; -- Actel lib -- component pll50to200 -- port ( -- powerdown, clka: in std_logic; -- lock, gla: out std_logic -- ); -- end component; component CLKINT port (A: in std_logic; Y: out std_logic); end component; component qcounter port ( clock: in std_logic; reset: in std_logic; a0, b0: in std_logic; qcount: out std_logic_vector (31 downto 0); a_rise, a_fall, b_rise, b_fall, ab_event: out std_logic; ab_error: out std_logic ); end component; component mcpwm is generic ( pwm_width: natural ); port ( clock: in std_logic; sync: in std_logic; --flag that counter "restarts-overflows" data_valid:in std_logic; --indicates data is consistent failsafe: in std_logic; --turn off both transistors en_p, en_n: in std_logic; --enable positive & enable shutdown match: in std_logic_vector (pwm_width-1 downto 0); --posion of counter when we swap output logic count: in std_logic_vector (pwm_width-1 downto 0); --we use an external counter out_p, out_n: out std_logic --pwm outputs: positive & shutdown --TODO add the rest of pwm signals, swap match to pwm_word ); end component; component div8 is port ( --reset: in std_logic; clk_in: in std_logic; clk_out: out std_logic ); end component; type state_type is (f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,f15,r15,reset,rst_wait); signal state : state_type; type channel_type is (ch0, ch1, ch2); signal adc_data: std_logic_vector(11 downto 0); --ADC income data signal adc_reset : std_logic; signal adc_rst_old : std_logic_vector(1 downto 0); signal adc_address: std_logic_vector(8 downto 0); signal adc_channels: std_logic_vector(35 downto 0); signal spiclk_old: std_logic_vector(1 downto 0); --pro detekci hrany SPI hodin --signal pwm_in, pwm_dir_in: std_logic; signal gpio_clk: std_logic; signal dat_reg : STD_LOGIC_VECTOR (95 downto 0); --shift register for spi signal position: std_logic_vector(31 downto 0); --pozice z qcounteru signal ce0_old: std_logic_vector(1 downto 0); --pwm signals constant pwm_n: natural := 3; --number of pwm outputs --number of ticks per pwm cycle, 2^11=2048 constant pwm_period : std_logic_vector (pwm_width-1 downto 0) := (others=>'1'); type pwm_res_type is array(1 to 3) of std_logic_vector (pwm_width-1 downto 0); signal pwm_match: pwm_res_type; --point of reversion of pwm output, 0 to 2047 signal pwm_count: std_logic_vector (pwm_width-1 downto 0); --counter, 0 to 2047 signal pwm_sync: std_logic; signal pwm_en_p: std_logic_vector(1 to 3); signal pwm_en_n: std_logic_vector(1 to 3); signal income_data_valid: std_logic; signal clk_3M1: std_logic; -- attribute syn_noprune of gpio2 : signal is true; -- attribute syn_preserve of gpio2 : signal is true; -- attribute syn_keep of gpio2 : signal is true; -- attribute syn_hier of gpio2 : signal is true; begin -- PLL as a reset generator --zesileni signalu GPIO CLK copyclk2: CLKINT port map ( a => gpio4, y => gpio_clk ); qcount: qcounter port map ( clock => gpio_clk, reset => '0', a0 => irc_a, b0 => irc_b, qcount => position, a_rise => open, a_fall => open, b_rise => open, b_fall => open, ab_event => open, ab_error => open ); pwm_block: for i in pwm_n downto 1 generate pwm_map: mcpwm generic map ( pwm_width => pwm_width ) port map ( clock => gpio_clk, --50 Mhz clk from gpclk on raspberry sync => pwm_sync, --counter restarts data_valid => income_data_valid, failsafe => '0', -- -- pwm config bits & match word -- en_n => pwm_en_n(i), --enable positive pwm en_p => pwm_en_p(i), --enable "negative" ->activate shutdown match => pwm_match(i), count => pwm_count, -- outputs out_p => pwm(i), --positive signal out_n => shdn(i) --reverse signal is in shutdown mode ); end generate; div8_map: div8 port map( --reset => income_data_valid, clk_in => gpio_clk, clk_out => clk_3M1 ); -- pll: pll50to200 -- port map ( -- powerdown => '1', -- clka => pll_clkin, -- gla => pll_clkout, -- lock => pll_lock); -- -- reset <= not pll_lock; -- reset <= '0'; -- TODO: apply reset for good failsafe -- upon power-on -- clock <= clkm; dummy_unused <= gpio2 and gpio3 and gpio4 and gpio5 and gpio6 and gpio12 and gpio13 and gpio14 and gpio15 and gpio16 and gpio19 and gpio20 and gpio21 and gpio26 and stat(1) and stat(2) and stat(3) and hal_in(1) and hal_in(2) and hal_in(3) and irc_i and power_stat and adc_miso and rs485_rxd and can_rx and can_tx and dip_sw(1) and dip_sw(2) and dip_sw(3) and irc_a and irc_b and gpio17 and gpio18 and gpio27 and gpio22 and gpio23 and gpio24 and gpio25 and gpio8 and gpio11 and gpio7 and gpio10 and ext_scs1 and ext_scs2 and ext_miso and ext_mosi and ext_sclk and ext_scs0; rs485_txd <= '1'; rs485_dir <= '0'; --shdn(1) <= '0'; --shdn(2) <= '0'; --shdn(3) <= '1'; --pwm(1) <= '0'; --pwm(2) <= '0'; --pwm(3) <= '0'; process begin wait until (gpio_clk'event and gpio_clk='1'); IF(pwm_count = pwm_period) THEN --end of period reached pwm_count <= (others=>'0'); --reset counter pwm_sync <= '1'; -- inform PWM logic about new period start ELSE --end of period not reached pwm_count <= std_logic_vector(unsigned(pwm_count)+1); --increment counter pwm_sync <= '0'; END IF; end process; process begin --position is obtained on rising edge -> we should write it on next cycle wait until (gpio_clk'event and gpio_clk='1'); --SCLK edge detection spiclk_old(0)<=gpio11; spiclk_old(1)<=spiclk_old(0); --SS edge detection ce0_old(0)<=gpio7; ce0_old(1)<=ce0_old(0); if (spiclk_old="01") then --rising edge, faze cteni if (gpio7 = '0') then -- SPI CS must be selected -- shift serial data into dat_reg on each rising edge -- of SCK, MSB first dat_reg(95 downto 0) <= dat_reg(94 downto 0) & gpio10; end if; elsif (spiclk_old="10" ) then --falling edge, faze zapisu if (gpio7 = '0') then gpio9 <= dat_reg(95); --zapisujeme nejdriv MSB end if; end if; --sestupna hrana SS, pripravime data pro prenos if (ce0_old = "10" ) then income_data_valid<='0'; dat_reg(95 downto 64) <= position(31 downto 0); --pozice dat_reg(63 downto 61) <= hal_in(1 to 3); --halovy sondy dat_reg(60 downto 36) <= (others => '1'); --let the rest fill with ones dat_reg(35 downto 0) <= adc_channels(35 downto 0); --current mesurments adc_reset<='0'; --remove reset flag, and wait on its rising edge elsif (ce0_old = "01") then --rising edge of SS, we should read the data adc_reset<=dat_reg(95); pwm_en_p(1 to 3)<=dat_reg(94 downto 92); pwm_en_n(1 to 3)<=dat_reg(91 downto 89); --11 bit pwm TODO: make it generic pwm_match(1)(pwm_width-1 downto 0)<=dat_reg(34 downto 24); pwm_match(2)(pwm_width-1 downto 0)<=dat_reg(22 downto 12); pwm_match(3)(pwm_width-1 downto 0)<=dat_reg(10 downto 0); income_data_valid<='1'; end if; end process; process variable data_ready : std_logic; variable channel: channel_type; variable reset_re: std_logic:='0'; variable reset_count: integer:=0; begin wait until (clk_3M1'event and clk_3M1='1'); --reset rising edge detection adc_rst_old(0)<=adc_reset; adc_rst_old(1)<=adc_rst_old(0); if (adc_rst_old="01") then reset_re:='1'; end if; case state is when reset=> reset_re:='0'; --clear reset flag adc_scs<='1'; --active-low SS adc_sclk<='0'; --lower clock data_ready:='0'; --addresse are CH(A2,A1,A0): CH0:(0,0,1),CH1:(1,0,1),CH2:(0,1,0) adc_address<="001101010"; channel:=ch0; adc_channels(35 downto 0)<=(others=>'1'); --for debug only - remove this line! adc_data(11 downto 0)<=(others=>'1'); reset_count:=0; state<=rst_wait; when rst_wait=> if (reset_count<10) then reset_count:=reset_count+1; if (reset_count=7) then adc_scs<='0'; --give the adc some time to prepare before trensfer end if; else state<=f1; end if; when f1=> --1st 'fallin edge' - its not falling edge in any case-if rst clock is low before adc_sclk<='0'; --clk adc_mosi<='1'; --start bit state<=r1; --next state when r1=> --1st rising edge (adc gets the start bit, we get date..) adc_sclk<='1'; adc_data(5)<=adc_miso; state<=f2; when f2=> --2nd falling edge adc_sclk<='0'; adc_mosi<=adc_address(8); --A2 address state<=r2; when r2=> --2nd rising edge (adc gets A2 address) adc_sclk<='1'; adc_data(4)<=adc_miso; state<=f3; when f3=> --3rd falling edge adc_sclk<='0'; adc_mosi<=adc_address(7); --A1 address state<=r3; when r3=> --rising edge adc_sclk<='1'; adc_data(3)<=adc_miso; state<=f4; when f4=> --4th falling edge adc_sclk<='0'; adc_mosi<=adc_address(6); --A0 address --shift the addresses adc_address(8 downto 0)<=adc_address(5 downto 0) & adc_address(8 downto 6); state<=r4; when r4=> --rising edge adc_sclk<='1'; adc_data(2)<=adc_miso; state<=f5; when f5=> --5th falling edge adc_sclk<='0'; adc_mosi<='0'; --MODE (LOW -12bit) state<=r5; when r5=> --rising edge adc_sclk<='1'; adc_data(1)<=adc_miso; state<=f6; when f6=> --6th falling edge adc_sclk<='0'; adc_mosi<='1'; --SGL/DIF (HIGH - SGL=Single Ended) state<=r6; when r6=> --6th rising edge (we read last bit of conversion, adc gets SGL/DIF) adc_sclk<='1'; adc_data(0)<=adc_miso; state<=f7; when f7=> -- 7th falling edge adc_sclk<='0'; adc_mosi<='0'; --PD1 (power down - PD1=PD0=0 -> power down between conversion) state<=r7; when r7=> --7th rising edge, data ready adc_sclk<='1'; if (data_ready='1') then case channel is when ch0=> adc_channels(35 downto 24)<=adc_data(11 downto 0); --adc_channels(35 downto 24)<=(others=>'0'); channel:=ch1; when ch1=> adc_channels(23 downto 12)<=adc_data(11 downto 0); --adc_channels(23 downto 12)<=(others=>'1'); channel:=ch2; when ch2=> adc_channels(11 downto 0)<=adc_data(11 downto 0); --adc_channels(11 downto 0)<=(others=>'0'); channel:=ch0; end case; end if; data_ready:='1'; state<=f8; when f8=> --8th falling edge adc_sclk<='0'; adc_mosi<='0'; --PD0 state<=r8; when r8=> --8th rising edge (adc gets PD0) adc_sclk<='1'; state<=f9; when f9=> --9th falling edge busy state between conversion (we write nothing) adc_sclk<='0'; state<=r9; when r9=> --9th rising edge (we nor ads get nothing) adc_sclk<='1'; state<=f10; when f10=> --10th falling edge adc_sclk<='0'; state<=r10; when r10=> --10th rising edge (we read 1. bit of conversion) adc_sclk<='1'; adc_data(11)<=adc_miso; state<=f11; when f11=> adc_sclk<='0'; state<=r11; when r11=> --11th rising edge adc_sclk<='1'; adc_data(10)<=adc_miso; state<=f12; when f12=> adc_sclk<='0'; state<=r12; when r12=> --12th rising edge adc_sclk<='1'; adc_data(9)<=adc_miso; state<=f13; when f13=> adc_sclk<='0'; state<=r13; when r13=> --13th rising edge adc_sclk<='1'; adc_data(8)<=adc_miso; state<=f14; when f14=> adc_sclk<='0'; state<=r14; when r14=> --14th rising edge adc_sclk<='1'; adc_data(7)<=adc_miso; state<=f15; when f15=> adc_sclk<='0'; --for rising edge detection in next cycle state<=r15; when r15=> --15th rising edge adc_sclk<='1'; adc_data(6)<=adc_miso; if (reset_re='1') then --we check rising edge of reset state<=reset; else state<=f1; end if; end case; end process; end behavioral;