Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Witam,

mam następujący problem.

Użyłem generycznego modułu VHDL do generowania sygnału PWM na FPGA za strony projektu:

https://eewiki.net/pages/viewpage.action?pageId=2093934...

Generator działa bardzo dobrze (zegar 10 MHz, częstotliwość PWM 20 KHz, rozdzielczość PWM 8 bit)

Teraz ośmiobitowy wektor zadający wypełnienie mam podłączony do ośmiu switchy na płytce FPGA.

Jakie jest prawidłowe podejście do generowania serii wektorów określających wypełnienia w funkcji czasu (zawsze taki sam przebieg jak odczyt z ROM ze stałą częstotliwością PWM).

Pewnie trzeba utworzyć proces sekwencyjny .. i co dalej. Jaki byłby szkielet kodu?

Na razie znalazłem jak zaemulować "mały" ROM:


library ieee;
use ieee.std_logic_1164.all;

entity ROM is
port ( address : in std_logic_vector(3 downto 0);
data : out std_logic_vector(7 downto 0) );
end entity ROM;

architecture behavioral of ROM is
type mem is array ( 0 to 2**5 - 1) of std_logic_vector(7 downto 0);
constant my_Rom : mem := (
0 => "00000000",
1 => "00000001",
2 => "00000010",
3 => "00000011",
4 => "00000100",
5 => "11110000",
6 => "11110000",
7 => "11110000",
8 => "11110000",
9 => "11110000",
10 => "11110000",
11 => "11110000",
12 => "11110000",
13 => "11110000",
14 => "11110000",
15 => "11110000",
16 => "00000000",
17 => "00000001",
18 => "00000010",
19 => "00000011",
20 => "00000100",
21 => "11110000",
22 => "11110000",
23 => "11110000",
24 => "11110000",
25 => "11110000",
26 => "11110000",
27 => "11110000",
28 => "11110000",
29 => "11110000",
30 => "11110000",
31 => "11110000",
);
begin
process (address)
begin
case address is
when "00000" => data <= my_rom(0);
when "00001" => data <= my_rom(1);
when "00010" => data <= my_rom(2);
when "00011" => data <= my_rom(3);
when "00100" => data <= my_rom(4);
when "00101" => data <= my_rom(5);
when "00110" => data <= my_rom(6);
when "00111" => data <= my_rom(7);
when "01000" => data <= my_rom(8);
when "01001" => data <= my_rom(9);
when "01010" => data <= my_rom(10);
when "01011" => data <= my_rom(11);
when "01100" => data <= my_rom(12);
when "01101" => data <= my_rom(13);
when "01110" => data <= my_rom(14);
when "01111" => data <= my_rom(15);
when "10000" => data <= my_rom(16);
when "10001" => data <= my_rom(17);
when "10010" => data <= my_rom(18);
when "10011" => data <= my_rom(19);
when "10100" => data <= my_rom(20);
when "10101" => data <= my_rom(21);
when "10110" => data <= my_rom(22);
when "10111" => data <= my_rom(23);
when "11000" => data <= my_rom(24);
when "11001" => data <= my_rom(25);
when "11010" => data <= my_rom(26);
when "11011" => data <= my_rom(27);
when "11100" => data <= my_rom(28);
when "11101" => data <= my_rom(29);
when "11110" => data <= my_rom(30);
when "11111" => data <= my_rom(31);
when others => data <= "00000000";
end case;
end process;
end architecture behavioral;


Czy teraz byłoby dobrym pomysłem utworzenie jeszcze jednego komponentu, który z odpowiednio wolnym zegarem "wypluje" zawartość ROM - wektory określające wypełnienie dla generatora PWM?

Zastanawiam się czy proces ROM mógłby wyglądać po prostu tak:

library ieee;
use ieee.std_logic_1164.all;

entity ROM1 is
port (data : out std_logic_vector(7 downto 0) );
end entity ROM1;

architecture behavioral of ROM1 is
begin
process ()
begin
data <= "00000000";
wait for 1000000000 ns;
data <= "00000000";
wait for 1000000000 ns;
data <= "00001111";
wait for 1000000000 ns;
data <= "00011111";
wait for 1000000000 ns;
data <= "00111111";
wait for 1000000000 ns;
data <= "01111111";
wait for 1000000000 ns;
data <= "11111111";
wait for 1000000000 ns;
end process;
end architecture behavioral;


Sorry wait for jest "nie-syntezowalne" i mam błędyTen post został edytowany przez Autora dnia 17.08.17 o godzinie 19:47
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Chłopie daj se spokój z jakimiś pamięciami ROM - szkoda zachodu. Jak chcesz generować wektory wypełnień to walnij po prostu tak (równoważnie zamiast pamięci ROM, którą dałeś):

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity WEKTORY_WYPELNIEN is
port ( clk :in std_logic;
data : out std_logic_vector(7 downto 0) );
end WEKTORY_WYPELNIEN;

