Fråga om VHDL (Xilinx FPGA)

C, C++, Pascal, Assembly, Raspberry, Java, Matlab, Python, BASIC, SQL, PHP, etc.
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: Fråga om VHDL (Xilinx FPGA)

Inlägg av Korken »

Nu ska vi köra på en ny fråga i VHDL djungeln! :)

Jag håller på att lär mig om generics (alltså att man kan generiskt skapa en modul genom att specificera dess storlek), och det fungerar hittills bra, men jag har en idé nu som jag är osäker på hur jag ska göra.
Det jag funderar på är automatisk generering av signaler.

Exempel:
Låt oss säga att jag har en generisk input av N bitar, det jag då vill är generiskt skapa antalet signaler som behövs för att addera par av bitar.
Dvs jag vill ha signaler, s1, s2, ..., sN/2 så s1 = input(0) + input(1), ... sN/2 = input(N-1) + input(N).

Går det att skapa signaler generiskt på detta vis eller har jag fel tänk här? :humm:
Det jag vill göra är bestämma antalet grejer att göra i parallel i stegen av en pipeline baserat på inputs storlek.

Edit: La in lite exempel kod.
Jag beräknar Hamming Distance och jag vill att alla mina beräkningar ska skala med N.
Så antalet steg i adder tree'et ska öka och att count_ones ska alltid arbeta i 8-bit chunks.

Kod: Markera allt

library ieee;
use ieee.std_logic_1164.all;        
use ieee.numeric_std.all;
use ieee.math_real.all;
use work.misc_functions.all;

entity hamming_distance is
    generic (
        N : positive := 64      -- N must be a multiple of 32.
    );
    port ( 
        clk    : in  std_logic;
        rst    : in  std_logic;
        inputA : in  std_logic_vector ((N - 1) downto 0);
        inputB : in  std_logic_vector ((N - 1) downto 0);
        output : out unsigned ((integer(ceil(log2(real(N)))) - 1) downto 0)
    );
end hamming_distance;

architecture Behavioral of hamming_distance is

    signal after_XOR : std_logic_vector ((N - 1) downto 0);
    signal after_c1 : integer;
    signal after_c2 : integer;
    signal after_c3 : integer;
    signal after_c4 : integer;
    signal after_c5 : integer;
    signal after_c6 : integer;
    signal after_c7 : integer;
    signal after_c8 : integer;
    signal after_add1 : integer;
    signal after_add2 : integer;
    signal after_add3 : integer;
    signal after_add4 : integer;
    signal after_add5 : integer;
    signal after_add6 : integer;
    signal output_stage : unsigned ((integer(ceil(log2(real(N)))) - 1) downto 0);

