From b39f0034394269c7dee2e3152e2f29c5453905ec Mon Sep 17 00:00:00 2001 From: Gyorgy Szombathelyi Date: Tue, 22 Jan 2019 12:27:56 +0100 Subject: [PATCH] [C64] Update CIA to Rayne's version --- cores/c64/C64_mist.qsf | 5 +- cores/c64/rtl/cia6526.vhd | 753 ------------------------------- cores/c64/rtl/fpga64_sid_iec.vhd | 72 +-- cores/c64/rtl/mos6526.v | 439 ++++++++++++++++++ cores/c64/rtl/mos6526.vhd | 29 ++ 5 files changed, 510 insertions(+), 788 deletions(-) delete mode 100644 cores/c64/rtl/cia6526.vhd create mode 100644 cores/c64/rtl/mos6526.v create mode 100644 cores/c64/rtl/mos6526.vhd diff --git a/cores/c64/C64_mist.qsf b/cores/c64/C64_mist.qsf index f6cb971..cff9bc3 100644 --- a/cores/c64/C64_mist.qsf +++ b/cores/c64/C64_mist.qsf @@ -156,7 +156,7 @@ set_global_assignment -name RESERVE_DCLK_AFTER_CONFIGURATION "USE AS REGULAR IO" set_global_assignment -name CYCLONEII_RESERVE_NCEO_AFTER_CONFIGURATION "USE AS REGULAR IO" set_global_assignment -name OPTIMIZE_HOLD_TIMING "ALL PATHS" set_global_assignment -name OPTIMIZE_MULTI_CORNER_TIMING ON -set_global_assignment -name FITTER_EFFORT "STANDARD FIT" +set_global_assignment -name FITTER_EFFORT "AUTO FIT" # Assembler Assignments # ===================== @@ -337,7 +337,8 @@ set_global_assignment -name VHDL_FILE rtl/fpga64_rgbcolor.vhd set_global_assignment -name VHDL_FILE rtl/fpga64_keyboard_matrix_mark_mcdougall.vhd set_global_assignment -name VHDL_FILE rtl/fpga64_bustiming.vhd set_global_assignment -name VHDL_FILE rtl/fpga64_buslogic_roms_mmu.vhd -set_global_assignment -name VHDL_FILE rtl/cia6526.vhd +set_global_assignment -name VERILOG_FILE rtl/mos6526.v +set_global_assignment -name VHDL_FILE rtl/mos6526.vhd set_global_assignment -name VHDL_FILE rtl/cpu_6510.vhd set_global_assignment -name VERILOG_FILE rtl/cartridge.v set_global_assignment -name VHDL_FILE rtl/rom_c64_chargen.vhd diff --git a/cores/c64/rtl/cia6526.vhd b/cores/c64/rtl/cia6526.vhd deleted file mode 100644 index 9a8a863..0000000 --- a/cores/c64/rtl/cia6526.vhd +++ /dev/null @@ -1,753 +0,0 @@ --- ----------------------------------------------------------------------- --- --- FPGA 64 --- --- A fully functional commodore 64 implementation in a single FPGA --- --- ----------------------------------------------------------------------- --- Peter Wendrich (pwsoft@syntiac.com) --- http://www.syntiac.com/fpga64.html --- ----------------------------------------------------------------------- --- --- 6526 Complex Interface Adapter --- --- rev 1 - june17 / TOD alarms --- ----------------------------------------------------------------------- - -library IEEE; -use IEEE.STD_LOGIC_1164.ALL; -use IEEE.numeric_std.ALL; - --- ----------------------------------------------------------------------- - -entity cia6526 is - generic ( - todEnabled : std_logic := '1' - ); - port ( - clk: in std_logic; - todClk: in std_logic; - reset: in std_logic; - enable: in std_logic; - cs: in std_logic; - we: in std_logic; -- Write strobe - rd: in std_logic; -- Read strobe - - addr: in unsigned(3 downto 0); - di: in unsigned(7 downto 0); - do: out unsigned(7 downto 0); - - ppai: in unsigned(7 downto 0); - ppao: out unsigned(7 downto 0); - ppad: out unsigned(7 downto 0); - - ppbi: in unsigned(7 downto 0); - ppbo: out unsigned(7 downto 0); - ppbd: out unsigned(7 downto 0); - - flag_n: in std_logic; - - irq_n: out std_logic - ); -end cia6526; - --- ----------------------------------------------------------------------- - -architecture Behavioral of cia6526 is - -- IO ports - signal pra: unsigned(7 downto 0); - signal prb: unsigned(7 downto 0); - signal ddra: unsigned(7 downto 0); - signal ddrb: unsigned(7 downto 0); - - -- Timer to IO ports - signal timerAPulse : std_logic; - signal timerAToggle : std_logic; - signal timerBPulse : std_logic; - signal timerBToggle : std_logic; - - -- Timer A reload registers - signal talo: unsigned(7 downto 0) := (others => '1'); - signal tahi: unsigned(7 downto 0) := (others => '1'); - - -- Timer B reload registers - signal tblo: unsigned(7 downto 0) := (others => '1'); - signal tbhi: unsigned(7 downto 0) := (others => '1'); - - -- Timer A and B internal registers - signal timerA : unsigned(15 downto 0); - signal forceTimerA : std_logic; - signal loadTimerA : std_logic; - signal nextClkTimerA : std_logic; - signal clkTimerA : std_logic; -- internal timer clock - - signal timerB: unsigned(15 downto 0); - signal forceTimerB : std_logic; - signal loadTimerB : std_logic; - signal nextClkTimerB : std_logic; - signal clkTimerB : std_logic; -- internal timer clock - - -- Config register A - signal cra_start : std_logic; - signal cra_pbon : std_logic; - signal cra_outmode : std_logic; - signal cra_runmode : std_logic; - signal cra_runmode_reg : std_logic; - signal cra_inmode : std_logic; - signal cra_spmode : std_logic; - signal cra_todin : std_logic; - - -- Config register B - signal crb_start : std_logic; - signal crb_pbon : std_logic; - signal crb_outmode : std_logic; - signal crb_runmode : std_logic; - signal crb_runmode_reg : std_logic; - signal crb_inmode5 : std_logic; - signal crb_inmode6 : std_logic; - signal crb_alarm : std_logic; - - -- TOD 50/60 hz clock - signal todTick : std_logic; - signal oldTodClk : std_logic; - signal tod_clkcnt: unsigned(2 downto 0); - - -- TOD counters - signal tod_running: std_logic; - signal tod_10ths: unsigned(3 downto 0); - signal tod_secs: unsigned(6 downto 0); - signal tod_mins: unsigned(6 downto 0); - signal tod_hrs: unsigned(7 downto 0); - signal tod_pm: std_logic; - - -- TOD latches - signal tod_latched: std_logic; - signal tod_latch_10ths: unsigned(3 downto 0); - signal tod_latch_secs: unsigned(6 downto 0); - signal tod_latch_mins: unsigned(6 downto 0); - signal tod_latch_hrs: unsigned(7 downto 0); - constant tod_latch_pm: std_logic := '0'; - - -- TOD alarms - LCA - signal tod_10ths_alarm: unsigned(3 downto 0); - signal tod_secs_alarm: unsigned(6 downto 0); - signal tod_mins_alarm: unsigned(6 downto 0); - signal tod_hrs_alarm: unsigned(7 downto 0); - signal tod_pm_alarm: std_logic; - - -- Interrupt processing - signal resetIrq : boolean; - signal intr_flagn : std_logic; - signal intr_serial : std_logic; - signal intr_alarm : std_logic; -- LCA - signal intr_timerA : std_logic; - signal intr_timerB : std_logic; - signal mask_timerA : std_logic; - signal mask_timerB : std_logic; - signal mask_alarm : std_logic; -- LCA - signal mask_serial : std_logic; - signal mask_flagn : std_logic; - signal ir: std_logic; - - signal prevFlag_n: std_logic; - - signal myWr : std_logic; - signal myRd : std_logic; -begin --- ----------------------------------------------------------------------- --- chip-select signals --- ----------------------------------------------------------------------- - myWr <= cs and we; - myRd <= cs and rd; - --- ----------------------------------------------------------------------- --- I/O ports --- ----------------------------------------------------------------------- - -- Port A - process(pra, ddra) - begin - ppad <= ddra; - ppao <= pra or (not ddra); - end process; - - -- Port B - process(prb, ddrb, cra_pbon, cra_outmode, crb_pbon, crb_outmode, timerAPulse, timerAToggle, timerBPulse, timerBToggle) - begin - ppbd <= ddrb; - ppbo <= prb or (not ddrb); - if cra_pbon = '1' then - ppbo(6) <= timerAPulse; - if cra_outmode = '1' then - ppbo(6) <= timerAToggle; - end if; - end if; - if crb_pbon = '1' then - ppbo(7) <= timerBPulse; - if crb_outmode = '1' then - ppbo(7) <= timerBToggle; - end if; - end if; - end process; - - -- I/O port registers - process(clk) - begin - if rising_edge(clk) then - if myWr = '1' then - case addr is - when X"0" => pra <= di; - when X"1" => prb <= di; - when X"2" => ddra <= di; - when X"3" => ddrb <= di; - when others => null; - end case; - end if; - if reset = '1' then - pra <= (others => '0'); - prb <= (others => '0'); - ddra <= (others => '0'); - ddrb <= (others => '0'); - end if; - end if; - end process; - --- ----------------------------------------------------------------------- --- TOD - time of day --- ----------------------------------------------------------------------- - process(clk) - begin - -- Process rising edge on the todClk. - -- There is a prescaler of 5 or 6 to get 10ths of seconds from - -- 50 Hz or 60 Hz line frequency. - -- - -- Output is a 'todTick' signal synchronished with enable signal (@ 1Mhz). - if rising_edge(clk) then - if todEnabled = '1' then - if enable = '1' then - todTick <= '0'; - end if; - - if todClk = '1' and oldTodClk = '0' then - -- Divide by 5 or 6 dependng on 50/60 Hz flag. - if tod_clkcnt /= "000" then - tod_clkcnt <= tod_clkcnt - 1; - else - todTick <= tod_running; - tod_clkcnt <= "101"; -- 60 Hz - if cra_todin = '1' then - tod_clkcnt <= "100"; -- 50 Hz - end if; - end if; - end if; - oldTodClk <= todClk; - else - todTick <= '0'; - end if; - end if; - end process; - - process(clk) - variable new_10ths : unsigned(3 downto 0); - variable new_secsL : unsigned(3 downto 0); - variable new_secsH : unsigned(2 downto 0); - variable new_minsL : unsigned(3 downto 0); - variable new_minsH : unsigned(2 downto 0); - variable new_hrsL : unsigned(3 downto 0); - variable new_hrsH : std_logic; - variable new_hrs_byte : unsigned(7 downto 0); -- LCA am/pm and hours - begin - if rising_edge(clk) then - new_10ths := tod_10ths; - new_secsL := tod_secs(3 downto 0); - new_secsH := tod_secs(6 downto 4); - new_minsL := tod_mins(3 downto 0); - new_minsH := tod_mins(6 downto 4); --- new_hrsL := tod_hrs(3 downto 0); --- new_hrsH := tod_hrs(4); - new_hrs_byte := tod_hrs (7 downto 0); -- LCA am/pm and hours --- new_hrs_byte := new_hrsH & new_hrsL; - - if enable = '1' - and todTick = '1' then - if new_10ths /= "1001" then - new_10ths := new_10ths + 1; - else - new_10ths := "0000"; - if new_secsL /= "1001" then - new_secsL := new_secsL + 1; - else - new_secsL := "0000"; - if new_secsH /= "101" then - new_secsH := new_secsH + 1; - else - new_secsH := "000"; - if new_minsL /= "1001" then - new_minsL := new_minsL + 1; - else - new_minsL := "0000"; - if new_minsH /= "101" then - new_minsH := new_minsH + 1; - else - new_minsH := "000"; - -- hrs were missing jun17 LCA - -- I mean completely absent from code :) !!!!!! - -- case to lookup then handles oddities in others - -- retarded am/pm flag flip madness handled at register load below (REG B) - - case tod_hrs is -- case state to set hours and am/pm - when "00010010" => - new_hrs_byte := "00000001"; -- 1 am set - when "00000001" => - new_hrs_byte := "00000010"; - when "00000010" => - new_hrs_byte := "00000011"; - when "00000011" => - new_hrs_byte := "00000100"; - when "00000100" => - new_hrs_byte := "00000101"; - when "00000101" => - new_hrs_byte := "00000110"; - when "00000110" => - new_hrs_byte := "00000111"; - when "00000111" => - new_hrs_byte := "00001000"; - when "00001000" => - new_hrs_byte := "00001001"; - when "00001001" => - new_hrs_byte := "00010000"; - when "00010000" => - new_hrs_byte := "00010001"; -- 11am set - when "00010001" => - new_hrs_byte := "10010010"; -- 12pm set - when "10010010" => - new_hrs_byte := "10000001"; -- 1 pm set - - when "10000001" => - new_hrs_byte := "10000010"; - when "10000010" => - new_hrs_byte := "10000011"; - when "10000011" => - new_hrs_byte := "10000100"; - when "10000100" => - new_hrs_byte := "10000101"; - when "10000101" => - new_hrs_byte := "10000110"; - when "10000110" => - new_hrs_byte := "10000111"; - when "10000111" => - new_hrs_byte := "10001000"; - when "10001000" => - new_hrs_byte := "10001001"; - when "10001001" => - new_hrs_byte := "10010000"; -- 10pm set - when "10010000" => - new_hrs_byte := "10010001"; -- 11pm set - when "10010001" => - new_hrs_byte := "00010010"; -- 12am set (midnight) - when others => - new_hrs_byte (3 downto 0) := new_hrs_byte (3 downto 0) + 1; - --null; - end case; - - end if; - end if; - end if; - end if; - end if; - end if; - - if myWr = '1' then - if crb_alarm = '0' then - case addr is - when X"8" => - new_10ths := di(3 downto 0); - tod_running <= '1'; - when X"9" => - new_secsL := di(3 downto 0); - new_secsH := di(6 downto 4); - when X"A" => - new_minsL := di(3 downto 0); - new_minsH := di(6 downto 4); - when X"B" => - new_hrs_byte := di(7) & "00" & di(4 downto 0); -- LCA - tod_running <= '0'; - if di(7 downto 0) = "10010010" or di(7 downto 0) = "00010010" then -- super bodge because cbm flips am/pm flag at 12 am or pm (its retarded!!!!!) - new_hrs_byte(7) := not new_hrs_byte(7); -- This P.O.S. now mimics real commodore 64 !!!!! LCA - end if; - when others => - null; - end case; - else -- TOD ALARM UPDATE - case addr is - when X"8" => - tod_10ths_alarm <= di(3 downto 0); - when X"9" => - tod_secs_alarm <= di(6 downto 0); - when X"A" => - tod_mins_alarm <= di(6 downto 0); - when X"B" => --- tod_hrs_alarm <= di(4 downto 0); --- tod_pm_alarm <= di(7); - tod_hrs_alarm <= di(7) & "00" & di(4 downto 0); -- LCA - if di(7 downto 0) = "10010010" or di(7 downto 0) = "00010010" then -- super bodge because cbm flips am/pm flag at 12 am or pm (its retarded!!!!!) - tod_hrs_alarm(7) <= not tod_hrs_alarm(7); -- This P.O.S. now mimics real commodore 64 !!!!! LCA - end if; - when others => - null; - end case; - end if; - end if; - - -- Update state - tod_10ths <= new_10ths; - tod_secs <= new_secsH & new_secsL; - tod_mins <= new_minsH & new_minsL; - tod_hrs <= new_hrs_byte; -- LCA - - if tod_latched = '0' then - tod_latch_10ths <= new_10ths; - tod_latch_secs <= new_secsH & new_secsL; - tod_latch_mins <= new_minsH & new_minsL; - tod_latch_hrs <= new_hrs_byte; -- LCA - end if; - - -- TOD ALARM test for match - LCA - if (tod_10ths = tod_10ths_alarm) and - (tod_secs = tod_secs_alarm) and - (tod_mins = tod_mins_alarm) and - (tod_hrs = tod_hrs_alarm) and - (crb_alarm = '1') then - intr_alarm <= '1' ; - end if; - - if reset = '1' then - tod_running <= '0'; - tod_10ths_alarm <= "0000" ; - tod_secs_alarm <= "0000000" ; - tod_mins_alarm <= "0000000" ; - tod_hrs_alarm <= "00000000" ; - tod_pm_alarm <= '0' ; - end if; - - if resetIrq then - intr_alarm <= '0' ; - end if; - end if; - end process; - - -- Control TOD output latch - -- Reading the hours latches the output until - -- the 10ths of seconds are read. While latched the - -- clock continues to run in the bankground. - process(clk) - begin - if rising_edge(clk) then - if myRd = '1' then - case addr is - when X"8" => tod_latched <= '0'; - when X"B" => tod_latched <= '1'; - when others => null; - end case; - end if; - end if; - end process; - - --- ----------------------------------------------------------------------- --- Timer A and B --- ----------------------------------------------------------------------- - - process(clk) - variable newTimerA : unsigned(15 downto 0); - variable timerAInput : std_logic; - variable timerBInput : std_logic; - variable newTimerB : unsigned(15 downto 0); - begin - if rising_edge(clk) then - loadTimerA <= '0'; - loadTimerB <= '0'; - - if resetIrq then - intr_timerA <= '0'; - intr_timerB <= '0'; - end if; - - if myWr = '1' then - case addr is - when X"4" => - talo <= di; - when X"5" => - tahi <= di; - if cra_start = '0' then - loadTimerA <= '1'; - end if; - when X"6" => - tblo <= di; - when X"7" => - tbhi <= di; - if crb_start = '0' then - loadTimerB <= '1'; - end if; - when X"E" => - if cra_start = '0' then - -- Only set on rising edge - timerAToggle <= timerAToggle or di(0); - end if; - cra_start <= di(0); - cra_runmode_reg <= di(3); - when X"F" => - if crb_start = '0' then - -- Only set on rising edge - timerBToggle <= timerBToggle or di(0); - end if; - crb_start <= di(0); - crb_runmode_reg <= di(3); - when others => null; - end case; - end if; - - if reset = '1' then - cra_runmode_reg <= '0'; - crb_runmode_reg <= '0'; - end if; - - - if enable = '1' then - - cra_runmode <= cra_runmode_reg; - crb_runmode <= crb_runmode_reg; - -- - -- process timer A - -- - timerAPulse <= '0'; - newTimerA := timerA; - - -- CNT is not emulated so don't count when inmode = 1 - timerAInput := not cra_inmode; - - nextClkTimerA <= timerAInput and cra_start; - clkTimerA <= nextClkTimerA; - if clkTimerA = '1' then - newTimerA := newTimerA - 1; - end if; - if nextClkTimerA = '1' - and newTimerA = 0 then - intr_timerA <= '1'; - loadTimerA <= '1'; - timerAPulse <= '1'; - timerAToggle <= not timerAToggle; - if (cra_runmode_reg or cra_runmode) = '1' then - cra_start <= '0'; - end if; - end if; - if forceTimerA = '1' then - loadTimerA <= '1'; - end if; - timerA <= newTimerA; - - -- - -- process timer B - -- - timerBPulse <= '0'; - newTimerB := timerB; - - if crb_inmode6 = '1' then - -- count timerA underflows - timerBInput := timerAPulse; - elsif crb_inmode5 = '0' then - -- count clock pulses - timerBInput := '1'; - else - -- CNT is not emulated so don't count - timerBInput := '0'; - end if; - - nextClkTimerB <= timerBInput and crb_start; - clkTimerB <= nextClkTimerB; - if clkTimerB = '1' then - newTimerB := newTimerB - 1; - end if; - if nextClkTimerB = '1' - and newTimerB = 0 then - intr_timerB <= '1'; - loadTimerB <= '1'; - timerBPulse <= '1'; - timerBToggle <= not timerBToggle; - if (crb_runmode_reg or crb_runmode) = '1' then - crb_start <= '0'; - end if; - end if; - if forceTimerB = '1' then - loadTimerB <= '1'; - end if; - timerB <= newTimerB; - end if; - - if loadTimerA = '1' then - timerA <= tahi & talo; - clkTimerA <= '0'; - end if; - - if loadTimerB = '1' then - timerB <= tbhi & tblo; - clkTimerB <= '0'; - end if; - end if; - end process; - - --- ----------------------------------------------------------------------- --- Interrupts --- ----------------------------------------------------------------------- - resetIrq <= ((myRd = '1') and (addr = X"D")) or (reset = '1'); - irq_n <= not(ir); - intr_serial <= '0'; - - process(clk) - begin - if rising_edge(clk) then - if enable = '1' then - ir <= ir - or (intr_timerA and mask_timerA) - or (intr_timerB and mask_timerB) - or (intr_alarm and mask_alarm) - or (intr_serial and mask_serial) - or (intr_flagn and mask_flagn); - end if; - - if myWr = '1' then - case addr is - when X"D" => - if di(7) ='0' then - mask_timerA <= mask_timerA and (not di(0)); - mask_timerB <= mask_timerB and (not di(1)); - mask_alarm <= mask_alarm and (not di(2)); -- LCA - mask_serial <= mask_serial and (not di(3)); - mask_flagn <= mask_flagn and (not di(4)); - else - mask_timerA <= mask_timerA or di(0); - mask_timerB <= mask_timerB or di(1); - mask_alarm <= mask_alarm or di(2); -- LCA - mask_serial <= mask_serial or di(3); - mask_flagn <= mask_flagn or di(4); - end if; - when others => - null; - end case; - end if; - - if resetIrq then - ir <= '0'; - end if; - - if reset = '1' then - mask_timerA <= '0'; - mask_timerB <= '0'; - mask_alarm <= '0' ; - mask_serial <= '0'; - mask_flagn <= '0'; - end if; - end if; - end process; - - - - --- ----------------------------------------------------------------------- --- FLAG_N input --- ----------------------------------------------------------------------- - process(clk) - begin - if rising_edge(clk) then - prevFlag_n <= flag_n; - if (flag_n = '0') and (prevFlag_n = '1') then - intr_flagn <= '1'; - end if; - if resetIrq then - intr_flagn <= '0'; - end if; - end if; - end process; - - --- ----------------------------------------------------------------------- --- Write registers --- ----------------------------------------------------------------------- - process(clk) - begin - if rising_edge(clk) then --- resetIrq <= '0'; - if enable = '1' then - forceTimerA <= '0'; - forceTimerB <= '0'; --- cra_runmode_reg <= cra_runmode; --- crb_runmode_reg <= crb_runmode; - end if; - if myWr = '1' then - case addr is - when X"E" => - cra_pbon <= di(1); - cra_outmode <= di(2); --- cra_runmode <= di(3); - forceTimerA <= di(4); - cra_inmode <= di(5); - cra_spmode <= di(6); - cra_todin <= di(7); - when X"F" => - crb_pbon <= di(1); - crb_outmode <= di(2); --- crb_runmode <= di(3); - forceTimerB <= di(4); - crb_inmode5 <= di(5); - crb_inmode6 <= di(6); - crb_alarm <= di(7); - when others => null; - end case; - end if; - if reset = '1' then - cra_pbon <= '0'; - cra_outmode <= '0'; --- cra_runmode <= '0'; - cra_inmode <= '0'; - cra_spmode <= '0'; - cra_todin <= '0'; - crb_pbon <= '0'; - crb_outmode <= '0'; --- crb_runmode <= '0'; - crb_inmode5 <= '0'; - crb_inmode6 <= '0'; - crb_alarm <= '0'; - end if; - end if; - end process; - - --- ----------------------------------------------------------------------- --- Read registers --- ----------------------------------------------------------------------- - process(clk) - begin - if rising_edge(clk) then - case addr is - when X"0" => do <= ppai; - when X"1" => do <= ppbi; - when X"2" => do <= DDRA; - when X"3" => do <= DDRB; - when X"4" => do <= timera(7 downto 0); - when X"5" => do <= timera(15 downto 8); - when X"6" => do <= timerb(7 downto 0); - when X"7" => do <= timerb(15 downto 8); - when X"8" => do <= "0000" & tod_latch_10ths; - when X"9" => do <= "0" & tod_latch_secs; - when X"A" => do <= "0" & tod_latch_mins; --- when X"B" => do <= tod_latch_pm & "00" & tod_latch_hrs; - when X"B" => do <= tod_latch_hrs; -- LCA - when X"C" => do <= (others => '0'); - when X"D" => do <= ir & "00" & intr_flagn & intr_serial & intr_alarm & intr_timerB & intr_timerA; - when X"E" => do <= cra_todin & cra_spmode & cra_inmode & '0' & cra_runmode & cra_outmode & cra_pbon & cra_start; - when X"F" => do <= crb_alarm & crb_inmode6 & crb_inmode5 & '0' & crb_runmode & crb_outmode & crb_pbon & crb_start; - when others => do <= (others => '-'); - end case; - end if; - end process; -end Behavioral; diff --git a/cores/c64/rtl/fpga64_sid_iec.vhd b/cores/c64/rtl/fpga64_sid_iec.vhd index 498ba85..7a9fa96 100644 --- a/cores/c64/rtl/fpga64_sid_iec.vhd +++ b/cores/c64/rtl/fpga64_sid_iec.vhd @@ -29,6 +29,7 @@ use IEEE.STD_LOGIC_1164.ALL; use IEEE.std_logic_unsigned.ALL; use IEEE.numeric_std.all; +use work.mos6526.all; -- ----------------------------------------------------------------------- entity fpga64_sid_iec is @@ -367,12 +368,13 @@ begin when CYCLE_VIC2 => enableVic <= '1'; when CYCLE_CPUE => - enableCia <= '1'; enableVic <= '1'; if baLoc = '1' or cpuWe = '1' then enableCpu <= '1'; end if; + when CYCLE_CPUF => + enableCia <= '1'; when others => null; end case; @@ -615,52 +617,56 @@ div1m: process(clk32) -- this process devides 32 MHz to 1MHz (for the SID) -- ----------------------------------------------------------------------- -- CIAs -- ----------------------------------------------------------------------- - cia1: entity work.cia6526 + cia1: mos6526 port map ( clk => clk32, - todClk => vicVSync, - reset => reset, - enable => enableCia, - cs => cs_cia1, - we => pulseWrIo, - rd => pulseRd, + phi2 => enableCia, + res_n => not reset, + cs_n => not cs_cia1, + rw => not cpuWe, - addr => cpuAddr(3 downto 0), - di => cpuDo, - do => cia1Do, + rs => std_logic_vector(cpuAddr)(3 downto 0), + db_in => std_logic_vector(cpuDo), + unsigned(db_out) => cia1Do, - ppai => cia1_pai, - ppao => cia1_pao, - ppbi => cia1_pbi, - ppbo => cia1_pbo, + pa_in => std_logic_vector(cia1_pai), + unsigned(pa_out) => cia1_pao, + pb_in => std_logic_vector(cia1_pbi), + unsigned(pb_out) => cia1_pbo, - flag_n => '1', + flag_n => '1', + sp_in => '1', + cnt_in => '1', - irq_n => irq_cia1 + pc_n => open, + tod => vicVSync, + irq_n => irq_cia1 ); - cia2: entity work.cia6526 + cia2: mos6526 port map ( clk => clk32, - todClk => vicVSync, - reset => reset, - enable => enableCia, - cs => cs_cia2, - we => pulseWrIo, - rd => pulseRd, + phi2 => enableCia, + res_n => not reset, + cs_n => not cs_cia2, + rw => not cpuWe, - addr => cpuAddr(3 downto 0), - di => cpuDo, - do => cia2Do, + rs => std_logic_vector(cpuAddr)(3 downto 0), + db_in => std_logic_vector(cpuDo), + unsigned(db_out) => cia2Do, - ppai => cia2_pai, - ppao => cia2_pao, - ppbi => cia2_pbi, - ppbo => cia2_pbo, + pa_in => std_logic_vector(cia2_pai), + unsigned(pa_out) => cia2_pao, + pb_in => std_logic_vector(cia2_pbi), + unsigned(pb_out) => cia2_pbo, - flag_n => '1', + flag_n => '1', + sp_in => '1', + cnt_in => '1', - irq_n => irq_cia2 + pc_n => open, + tod => vicVSync, + irq_n => irq_cia2 ); -- ----------------------------------------------------------------------- diff --git a/cores/c64/rtl/mos6526.v b/cores/c64/rtl/mos6526.v new file mode 100644 index 0000000..088c0fd --- /dev/null +++ b/cores/c64/rtl/mos6526.v @@ -0,0 +1,439 @@ +module mos6526 ( + input wire clk, + input wire phi2, + input wire res_n, + input wire cs_n, + input wire rw, + + input wire [3:0] rs, + input wire [7:0] db_in, + output reg [7:0] db_out, + + input wire [7:0] pa_in, + output reg [7:0] pa_out, + input wire [7:0] pb_in, + output reg [7:0] pb_out, + + input wire flag_n, + output reg pc_n, + + input wire tod, + + input wire sp_in, + output reg sp_out, + + input wire cnt_in, + output reg cnt_out, + + output reg irq_n +); + +// Internal Registers +reg [7:0] pra; +reg [7:0] prb; +reg [7:0] ddra; +reg [7:0] ddrb; + +reg [7:0] ta_lo; +reg [7:0] ta_hi; +reg [7:0] tb_lo; +reg [7:0] tb_hi; + +reg [3:0] tod_10ths; +reg [6:0] tod_sec; +reg [6:0] tod_min; +reg [5:0] tod_hr; + +reg [7:0] sdr; +reg [4:0] icr; +reg [7:0] cra; +reg [7:0] crb; + +// Internal Signals +reg flag_n_prev; + +reg [15:0] timer_a; +reg [ 1:0] timer_a_out; +reg [15:0] timer_b; +reg [ 1:0] timer_b_out; + +reg tod_prev; +reg tod_run; +reg [ 2:0] tod_count; +reg tod_tick; +reg [23:0] tod_alarm; +reg tod_alarm_reg; +reg [23:0] tod_latch; +reg tod_latched; + +reg sp_pending; +reg sp_received; +reg sp_transmit; +reg [ 7:0] sp_shiftreg; + +reg cnt_in_prev; +reg cnt_out_prev; +reg [ 2:0] cnt_pulsecnt; + +reg [ 4:0] int_data; +reg [ 1:0] int_reset; + +// Register Decoding +always @(posedge clk) begin + if (!res_n) db_out <= 8'h00; + else if (!cs_n && rw) + case (rs) + 4'h0: db_out <= pa_in; + 4'h1: db_out <= pb_in; + 4'h2: db_out <= ddra; + 4'h3: db_out <= ddrb; + 4'h4: db_out <= timer_a[ 7:0]; + 4'h5: db_out <= timer_a[15:8]; + 4'h6: db_out <= timer_b[ 7:0]; + 4'h7: db_out <= timer_b[15:8]; + 4'h8: db_out <= tod_latched ? + {4'h0, tod_latch[3:0]} : {4'h0, tod_10ths}; + 4'h9: db_out <= tod_latched ? + {1'b0, tod_latch[10:4]} : {1'b0, tod_sec}; + 4'ha: db_out <= tod_latched ? + {1'b0, tod_latch[17:11]} : {1'b0, tod_min}; + 4'hb: db_out <= tod_latched ? + {tod_latch[23], 2'h0, tod_latch[22:18]} : + {tod_hr[5], 2'h0, tod_hr[4:0]}; + 4'hc: db_out <= sdr; + 4'hd: db_out <= {~irq_n, 2'b00, int_data}; + 4'he: db_out <= {cra[7:5], 1'b0, cra[3:0]}; + 4'hf: db_out <= {crb[7:5], 1'b0, crb[3:0]}; + endcase +end + +// Port A Output +always @(posedge clk) begin + if (!res_n) begin + pra <= 8'h00; + ddra <= 8'h00; + end + else if (!cs_n && !rw) + case (rs) + 4'h0: pra <= db_in; + 4'h2: ddra <= db_in; + default: begin + pra <= pra; + ddra <= ddra; + end + endcase + if (phi2) pa_out <= pra | ~ddra; +end + +// Port B Output +always @(posedge clk) begin + if (!res_n) begin + prb <= 8'h00; + ddrb <= 8'h00; + end + else if (!cs_n && !rw) + case (rs) + 4'h1: prb <= db_in; + 4'h3: ddrb <= db_in; + default: begin + prb <= prb; + ddrb <= ddrb; + end + endcase + if (phi2) begin + pb_out[7] <= crb[1] ? crb[2] ? timer_b_out[1] | ~ddrb[7] : + timer_b_out[0] | ~ddrb[7] : prb[7] | ~ddrb[7]; + pb_out[6] <= cra[1] ? cra[2] ? timer_a_out[1] | ~ddrb[6] : + timer_a_out[0] | ~ddrb[7] : prb[6] | ~ddrb[6]; + pb_out[5:0] <= prb[5:0] | ~ddrb[5:0]; + end +end + +// FLAG Input +always @(posedge clk) begin + if (!res_n || int_reset[1]) int_data[4] <= 1'b0; + else if (!flag_n && flag_n_prev) int_data[4] <= 1'b1; + if (phi2) flag_n_prev <= flag_n; +end + +// Port Control Output +always @(posedge clk) begin + if (!cs_n && rs == 4'h1) pc_n <= 1'b0; + else pc_n <= phi2 ? 1'b1 : pc_n; +end + +// Timer A +always @(posedge clk) begin + if (!res_n) begin + ta_lo <= 8'hff; + ta_hi <= 8'hff; + cra <= 8'h00; + timer_a <= 16'h0000; + timer_a_out[1] <= 1'b0; + int_data[0] <= 1'b0; + end + else if (!cs_n && !rw) + case (rs) + 4'h4: ta_lo <= db_in; + 4'h5: ta_hi <= db_in; + 4'he: begin + cra <= db_in; + timer_a_out[1] <= timer_a_out[1] | db_in[0]; + end + default: begin + ta_lo <= ta_lo; + ta_hi <= ta_hi; + cra <= cra; + timer_a_out[1] <= timer_a_out[1]; + end + endcase + timer_a_out[0] <= phi2 ? 1'b0 : timer_a_out[0]; + if (phi2 && cra[0] && !cra[4]) begin + if (!cra[5]) timer_a <= timer_a - 1'b1; + else timer_a <= (cnt_in && !cnt_in_prev) ? timer_a - 1'b1 : timer_a; + if (!timer_a) begin + cra[0] <= ~cra[3]; + int_data[0] <= 1'b1; + timer_a <= {ta_hi, ta_lo}; + timer_a_out <= {~timer_a_out[1], 1'b1}; + end + end + if ((phi2 && cra[4]) || (!cra[0] && !cs_n && !rw && rs == 4'h5)) begin + cra[4] <= 1'b0; + timer_a <= {ta_hi, ta_lo}; + end + if (int_reset[1]) int_data[0] <= 1'b0; +end + +// Timer B +always @(posedge clk) begin + if (!res_n) begin + tb_lo <= 8'hff; + tb_hi <= 8'hff; + crb <= 8'h00; + timer_b <= 16'h0000; + timer_b_out[1] <= 1'b0; + int_data[1] <= 1'b0; + end + else if (!cs_n && !rw) + case (rs) + 4'h6: tb_lo <= db_in; + 4'h7: tb_hi <= db_in; + 4'hf: begin + crb <= db_in; + timer_b_out[1] <= timer_b_out[1] | db_in[0]; + end + default: begin + tb_lo <= tb_lo; + tb_hi <= tb_hi; + crb <= crb; + timer_b_out[1] <= timer_b_out[1]; + end + endcase + timer_b_out[0] <= phi2 ? 1'b0 : timer_b_out[0]; + if (phi2 && crb[0] && !crb[4]) begin + case (crb[6:5]) + 2'b00: timer_b <= timer_b - 1'b1; + 2'b01: timer_b <= (cnt_in && !cnt_in_prev) ? timer_b - 1'b1 : timer_b; + 2'b10: timer_b <= timer_a_out[0] ? timer_b - 1'b1 : timer_b; + 2'b11: timer_b <= (timer_a_out[0] && cnt_in) ? timer_b - 1'b1 : timer_b; + endcase + if (!timer_b) begin + crb[0] <= ~crb[3]; + int_data[1] <= 1'b1; + timer_b <= {tb_hi, tb_lo}; + timer_b_out <= {~timer_b_out[1], 1'b1}; + end + end + if ((phi2 && crb[4]) || (!crb[0] && !cs_n && !rw && rs == 4'h7)) begin + crb[4] <= 1'b0; + timer_b <= {tb_hi, tb_lo}; + end + if (int_reset[1]) int_data[1] <= 1'b0; +end + +// Time of Day +always @(posedge clk) begin + if (!res_n) begin + tod_10ths <= 4'h0; + tod_sec <= 7'h00; + tod_min <= 7'h00; + tod_hr <= 6'h01; + tod_run <= 1'b0; + tod_alarm <= 24'h000000; + tod_latch <= 24'h000000; + tod_latched <= 1'b0; + int_data[2] <= 1'b0; + end + else if (!cs_n && !rw) + case (rs) + 4'h8: if (crb[7]) tod_alarm[3:0] <= db_in[3:0]; + else tod_10ths <= db_in[3:0]; + 4'h9: if (crb[7]) tod_alarm[10:4] <= db_in[6:0]; + else tod_sec <= db_in[6:0]; + 4'ha: if (crb[7]) tod_alarm[17:11] <= db_in[6:0]; + else tod_min <= db_in[6:0]; + 4'hb: if (crb[7]) tod_alarm[23:18] <= {db_in[7], db_in[4:0]}; + else tod_hr <= {db_in[7], db_in[4:0]}; + default: begin + tod_10ths <= tod_10ths; + tod_sec <= tod_sec; + tod_min <= tod_min; + tod_hr <= tod_hr; + tod_alarm <= tod_alarm; + end + endcase + if (!cs_n) + if (rs == 4'h8) + if (!rw) tod_run <= !crb[7] ? 1'b1 : tod_run; + else begin + tod_latched <= 1'b0; + tod_latch <= 24'h000000; + end + else if (rs == 4'hb) + if (!rw) tod_run <= !crb[7] ? 1'b0 : tod_run; + else begin + tod_latched <= 1'b1; + tod_latch <= {tod_hr, tod_min, tod_sec, tod_10ths}; + end + tod_prev <= tod; + tod_tick <= 1'b0; + if (tod_run) begin + tod_count <= (tod && !tod_prev) ? tod_count + 1'b1 : tod_count; + if ((cra[7] && tod_count == 3'h5) || tod_count == 3'h6) begin + tod_tick <= 1'b1; + tod_count <= 3'h0; + end + if (tod_tick) begin + tod_10ths <= (tod_10ths == 4'h9) ? 1'b0 : tod_10ths + 1'b1; + if (tod_10ths == 4'h9) begin + tod_sec[3:0] <= tod_sec[3:0] + 1'b1; + if (tod_sec[3:0] == 4'h9) begin + tod_sec[3:0] <= 4'h0; + tod_sec[6:4] <= tod_sec[6:4] + 1'b1; + end + if (tod_sec == 7'h59) begin + tod_sec[6:4] <= 3'h0; + tod_min[3:0] <= tod_min[3:0] + 1'b1; + end + if (tod_min[3:0] == 4'h9 && tod_sec == 7'h59) begin + tod_min[3:0] <= 4'h0; + tod_min[6:4] <= tod_min[6:4] + 1'b1; + end + if (tod_min == 7'h59 && tod_sec == 7'h59) begin + tod_min[6:4] <= 3'h0; + tod_hr[3:0] <= tod_hr[3:0] + 1'b1; + end + if (tod_hr[3:0] == 4'h9 && tod_min == 7'h59 && tod_sec == 7'h59) begin + tod_hr[3:0] <= 4'h0; + tod_hr[4] <= tod_hr[4] + 1'b1; + end + if (tod_min == 7'h59 && tod_sec == 7'h59) + if (tod_hr[4:0] == 5'h11) + if (!tod_hr[5]) begin + tod_hr[5] <= ~tod_hr[5]; + tod_hr[3:0] <= tod_hr[3:0] + 1'b1; + end + else begin + tod_hr[5] <= ~tod_hr[5]; + tod_hr[4:0] <= 5'h00; + end + else if (tod_hr[4:0] == 5'h12) tod_hr[4:0] <= 5'h01; + end + end + end + else tod_count <= 3'h0; + if ({tod_hr, tod_min, tod_sec, tod_10ths} == tod_alarm) begin + tod_alarm_reg <= 1'b1; + int_data[2] <= !tod_alarm_reg ? 1'b1 : int_data[2]; + end + else tod_alarm_reg <= 1'b0; + if (int_reset[1]) int_data[2] <= 1'b0; +end + +// Serial Port Input/Output +always @(posedge clk) begin + if (!res_n) begin + sdr <= 8'h00; + sp_out <= 1'b0; + sp_pending <= 1'b0; + sp_received <= 1'b0; + sp_transmit <= 1'b0; + sp_shiftreg <= 8'h00; + int_data[3] <= 1'b0; + end + else if (!cs_n && !rw) + case (rs) + 4'hc: sdr <= db_in; + default: sdr <= sdr; + endcase + if (!cra[6]) begin + if (sp_received) begin + sdr <= sp_shiftreg; + int_data[3] <= 1'b1; + sp_received <= 1'b0; + sp_shiftreg <= 8'h00; + end + else if (cnt_in && !cnt_in_prev) begin + sp_shiftreg <= {sp_shiftreg[6:0], sp_in}; + sp_received <= (cnt_pulsecnt == 3'h7) ? 1'b1 : sp_received; + end + end + else if (cra[6] && !cra[3] && cra[0]) begin + if (!cs_n && !rw && rs == 8'hc) sp_pending <= 1'b1; + if (sp_pending && !sp_transmit) begin + sp_pending <= 1'b0; + sp_transmit <= 1'b1; + sp_shiftreg <= sdr; + end + else if (!cnt_out && cnt_out_prev) begin + if (cnt_pulsecnt == 3'h7) begin + int_data[3] <= 1'b1; + sp_transmit <= 1'b0; + end + sp_out <= sp_shiftreg[7]; + sp_shiftreg <= {sp_shiftreg[6:0], 1'b0}; + end + end + if (int_reset[1]) int_data[3] <= 1'b0; +end + +// CNT Input/Output +always @(posedge clk) begin + if (!res_n) begin + cnt_out <= 1'b1; + cnt_out_prev <= 1'b1; + cnt_pulsecnt <= 3'h0; + end + else if (phi2) begin + cnt_in_prev <= cnt_in; + cnt_out_prev <= cnt_out; + end + if (!cra[6] && cnt_in && !cnt_in_prev) cnt_pulsecnt <= cnt_pulsecnt + 1'b1; + else if (cra[6] && !cra[3] && cra[0]) begin + if (sp_transmit) begin + cnt_out <= timer_a_out[0] ? ~cnt_out : cnt_out; + if (!cnt_out && cnt_out_prev) cnt_pulsecnt <= cnt_pulsecnt + 1'b1; + end + else cnt_out <= timer_a_out[0] ? 1'b1 : cnt_out; + end +end + +// Interrupt Control +always @(posedge clk) begin + if (!res_n) begin + icr <= 5'h00; + irq_n <= 1'b1; + int_reset <= 2'b00; + end + else if (!cs_n && !rw) + case (rs) + 4'hd: icr <= db_in[7] ? icr | db_in[4:0] : icr & ~db_in[4:0]; + default: icr <= icr; + endcase + else irq_n <= irq_n ? ~|(icr & int_data) : int_reset[1] ? 1'b1 : irq_n; + if (!cs_n && rw && rs == 4'hd) int_reset <= 2'b01; + else if (int_reset) int_reset <= {int_reset[0], 1'b0}; +end + +endmodule diff --git a/cores/c64/rtl/mos6526.vhd b/cores/c64/rtl/mos6526.vhd new file mode 100644 index 0000000..af92849 --- /dev/null +++ b/cores/c64/rtl/mos6526.vhd @@ -0,0 +1,29 @@ +library IEEE; +use IEEE.std_logic_1164.all; + +package mos6526 is +component mos6526 + PORT ( + clk : in std_logic; + phi2 : in std_logic; + res_n : in std_logic; + cs_n : in std_logic; + rw : in std_logic; -- '1' - read, '0' - write + rs : in std_logic_vector(3 downto 0); + db_in : in std_logic_vector(7 downto 0); + db_out : out std_logic_vector(7 downto 0); + pa_in : in std_logic_vector(7 downto 0); + pa_out : out std_logic_vector(7 downto 0); + pb_in : in std_logic_vector(7 downto 0); + pb_out : out std_logic_vector(7 downto 0); + flag_n : in std_logic; + pc_n : out std_logic; + tod : in std_logic; + sp_in : in std_logic; + sp_out : out std_logic; + cnt_in : in std_logic; + cnt_out : out std_logic; + irq_n : out std_logic + ); +end component; +end package; \ No newline at end of file