Skocz do zawartości

Kurs FPGA - własne programy #2


Pomocna odpowiedź

Poprzednio nie najlepiej wyszło mi prezentowanie własnych bojów z FPGA, więc tym razem spróbuję pokazać w małych krokach jak doszedłem do pewnego rozwiązania. Jeśli gdzieś wyjdzie jak straszne błędy popełniłem, będzie chociaż wiadomo kiedy się pojawiły. A w sumie każdy kiedyś się uczy, więc z góry uprzedzam, nie jestem ekspertem od FPGA, też się dopiero uczę i z góry dziękuję za poprawki i sugestie.

Ogólnie moim celem było obsłużenie 7-segmentowego wyświetlacza LED, który jest na pokładzie Elbert-a. Pierwszy program był bardzo prosty, wyświetlana jest tylko jedna cyfra, a całość to 4-bitowy licznik. To tylko program wyjściowy, później będzie poprawiany.

Kod wyglądał tak:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

entity seg7_counter is port(
clk : in std_logic;
led_segment : out std_logic_vector(7 downto 0);
led_enable : out std_logic_vector(2 downto 0)	
);
end seg7_counter;

architecture Behavioral of seg7_counter is

signal digit : std_logic_vector(3 downto 0);
signal counter : std_logic_vector(22 downto 0);
begin
process (clk) is
begin
	if rising_edge(clk) then
		if counter = 0 then
			digit <= digit + 1;
		end if;
		counter <= counter + 1;
	end if;
end process;

with digit  
	select led_segment <=
		not "11111100" when "0000",
		not "01100000" when "0001",
		not "11011010" when "0010",
		not "11110010" when "0011",
		not "01100110" when "0100",
		not "10110110" when "0101",
		not "10111110" when "0110",
		not "11100000" when "0111",
		not "11111110" when "1000",
		not "11110110" when "1001",
		not "11101110" when "1010",
		not "00111110" when "1011",
		not "10011100" when "1100",
		not "01111010" when "1101",
		not "10011110" when "1110",	
		not "10001110" when others;	

led_enable <= not "001";

end Behavioral;

Natomiast efekt działania:

[ Dodano: 26-11-2017, 18:49 ]

Oczywiście wyświetlanie jednej cyfry to dopiero jedna trzecia sukcesu. W kolejnym kroku dodałem więc wybieranie aktywnego segmentu. Na początek samo sterowanie wyświetlaną wartością pozostało bez zmian, ale to fajnie pokazuje jak działa stoerowanie wyświetlaczem.

Do wyboru segmentu dodałem nową zmienną o niezbyt ambitnej nazwie "enable". Zdecydowałem wybrać sterowanie z "gorącą jedynką", co wymagało o 1 bit dłuższego wektora, ale uprościło kod.

Przełączanie segmentów wykonuje kod:

				if enable = "001" then
				enable <= "010";
			elsif enable = "010" then
				enable <= "100";
			else
				enable <= "001";
			end if;