begin
    
    -- Calculate the Hamming distance between inputA and inputB.
    -- Pipeline: 5-stages.
    
    process (clk, rst) begin
        if rising_edge(clk) then
            if rst = '1' then
                after_XOR <= (others => '0');
                output_stage <= (others => '0');
                after_c1 <= 0;
                after_c2 <= 0;
                after_c3 <= 0;
                after_c4 <= 0;
                after_c5 <= 0;
                after_c6 <= 0;
                after_c7 <= 0;
                after_c8 <= 0;
                after_add1 <= 0;
                after_add2 <= 0;
                after_add3 <= 0;
                after_add4 <= 0;
                after_add5 <= 0;
                after_add6 <= 0;
			else
                -- Stage 1: XOR
                after_XOR <= inputA xor inputB;
                
                -- Stage 2: count ones in 8-bit chunks
                after_c1 <= count_ones(after_XOR(63 downto 56));
                after_c2 <= count_ones(after_XOR(55 downto 48)); 
                after_c3 <= count_ones(after_XOR(47 downto 40)); 
                after_c4 <= count_ones(after_XOR(39 downto 32)); 
                after_c5 <= count_ones(after_XOR(31 downto 24));
                after_c6 <= count_ones(after_XOR(23 downto 16)); 
                after_c7 <= count_ones(after_XOR(15 downto 8)); 
                after_c8 <= count_ones(after_XOR(7 downto 0)); 
                
                -- Stage 3: first level of adder tree
                after_add1 <= after_c1 + after_c2;
                after_add2 <= after_c3 + after_c4;
                after_add3 <= after_c5 + after_c6;
                after_add4 <= after_c7 + after_c8;
                
                -- Stage 4: second level of adder tree
                after_add5 <= after_add1 + after_add2;
                after_add6 <= after_add3 + after_add4;
                
                -- Stage 5: third level of adder tree
                output_stage <= to_unsigned(after_add5 + after_add6, output_stage'length);
			end if;
	    end if;
    end process;

    output <= output_stage;

end Behavioral;
Användarvisningsbild
Andax
Inlägg: 4373
Blev medlem: 4 juli 2005, 23:27:38
Ort: Jönköping

Re: Fråga om VHDL (Xilinx FPGA)

Inlägg av Andax »

Till viss del kan du lösa en del mha av variable istället för signal och sedan uttrycka beräkningen i en for-loop. Det blir visserligen inte pipeline uppdelat utan det blir ett expanderat logiskt utryck direkt. Kan vara svårt att få hög frekvens på.

Annars kan du ev använda GENERATE för att ha mer kod som funktionen av din generic N.
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: Fråga om VHDL (Xilinx FPGA)

Inlägg av Korken »

Tack för svaret!

Jag testade med generate, men problemet va att signaler som skapas i en generate function kan inte kommas åt utifrån.
Jag hittade fler som vill göra det jag vill också efter mer sökande, och ingen hittade en lösning - så jag tror jag gör en baskomponent med fixed width (typ 32 eller 64 bits), och sedan tar jag och gör den "stora" från dessa.

Det kommer ge korrekt flöde, men inte lika elegant som det annars kunde ha blivit. :)
extradrajven
Inlägg: 18
Blev medlem: 1 juni 2013, 18:07:48

Re: Fråga om VHDL (Xilinx FPGA)

Inlägg av extradrajven »

Hej, här är ett par tips.

Om det är viktigt att användaren följer kravet i kommentaren till genericen N i entity-deklarationen så kan du ju alltid låta parametern beskriva antaletet multiplar av 32, och sedan skapa en konstant som används av din kod.

Kod: Markera allt

constant M : integer := N*32
Ett alternativ är att sätta assert i en process för att varna användaren.


Port-siglanerna kan också använda

Kod: Markera allt

inputA : in  std_logic_vector (N*32 - 1 downto 0);
Deklarera en array-typ för signalerna. Då räcker det med en signal var för after_c och after_add. De individuella integer-signalerna refereras då genom after_c(i), after_add(j). osv.

Kod: Markera allt

type intarray_type is array (natural range <>) of integer;
signal after_c : intarray_type(1 to något som beror på N?)
Reseten blir då exempelvis:

Kod: Markera allt

after_c <= (others=>0);
De upprepade tilldelningarna till after_c blir:

Kod: Markera allt

proces ...
  variable low, high : integer;
  ...
begin
  ...
  for i in 0 to M loop
    low := (något som beror på i och M)
    high := (något som beror på i och M)
    after_c(i) <= count_ones(after_XOR(high downto low));
  end loop;
Tilldelningen till output_stage i slutet av processen kan också göras med for-loop om tilldelningen beror på genericen.

OBS. Dessa for-loopar genererar ingen extra hårdvara - de loopar på väden som är konstanta vid syntes och används bara för indexering.


EDIT:
Såg att jag inte läste ditt inlägg ordentligt.
Det bör gå att skriva koden skalbar med avseende på antalet adderarsteg också. Skiss: Räkna ut hur många adderarsteg som behövs beroende på genericen. Skriv en loop som iterar över antalet addersteg. För varje iteration i loopen så anropas en funktion som utför additionen. Funktionen tar lämpliga parametrar som skapar lämpligt antal additioner. Typ :-) Finns säkert andra sätt.
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: Fråga om VHDL (Xilinx FPGA)

Inlägg av Korken »

Tackar!!! Det tänkte jag inte på! :tumupp:

Jag ska testa detta ikväll och se om jag lyckas med något magiskt! :D
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: Fråga om VHDL (Xilinx FPGA)

Inlägg av Korken »

Tack för att du fick mig att tänka på ett nytt sätt! :D

