-- Copyright: Matthias Heinrichs 2014 -- Free for non-comercial use -- No warranty just for fun -- If you want to earn money with this code, ask me first! library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; entity BUS68030 is port( AS_030: inout std_logic ; AS_000: inout std_logic ; RW_000: inout std_logic ; DS_030: inout std_logic ; UDS_000: inout std_logic; LDS_000: inout std_logic; SIZE: inout std_logic_vector ( 1 downto 0 ); AHIGH: inout std_logic_vector ( 31 downto 24 ); A_DECODE: in std_logic_vector ( 23 downto 2 ); A: inout std_logic_vector ( 1 downto 0 ); --A0: inout std_logic; --A1: in std_logic; nEXP_SPACE: in std_logic ; BERR: inout std_logic ; BG_030: in std_logic ; BG_000: out std_logic ; BGACK_030: out std_logic ; BGACK_000: in std_logic ; CLK_030: in std_logic ; CLK_000: in std_logic ; CLK_OSZI: in std_logic ; CLK_DIV_OUT: out std_logic ; CLK_EXP: out std_logic ; FPU_CS: out std_logic ; FPU_SENSE: in std_logic ; IPL_030: out std_logic_vector ( 2 downto 0 ); IPL: in std_logic_vector ( 2 downto 0 ); DSACK1: inout std_logic; DTACK: inout std_logic ; AVEC: out std_logic ; E: out std_logic ; VPA: in std_logic ; VMA: out std_logic ; RST: in std_logic ; RESET: inout std_logic ; RW: inout std_logic ; -- D: inout std_logic_vector ( 31 downto 28 ); FC: in std_logic_vector ( 1 downto 0 ); AMIGA_ADDR_ENABLE: out std_logic ; AMIGA_BUS_DATA_DIR: out std_logic ; AMIGA_BUS_ENABLE_LOW: out std_logic; AMIGA_BUS_ENABLE_HIGH: out std_logic; CIIN: out std_logic ); end BUS68030; architecture Behavioral of BUS68030 is -- values are determined empirically constant DS_SAMPLE : integer := 12; -- for 7.09 MHz Clock with a base clock of 100Mhz and CPU running at 25MHZ --constant DS_SAMPLE : integer := 12; -- for 7.09 MHz Clock with a base clock of 100Mhz and CPU running at 50MHZ TYPE SM_E IS ( E1, E2, E3, E4, E5, E6, E7, E8, E9, E10 ); signal cpu_est : SM_E; TYPE SM_68000 IS ( IDLE_P, IDLE_N, AS_SET_P, AS_SET_N, SAMPLE_DTACK_P, DATA_FETCH_N, DATA_FETCH_P, END_CYCLE_N ); signal SM_AMIGA : SM_68000; --signal Dout:STD_LOGIC_VECTOR(3 downto 0) := "0000"; signal AS_000_INT:STD_LOGIC := '1'; signal AS_000_D0:STD_LOGIC := '1'; signal RW_000_INT:STD_LOGIC := '1'; signal AMIGA_BUS_ENABLE_DMA_HIGH:STD_LOGIC := '1'; signal AMIGA_BUS_ENABLE_DMA_LOW:STD_LOGIC := '1'; signal AS_030_D0:STD_LOGIC := '1'; signal AS_030_D1:STD_LOGIC := '1'; signal nEXP_SPACE_D0:STD_LOGIC := '0'; signal DS_030_D0:STD_LOGIC := '1'; signal AS_030_000_SYNC:STD_LOGIC := '1'; signal BGACK_030_INT:STD_LOGIC := '1'; signal BGACK_030_INT_D:STD_LOGIC := '1'; signal BGACK_030_INT_PRE:STD_LOGIC := '1'; signal AS_000_DMA:STD_LOGIC := '1'; signal DS_000_DMA:STD_LOGIC := '1'; signal RW_000_DMA:STD_LOGIC := '1'; signal CYCLE_DMA: STD_LOGIC_VECTOR ( 1 downto 0 ) := "00"; signal SIZE_DMA: STD_LOGIC_VECTOR ( 1 downto 0 ) := "11"; signal IPL_D0: STD_LOGIC_VECTOR ( 2 downto 0 ) := "111"; signal A0_DMA: STD_LOGIC := '1'; signal VMA_INT: STD_LOGIC := '1'; signal VPA_D: STD_LOGIC := '1'; signal UDS_000_INT: STD_LOGIC := '1'; signal LDS_000_INT: STD_LOGIC := '1'; signal DS_000_ENABLE: STD_LOGIC := '0'; signal DSACK1_INT: STD_LOGIC := '1'; signal CLK_OUT_PRE_50: STD_LOGIC := '1'; signal CLK_OUT_PRE_25: STD_LOGIC := '1'; signal CLK_OUT_PRE: STD_LOGIC := '1'; signal CLK_OUT_PRE_D: STD_LOGIC := '1'; signal CLK_OUT_INT: STD_LOGIC := '1'; signal CLK_OUT_EXP_INT: STD_LOGIC := '1'; signal CLK_030_H: STD_LOGIC := '1'; signal CLK_000_D: STD_LOGIC_VECTOR ( DS_SAMPLE downto 0 ); signal CLK_000_PE: STD_LOGIC := '0'; signal CLK_000_NE: STD_LOGIC := '0'; signal DTACK_D0: STD_LOGIC := '1'; signal RESET_OUT: STD_LOGIC := '0'; signal CLK_030_D0: STD_LOGIC := '0'; signal RST_DLY: STD_LOGIC_VECTOR ( 2 downto 0 ) := "000"; signal CLK_030_PE: STD_LOGIC_VECTOR ( 1 downto 0 ) := "00"; signal AMIGA_DS: STD_LOGIC := '1'; signal DTACK_DMA: STD_LOGIC := '1'; begin CLK_000_PE <= CLK_000_D(0) AND NOT CLK_000_D(1); CLK_000_NE <= NOT CLK_000_D(0) AND CLK_000_D(1); --pos edge clock process --no ansynchronious reset! the reset is sampled synchroniously --this mut be because of the e-clock: The E-Clock has to run CONSTANTLY --or the Amiga will fail to boot from a reset. --However a compilation with no resets on the E-Clock and resets on other signals does not work, either! pos_clk: process(CLK_OSZI) begin if(rising_edge(CLK_OSZI)) then --clk generation : CLK_030_D0 <=CLK_030; CLK_OUT_PRE_50 <= not CLK_OUT_PRE_50; if(CLK_OUT_PRE_50 = '1' )then CLK_OUT_PRE_25<= not CLK_OUT_PRE_25; end if; --here the clock is selected --CLK_OUT_PRE_D <= CLK_OUT_PRE_25; CLK_OUT_PRE_D <= CLK_OUT_PRE_50; -- the external clock to the processor is generated here CLK_OUT_INT <= CLK_OUT_PRE_D; --this way we know the clock of the next state: Its like looking in the future, cool! CLK_OUT_EXP_INT <= CLK_OUT_PRE_D; --delayed Clocks and signals for edge detection CLK_000_D(0) <= CLK_000; CLK_000_D(DS_SAMPLE downto 1) <= CLK_000_D((DS_SAMPLE-1) downto 0); -- e-clock is changed on the FALLING edge! if(CLK_000_NE = '1' ) then case (cpu_est) is when E1 => cpu_est <= E2 ; when E2 => cpu_est <= E3 ; when E3 => cpu_est <= E4; when E4 => cpu_est <= E5 ; when E5 => cpu_est <= E6 ; when E6 => cpu_est <= E7 ; when E7 => cpu_est <= E8 ; when E8 => cpu_est <= E9 ; when E9 => cpu_est <= E10; when E10 => cpu_est <= E1 ; end case; end if; --the statemachine if(RST = '0' ) then VPA_D <= '1'; DTACK_D0 <= '1'; SM_AMIGA <= IDLE_P; AS_000_INT <= '1'; RW_000_INT <= '1'; RW_000_DMA <= '1'; AS_030_000_SYNC <= '1'; UDS_000_INT <= '1'; LDS_000_INT <= '1'; DS_000_ENABLE <= '0'; VMA_INT <= '1'; BG_000 <= '1'; BGACK_030_INT <= '1'; BGACK_030_INT_D <= '1'; BGACK_030_INT_PRE<= '1'; DSACK1_INT <= '1'; IPL_D0 <= "111"; IPL_030 <= "111"; AS_000_DMA <= '1'; DS_000_DMA <= '1'; SIZE_DMA <= "11"; A0_DMA <= '1'; AMIGA_BUS_ENABLE_DMA_HIGH <= '1'; AMIGA_BUS_ENABLE_DMA_LOW <= '1'; AS_030_D0 <= '1'; nEXP_SPACE_D0 <= '1'; DS_030_D0 <= '1'; CYCLE_DMA <= "00"; RST_DLY <= "000"; RESET_OUT <= '0'; AS_000_D0 <='1'; AMIGA_DS <='1'; CLK_030_PE <= "00"; else if(CLK_000_NE='1')then if(RST_DLY="111")then RESET_OUT <= '1'; else RST_DLY <= RST_DLY+1; end if; end if; --now: 68000 state machine and signals --buffering signals AS_030_D0 <= AS_030; AS_030_D1 <= AS_030_D0; nEXP_SPACE_D0 <= nEXP_SPACE; DS_030_D0 <= DS_030; DTACK_D0 <= DTACK; VPA_D <= VPA; --bgack is simple: assert as soon as Amiga asserts but hold bg_ack for one amiga-clock if(BGACK_000='0') then BGACK_030_INT <= '0'; --BGACK_030_INT_PRE<= '0'; elsif ( BGACK_000='1' AND CLK_000_NE='1' AND AS_000 = '1' --the amiga AS can be still active while bgack is deasserted, so wait for this signal too! ) then -- BGACK_000 is high here! --BGACK_030_INT_PRE<= '1'; BGACK_030_INT <= '1'; --hold this signal high until 7m clock goes low end if; BGACK_030_INT_D <= BGACK_030_INT; --bus grant only in idle state if(BG_030= '1')then BG_000 <= '1'; elsif( BG_030= '0' --AND (SM_AMIGA = IDLE_P) and nEXP_SPACE = '1' and AS_030_D0='1' and CLK_000_D(0)='1' ) then --bus granted no local access and no AS_030 running! BG_000 <= '0'; end if; --interrupt buffering to avoid ghost interrupts IPL_D0<=IPL; if(IPL = IPL_D0) then --and CLK_000_PE = '1')then IPL_030<=IPL; end if; -- as030-sampling and FPU-Select if(AS_030 ='1') then -- "async" reset of various signals AS_030_000_SYNC <= '1'; DSACK1_INT <= '1'; AS_000_INT <= '1'; DS_000_ENABLE <= '0'; --RW_000_INT <= '1'; elsif( --CLK_030 = '1' AND --68030 has a valid AS on high clocks AS_030_D1 = '0' AND --as set BGACK_030_INT='1' AND BGACK_030_INT_D='1' AND --no dma -cycle NOT (FC(1)='1' and FC(0)='1' and A_DECODE(19)='0' and A_DECODE(18)='0' and A_DECODE(17)='1' and A_DECODE(16)='0') AND --FPU-Select nEXP_SPACE ='1' and --not an expansion space cycle SM_AMIGA = IDLE_P --last amiga cycle terminated ) then AS_030_000_SYNC <= '0'; end if; -- VMA generation if(CLK_000_NE='1' AND VPA_D='0' AND cpu_est = E4)then --assert VMA_INT <= '0'; elsif(CLK_000_PE='1' AND cpu_est=E1)then --deassert VMA_INT <= '1'; end if; --uds/lds precalculation if (SM_AMIGA = IDLE_N) then --DS: set udl/lds if(A(0)='0') then UDS_000_INT <= '0'; else UDS_000_INT <= '1'; end if; if((A(0)='1' OR SIZE(0)='0' OR SIZE(1)='1')) then LDS_000_INT <= '0'; else LDS_000_INT <= '1'; end if; end if; --Amiga statemachine case (SM_AMIGA) is when IDLE_P => --68000:S0 wait for a falling edge RW_000_INT <= '1'; if( CLK_000_D(3)='0' and CLK_000_D(4)= '1' and AS_030_000_SYNC = '0' and nEXP_SPACE ='1')then -- if this a delayed expansion space detection, do not start an amiga cycle! SM_AMIGA<=IDLE_N; --go to s1 end if; when IDLE_N => --68000:S1 place Adress on bus and wait for rising edge, on a rising CLK_000 look for a amiga adressrobe if(CLK_000_PE='1')then --go to s2 SM_AMIGA <= AS_SET_P; --as for amiga set! RW_000_INT <= RW; AS_000_INT <= '0'; if (RW='1' ) then --read: set udl/lds DS_000_ENABLE <= '1'; end if; end if; when AS_SET_P => --68000:S2 Amiga cycle starts here: since AS is asserted during transition to this state we simply wait here if(CLK_000_NE='1')then --go to s3 SM_AMIGA<=AS_SET_N; end if; when AS_SET_N => --68000:S3: nothing happens here; on a transition to s4: assert uds/lds on write if(CLK_000_PE='1')then --go to s4 -- set DS-Enable without respect to rw: this simplifies the life for the syntesizer DS_000_ENABLE <= '1';--write: set udl/lds earlier than in the specs. this does not seem to harm anything and is saver, than sampling uds/lds too late SM_AMIGA <= SAMPLE_DTACK_P; end if; when SAMPLE_DTACK_P=> --68000:S4 wait for dtack or VMA if( CLK_000_NE='1' and --falling edge ((VPA_D = '1' AND DTACK_D0='0') OR --DTACK end cycle (BERR='0') OR --Bus error (VPA_D='0' AND cpu_est=E9 AND VMA_INT='0')) --VPA end cycle )then --go to s5 SM_AMIGA<=DATA_FETCH_N; end if; when DATA_FETCH_N=> --68000:S5 nothing happens here just wait for positive clock if(CLK_000_PE = '1')then --go to s6 SM_AMIGA<=DATA_FETCH_P; end if; when DATA_FETCH_P => --68000:S6: READ: here comes the data on the bus! --if( (CLK_000_D(DS_SAMPLE-2)='0' AND CLK_000_D((DS_SAMPLE-1))='1' AND not (CLK_030 ='1' and CLK_OUT_PRE_D='0')) OR -- (CLK_000_D(DS_SAMPLE-1)='0' AND CLK_000_D((DS_SAMPLE-0))='1' )) then --go to s7 next 030-clock is not a falling edge: dsack is sampled at the falling edge -- DSACK1_INT <='0'; --end if; --go to s7 dsack is sampled at the falling edge of the 030-clock --if(CLK_000_D(0)='0' and CLK_000_D(1)='1')then if( CLK_000_NE ='1') then SM_AMIGA<=END_CYCLE_N; DSACK1_INT <='0'; end if; when END_CYCLE_N =>--68000:S7: Latch/Store data. Wait here for new cycle and go to IDLE on high clock if(CLK_000_PE='1')then --go to s0 SM_AMIGA<=IDLE_P; RW_000_INT <= '1'; end if; end case; --dma stuff AS_000_D0 <=AS_000; if(UDS_000='0' or LDS_000='0') then AMIGA_DS <='0'; else AMIGA_DS <='1'; end if; if(BGACK_030_INT='0')then --set some signals NOT linked to AS_000='0' RW_000_DMA <= RW_000; -- now determine the size: if both uds and lds is set its 16 bit else 8 bit! if(UDS_000='0' and LDS_000='0') then SIZE_DMA <= "10"; --16bit else SIZE_DMA <= "01"; --8 bit end if; --now calculate the offset: --if uds is set low, a0 is so too. --if only lds is set a1 is high --therefore a1 = uds --great! life is simple here! A0_DMA <= UDS_000; --A0_DMA <= '0'; --A1 is set by the amiga side --here we determine the upper or lower half of the databus if(AHIGH=x"00" and A_DECODE(23 downto 19) = "11101") then --evil hack: E8-EF is assumed to be only 16 bit wide! AMIGA_BUS_ENABLE_DMA_HIGH <= '0'; AMIGA_BUS_ENABLE_DMA_LOW <= '1'; else AMIGA_BUS_ENABLE_DMA_HIGH <= A(1); AMIGA_BUS_ENABLE_DMA_LOW <= not A(1); end if; else RW_000_DMA <= '1'; SIZE_DMA <= "00"; A0_DMA <= '0'; AMIGA_BUS_ENABLE_DMA_HIGH <= '1'; AMIGA_BUS_ENABLE_DMA_LOW <= '1'; end if; if(BGACK_030_INT='0' and AS_000='0')then -- an 68000-memory cycle is three negative edges long! if(CLK_000_NE='1' and CYCLE_DMA<"11")then CYCLE_DMA <= CYCLE_DMA+1; end if; if(nEXP_SPACE ='0') then --presume that all expansion devices can provide a buscycle in 320ns! DTACK_DMA <= '0'; end if; else DTACK_DMA <= '1'; CYCLE_DMA <= "00"; end if; --as can only be done if we know the uds/lds! if( CYCLE_DMA >"00" and AS_000 = '0' and AMIGA_DS ='0' and ( CYCLE_DMA < "11" or RW_000 = '1') )then --set AS_000 if( not(CLK_OUT_INT='0' and CLK_OUT_PRE_D ='1')) then --sampled on rising edges, so we can set AS only if the next clock is not rising!! AS_000_DMA <= '0'; --if(RW_000='1') then DS_000_DMA <='0'; --end if; end if; --if( CLK_OUT_INT='0' and CLK_OUT_PRE_D ='1' and CLK_030_PE <"11" and AS_000_DMA = '0') then --sample rising edges -- CLK_030_PE <= CLK_030_PE+1; --end if; --if(RW_000='0' and CLK_030_PE="01" and CLK_030='1')then -- DS_000_DMA <= '0'; -- write: one clock delayed! --end if; else --CLK_030_PE <= "00"; AS_000_DMA <= '1'; DS_000_DMA <= '1'; end if; end if; end if; end process pos_clk; --output clock assignment CLK_DIV_OUT <= CLK_OUT_INT; CLK_EXP <= CLK_OUT_INT;--not CLK_OUT_EXP_INT; --CLK_DIV_OUT <= 'Z'; --CLK_EXP <= CLK_030; RESET <= 'Z'; --RESET <= 'Z' when RESET_OUT ='1' else '0'; --RST <= '0' when RESET_OUT_AMIGA = '1' else 'Z'; --RESET <= RESET_OUT; -- bus drivers AMIGA_ADDR_ENABLE <= '0'; AMIGA_BUS_ENABLE_HIGH <= '0' WHEN BGACK_030_INT ='1' and AS_030_000_SYNC='0' and AS_030 = '0' else --not (SM_AMIGA = IDLE_P or (SM_AMIGA = END_CYCLE_N and CLK_000 = '1')) ELSE '0' WHEN BGACK_030_INT ='0' AND AMIGA_BUS_ENABLE_DMA_HIGH = '0' ELSE '1'; AMIGA_BUS_ENABLE_LOW <= '0' WHEN BGACK_030_INT ='0' AND AMIGA_BUS_ENABLE_DMA_LOW = '0' ELSE '1'; AMIGA_BUS_DATA_DIR <= not RW_000 WHEN (BGACK_030_INT ='1') ELSE --Amiga READ/WRITE --'0' WHEN (RW_000='1' AND BGACK_030_INT ='1') ELSE --Amiga READ '1' WHEN (RW_000='1' AND BGACK_030_INT ='0' AND nEXP_SPACE = '0' AND AS_000 = '0') ELSE --DMA READ to expansion space --'0' WHEN (RW_000='0' AND BGACK_030_INT ='0' AND AS_000 = '0') ELSE --DMA WRITE to expansion space '0'; --Point towarts TK --dma stuff DTACK <= 'Z';--DTACK will be generated by GARY! --DTACK <= 'Z' when DTACK_DMA='1' else '0'; AS_030 <= 'Z' when BGACK_030_INT ='1' else '0' when AS_000_DMA ='0' and AS_000 ='0' else '1'; DS_030 <= 'Z' when BGACK_030_INT ='1' else '0' when DS_000_DMA ='0' and AS_000 ='0' else '1'; A(0) <= 'Z' when BGACK_030_INT ='1' --tristate on CPU-Cycle else A0_DMA; --drive on DMA-Cycle A(1) <= 'Z'; AHIGH <= "ZZZZZZZZ" when BGACK_030_INT ='1' else x"00"; SIZE <= "ZZ" when BGACK_030_INT ='1' else SIZE_DMA; --rw RW <= 'Z' when BGACK_030_INT ='1' --tristate on CPU cycle else RW_000_DMA; --drive on DMA-Cycle BGACK_030 <= BGACK_030_INT; --fpu FPU_CS <= '0' when AS_030 ='0' and FC(1)='1' and FC(0)='1' and A_DECODE(19)='0' and A_DECODE(18)='0' and A_DECODE(17)='1' and A_DECODE(16)='0' AND BGACK_000='1' AND FPU_SENSE ='0' else '1'; --if no copro is installed: BERR <= '0' when AS_030 ='0' and FC(1)='1' and FC(0)='1' and A_DECODE(19)='0' and A_DECODE(18)='0' and A_DECODE(17)='1' and A_DECODE(16)='0' AND BGACK_000='1' AND FPU_SENSE ='1' else 'Z'; --cache inhibit: Tristate for expansion (it decides) and off for the Amiga CIIN <= '1' WHEN AHIGH(31 downto 24) = x"00" and A_DECODE(23 downto 20) = x"F" and AS_030_D0 ='0' ELSE -- Enable for Kick-rom 'Z' WHEN nEXP_SPACE = '0' ELSE --Tristate for expansion (it decides) '0'; --off for the Amiga --e and VMA E <= '1' when cpu_est = E7 or cpu_est = E8 or cpu_est = E9 or cpu_est = E10 else '0'; VMA <= 'Z' when BGACK_030_INT ='0' else VMA_INT; --AVEC AVEC <= '1'; --as and uds/lds AS_000 <= 'Z' when BGACK_030_INT ='0' or RST ='0' else '0' when AS_000_INT ='0' and AS_030 ='0' else '1'; RW_000 <= 'Z' when BGACK_030_INT ='0' or RST ='0' --tristate on DMA-cycle else RW_000_INT; -- drive on CPU cycle UDS_000 <= 'Z' when BGACK_030_INT ='0' or RST ='0' else --tristate on DMA cycle --'1' when DS_000_ENABLE ='0' else UDS_000_INT when DS_000_ENABLE ='1' -- output on cpu cycle else '1'; -- datastrobe not ready jet LDS_000 <= 'Z' when BGACK_030_INT ='0' or RST ='0' else --tristate on DMA cycle --'1' when DS_000_ENABLE ='0' else LDS_000_INT when DS_000_ENABLE ='1' -- output on cpu cycle else '1'; -- datastrobe not ready jet --dsack DSACK1 <= 'Z' when nEXP_SPACE = '0' or BGACK_030_INT ='0' else --tristate on expansionboard cycle DSACK1_INT when AS_030 = '0' else -- output on amiga cycle --'1' when AS_030 = '1' and AS_030_D0 = '0' else --pull high '1'; end Behavioral;