Vlákno názorů k článku Navrhujeme a vyrábíme vlastní CPU: první pokusy s FPGA od radioing - Ahoj, predne musim rict, ze se tu rozjizdi...

  • Článek je starý, nové názory již nelze přidávat.
  • 5. 3. 2025 1:53

    radioing

    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.

  • 5. 3. 2025 7:51

    echo_zulu

    Budete-li s FPGA pokracovat (vrele doporucuji, je nas malo) ...

    Čiste pre zaujímavosť, zodpovedá tomu aj ohodnotenie?

  • 5. 3. 2025 11:18

    RDa

    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.

  • 21. 3. 2025 10:02

    Biktop

    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.

  • 21. 3. 2025 13:35

    RDa

    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.

  • 5. 3. 2025 21:30

    Martin Beran

    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;
  • 5. 3. 2025 22:54

    radioing

    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.

  • 5. 3. 2025 23:46

    Martin Beran

    Trik s citanim dolu spociva v tom, ze registr citace ma jeden DFF na nejvyssi pozici navic

    Díky za vysvětlení, tenhle trik jsem neznal. Zkusil jsem si Váš popis přepsat do VHDL a dává mi to smysl.

  • 6. 3. 2025 12:46

    calvera

    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 <= "010111110101­11100001000000";
    end if;
    end if;
    end process;
    end architecture;

    6. 3. 2025, 12:49 editováno autorem komentáře

  • 7. 3. 2025 1:16

    radioing

    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;