architecture BEHAVE of WEKTORY_WYPELNIEN is

signal ADDRESS :std_logic_vector(7 downto 0);
signal TEMP :integer range 0 to 1000000 :=0; -- liczba impulsów
-- jaka ma minąć zanim dojdzie do zmiany adresu

begin

-- ZWIEKSZANIE ADRESU KOLEJNYCH WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then
if (TEMP < 1000000) then TEMP <= TEMP + 1;
else TEMP <= 0; ADDRESS <= ADDRESS + 1;
end if;
end if;
end process;

-- GENEROWANIE WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then
for I in 0 to 1 loop
if (ADDRESS >= 0+i*16 and ADDRESS <= 4+i*16) then
DATA <= "0000"&STD_LOGIC_VECTOR(TO_UNSIGNED(TO_INTEGER(UNSIGNED(ADDRESS))-i*16,4));
end if;

if (ADDRESS >= 5+i*16 and ADDRESS <= 15+i*16) then
DATA <= "11110000";
end if;
end loop;
end if;
end process;
end architecture;


Krótko mówiąc: tam gdzie się da obliczać wartości wektorów na podstawie adresu to obliczaj je :) Co do natomiast zmiennej TEMP to wartość zależy od tego co jaki czas chcesz zmieniać adres (dla przykładu: jeśli podajesz zegar np: 1 MHz to jeden impuls takiego zegara trwa jedną mikrosekundę. Skoro tak to np: dla zmiany co jedną sekundę potrzeba zatem miliona takich impulsów i stąd wtedy zmienna TEMP przyjmie maksymalną wartość równą jeden itd.)Ten post został edytowany przez Autora dnia 18.08.17 o godzinie 00:43
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Jakub,

jak zwykle dzielisz się swoją dużą wiedzą dot VHDL - dziękuję za odpowiedź.

Na bazie twojej podpowiedzi zrobiłem to docelowo tak:



library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity WEKTORY_WYPELNIEN is
port ( clk :in std_logic;
data : out std_logic_vector(7 downto 0) );
end WEKTORY_WYPELNIEN;

architecture BEHAVE of WEKTORY_WYPELNIEN is

signal ADDRESS :std_logic_vector(7 downto 0);
signal TEMP :integer range 0 to 10000000 :=0; -- liczba impulsów
-- jaka ma minąć zanim dojdzie do zmiany adresu
type mem is array ( 0 to 2**5 - 1) of std_logic_vector(7 downto 0);
constant my_Rom : mem := (
0 => "00000111",
1 => "00000111",
2 => "00000111",
3 => "00001111",
4 => "00001111",
5 => "00011111",
6 => "00011111",
7 => "00010000",
8 => "10110000",
9 => "11110000",
10 => "11110000",
11 => "11110000",
12 => "11110000",
13 => "11110000",
14 => "11110000",
15 => "11110000",
16 => "00000000",
17 => "00000001",
18 => "00000010",
19 => "00000011",
20 => "00000100",
21 => "11110000",
22 => "11110000",
23 => "11110000",
24 => "11110000",
25 => "11110000",
26 => "11110000",
27 => "11110000",
28 => "11110000",
29 => "11111111",
30 => "11110111",
31 => "11111111"
);

begin

-- ZWIEKSZANIE ADRESU KOLEJNYCH WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then
if (TEMP < 10000000) then TEMP <= TEMP + 1;
else TEMP <= 0; ADDRESS <= ADDRESS + 1;
end if;
end if;
end process;

-- GENEROWANIE WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then
DATA <= my_rom(TO_INTEGER(UNSIGNED(ADDRESS)));
end if;
end process;
end architecture;

Sprawdzałem z analizatorem stanów logicznych i wydaje sie , iż działa to OK.
PozdrawiamTen post został edytowany przez Autora dnia 18.08.17 o godzinie 19:22
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Cały projekt wygląda teraz tak:

Projekt.vhd:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity projekt is
Port ( clk : in STD_LOGIC;
reset_n : in STD_LOGIC;
ena : in STD_LOGIC;
duty : in STD_LOGIC_VECTOR (7 downto 0);
pwm_out : out STD_LOGIC_VECTOR (0 downto 0);
pwm_n_out : out STD_LOGIC_VECTOR (0 downto 0));

end projekt;

architecture Behavioral of projekt is

component WEKTORY_WYPELNIEN is
port ( clk :in std_logic;
data : out std_logic_vector(7 downto 0) );
end component;

component pwm is
Port ( clk : in STD_LOGIC;
reset_n : in STD_LOGIC;
ena : in STD_LOGIC;
duty : in STD_LOGIC_VECTOR (7 downto 0);
pwm_out : out STD_LOGIC_VECTOR (0 downto 0);
pwm_n_out : out STD_LOGIC_VECTOR (0 downto 0));
end component;

