Search code examples
vhdl

VHDL 2008 calculate length of vector without leading zeros


i'm writing a small vhdl module, but i'm quite new to this language. The length of the output vector of my entity is variable, depending on the msb of my polynom. For example if i have a polynom like 100111, len should be 4. So the output vector has 5 bits. One less than the input vector.

For clarification: If the inputvector looks like "000100111", the output vector should also be (4 downto 0). That's why i just can't use 'length.

entity CRC_n_par is
generic(
    polynom     : std_logic_vector
    );
port(
    clk     : in    std_logic;
    input   : in    std_logic;
    reset   : in    std_logic;
    output  : out   std_logic_vector(integer(ceil(log2(real(to_integer(unsigned(polynom))))))-2 downto 0) );
end entity;
architecture Behavioral of CRC_n_par is

    constant len : integer := integer(ceil(log2(real(to_integer(unsigned(polynom))))))-2;

Is there any way, to do this more elegant. Here's a similar question, but i cannot use a function in the port declaration. After the port declaration, i use the constant len again. Is there a way to use a c-like macro in vhdl? I'm using vhdl 2008, that's why the conversion is so complicated, because i can only convert between closely related types.


Solution

  • The length is also needed were crc_n_par is instantiated:

    library ieee;
    use ieee.std_logic_1164.all;
    
    entity crc_n_par is
        generic (
            len:           natural
        );
        port (
            clk:      in    std_logic;
            input:    in    std_logic;
            reset:    in    std_logic;
            output:   out   std_logic_vector (len - 1 downto 0) 
        );
    end entity;
    architecture Behavioral of crc_n_par is
    begin
    MONITOR:
        process
        begin
            report "crc_n_par output len = " & integer'image(len);
            wait;
        end process;
    end architecture;
    
    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    use ieee.math_real.all;
    
    entity some_top_level is
    end entity;
    
    architecture foo of some_top_level is
        -- For -2002 and earlier, present in -2008:
        function to_string (inp: std_logic_vector) return string is
            variable image_str: string (1 to inp'length);
            alias input_str:  std_logic_vector (1 to inp'length) is inp;
        begin
            for i in input_str'range loop
                image_str(i) := character'VALUE(std_ulogic'IMAGE(input_str(i)));
            end loop;
            return image_str;
        end function;
        constant polynom: std_logic_vector := "000100111";
        constant crc_inst_len : natural := 
            integer(ceil(log2(real(to_integer(unsigned(polynom))))))-1;
        signal clk:     std_logic;
        signal input:   std_logic;
        signal reset:   std_logic;
        signal output:  std_logic_vector (crc_inst_len - 1 downto 0);
    begin
    MONITOR:
        process
        begin
            report LF & "polynom len = " & integer'image(polynom'length) &
                   " crc_inst_len = " & integer'image(crc_inst_len) & LF &
                   " output length = " & integer'image(output'length) & 
                   " polynom = " & to_string(polynom);
            wait;
        end process;
    CRC_INSTANCE:
        entity work.crc_n_par 
            generic map (
                len => crc_inst_len
            )
            port map (
                clk => clk,
                input => input,
                reset => reset,
                output => output
            );
    
    end architecture;
    

    This moves the length calculation to a higher point in the design hierarchy allowing the actual used for the port output to be declared with the proper length as well.

    When analyzed, elaborated and simulated it produces:

    ghdl -a some_top_level.vhdl
    ghdl -e some_top_level
    ghdl -r some_top_level
    some_top_level.vhdl:20:9:@0ms:(report note): crc_n_par output len = 5
    some_top_level.vhdl:55:9:@0ms:(report note):
    polynom len = 9 crc_inst_len = 5
     output length = 5 polynom = 000100111
    

    You can independently calculate the length two places:

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    use ieee.math_real.all;
    
    entity crc_n_par is
        generic (
            polynom:       std_logic_vector;
            len:           natural :=  -- requires -2008 to access polynom
                integer(ceil(log2(real(to_integer(unsigned(polynom)))))) - 1
            );
        port (
            clk:      in    std_logic;
            input:    in    std_logic;
            reset:    in    std_logic;
            output:   out   std_logic_vector (len - 1 downto 0) 
        );
    end entity;
    architecture Behavioral of crc_n_par is
    begin
    MONITOR:
        process
        begin
            report "len = " & integer'image(len);
            wait;
        end process;
    end architecture;
    
    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    use ieee.math_real.all;
    
    entity some_top_level is
    end entity;
    
    architecture foo of some_top_level is
        constant polynom: std_logic_vector := "000100111";
        constant crc_inst_len : natural := 
            integer(ceil(log2(real(to_integer(unsigned(polynom))))))-1;
        signal clk:     std_logic;
        signal input:   std_logic;
        signal reset:   std_logic;
        signal output:  std_logic_vector (crc_inst_len - 1 downto 0);
    begin
    MONITOR:
        process
        begin
            report LF & "polynom len = " & integer'image(polynom'length) &
                   " crc_inst_len = " & integer'image(crc_inst_len) & LF &
                   " output length = " & integer'image(output'length) & 
                   " polynom = " & to_string(polynom);
            wait;
        end process;
    CRC_INSTANCE:
        entity work.crc_n_par 
            generic map (
                polynom => polynom
                -- don't pass len
            )
            port map (
                clk => clk,
                input => input,
                reset => reset,
                output => output
            );
    end architecture;
    

    But you can see that's unnecessary duplication and requires -2008 to analyze and elaborate:

    ghdl -a --std=08 crc_n_par.vhdl
    ghdl -e --std=08 crc_n_par
    ghdl -r --std=08 some_top_level
    some_top_level.vhdl:20:9:@0ms:(report note): crc_n_par output len = 5
    some_top_level.vhdl:55:9:@0ms:(report note):
    polynom len = 9 crc_inst_len = 5
     output length = 5 polynom = 000100111
    

    Note the - 1 in calculating polynom length matches your question title:

    VHDL 2008 calculate length of vector without leading zeros

    And this is the purpose of using conversion of the array value to an integer and determining it's log2 ceiling and subtracting one.