-- 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:					Patrick Lehmann
--
-- Entity:					Converter binary numbers to BCD encoded numbers.
--
-- Description:
-- 
-- .. TODO:: No documentation available.
--
-- License:
-- =============================================================================
-- Copyright 2007-2015 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;

library	PoC;
use			PoC.utils.all;
use			PoC.components.all;


entity [docs]arith_convert_bin2bcd is
	generic (
		BITS					: positive		:= 8;
		DIGITS				: positive		:= 3;
		RADIX					: positive		:= 2
	);
	port (
		Clock					: in	std_logic;
		Reset					: in	std_logic;

		Start					: in	std_logic;
		Busy					: out	std_logic;

		Binary				:	in	std_logic_vector(BITS - 1 downto 0);
		IsSigned			: in	std_logic															:= '0';
		BCDDigits			: out	T_BCD_VECTOR(DIGITS - 1 downto 0);
		Sign					: out std_logic
	);
end entity;


architecture [docs]rtl of arith_convert_bin2bcd is
	constant RADIX_BITS			: positive	:= log2ceil(RADIX);
	constant BINARY_SHIFTS	: positive	:= div_ceil(BITS, RADIX_BITS);
	constant BINARY_BITS		: positive	:= BINARY_SHIFTS * RADIX_BITS;

	subtype T_CARRY					is unsigned(RADIX_BITS - 1 downto 0);
	type T_CARRY_VECTOR			is array(natural range <>) of T_CARRY;

	signal Digit_Shift_rst	: std_logic;
	signal Digit_Shift_en		: std_logic;
	signal Digit_Shift_in		: T_CARRY_VECTOR(DIGITS downto 0);

	signal Binary_en				: std_logic;
	signal Binary_rl				: std_logic;
	signal Binary_d					: std_logic_vector(BINARY_BITS - 1 downto 0)	:= (others => '0');

	signal Sign_d						: std_logic																		:= '0';
	signal DelayShifter			: std_logic_vector(BINARY_SHIFTS downto 0)		:= '1' & (BINARY_SHIFTS - 1 downto 0 => '0');

	function [docs]nextBCD(Value : unsigned(4 downto 0)) return unsigned is
		constant Temp : unsigned(4 downto 0)	:= Value - 10;
	begin
		if Value > 9 then
			return '1' & Temp(3 downto 0);
		else
			return Value;
		end if;
	end function;

begin
	Busy							<= not DelayShifter(DelayShifter'high);

	Binary_en					<= Start;
	Binary_rl					<= Start nor DelayShifter(DelayShifter'high);
	Digit_Shift_rst		<= Start;
	Digit_Shift_en		<= Start nor DelayShifter(DelayShifter'high);

	process(Clock)
	begin
		if rising_edge(Clock) then
			if (Reset = '1') then
				Binary_d	<= (others => '0');
			elsif (Binary_en = '1') then
				Binary_d(Binary_d'high downto Binary'high)	<= (others => '0');
				if ((IsSigned and Binary(Binary'high)) = '1') then
					Binary_d(Binary'high downto 0)						<= std_logic_vector(-signed(Binary));
					Sign_d																		<= '1';
				else
					Binary_d(Binary'high downto 0)						<= Binary;
					Sign_d																		<= '0';
				end if;
				DelayShifter																<= (BINARY_SHIFTS downto 1 => '0') & '1';
			elsif (Binary_rl = '1') then
				DelayShifter	<= DelayShifter(DelayShifter'high - 1 downto 0) & DelayShifter(DelayShifter'high);
				Binary_d			<= Binary_d(Binary_d'high - RADIX_BITS downto 0) & Binary_d(Binary_d'high downto Binary_d'high - RADIX_BITS + 1);
			end if;
		end if;
	end process;

	Sign								<= Sign_d;
	Digit_Shift_in(0)		<= unsigned(Binary_d(Binary_d'high downto Binary_d'high - RADIX_BITS + 1));

	-- generate DIGITS many systolic elements
	genDigits : for i in 0 to DIGITS - 1 generate
		signal Digit_nxt	: unsigned(3 + RADIX_BITS downto 0);
		signal Digit_d		: unsigned(3 downto 0)							:= (others => '0');
	begin
		process(Digit_d, Digit_Shift_in)
			variable Temp : unsigned(4 downto 0);
		begin
			Temp := '0' & Digit_d;
			for j in RADIX_BITS - 1 downto 0 loop
				Temp												:= nextBCD(Temp(3 downto 0) & Digit_Shift_in(i)(j));
				Digit_nxt(j + 4 downto j) 	<= Temp;
			end loop;
		end process;

		Digit_Shift_in(i + 1)	<= Digit_nxt(Digit_nxt'high downto Digit_nxt'high - RADIX_BITS + 1);

		process(Clock)
		begin
			if rising_edge(Clock) then
				if (Digit_Shift_rst = '1') then
					Digit_d	<= "0000";
				elsif (Digit_Shift_en = '1') then
					Digit_d	<= Digit_nxt(Digit_d'range);
				end if;
			end if;
		end process;

		BCDDigits(i)	<= T_BCD(std_logic_vector(Digit_d));
	end generate;
end;