-- file Rotary.vhd
-- Rotary easy model controller implementation
-- copyright: (C) 2016-2020 MPSI Technologies GmbH
-- author: Alexander Wirthmueller (auto-generation)
-- date created: 10 May 2025
-- IP header --- ABOVE

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

use work.Dbecore.all;
use work.Tivsp.all;

entity Rotary is
	port (
		reset: in std_logic;
		mclk: in std_logic;
		tkclk: in std_logic;

		toStepmuxNslp: out std_logic;
		toStepmuxM0: out std_logic;
		toStepmuxM0z: out std_logic;
		toStepmuxM1: out std_logic;
		toStepmuxDir: out std_logic;
		toStepmuxStep: out std_logic;

		reqInvConfig: in std_logic;
		ackInvConfig: out std_logic;

		configTixVStepmode: in std_logic_vector(7 downto 0);
		configTStep: in std_logic_vector(7 downto 0);

		getInfoTixVState: out std_logic_vector(7 downto 0);
		getInfoAngle: out std_logic_vector(15 downto 0);

		reqInvMoveto: in std_logic;
		ackInvMoveto: out std_logic;

		movetoAngle: in std_logic_vector(15 downto 0);

		reqInvSet: in std_logic;
		ackInvSet: out std_logic;

		setRng: in std_logic_vector(7 downto 0);
		setCcwNotCw: in std_logic_vector(7 downto 0);

		reqInvZero: in std_logic;
		ackInvZero: out std_logic
	);
end Rotary;

architecture Rtl of Rotary is

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

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

	constant tixVStateIdle: std_logic_vector(7 downto 0) := x"00";
	constant tixVStateMove: std_logic_vector(7 downto 0) := x"01";

	constant tixVStepmodeFull: std_logic_vector(7 downto 0) := x"00";
	constant tixVStepmodeHalf: std_logic_vector(7 downto 0) := x"01";
	constant tixVStepmodeQuarter: std_logic_vector(7 downto 0) := x"02";

	---- main operation (op)
	type stateOp_t is (
		stateOpInit,
		stateOpInv,
		stateOpRunA, stateOpRunB
	);
	signal stateOp: stateOp_t;

	signal ackInvConfig_sig: std_logic;
	signal ackInvMoveto_sig: std_logic;
	signal ackInvSet_sig: std_logic;
	signal ackInvZero_sig: std_logic;
	signal tixVStepmode: std_logic_vector(7 downto 0);
	signal TStep: natural range 0 to 255; -- should be > 15 ms full cycle or 75
	signal rng: std_logic;
	signal angle: unsigned(13 downto 0);
	signal ccwNotCw: std_logic;
	signal step: std_logic;

	signal m0: std_logic;
	signal m0z: std_logic;
	signal m1: std_logic;

	-- IP sigs.op.cust --- INSERT

	---- other
	-- IP sigs.oth.cust --- INSERT

