FPGA et Chaine de Developpement

FPGA et Chaine de Developpement

Carte Icebreaker avec FPGA LATTICE ICE40UP5k

fpga_1.svg

DOCUMENTATION

https://github.com/icebreaker-fpga


Installations ( sur vos PCs persos )

Les outils logiciels utilisés sont open source.
L’open source dans la synthèse logique est récente, le site de Fabien Marteau propose un tour d’horizon complet des possibilités logicielles et matérielles.

Installation de ghdl, yosys, …

Plusieurs logiciels doivent être installés ( yosys, nextpnr, iceprog ), cela peut se faire individuellement, mais il est plus pratique d’installer l’ensemble de ces outils avec oss-cad-suite :

Pour Linux :

  • extraire l’archive et mettre à jour la variable d’environnement $PATH ( ligne à ajouter dans le fichier ~/.bashrc ) :
export PATH="<extracted_location>/oss-cad-suite/bin:$PATH"

Créer le fichier :

sudo touch /etc/udev/rules.d/53-lattice-ftdi.rules 

et écrire dans ce fichier ( afin de donner les droits USB ) :

sudo echo "ACTION==\"add\", ATTR{idVendor}==\"0403\", ATTR{idProduct}==\"6010\", MODE:=\"666\"">/etc/udev/rules.d/53-lattice-ftdi.rules

Chaîne de développement

testbench.svg

Avec le logiciel de simulation ghdl, le forçage des signaux d’entrée d’un composant à tester nécessite l’utilisation d’un composant testbench, à savoir un composant incluant le composant à tester

Dans notre exemple, le composant à tester est le composant icebreaker, décrit dans le fichier vhdl icebreaker.vhd.
Le test nécessite un composant icebreaker_tb, décrit dans le fichier vhdl cicebreaker_tb.vhd, dans lequel on instancie icebreaker.

Composants Testbench
-- Pressing button 1 (A) and/or 2 (B) will compare the two signals
-- and enable one of the built-in LEDs:
--      A < B: L2
--      A = B: L1
--      A > B: L3

library IEEE;
use IEEE.std_logic_1164.all;

entity cmp_1_bit is
port(  but1, but2, clk, reset: in std_logic;
		led1, led2, led3 : out std_logic
		);
end entity cmp_1_bit;

architecture arch_cmp_1_bit of cmp_1_bit is
begin

process(clk, reset)
begin
	if reset = '1' then led1 <= '0'; led2 <= '0'; led3 <= '0';
	elsif rising_edge(clk) then
		if (but1 = '1' and but2 = '0')  then led1 <= '1'; led2 <= '0'; led3 <= '0';
		elsif (but1 = but2) then led1 <= '0'; led2 <= '1'; led3 <= '0';
		elsif (but1 = '0' and but2 = '1') then led1 <= '0'; led2 <= '0'; led3 <= '1';
		else led1 <= '0'; led2 <= '0'; led3 <= '0';
		end if;
	end if;	
end process;

end arch_cmp_1_bit;
--======================================================================
library IEEE;
use IEEE.std_logic_1164.all;
use work.all;

entity icebreaker is
port( CLK, BTN_N : std_logic ; 
		BTN1, BTN2 : in std_logic;
		LED1, LED2, LED3 : out std_logic
	   );
end icebreaker;

architecture arch_icebreaker of icebreaker is
signal reset : std_logic;
begin

reset <= not(BTN_N);

cmp_1_bit0 : entity cmp_1_bit port map (
	but1 => BTN1, 
	but2 => BTN2,
	clk  => CLK,
	reset=> reset,
	led1 => LED1,
	led2 => LED2,
	led3 => LED3
);

end arch_icebreaker;
--======================================================================
library IEEE;
use IEEE.std_logic_1164.all;
use work.all;

entity icebreaker_tb is
end icebreaker_tb;

architecture arch_icebreaker_tb of icebreaker_tb is

    signal BTN1, BTN2, BTN_N, LED1, LED2, LED3, CLK  : std_logic;
    constant CLK_period : time := 83_333 ps; -- 12 MHz
begin

    icebreaker_0 : entity work.icebreaker
        port map (
            CLK   => CLK,
            BTN_N => BTN_N,
            BTN1  => BTN1,
            BTN2  => BTN2,
            LED1  => LED1,
            LED2  => LED2,
            LED3  => LED3
        );

    -- Clock definition
    CLK_process : process
    begin
        CLK <= '0';
        wait for CLK_period / 2;
        CLK <= '1';
        wait for CLK_period / 2;
    end process;

    -- Stimuli
    stimuli : process
    begin
        BTN_N <= '0'; -- Reset actif
        wait for 500 us;
        BTN_N <= '1'; -- Fin reset
        wait for 500 us;

        BTN1 <= '0'; BTN2 <= '0';
        wait for 1 ms;
        BTN1 <= '0'; BTN2 <= '1';
        wait for 1 ms;
        BTN1 <= '1'; BTN2 <= '0';
        wait for 1 ms;
        BTN1 <= '1'; BTN2 <= '1';
        wait;
    end process;

