--
 Subcutaneous Transmitter (SCT) Message Decoder for Animal Location Tracker (ALT)

-- Receives as input the bit sequence, Q, from the discriminator of the A3038DM detector module. 
-- Interprets the sequence with the help of a 40-MHz clock, which allows it to synchronize its
-- state to that of the transmitter by using the eleven synchronizing bits with which each SCT
-- transmission begins. 

-- Version 1: [01-APR-21] Translated from ABEL program P302702A12_Decoder.abl.

-- Version 2: [10-MAY-21] Remove the CNT (continue) input. We will use the RST (reset)
-- input to restore the decoder to its initial state. In order to simplify the detector
-- readout process, we reset all decoders after they store a message record in their 
-- message buffers. The decoder stays in its message-store state until reset. We correct
-- bugs in the reset implementation and forbid the reception of messages with ID exactly
-- divisible by sixteen.

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

entity message_decoder is 
	port (
		CK : in std_logic; -- 40-MHz Clock
		Q : in std_logic; -- Demodulator Output
		RST : in std_logic; -- Reset
		INCOMING : out std_logic; -- Message Incoming
		RECEIVED : out std_logic; -- Message Received		
		message_id : out integer range 0 to 255;
		message_data : out std_logic_vector (15 downto 0)
	);
end;

architecture behavior of message_decoder is 

constant min_sync_edges : integer := 5;
constant num_message_bits : integer := 24;
constant min_inc_bits : integer := 16;

-- Global Signals
signal SQ, DSQ : boolean;

-- 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;

