----------------------------------------------------------------------------------
-- Company: University of Connecticut
-- Engineer: Igor Senderovich
-- 
-- Create Date:    19:47:05 08/16/2009 
-- Design Name: 
-- Module Name:    SerialOut - behavioral 
-- Description:    Shifts out parallel input into a properly-conditioned
--		   RS-232 serial out.
----------------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;

library FPGA_BasicComp;
use FPGA_BasicComp.BasicComp.all;

entity SerialOut is
    Port ( Clk : in  std_logic;
	   Go : in std_logic;
	   Done : out std_logic;
           D : in  std_logic_vector (7 downto 0);
           Q : out  std_logic);
end SerialOut;

architecture behavioral of SerialOut is

	type stage_t is (S0,S1,S2,S3,S4,S5,S6,S7,S8,S9,SA,SB);
	signal stage : stage_t := S0;

	signal SerClk_edge : std_logic := '0';
	signal SerClk : std_logic := '0';
	signal SerCnt : std_logic_vector (7 downto 0) := (others => '0');
	signal Done_reg : std_logic := '0';
	signal Q_reg : std_logic := '0';
begin
	--Synthesize the serial clock ~115200 Hz using the 20MHz system clock
	Gen_SerClk : process (Clk)
	begin
		if rising_edge(Clk) then
			if (SerCnt(7) = '1') then 
				SerCnt <= X"55";
				SerClk_edge <= not SerClk;
				SerClk <= not SerClk;
			else
				SerCnt <= SerCnt - 1;
				SerClk_edge <= '0';
				SerClk <= SerClk;
			end if;
		else
			SerCnt <= SerCnt;
			SerClk_edge <= SerClk_edge;
			SerClk <= SerClk;
		end if;
	end process;

	Gen_Output : process (Clk)
	begin
		if rising_edge(Clk) then
			stage <= stage;
			Done_reg <= '0';
			Q_reg <= '0';
			case stage is
			    when S0 =>
				if (Go = '1' and SerClk_edge = '1') then
					stage <= S1;
				end if;
			    when S1 =>
				Q_reg <= '1';
				if (SerClk_edge = '1') then
					stage <= S2;
				end if;
			    when S2 =>
				Q_reg <= D(7);
				if (SerClk_edge = '1') then
					stage <= S3;
				end if;
			    when S3 =>
				Q_reg <= D(6);
				if (SerClk_edge = '1') then
					stage <= S4;
				end if;
			    when S4 =>
				Q_reg <= D(5);
				if (SerClk_edge = '1') then
					stage <= S5;
				end if;
			    when S5 =>
				Q_reg <= D(4);
				if (SerClk_edge = '1') then
					stage <= S6;
				end if;
			    when S6 =>
				Q_reg <= D(3);
				if (SerClk_edge = '1') then
					stage <= S7;
				end if;
			    when S7 =>
				Q_reg <= D(2);
				if (SerClk_edge = '1') then
					stage <= S8;
				end if;
			    when S8 =>
				Q_reg <= D(1);
				if (SerClk_edge = '1') then
					stage <= S9;
				end if;
			    when S9 =>
				Q_reg <= D(0);
				if (SerClk_edge = '1') then
					stage <= SA;
				end if;
			    when SA =>
				Q_reg <= '0';
				if (SerClk_edge = '1') then
					stage <= SB;
				end if;
			    when SB =>
				if (SerClk_edge = '1') then
					Done_reg <= '1';
				elsif (Go = '0') then
					stage <= S0;
				end if;
			end case;
		else
			stage <= stage;
			Done_reg <= Done_reg;
			Q_reg <= Q_reg;
		end if;
	end process;

	Q <= not Q_reg;
	Done <= Done_reg;

end behavioral;

--------------------------------------------------------
-- SerialOutFIFO is a wrapper around SerialOut that
-- buffers the output and allows the host process to
-- continue without waiting for the output to finish.
--------------------------------------------------------

library IEEE;
use IEEE.std_logic_1164.ALL;
use IEEE.std_logic_ARITH.ALL;
use IEEE.std_logic_UNSIGNED.ALL;

library UNISIM;
use UNISIM.VComponents.all;

library FPGA_BasicComp;
use FPGA_BasicComp.BasicComp.all;

entity SerialOutFIFO is
    Port ( Clk : in  std_logic;
	   Go  : in std_logic;
	   Done : out std_logic;
           D : in  std_logic_vector (7 downto 0);
           Q : out  std_logic);
end SerialOutFIFO;

