Files
SharpMZ/common/i8254/i8254.vhd
Philip Smart 4a64af4a00 Initial commit
2019-10-25 17:16:34 +01:00

215 lines
8.2 KiB
VHDL

---------------------------------------------------------------------------------------------------------
--
-- Name: i8254.vhd
-- Created: November 2018
-- Author(s): Philip Smart
-- Description: Sharp MZ series i8254 Timer
-- This module emulates the Intel i8254 Programmable Interval Timer.
--
-- Credits:
-- Copyright: (c) 2018 Philip Smart <philip.smart@net2net.org>
--
-- History: November 2018 - Initial write.
--
---------------------------------------------------------------------------------------------------------
-- This source file is free software: you can redistribute it and-or modify
-- it under the terms of the GNU General Public License as published
-- by the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This source file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http:--www.gnu.org-licenses->.
---------------------------------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity i8254 is
Port (
RST : in std_logic;
CLK : in std_logic;
ENA : in std_logic;
A : in std_logic_vector(1 downto 0);
DI : in std_logic_vector(7 downto 0);
DO : out std_logic_vector(7 downto 0);
CS_n : in std_logic;
WR_n : in std_logic;
RD_n : in std_logic;
--
CLK0 : in std_logic;
GATE0 : in std_logic;
OUT0 : out std_logic;
CLK1 : in std_logic;
GATE1 : in std_logic;
OUT1 : out std_logic;
CLK2 : in std_logic;
GATE2 : in std_logic;
OUT2 : out std_logic
);
end i8254;
architecture Behavioral of i8254 is
signal WREN : std_logic;
signal RDEN : std_logic;
signal WRCTRLEN : std_logic;
signal WR0 : std_logic;
signal WR1 : std_logic;
signal WR2 : std_logic;
signal RD0 : std_logic;
signal RD1 : std_logic;
signal RD2 : std_logic;
signal DO0 : std_logic_vector(7 downto 0);
signal DO1 : std_logic_vector(7 downto 0);
signal DO2 : std_logic_vector(7 downto 0);
signal LDO0 : std_logic_vector(7 downto 0);
signal LDO1 : std_logic_vector(7 downto 0);
signal LDO2 : std_logic_vector(7 downto 0);
signal READDATA_NEXT : std_logic_vector(7 downto 0);
signal CTRLM0 : std_logic;
signal CTRLM1 : std_logic;
signal CTRLM2 : std_logic;
signal LATCNT0 : std_logic;
signal LATCNT1 : std_logic;
signal LATCNT2 : std_logic;
signal LATSTS0 : std_logic;
signal LATSTS1 : std_logic;
signal LATSTS2 : std_logic;
component i8254_counter
Port (
CLK : in std_logic;
RESET : in std_logic;
--
DATA_IN : in std_logic_vector(7 downto 0);
DATA_OUT : out std_logic_vector(7 downto 0);
WRITE : in std_logic;
READ : in std_logic;
CTRL_MODE_EN : in std_logic;
LATCH_COUNT_EN : in std_logic;
LATCH_STATUS_EN : in std_logic;
--
CTR_CLK : in std_logic;
CTR_GATE : in std_logic;
CTR_OUT : out std_logic
);
end component;
begin
-- Create signals to select a given register for read or write.
--
WREN <= '1' when ENA = '1' and CS_n = '0' and WR_n = '0'
else '0';
RDEN <= '1' when ENA = '1' and CS_n = '0' and RD_n = '0'
else '0';
WRCTRLEN <= '1' when WREN = '1' and A = "11"
else '0';
WR0 <= '1' when WREN = '1' and A = "00"
else '0';
WR1 <= '1' when WREN = '1' and A = "01"
else '0';
WR2 <= '1' when WREN = '1' and A = "10"
else '0';
RD0 <= '1' when RDEN = '1' and A = "00"
else '0';
RD1 <= '1' when RDEN = '1' and A = "01"
else '0';
RD2 <= '1' when RDEN = '1' and A = "10"
else '0';
-- Create signals to enable setting of a command, a count value or latching status per counter.
--
CTRLM0 <= '1' when WRCTRLEN = '1' and DI(7 downto 6) = "00" and DI(5 downto 4) /= "00"
else '0';
CTRLM1 <= '1' when WRCTRLEN = '1' and DI(7 downto 6) = "01" and DI(5 downto 4) /= "00"
else '0';
CTRLM2 <= '1' when WRCTRLEN = '1' and DI(7 downto 6) = "10" and DI(5 downto 4) /= "00"
else '0';
LATCNT0 <= '1' when WRCTRLEN = '1' and ((DI(7 downto 6) = "00" and DI(5 downto 4) = "00") or (DI(7 downto 5) = "110" and DI(1) = '1'))
else '0';
LATCNT1 <= '1' when WRCTRLEN = '1' and ((DI(7 downto 6) = "01" and DI(5 downto 4) = "00") or (DI(7 downto 5) = "110" and DI(2) = '1'))
else '0';
LATCNT2 <= '1' when WRCTRLEN = '1' and ((DI(7 downto 6) = "10" and DI(5 downto 4) = "00") or (DI(7 downto 5) = "110" and DI(3) = '1'))
else '0';
LATSTS0 <= '1' when WRCTRLEN = '1' and DI(7 downto 6) = "11" and DI(4) = '0' and DI(1) = '1'
else '0';
LATSTS1 <= '1' when WRCTRLEN = '1' and DI(7 downto 6) = "11" and DI(4) = '0' and DI(2) = '1'
else '0';
LATSTS2 <= '1' when WRCTRLEN = '1' and DI(7 downto 6) = "11" and DI(4) = '0' and DI(3) = '1'
else '0';
-- Assign the counter whose address is active. Not permissible to read back control register.
--
DO <= DO0 when A = "00"
else
DO1 when A = "01"
else
DO2 when A = "10"
else
(others => '0');
-- Instantiate the 3 counters within the 8254.
--
CTR0 : i8254_counter port map (
CLK => CLK,
RESET => RST,
--
DATA_IN => DI,
DATA_OUT => DO0,
WRITE => WR0,
READ => RD0,
CTRL_MODE_EN => CTRLM0,
LATCH_COUNT_EN => LATCNT0,
LATCH_STATUS_EN => LATSTS0,
--
CTR_CLK => CLK0,
CTR_GATE => GATE0,
CTR_OUT => OUT0
);
CTR1 : i8254_counter port map (
CLK => CLK,
RESET => RST,
--
DATA_IN => DI,
DATA_OUT => DO1,
WRITE => WR1,
READ => RD1,
CTRL_MODE_EN => CTRLM1,
LATCH_COUNT_EN => LATCNT1,
LATCH_STATUS_EN => LATSTS1,
--
CTR_CLK => CLK1,
CTR_GATE => GATE1,
CTR_OUT => OUT1
);
CTR2 : i8254_counter port map (
CLK => CLK,
RESET => RST,
--
DATA_IN => DI,
DATA_OUT => DO2,
WRITE => WR2,
READ => RD2,
CTRL_MODE_EN => CTRLM2,
LATCH_COUNT_EN => LATCNT2,
LATCH_STATUS_EN => LATSTS2,
--
CTR_CLK => CLK2,
CTR_GATE => GATE2,
CTR_OUT => OUT2
);
end Behavioral;