signal dataR_signal :STD_LOGIC_VECTOR (7 downto 0);
begin
C1: WEKTORY_WYPELNIEN port map (clk, dataR_signal);
C2: pwm port map (clk, reset_n, ena, dataR_signal, pwm_out, pwm_n_out);end Behavioral;



Plik: pwm.vhd


LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;

ENTITY pwm IS
GENERIC(
sys_clk : INTEGER := 1_000_000; --system clock frequency in Hz
pwm_freq : INTEGER := 20_000; --PWM switching frequency in Hz
bits_resolution : INTEGER := 8; --bits of resolution setting the duty cycle
phases : INTEGER := 1); --number of output pwms and phases
PORT(
clk : IN STD_LOGIC; --system clock
reset_n : IN STD_LOGIC; --asynchronous reset
ena : IN STD_LOGIC; --latches in new duty cycle
duty : IN STD_LOGIC_VECTOR(bits_resolution-1 DOWNTO 0); --duty cycle
pwm_out : OUT STD_LOGIC_VECTOR(phases-1 DOWNTO 0); --pwm outputs
pwm_n_out : OUT STD_LOGIC_VECTOR(phases-1 DOWNTO 0)); --pwm inverse outputs
END pwm;

ARCHITECTURE logic OF pwm IS
CONSTANT period : INTEGER := sys_clk/pwm_freq; --number of clocks in one pwm period
TYPE counters IS ARRAY (0 TO phases-1) OF INTEGER RANGE 0 TO period - 1; --data type for array of period counters
SIGNAL count : counters := (OTHERS => 0); --array of period counters
SIGNAL half_duty_new : INTEGER RANGE 0 TO period/2 := 0; --number of clocks in 1/2 duty cycle
TYPE half_duties IS ARRAY (0 TO phases-1) OF INTEGER RANGE 0 TO period/2; --data type for array of half duty values
SIGNAL half_duty : half_duties := (OTHERS => 0); --array of half duty values (for each phase)
BEGIN
PROCESS(clk, reset_n)
BEGIN
IF(reset_n = '0') THEN --asynchronous reset
count <= (OTHERS => 0); --clear counter
pwm_out <= (OTHERS => '0'); --clear pwm outputs
pwm_n_out <= (OTHERS => '0'); --clear pwm inverse outputs
ELSIF(clk'EVENT AND clk = '1') THEN --rising system clock edge
IF(ena = '1') THEN --latch in new duty cycle
half_duty_new <= conv_integer(duty)*period/(2**bits_resolution)/2; --determine clocks in 1/2 duty cycle
END IF;
FOR i IN 0 to phases-1 LOOP --create a counter for each phase
IF(count(0) = period - 1 - i*period/phases) THEN --end of period reached
count(i) <= 0; --reset counter
half_duty(i) <= half_duty_new; --set most recent duty cycle value
ELSE --end of period not reached
count(i) <= count(i) + 1; --increment counter
END IF;
END LOOP;
FOR i IN 0 to phases-1 LOOP --control outputs for each phase
IF(count(i) = half_duty(i)) THEN --phase's falling edge reached
pwm_out(i) <= '0'; --deassert the pwm output
pwm_n_out(i) <= '1'; --assert the pwm inverse output
ELSIF(count(i) = period - half_duty(i)) THEN --phase's rising edge reached
pwm_out(i) <= '1'; --assert the pwm output
pwm_n_out(i) <= '0'; --deassert the pwm inverse output
END IF;
END LOOP;
END IF;
END PROCESS;
END logic;



Plik: WEKTORY_WYPELNIEN.vhd


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity WEKTORY_WYPELNIEN is
port ( clk :in std_logic;
data : out std_logic_vector(7 downto 0) );
end WEKTORY_WYPELNIEN;

architecture BEHAVE of WEKTORY_WYPELNIEN is

signal ADDRESS :std_logic_vector(7 downto 0);
signal TEMP :integer range 0 to 10000000 :=0; -- liczba impulsów
-- jaka ma minąć zanim dojdzie do zmiany adresu
type mem is array ( 0 to 2**5 - 1) of std_logic_vector(7 downto 0);
constant my_Rom : mem := (
0 => "00000111",
1 => "00000111",
2 => "00000111",
3 => "00001111",
4 => "00001111",
5 => "00011111",
6 => "00011111",
7 => "00010000",
8 => "10110000",
9 => "11110000",
10 => "11110000",
11 => "11110000",
12 => "11110000",
13 => "11110000",
14 => "11110000",
15 => "11110000",
16 => "00000000",
17 => "00000001",
18 => "00000010",
19 => "00000011",
20 => "00000100",
21 => "11110000",
22 => "11110000",
23 => "11110000",
24 => "11110000",
25 => "11110000",
26 => "11110000",
27 => "11110000",
28 => "11110000",
29 => "11111111",
30 => "11110111",
31 => "11111111"
);

begin

-- ZWIEKSZANIE ADRESU KOLEJNYCH WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then
if (TEMP < 10000000) then TEMP <= TEMP + 1;
else TEMP <= 0; ADDRESS <= ADDRESS + 1;
end if;
end if;
end process;

-- GENEROWANIE WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then
DATA <= my_rom(TO_INTEGER(UNSIGNED(ADDRESS)));
end if;
end process;
end architecture;


Plik: PWMICF.ucf (dla mojej płytki Elbert v2):


NET "clk" LOC = P129;
NET "clk" PERIOD = 1MHz;

NET "reset_n" PULLUP;
NET "reset_n" LOC = "P75";

NET "ena" PULLUP;
NET "ena" LOC = "P76";

NET "pwm_out<0>" LOC = "P19";
NET "pwm_n_out<0>" LOC = "P21";


Mam zamiar go użyć do sterowania silniczkiem 12V (jednofazowym).
Jeszcze raz bardzo dziękuję Jakubowi za rzeczową pomoc.

PozdrawiamTen post został edytowany przez Autora dnia 18.08.17 o godzinie 19:44
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Drobiazg ;) Sam projekt może nawet być, choć widzę, że jednak wolałeś pozostać przy tablicy przechowującej wektory, aniżeli, przy dosłownym wyliczaniu wartości wektorów na podstawie adresu tak jak ja to zastosowałem, aby wyeliminować tą tablicę - nie jest to oczywiście błąd, ale przy dużych ilościach wektorów takie powielanie kodu w sensie pisania każdej odrębnej wartości wektora może zwyczajnie znużyć - ale to tak na boku z własnego doświadczenia :) Docelowo jednak jeśli chcesz iść tak (czyli jakbyś stosował pamięć ROM), zwłaszcza w sytuacji gdybyś miał np: 10 tys takich wektorów,to użyj pamięci ROM w postaci odrębnego pliku,który potem jako komponent wstawiasz do pliku głównego projektu (jest zwykle gotowy kod na pamięć ROM, który po rozpoznaniu przez środowisko czy to Xilinxa czy Altery zostaje przelozony na bloki pamięciowe zawarte w układzie FPGA) - w przypadku twego kodu zachodzi wymuszenie na układzie aby zachowywał się jak pamięć, którą buduje wówczas z pojedynczych zasobów, a nie z dedykowanych komórek pamięci (jednak w przypadku małej ilości wektorów jest to wystarczające i można na czymś takim poprzestać).Ten post został edytowany przez Autora dnia 19.08.17 o godzinie 00:01
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Aa - i warto dodać, że generator jaki znalazłeś i użyłeś w projekcie to generator PWM działający w trybie Phase Correct, jako że wyznaczane tu są de facto dwie wartości graniczne wokół środka trwania pojedynczego impulsu zegara (stała "faza"). Dokładniej chodzi o ten fragment kodu:

      FOR i IN 0 to phases-1 LOOP                                            --control outputs for each phase