A samo wysterowanie linii to po prostu zanegowana wartość enable (zanegowana, bo tak zostały podłączone wyświetlacze na płytce Elbert:

led_enable <= not enable;

Pełny kod wygląda teraz tak:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

entity seg7_counter is port(
clk : in std_logic;
led_segment : out std_logic_vector(7 downto 0);
led_enable : out std_logic_vector(2 downto 0)	
);
end seg7_counter;

architecture Behavioral of seg7_counter is

signal digit : std_logic_vector(3 downto 0);
signal counter : std_logic_vector(22 downto 0);
signal enable : std_logic_vector(2 downto 0) := "001";
begin
process (clk) is
begin
	if rising_edge(clk) then
		if counter = 0 then
			digit <= digit + 1;

			if enable = "001" then
				enable <= "010";
			elsif enable = "010" then
				enable <= "100";
			else
				enable <= "001";
			end if;

		end if;
		counter <= counter + 1;
	end if;
end process;

with digit  
	select led_segment <=
		not "11111100" when "0000",
		not "01100000" when "0001",
		not "11011010" when "0010",
		not "11110010" when "0011",
		not "01100110" when "0100",
		not "10110110" when "0101",
		not "10111110" when "0110",
		not "11100000" when "0111",
		not "11111110" when "1000",
		not "11110110" when "1001",
		not "11101110" when "1010",
		not "00111110" when "1011",
		not "10011100" when "1100",
		not "01111010" when "1101",
		not "10011110" when "1110",	
		not "10001110" when others;	

led_enable <= not enable;

end Behavioral;

Efekt działania jest nieco dziwny, ale to nie koniec:

[ Dodano: 26-11-2017, 19:03 ]

Do dyspozycji są trzy cyfry, każda odpowiada 4 bitom. Można więc dodać nową zmienną, niech się nazwya np. "value" i przechowuje aktualną wartość do wyświetlenia. Będzie miała rozmiar 12 bitów i będzie zwiększana zamiast zmiennej digit. Natomiast wartość digit jest zależna od enable i odpowiada tej części liczby która aktualnie jest widoczna.

Nowy program wygląda następująco:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

entity seg7_counter is port(
clk : in std_logic;
led_segment : out std_logic_vector(7 downto 0);
led_enable : out std_logic_vector(2 downto 0)	
);
end seg7_counter;

architecture Behavioral of seg7_counter is

signal value : std_logic_vector(11 downto 0);
signal digit : std_logic_vector(3 downto 0);
signal counter : std_logic_vector(20 downto 0);
signal enable : std_logic_vector(2 downto 0) := "001";
begin
process (clk) is
begin
	if rising_edge(clk) then
		if counter = 0 then
			value <= value + 1;

			if enable = "001" then
				enable <= "010";
				digit  <= value(7 downto 4);
			elsif enable = "010" then
				enable <= "100";
				digit  <= value(11 downto 8);
			else
				enable <= "001";
				digit  <= value(3 downto 0);
			end if;

		end if;
		counter <= counter + 1;
	end if;
end process;

with digit  
	select led_segment <=
		not "11111100" when "0000",
		not "01100000" when "0001",
		not "11011010" when "0010",
		not "11110010" when "0011",
		not "01100110" when "0100",
		not "10110110" when "0101",
		not "10111110" when "0110",
		not "11100000" when "0111",
		not "11111110" when "1000",
		not "11110110" when "1001",
		not "11101110" when "1010",
		not "00111110" when "1011",
		not "10011100" when "1100",
		not "01111010" when "1101",
		not "10011110" when "1110",	
		not "10001110" when others;	

led_enable <= not enable;

end Behavioral;

Prędkość zliczania została trochę zwiększona, ale nadal widoczne jest odświeżanie wyświetlacza:

[ Dodano: 26-11-2017, 19:16 ]

Teraz program jest już prawie gotowy, ale widać dużą wadę - za szybko liczy a za wolno odświeża "ekran". Czas więc oddzielić oba liczniki. Zmienna counter była używana do sterowania prędkością zliczania i niech tak zostanie, dodałem drugą "refresh" do sterowania częstotliwością wyświetlania wartości. Kod jest prawie taki sam, poza tym że zamiast jednego procesu są dwa i każdy używa innej zminnej:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

entity seg7_counter is port(
clk : in std_logic;
led_segment : out std_logic_vector(7 downto 0);
led_enable : out std_logic_vector(2 downto 0)	
);
end seg7_counter;

architecture Behavioral of seg7_counter is

signal value : std_logic_vector(11 downto 0);
signal digit : std_logic_vector(3 downto 0);
signal counter : std_logic_vector(21 downto 0);
signal refresh : std_logic_vector(12 downto 0);
signal enable : std_logic_vector(2 downto 0) := "001";
begin
process (clk) is
begin
	if rising_edge(clk) then
		if refresh = 0 then
			if enable = "001" then
				enable <= "010";
				digit  <= value(7 downto 4);
			elsif enable = "010" then
				enable <= "100";
				digit  <= value(11 downto 8);
			else
				enable <= "001";
				digit  <= value(3 downto 0);
			end if;				
		end if;
		refresh <= refresh + 1;
	end if;
end process;

process (clk) is
begin
	if rising_edge(clk) then
		if counter = 0 then
			value <= value + 1;
		end if;
		counter <= counter + 1;
	end if;
end process;

with digit  
	select led_segment <=
		not "11111100" when "0000",
		not "01100000" when "0001",
		not "11011010" when "0010",
		not "11110010" when "0011",
		not "01100110" when "0100",
		not "10110110" when "0101",
		not "10111110" when "0110",
		not "11100000" when "0111",
		not "11111110" when "1000",
		not "11110110" when "1001",
		not "11101110" when "1010",
		not "00111110" when "1011",
		not "10011100" when "1100",
		not "01111010" when "1101",
		not "10011110" when "1110",	
		not "10001110" when others;	

led_enable <= not enable;

end Behavioral;

Teraz rezultat jest wreszcie zgodny z oczekiwaniem 🙂

Natomiast ten cały przydługi wstęp napisałem żeby porozmawiać o nieco innej wersji programu. Ale to chyba umieszczę w oddzielnym wpisie.

[ Dodano: 26-11-2017, 19:41 ]

Pewnie zabrzmi to dziwnie, ale właśnie sobie uświadomiłem, że pisząc raz jeszcze krok-po-kroku cały program rozwiązałem problem, o który chciałem zapytać...

Wcześniej napisana wersja zawierała dzielnik częstotliwości zegara, kod który wstawiłem na końcu wpisu https://www.forbot.pl/forum/topics51/kurs-fpga-wlasne-programy-vt14886.htm był właśnie z tego programu. Nie bardzo mi się to rozwiązanie podobało, miałem wrażenie że można byłoby je jakoś uprościć. I pisząc wszystko od nowa uprościłem - teraz ten dzielnik nie jest już potrzebny 🙂

Ale jeśli są inne uwagi lub pomysły na poprawienie programu, proszę pisać - jak widać samo opisywanie problemu czasem pozwala na jego rozwiązanie.

W ostatecznej części kodu wydzieliłem komponent wyświetlacza do nowego pliku. Nazywa się może niezbyt ładnie seg7_display.vhd i wygląda następująco:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

entity seg7_display is port(
clk : in std_logic;
value : in std_logic_vector(11 downto 0);
led_segment : out std_logic_vector(7 downto 0);
led_enable : out std_logic_vector(2 downto 0));
end seg7_display;

architecture Behavioral of seg7_display is

signal refresh : std_logic_vector(12 downto 0);
signal enable : std_logic_vector(2 downto 0) := "001";
signal digit : std_logic_vector(3 downto 0);

begin
process (clk) is
begin
	if rising_edge(clk) then
		if refresh = 0 then
			if enable = "001" then
				enable <= "010";
				digit  <= value(7 downto 4);
			elsif enable = "010" then
				enable <= "100";
				digit  <= value(11 downto 8);
			else
				enable <= "001";
				digit  <= value(3 downto 0);
			end if;				
		end if;
		refresh <= refresh + 1;
	end if;
end process;

with digit  
	select led_segment <=
		not "11111100" when "0000",
		not "01100000" when "0001",
		not "11011010" when "0010",
		not "11110010" when "0011",
		not "01100110" when "0100",
		not "10110110" when "0101",
		not "10111110" when "0110",
		not "11100000" when "0111",
		not "11111110" when "1000",
		not "11110110" when "1001",
		not "11101110" when "1010",
		not "00111110" when "1011",
		not "10011100" when "1100",
		not "01111010" when "1101",
		not "10011110" when "1110",	
		not "10001110" when others;	

led_enable <= not enable;
end Behavioral;

Teraz program "główny" zawiera tylko licznik wartości oraz instancję wyświetlacza. Co najważniejsze komponent można wykorzystać w kolejnych przykładach (jego poprzednią wersję używałem do zliczania naciśnięć przycisków).

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

entity seg7_counter2 is port(
clk : in std_logic;
led_segment : out std_logic_vector(7 downto 0);
led_enable : out std_logic_vector(2 downto 0)	
);
end seg7_counter2;

architecture Behavioral of seg7_counter2 is

signal value : std_logic_vector(11 downto 0);
signal counter : std_logic_vector(21 downto 0);

begin
disp: entity seg7_display port map(clk, value, led_segment, led_enable);

process (clk) is
begin
	if rising_edge(clk) then
		if counter = 0 then
			value <= value + 1;
		end if;
		counter <= counter + 1;
	end if;
end process;

end Behavioral;

Jeśli macie swoje programy na płytkę Elbert może też będziecie się chcieli podzielić z innymi uczestnikami forum? To dobra okazja do uczenia się, a może i innych przy okazji 🙂

Link to post
Share on other sites

@Elvis Myślałam nad zrobieniem licznika na podstawie przerzutników D. Trajektoria jest taka: 0->2->4->5->6->0 i wówczas przerzutniki mają takie równania DA=QA'*QB+QA*QB'; DB=QC+QA'*QB'; DC=QA*QB'*QC'

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

entity main is
	Port(
		clk : in STD_LOGIC;
		led_segment : out std_logic_vector(7 downto 0);--czy te potrzebne jak sa w display?
		led_enable : out std_logic_vector(2 downto 0)
	);
end main;

architecture Behavioral of main is

variable Q : std_logic_vector(2 downto 0):="000";
variable D : std_logic_vector(2 downto 0):="000";
variable table : std_logic_vector(2 downto 0):="000";
signal value : std_logic_vector(11 downto 0); 

begin
	process(clk)
	begin
		--tu przypisanie do wyswietlacza odpowiednich wartosci			
		loop			
			Q:=table;--przypisanie wartosci table do Q
	
			D(2) := Q(2) xor Q(1); --na lewo
			D(1) := Q(0) nor ((not Q(2)) and (not Q(1)));
			D(0) := Q(2) and (not Q(1)) and (not Q(0));
			
			--tu przypisanie do wyswietlacza odpowiednich wartosci
			
			with value select led_segment <=
				not "11111100" when "0000",--gdy D=000 wyswietl linie wyznaczone przez 2 pierwsze bity co daje 0
				not "01100000" when "0001",--gdy licznik 1 wyswietl 
				not "11011010" when "0010",--gdy licznik 2
				not "11110010" when "0011",--gdy licznik 3
				not "01100110" when "0100",--gdy licznik 4
				not "10110110" when "0101",
				not "10111110" when "0110",
				not "11100000" when "0111",
				not "11111110" when "1000",
				not "11110110" when "1001",
				not "11101110" when "1010",
				not "00111110" when "1011",
				not "10011100" when "1100",
				not "01111010" when "1101",
				not "10011110" when "1110",	
				not "10001110" when others;	

			led_enable <= not "001"; --wybrany konkretny segment do wyswietlania na stale

			table:=D;--wpisanie do table wynikow D dla nastepnej iteracji
			
			wait for 2 sec;
			
		end loop;
		
   end process;

end Behavioral;

Nie wiem czy dobrze kombinuje albo czy tak się da zrobić w VHDL

Link to post
Share on other sites

Wprawdzie pytanie ma konkretnego adresata, ale ponieważ wisi kilka godzin, to wtrącę swoje 3 grosze:

1. Przedstawiony kod nie jest do końca zgodny z zasadami składniowymi vhdla. Podstawowy błąd to deklaracja zmiennych poza procesem – zwykłe zmienne definiuje się w funkcjach/procesach.

2. Nie zgadzają się długości wektorów – w instrukcji with select sygnał value jest traktowana jako czterobitowy, a deklarowany jako dwunastobitowy. Poza tym nie ma nigdzie jakiegokolwiek przypisania wartości do niego.

3. Instrukcja with select jest współbieżna, używana poza procesami (przynajmniej w starszej wersji języka nie można użyć with select wewnątrz procesu). „Procesowym" odpowiednikiem jest case when.

4. Wpisywanie wartości do zmiennych dla następnych iteracji można uprościć - zmienne zachowują wartość pomiędzy iteracjami procesu, zmienna table jest niepotrzebna.

5. Proces powinien mieć albo listę czułości albo instrukcję wait for. Instrukcja wait for  może działać tylko w symulacji. Jeżeli kod ma być syntezowalny, to trzeba go przerobić – w inny sposób odmierzać czas. Poza tym, brakuje wybrania konkretnego zbocza na który ma działać ten proces – bez instrukcji if rising_edge(clk) lub odpowiednika powinien teoretycznie działać na obydwu zboczach zegara, co raczej również nie jest syntezowalne.

6. Pętla loop też jest niepotrzebna, jeżeli proces ma się wykonywać co zbocze zegara. Jeżeli zaś w symulacji ma działać z instrukcją wait for, to również jest to niepotrzebna instrukcja – proces będzie się automatycznie powtarzał (jeżeli na jego końcu nie będzie instrukcji wait, na której mógłby się zawiesić).

7. W funkcji logicznej przerzutnika środkowego powinna być zwykła suma logiczna (or nie nor).

 

Uwzględniając powyższe, do symulacji powinien wystarczyć taki kod:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity main is
	Port(
		clk : in STD_LOGIC;
		led_segment : out std_logic_vector(7 downto 0) := (others => '0');--czy te potrzebne jak sa w display?
		led_enable : out std_logic_vector(2 downto 0) := (others => '0')
	);
end main;

architecture Behavioral of main is

begin
	process
		variable Q, D : std_logic_vector(2 downto 0) := (others => '0');
	begin
			D(2) := Q(2) xor Q(1);
			D(1) := Q(0) or ((not Q(2)) and (not Q(1)));
			D(0) := Q(2) and (not Q(1)) and (not Q(0));
			
			case D is 
				when "000" =>
					led_segment <= not "11111100";
				when "010" =>
					led_segment <= not "11011010";
				when "100" =>
					led_segment <= not "01100110";
				when "101" =>
					led_segment <= not "10110110";
				when "110" =>
					led_segment <= not "10111110";
				when others =>
					led_segment <= not x"FF"; -- Teoretycznie bez znaczenia, sytuacja niemozliwa
			end case;

			led_enable <= not "001"; --wybrany konkretny segment do wyswietlania na stale

			Q := D; --wpisanie do table wynikow D dla nastepnej iteracji
			
			wait for 2 sec;
		
   end process;

end Behavioral;

 

Używając sygnałów zamiast zmiennych, można go trochę przekształcić/skrócić:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity main is
	Port(
		clk : in STD_LOGIC;
		led_segment : out std_logic_vector(7 downto 0) := (others => '0');--czy te potrzebne jak sa w display?
		led_enable : out std_logic_vector(2 downto 0) := (others => '0')
	);
end main;

architecture Behavioral of main is

signal D : std_logic_vector(2 downto 0) := (others => '0');

begin
	process
	begin
			D(2) <= D(2) xor D(1);
			D(1) <= D(0) or ((not D(2)) and (not D(1)));
			D(0) <= D(2) and (not D(1)) and (not D(0));
			
			wait for 2 sec;
   end process;
	
	led_enable <= not "001"; -- Stale wyrazenie moze byc w procesie lub na zewnatrz
	
	with D select
		led_segment <=  not "11111100" when "000",
				not "11011010" when "010",
				not "01100110" when "100",
				not "10110110" when "101",
				not "10111110" when "110",
				not "11111111" when others;

end Behavioral;

Z kolei jeżeli kod ma być syntezowalny, to trzeba go zmienić: usunąć wait for, proces „uwrażliwić" na zbocze zegara i w jakiś sposób odmierzać czas (najprościej użyć prosty licznik, tylko trzeba znać częstotliwość zegara).

  • Lubię! 1
Link to post
Share on other sites
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

(edytowany)
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

entity main is
	Port(
		clk : in STD_LOGIC;
		led_segment : out std_logic_vector(7 downto 0) := (others => '0');
		led_enable : out std_logic_vector(2 downto 0) := (others => '0')
	);
end main;

architecture Behavioral of main is

signal D : std_logic_vector(2 downto 0) := (others => '0');
signal counter : std_logic_vector(0 to 22);

begin
	process (clk) is
	begin
	if rising_edge(clk) then
			if counter = 0 then
				D(2) <= D(2) xor D(1);
				D(1) <= D(0) or ((not D(2)) and (not D(1)));
				D(0) <= D(2) and (not D(1)) and (not D(0));
			end if;
		counter <= (counter + 1);
	end if;
   end process;
	
	led_enable <= not "001"; -- Stale wyrazenie moze byc w procesie lub na zewnatrz
	
	with D select
		led_segment <=  not "11111100" when "000",
				not "11011010" when "010",
				not "01100110" when "100",
				not "10110110" when "101",
				not "10111110" when "110",
				not "11111111" when others;

end Behavioral;

OK, zmieniłam trochę i taki efekt otrzymałam

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

ENTITY test IS
END test;
 
ARCHITECTURE behavior OF test IS 
    COMPONENT main
    PORT(
         clk : IN  std_logic;
         led_segment : OUT  std_logic_vector(7 downto 0);
         led_enable : OUT  std_logic_vector(2 downto 0)
        );
    END COMPONENT;
    
   --Inputs
   signal clk : std_logic := '0';

 	--Outputs
   signal led_segment : std_logic_vector(7 downto 0);
   signal led_enable : std_logic_vector(2 downto 0);

   -- Clock period definitions
   constant clk_period : time := 10 ns;
 
BEGIN
	-- Instantiate the Unit Under Test (UUT)
   uut: main PORT MAP (
          clk => clk,
          led_segment => led_segment,
          led_enable => led_enable
        );
   -- Stimulus process
   stim_proc: process
   begin		
      -- hold reset state for 100 ns.
      wait for 100 ns;	

      wait for clk_period*10;

      -- insert stimulus here 

      wait;
   end process;
END;

A z testem kombinuje, bo nawet jak wstawiłam przypisania do D z odstępem czasowym

		wait for 50 ns;    
        D<="000";
        wait for 10 ns;
        D<="010";
        wait for 10 ns;
        D<="100";
        wait for 10 ns;
        D<="101";
        wait for 10 ns;
        D<="110";
        wait;

to led_segment ciągle miał 00000011, a jak samo D<="000" (tak, żeby samo się aktualizowało od wartości inicjalizującej) to też D ciągle to samo i ten sam led_segment (00000011). Nie wiem do końca jak się robi te symulację, czy przepisać kod z maina do stim_proc:process ?

Edytowano przez monsiw
  • Lubię! 1
Link to post
Share on other sites
(edytowany)

D jest wewnętrznym sygnałem modułu. Testowanie polega na pobudzaniu zewnętrznych „wyprowadzeń/" (wejść) modułu. Wewnątrz procesu stim_proc umieszcza się takie pobudzenia. W tym przypadku jedynym wejściem jest zegar. Zazwyczaj w takim przypadku jest pisany osobny proces do wytwarzania sygnału taktującego. Proces ten powinien być automatycznie wygenerowany przez program ISE. Tego przede wszystkim brakuje w przedstawionym kodzie. Sygnału D nie zmienia się w symulacji, można jedynie sprawdzać jego wartość.

Nie przepisuje się też kodu z modułu do procesu symulacyjnego. Z punktu widzenia modułu testowego testowana jednostka projektowa jest „czarną skrzynką", którą pobudza się w pewien sposób (sterując wejścia) i obserwuje (sprawdzając wartości sygnałów wyjściowych). Można badać stan wewnątrz modułu testowanego, wyświetlając odpowiednie przebiegi sygnałów wewnętrznych (ale nie ma możliwości bezpośredniego sterowania nimi).

Poza tym, do przydałoby się nadać jakąś początkową wartość temu licznikowi, zwłaszcza w symulacji. Bez tego może otrzymać jakieś dziwne wartości w symulacji (inne niż '0' i '1', na przykład 'x'). Myślę, że bardziej w duchu zasad dobrego programowania byłoby nadać tę wartość początkową i sprawdzać samemu, kiedy licznik osiągnie odpowiednią wartość niż zdawać się na jego przepełnienie – dla mnie byłoby to bardziej czytelne i bardziej elastyczne (jeżeli jednak licznik będzie miał się zerować co liczbę taktów niebędących potęgą liczby 2, to trzeba wprowadzać zmiany w kodzie). Poza tym wektor counter ma duży rozmiar, na początek może zmniejsz go do kilku bitów, tak żeby obejrzeć krótsze przebiegi (przełączanie nie co kilka sekund tylko kilkadziesiąt nanosekund). Czas symulacji się wtedy znacząco skróci, a zobaczysz dokładnie jak działa ten moduł. Później możesz wrócić do docelowej wersji.

 

Podsumowując, w symulacji brakuje fragmentu:

   -- Clock process definitions
   clk_process :process
   begin
		clk <= '0';
		wait for clk_period/2;
		clk <= '1';
		wait for clk_period/2;
   end process;

A w kodzie modułu trzeba przynajmniej przypisać wartość początkową do zmiennej counter i tymczasowo zmniejszyć jego rozmiar do na przykład trzech bitów.

Edytowano przez piotr96
Uzupełnienie fragmentu
  • Lubię! 1
  • Pomogłeś! 1
Link to post
Share on other sites

@piotr96 Mam jeszcze takie pytanie dodałam w kodzie wybór pomiędzy przerzutnikiem D, a JK, który realizuje taką trajektorię

1->5->3->6->7->1, ale rezultat się nie zgadza z tym co jest wyświetlane. Nie wiem czy to funkcje wzbudzeń źle wyznaczyłam czy błąd tkwi w kodzie. Oto ten fragment:

elsif DPSwitch(0)='1' and DPSwitch(1)='0'	then
					J(2) <= '1';
					J(1) <= qn(2);
					J(0) <= '1';
					K(2) <= qn(0);
					K(1) <= qn(2) or qn(0);
					K(0) <= (not qn(2)) or qn(1);
					
					led_enable <= not "100";

					if (J(0)='0' and K(0)='0') then
						qn(0) <= qn(0);
					elsif (J(0)='0' and K(0)='1') then
						qn(0) <= '0';
					elsif (J(0)='1' and K(0)='0') then
						qn(0) <= '1';
					elsif (J(0)='1' and K(0)='1') then
						qn(0) <= not qn(0);
					end if;	
					
					if (J(1)='0' and K(1)='0') then
						qn(1) <= qn(1);
					elsif (J(1)='0' and K(1)='1') then
						qn(1) <= '0';
					elsif (J(1)='1' and K(1)='0') then
						qn(1) <= '1';
					elsif (J(1)='1' and K(1)='1') then
						qn(1) <= not qn(1);
					end if;	
					
					if (J(2)='0' and K(2)='0') then
						qn(2) <= qn(2);
					elsif (J(2)='0' and K(2)='1') then
						qn(2) <= '0';
					elsif (J(2)='1' and K(2)='0') then
						qn(2) <= '1';
					elsif (J(2)='1' and K(2)='1') then
						qn(2) <= not qn(2);
					end if;
					
					case qn is
						when "001" => led_segment <= not "01100000";
						when "011" => led_segment <= not "11110010";
						when "101" => led_segment <= not "10110110";
						when "110" => led_segment <= not "10111110";
						when "111" => led_segment <= not "11100000";
						when others => led_segment <= not "11111111";					
					end case;			
				end if;
			end if;

 

Pierwszy 'if' dotyczy przerzutnika D, a po end if zamykającym obie instrukcje jest instrukcja licznika.

Wyświetla mi się 1->7->8.->7->8.->(1->6)...

Link to post
Share on other sites

Wydaje mi się, że problem tkwi w funkcjach wzbudzeń K(1) i K(2), gdzie powinny być iloczyny logiczne a nie sumy:

K(1) <= qn(2) and qn(0);
K(0) <= (not qn(2)) and qn(1);

Reszta jest chyba poprawna. Jedynie mogę zasugerować, że fragmenty opisujące przerzutniki można napisać w pętli for:

przerzutnikiJK : for ii in J'range loop -- lub for ii in 0 to 2 loop
  if (J(ii)='0' and K(ii)='0') then
    qn(ii) <= qn(ii);
  elsif (J(ii)='0' and K(ii)='1') then
    qn(ii) <= '0';
  elsif (J(ii)='1' and K(ii)='0') then
    qn(ii) <= '1';
  elsif (J(ii)='1' and K(ii)='1') then
    qn(ii) <= not qn(ii);
  end if;
end loop przerzutnikiJK;

Ale jest to tylko skrócenie zapisu. VHDL jest dość rozwlekły, dlatego myślę, że warto tego typu konstrukcje stosować.

Link to post
Share on other sites
55 minut temu, piotr96 napisał:

Wydaje mi się, że problem tkwi w funkcjach wzbudzeń K(1) i K(2), gdzie powinny być iloczyny logiczne a nie sumy:


K(1) <= qn(2) and qn(0);
K(0) <= (not qn(2)) and qn(1);

Reszta jest chyba poprawna. Jedynie mogę zasugerować, że fragmenty opisujące przerzutniki można napisać w pętli for:


przerzutnikiJK : for ii in J'range loop -- lub for ii in 0 to 2 loop
  if (J(ii)='0' and K(ii)='0') then
    qn(ii) <= qn(ii);
  elsif (J(ii)='0' and K(ii)='1') then
    qn(ii) <= '0';
  elsif (J(ii)='1' and K(ii)='0') then
    qn(ii) <= '1';
  elsif (J(ii)='1' and K(ii)='1') then
    qn(ii) <= not qn(ii);
  end if;
end loop przerzutnikiJK;

Ale jest to tylko skrócenie zapisu. VHDL jest dość rozwlekły, dlatego myślę, że warto tego typu konstrukcje stosować.

Teraz inna trajektoria, ale wciąż nieprawidłowa. Czy to kwestia hazardu?

Link to post
Share on other sites

Hazard to inne zagadnienie. Tutaj problemem jest logika – funkcje wzbudzeń nie współgrają czasowo z aktualną wartością licznika. Sygnały w języku VHDL są uaktualniane przy zakończeniu procesu. To znaczy, przez cały proces zachowują przy odczycie swoją pierwszą wartość. Dlatego obliczeniach funkcje wzbudzeń czytają ich poprzednie wartości. Rozwiązaniem tego problemu może być na przykład zamiana sygnałów (funkcji wzbudzeń) na zmienne – coś takiego (uproszczony proces do symulacji):

proc: process(Clk)
variable J, K : std_logic_vector(qn'range) := (others => '0');
begin
	if rising_edge(Clk) then
		J(2) := '1';
		J(1) := qn(2);
		J(0) := '1';
		K(2) := qn(0);
		K(1) := qn(2) and qn(0);
		K(0) := (not qn(2)) and qn(1);
		
		petla: for ii in J'range loop
			if (J(ii)='0' and K(ii)='0') then
				qn(ii) <= qn(ii);
			elsif (J(ii)='0' and K(ii)='1') then
				qn(ii) <= '0';
			elsif (J(ii)='1' and K(ii)='0') then
				qn(ii) <= '1';
			elsif (J(ii)='1' and K(ii)='1') then
				qn(ii) <= not qn(ii);
			end if;
		end loop;
	end if;
end process proc;

Zmienne w procesie są uaktualniane natychmiast, w miejscu przypisania. W tej sytuacji przerzutniki otrzymują wartości funkcji wzbudzeń obliczone na podstawie ostatniego stanu.

Nie wiem, czy ta różnica pomiędzy sygnałem a zmienną jest jasna. Tak z grubsza, dla tego typu procesów:

sygnał: ma określoną wartość na rozpoczęciu procesu (z poprzedniej iteracji); przez cały proces nie zmienia swojej wartości, to znaczy pomimo przypisania wartości do sygnału w jednej linii, wykorzystanie tego sygnału w następnych wierszach kodu powoduje użycie „zatrzaśniętej", początkowej wartości; na koniec procesu sygnał otrzymuje nową wartość (na podstawie ostatniej wykonanej instrukcji przypisania)

zmienna ma określoną wartość na rozpoczęciu procesu (z poprzedniej iteracji); każde przypisanie nowej wartości powoduje jej użycie w kolejnych instrukcjach; zmienna kończy proces z wartością z ostatniego przypisania

 

Oczywiście, problem można pewnie rozwiązać też w inny sposób (chociażby wklejając bezpośrednio formuły obliczania funkcji wzbudzeń do instrukcji warunkowej opisującej przerzutniki), ale użycie zmiennych wydaje się najprostsze i uniwersalne.

  • Lubię! 1
  • Pomogłeś! 1
Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.