0
votes

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.

2
Your two declarations involve globally static values meaning the calculations are done at elaboration of the design hierarchy. Why not make constant len a generic? Calculate only once in the generic interface and use len for the port array bound. You could also hide the len calculation in a function call, the function declared in a convenient package just like ceil or log2. - user1155120
I wanted to give only one generic value, so the upper hierarchy don't have to calculate the lenght, which can cause errors. Or do you mean to define the constant just below the polynom generic? GHDL throws me an error, "polynom" is not visible here - Franz Forstmayr
The algorithm to get from polynom to len is still not clear. Could you describe that more specifically? - JHBonarius
Use ghdl's -08 flag (ghdl -a --std=08 crc_par_n.vhdl) to make polynom visible in generic. It's a -2008 feature and required to be used where elaboration occurs (commands -e for gcc/llvm or -r for mcode) . You could also pass len as a natural and do the calculation where crc_n_par is instantiated which will work -1993 and later. Ask your self how the place instantiating crc_n_par knows the length of the output port, needed to declare an actual signal, used during elaboration associating an actual signal and formal port. - user1155120
@JHBonarius The log2 needs an real input, thats why i cast the polynom to unsigned and after that to real. log2(0b000100111) = 5,28, ceil increases to 6 (bits) My output length should be 1 less, so 5 bit. When defining a vector range, i have to specify (4 downto 0) - Franz Forstmayr

2 Answers

1
votes

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.

0
votes

First off: your question is unclear. You say you want to determine the length of a vector without leading zeroes. 100111 has no leading zeros, so the length is 6. And it is also not clear how you go from a length of 4 to 5 bytes.

Well, it depends on your specific implementation what would be an optimal approach. I would use a function in a package. I.e.

library ieee;
use ieee.std_logic_1164.all;

package my_functions is
    function det_output_len(polynom:std_logic_vector) return positive;
end package;

library ieee;
use ieee.numeric_std.all;
use ieee.math_real.all;

package body my_functions is
    function det_output_len(polynom:std_logic_vector) return positive is
    begin
        return integer(ceil(log2(real(to_integer(unsigned(polynom))))))-1;
    end function;
end package body;

Then in your entity:

use work.my_functions.all;

entity CRC_n_par is
    [...]
port(
    [...]
    output  : out   std_logic_vector(det_output_len(polynom)-1 downto 0) );
end entity;

As for constant 'len', you don't need to recalculate the function. If the length has already been set for the output vector, you can just get is using VHDL attributes (old site, not up-to-date on VHDL-2008). I.e.

constant len : positive := output'length;

By the way, this is not a VHDL-2008 specific thing, as it is supported in VHDL for some time.