IF(count(i) = half_duty(i)) THEN --phase's falling edge reached
pwm_out(i) <= '0'; --deassert the pwm output
pwm_n_out(i) <= '1'; --assert the pwm inverse output
ELSIF(count(i) = period - half_duty(i)) THEN --phase's rising edge reached
pwm_out(i) <= '1'; --assert the pwm output
pwm_n_out(i) <= '0'; --deassert the pwm inverse output
END IF;
END LOOP;


Jest to w zasadzie tryb, który jest zalecany właśnie do sterowania silnikami, jasnością diod itp. sprawami, w stosunku do trybu Fast PWM, w którym po prostu wyznaczana jest wartość, przy której rozpoczyna się generacja impulsu trwająca do momentu wyzerowania się licznika zliczającego impulsy zegara. To tak też na boku :)

Dla ciekawskich o co chodzi odnośnie trybów PWM: http://www.ermicro.com/blog/?p=1971

a także porównanie trybu Fast PWM i Phase Correct PWM w praktyce na przykładzie latarek:

Obrazek
Ten post został edytowany przez Autora dnia 19.08.17 o godzinie 00:44
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Jakub,

a gdybym chciał podłączyć "pushbutton" ( z debouncerem ) aby zmiana stanu tego klawisza powodowała jednorazowe wygenerowanie serii wypełnień, a potem wyjście pwm przyjmowałoby stan niski.

Jak można to zrobić w tym projekcie?

Traktujmy, że jest jedno wyjście z klawisza (z debouncingiem - mam to już przetestowane).

Podejrzewam, że trzeba by dodać wyjście z klawisza do Wektory_Wypelnien i uruchamiać licznik, który zliczy odpowiednią liczbe impulsów, a potem będzie wstawiał zerta do wektora wypełnien?

PozdrawiamTen post został edytowany przez Autora dnia 19.08.17 o godzinie 09:39
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Maciej G.:
Jakub,