architecture behavioral of SerialOutFIFO is

	signal Ser_cycle_Go : std_logic := '0';
	signal Ser_cycle_Done : std_logic;
	signal datachar : std_logic_vector (7 downto 0);
	signal countup : std_logic := '0';
	signal countdown : std_logic := '0';
	signal counter : std_logic_vector (11 downto 0) := (others => '0');
	signal push_addr : std_logic_vector (10 downto 0) := (others => '0');
	signal pop_addr : std_logic_vector (10 downto 0) := (others => '0');
	signal pushing : std_logic := '0';
	signal Done_reg : std_logic := '0';

	--signals for FIFO port A
	signal FIFO_ENA : std_logic;
	signal FIFO_SSRA : std_logic;
	signal FIFO_ADDRA : std_logic_vector (13 downto 0);
	signal FIFO_WEA : std_logic_vector (3 downto 0);
	signal FIFO_DIA : std_logic_vector (31 downto 0);
	signal FIFO_DIPA : std_logic_vector (3 downto 0);
	signal FIFO_DOA : std_logic_vector (31 downto 0);
	signal FIFO_DOPA : std_logic_vector (3 downto 0);

	--signals for FIFO port B
	signal FIFO_ENB : std_logic;
	signal FIFO_SSRB : std_logic;
	signal FIFO_ADDRB : std_logic_vector (13 downto 0);
	signal FIFO_WEB : std_logic_vector (3 downto 0);
	signal FIFO_DIB : std_logic_vector (31 downto 0);
	signal FIFO_DIPB : std_logic_vector (3 downto 0);
	signal FIFO_DOB : std_logic_vector (31 downto 0);
	signal FIFO_DOPB : std_logic_vector (3 downto 0);
begin
	----------------------------------------
	-- Component for sending one character
	----------------------------------------
	SerialOut_inst : SerialOut
	port map ( Clk => Clk,
		   Go => Ser_cycle_Go,
		   Done => Ser_cycle_Done,
		   D => datachar,
		   Q => Q
	);

	-----------------------------------------
	-- Dual-ported RAM block for output FIFO
	-- port A - used to load the FIFO
	-- port B - used to unload the FIFO
	-----------------------------------------
	RAMB16BWE_inst : RAMB16BWE
	generic map ( DATA_WIDTH_A => 9,
		      DATA_WIDTH_B => 9,
		      WRITE_MODE_A => "WRITE_FIRST",
		      WRITE_MODE_B => "WRITE_FIRST" )
	port map ( -- port A connections
		   CLKA => Clk,
		   ENA => FIFO_ENA,
		   SSRA => FIFO_SSRA,
		   ADDRA => FIFO_ADDRA,
		   DIA => FIFO_DIA,
		   DIPA => FIFO_DIPA,
		   DOA => FIFO_DOA,
		   DOPA => FIFO_DOPA,
		   WEA => FIFO_WEA,
		   -- port B connections
		   CLKB => Clk,
		   ENB => FIFO_ENB,
		   SSRB => FIFO_SSRB,
		   ADDRB => FIFO_ADDRB,
		   DIB => FIFO_DIB,
		   DIPB => FIFO_DIPB,
		   DOB => FIFO_DOB,
		   DOPB => FIFO_DOPB,
		   WEB => FIFO_WEB
	);

	FIFO_ENA <= pushing;
	FIFO_SSRA <= '0';
	FIFO_ADDRA <= push_addr & "000";
	FIFO_DIA <= X"000000" & D;
	FIFO_DIPA <= X"0";
	FIFO_WEA <= pushing & pushing & pushing & pushing;

	FIFO_ENB <= Ser_cycle_Go;
	FIFO_SSRB <= '0';
	FIFO_ADDRB <= pop_addr & "000";
	FIFO_DIB <= (others => '0');
	FIFO_DIPB <= X"0";
	datachar <= FIFO_DOB(7 downto 0);
	FIFO_WEB <= X"0";

	-----------------------------------------------------------
	-- Append next output character to the end of the buffer.
	-----------------------------------------------------------
	push_process : process (Clk) is
	begin
		if rising_edge(Clk) then
			Done_reg <= Go;
			if (Go = '1' and Done_reg = '0') then
				countdown <= '1';
				pushing <= '1';
			else
				countdown <= '0';
				pushing <= '0';
			end if;
		else
			countdown <= countdown;
			pushing <= pushing;
			Done_reg <= Done_reg;
		end if;
	end process;

	Done <= Done_reg;
	
	-----------------------------------------------------------
	-- Get next output character from the start of the buffer.
	-----------------------------------------------------------
	pop_process : process (Clk) is
	begin
		if rising_edge(Clk) then
			Ser_cycle_Go <= counter(11) and not Ser_cycle_Done;
			if (Ser_cycle_Go = '1' and Ser_cycle_Done = '1') then
				countup <= '1';
			else
				countup <= '0';
			end if;
		else
			Ser_cycle_Go <= Ser_cycle_Go;
			countup <= countup;
		end if;
	end process;

	----------------------------
	-- Manage the FIFO counter
	----------------------------
	count_process : process (Clk) is
		variable countup_down_var : std_logic_vector (1 downto 0);
	begin
		if rising_edge(Clk) then
			pop_addr <= pop_addr;
			push_addr <= push_addr;
			countup_down_var := countup & countdown;
			case (countup_down_var) is
			    when "01" =>
				push_addr <= push_addr + 1;
				counter <= counter - 1;
			    when "10" =>
				pop_addr <= pop_addr + 1;
				counter <= counter + 1;
			    when "11" =>
				push_addr <= push_addr + 1;
				pop_addr <= pop_addr + 1;
			    when others =>
				counter <= counter;
			end case;
		else
			pop_addr <= pop_addr;
			push_addr <= push_addr;
			counter <= counter;
		end if;
	end process;

