-- file Cputrack.vhd
-- Cputrack fsmtrack_Easy_v1_0 easy model debug controller implementation
-- copyright: (C) 2023 MPSI Technologies GmbH
-- author: Alexander Wirthmueller (auto-generation)
-- date created: 28 Jun 2025
-- IP header --- ABOVE

-- module parameters:
-- trkclk (clock): mclk
-- ratioTrkclk (clock ratio wrt. master clock): 1
-- capts (FSM's to be captured): hostif.op;rpuif.op
-- trigs (triggers): trigA;trigB;trigC;trigD
-- sizeSeqbuf (sequence buffer size [kB]): 4

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

use work.Dbecore.all;
use work.Zuvsp.all;

entity Cputrack is
	port (
		reset: in std_logic;
		mclk: in std_logic;

		trigA: in std_logic;
		trigB: in std_logic;
		trigC: in std_logic;
		trigD: in std_logic;

		getInfoTixVState: out std_logic_vector(7 downto 0);
		getInfoTCapt: out std_logic_vector(31 downto 0);
		getInfoPtr0Seqbuf: out std_logic_vector(15 downto 0);

		reqInvSelect: in std_logic;
		ackInvSelect: out std_logic;

		selectTixVCapture: in std_logic_vector(7 downto 0);
		selectStaTixVTrigger: in std_logic_vector(7 downto 0);
		selectStaFallingNotRising: in std_logic_vector(7 downto 0);
		selectStoTixVTrigger: in std_logic_vector(7 downto 0);
		selectStoFallingNotRising: in std_logic_vector(7 downto 0);

		reqInvSet: in std_logic;
		ackInvSet: out std_logic;

		setRng: in std_logic_vector(7 downto 0);
		setTCaptMax: in std_logic_vector(31 downto 0);

		reqSeqbufToHostif: in std_logic;
		ackSeqbufToHostif: out std_logic;
		dneSeqbufToHostif: in std_logic;
		avllenSeqbufToHostif: out std_logic_vector(31 downto 0);

		seqbufToHostifAXIS_tready: in std_logic;
		seqbufToHostifAXIS_tvalid: out std_logic;
		seqbufToHostifAXIS_tdata: out std_logic_vector(63 downto 0);
		seqbufToHostifAXIS_tlast: out std_logic;

		hostifStateOp: in std_logic_vector(7 downto 0);
		rpuifStateOp: in std_logic_vector(7 downto 0)
	);
end Cputrack;

architecture Rtl of Cputrack is

	------------------------------------------------------------------------
	-- component declarations
	------------------------------------------------------------------------

	component Dpbram_size4kB_a32b64 is
		port (
			rstA: in std_logic;
			clkA: in std_logic;

			enA: in std_logic;
			weA: in std_logic;

			aA: in std_logic_vector(9 downto 0);
			drdA: out std_logic_vector(31 downto 0);
			dwrA: in std_logic_vector(31 downto 0);

			rstB: in std_logic;
			clkB: in std_logic;

			enB: in std_logic;
			weB: in std_logic;

			aB: in std_logic_vector(8 downto 0);
			drdB: out std_logic_vector(63 downto 0);
			dwrB: in std_logic_vector(63 downto 0)
		);
	end component;

	------------------------------------------------------------------------
	-- signal declarations
	------------------------------------------------------------------------

	constant wD: natural := 64;
	constant logWD: natural := 3;

	constant sizeSeqbuf: natural := 4096;
	constant logSizeSeqbuf: natural := 12;

	constant tixVCaptureHostifOp: std_logic_vector(7 downto 0) := x"01";
	constant tixVCaptureRpuifOp: std_logic_vector(7 downto 0) := x"02";

	constant tixVStateIdle: std_logic_vector(7 downto 0) := x"00";
	constant tixVStateArm: std_logic_vector(7 downto 0) := x"01";
	constant tixVStateAcq: std_logic_vector(7 downto 0) := x"02";
	constant tixVStateDone: std_logic_vector(7 downto 0) := x"03";

	constant tixVTriggerVoid: std_logic_vector(7 downto 0) := x"00";
	constant tixVTriggerTrigA: std_logic_vector(7 downto 0) := x"02";
	constant tixVTriggerTrigB: std_logic_vector(7 downto 0) := x"03";
	constant tixVTriggerTrigC: std_logic_vector(7 downto 0) := x"04";
	constant tixVTriggerTrigD: std_logic_vector(7 downto 0) := x"05";

	---- main operation (op)
	type stateOp_t is (
		stateOpInit,
		stateOpInv,
		stateOpIdle,
		stateOpStart,
		stateOpRun,
		stateOpStop,
		stateOpClear
	);
	signal stateOp: stateOp_t;

	signal ackInvSelect_sig: std_logic;
	signal ackInvSet_sig: std_logic;

	signal tixVCapture: std_logic_vector(7 downto 0);

	signal staTixVTrigger: std_logic_vector(7 downto 0);
	signal staFallingNotRising: boolean;

	signal stoTixVTrigger: std_logic_vector(7 downto 0);
	signal stoFallingNotRising: boolean;

	signal strbStart: std_logic;
	signal strbStop: std_logic;

	---- sequence operation (seq)
	type stateSeq_t is (
		stateSeqInit,
		stateSeqIdle,
		stateSeqRun,
		stateSeqDone,
		stateSeqClear,
		stateSeqAckClear
	);
	signal stateSeq: stateSeq_t;

	signal rdySeq: std_logic;
	signal dneSeq: std_logic;

	signal TCapt: std_logic_vector(31 downto 0);

	signal ptr0SeqbufTrackNotFixed: std_logic;
	signal ptr0Seqbuf: natural range 0 to sizeSeqbuf/4-1;

	signal enSeqbuf: std_logic;

	signal aSeqbuf: natural range 0 to sizeSeqbuf/4-1;
	signal aSeqbuf_vec: std_logic_vector(logSizeSeqbuf-2-1 downto 0);

	signal dwrSeqbuf: std_logic_vector(31 downto 0);

	---- sequence buffer B/hostif-facing operation (seqbufB)
	type stateSeqbufB_t is (
		stateSeqbufBInit,
		stateSeqbufBIdle,
		stateSeqbufBXferA, stateSeqbufBXferB,
		stateSeqbufBDone,
		stateSeqbufBClear
	);
	signal stateSeqbufB: stateSeqbufB_t;

	signal enSeqbufB: std_logic;

	signal aSeqbufB: natural range 0 to sizeSeqbuf/(wD/8)-1;
	signal aSeqbufB_vec: std_logic_vector(logSizeSeqbuf-logWD-1 downto 0);

	---- sampling operation (sample)
	signal state: std_logic_vector(7 downto 0);

	signal start: std_logic;
	signal stop: std_logic;

	---- handshake

	-- op to (many)
	signal reqOpClear: std_logic;

	-- seqbufB to seq
	signal reqSeqbufBToSeqClear: std_logic;
	signal ackSeqbufBToSeqClear: std_logic;

begin

	------------------------------------------------------------------------
	-- sub-module instantiation
	------------------------------------------------------------------------

	mySeqbuf : Dpbram_size4kB_a32b64
		port map (
			rstA => reset,
			clkA => mclk,

			enA => enSeqbuf,
			weA => '1',

			aA => aSeqbuf_vec,
			drdA => open,
			dwrA => dwrSeqbuf,

			rstB => reset,
			clkB => mclk,

			enB => enSeqbufB,
			weB => '0',

			aB => aSeqbufB_vec,
			drdB => seqbufToHostifAXIS_tdata,
			dwrB => (others => '0')
		);

	------------------------------------------------------------------------
	-- implementation: main operation (op)
	------------------------------------------------------------------------

	ackInvSelect <= ackInvSelect_sig;
	ackInvSet <= ackInvSet_sig;

	getInfoTixVState <= tixVStateArm when stateOp=stateOpIdle
				else tixVStateAcq when stateOp=stateOpRun
				else tixVStateDone when stateOp=stateOpStop
				else tixVStateIdle;
	getInfoTCapt <= TCapt;
	getInfoPtr0Seqbuf <= std_logic_vector(to_unsigned(ptr0Seqbuf, 16));

	strbStart <= '1' when stateOp=stateOpStart else '0';

	reqOpClear <= '1' when stateOp=stateOpClear else '0';

	process (reset, mclk, stateOp)
		variable rng: boolean;
		variable restart: boolean;

		variable TCaptMax: std_logic_vector(31 downto 0);

		variable strb_last: std_logic;

	begin
		if reset='1' then
			stateOp <= stateOpInit;
			ackInvSelect_sig <= '0';
			ackInvSet_sig <= '0';
			tixVCapture <= x"01";
			staTixVTrigger <= tixVTriggerVoid;
			staFallingNotRising <= false;
			stoTixVTrigger <= tixVTriggerVoid;
			stoFallingNotRising <= false;
			strbStop <= '0';

			rng := false;
			restart := false;
			TCaptMax := (others => '0');
			strb_last := '0';

		elsif rising_edge(mclk) then
			if stateOp=stateOpInit or (stateOp/=stateOpInv and (reqInvSelect='1' or reqInvSet='1'))  then
				strbStop <= '0';

				if reqInvSelect='1' then
					tixVCapture <= selectTixVCapture;
					staTixVTrigger <= selectStaTixVTrigger;
					staFallingNotRising <= (selectStaFallingNotRising=tru8);
					stoTixVTrigger <= selectStoTixVTrigger;
					stoFallingNotRising <= (selectStoFallingNotRising=tru8);

					ackInvSelect_sig <= '1';
					stateOp <= stateOpInv;

				elsif reqInvSet='1' then
					if setRng=tru8 then
						rng := true;
					else
						rng := false;
					end if;
					TCaptMax := setTCaptMax;

					ackInvSet_sig <= '1';
					stateOp <= stateOpInv;

				else
					restart := false;

					if rng then
						strb_last := start;

						stateOp <= stateOpIdle;

					else
						stateOp <= stateOpInit;
					end if;
				end if;

			elsif stateOp=stateOpInv then
				if (reqInvSet='0' and ackInvSet_sig='1') or (reqInvSelect='0' and ackInvSelect_sig='1') then
					ackInvSet_sig <= '0';
					ackInvSelect_sig <= '0';

					strbStop <= '1';

					restart := true;

					stateOp <= stateOpStop;
				end if;

			elsif stateOp=stateOpIdle then
				if staTixVTrigger=tixVTriggerVoid or (not staFallingNotRising and strb_last='0' and start='1') or (staFallingNotRising and strb_last='1' and start='0') then
					stateOp <= stateOpStart;
				else
					strb_last := start;
				end if;

			elsif stateOp=stateOpStart then -- strbStart='1'
				strb_last := stop;

				stateOp <= stateOpRun;

			elsif stateOp=stateOpRun then
				if (stoTixVTrigger=tixVTriggerVoid and unsigned(TCaptMax)/=0 and (unsigned(TCapt)>=unsigned(TCaptMax) or dneSeq='1')) or unsigned(TCapt)>=x"FFFFFF00" or (not stoFallingNotRising and strb_last='0' and stop='1') or (stoFallingNotRising and strb_last='1' and stop='0') then
					strbStop <= '1';

					stateOp <= stateOpStop;

				else
					strb_last := stop;
				end if;

			elsif stateOp=stateOpStop then
				strbStop <= '0';

				if restart then
					stateOp <= stateOpClear;

				else
					if rdySeq='1' then
						stateOp <= stateOpInit;
					end if;
				end if;

			elsif stateOp=stateOpClear then -- reqOpClear='1'
				if rdySeq='1' then
					stateOp <= stateOpInit;
				end if;
			end if;
		end if;
	end process;

	------------------------------------------------------------------------
	-- implementation: sampling operation (sample)
	------------------------------------------------------------------------

	process (reset, mclk)

	begin
		if reset='1' then
			state <= (others => '1');
			start <= '0';
			stop <= '0';

		elsif rising_edge(mclk) then
			case tixVCapture is
				when tixVCaptureHostifOp =>
					state <= hostifStateOp;
				when tixVCaptureRpuifOp =>
					state <= rpuifStateOp;
				when others =>
					state <= (others => '1');
			end case;

			case staTixVTrigger is
				when tixVTriggerTrigA =>
					start <= trigA;
				when tixVTriggerTrigB =>
					start <= trigB;
				when tixVTriggerTrigC =>
					start <= trigC;
				when tixVTriggerTrigD =>
					start <= trigD;
				when others =>
					start <= '0';
			end case;

			case stoTixVTrigger is
				when tixVTriggerTrigA =>
					stop <= trigA;
				when tixVTriggerTrigB =>
					stop <= trigB;
				when tixVTriggerTrigC =>
					stop <= trigC;
				when tixVTriggerTrigD =>
					stop <= trigD;
				when others =>
					stop <= '0';
			end case;
		end if;
	end process;

	------------------------------------------------------------------------
	-- implementation: sequence operation (seq)
	------------------------------------------------------------------------

	rdySeq <= '1' when stateSeq=stateSeqIdle else '0';
	dneSeq <= '1' when stateSeq=stateSeqDone else '0';

	ptr0Seqbuf <= aSeqbuf when ptr0SeqbufTrackNotFixed='1' else 0;

	aSeqbuf_vec <= std_logic_vector(to_unsigned(aSeqbuf, logSizeSeqbuf-2));

	ackSeqbufBToSeqClear <= '1' when stateSeq=stateSeqAckClear else '0';

	process (reset, mclk, stateSeq)
		variable quad: std_logic_vector(31 downto 0);

		variable i: natural range 0 to 3;

		variable first: boolean;
		variable stop_lcl: boolean;

		variable burstState: std_logic_vector(7 downto 0);
		variable burstCnt: natural range 0 to 65535;

	begin
		if reset='1' then
			stateSeq <= stateSeqInit;
			TCapt <= (others => '0');
			ptr0SeqbufTrackNotFixed <= '0';
			enSeqbuf <= '0';
			aSeqbuf <= 0;
			dwrSeqbuf <= (others => '0');

			quad := (others => '0');
			i := 0;
			first := true;
			stop_lcl := false;
			burstState := (others => '1');
			burstCnt := 0;

		elsif rising_edge(mclk) then
			if stateSeq=stateSeqInit then
				TCapt <= (others => '0');
				ptr0SeqbufTrackNotFixed <= '0';
				enSeqbuf <= '0';
				aSeqbuf <= 0;
				dwrSeqbuf <= (others => '0');

				quad := (others => '0');
				i := 0;
				first := true;
				stop_lcl := false;
				burstState := (others => '1');
				burstCnt := 0;

				stateSeq <= stateSeqIdle;

			elsif stateSeq=stateSeqIdle then -- rdySeq='1'
				if strbStart='1' then
					stateSeq <= stateSeqRun;
				end if;

			elsif stateSeq=stateSeqRun then
				TCapt <= std_logic_vector(unsigned(TCapt) + 1);

				if strbStop='1' or (stoTixVTrigger=tixVTriggerVoid and (i=0 and aSeqbuf=sizeSeqbuf/4-1)) then
					stop_lcl := true;
				end if;

				quad((i+1)*8-1 downto i*8) := state;

				if i=3 then
					if quad(23 downto 16)=state and quad(15 downto 8)=state and quad(7 downto 0)=state then
						if state/=burstState or burstCnt=65535 then
							if not first then
								if aSeqbuf=sizeSeqbuf/4-1 then
									ptr0SeqbufTrackNotFixed <= '1';
									aSeqbuf <= 0;
								else
									aSeqbuf <= aSeqbuf + 1;
								end if;
							end if;

							burstCnt := 0;
						end if;

						burstState := state;
						burstCnt := burstCnt + 1;

						dwrSeqbuf <= x"FF" & burstState & std_logic_vector(to_unsigned(burstCnt, 16));

					else
						burstState := (others => '1');
						burstCnt := 0;

						if not first then
							if aSeqbuf=sizeSeqbuf/4-1 then
								ptr0SeqbufTrackNotFixed <= '1';
								aSeqbuf <= 0;
							else
								aSeqbuf <= aSeqbuf + 1;
							end if;
						end if;

						dwrSeqbuf <= quad;
					end if;

					enSeqbuf <= '1';

					i := 0;
					first := false;

					stateSeq <= stateSeqRun;

				else
					enSeqbuf <= '0';

					if stop_lcl then
						stateSeq <= stateSeqDone;

					else
						i := i + 1;

						stateSeq <= stateSeqRun;
					end if;
				end if;

			elsif stateSeq=stateSeqDone then
				if reqOpClear='1' or reqSeqbufBToSeqClear='1' then
					aSeqbuf <= 0;
					dwrSeqbuf <= (others => '1');

					stateSeq <= stateSeqClear;
				end if;

			elsif stateSeq=stateSeqClear then
				if aSeqbuf=sizeSeqbuf/4-1 then
					if reqSeqbufBToSeqClear='1' then
						stateSeq <= stateSeqAckClear;
					else
						stateSeq <= stateSeqInit;
					end if;

				else
					aSeqbuf <= aSeqbuf + 1;
				end if;

			elsif stateSeq=stateSeqAckClear then -- ackSeqbufBToSeqClear='1'
				if reqSeqbufBToSeqClear='0' then
					stateSeq <= stateSeqInit;
				end if;
			end if;
		end if;
	end process;

	------------------------------------------------------------------------
	-- implementation: sequence buffer B/hostif-facing operation (seqbufB)
	------------------------------------------------------------------------

	ackSeqbufToHostif <= '1' when stateSeqbufB=stateSeqbufBXferA or stateSeqbufB=stateSeqbufBXferB or stateSeqbufB=stateSeqbufBDone else '0';

	avllenSeqbufToHostif <= std_logic_vector(to_unsigned(sizeSeqbuf, 32)) when dneSeq='1'
				else (others => '0');

	seqbufToHostifAXIS_tvalid <= '1' when stateSeqbufB=stateSeqbufBXferB else '0';

	seqbufToHostifAXIS_tlast <= '1' when stateSeqbufB=stateSeqbufBXferB and aSeqbufB=(sizeSeqbuf/(wD/8))-1 else '0';

	enSeqbufB <= '1' when stateSeqbufB=stateSeqbufBXferA else '0';

	aSeqbufB_vec <= std_logic_vector(to_unsigned(aSeqbufB, logSizeSeqbuf-logWD));

	reqSeqbufBToSeqClear <= '1' when stateSeqbufB=stateSeqbufBClear else '0';

	process (reset, mclk, stateSeqbufB)

	begin
		if reset='1' then
			stateSeqbufB <= stateSeqbufBInit;
			aSeqbufB <= 0;

		elsif rising_edge(mclk) then
			if stateSeqbufB=stateSeqbufBInit then
				aSeqbufB <= 0;

				stateSeqbufB <= stateSeqbufBIdle;

			elsif stateSeqbufB=stateSeqbufBIdle then
				if reqSeqbufToHostif='1' then
					stateSeqbufB <= stateSeqbufBXferA;
				end if;

			elsif stateSeqbufB=stateSeqbufBXferA then -- enSeqbufB='1'
				stateSeqbufB <= stateSeqbufBXferB;
				
			elsif stateSeqbufB=stateSeqbufBXferB then -- seqbufToHostifAXIS_tvalid='1'
				if seqbufToHostifAXIS_tready='1' then
					if aSeqbufB=(sizeSeqbuf/(wD/8))-1 then
						stateSeqbufB <= stateSeqbufBDone;

					else
						aSeqbufB <= aSeqbufB + 1;

						stateSeqbufB <= stateSeqbufBXferA;
					end if;

				elsif reqSeqbufToHostif='0' then
					stateSeqbufB <= stateSeqbufBInit;
				end if;

			elsif stateSeqbufB=stateSeqbufBDone then
				if dneSeqbufToHostif='1' then
					stateSeqbufB <= stateSeqbufBClear;

				elsif reqSeqbufToHostif='0' then
					stateSeqbufB <= stateSeqbufBInit;
				end if ;

			elsif stateSeqbufB=stateSeqbufBClear then -- reqSeqbufBToSeqClear='1'
				if reqSeqbufToHostif='0' and ackSeqbufBToSeqClear='1' then
					stateSeqbufB <= stateSeqbufBInit;
				end if;
			end if;
		end if;
	end process;

end Rtl;