Resultatet blev mycket snyggare kod och helt generisk.
Den lägger automatisk till fler steg i adder trädet för att allt ska stämma samt att den kollar med en assert att N är korrekt. :)

Det enda jag inte gillar är att när gen konveterar integers till HW så tycks den anta 32 bitar (inte vad som egentligen behövs), vilket gör att jag får ish 400 warnings om att den trimmar bort oanvända delar av signaler.
Skulle va skönt om man inte fick alla errors. :humm:

Kod: Markera allt

library ieee;
use ieee.std_logic_1164.all;        
use ieee.numeric_std.all;
use ieee.math_real.all;
use work.misc_functions.all;

entity hamming_distance is
    generic (
        N : positive := 64      -- N must be a multiple of 16.
    );
    
    port ( 
        clk    : in  std_logic;
        rst    : in  std_logic;
        inputA : in  std_logic_vector ((N - 1) downto 0);
        inputB : in  std_logic_vector ((N - 1) downto 0);
        output : out unsigned ((integer(ceil(log2(real(N))))) downto 0)
    );
end hamming_distance;

architecture Behavioral of hamming_distance is

    -- Bit for the bit counters to count
    constant BitsPerAdder : integer := 8;
    
    -- Calculate the number of bit counters needed
    constant NumBitCounters : integer := N / BitsPerAdder;
    
    -- Define the signal type for intermediate calculations
    type intarray_type is array (natural range <>) of integer;
    
    -- Temporary containers during the pipeline stages
    signal after_XOR   : std_logic_vector ((N - 1) downto 0);
    signal after_count : intarray_type(0 to NumBitCounters-1);
    signal after_add   : intarray_type(0 to NumBitCounters-2);
    
begin

    -- Error check.
    assert ((N mod (BitsPerAdder*2)) = 0)
        report "N in 'hamming_distance' must be a multiple of " & integer'image(BitsPerAdder*2) & "."
        severity failure;

    -- Calculate the Hamming distance between inputA and inputB.
    -- Pipeline: log2(N)-1 stages.
    
    process (clk, rst)
        variable low, high : integer;
    begin
        
        if rising_edge(clk) then
            if rst = '1' then
                after_XOR   <= (others => '0');
                after_count <= (others => 0);
                after_add   <= (others => 0);
            else
                --
                -- Stage 1: XOR
                --
                after_XOR <= inputA xor inputB;
                
                --
                -- Stage 2: count ones in 8-bit chunks
                --
                for i in 0 to (NumBitCounters-1) loop
                    high := 7+i*8;
                    low := i*8;
                    
                    after_count(i) <= count_ones(after_XOR(high downto low)); 
                end loop;
                
                --
                -- Stage 3: first level of adder tree, used to connect after_count and after_add
                --
                for i in 0 to (NumBitCounters/2-1) loop
                    after_add(i) <= after_count(2*i) + after_count(2*i+1);
                end loop;
                
                --
                -- Stage 4-END: final levels of adder tree
                --
                for i in (NumBitCounters/2) to (NumBitCounters-2) loop
                    after_add(i) <= after_add(i*2 - NumBitCounters) + after_add(i*2 - NumBitCounters + 1);
                end loop;
            end if;
        end if;
    end process;
    
    output <= to_unsigned(after_add(NumBitCounters-2), output'length);

end Behavioral;
Simulering där jag satte så varje 8-bit counter ska få 1, 2, 3, .., 8 ettor respektive (totalt 36 stycken) och simuleringen tycks hålla med:
sim_hamming_distance.png
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
extradrajven
Inlägg: 18
Blev medlem: 1 juni 2013, 18:07:48

Re: Fråga om VHDL (Xilinx FPGA)

Inlägg av extradrajven »

Bra lösning. Riktigt snyggt!

Om du inte behöver hela integer-intervallet kan du prova att skapa egna subtyper. Tror syntesverktyget blir lite lugnare då.

Kod: Markera allt

subtype mintyp is integer range 0 to B;
B kan vara en generic, en konstant, ett funktionsanrop med generic/konstanter som parametrar osv.
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: Fråga om VHDL (Xilinx FPGA)

Inlägg av Korken »

Tack för tipset! :D

Ändrade

Kod: Markera allt

type intarray_type is array (natural range <>) of integer;
till

Kod: Markera allt

subtype small_int is integer range 0 to (integer(ceil(log2(real(N))))) ;
type intarray_type is array (natural range <>) of small_int;
och nu så blev det inga errors alls!
Dock lite konstigt då de första stegen av beräkningen använder färre bitar än de senare :humm: , men att göra en hamming distance på en 256 bitars variabel tar ca 400 LUTs och kan göra i 250 MHz på en Artix-7 (-1).
Så ska nog inte klaga. :)

