-- $Source: /evtfs/home/tres/vhdl/ref_design/RCS/rise_count.vhd,v $
-- $Revision: 1.20 $     $Date: 2007/05/02 19:47:06 $
-- Synthesis Template Example, Counting 0,1 sequences on Input Port
-- Mike Treseler  updated Wed Dec 21 14:32:51 2005
-------------------------------------------------------------------------------
-- Combinational Node = UPDATE the variable first. 
-- Registered    Node = USE    the variable first.
-------------------------------------------------------------------------------
-- See http://home.comcast.net/~mike_treseler/rise_count.vhd for this
-- file.  See http://home.comcast.net/~mike_treseler/rise_count.pdf
-- for the synthesis results. This example illustrates the design and
-- use of procedural components. These "components" consist of a type
-- declaration used to "instance" the variable and a procedure to
-- update it.
-------------------------------------------------------------------------------
-- This tutorial design includes input synchronization, a 0,1 sequence
-- detector that enables a roll-over counter. See the single process
-- template at the very end of this file. This template calls the top
-- procedures: init_regs, update_regs, and update_ports.
-------------------------------------------------------------------------------
-- Wed May  2 12:45:11 2007 removed unused use statement
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-------------------------------------------------------------------------------
entity rise_count is
   generic (w : positive := 3);
   port (clock     : in  std_ulogic;
         reset     : in  std_ulogic;
         watch     : in  std_ulogic;    -- watch for '0', '1'
         strobe_tp : out std_ulogic;    -- detector testpoint
         retime_tp : out std_ulogic;    -- Synchronized input testpoint
         count     : out std_logic_vector(w-1 downto 0)  -- number of edges
         );
end entity rise_count;
-------------------------------------------------------------------------------
architecture synth of rise_count is     -- no signals required
begin
   one : process(reset, clock) is
-------------------------------------------------------------------------------
-- Process declarations for the Template Procedures
-- These could be packaged, shown here as a tutorial
      function now_high (std_arg : std_ulogic)
      return boolean is -- convert active high bit to boolean
   begin
      if std_arg = '1' then return true;
      else return false;
      end if;
   end function now_high;
-------------------------------------------------------------------------------
-- RETIME is a four node procedural component for input
-- synchronization.  It is a variable of type retime_t that is UPDATED
-- by the procedure retime declared below.

-- USAGE: Declare a variable of the type retime_t for each instance.
-- Output options(sequence constraints):
-- Registered Node   = USE the variable.f3 first -- required in this case
-- Unregistered Node = UPDATE the variable first -- would drop a sync stage       

   type retime_t
      is
   record
      f1 : std_ulogic;  -- .f1 output covers races only
      f2 : std_ulogic;  -- .f2 output node covers metastable events also
      f3 : std_ulogic;  -- .f3 output covers synthesis register duplication
   end record;

   procedure retime
      (arg_in : in    std_ulogic;       -- input value
       update : inout retime_t          -- 3 bit shifter
       )
   is
   begin
      update.f3 := update.f2;           -- f2[DQ]f3 
                                        --  \__
                                        --      \
      update.f2 := update.f1;           -- f1[DQ]f2
                                        --  \__
                                        --      \
      update.f1 := arg_in;              -- in[DQ]f1
      
      -- Note that reversing the order the statements above would make
      -- wires instead of registers.
   end procedure retime;