begin

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

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

	-- IP impl.op.wiring --- BEGIN
	ackInvConfig <= ackInvConfig_sig;
	ackInvMoveto <= ackInvMoveto_sig;
	ackInvSet <= ackInvSet_sig;
	ackInvZero <= ackInvZero_sig;
	toStepmuxNslp <= '1';
	toStepmuxDir <= ccwNotCw;
	toStepmuxStep <= step;

	m0 <= '1' when tixVStepmode=tixVStepmodeHalf else '0';
	toStepmuxM0 <= m0;
	m0z <= '1' when tixVStepmode=tixVStepmodeQuarter else '0';
	toStepmuxM0z <= m0z;
	m1 <= '0';
	toStepmuxM1 <= m1;
	-- IP impl.op.wiring --- END

	-- IP impl.op.wiring.cust --- RBEGIN
	getInfoTixVState <= tixVStateMove when stateOp=stateOpRunA or stateOp=StateOpRunB else tixVStateIdle;
	getInfoAngle <= "00" & std_logic_vector(angle);
	-- IP impl.op.wiring.cust --- REND

	-- IP impl.op.rising --- BEGIN
	process (reset, mclk)
		-- IP impl.op.vars --- RBEGIN
		constant anglemax: natural := 15999;
		constant anglemax_vec: unsigned(13 downto 0) := "11111001111111";

		variable lsb: natural range 0 to 2;

		variable target: unsigned(13 downto 0);
		variable dAngle: integer range -anglemax to anglemax;

		variable targetNotSteady: boolean;
		variable atTarget: boolean;

		variable i: natural range 0 to 255;
		-- IP impl.op.vars --- REND

	begin
		if reset='1' then
			-- IP impl.op.asyncrst --- BEGIN
			stateOp <= stateOpInit;
			ackInvConfig_sig <= '0';
			ackInvMoveto_sig <= '0';
			ackInvSet_sig <= '0';
			ackInvZero_sig <= '0';
			tixVStepmode <= tixVStepmodeFull;
			TStep <= 75;
			rng <= '0';
			angle <= (others => '0');
			ccwNotCw <= '0';
			step <= '0';

			-- IP impl.op.asyncrst --- END
			-- IP impl.op.asyncrst.cust --- IBEGIN
			lsb := 2;
			target := (others => '0');
			dAngle := 0;
			targetNotSteady := false;
			atTarget := false;
			i := 0;
			-- IP impl.op.asyncrst.cust --- IEND

		elsif rising_edge(mclk) then
			if (stateOp=stateOpInit or (stateOp/=stateOpInv and (reqInvConfig='1' or reqInvMoveto='1' or reqInvSet='1' or reqInvZero='1'))) then
				-- IP impl.op.syncrst --- RBEGIN
				step <= '0';
				-- IP impl.op.syncrst --- REND
				-- IP impl.op.syncrst.cust --- IBEGIN
				i := 0;
				-- IP impl.op.syncrst.cust --- IEND

				if reqInvConfig='1' then
					-- IP impl.op.init.invConfig --- IBEGIN
					tixVStepmode <= configTixVStepmode;
					case configTixVStepmode is
						when tixVStepmodeFull =>
							lsb := 2;
						when tixVStepmodeHalf =>
							lsb := 1;
						when others =>
							lsb := 0;
					end case;

					TStep <= to_integer(unsigned(configTStep));

					ackInvConfig_sig <= '1';
					ackInvMoveto_sig <= '0';
					ackInvSet_sig <= '0';
					ackInvZero_sig <= '0';
					-- IP impl.op.init.invConfig --- IEND

					stateOp <= stateOpInv;

				elsif reqInvMoveto='1' then
					-- IP impl.op.init.invMoveto --- IBEGIN
					targetNotSteady := true;

					-- determine shortest path
					target := unsigned(movetoAngle(13 downto 0));
					dAngle := to_integer(target) - to_integer(angle);

					if dAngle > anglemax/2 then
						-- dAngle := dAngle - anglemax - 1; -- will become negative
						ccwNotCw <= '1';
					elsif dAngle < -anglemax/2 then
						--dAngle := dAngle + anglemax + 1; -- will become positive
						ccwNotCw <= '0';
					elsif dAngle > 0 then
						ccwNotCw <= '0';
					else
						ccwNotCw <= '1';
					end if;

					ackInvConfig_sig <= '0';
					ackInvMoveto_sig <= '1';
					ackInvSet_sig <= '0';
					ackInvZero_sig <= '0';
					-- IP impl.op.init.invMoveto --- IEND

					stateOp <= stateOpInv;

				elsif reqInvSet='1' then
					-- IP impl.op.init.invSet --- IBEGIN
					targetNotSteady := false;

					if setRng=tru8 then
						rng <= '1';
					else
						rng <= '0';
					end if;
					if setCcwNotCw=tru8 then
						ccwNotCw <= '1';
					else
						ccwNotCw <= '0';
					end if;

					ackInvConfig_sig <= '0';
					ackInvMoveto_sig <= '0';
					ackInvSet_sig <= '1';
					ackInvZero_sig <= '0';
					-- IP impl.op.init.invSet --- IEND

					stateOp <= stateOpInv;

				elsif reqInvZero='1' then
					-- IP impl.op.init.invZero --- IBEGIN
					angle <= (others => '0');

					ackInvConfig_sig <= '0';
					ackInvMoveto_sig <= '0';
					ackInvSet_sig <= '0';
					ackInvZero_sig <= '1';
					-- IP impl.op.init.invZero --- IEND

					stateOp <= stateOpInv;

				else
					-- IP impl.op.init.start --- IBEGIN
					ackInvConfig_sig <= '0';
					ackInvMoveto_sig <= '0';
					ackInvSet_sig <= '0';
					ackInvZero_sig <= '0';

					atTarget := (angle(13 downto lsb) = target(13 downto lsb));
					-- IP impl.op.init.start --- IEND

					if TStep/=0 and ((not targetNotSteady and rng='1') or (targetNotSteady and not atTarget)) then
						stateOp <= stateOpRunB;

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

			elsif stateOp=stateOpInv then
				if ((reqInvConfig='0' and ackInvConfig_sig='1') or (reqInvMoveto='0' and ackInvMoveto_sig='1') or (reqInvSet='0' and ackInvSet_sig='1') or (reqInvZero='0' and ackInvZero_sig='1')) then
					-- IP impl.op.inv.reset --- IBEGIN
					ackInvConfig_sig <= '0';
					ackInvMoveto_sig <= '0';
					ackInvSet_sig <= '0';
					ackInvZero_sig <= '0';
					-- IP impl.op.inv.reset --- IEND

					stateOp <= stateOpInit;
				end if;

			elsif stateOp=stateOpRunA then
				if tkclk='1' then
					-- IP impl.op.runA --- IBEGIN
					if i=TStep then
						i := 0;

						if ccwNotCw='0' then
							if angle(13 downto lsb)=anglemax_vec(13 downto lsb) then
								angle(13 downto lsb) <= (others => '0');
							else
								angle(13 downto lsb) <= angle(13 downto lsb) + 1;
							end if;
						else
							if angle(13 downto lsb)=0 then
								angle(13 downto lsb) <= anglemax_vec(13 downto lsb);
							else
								angle(13 downto lsb) <= angle(13 downto lsb) - 1;
							end if;
						end if;
						
						step <= not step;
					end if;
					-- IP impl.op.runA --- IEND

					stateOp <= stateOpRunB;
				end if;

			elsif stateOp=stateOpRunB then
				if tkclk='0' then
					-- IP impl.op.runB --- IBEGIN
					i := i + 1;

					atTarget := (angle(13 downto lsb) = target(13 downto lsb));
					-- IP impl.op.runB --- IEND

					if targetNotSteady and atTarget then
						stateOp <= stateOpInit;

					else
						stateOp <= stateOpRunA;
					end if;
				end if;
			end if;
		end if;
	end process;
	-- IP impl.op.rising --- END

	------------------------------------------------------------------------
	-- implementation: other 
	------------------------------------------------------------------------

	
	-- IP impl.oth.cust --- INSERT

end Rtl;