EDIT: Ops...

Kod: Markera allt

subtype small_int is integer range 0 to N ;
Ska det vara... Nu blev det errors som förut men bara 34st istället för 400. :)

EDIT 2: Detta va lite bättre, satte en storlek för after_count som jag vet storleken för, nu bara 6 errors. :)

Kod: Markera allt

-- Define the signal type for intermediate calculations
subtype count_int is integer range 0 to BitsPerAdder ;
type countarray_type is array (natural range <>) of count_int;
subtype small_int is integer range 0 to N ;
type intarray_type is array (natural range <>) of small_int;
EDIT 3:
Fixade lite med constraints och det ser ut som att max hastighet för designen ligger på ca 300 MHz vid 128-bit. :)
Så kan inte klaga på vad som genereras från massa loopar helt klart! Detta sätt att koda VHDL ska jag ta god vara på. :tumupp:
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: Fråga om VHDL (Xilinx FPGA)

Inlägg av Korken »

Har fixat en renskriven version för de som vill studera den :)
Constraints: N ska vara power of 2 annars blir det snett i pipelinen och större än 32.

Kod: Markera allt

library ieee;
use ieee.std_logic_1164.all;        
use ieee.numeric_std.all;
use ieee.math_real.all;
use work.misc_functions.all;

-------------------------------------------------------------------------------
-- Brief:     Calculates the Hamming distance between inputA and inputB.
--            Reset is active high and is synchronous.
--
-- Note:      Hamming Distance is the number of bits that are different in two
--            logic vectors.
--
-- Note:      N is the bit width of the input must be a power of 2  and at least
--            32. If less is needed set a power of 2 and let the synthesis
--            simplify, else the pipeline will be messed up.
--
-- Pipeline:  log2(N) - 1 stages.
--
-- Author:    Emil Fresk
-------------------------------------------------------------------------------

entity hamming_distance is
    generic (
        N : positive := 128     -- N must be a power of 2 and at least 32.
    );
    
    port ( 
        clk    : in  std_logic;
        rst    : in  std_logic;
        inputA : in  std_logic_vector ((N - 1) downto 0);
        inputB : in  std_logic_vector ((N - 1) downto 0);
        result : out integer range 0 to N
    );
end hamming_distance;


architecture Behavioral of hamming_distance is

    -- Number of bits for each BitAdder to count.
    constant BitsPerAdder : integer := 8;
    
    -- Calculate the number of bit counters needed.
    constant NumBitCounters : integer := N / BitsPerAdder;
    
    -- Define the signal type for intermediate calculations.
    subtype count_int is integer range 0 to BitsPerAdder ;
    type countarray_type is array (natural range <>) of count_int;

    subtype small_int is integer range 0 to N ;
    type intarray_type is array (natural range <>) of small_int;
    
    -- Temporary containers during the pipeline stages.
    signal after_XOR   : std_logic_vector ((N - 1) downto 0);
    signal after_count : countarray_type(0 to NumBitCounters-1);
    signal after_add   : intarray_type(0 to NumBitCounters-2);
    