end arch_icebreaker_tb;

chaine_developpement.svg

Sources

cmp_1bit.zip

unzip cmp_1bit.zip
cd cmp_1bit

COMPILATION

ghdl -a icebreaker.vhd
ghdl -a icebreaker_tb.vhd

SIMULATION

bash simu.sh -t icebreaker_tb -s 5ms  

Dans gtkwave, sélectionner l’entité à tester de plus haut niveau ( icebreaker ), et faire clic droit –> Recursive Import –> Insert

gtkwave.png

REMARQUE : dans gtkwave, pour sauvegarder la configuration , faire File –> write save file –> layout.gtkw

SYNTHESE LOGIQUE

bash build_fpga.sh -e icebreaker -p broches.pcf  

Application : Chenillard

Objectif : allumer successivement led1, led2, led3, pendant 1s.

Le fonctionnement du système est échelonné dans le temps, on peut donc utiliser une machine à états :

chenillard.svg

Diagramme d’états :

fsm_chenillard.svg

Valeurs de comptage :

La fréquence de l’horloge de base de la carte icebreaker est f_CLK = 12MHz
T_CLK = 83.33ns

  • Durée 1s : count = 183.33.109=12.106\frac{1}{83.33.10^{-9}} = 12.10^6 = x"0B71B00"
  • Durée 2s : count = 283.33.109=24.106\frac{2}{83.33.10^{-9}} = 24.10^6 = x"16E3600"
  • Durée 3s : count = 383.33.109=36.106\frac{3}{83.33.10^{-9}} = 36.10^6 = x"2255100"

En simulation, ces valeurs demandant un temps de calcul trop long, on pourra considérer une fréquence de 12kHz, et donc les valeurs de comptage suivantes :

  • Durée 1s : count = 12.10312.10^3 = x"2EE0"
  • Durée 2s : count = 24.10324.10^3 = x"5DC0"
  • Durée 3s : count = 36.10336.10^3 = x"BB80"

Sources

chenillard.zip

Chenillard
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity compteur is
port(
  clk, reset, ce, raz : in  std_logic;
  count      : out std_logic_vector(27 downto 0)
);
end compteur;

architecture arch_compteur of compteur is
  signal count_int : unsigned(27 downto 0);
begin
  process(clk, reset)
  begin
    if reset = '1' then
      count_int <= (others => '0');
    elsif rising_edge(clk) then
		if raz = '1' then count_int <= (others => '0');
		elsif ( ce = '1' ) then
		  if count_int = x"FFFFFFF" then
			count_int <= (others => '0'); -- fin de comptage
		  else
			count_int <= count_int + 1; -- "+" (unsigned, int)
		  end if;
		end if;  
    end if;
  end process;

  count <= std_logic_vector(count_int); -- copie de count_int
end arch_compteur;

library IEEE;
use IEEE.std_logic_1164.all;

entity me is 
port( 
	clk, reset : in std_logic;
	count : in std_logic_vector(27 downto 0);
	led1, led2, led3, ce, raz : out std_logic
	);
end me;

architecture arch_me of me is
type etat_me is (INIT, LED_1, LED_2, LED_3);
signal etat_cr, etat_sv : etat_me;
begin 	
	
	process(clk,reset)	-- registre synchrone, maj etat_cr
	begin
		if reset='1' then etat_cr <= LED_1;
		elsif rising_edge(clk) then etat_cr <= etat_sv;
		end if;
	end process;

process(count, etat_cr)
begin
	led1 <= '0'; led2 <= '0'; led3 <= '0'; ce <= '0'; raz <= '0'; etat_sv <= etat_cr;
	
	case etat_cr is
		when INIT => etat_sv <= LED_1;
						raz <= '1';
		when LED_1 =>  if count = x"0B71B00" then etat_sv <= LED_2; end if; --"0B71B00" "0002EE0"
						led1 <= '1'; ce <= '1';
		when LED_2 =>  if count = x"16E3600" then etat_sv <= LED_3; end if; --"16E3600" "0005DC0"
						led2 <= '1'; ce <= '1';
		when LED_3 =>  if count = x"2255100" then etat_sv <= INIT; end if; --"2255100" "0008CA0"
						led3 <= '1'; ce <= '1';
	end case;
end process;	

end arch_me;

--======================================================================
library IEEE;
use IEEE.std_logic_1164.all;
use work.all;

entity icebreaker is
port( CLK, BTN_N : std_logic ; 
		BTN1, BTN2 : in std_logic;
		LED1, LED2, LED3 : out std_logic
	   );
end icebreaker;

architecture arch_icebreaker of icebreaker is
signal reset, ce, raz : std_logic;
signal count : std_logic_vector(27 downto 0);
begin

reset <= not(BTN_N);

compteur0 : entity compteur port map (
clk => CLK,
reset => reset,
ce => ce,
raz => raz,
count => count
);

me0 : entity me port map (
clk => CLK, 
reset => reset,
count => count,	
led1 => LED1,
led2 => LED2,
led3 => LED3,
ce => ce,
raz => raz
);

end arch_icebreaker;