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:
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.
really helpful!
ReplyDeletehello my friend, i dont understand this line:
ReplyDeleteDAT_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
then only + operator support,otherwise error will come.so unsigned operation is used here.
DeleteX"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
ReplyDeleteThanks for you example. Can i ask why the X"1000" isnt X"0000"?
ReplyDeleteCheers
I had to change DAT_O <= X"1_0000_0000_0000" - Prev_Count + Curr_Count;
ReplyDeleteto DAT_O <= X"FFFF_FFFF_FFFF" - Prev_Count + Curr_Count + 1; to get mine to compile since the original was a 49 bit number.