a gdybym chciał podłączyć "pushbutton" ( z debouncerem ) aby zmiana stanu tego klawisza powodowała jednorazowe wygenerowanie serii wypełnień, a potem wyjście pwm przyjmowałoby stan niski.
Na razie mam tak:
WEKTORY_WYPELNIEN


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity WEKTORY_WYPELNIEN is
port ( clk :in std_logic;
pushB :in std_logic;
data : out std_logic_vector(7 downto 0) );
end WEKTORY_WYPELNIEN;

architecture BEHAVE of WEKTORY_WYPELNIEN is

signal ADDRESS :std_logic_vector(7 downto 0);
signal TEMP :integer range 0 to 1000000 :=0; -- liczba impulsów
-- jaka ma minąć zanim dojdzie do zmiany adresu
signal LICZNIK :integer range 0 to 1000000*32 :=0;
type mem is array ( 0 to 2**5 - 1) of std_logic_vector(7 downto 0);
constant my_Rom : mem := (
0 => "00000111",
1 => "00000111",
2 => "00000111",
3 => "00001111",
4 => "00001111",
5 => "00011111",
6 => "00011111",
7 => "00010000",
8 => "10110000",
9 => "11110000",
10 => "11110000",
11 => "11110000",
12 => "11110000",
13 => "11110000",
14 => "11110000",
15 => "11110000",
16 => "00000000",
17 => "00000001",
18 => "00000010",
19 => "00000011",
20 => "00000100",
21 => "11110000",
22 => "11110000",
23 => "11110000",
24 => "11110000",
25 => "11110000",
26 => "11110000",
27 => "11110000",
28 => "11110000",
29 => "11111111",
30 => "11110111",
31 => "11111111"
);

begin

-- ZWIEKSZANIE ADRESU KOLEJNYCH WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then
if (TEMP < 1000000) then TEMP <= TEMP + 1;
else TEMP <= 0; ADDRESS <= ADDRESS + 1;
end if;
end if;
end process;

-- GENEROWANIE WEKTOROW WYPELNIEN
process (CLK, pushB)
begin
if falling_edge(pushB) THEN
LICZNIK <= 0;
end if;
if rising_edge(CLK) then
LICZNIK <= LICZNIK +1;
if LICZNIK < 1000000*32 then
DATA <= my_rom(TO_INTEGER(UNSIGNED(ADDRESS)));
else DATA <= "00000000";
end if;
end if;
end process;
end architecture;


Projekt


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity projekt is
Port ( clk : in STD_LOGIC;
reset_n : in STD_LOGIC;
ena : in STD_LOGIC;
button1 : in STD_LOGIC;
duty : in STD_LOGIC_VECTOR (7 downto 0);
pwm_out : out STD_LOGIC_VECTOR (0 downto 0);
pwm_n_out : out STD_LOGIC_VECTOR (0 downto 0));

end projekt;

architecture Behavioral of projekt is

component WEKTORY_WYPELNIEN is
port ( clk :in std_logic;
pushB :in std_logic;
data : out std_logic_vector(7 downto 0) );
end component;

component pwm is
Port ( clk : in STD_LOGIC;
reset_n : in STD_LOGIC;
ena : in STD_LOGIC;
duty : in STD_LOGIC_VECTOR (7 downto 0);
pwm_out : out STD_LOGIC_VECTOR (0 downto 0);
pwm_n_out : out STD_LOGIC_VECTOR (0 downto 0));
end component;

component debounce is
port ( clk : IN STD_LOGIC; --input clock
button : IN STD_LOGIC; --input signal to be debounced
result : OUT STD_LOGIC); --debounced signal
end component;

signal dataR_signal :STD_LOGIC_VECTOR (7 downto 0);
signal buttonD1 :STD_LOGIC;
begin
C1: debounce port map (clk, button1, buttonD1);
C2: WEKTORY_WYPELNIEN port map (clk, buttonD1, dataR_signal);
C3: pwm port map (clk, reset_n, ena, dataR_signal, pwm_out, pwm_n_out);end Behavioral;



Plik: debounce.vhd


LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;

ENTITY debounce IS
GENERIC(
counter_size : INTEGER := 19); --counter size (19 bits gives 10.5ms with 50MHz clock)
PORT(
clk : IN STD_LOGIC; --input clock
button : IN STD_LOGIC; --input signal to be debounced
result : OUT STD_LOGIC); --debounced signal
END debounce;

ARCHITECTURE logic OF debounce IS
SIGNAL flipflops : STD_LOGIC_VECTOR(1 DOWNTO 0); --input flip flops
SIGNAL counter_set : STD_LOGIC; --sync reset to zero
SIGNAL counter_out : STD_LOGIC_VECTOR(counter_size DOWNTO 0) := (OTHERS => '0'); --counter output
BEGIN

counter_set <= flipflops(0) xor flipflops(1); --determine when to start/reset counter

