UNIVERSITÀ DEGLI STUDI DI CATANIA FACOLTÀ DI INGEGNERIA CORSO DI LAUREA SPECIALISTICA IN INGEGNERIA DELL’AUTOMAZIONE E DEL CONTROLLO DEI SISTEMI COMPLESSI Dipartimento di Ingegneria Elettrica, Elettronica e dei Sistemi
L’Episcopo Gaetano Mangano Claudia Nicolosi Giuseppe Sansone Giuseppe
Implementazione di un integratore di ordine non intero su FPGA
Tesina di Controllo dei Processi
Docenti : Prof. Antonio Gallo Prof. Ing. Riccardo Caponetto Ing. Giovanni Dongola
Anno Accademico 2007-2008
Integrale di ordine frazionale La definizione più comune di integrale di ordine frazionale positivo q è stata introdotta da Riemann-Liouville: q −1 d − q f (t ) 1 t ( ) = t − τ f (τ )dτ dt − q Γ(q ) ∫0
(1.1)
dove Г(.) indica la funzione Gamma di Eulero definita per valori reali positivi della quantità n-α: ∞
Γ(n −α) = ∫ x n−α−1e −x dx
(1.2)
0
Metodo numerico di approssimazione per operatori di ordine frazionale La risoluzione analitica dell’integrale di ordine non intero q definito nella relazione (1.1) non è sempre immediata, quindi si ricorre spesso all’utilizzo di metodi numerici di semplice implementazione. Al fine dunque di poter raggiungere un’ottima approssimazione, si può dividere l’intervallo [0,t] dell’integrale q −1 t d −q f (t ) 1 = ( t − τ ) f (τ ) dτ dt −q Γ( q ) ∫0
in k sottointervalli e considerare i campioni della funzione f(t), per semplicità, nel modo seguente :
f0 ≡ f ( 0) t f1≡ f k .............. jt fj ≡ f k ............... fk ≡ f ( t )
L’integrale esteso all’intero intervallo [0,t] può essere così decomposto nella somma di k integrali secondo la relazione : d −q f (t ) 1 k −1 [ jt +t ] / k = t −τ ∑ Γ( q ) j =0 ∫jt / k dt −q
q −1
f (τ ) dτ
(1.3)
Assumendo la funzione f costante all’interno di ogni sottointervallo (ad esempio uguale alla media dei due valori che la f(t) assume agli estremi di integrazione) si può portare fuori dall’integrale (1.3) jt ( j + 1) t f + f q −1 k k [ jt +1] / k ( ) ( ) t − τ f ( τ ) d τ ≈ t − τ dτ = ∫jt / k ∫jt / k 2 q q f j + f j +1 jt ( j + 1) t = − t− t− 2q k k [ jt +1] / k
q −1
ottenendo l’integrale k-esimo di ordine non intero espresso nella forma:
[
d −q fk T q k −1 f j + f j +1 ( k − j ) q − ( k − j − 1) q = ∑ −q dt Γ( q + 1) j = 0 2
dove T indica il tempo di campionamento t/k.
]
(1.4)
Implementazione software Per la realizzazione del modello che implementa la formula di integrazione di ordine non intero (1.4), è stato utilizzato il Project Navigator 8.2 della Xilinx. Come primo passo è stata definita la codifica da utilizzare: è stata scelta una codifica a 32 bit fixed point, in cui sono stati utilizzati 12 bit (compreso il segno) per la parte intera e 20 bit per la parte decimale. Per testare la veridicità dei risultati, sono stati implementati 2 script MatLab atti alla conversione binario/decimale e viceversa, che riportiamo di seguito. BINARIO/DECIMALE function n_decimale=bin2dec32(n_binario) n_decimale=0; if n_binario(1)==0 for k=1:1:32 n_decimale=n_decimale+(n_binario(k)*(2^(12-k))); end else n_decimale=-2^11; for k=2:1:32 n_decimale=n_decimale+(n_binario(k)*(2^(12-k))); end end
DECIMALE/BINARIO function n_binario=dec2bin32(n_decimale) n_int=fix(abs(n_decimale)); %parte intera n_dec=abs(n_decimale)-n_int; %parte decimale risultato_int=n_int; risultato_dec=n_dec; for k=1:1:12 vettore(13-k)=rem(risultato_int,2); risultato_int=fix(risultato_int/2); end for k=13:1:32 risultato_dec=risultato_dec*2;
vettore(k)=fix(risultato_dec); risultato_dec=risultato_dec-fix(risultato_dec); end i=0; if n_decimale<0 for k=32:-1:1 if vettore(k)==1 && i==0 i=i+1.1; elseif i>1 vettore(k)= ~vettore(k); end end end n_binario=' '; for k=1:1:32 n_binario=strcat(n_binario,num2str(vettore(k))); end
Come secondo passo è stata progettata la macchina a stati, definendone gli stati di lavoro e le condizioni che permettono le transizioni tra i vari stati. Quanto detto è spiegato dal seguente schema:
1
S1
S0
0 0
1
1
S2 0 in cui con lo stato S0 indichiamo la fase di reset, con lo stato S1 la fase di read e con lo stato S2 la fase di calcolo. Oltre alla macchina a stati, sono stati implementati:
1 memoria per il contenimento dei campioni; 1 sommatore; 1 sottrattore; 6 blocchi per il controllo del segno; 3 moltiplicatori; 2 porte XOR; 1 porta AND; 1 accumulatore; 12 flip flop di tipo D per la sincronizzazione. Nel complesso, gli ingressi da fornire al circuito sono il clock, l'ingresso, il valore della costante e il numero di campioni da utilizzare. Questi ultimi due devono mantenersi costanti durante tutto il periodo della simulazione. Inoltre, ogni ingresso, durante la fase di accumulazione dei dati, deve mantenersi costante per un intero periodo di clock. In uscita ritroveremo il valore dell'integrale. Riportiamo di seguito i sorgenti dei blocchi utilizzati per la realizzazione della (1.4).
MACCHINA A STATI
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity macchina_stati is Port ( clk : in
STD_LOGIC;
control : in
STD_LOGIC;
num_campioni: in STD_LOGIC_VECTOR (4 downto 0); -- max 32 addrA_sampleM : out
STD_LOGIC_VECTOR (4 downto 0);
addrB_sampleM : out
STD_LOGIC_VECTOR (4 downto 0);
enA_sampleM : out
STD_LOGIC;
enB_sampleM : out
STD_LOGIC;
wenA_sampleM : out
STD_LOGIC;
guadagno : out
STD_LOGIC_VECTOR (31 downto 0);
count_dec : out
STD_LOGIC_VECTOR (4 downto 0);
count_dec_dec : out
STD_LOGIC_VECTOR (4 downto 0);
enable_coeff : out STD_LOGIC; clk_en_acc : out STD_LOGIC; reset_acc : out STD_LOGIC); end macchina_stati; architecture Behavioral of macchina_stati is type STATE_TYPE is (RESET,LETTURA,CALCOLO); signal CURRENT_STATE, NEXT_STATE: STATE_TYPE; begin COMBIN: process(CURRENT_STATE,control) begin case CURRENT_STATE is when RESET => if control='0' then NEXT_STATE <= LETTURA; else NEXT_STATE <= CALCOLO; end if; guadagno <= "00000000000000000000000000000000"; enA_sampleM <= '0'; enB_sampleM <= '0'; wenA_sampleM <= '0'; enable_coeff <= '0'; clk_en_acc <= '0'; reset_acc <= '1'; when LETTURA => if control='0' then NEXT_STATE <= LETTURA; else NEXT_STATE <= RESET; end if; guadagno <= "00000000000000000000000000000000"; enA_sampleM <= '1'; enB_sampleM <= '0'; wenA_sampleM <= '1'; enable_coeff <= '0'; clk_en_acc <= '0'; reset_acc <= '1';
when CALCOLO => if control='0' then NEXT_STATE <= CALCOLO; else NEXT_STATE <= RESET; end if; guadagno <= "00000000000010000000000000000000"; enA_sampleM <= '1'; enB_sampleM <= '1'; wenA_sampleM <= '0'; enable_coeff <= '1'; clk_en_acc <= '1'; reset_acc <= '0'; end case; end process; SYNCH: process(clk) variable A: STD_LOGIC_VECTOR (4 downto 0); variable B: STD_LOGIC_VECTOR (4 downto 0); variable C: STD_LOGIC_VECTOR (4 downto 0); variable D: STD_LOGIC_VECTOR (4 downto 0); variable E: STD_LOGIC_VECTOR (4 downto 0); variable F: STD_LOGIC_VECTOR (4 downto 0); begin E := num_campioni; F := num_campioni-1; if clk'event and clk = '1' then CURRENT_STATE <= NEXT_STATE; case CURRENT_STATE is when RESET => A := "00000"; B := "00001"; C := E; D := F; when LETTURA => A := A + 1 ;
B := "00000"; C := "00000"; D := "00000";
WHEN CALCOLO => A := A + 1 ; B := B + 1 ; C := C - 1 ; D := D - 1 ; end case; addrA_sampleM <= A ; addrB_sampleM <= B ; count_dec <= C ; count_dec_dec <= D; end if; end process; end Behavioral;
MEMORIA CAMPIONI
LIBRARY ieee; USE ieee.std_logic_1164.ALL; -- synopsys translate_off Library XilinxCoreLib; -- synopsys translate_on ENTITY memoria_campioni IS port ( addra: IN std_logic_VECTOR(4 downto 0); addrb: IN std_logic_VECTOR(4 downto 0); clka: IN std_logic; clkb: IN std_logic; dina: IN std_logic_VECTOR(31 downto 0); douta: OUT std_logic_VECTOR(31 downto 0); doutb: OUT std_logic_VECTOR(31 downto 0); ena: IN std_logic; enb: IN std_logic; wea: IN std_logic); END memoria_campioni; ARCHITECTURE memoria_campioni_a OF memoria_campioni IS
-- synopsys translate_off component wrapped_memoria_campioni port ( addra: IN std_logic_VECTOR(4 downto 0); addrb: IN std_logic_VECTOR(4 downto 0); clka: IN std_logic; clkb: IN std_logic; dina: IN std_logic_VECTOR(31 downto 0); douta: OUT std_logic_VECTOR(31 downto 0); doutb: OUT std_logic_VECTOR(31 downto 0); ena: IN std_logic; enb: IN std_logic; wea: IN std_logic); end component; -- Configuration specification for all : wrapped_memoria_campioni use entity XilinxCoreLib.blkmemdp_v6_3(behavioral) generic map( c_reg_inputsb => 0, c_reg_inputsa => 0, c_has_ndb => 0, c_has_nda => 0, c_ytop_addr => "1024", c_has_rfdb => 0, c_has_rfda => 0, c_ywea_is_high => 1, c_yena_is_high => 1, c_yclka_is_rising => 1, c_yhierarchy => "hierarchy1", c_ysinita_is_high => 1, c_ybottom_addr => "0", c_width_b => 32, c_width_a => 32, c_sinita_value => "0", c_sinitb_value => "0", c_limit_data_pitch => 18, c_write_modeb => 2, c_write_modea => 2, c_has_rdyb => 0, c_yuse_single_primitive => 0, c_has_rdya => 0, c_addra_width => 5, c_addrb_width => 5,
c_has_limit_data_pitch => 0, c_default_data => "0", c_pipe_stages_b => 0, c_yweb_is_high => 1, c_yenb_is_high => 1, c_pipe_stages_a => 0, c_yclkb_is_rising => 1, c_yydisable_warnings => 1, c_enable_rlocs => 0, c_ysinitb_is_high => 1, c_has_web => 0, c_has_default_data => 1, c_has_sinitb => 0, c_has_wea => 1, c_has_sinita => 0, c_has_dinb => 0, c_has_dina => 1, c_ymake_bmm => 0, c_sim_collision_check => "NONE", c_has_enb => 1, c_has_ena => 1, c_depth_b => 32, c_mem_init_file => "mif_file_16_1", c_depth_a => 32, c_has_doutb => 1, c_has_douta => 1, c_yprimitive_type => "16kx1"); -- synopsys translate_on BEGIN -- synopsys translate_off U0 : wrapped_memoria_campioni port map ( addra => addra, addrb => addrb, clka => clka, clkb => clkb, dina => dina, douta => douta, doutb => doutb, ena => ena, enb => enb, wea => wea);
-- synopsys translate_on END memoria_campioni_a;
SOMMATORE
LIBRARY ieee; USE ieee.std_logic_1164.ALL; -- synopsys translate_off Library XilinxCoreLib; -- synopsys translate_on ENTITY sommatore IS port ( A: IN std_logic_VECTOR(31 downto 0); B: IN std_logic_VECTOR(31 downto 0); Q: OUT std_logic_VECTOR(31 downto 0); CLK: IN std_logic); END sommatore; ARCHITECTURE sommatore_a OF sommatore IS -- synopsys translate_off component wrapped_sommatore port ( A: IN std_logic_VECTOR(31 downto 0); B: IN std_logic_VECTOR(31 downto 0); Q: OUT std_logic_VECTOR(31 downto 0); CLK: IN std_logic); end component; -- Configuration specification for all : wrapped_sommatore use entity XilinxCoreLib.C_ADDSUB_V7_0(behavioral) generic map( c_has_bypass_with_cin => 0, c_a_type => 0, c_has_sclr => 0, c_sync_priority => 1,
c_has_aset => 0, c_has_b_out => 0, c_has_s => 0, c_has_q => 1, c_bypass_enable => 0, c_b_constant => 0, c_has_ovfl => 0, c_high_bit => 31, c_latency => 1, c_sinit_val => "0", c_has_bypass => 0, c_pipe_stages => 1, c_has_sset => 0, c_has_ainit => 0, c_has_a_signed => 0, c_has_q_c_out => 0, c_b_type => 0, c_has_add => 0, c_has_sinit => 0, c_has_b_in => 0, c_has_b_signed => 0, c_bypass_low => 0, c_enable_rlocs => 1, c_b_value => "0", c_add_mode => 0, c_has_aclr => 0, c_out_width => 32, c_ainit_val => "0000", c_low_bit => 0, c_has_q_ovfl => 0, c_has_q_b_out => 0, c_has_c_out => 0, c_b_width => 32, c_a_width => 32, c_sync_enable => 0, c_has_ce => 0, c_has_c_in => 0); -- synopsys translate_on BEGIN -- synopsys translate_off U0 : wrapped_sommatore port map (
A => A, B => B, Q => Q, CLK => CLK); -- synopsys translate_on END sommatore_a;
SOTTRATTORE
LIBRARY ieee; USE ieee.std_logic_1164.ALL; -- synopsys translate_off Library XilinxCoreLib; -- synopsys translate_on ENTITY sottrattore IS port ( A: IN std_logic_VECTOR(31 downto 0); B: IN std_logic_VECTOR(31 downto 0); Q: OUT std_logic_VECTOR(31 downto 0); CLK: IN std_logic); END sottrattore; ARCHITECTURE sottrattore_a OF sottrattore IS -- synopsys translate_off component wrapped_sottrattore port ( A: IN std_logic_VECTOR(31 downto 0); B: IN std_logic_VECTOR(31 downto 0); Q: OUT std_logic_VECTOR(31 downto 0); CLK: IN std_logic); end component; -- Configuration specification for all : wrapped_sottrattore use entity XilinxCoreLib.C_ADDSUB_V7_0(behavioral)
generic map( c_has_bypass_with_cin => 0, c_a_type => 0, c_has_sclr => 0, c_sync_priority => 1, c_has_aset => 0, c_has_b_out => 0, c_has_s => 0, c_has_q => 1, c_bypass_enable => 0, c_b_constant => 0, c_has_ovfl => 0, c_high_bit => 31, c_latency => 1, c_sinit_val => "0", c_has_bypass => 0, c_pipe_stages => 1, c_has_sset => 0, c_has_ainit => 0, c_has_a_signed => 0, c_has_q_c_out => 0, c_b_type => 0, c_has_add => 0, c_has_sinit => 0, c_has_b_in => 0, c_has_b_signed => 0, c_bypass_low => 0, c_enable_rlocs => 1, c_b_value => "0", c_add_mode => 1, c_has_aclr => 0, c_out_width => 32, c_ainit_val => "0000", c_low_bit => 0, c_has_q_ovfl => 0, c_has_q_b_out => 0, c_has_c_out => 0, c_b_width => 32, c_a_width => 32, c_sync_enable => 0, c_has_ce => 0, c_has_c_in => 0);
-- synopsys translate_on BEGIN -- synopsys translate_off U0 : wrapped_sottrattore port map ( A => A, B => B, Q => Q, CLK => CLK); -- synopsys translate_on END sottrattore_a;
CONTROLLO DEL SEGNO (INGRESSO) library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity controllo_segno is Port ( INGRESSO : in SEGNO : out USCITA : out
STD_LOGIC_VECTOR (31 downto 0); STD_LOGIC; STD_LOGIC_VECTOR (31 downto 0));
end controllo_segno; architecture Behavioral of controllo_segno is begin process (INGRESSO) variable indice: std_logic; begin indice := '0'; if INGRESSO(31)='1' then for i in 0 to 31 loop if INGRESSO(i) = '1' and indice = '0' then indice := '1'; USCITA(i) <= INGRESSO(i); elsif indice = '0' then USCITA(i) <= INGRESSO(i); else USCITA(i) <= not INGRESSO(i);
end if; end loop; else USCITA <= INGRESSO; end if; SEGNO <=INGRESSO(31); end process; end Behavioral;
CONTROLLO SEGNO (USCITA) library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity controllo_segno_uscita is Port ( INGRESSO : in
STD_LOGIC_VECTOR (31 downto 0);
CONTROLLO : in USCITA : out
STD_LOGIC; STD_LOGIC_VECTOR (31 downto 0));
end controllo_segno_uscita; architecture Behavioral of controllo_segno_uscita is begin process (INGRESSO) variable indice: std_logic; begin indice := '0'; if CONTROLLO = '1' then for i in 0 to 31 loop if INGRESSO(i) = '1' and indice = '0' then indice := '1'; USCITA(i) <= INGRESSO(i); elsif indice = '0' then USCITA(i) <= INGRESSO(i); else USCITA(i) <= not INGRESSO(i); end if; end loop; else USCITA <= INGRESSO; end if;
end process; end Behavioral;
MOLTIPLICATORE library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity moltiplicazione is Port ( CLK : in
STD_LOGIC;
INA : in
STD_LOGIC_VECTOR (31 downto 0);
INB : in
STD_LOGIC_VECTOR (31 downto 0);
USCITA : out
STD_LOGIC_VECTOR (31 downto 0));
end moltiplicazione; architecture Behavioral of moltiplicazione is begin calcolo: process (clk) variable ris: std_logic_vector (63 downto 0); begin if CLK='1' and CLK'event then ris := INA*INB; USCITA <= ris(51 downto 20); end if; end process; end Behavioral;
PORTA XOR library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity porta_xor is Port ( A : in
STD_LOGIC;
B : in
STD_LOGIC;
C : out
STD_LOGIC);
end porta_xor; architecture Behavioral of porta_xor is
begin process(A,B) begin C <= A xor B; end process; end Behavioral;
PORTA AND library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity port_and is Port ( A : in
STD_LOGIC_VECTOR (31 downto 0);
B : in
STD_LOGIC;
USCITA : out
STD_LOGIC_VECTOR (31 downto 0));
end port_and; architecture Behavioral of port_and is begin process (A,B) begin for i in 0 to 31 loop USCITA(i) <= A(i) and B; end loop; end process; end Behavioral;
ACCUMULATORE
LIBRARY ieee; USE ieee.std_logic_1164.ALL; -- synopsys translate_off Library XilinxCoreLib; -- synopsys translate_on ENTITY accumulatore IS port ( B: IN std_logic_VECTOR(31 downto 0); Q: OUT std_logic_VECTOR(31 downto 0); CLK: IN std_logic; CE: IN std_logic; SCLR: IN std_logic); END accumulatore; ARCHITECTURE accumulatore_a OF accumulatore IS -- synopsys translate_off component wrapped_accumulatore port ( B: IN std_logic_VECTOR(31 downto 0); Q: OUT std_logic_VECTOR(31 downto 0); CLK: IN std_logic; CE: IN std_logic; SCLR: IN std_logic); end component; -- Configuration specification for all : wrapped_accumulatore use entity XilinxCoreLib.C_ACCUM_V7_0(behavioral) generic map( c_has_bypass_with_cin => 0, c_has_sclr => 1, c_sync_priority => 1, c_has_b_out => 0, c_has_aset => 0, c_has_s => 0, c_bypass_enable => 1, c_b_constant => 0, c_has_ovfl => 0, c_high_bit => 31, c_sinit_val => "0", c_has_bypass => 0, c_pipe_stages => 0,
c_has_sset => 0, c_has_ainit => 0, c_has_q_c_out => 0, c_b_type => 0, c_has_add => 0, c_has_sinit => 0, c_has_b_in => 0, c_has_b_signed => 0, c_bypass_low => 0, c_enable_rlocs => 1, c_b_value => "0000", c_add_mode => 0, c_has_aclr => 0, c_out_width => 32, c_ainit_val => "0000", c_saturate => 0, c_low_bit => 0, c_has_q_ovfl => 0, c_has_q_b_out => 0, c_has_c_out => 0, c_b_width => 32, c_scale => 0, c_sync_enable => 0, c_has_ce => 1, c_has_c_in => 0); -- synopsys translate_on BEGIN -- synopsys translate_off U0 : wrapped_accumulatore port map ( B => B, Q => Q, CLK => CLK, CE => CE, SCLR => SCLR); -- synopsys translate_on END accumulatore_a;
FLIP FLOP D library ieee; use ieee.std_logic_1164.all; entity flipflopD is port( D,clk:in std_logic; Q:out std_logic); end flipflopD; architecture flipflopD1 of flipflopD is begin process(clk) begin if(clk'event and clk='1') then Q <= D; end if; end process; end flipflopD1;
Presentiamo adesso lo schema totale che racchiude tutti i blocchi implementati e le varie interconnessioni tra essi. INTEGRATORE GENERALE
MACCHINA A STATI + MEMORIA
MACCHINA A STATI + MEMORIA + BLOCCHI CALCOLO COEFF.
BLOCCO DI MOLTIPLICAZIONE TRA COEFFICIENTI E MEDIA DI 2 CAMPIONI CONSECUTIVI
BLOCCO DI CALCOLO + ACCUMULATORE
Come ultimo passo, è stato testato l'intero circuito fornendogli una serie di campioni di una sinusoide che vediamo nella seguente figura. In particolare sono stati prelevati 32 campioni per un periodo di sinusoide, con i quali è stato calcolato l'integrale di ordine uno, applicando la formula (1.4) supponendo T=1. Il risultato che ci si aspetta è una cosinusoide negativa, ed è ciò che abbiamo provato con tale simulazione. Nelle prime immagini, vediamo il risultato fornito dal simulatore; l'ultimo grafico è il plot dei campioni pervenuti dalla simulazione.