Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
common_counter.vhd 4.47 KiB
-------------------------------------------------------------------------------
--
-- Copyright (C) 2009
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
--
-------------------------------------------------------------------------------

-- Purpose : Counter with extra options
-- Description:
--   - default wrap at 2**g_width or special wrap at fixed g_max or dynamically via cnt_max
--   - count can be clipped instead of wrapped by setting g_clip to True.
--   - default increment +1 or other g_step_size
--   - external clr
--   - external load with g_init or dynamically via load
-- Remarks:
-- . If g_max = 2**g_width then use g_max=0 for default wrap and avoid truncation warning.
--   The check g_max = 2**g_width does not work for g_width >= 31 due to that the INTEGER
--   range in VHDL is limited to -2**31 to +2**31-1. Therefore detect that g_max = 2**g_width
--   via ceil_log2(g_max+1)>g_width and use this to init the cnt_max input.

library IEEE;
use IEEE.std_logic_1164.all;
use work.common_pkg.all;


entity common_counter is
  generic (
    g_latency   : natural := 1;  -- default 1 for registered count output, use 0 for immediate combinatorial count output
    g_init      : integer := 0;
    g_width     : natural := 32;
    g_max       : natural := 0;  -- default 0 to disable the g_max setting.
    g_step_size : integer := 1;  -- counting in steps of g_step_size, can be + or -
    g_clip      : boolean := false  -- when True, counter will clip at g_max, if g_max = 0 and g_step_size > 0, the counter clips at 2**g_width -1.
  );
  port (
    rst     : in  std_logic := '0';  -- either use asynchronous rst or synchronous cnt_clr
    clk     : in  std_logic;
    clken   : in  std_logic := '1';
    cnt_clr : in  std_logic := '0';  -- synchronous cnt_clr is only interpreted when clken is active
    cnt_ld  : in  std_logic := '0';  -- cnt_ld loads the output count with the input load value, independent of cnt_en
    cnt_en  : in  std_logic := '1';
    cnt_max : in  std_logic_vector(g_width - 1 downto 0) := sel_a_b( g_step_size > 0 and g_max = 0, array_init('1', g_width),
                                                          sel_a_b( ceil_log2(g_max + 1) > g_width,  array_init('1', g_width), TO_UVEC(g_max, g_width) ));  -- see remarks
    load    : in  std_logic_vector(g_width - 1 downto 0) := TO_SVEC(g_init, g_width);
    count   : out std_logic_vector(g_width - 1 downto 0)
  );
end common_counter;


architecture rtl of common_counter is

  signal reg_count  : std_logic_vector(count'range) := TO_SVEC(g_init, g_width);  -- in case rst is not used
  signal nxt_count  : std_logic_vector(count'range) := TO_SVEC(g_init, g_width);  -- to avoid Warning: NUMERIC_STD.">=": metavalue detected, returning FALSE, when using unsigned()
  signal comb_count : std_logic_vector(count'range) := TO_SVEC(g_init, g_width);  -- to avoid Warning: NUMERIC_STD.">=": metavalue detected, returning FALSE, when using unsigned()

begin

  comb_count <= nxt_count;

  count <= comb_count when g_latency = 0 else reg_count;

  assert g_step_size /= 0 report "common_counter: g_step_size must be /= 0" severity FAILURE;

  p_clk : process (rst, clk)
  begin
    if rst = '1' then
      reg_count <= TO_SVEC(g_init, g_width);
    elsif rising_edge(clk) then
      if clken = '1' then
        reg_count <= nxt_count;
      end if;
    end if;
  end process;

  p_count : process(reg_count, cnt_clr, cnt_en, cnt_ld, load, cnt_max)
  begin
    nxt_count <= reg_count;
    if cnt_clr = '1' then
      nxt_count <= (others => '0');
    elsif cnt_ld = '1' then
      nxt_count <= load;
    elsif cnt_en = '1' and reg_count = cnt_max then
      if not g_clip then
        nxt_count <= (others => '0');
      end if;
    elsif cnt_en = '1' then
      nxt_count <= INCR_UVEC(reg_count, g_step_size);
    end if;
  end process;

end rtl;