PROCESS(clk)
BEGIN
IF(clk'EVENT and clk = '1') THEN
flipflops(0) <= button;
flipflops(1) <= flipflops(0);
If(counter_set = '1') THEN --reset counter because input is changing
counter_out <= (OTHERS => '0');
ELSIF(counter_out(counter_size) = '0') THEN --stable input time is not yet met
counter_out <= counter_out + 1;
ELSE --stable input time is met
result <= flipflops(1);
END IF;
END IF;
END PROCESS;
END logic;



ale nie syntetyzuje się - błąd złe użycie sygnału LICZNIKTen post został edytowany przez Autora dnia 19.08.17 o godzinie 10:30
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Sygnał LICZNIK nie zsyntezuje się, ponieważ próbujesz przypisać wartość do sygnału jednocześnie i na zboczu opadającym i narastającym i to jeszcze na dwóch odrębnych sygnałach, tj. przyczyną błędu jest ten kod:

 if falling_edge(pushB) THEN
LICZNIK <= 0;
end if;

if rising_edge(CLK) then
LICZNIK <= LICZNIK +1;
if LICZNIK < 1000000*32 then
DATA <= my_rom(to_integer(unsigned(ADDRESS)));
else DATA <= "00000000";
end if;
end if;

Układy FPGA nie mają takiej architektury, aby jednocześnie i na ten rodzaj i ten rodzaj zbocza była reakcja (bo skąd mają wiedzieć czy czasami jednocześnie one nie wystąpią, co w ogóle jest niemożliwe do syntezy - ty może wiesz, że nie, ale układ tego nie wie). Takie coś to przejdzie najwyżej na byle symulacji.

Jak już to należy napisać to tak:

architecture ...
.
.
signal GENERACJA_SERII_WEKTOROW : std_logic :='0';
-- okreslanie kiedy ma sie zaczac generacja serii wektorow

signal KONIEC_GENERACJI_WEKTOROW : std_logic :='0';
-- okreslenie kiedy koniec generacji serii wektorow

signal OPOZNIENIE_BUTTONA :integer range 0 to 10000000 :=0;
.
.
begin

-- ZWIEKSZANIE ADRESU KOLEJNYCH WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then

-- SPRAWDZENIE CZY PRZYCISK JEST NACISNIETY CZY NIE
if (BUTTON = '1') then -- lub '0' gdy po nacisnieciu
-- przycisku generowane jest przerwanie
if (OPOZNIENIE_BUTTONA < JAKAS_WARTOSC) then
-- opoznienie dzialania przycisku, aby za szybko
-- nie szla generacja po nacisnieciu
OPOZNIENIE_BUTTONA <= OPOZNIENIE_BUTTONA + 1;
else
if (KONIEC_GENERACJI_WEKTOROW = '0') then
-- jesli jest koniec to nie wznawiaj generacji
GENERACJA_SERII_WEKTOROW <= '1';
end if;
end if;
else
KONIEC_GENERACJI_WEKTOROW <= '0';
OPOZNIENIE_BUTTONA<=0;
-- zeruj koniec, gdy przycisk jest puszczony
-- zeruj opoznienie przycisku
end if;


-- GENERACJA SERII WEKTOROW PO NACISNIECIU PRZYCISKU
-- ORAZ OKRESLENIE JEJ KONCA PO JEJ WYKONANIU
if (GENERACJA_SERII_WEKTOROW = '1') then
if (TEMP < 1000000) then
TEMP <= TEMP + 1;
else
TEMP <= 0;
if (ADDRESS <32) then
ADDRESS <= ADDRESS + 1;
else
KONIEC_GENERACJI_WEKTOROW <= '1';
GENERACJA_SERII_WEKTOROW <= '0';
ADDRESS <= 0;
end if;
end if;
else
TEMP <= '0';
end if;
end if;
end process;


-- GENEROWANIE WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then
if (GENERACJA_SERII_WEKTOROW = '1') -- gdy trwa generacja -- wektorow
DATA <= my_rom(ADDRESS) <----EDIT (tu był błąd)
else -- gdy generacja nie trwa
DATA <= "00000000";
end if;
end if;

Krótko mówiąc: to jest coś w rodzaju maszyny stanu, że określa się czy przycisk został wciśnięty (nieważne czy przez chwilę czy dłużej czy nawet jest on trzymany cały czas), a potem ustawia się flagę, która odblokowuje kod generujący automatycznie serię wektorów (realizuje się po puszczeniu przycisku).

Gdy ta dojdzie do końca flaga jest zerowana, a sam kod blokowany. W zamian ustawiana jest flaga oznaczająca koniec generacji, która przydaje się wtedy, gdy przycisk jest trzymany cały czas (chodzi o uniknięcie ponownej generacji w wyniku ustawicznego trzymania przycisku) - flaga ta jest zerowana w momencie puszczenia przycisku.Ten post został edytowany przez Autora dnia 19.08.17 o godzinie 15:57
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Ta wersja się kompiluje, ale nie generuje serii wypełnień po naci śnięciu pushbuttona:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity WEKTORY_WYPELNIEN is
port ( clk :in std_logic;
pushB :in std_logic;
data : out std_logic_vector(7 downto 0) );
end WEKTORY_WYPELNIEN;

architecture BEHAVE of WEKTORY_WYPELNIEN is

signal ADDRESS :integer range 0 to 32 :=0;
signal TEMP :integer range 0 to 10000000 :=0; -- liczba impulsów
-- jaka ma minąć zanim dojdzie do zmiany adresu

signal GENERACJA_SERII_WEKTOROW : std_logic :='0';
-- okreslanie kiedy ma sie zaczac generacja serii wektorow

signal KONIEC_GENERACJI_WEKTOROW : std_logic :='0';
-- okreslenie kiedy koniec generacji serii wektorow

signal OPOZNIENIE_BUTTONA :integer range 0 to 10000000 :=0;
type mem is array ( 0 to 2**5 - 1) of std_logic_vector(7 downto 0);
constant my_Rom : mem := (
0 => "00000111",
1 => "00000111",
2 => "00000111",
3 => "00001111",
4 => "00001111",
5 => "00011111",
6 => "00011111",
7 => "00010000",
8 => "10110000",
9 => "11110000",
10 => "11110000",
11 => "11110000",
12 => "11110000",
13 => "11110000",
14 => "11110000",
15 => "11110000",
16 => "00000000",
17 => "00000001",
18 => "00000010",
19 => "00000011",
20 => "00000100",
21 => "11110000",
22 => "11110000",
23 => "11110000",
24 => "11110000",
25 => "11110000",
26 => "11110000",
27 => "11110000",
28 => "11110000",
29 => "11111111",
30 => "11110111",
31 => "11111111"
);

begin

-- ZWIEKSZANIE ADRESU KOLEJNYCH WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then

-- SPRAWDZENIE CZY PRZYCISK JEST NACISNIETY CZY NIE
if (pushB = '0') then -- lub '0' gdy po nacisnieciu
-- przycisku generowane jest przerwanie
if (OPOZNIENIE_BUTTONA < 1000000) then
-- opoznienie dzialania przycisku, aby za szybko
-- nie szla generacja po nacisnieciu
OPOZNIENIE_BUTTONA <= OPOZNIENIE_BUTTONA + 1;
else
if (KONIEC_GENERACJI_WEKTOROW = '0') then
-- jesli jest koniec to nie wznawiaj generacji
GENERACJA_SERII_WEKTOROW <= '1';
end if;
end if;
else
KONIEC_GENERACJI_WEKTOROW <= '0';
OPOZNIENIE_BUTTONA<=0;
-- zeruj koniec, gdy przycisk jest puszczony
-- zeruj opoznienie przycisku
end if;


-- GENERACJA SERII WEKTOROW PO NACISNIECIU PRZYCISKU
-- ORAZ OKRESLENIE JEJ KONCA PO JEJ WYKONANIU
if (GENERACJA_SERII_WEKTOROW = '1') then
if (TEMP < 1000000) then
TEMP <= TEMP + 1;
else
TEMP <= 0;
if (ADDRESS <32) then
ADDRESS <= ADDRESS + 1;
else
KONIEC_GENERACJI_WEKTOROW <= '1';
GENERACJA_SERII_WEKTOROW <= '0';
ADDRESS <= 0;
end if;
end if;
else
TEMP <= 0;
end if;
end if;
end process;


-- GENEROWANIE WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then
if (GENERACJA_SERII_WEKTOROW = '1') then-- gdy trwa generacja -- wektorow
DATA <= my_rom(ADDRESS);
else -- gdy generacja nie trwa
DATA <= "00000000";
end if;
end if;
end process;
end BEHAVE;



Musiałem zmienić typy ADDRES i TEMP na integer, aby się kompilowało

PozdrawiamTen post został edytowany przez Autora dnia 19.08.17 o godzinie 15:04
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Fakt, nie zauważyłem, że TEMP i ADDRESS wzialem jako INTEGER, ale to juz takie moje przyzwyczajenie. Co do dzialania - moze przycisk nie jest jednak podlaczony? :) A tak to możesz zmienić warunek, że ADDRESS <31 zamiast ADDRESS <32 jako, że wtedy wygeneruje się niepotrzebnie drugi raz pierwszy wektor (mój błąd - nie zauważyłem)Ten post został edytowany przez Autora dnia 19.08.17 o godzinie 16:32
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Tu na potwierdzenie, że to działa symulacja (dla ktorej potrzeb zmieniłem wartości graniczne dla TEMP i OPOZNIENIE_BUTTONA z milionów na 10):