begin 

	synchronizer : process (CK) is
	begin
		if rising_edge (CK) then
			SQ <= (Q = '1');
			DSQ <= SQ;
		end if;
	end process;

	decoder : process (CK,RST) is 
	
	variable rvs, next_rvs : integer range 0 to 31 := 0;
	variable rvd : std_logic_vector (23 downto 0);
	variable channel_id, completion_code : integer range 0 to 15;
	variable ec : integer range 0 to 31;
	variable done, valid : boolean;
	
	constant rvs_rest : integer := 0;
	constant rvs_s1 : integer := 1;
	constant rvs_s2 : integer := 2;
	constant rvs_s3 : integer := 3;
	constant rvs_s4 : integer := 4;
	constant rvs_s5 : integer := 5;
	constant rvs_s6 : integer := 6;
	constant rvs_s7 : integer := 7;
	constant rvs_s8 : integer := 8;
	constant rvs_s9 : integer := 9;
	constant rvs_s10 : integer := 10;

	constant rvs_r2 : integer := 11;
	constant rvs_r3 : integer := 12;
	constant rvs_r4 : integer := 13;
	constant rvs_r5 : integer := 14;
	constant rvs_r6 : integer := 15;

	constant rvs_receive_start : integer := 16;
	constant rvs_receive_1 : integer := 17;
	constant rvs_receive_2 : integer := 18;
	constant rvs_receive_3 : integer := 19;
	constant rvs_receive_4 : integer := 20;
	
	constant rvs_one : integer := 24;
	constant rvs_zero : integer := 25;
	constant rvs_check : integer := 26;
	constant rvs_store : integer := 27;
	
	constant rvs_clr_ec : integer := 31;

	begin
		if (RST = '1') then
			rvs := rvs_rest;
			rvd := "000000000000000000000000";
			ec := 0;
			RECEIVED <= '0';
		elsif rising_edge(CK) then
			next_rvs := rvs_rest;
			RECEIVED <= '0';
			case rvs is 
				when rvs_rest => -- Waiting for a rising edge on Q.
					if SQ and (not DSQ) then 
						next_rvs := rvs_s1; 
					else
						next_rvs := rvs_rest;
					end if;
					rvd := "000000000000000000000000";
				when rvs_s1 => -- Check for !Q too soon.
					if not SQ then 
						next_rvs := rvs_clr_ec; 
					else
						next_rvs := rvs_s2;
					end if;
				when rvs_s2 => -- Check for !Q too soon.
					if not SQ then 
						next_rvs := rvs_clr_ec; 
					else
						next_rvs := rvs_s3;
					end if;
				when rvs_s3 => -- Check for !Q.
					if not SQ then 
						next_rvs := rvs_s6; 
					else
						next_rvs := rvs_s4;
					end if;
					if (ec < min_sync_edges) then
						ec := ec + 1;
					end if;
				when rvs_s4 => -- Check for !Q.
					if not SQ then 
						next_rvs := rvs_s6; 
					else
						next_rvs := rvs_s5;
					end if;
				when rvs_s5 => 
					-- If Q is still HI, and we have enough edges, this is the start bit.
					-- But if Q is still HI, and we do not have enough edges, we abort.
					-- If Q is LO, we are still receiving start bits.
					if SQ and (ec >= min_sync_edges) then 
						next_rvs := rvs_receive_start; 
					elsif SQ and (ec < min_sync_edges) then
						next_rvs := rvs_clr_ec;
					else
						next_rvs := rvs_s6;
					end if;
				when rvs_s6 => -- Check for Q too soon.
					if SQ then 
						next_rvs := rvs_clr_ec;
					else
						next_rvs := rvs_s7;
					end if;
				when rvs_s7 => -- Check for Q too soon.
					if SQ then 
						next_rvs := rvs_clr_ec;
					else
						next_rvs := rvs_s8;
					end if;
				when rvs_s8 => -- Check for Q.
					if SQ then 
						next_rvs := rvs_s1;
					else
						next_rvs := rvs_s9;
					end if;
				when rvs_s9 => -- Check for Q.
					if SQ then 
						next_rvs := rvs_s1;
					else
						next_rvs := rvs_s10;
					end if;
				when rvs_s10 => -- Check for Q, abort if not.
					if SQ then 
						next_rvs := rvs_s1;
					else
						next_rvs := rvs_clr_ec;
					end if;
					
				when rvs_receive_start =>
					if SQ and (not DSQ) then 
						next_rvs := rvs_one;
					elsif (not SQ) and DSQ then
						next_rvs := rvs_zero;
					else
						next_rvs := rvs_receive_2;
					end if;
					ec := 0;
					
				when rvs_r2 =>
					if (SQ /= DSQ) then
						next_rvs := rvs_clr_ec;
					else 
						next_rvs := rvs_r3;
					end if;
					ec := ec + 1;
				when rvs_r3 =>
					next_rvs := rvs_r4;
				when rvs_r4 =>
					next_rvs := rvs_r5;
				when rvs_r5 =>
					next_rvs := rvs_r6;
				when rvs_r6 =>
					next_rvs := rvs_receive_1;
					
				when rvs_receive_1 =>
					if SQ and (not DSQ) then
						next_rvs := rvs_one;
					elsif (not SQ) and DSQ then 
						next_rvs := rvs_zero;
					else 
						next_rvs := rvs_receive_2;
					end if;
				when rvs_receive_2 =>
					if SQ and (not DSQ) then
						next_rvs := rvs_one;
					elsif (not SQ) and DSQ then 
						next_rvs := rvs_zero;
					else 
						next_rvs := rvs_receive_3;
					end if;
				when rvs_receive_3 =>
					if SQ and (not DSQ) then
						next_rvs := rvs_one;
					elsif (not SQ) and DSQ then 
						next_rvs := rvs_zero;
					else 
						next_rvs := rvs_receive_4;
					end if;
				when rvs_receive_4 =>
					if SQ and (not DSQ) then
						next_rvs := rvs_one;
					elsif (not SQ) and DSQ then 
						next_rvs := rvs_zero;
					else 
						next_rvs := rvs_clr_ec;
					end if;
					
				when rvs_one =>
					if not SQ then
						next_rvs := rvs_clr_ec;
					elsif not DONE then
						next_rvs := rvs_r2;
					else
						next_rvs := rvs_check;
					end if;
					rvd(23 downto 1) := rvd(22 downto 0);
					rvd(0) := '1';
				when rvs_zero =>
					if SQ then 
						next_rvs := rvs_clr_ec;
					elsif not DONE then 
						next_rvs := rvs_r2;
					else
						next_rvs := rvs_check;
					end if;
					rvd(23 downto 1) := rvd(22 downto 0);
					rvd(0) := '0';
	
				when rvs_check =>
					if VALID then
						next_rvs := rvs_store;
					else
						next_rvs := rvs_clr_ec;
					end if;
					
				when rvs_store =>
					next_rvs := rvs_store;
					RECEIVED <= '1';
					
				when rvs_clr_ec =>
					next_rvs := rvs_rest;
					ec := 0;
					
				when others =>
					next_rvs := rvs_rest;
					ec := 0;
			end case;
			
			rvs := next_rvs;
		end if;
		
		DONE := (ec = num_message_bits);
		channel_id := to_integer(unsigned(rvd(23 downto 20)));
		completion_code := to_integer(unsigned(rvd(3 downto 0)));
		VALID := (channel_id /= 0);
		if (ec >= min_inc_bits) then
			INCOMING <= '1';
		else
			INCOMING <= '0';
		end if;
	
		message_data <= rvd(19 downto 4);
		message_id <= (16 * (completion_code - 15 + channel_id)) + channel_id;
	end process;
end behavior;