VHDL coding tips and tricks: VHDL: How To Measure The Width Of An Input Pulse

Monday, September 13, 2010

VHDL: How To Measure The Width Of An Input Pulse

    Is it possible to find the time period of an input pulse? Yes, it is possible and that too with a simple counter, adder and some logic gates. Let's look into how this is done.

    For this to work well, we have to assume that our system clock has a time period much shorter than the width of the pulse we are going to measure. Why so? Because, what we are essentially doing is that, we start a counter when the pulse is High and stop when its Low. The final count, which is when the pulse has just gone Low, gives us the duration of the pulse, in terms of the time period of our clock signal.

    For example, lets say we have a system clock frequency of 100 MHz, which means its time period is 10 ns. If a pulse stays High for 55 ns, the counter would have a final count of 5 by the time the pulse has gone down to Low. 
So we can say that the duration of the pulse = time period of clock * count = 10 ns * 5 = 50 ns.

    Yes, its not correct. But that is unavoidable. This is why I had mentioned that for this to work well, our system clock frequency should be pretty high, compared to that of the input pulse.

    Let us look into the code now:

Pulse Duration Counter:


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

entity pulse_duration_counter is
port(clk : in std_logic;
    pulse_in : in std_logic;
    valid_output : out std_logic;
    pulse_duration : out unsigned(47 downto 0)
    );
end pulse_duration_counter;

architecture Behavioral of pulse_duration_counter is

--In VHDL-1997, output ports cannot be read. Thats why we use temp here.
signal temp : unsigned(47 downto 0) := (others => '0');

begin

process(clk)
begin
if(rising_edge(clk)) then
    if (pulse_in = '1') then 
        temp <= temp + 1;
        valid_output <= '0';
        --signals get their values updated only at the end of the process.
        --thats why I have to add '1' to temp before assigning it as output.
    else
        pulse_duration <= temp; 
        if(temp > 0) then
            valid_output <= '1';
        else
            valid_output <= '0';
        end if;
        temp <= (others => '0');
    end if;
end if;
end process;
   
end Behavioral;

Testbench for Pulse Duration Counter:


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

--testbench has empty entity
entity tb_pulse_counter is
end entity tb_pulse_counter;

architecture behavioral of tb_pulse_counter is

signal clk,pulse_in,valid_output : std_logic := '0';
signal pulse_duration :unsigned(47 downto 0) := (others => '0');
constant clk_period : time := 10 ns;

begin

--entity instantiation with named association port mapping
pulse_counter_uut: entity work.pulse_duration_counter
    port map(clk => clk,
        pulse_in => pulse_in,
        valid_output => valid_output,
        pulse_duration => pulse_duration);

--generate clock
Clk_generation: process
begin
    wait for clk_period/2;
    clk <= not clk; --toggle clock when half of clk_period is over
end process;

stimulus: process
begin 
    pulse_in <= '0';    wait for Clk_period;
    pulse_in <= '1';    wait for Clk_period*10;
    pulse_in <= '0';    wait for Clk_period*2;
    pulse_in <= '1';    wait for Clk_period*20;
    pulse_in <= '0';    wait for Clk_period*5;
    pulse_in <= '1';    wait for Clk_period*15;
    pulse_in <= '0';    
    wait;  --testing done. wait endlessly
end process;

end behavioral;

Simulation waveform from Modelsim:


simulation waveform of pulse counter from modelsim vhdl


Synthesis:


The pulse duration counter entity was synthesised successfully using Xilinx Vivado 2023.2. 

Notes:


    We can create many variations for this design. The current counter only counts the duration of the High pulse. We could make it so that both High and Low pulse widths are counted. 

    If our pulses arent going to be High for very long, we could decrease the size of the counter from 48 bits to something smaller, say 16 bits. I had used a width of 48 bits, keeping in mind of an extreme usage case. 

6 comments:

  1. hello my friend, i dont understand this line:
    DAT_O <= X"1_0000_0000_0000" - Prev_Count + Curr_Count;
    and I dont understand why you use unsigned numbers. Can you help me? thank you

    ReplyDelete
    Replies
    1. then only + operator support,otherwise error will come.so unsigned operation is used here.

      Delete
  2. X"1_0000_0000_0000" this portion is giving error... using this"1000000000000" no error but output is not as expected only error_o is 1 rest all are zero in tb

    ReplyDelete
  3. Thanks for you example. Can i ask why the X"1000" isnt X"0000"?

    Cheers

    ReplyDelete
  4. I had to change DAT_O <= X"1_0000_0000_0000" - Prev_Count + Curr_Count;
    to DAT_O <= X"FFFF_FFFF_FFFF" - Prev_Count + Curr_Count + 1; to get mine to compile since the original was a 49 bit number.

    ReplyDelete