⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cia6526.vhd

📁 FPGA64的VHDL源代码
💻 VHD
📖 第 1 页 / 共 2 页
字号:
-- -----------------------------------------------------------------------
--
--                                 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
--
-- -----------------------------------------------------------------------

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 clkTimerA : std_logic; -- internal timer clock

	signal timerB: unsigned(15 downto 0);
	signal forceTimerB : std_logic;
	signal loadTimerB : 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(4 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(4 downto 0);
	signal tod_latch_pm: std_logic;

	-- Interrupt processing
	signal resetIrq : boolean;
	signal intr_flagn : std_logic;
	signal intr_serial : std_logic;
	signal intr_timerA : std_logic;
	signal intr_timerB : std_logic;
	signal mask_timerA : std_logic;
	signal mask_timerB : std_logic;
	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 or (not ddrb(6));
			if cra_outmode = '1' then
				ppbo(6) <= timerAToggle or (not ddrb(6));
			end if;
		end if;
		if crb_pbon = '1' then
			ppbo(7) <= timerBPulse or (not ddrb(7));
			if crb_outmode = '1' then
				ppbo(7) <= timerBToggle or (not ddrb(7));
			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;
	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);
			
			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";
								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_hrsL := di(3 downto 0);
						new_hrsH := di(4);
						tod_running <= '0';
					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_hrsH & new_hrsL;
			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_hrsH & new_hrsL;
			end if;

			if reset = '1' then

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -