Shift register delay generator error

Status
Not open for further replies.

shaiko

Advanced Member level 5
Joined
Aug 20, 2011
Messages
2,644
Helped
303
Reputation
608
Reaction score
297
Trophy points
1,363
Activity points
18,302
I described a delay generator based on a shift register.

CLOCK - Global clock.
RESET - Global reset.
"INPUT" - Input signal to apply the delay on.
"DELAY_DURATION" - Desired time (in nano seconds) to delay the input.
"OUTPUT" - The "INPUT" signal delayed by "DELAY_DURATION" nanoseconds.
"FREQUENCY" - The frequency of the global clock that drives the design.

My code:

Code:
library ieee ;
use ieee.std_logic_1164.all ;
use ieee.numeric_std.all ;
library work ;
use work.package_functions_general.all ;
		
entity delay_generator is

generic 
(
  FREQUENCY : positive := 50000000
) ;                                                                            
															                   
port                                                                           
(							                                                   
  CLOCK : in std_logic ;            
  INPUT : in std_logic ; 		
  RESET : in std_logic ;            
  DELAY_DURATION : in unsigned ;             
  OUTPUT : out std_logic		       
);                                       
	   
end entity delay_generator ;

architecture synthesizable_generator_delay of delay_generator is

constant width_unsigned_frequency : positive := log_2_decimal ( FREQUENCY ) ;	
constant unsigned_frequency : unsigned := to_unsigned ( FREQUENCY , width_unsigned_frequency ) ;
constant maximum_possible_delay_duration : unsigned ( DELAY_DURATION ' range ) := ( others => '1' ) ;  
constant maximum_possible_cycles_to_delay : unsigned := cylces_from_time ( maximum_possible_delay_duration , "ns" , unsigned_frequency ) ;
constant shift_register_width : positive := to_integer ( maximum_possible_cycles_to_delay ) ;
signal shift_register : unsigned ( shift_register_width - 1 downto 0 ) := ( others => '0' ) ;
signal required_number_of_cycles : unsigned ( 31 downto 0 ) := ( others => '0' ) ;

begin
					
  required_number_of_cycles <= cylces_from_time ( DELAY_DURATION , "ns" , unsigned_frequency ) ;
  [COLOR="#FF0000"]OUTPUT <= shift_register ( to_integer ( required_number_of_cycles ) ) ;[/COLOR]
  
  shifting_left : process ( RESET , CLOCK ) is 
  begin		
    if RESET = '1' then
      shift_register <= ( others => '0' ) ;
    elsif rising_edge ( CLOCK ) then
      shift_register <= shift_register ( shift_register ' high - 1 downto 0 ) & INPUT ; 
    end if ;
  end process shifting_left ;  							
	
end architecture synthesizable_generator_delay ;


At my Modelsim TB I drive DELAY_DURATION port with the value: "000000010011100010000" (decimal 10000) and press the run key.

Simulation terminates immediately with a fatal error pointing to the line I marked in red:
** Fatal: (vsim-3734) Index value 2097151 is out of range 104856 downto 0.
This suggests that the signal "required_number_of_cycles" is supposedly 2097151 . Looking at the waveform window indeed shows that the signal is given this value (when it should've been given "000000000000111110100" (500 decimal).

Strange enough, when I omit the erroneous line and rerun the simulation
Code:
--OUTPUT <= shift_register ( to_integer ( required_number_of_cycles ) ) ;
"required_number_of_cycles" becomes "000000000000111110100" (500 decimal) as it should!

Any ideas?
 

we need package_functions_general.all to compile the code

Wouldn't it be better to use log2() function from math_real library then create ist's own version ?

Also you need to take into account that if DELAY_DURATION port is not driven by a constant you will end up with very unsythesable code for higher f_clk frequency. As synthezer will create shift registers on DFF and not on SR modules, each DFF output will be connected to OUTPUT port via mux driven by DELAY_DURATION control signal.
 
Last edited:

Thanks for the reply.

Wouldn't it be better to use log2() function from math_real library then create ist's own version ?
I don't see why. My log2 is thoroughly tested and works fine.

Also you need to take into account that if DELAY_DURATION port is not driven by a constant you will end up with very unsythesable code.
I disagree. Driving "DELAY_DURATION" with a variable ( not a constant) may result in the implementation of very slow combinatorial logic - yet it's perfectly synthesizable. Anyways, this is out of the scope for this post.

we need package_functions_general.all to compile the code
sure.

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




package package_functions_general is



function log_2_decimal ( d : natural ) return natural ;	-- Gives the bit number of the MSB ( for example 230 in decimal is 11100110	in binary. So the function will return the number: 7. 		



function restoring_divide 
( 
numerator : unsigned ; 
denominator : unsigned 
) 
return unsigned ;	

	

function time_period_from_frequency -- Calculates the time period in "time_units" of "frequency_hz". 	
( 
frequency_hz : unsigned ; 
time_units : string 
) 
return unsigned ;					

	
	
function cylces_from_time -- Calculates the number of cycles that are needed for "desired_time" in "time_units" to pass when the clock frequency is "frequency_hz".
( 
desired_time : unsigned ; 
time_units : string ; 
frequency_hz : unsigned 
) 
return unsigned ;	


end package package_functions_general ;






package body package_functions_general is

function restoring_divide 
( 
numerator : unsigned ; 
denominator : unsigned 
) 
return unsigned is
variable temp_numerator : unsigned ( numerator ' range ) := numerator ;
variable temp_numerator_with_denominator_length : unsigned ( denominator ' range ) := ( others => '0' ) ;	
variable temp_denominator : unsigned ( denominator ' range ) := denominator ;
variable temp_denominator_with_numerator_length : 	unsigned ( numerator ' range ) := ( others => '0' ) ;	
variable temp_remainder : unsigned ( denominator ' length downto 0 ) := ( others => '0' ) ;
variable temp_remainder_with_numerator_length : unsigned ( numerator ' length downto 0 ) := ( others => '0' ) ;	
begin
  if numerator ' length = denominator ' length then 
    for index in 0 to denominator ' length - 1 
    loop
      temp_remainder ( denominator ' length - 1 downto 1 ) := 
      temp_remainder ( denominator ' length - 2 downto 0 ) ;
      temp_remainder ( 0 ) := 
      temp_numerator ( numerator ' length - 1 ) ;
      temp_numerator ( numerator ' length - 1 downto 1 ) := 
      temp_numerator ( numerator ' length - 2 downto 0 ) ;
      temp_remainder := temp_remainder - temp_denominator ;
      if ( temp_remainder ( denominator ' length ) = '1' ) then
        temp_numerator ( 0 ) := '0' ;
        temp_remainder := temp_remainder + temp_denominator ;
      else
        temp_numerator ( 0 ) := '1' ;
      end if ;
    end loop ;
    return temp_numerator ;
  elsif numerator ' length > denominator ' length then 
    temp_denominator_with_numerator_length := resize ( denominator , numerator ' length ) ;	
    for index in 0 to temp_denominator_with_numerator_length ' length - 1 
    loop
      temp_remainder_with_numerator_length ( temp_denominator_with_numerator_length ' length - 1 downto 1 ) := 
      temp_remainder_with_numerator_length ( temp_denominator_with_numerator_length ' length - 2 downto 0 ) ;
      temp_remainder_with_numerator_length ( 0 ) := temp_numerator ( numerator ' length - 1 ) ;
      temp_numerator ( numerator ' length - 1 downto 1 ) := temp_numerator ( numerator ' length - 2 downto 0 ) ;
      temp_remainder_with_numerator_length := temp_remainder_with_numerator_length - temp_denominator ;
      if ( temp_remainder_with_numerator_length ( temp_denominator_with_numerator_length ' length ) = '1' ) then
        temp_numerator ( 0 ) := '0' ;
        temp_remainder_with_numerator_length := temp_remainder_with_numerator_length + temp_denominator ;
      else
        temp_numerator ( 0 ) := '1' ;
      end if ;
      end loop ;
    return temp_numerator ;			
  else 
    temp_numerator_with_denominator_length := resize ( numerator , denominator ' length ) ;	
    for index in 0 to denominator ' length - 1 
    loop
      temp_remainder ( denominator ' length - 1 downto 1 ) := temp_remainder ( denominator ' length - 2 downto 0 ) ;
      temp_remainder ( 0 ) := temp_numerator_with_denominator_length ( temp_numerator_with_denominator_length ' length - 1 ) ;
      temp_numerator_with_denominator_length ( temp_numerator_with_denominator_length ' length - 1 downto 1 ) := 
      temp_numerator_with_denominator_length ( temp_numerator_with_denominator_length ' length - 2 downto 0 ) ;
      temp_remainder := temp_remainder - temp_denominator ;
      if ( temp_remainder ( denominator ' length ) = '1' ) then
        temp_numerator_with_denominator_length ( 0 ) := '0' ;
        temp_remainder := temp_remainder + temp_denominator ;
      else
        temp_numerator_with_denominator_length ( 0 ) := '1' ;
      end if ;
    end loop ;
    return temp_numerator_with_denominator_length ;
  end if ;			
end function restoring_divide ;  



function log_2_decimal 
( 
d : natural 
) 
return natural is
variable temp_d : natural := d ;
variable n : natural := 0 ;
begin
  while temp_d > 1 
  loop
    temp_d := temp_d / 2 ;
    n := n + 1 ;
  end loop ;
  if d > 2 ** n then
    return ( n + 1 ) ; 
  else
    return ( n ) ; 
  end if ;
end function log_2_decimal ;

	
	
function time_period_from_frequency 
( 
frequency_hz : unsigned ; 
time_units : string 
) 
return unsigned is
variable second_ms : unsigned ( 9 downto 0 ) := "1111101000" ; -- 1,000 ms is 1 second.
variable second_us : unsigned ( 19 downto 0 ) := "11110100001001000000" ; -- 1,000,000 us is 1 second.
variable second_ns : unsigned ( 31 downto 0 ) := "00111011100110101100101000000000" ; -- 1,000,000,000 ns is 1 second.
variable result_ms : unsigned ( 9 downto 0 ) := ( others => '0' ) ;
variable result_us : unsigned ( 19 downto 0 ) := ( others => '0' ) ;
variable result_ns : unsigned ( 31 downto 0 ) := ( others => '0' ) ;
begin 
  if time_units = "ms" then 
    result_ms := restoring_divide ( second_ms , frequency_hz ) ;
    return result_ms ;
  elsif time_units = "us" then 
    result_us := restoring_divide ( second_us , frequency_hz ) ;
    return result_us ;
  elsif time_units = "ns" then 
    result_ns := restoring_divide ( second_ns , frequency_hz ) ;
    return result_ns ;
  end if ;	
end function time_period_from_frequency ;		

	
	
function cylces_from_time -- Calculates the number of cycles that are needed for "desired_time" in "time_units" to pass when the clock frequency is "frequency_hz".
( 
desired_time : unsigned ; 
time_units : string ; 
frequency_hz : unsigned 
) 
return unsigned is	
variable result_ms : unsigned ( 9 downto 0 ) := ( others => '0' ) ;
variable result_us : unsigned ( 19 downto 0 ) := ( others => '0' ) ;
variable result_ns : unsigned ( 31 downto 0 ) := ( others => '0' ) ;
variable result_ps : unsigned ( 39 downto 0 ) := ( others => '0' ) ;
variable result_fs : unsigned ( 49 downto 0 ) := ( others => '0' ) ;
begin
if time_units = "ms" then 
  result_ms := restoring_divide ( desired_time , time_period_from_frequency ( frequency_hz , time_units ) ) ;
  return result_ms ;
elsif time_units = "us" then 
  result_us := restoring_divide ( desired_time , time_period_from_frequency ( frequency_hz , time_units ) ) ;
  return result_us ;
elsif time_units = "ns" then 
  result_ns := restoring_divide ( desired_time , time_period_from_frequency ( frequency_hz , time_units ) ) ;
  return result_ns ;
elsif time_units = "ps" then 
  result_ps := restoring_divide ( desired_time , time_period_from_frequency ( frequency_hz , time_units ) ) ;
  return result_ps ;
elsif time_units = "fs" then 
  result_fs := restoring_divide ( desired_time , time_period_from_frequency ( frequency_hz , time_units ) ) ;
  return result_fs ;
end if ;	
end function cylces_from_time ;		
	


end package body package_functions_general ;
 

I don't see why. My log2 is thoroughly tested and works fine.

It's just reinventing the wheel situation

Again in Active-HDL with stimulus:

Code:
  signal CLOCK,INPUT,RESET,OUTPUT : std_logic := '1';
  signal DELAY_DURATION : unsigned(20 downto 0) := to_unsigned(0,21);
  
begin
	DELAY_DURATION <= to_unsigned(10000,21);
  CLOCK <= not CLOCK after 10 ns;
  RESET <= '0' after 5 ns;
  
  INPUT <= '0' after 30 ns;

works well.


I disagree. Driving "DELAY_DURATION" with a variable ( not a constant) may result in the implementation of very slow combinatorial logic - yet it's perfectly synthesizable. Anyways, this is out of the scope for this post.

On 2nd biggest Cyclone V with 21-bit delay_duraration, Quartus try to synthesie the code now for 16 minutes (46%) and still counting the time



update

On modelsim 10.1 also no problems
 
Last edited:
Reactions: shaiko

    shaiko

    Points: 2
    Helpful Answer Positive Rating
works well.
Strange. I use Modelsim and it fails...

On 2nd biggest Cyclone V with 21-bit delay_duraration, Quartus try to synthesie the code now for 16 minutes (46%) and still counting the time.
There's nothing un-synthesizable in nature with any of the functions in the package.

How did you constraint "DELAY_DURATION" ?
 

DELAY_DURATION is 21 -bit variable.

I used now Modelsim ALTERA STARTER EDITION 10.1d still ok
 
Reactions: shaiko

    shaiko

    Points: 2
    Helpful Answer Positive Rating
21 bits will require >100K DFFs...try limiting it to 16 bits for example. It should work.

I used now Modelsim ALTERA STARTER EDITION 10.1d still ok
can you please post your TB ?
I'll try to retest it with your code...
 

Code:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;



entity eda is
 
end entity eda;

architecture BEHAVE of eda is




component delay_generator is

generic 
(
  FREQUENCY : positive := 50000000
) ;                                                                            
															                   
port                                                                           
(							                                                   
  CLOCK : in std_logic ;            
  INPUT : in std_logic ; 		
  RESET : in std_logic ;            
  DELAY_DURATION : in unsigned ;             
  OUTPUT : out std_logic		       
);                                       
	   
end component delay_generator ;
-----------------------------------------------------
  signal CLOCK,INPUT,RESET,OUTPUT : std_logic := '1';
  signal DELAY_DURATION : unsigned(20 downto 0) := to_unsigned(0,21);
  
begin
	DELAY_DURATION <= to_unsigned(10000,21);
  CLOCK <= not CLOCK after 10 ns;
  RESET <= '0' after 5 ns;
  
  INPUT <= '0' after 30 ns;
  
  DD: delay_generator port map (
    CLOCK => CLOCK,
  	INPUT => INPUT,
	RESET => RESET,
	DELAY_DURATION => DELAY_DURATION,
	OUTPUT => OUTPUT
	);




end architecture BEHAVE;
 
Reactions: shaiko

    shaiko

    Points: 2
    Helpful Answer Positive Rating
Works.
checking for deltas...

- - - Updated - - -

Code:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;



entity eda is
 
end entity eda;

architecture BEHAVE of eda is




component delay_generator is

generic 
(
  FREQUENCY : positive := 50000000
) ;                                                                            
															                   
port                                                                           
(							                                                   
  CLOCK : in std_logic ;            
  INPUT : in std_logic ; 		
  RESET : in std_logic ;            
  DELAY_DURATION : in unsigned ;             
  OUTPUT : out std_logic		       
);                                       
	   
end component delay_generator ;
-----------------------------------------------------
  signal CLOCK,INPUT,RESET,OUTPUT : std_logic := '1';
  signal DELAY_DURATION : unsigned(20 downto 0) [COLOR="#FF0000"]:= to_unsigned(0,21)[/COLOR];
  
begin
	DELAY_DURATION <= to_unsigned(10000,21);
  CLOCK <= not CLOCK after 10 ns;
  RESET <= '0' after 5 ns;
  
  INPUT <= '0' after 30 ns;
  
  DD: delay_generator port map (
    CLOCK => CLOCK,
  	INPUT => INPUT,
	RESET => RESET,
	DELAY_DURATION => DELAY_DURATION,
	OUTPUT => OUTPUT
	);




end architecture BEHAVE;

Please remove the line I marked in red.
Eliminating the pre-assignment during signal declaration, leaving only the runtime driver.

What do you get?
 

not working , because
# RUNTIME: Fatal Error: RUNTIME_0047 delay_generator2.vhd (38): Index 104857 out of range (104856 downto 0).

Modelsim initialize unintialize vectors with maximum number. Since
signal shift_register : unsigned ( shift_register_width - 1 downto 0 ) := ( others => '0' ) ; and
required_number_of_cycles <= cylces_from_time ( DELAY_DURATION , "ns" , unsigned_frequency ) ; is w/o -1 u got this error as required_number_of_cycles is 1 more then shift_register_width
 
Reactions: shaiko

    shaiko

    Points: 2
    Helpful Answer Positive Rating
Please elaborate.

Signal "shift_register" IS defined with a "-1". This is done durnig compilation time not runtime...
Please note that the width of "shift_register" is set with the help of a constant named "maximum_possible_cycles_to_delay".

The only role "DURATION_DELAY" plays during compilation is setting the uncontrained boundaries of "shift_register".

Even if Modelsim does assume that "DURATION_DELAY" is a vector of 21 logic ones this shouldn't cause an error because it's a perfectly legal value for it...

Also,
Your error message isn't the same as mine. Please review post #1.
 

when u define DELAY_DURATION as 21 bit unsigned value the constant maximum_possible_delay_duration is 2^21-1= 2097151.
maximum_possible_cycles_to_delay = maximum_possible_delay_duration /20 = 104857
so shift_register is unsigned(104857-1 downto 0) = unsigned(104856 downto 0)
but
required_number_of_cycles <= cylces_from_time ( DELAY_DURATION , "ns" , unsigned_frequency ) ; = maximum_possible_cycles_to_delay = 104857

so
OUTPUT <= shift_register ( to_integer ( required_number_of_cycles ) ) ;
calls for shift_register(104857) but shift_register max value is 104856

my error message in MODELSIM is the same, the other one is from Active-HDL
 
Reactions: shaiko

    shaiko

    Points: 2
    Helpful Answer Positive Rating
So...you suggest to define "shift_register" as:
Code:
signal shift_register : unsigned ( shift_register_width downto 0 ) := ( others => '0' ) ;
and not as:
Code:
signal shift_register : unsigned ( shift_register_width [COLOR="#FF0000"]- 1[/COLOR] downto 0 ) := ( others => '0' ) ;
?
 

generally when u apply signal to dff ( shift_reg ) you apply already 1 clock delay so n delay requires n-1 registers. So i would rather change function that will return current -1 result, but that will need change in function that 0 delay will not wrap to "111....1111".

all in all

1. you take into account that delay_generator is always applying 1 cycle latency and change shift_register to : unsigned ( shift_register_width downto 0 ) := ( others => '0' ) ;

2. or change function to return -1 value and add some code that for 0 delay OUTPUT <= INPUT.
 
Reactions: shaiko

    shaiko

    Points: 2
    Helpful Answer Positive Rating
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…