1

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

jtw71
  • 13
  • 5
  • Show the actual complete error message(s). Also provide a [Minimal, Complete and Verifiable example](https://stackoverflow.com/help/mcve) - your code snippet is missing a subprogram name and you can't assign decimal literals as initial values to objects of type std_logic. Your synthesis tool's documentation should relate synthesis eligibility guidelines as well corrective actions for particular error messages. There are more issues than your one noted error. –  Oct 20 '17 at 20:15
  • My bad. I have to had type this as my work computer doesn't have internet access (no copy/paste). I corrected the code (I think I found them all). It behaviorally simulates find, but when I synthesis using Cadence's Genus v16 I get "Error : Could not synthesize non-constant range values. [CDFG-231] [elaborate] The non-constant range values are in file .... on line ... (it points to the or_reduce line. – jtw71 Oct 20 '17 at 20:44
  • Unfortunately, everything I can find in Cadence's documentation related to that error is just that I can't use non-constant ranges without any direction. If you've ever used Cadence software, you know it's not the best :(. So for some reason, the tool is unable to rollout the ranges it seems. I'm hoping there's a better way to do a binary search rather than having to type out all the possible ranges. – jtw71 Oct 20 '17 at 20:46
  • Thanks for the comments. I'll give your advice some thought. I'm also thinking about just using barrel right only shifter since I think I can use fixed indexing with that. – jtw71 Oct 21 '17 at 22:01
  • If you were to examine IEEE Std 1076.6-2004 (withdrawn, RTL Synthesis) 8.8.9 Loop statement you'd find that while loops with while iteration schemes weren't supported and 8.6.5 Slice names - *For a discrete range that appears as part of a slice name, the bounds of the discrete range shall be specified directly or indirectly as static values belonging to an integer type.* The idea that having a range of bounds matching the range of the integer bounds requires multiplexers (selectors) with every possible width and set of boundaries to express a slice which can easily be impractical. –  Oct 22 '17 at 18:57
  • Your problem is that you're not thinking hardware. What does the proposed implementation of your code look like? Are you really expecting Genus to produce a vast looping *unclocked* binary chop for you? How? Hint: look up priority encoders. – EML Oct 23 '17 at 08:13
  • Hi EML - I am thinking hardware. Perhaps there's a disconnect between what I'm thinking and what the synthesizer is thinking. And yes, I was expecting Genus to be able to figure out the looping. It seems to have nothing to do with the looping complexity. I've tried a simple case of looping and Genus still complained. For example, if 'mid' is either 4 or 2, Genus complains about non-constant range for if ( tmp( mid-1 downto 0 ) = 0 ) then.... I'm in the beginning stages of trying to figure out how many iterations aka looping the design can do within a single clock cycle. – jtw71 Oct 23 '17 at 14:07

2 Answers2

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

enter image description here

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

CapnJJ
  • 151
  • 5
0

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;
JHBonarius
  • 10,824
  • 3
  • 22
  • 41