Implementing a VHDL binary search on a std_logic_v

2019-07-30 08:59发布

问题:

I'm attempting to create synthesizable VHDL (function or procedure) for an ASIC (it must be part of the ASIC) that will look for the first '1' in a standard_logic_vector and output which vector position that '1' was in. For example, I have an 8-bit slv of "10001000" (a '1' in position 3 and 7). If I use this slv, the output should be 4 (the output is 1 based).

The actual VHDL will be searching a large slv, up to 512 bits in length. I tried implementing a binary search function but I get synthesis errors that states "Could not synthesize non-constant range values. [CDFG-231] [elaborate] The non-constant range values are in file '...' on line 61" I indicated in the code below where it complains. I'm not sure how to implement a binary search algorithm without having non-constant range values. How would I modify this code so it's synthesizable?

I have attempted to search for binary search algorithms for HDL for potential code to look at and for my error, but I didn't find anything.

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

entity bin_search is
    generic (
        constant NREGS  : positive  := 16               -- number of registers
    );
    port (
        clk_i   : in    std_logic;                      -- clock
        bin_i   : in    unsigned( NREGS-1 downto 0 );   -- input
        en_i    : in    std_logic;                      -- input enable
        addr_o  : out   natural range 0 to NREGS        -- first binary location
    );
end bin_search;

architecture rtl of bin_search   is

  function f_bin_search( input: unsigned; nob: positive ) return natural is
        constant nbits  : positive                      := 2**nob;
        variable lower  : natural   range 0 to 1        := 0;
        variable upper  : natural   range 0 to 1        := 0;
        variable idx    : natural   range 0 to nob      := 4;
        variable cnt    : natural   range 0 to nbits    := 0;
        variable mid    : positive  range 1 to nbits    := nbits/2; --
        variable ll     : natural   range 0 to nbits    := 0;
        variable ul     : positive  range 1 to nbits    := nbits;   -- 
    begin
        if input = 0 then
            cnt := 0;
            return cnt;
        else
            loop1: while ( idx > 0 ) loop
                if ( input( mid-1 downto ll ) > 0 ) then --  <===WHERE SYNTH COMPLAINS
                    lower   := 1;
                else
                    lower   := 0;
                end if;
                if ( input( ul-1 downto mid ) > 0 ) then
                    upper   := 1;
                else
                    upper   := 0;
                end if;
                if ( idx = 1 ) then
                    if ( lower = 1 ) then
                        cnt := mid;
                    else
                        cnt := ul;
                    end if;
                elsif ( lower = 1 ) then
                    ul  := mid;
                    mid := ( ( ll+ul )/2 );
                elsif ( upper = 1 ) then
                    ll  := mid;
                    mid := ( ll+ul )/2;
                else
                    cnt := 0;
                    exit loop1;
                end if;
                idx := idx-1;
            end loop loop1;
            return cnt;
        end if;
    end f_bin_search;

begin

    test_proc: process ( clk_i )
    begin
        if rising_edge( clk_i ) then
            if en_i = '1' then
                addr_o  <= f_bin_search( bin_i, 4 );
            end if;
        end if;
    end process test_proc;
end rtl;

Here's a simple test bench where the input is inc'd by '1'. The addr_o should be the location (1 based) of the input lsb with a '1'.

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

entity bin_search_tb is
end bin_search_tb;

architecture behavior of bin_search_tb is

    constant    NREGS   : positive  := 16;

    signal      clk     : std_logic;
    signal      input   : unsigned( NREGS-1 downto 0 );
    signal      start   : std_logic;
    signal      addr    : natural range 0 to NREGS;

    constant    clk_per : time  := 1 ns;
    signal      row     : natural range 0 to 2**NREGS-1;

begin

    bin_search_inst: entity work.bin_search( rtl )
    generic map (
        NREGS   => NREGS
    )
    port map (
        clk_i   => clk,     -- master clock
        bin_i   => input,   -- captured events
        en_i    => start,   -- start binary search
        addr_o  => addr     -- addr where the first '1' appears
    );

    -- master clock process
    clk_proc: process
    begin
        clk <= '0';
        wait for clk_per / 2;
        clk <= '1';
        wait for clk_per / 2;
    end process clk_proc;

    -- 
    stim1_proc: process
    begin
        input   <= ( others => '0' );
        start   <= '0';
        row     <= 1;   
        wait until clk'event and clk = '1';
        loop
            wait until clk'event and clk = '1';
            input   <= to_unsigned( row, input'length );
            start   <= '1';
            wait until clk'event and clk = '1';
            start   <= '0';
            wait for 4*clk_per;
            row <= row+1;
        end loop;   
    end process stim1_proc;

end architecture behavior;

Thanks for your assistance! -Jason

Edited code and added a testbench

回答1:

Your design will most certainly depend on latency and other performance requirements, but, you could use some combination of or-reduction, sequencers (for mux selection of sliced vectors), shift register, and counters. I drew up a simple circuit that should find your lsb instance of "1" in ~30 clock cycles

The RTL translation that implements this design should be straight forward.



回答2:

You say that you are thinking in hardware, but in fact you're not. Or you are misleading yourself.

input( mid-1 downto ll ) > 0

is not an OR-reduction, but a comparison operation. You must know > is the larger than comparison operator. The synthesis will therefor infer a comparator. But how many inputs must that comparator have, I ask? Well, there's your problem: it depends on the value of mid, which:

  • initially depends on the value of nbits, which depends on the value of nob which is a variable input for the function.
  • is changed within the loop. Thus it's value is not constant.

A hardware component cannot have a variable amount of wires.

But why do you want binary search? Why not keep-it-simple?

library ieee;
use ieee.std_logic_1164.all;

entity detect_one is
    generic(
        input_size : positive := 512);
    port(
        input : in std_logic_vector (input_size-1 downto 0);
        output : out natural range 0 to input_size);
end entity;

architecture rtl of detect_one is
begin
    main: process(input)
    begin
        output <= 0;
        for i in input_size-1 downto 0 loop
            if input(i)='1' then
                output <= i+1;
            end if;
        end loop;
    end process;
end architecture;


entity detect_one_tb is end entity;
library ieee;
architecture behavior of detect_one_tb is
    constant input_size : positive := 512;
    use ieee.std_logic_1164.all;
    signal input : std_logic_vector (input_size-1 downto 0) := (others => '0');
    signal output : integer;
begin
    DUT : entity work.detect_one
        generic map ( input_size => input_size )
        port map(
            input => input,
            output => output);

    test: process begin
        wait for 1 ns;
        assert (output = 0) report "initial test failure" severity warning;
        for i in 0 to input_size-1 loop
            input <= (others => '0');
            input(i) <= '1';
            wait for 1 ns;
            assert (output = i+1) report "single ones test failure" severity warning;
        end loop;
        input <= (others => '1');
        wait for 1 ns;
        assert (output = 1) report "initial multiple ones test failure" severity warning;
        for i in 0 to input_size-2 loop
            input(i) <= '0';
            wait for 1 ns;
            assert (output = i+2) report "multiple ones test failure" severity warning;
        end loop;
        wait;
    end process;
end architecture;