Ahoj, predne musim rict, ze se tu rozjizdi pekny serial (moc dik za nej), a jelikoz se v branzi pohybuji uz nejaky ten patek a nemohl jsem si nevsimnout komentaru vyse, tak jsem nevydrzel, a podival se na github a i na ten procesor 5016.
Vytky kolegu bych shrnul asi tak, ze se jim nelibi nektere spatne navyky a nebezpecne konstrukty, ktere se mohou v budoucnnosti pekne vymstit (ve smyslu, ze se u komplikovanejsich obvodu bude pripadna chyba pekne spatne hledat), nekdy zbytecne prodluzuji signalpath (tj. cas mezi Q->D DFFs, coz se projevi snizenym max. hodinovym kmitoctem vysledneho designu) a design pozira zbytecne mnoho resources.
Nicmene pokud jste se sam v uvedenem case dopracoval az sem, tak klobouk dolu, pokud mohu srovnavat se studenty a juniory. Budete-li s FPGA pokracovat (vrele doporucuji, je nas malo), pak by stalo za to poohlednout se po najake ucebnici VHDL + VHDL standard, a vstrebat nektere lety proverene "best practices" (Risc-V nekde na githubu nemusi byt dobry vzor.)
Ono napriklad ten prvni proces led12 je v podstate OK (wait on clk...) a je syntetizovatelny (jinak by to nechodilo, ze), ale jen diky tomu, jak je to napsane (tj. forma zapisu wait, jeden wait v procesu), protoze jinak obecne wait syntetizovatelny byt nemusi. Takto zapsane je to skutecne ekvivalentni "if rising_edge(clk)", coz je patrne z toho, ze cnt, state, led1 a led2 jsou vystupy DFF. Nicmene rozhodne to neni "best practice" a pri komplikovanejsim designu jsou vidle uz hodne blizko.
Par hintu:
1. Pokud to jde, strkejte vse dovnitr procesu uvozenych rising_edge (running12, running34). Vidite, co napachalo to running34 (zbytecna LUT na vystupu), nebo running12 (latch). Logika se vam pak zdrcne do LUT pred DFF (tj. signaly na leve strane = vystup DFF) a design se stane plne synchronni, coz je z mnoha duvodu zadana vlastnost.
2. Zkuste zapremyslet, jaka je presne perioda blikani (dabel je ukryt v detailu).
3. Pro casovani pomoci citacu pouzivejte citani smerem dolu s preloadem. On ten konstrukt citac nahoru-komparace-reset je obecne rozsireny nesvar i v lepsich ucebnicich, ale vidite, co to udela - krome uz tak pomaleho citace (carry pres moc moc bitu) je za nim jeste dalsi logika (komparator), a pak teprve feedback. To vede k tomu, ze jednak ma citac nizsi fmax, druhak to zere zbytecne moc zdroju. Pritom by na to deleni 50 MHz stacilo 27 DFF + pridruzene LUT v ramci makrocely. Jeden proces by pak vystacil s 29 DFF a cely design pri jeste stale citelnem zapisu (idealne FSM se stavy = led vystupy) s 32 DFF. Chapu, ze to zde vubec neni o optimalizaci, na druhou stranu VHDL je o praxi, tak proc si to neprocvicovat hned od zacatku.
Ja si treba nestezuji, ale nedelam to jako full-time job, spise se povede raz za cas licencovat vetsi a ucelenejsi IP celek, takze klient tam plati za to, ze neco dostane "hned", a to muze byt pak zajimava vysokohorska prirazka. Zatimco v kodu nebo sw utilitach vam konkuruje mnoho i free alternativ, tak v pripade embedded, a HDL IP ten prostor neni tak na krev konkurencni a porad existuji nepokryta mista, kde se lze realizovat. Plus temer vse komercniho neni ve forme zdrojaku, na coz nekteri zakaznici slysi (to jenom stat je neschopnej si specifikovat pristup a licenci ke zdrojakum).
Pokud se budeme bavit o nabidce vyvoje za hodinovku, resp zamestnani, tak tam rozdil nebude.. zidle jako zidle, ruce jak ruce, jste jen kapkou v korporatnim mori.
Docela mě děsí, že někdo, kdo tvrdí, že inferred latch není problém, navrhuje FPGA za peníze. Pro koho jste dělal návrhy? Abych se těmto produktům mohl co největším obloukem vyhnout.
Elektrikáře, který by mi tvrdil, že nezapojený PE vodič není problém, že bez něho to taky bude fungovat, bych vyhnal okamžitě. Vaše tvrzení o latchích je na podobné úrovni.
Tak jeste jednou - jestli projde simulace, jestli vyhovuji constrainty, jestli produkt funguje - a nastroje s tim nemaji vubec problem, tak proc je inferred latch problem, na rozdil treba od inferred block ram? Evidentne tool sam vi, co si muze dovolit.
Jina vec by byla, kdyby design nebyl spravne naconstrainovan, coz je vas pripad PE vodice, ale nemit constrainty by v pripade FPGA vedlo na neco co ani fungovat nebude.
Ahoj, predne musim rict, ze se tu rozjizdi pekny serial
Dík za pochvalu.
Vytky kolegu bych shrnul asi tak, ze se jim nelibi nektere spatne navyky a nebezpecne konstrukty, ktere se mohou v budoucnnosti pekne vymstit
Bez obav, už se vymstily, když se mi někde při dalším vývoji nějaké podobné konstrukce podařilo udělat. Všichni se tu pořád točí na asi třetím .vhd souboru, co jsem kdy napsal, navíc jsem si na něm zkoušel, jak a proč se různé konstrukce chovají, ať už jsou dobře nebo špatně.
protoze jinak obecne wait syntetizovatelny byt nemusi
To vím, ostatně je to i napsané v dokumentaci Quartusu.
Pokud to jde, strkejte vse dovnitr procesu uvozenych rising_edge
To dělám, ten wait je nikde jinde v celém repozitáři nevyskytuje (kromě jednoho testbenche), navíc často používám ještě asynchronní reset, což se s if píše lépe.
Zkuste zapremyslet, jaka je presne perioda blikani (dabel je ukryt v detailu).
Hledání chyb v časování událostí +/- jeden takt hodin už jsem si užil dost.
Pro casovani pomoci citacu pouzivejte citani smerem dolu s preloadem.
Můžete sem hodit ukázku kódu, jak přesně to myslíte? Zkusil jsem toto a Quartus hlásí 49 LEs a 27 registrů. Tak nevím co a jak ušetřím čítáním dolů.
process (Clk) is
variable cnt: unsigned(25 downto 0) := (others=>'0');
begin
if rising_edge(Clk) then
if cnt = 50_000_000 then
cnt := (others=>'0');
state <= not state;
else
cnt := cnt + 1;
end if;
end if;
end process;
LED(0) <= state;
Trik s citanim dolu spociva v tom, ze registr citace ma jeden DFF na nejvyssi pozici navic (tj. chcete delit napr. 200, potrebujete 8 DFF, pridate devaty). Pri citani dolu dojde k podteceni hodnoty v citaci (2 -> 1-> 0 -> 511 (1FFh)), a prave tento signal z pridaneho DFF je zaveden zpet do vsech LUT pridruzenych k DFF a zpusobi nastaveni nove hodnoty citace (preload). V pripade deleni 200 by to byla hodnota 198. Nastaveni nove hodnoty preklapi nejvyssi DFF zpet do nuly, takze ten generuje pouze 1T 'reload' puls.
Co ztracim:
1. Pozere mne to jeden DFF navic.
2. Citani dolu s podtecenim znamena, ze hodnoty v citaci nejsou "slusne vychovane", ale v drtive vetsine pripadu (delicky kmitoctu, casove zakladny generujici 1T pulsy dane repetice, zpozdovaci obvody,... odhaduji tak 90 % aplikaci), to neni na zavadu.
Co ziskam: fmax a resources (LUT)
Reseni s komparatorem znamena, ze povetsinou vsechny bity citace (vystupy DFF) musi byt zavedeny do kaskady LUTs (mame typ. 4- nebo 6vstupe), a teprve vysledek komb. obvodu je zaveden zpet do LUT pridruzenych k DFF nebo sync resetu vsech DFF, pokud jiz neni obsazen 'POR' resetem. Vysledkem potom je signalova smycka LUT-DFF-LUT, kde LUT za DFF malokdy byva soucasti makrocely (tu obsadi citac, nebot v ramci makrocely je vertikalne natazena cesta fast carry).
Reseni s downcounterem usetri LUT za DFF a muze byt vyrazne rychlejsi (reload signal se nevytahuje do right/left makrocel, ale do top/bottom makrocel, ktere maji rychle interni propojeni, protoze vyrobce s n-bitovymi operacemi pocita - podobne, jako je zde natazena fast carry logika).
Treba na Versalu to za priznivych okolnosti muze zvysit max fclk az o desitky procent.
Samozrejme existuje i symetricke reseni s upcounterem, kde preteceni je indikovano pulsem do log. 0 na vystupu pridaneho DFF.
takze takhle?
entity test is
port (
reset : in std_logic;
clk_i : in std_logic;
clk_o : out std_logic
);
end entity;
architecture arch of test is
signal count : STD_LOGIC_VECTOR (25 downto 0) := (25 => '1', others => '0');
begin
process (clk_i) -- delicka na ziskanie nizsej freq
begin
if reset = '0' then
count <= (25 => '1', others => '0');
elsif (rising_edge(clk_i)) then
count <= count - 1;
if count(25) = '1' then
clk_o <= NOT clk_o;
count <= "01011111010111100001000000";
end if;
end if;
end process;
end architecture;
6. 3. 2025, 12:49 editováno autorem komentáře
Jj, treba tak.
Takhle by to bylo napr. s temi 4 x LED, kdy vystupy na LED jsou primo vystupy DFF a zmena urovne nastava ve stejnem okamziku na vsech LED.
Prvni proces je optimalizovan pro 4 x LUT4 + 4 x DFF (citac nepocitaje). Ma ale sve neduhy -> prechody mezi LED nesviti/LED blikaji nejsou uplne "slusne vychovane".
Druhy proces to resi ponekud lepe, ale uz potrebuje LUT6 (stop by mel byt zaveden do CLR DFFs), nicmene co se tyce resources, stale by se mel vejit do 4 x LUT6 + 4 x DFF.
Omlouvam se za blbe formatovani, ale nic lepsiho jsem v povolenych tags nenasel.
----------------------------------------------------------------------------------
-- LED1234
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
entity led is
generic (
crystal_khz : integer := 50_000;
period_ms : integer := 1_000
);
port (
start : in std_logic;
stop : in std_logic;
rst : in std_logic;
clk : in std_logic;
led1 : out std_logic;
led2 : out std_logic;
led3 : out std_logic;
led4 : out std_logic
);
end led;
architecture rtl of led is
-- Select variant
constant GEN_TYPE : natural := 1;
-- Counter
constant cnt_width : integer := integer(ceil(log2(real(2 * crystal_khz * period_ms)))) - 1;
signal cnt : std_logic_vector(cnt_width downto 0);
-- FSM with std_logic_vector as state memory
subtype stdlogicvector2 is std_logic_vector(1 downto 0);
constant LED_OFF : stdlogicvector2 := "00";
constant LED1_ON : stdlogicvector2 := "01";
constant LED2_ON : stdlogicvector2 := "10";
signal state : stdlogicvector2 := LED_OFF;
--With enum:
--type state_type is (LED_OFF, LED1_ON, LED2_ON);
--signal state : state_type; etc.
-- LED3 & LED4 common trigger signal
signal trig34 : std_logic;
-- LEDs intern
signal led3int : std_logic;
signal led4int : std_logic;
-- FSM with std_logic_vector as state memory
subtype stdlogicvector4 is std_logic_vector(3 downto 0);
constant LED00 : stdlogicvector4 := "0000";
constant LED13 : stdlogicvector4 := "0101";
constant LED14 : stdlogicvector4 := "1001";
constant LED23 : stdlogicvector4 := "0110";
constant LED24 : stdlogicvector4 := "1010";
signal state4 : stdlogicvector4 := LED00;
begin
TYPE0 : if GEN_TYPE = 0 generate
led12 : process (clk) is
begin
if rising_edge(clk) then
-- Downcounter as a timebase
if (rst = '1') then
-- We start at 0 so overflow at nexk clock cycle resets FSM
cnt <= (others => '0');
elsif (cnt(cnt'high) = '1') then
cnt <= std_logic_vector(to_signed((crystal_khz * period_ms) - 2 , cnt'length));
else
cnt <= std_logic_vector(unsigned(cnt) - 1);
end if;
-- FSM for LED1 and LED2 - 2 x DFF
if (stop = '1') then
-- 'stop' as common reset
state <= LED_OFF;
else
case state is
when LED_OFF =>
if (start = '1') then
state <= LED1_ON;
end if;
when LED1_ON =>
if (cnt(cnt'high) = '1') then
state <= LED2_ON;
end if;
when LED2_ON =>
if (cnt(cnt'high) = '1') then
state <= LED1_ON;
end if;
when others =>
state <= LED_OFF;
end case;
end if;
-- LED3, LED4 - 2 x DFF
if (state = LED_OFF) then
led3int <= '0';
led4int <= '0';
elsif (trig34 = '1') then
led3int <= not led3int;
led4int <= led3int;
end if;
end if;
end process;
-- Trig pulse for LED3 and LED4
trig34 <= state(1) and cnt(cnt'high);
-- Outputs from DFFs
led1 <= state(0);
led2 <= state(1);
led3 <= led3int;
led4 <= led4int;
end generate TYPE0;
TYPE1 : if GEN_TYPE = 1 generate
led1234 : process (clk) is
begin
if rising_edge(clk) then
-- Downcounter as a timebase
if (rst = '1') then
-- We start at 0 so overflow at nexk clock cycle resets FSM
cnt <= (others => '0');
elsif (cnt(cnt'high) = '1') then
cnt <= std_logic_vector(to_signed((crystal_khz * period_ms) - 2 , cnt'length));
else
cnt <= std_logic_vector(unsigned(cnt) - 1);
end if;
-- FSM for 4 x LED - 4 x DFF
if (stop = '1') then
-- 'stop' as common reset
state4 <= LED00;
else
case state4 is
when LED00 =>
if (start = '1') then
state4 <= LED13;
end if;
when LED13 =>
if (cnt(cnt'high) = '1') then
state4 <= LED14;
end if;
when LED14 =>
if (cnt(cnt'high) = '1') then
state4 <= LED23;
end if;
when LED23 =>
if (cnt(cnt'high) = '1') then
state4 <= LED24;
end if;
when LED24 =>
if (cnt(cnt'high) = '1') then
state4 <= LED13;
end if;
when others =>
state4 <= LED00;
end case;
end if;
end if;
end process;
-- Outputs from DFFs
led1 <= state4(0);
led2 <= state4(1);
led3 <= state4(2);
led4 <= state4(3);
end generate TYPE1;
end rtl;