Obrazek

Z samej symulacji co prawda wynika, że zmiana wektora następuje nie co 10 a co 12 impulsów, ale to norma jako, że po pierwsze zmiana adreus następuje na 11 impulsie, a po drugie mamy dwa odrębne procesy - w tym samym czasie w jednym tworzona jest nowa wartość, a w drugim czytana jest jeszcze stara wartość - stąd dodatkowy impuls zegarowy zanim nastąpi zmiana wektora tj. gdy dotrze nowa wartość z pierwszego procesu. Ale to na taki użytek jest nieistotne i dopuszczalne, więc się tym nie przejmuj (nie odczujesz w praktyce różnicy).Ten post został edytowany przez Autora dnia 19.08.17 o godzinie 16:51
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Jakub T.:
Tu na potwierdzenie, że to działa symulacja (dla ktorej potrzeb zmieniłem wartości graniczne dla TEMP i OPOZNIENIE_BUTTONA z milionów na 10):


Obrazek

Z samej symulacji co prawda wynika, że zmiana wektora następuje nie co 10 a co 12 impulsów, ale to norma jako, że po pierwsze zmiana adreus następuje na 11 impulsie, a po drugie mamy dwa odrębne procesy - w tym samym czasie w jednym tworzona jest nowa wartość, a w drugim czytana jest jeszcze stara wartość - stąd dodatkowy impuls zegarowy zanim nastąpi zmiana wektora tj. gdy dotrze nowa wartość z pierwszego procesu. Ale to na taki użytek jest nieistotne i dopuszczalne, więc się tym nie przejmuj (nie odczujesz w praktyce różnicy).