begin

    -- Error check, N must be a power of 2 and at least 32.
    assert (((N/2) mod 2) = 0) and (N >= 32)
        report "N in 'hamming_distance' must be a power of 2 and at least 32."
        severity failure;

    ----------------------------------------------------------
    -- Process that implements the Hamming Distance pipeline.
    -- Depth of the pipeline will be log2(N) - 1 stages.
    ----------------------------------------------------------
    process (clk, rst)
        variable low, high : integer; -- Temporary variables
    begin
        
        if rising_edge(clk) then
            if rst = '1' then
                after_XOR   <= (others => '0');
                after_count <= (others => 0);
                after_add   <= (others => 0);
            else
                --
                -- Stage 1: XOR to get ones at all the bit differences.
                --
                after_XOR <= inputA xor inputB;
                
                --
                -- Stage 2: Count all ones from the XOR in 8-bit chunks.
                --
                for i in 0 to (NumBitCounters-1) loop
                    high := (BitsPerAdder - 1) + i*BitsPerAdder;
                    low  := i*BitsPerAdder;
                    
                    after_count(i) <= count_ones(after_XOR(high downto low)); 
                end loop;
                
                --
                -- Stage 3: First level of the adder tree, used to connect
                --           after_count and after_add.
                --
                for i in 0 to (NumBitCounters/2-1) loop
                    after_add(i) <= after_count(2*i) + after_count(2*i+1);
                end loop;
                
                --
                -- Stage 4-END: Final levels of the adder tree. Sums all
                --              calculations to produce the answer and adds
                --              new pipeline stages when necessary.
                --
                for i in (NumBitCounters/2) to (NumBitCounters-2) loop
                    after_add(i) <= after_add(2*i - NumBitCounters) +
                                    after_add(2*i - NumBitCounters + 1);
                end loop;
            end if;
        end if;
    end process;
    
    --
    -- Move the result to the output
    --
    result <= after_add(NumBitCounters-2);

end Behavioral;

Kod: Markera allt

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

package misc_functions is
    function count_ones(input_vector : std_logic_vector) return integer;
end package;

package body misc_functions is

    ---------------------------------------------------------------------------
    -- Brief:   Function for counting the number of ones in a vector.
    -- Input:   The vector to be counted.
    -- Output:  Integer with the number of ones.
    ---------------------------------------------------------------------------
    function count_ones(input_vector : std_logic_vector) return integer is
        variable temp : natural := 0;
    begin
        -- Loop over the size of the input vector and add 1 to temp if the
        -- bit is a one.
        for i in input_vector'range loop
            if input_vector(i) = '1' then
                temp := temp + 1; 
            end if;
        end loop;
      
        -- Return the result.
        return temp;

    end function count_ones;

end package body;
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: Fråga om VHDL (Xilinx FPGA)

Inlägg av Korken »

Hej igen! Då va det dags för nästa fråga! :)

Jag har under senaste tiden arbetat med att lära mig LVDS SERDES och, efter mycket slit, så kan jag dessa ganska bra nu.
Då kom nästa problem som jag skulle vilja ha lite vägledning i, så jag inte övertänker mitt problem och skapar dålig kod. :) Tack på förhand!!!

Problemet jag har är, hur vet jag att min utläsning är i sync med dataströmmen?

Exempel:
Datat kommer i paket om (bits): 1XXXXXXXXXX0 (start - 10-bit payload - stop)
Beroende på när LVDS klockan började och när jag släppte min reset så kan jag läsa ut paket med en godtycklig offset.

Så, någon form av sync logik måste skapas för att detektera att start och stop biten hittas för att synca allt..
Jag har en massa idéer för hur jag skulle kunna skapa detta, men vad är standard metoden? :humm:
Är det något speciellt man ska tänka på när man gör detta? Alla tips är välkomna! :)
extradrajven
Inlägg: 18
Blev medlem: 1 juni 2013, 18:07:48

Re: Fråga om VHDL (Xilinx FPGA)

Inlägg av extradrajven »

LVDS specar väl det fysiska lagret? Vilken kodning avses?
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: Fråga om VHDL (Xilinx FPGA)

Inlägg av Korken »

Jo precis, det är den fysiska biten.
Kodningen i mitt fall är som jag skrev i inlägget 1XXXXXXXXXX0 (start - 10-bit payload - stop) och jag har gjort ett gäng olika statemachines för att koda av detta samt att använda BITSLIP funktionaliteten av SERDES blocket för att synca datat.
Så detta gick ganska bra, har så jag kan välja de-serialization ratio och att koden med statemachine ändras automatiskt. Ska bara fixa min hårdvara för att göra lite verkliga tester nu. :)
Skriv svar