end behavioral;

----------------------------------------------------------
--Below is a second implementation of the SerialOutFIFO
--component.  This one is based on single-ported RAM
--and entails more complicated logic than is required
--for the dual-ported RAM implementation.  It is kept
--here for reference only.
----------------------------------------------------------
--
--architecture behavioral of SerialOutFIFO is
--
--	signal counter : std_logic_vector (12 downto 0);
--	signal push_addr : std_logic_vector (12 downto 0);
--	signal pop_addr : std_logic_vector (12 downto 0);
--	signal pushing : std_logic;
--	signal popping : std_logic;
--	signal Done_reg : std_logic;
--	signal Ser_cycle_Go : std_logic;
--	signal Ser_cycle_Done : std_logic;
--	signal datachar : std_logic;
--	signal FIFO_EN : in std_logic;
--	signal FIFO_ADDR : in std_logic_vector (9 downto 0);
--	signal FIFO_WE : in std_logic_vector( 1 downto 0);
--	signal FIFO_DI : in std_logic_vector (15 downto 0);
--	signal FIFO_DO : out std_logic_vector (15 downto 0);
--begin
--	----------------------------------------
--	-- Component for sending one character
--	----------------------------------------
--	SerialOut_inst : SerialOut
--	port map ( Clk => Clk,
--		   Go => Ser_cycle_Go,
--		   Done => Ser_cycle_Done,
--		   D => datachar,
--		   Q => Q
--	);
--
--	---------------------------------
--	-- RAM block for output FIFO
--	---------------------------------
--	RAMB16BWE_S18_inst : RAMB16BWE_S18
--	generic map ( WRITE_MODE => "WRITE_FIRST" )
--	port map ( CLK => Clk,		-- 1-bit Clock
--		   DO => FIFO_DO,	-- 16-bit Data Output
--		   DOP => FIFO_DOP,	-- 2-bit parity Output
--		   ADDR => FIFO_ADDR,	-- 10-bit Address Input
--		   DI => FIFO_DI, 	-- 16-bit Data Input
--		   DIP => FIFO_DIP,	-- 2-bit parity Input
--		   EN => FIFO_EN,	-- 1-bit RAM Enable Input
--		   SSR => FIFO_SSR,	-- 1-bit Synchronous Set/Reset Input
--		   WE => FIFO_WE	-- 2-bit Write Enable Input
--	);
--
--	FIFO_EN <= '1';
--	FIFO_SSR <= '0';
--	FIFO_DIP <= "00";
--
--	-----------------------------------------------------------
--	-- Put next received character on the end of the buffer,
--	-- get next output character from the start of the buffer.
--	-----------------------------------------------------------
--	FIFO_loop: process (Clk) is
--	begin
--		if rising_edge(Clk) then
--			pushing <= Go;
--			Done_reg <= Go and pushing;
--			popping <= popping;
--			counter <= counter;
--			push_addr <= push_addr;
--			pop_addr <= pop_addr;
--			FIFO_WE <= "00";
--			FIFO_ADDR <= FIFO_ADDR;
--			FIFO_DI <= FIFO_DI;
--			Ser_cycle_Go <= Ser_cycle_Go;
--
--			if (popping = '1') then
--				counter <= counter - 1;
--				pop_addr <= pop_addr + 1;
--				datachar <= FIFO_DO(15 downto 8) when (pop_addr(0) = '1') else
--					    FIFO_DO(7 downto 0);
--				Ser_cycle_Go <= '1';
--				popping <= '0';
--			elsif (Go = '1' and pushing ='0') then
--				counter <= counter + 1;
--				push_addr <= push_addr + 1;
--				FIFO_WE <= "01" when (push_addr(0) = '0') else "10";
--				FIFO_ADDR <= push_addr(10 downto 1);
--				FIFO_DI <= D & D;
--			elsif (Ser_cycle_Go = '1') then
--				Ser_cycle_Go <= not Ser_cycle_Done;
--			elsif (counter(11) = '0') then
--				FIFO_ADDR <= pop_addr(10 downto 1);
--				popping <= '1';
--			else
--				counter <= (others => '1');
--				push_addr <= (others => '0');
--				pop_addr <= (others => '0');
--			end if;
--		else
--			pushing <= pushing;
--			Done_reg <= Done_reg;
--			popping <= popping;
--			counter <= counter;
--			push_addr <= push_addr;
--			pop_addr <= pop_addr;
--			FIFO_WE <= "00";
--			FIFO_ADDR <= FIFO_ADDR;
--			FIFO_DI <= FIFO_DI;
--			Ser_cycle_Go <= Ser_cycle_Go;
--			datachar <= datachar;
--		end if;
--	end process;
--
--	Done <= Done_reg;
--
--end behavioral;
