--
 Implantable Stimulator and Transponder (IST, A3036) Firmware, Oscillator Unit

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

-- Version 01, 31-DEC-19: Expand ring to a maximum of thirteen buffers. Those not used will be eliminated by the

-- compiler. The incoming calib value sets the output frequyency. We support calib values 10-25, which in our 
-- A3036A provide a CK period 80 to 127 ns, with the period non-decreasing with increasing calib value.

-- Versin 02, 26-AUG-20: Revert to a five-gate ring oscillator with variable divisor, hoping to obtain better resolution
-- in FCK period. We have found that the A3037A core logic power supply voltage suffers from 30 mVpp, an inevitable result
-- of the hysteresis-mode operation of its 1.2-V buck converter, and this variation causes the FCK period to vary. But if
-- we place the maximum FCK period in a 5-ns window 105-110 ns, reception is >95%.

-- Version 03, 31-AUG-20: Change the end count from a signal to a variable.

entity ring_oscillator is 
	port (
		ENABLE : in std_logic;
		calib : in integer range 0 to 31;
		CK : out std_logic);
end;

architecture behavior of ring_oscillator is 

-- Functions and Procedures	
	function to_std_logic (v: boolean) return std_ulogic is
	begin if v then return('1'); else return('0'); end if; end function;

-- Attributes to guide the compiler.
	attribute syn_keep : boolean;
	attribute nomerge : string;

-- Ring Oscillator and Transmit Clock
	component BUFBA is port (A : in std_logic; Z : out std_logic); end component;
	signal RIN, R1, R2, R3, R4 : std_logic;
	attribute syn_keep of RIN, R1, R2, R3, R4 : signal is true;
	attribute nomerge of RIN, R1, R2, R3, R4 : signal is ""; 
	signal end_count : integer;

begin
	ring1 : BUFBA port map (RIN,R1);
	ring2 : BUFBA port map (R1,R2);
	ring3 : BUFBA port map (R2,R3);
	ring4 : BUFBA port map (R3,R4);
	
	divider : process (RIN, calib) is
		constant ck_length : integer := 4;
		variable count : integer range 0 to 31 := 0;
		variable end_count : integer range 0 to 31;
	begin	
		RIN <= to_std_logic((ENABLE = '1') and (R4 = '0'));
		end_count := calib;
	
		if rising_edge(RIN) then
			if (count = end_count - 1) then 
				count := 0;
			else 
				count := count + 1;
			end if;
			if (count >= 0) and (count <= ck_length) then
				CK <= '1';
			else
				CK <= '0';
			end if;
		end if;
	end process;
	
end behavior;