-------------------------------------------------------------------------------
-- RISING is a three node procedural component for edge detection.
-- It is a variable of type ck_rise_t that is UPDATED
-- by the procedure rising declared below.
-- USAGE: Declare a variable of the type retime_t for each instance.
-- Output options (sequence constraints):
-- Registered Internal Node   = USE the .strobe first 
-- Unregistered Internal Node = UPDATE the variable first
--                              that is call rising, then use .strobe    
-- Instructions: Use a variable of the type ck_rise_t as a synchronized
-- output.  Call the procedure rising *after* such usage to specify
-- the input port "arg_in" and the rising variable structure "update".
-------------------------------------------------------------------------------   
   type ck_rise_t is record             -- two bit object for edge detection
      history : std_ulogic;             -- history register (private)
      strobe  : std_ulogic;             -- output node
   end record;
   procedure rising (
      arg_in : in    std_ulogic;
      update : inout ck_rise_t
      ) is

      -- A three node procedural component for rising edge detection
      -- Unregistered Internal Node = Call rising, then use .strobe
      --                                     .history    ___
      --  arg_in >--------.--o|>--[DQ]------------------|AND\__ .strobe
      --                   \____________________________|___/
      -- purpose: first cut with extra register
   begin
      update.strobe  := arg_in and update.history;  -- history USED first
      update.history := not arg_in; 
      -- Note that the internal node .history is always registered
      -- as it is USED first.
      
      -- Note that if the two assignments above cannot be reversed.
      -- If they were reversed, the register would drop out leaving:
      --    .strobe := (arg_in and not arg_in);
      --    .strobe := ('0'); -- No gates, no flops!
   end procedure rising;
------------------------------------------------------------------------------
   -- Procedural "Component" Instances
   variable count_v  : unsigned(count'range); -- counter is enabled by a
   variable rising_v : ck_rise_t;  -- 0,1 sequence detector strobe that watches
   variable retime_v : retime_t; -- a retimed input
-------------------------------------------------------------------------------
-- Template Procedures: Always same three names. Contents varies.
-------------------------------------------------------------------------------
   procedure init_regs is           -- init of register variables only
      -- "regs" may end up as registers or wires (like verilog)
   begin
      retime_v := (others => '0');
      rising_v := (others => '0');
      count_v  := (others => '0');
   end procedure init_regs;
-------------------------------------------------------------------------------
   procedure update_regs is
   begin  -- Procedural Block Diagram:
          -- Write this first, then fill in the declarations above.

-- Lets say some procedure rising watches the third stage of
-- of an input synchronizer, say retime_v.f3 for a 0,1 sequence:
      
      rising
         (arg_in => retime_v.f3,        -- USE the variable retime_v.f3 
          update => rising_v            -- UPDATE the variable rising_v
          );                      

-- Say we have a counter, count_v enabled by some detector output,
-- say rising_v.strobe:

      cnt_enable : if now_high(rising_v.strobe)  -- USE rising_v
      then
         count_v := count_v+1; -- USED then UPDATED on same line
      end if cnt_enable;

-- Finally, a retime procedure to run a three bit shifter named
-- retime_v in front of the detector:
      
      retime(
         arg_in => watch,               -- entity port value 
         update => retime_v             -- UPDATE the variable retime_v
         );

   end procedure update_regs;
-------------------------------------------------------------------------------
   procedure update_ports is     -- wire register variables out to port

      -- This procedure runs for all clock and reset events.  Simple
      -- assignements from process variables to port are allowed for
      -- synthesis. Process variables must be intitialized in the
      -- init_regs procedure above.

      -- Note that these signal assignments infer a register *only* if
      -- the variable on the right is combinational, like rising_v.strobe.
      
      -- If the variable is already registered, like count_v or
      -- retime_v.f3, just a wire is inferred to connect the variable
      -- to the output port.  This template style does not allow
      -- unregistered port outputs.  However, variables representing
      -- combinational nets and may be used anywhere inside the
      -- process.
   begin
      retime_tp <= retime_v.f3; -- let's bring out some testpoints:
      strobe_tp <= rising_v.strobe;
      count     <= std_logic_vector(count_v); -- wire counter register to port

   end procedure update_ports;
-------------------------------------------------------------------------------
-- Top Process Template -- Always exactly the same:
-------------------------------------------------------------------------------   
   begin  -- process template
      if reset = '1' then
         init_regs;                 -- no port init required
      elsif rising_edge(clock) then
         update_regs;
      end if;
      update_ports;  -- will synth port wires ok for reset or clk
   end process one;
end architecture synth;
-------------------------------------------------------------------------------