Też myślałem o symulacji , zacząłem szukać inforamcji. Może zrobiłem błąd gdzie indziej, np. w mapowaniu pinów. Mając pewność, że ten kod działa będę szukał przyczyny potencjalnego błędu.

Realny układ też już działa zrobiłem błąd w pliku ucf z pushbuttonem.

Jakub jak już tyle jest w sieci to może wrzucisz jeszcze kod do symulacji - może komuś się przyda (ja też z chęcią bym go obejrzał) , generowanie PWM to dosyć często używana opcja.

PozdrawiamTen post został edytowany przez Autora dnia 19.08.17 o godzinie 17:50
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Wrzuciłem ta symulacje celowo, żeby ci trochę pomóc, a przynajmniej potwierdzić, że ten kod istotowo działa (nawet dałem celowo takie różne przebiegi na linii z przyciskiem, aby pokazać, że działa jak należy tj. raz naciśniesz to potem możesz naciskać, nie naciskać a i tak się do końca zgodnie z założeniem realizuje :)
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

W każdy razie co do przycisków też - z doświadczenia wynika, że czasem występują przyciski co nie kontaktują autentycznie (są albo słabo podłączone do płytki, albo styki się poprzecierały i przycisk jest do wywalenia) lub też należy przycisk odwrócić (ale to raczej w twoim przypadku nie ma znaczenia skoro mimo naciskania i trzymania przycisku u ciebie nie działa i tak). Czasem zdarza się też błąd przy nieprawidłowym podłączaniu przycisku (nie te piny co trzeba) lub zwyczajnie człowiek zapomina o podłączeniu. Czasem też może się okazać, że nie działa to co jest pomiędzy przyciskiem, a tym co ma realizować kod (czyli debouncer w twoim przypadku, o którym mówiłeś) i to też warto na sucho sprawdzić czy rzeczywiście działa.

Czasem może się też okazać, że zegar nie jest podłączony do komponentu czy takiego debouncera (sam sięt ak motałem na początku swej przygody nie pamiętając o podłączeniu źródła lub pętli PLL czy też DCM w przypadku Xilinxa). Warto i na takie drobiazgi zwrócić uwagę :)Ten post został edytowany przez Autora dnia 19.08.17 o godzinie 17:31
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Przyczyna była prostsza zamiast do pushbuttona podpiałem button1 do jednego z przełączników dip.

Jakub masz jakąś własną stronę dot. programowania (a raczej konstruowania) układów FPGA?

To będzie (dzięki twojej pomocy) pierwszy projekt FPGA, który użyje praktycznie ;)

PozdrawiamTen post został edytowany przez Autora dnia 19.08.17 o godzinie 18:02
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Rozumiem, że działa tak jak należy? :) Strony jeszcze nie mam :) Dopiero co będę sie przymierzał :)
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Jakub T.:
Rozumiem, że działa tak jak należy? :) Strony jeszcze nie mam :) Dopiero co będę sie przymierzał :)

Myślę, że z twoją wiedzą byłaby to opcja korzystna dla Ciebie i dla innych ;)

Pozdrawiam
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Troche ubiegles mnie, ale fakt - z ukrycia przymierzam się do działania :)
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Jak prawidłowo wygenerować funkcję (odczyt z ROM) w VHDL

Cześć Jakub,

czy mógłbyś udostępnić testbench'a do tego projektu dla WEKTORY_WYPELNIEN (chciałbym zobaczyć jak się je pisze).

Pozdrawiam



Wyślij zaproszenie do