-- EMACS settings: -*-  tab-width: 2; indent-tabs-mode: t -*-
-- vim: tabstop=2:shiftwidth=2:noexpandtab
-- kate: tab-width 2; replace-tabs off; indent-width 2;
-- =============================================================================
-- Authors:				 	Thomas B. Preusser
--									Martin Zabel
--									Steffen Koehler
--
-- Entity:				 	Gray-Code counter.
--
-- Description:
-- 
-- .. TODO:: No documentation available.
--
-- License:
-- =============================================================================
-- Copyright 2007-2014 Technische Universitaet Dresden - Germany
--										 Chair of VLSI-Design, Diagnostics and Architecture
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--		http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-- =============================================================================

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


entity [docs]arith_counter_gray is
	generic (
		BITS : positive;															-- Bit width of the counter
		INIT : natural 				:= 0										-- Initial/reset counter value
	);
	port (
		clk : in	std_logic;
		rst : in	std_logic;													-- Reset to INIT value
		inc : in	std_logic;													-- Increment
		dec : in	std_logic		:= '0';									-- Decrement
		val : out std_logic_vector(BITS-1 downto 0);	-- Value output
		cry : out std_logic														-- Carry output
	);
end entity arith_counter_gray;


architecture [docs]rtl of arith_counter_gray is

	-- purpose: gray constant encoder
	function [docs]gray_encode (val : natural; len : positive) return unsigned is
		variable bin : unsigned(len-1 downto 0) := to_unsigned(val, len);
	begin
		if len = 1 then
			return bin;
		end if;
		return bin xor '0' & bin(len-1 downto 1);
	end gray_encode;

	-- purpose: parity generation
	function [docs]parity (val : unsigned) return std_logic is
		variable res : std_logic := '0';
	begin	-- parity
		for i in val'range loop
			res := res xor val(i);
		end loop;
		return res;
	end parity;

	-- Counter Register
	constant INIT_GRAY	: unsigned(BITS-1 downto 0) := gray_encode(INIT, BITS);
	signal gray_cnt_r	 : unsigned(BITS-1 downto 0) := INIT_GRAY;
	signal gray_cnt_nxt : unsigned(BITS-1 downto 0);

	signal en : std_logic;								-- enable: inc xor dec

begin

	-----------------------------------------------------------------------------
	-- Actual Counter Register
	en <= inc xor dec;
	process(clk)
	begin
		if rising_edge(clk) then
			if rst = '1' then
				gray_cnt_r <= INIT_GRAY;
			elsif en = '1' then
				gray_cnt_r <= gray_cnt_nxt;
			end if;
		end if;
	end process;
	val <= std_logic_vector(gray_cnt_r);

	-----------------------------------------------------------------------------
	-- Computation of Increment/Decrement


	-- Trivial one-bit Counter
	g1: if BITS = 1 generate
		gray_cnt_nxt <= not gray_cnt_r;
		cry					<= gray_cnt_r(0) xor dec;
	end generate g1;

	-- Multi-Bit Counter
	g2: if BITS > 1 generate

		constant INIT_PAR : std_logic := parity(INIT_GRAY);
		-- search for first one in gray_cnt_r(MSB-1 downto LSB)
		-- first_one_n(i) = '1' denotes position i

		-- parity of gray_cnt_r
		signal par_r	 : std_logic := INIT_PAR;
		signal par_nxt : std_logic;

	begin

		-- Parity Register
		process(clk)
		begin
			if rising_edge(clk) then
				if rst = '1' then
					par_r <= INIT_PAR;
				elsif en = '1' then
					par_r <= par_nxt;
				end if;
			end if;
		end process;

		-- Computation of next Value
		process(gray_cnt_r, par_r, dec)
			variable	x : unsigned(BITS-1 downto 0);
			variable	s : unsigned(BITS-1 downto 0);
		begin

			-- Prefer inc over dec to keep combinational path short in standard use.
			x				 := gray_cnt_r(BITS-2 downto 0) & (par_r xnor dec);
			x(x'left) := not gray_cnt_r(BITS-1);	-- catch final carry to invert last bit
			s				 := not x + 1;							 -- locate first intermediate '1'

			gray_cnt_nxt <= s(BITS-1) & (gray_cnt_r(BITS-2 downto 0) xor
																	 (s(BITS-2 downto 0) and x(BITS-2 downto 0)));
			par_nxt			<= s(0) xor dec;
		end process;

		cry <=	(gray_cnt_r(BITS-1) xor dec) and (gray_cnt_nxt(BITS-1) xnor dec);
	end generate g2;

end rtl;