571 lines
18 KiB
VHDL
571 lines
18 KiB
VHDL
-------------------------------------------------------------------------------
|
|
--
|
|
-- Synthesizable model of TI's TMS9918A, TMS9928A, TMS9929A.
|
|
--
|
|
-- $Id: vdp18_cpuio.vhd,v 1.17 2006/06/18 10:47:01 arnim Exp $
|
|
--
|
|
-- CPU I/O Interface Module
|
|
--
|
|
-------------------------------------------------------------------------------
|
|
--
|
|
-- Copyright (c) 2006, Arnim Laeuger (arnim.laeuger@gmx.net)
|
|
--
|
|
-- All rights reserved
|
|
--
|
|
-- Redistribution and use in source and synthezised forms, with or without
|
|
-- modification, are permitted provided that the following conditions are met:
|
|
--
|
|
-- Redistributions of source code must retain the above copyright notice,
|
|
-- this list of conditions and the following disclaimer.
|
|
--
|
|
-- Redistributions in synthesized form must reproduce the above copyright
|
|
-- notice, this list of conditions and the following disclaimer in the
|
|
-- documentation and/or other materials provided with the distribution.
|
|
--
|
|
-- Neither the name of the author nor the names of other contributors may
|
|
-- be used to endorse or promote products derived from this software without
|
|
-- specific prior written permission.
|
|
--
|
|
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
|
|
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
-- POSSIBILITY OF SUCH DAMAGE.
|
|
--
|
|
-- Please report bugs to the author, but before you do so, please
|
|
-- make sure that this is not a derivative work and that
|
|
-- you have the latest version of this file.
|
|
--
|
|
-------------------------------------------------------------------------------
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
|
|
use work.vdp18_pack.access_t;
|
|
use work.vdp18_pack.opmode_t;
|
|
|
|
entity vdp18_cpuio is
|
|
|
|
port (
|
|
clk_i : in std_logic;
|
|
clk_en_10m7_i : in boolean;
|
|
clk_en_acc_i : in boolean;
|
|
reset_i : in boolean;
|
|
rd_i : in boolean;
|
|
wr_i : in boolean;
|
|
mode_i : in std_logic;
|
|
cd_i : in std_logic_vector(0 to 7);
|
|
cd_o : out std_logic_vector(0 to 7);
|
|
cd_oe_o : out std_logic;
|
|
access_type_i : in access_t;
|
|
opmode_o : out opmode_t;
|
|
vram_we_o : out std_logic;
|
|
vram_a_o : out std_logic_vector(0 to 13);
|
|
vram_d_o : out std_logic_vector(0 to 7);
|
|
vram_d_i : in std_logic_vector(0 to 7);
|
|
spr_coll_i : in boolean;
|
|
spr_5th_i : in boolean;
|
|
spr_5th_num_i : in std_logic_vector(0 to 4);
|
|
reg_ev_o : out boolean;
|
|
reg_16k_o : out boolean;
|
|
reg_blank_o : out boolean;
|
|
reg_size1_o : out boolean;
|
|
reg_mag1_o : out boolean;
|
|
reg_ntb_o : out std_logic_vector(0 to 3);
|
|
reg_ctb_o : out std_logic_vector(0 to 7);
|
|
reg_pgb_o : out std_logic_vector(0 to 2);
|
|
reg_satb_o : out std_logic_vector(0 to 6);
|
|
reg_spgb_o : out std_logic_vector(0 to 2);
|
|
reg_col1_o : out std_logic_vector(0 to 3);
|
|
reg_col0_o : out std_logic_vector(0 to 3);
|
|
irq_i : in boolean;
|
|
int_n_o : out std_logic
|
|
);
|
|
|
|
end vdp18_cpuio;
|
|
|
|
|
|
library ieee;
|
|
use ieee.numeric_std.all;
|
|
|
|
use work.vdp18_pack.all;
|
|
|
|
architecture rtl of vdp18_cpuio is
|
|
|
|
type state_t is (ST_IDLE,
|
|
ST_RD_MODE0, ST_WR_MODE0,
|
|
ST_RD_MODE1,
|
|
ST_WR_MODE1_1ST, ST_WR_MODE1_1ST_IDLE,
|
|
ST_WR_MODE1_2ND_VREAD, ST_WR_MODE1_2ND_VWRITE,
|
|
ST_WR_MODE1_2ND_RWRITE);
|
|
signal state_s,
|
|
state_q : state_t;
|
|
|
|
signal buffer_q : std_logic_vector(0 to 7);
|
|
|
|
signal addr_q : unsigned(0 to 13);
|
|
|
|
signal incr_addr_s,
|
|
load_addr_s : boolean;
|
|
|
|
signal wrbuf_cpu_s : boolean;
|
|
signal sched_rdvram_s,
|
|
rdvram_sched_q,
|
|
rdvram_q : boolean;
|
|
signal abort_wrvram_s,
|
|
sched_wrvram_s,
|
|
wrvram_sched_q,
|
|
wrvram_q : boolean;
|
|
|
|
signal write_tmp_s : boolean;
|
|
signal tmp_q : std_logic_vector(0 to 7);
|
|
signal write_reg_s : boolean;
|
|
|
|
-- control register bits ----------------------------------------------------
|
|
type ctrl_reg_t is array (natural range 7 downto 0) of
|
|
std_logic_vector(0 to 7);
|
|
signal ctrl_reg_q : ctrl_reg_t;
|
|
|
|
-- status register ----------------------------------------------------------
|
|
signal status_reg_s : std_logic_vector(0 to 7);
|
|
signal destr_rd_status_s : boolean;
|
|
signal sprite_5th_q : boolean;
|
|
signal sprite_5th_num_q : std_logic_vector(0 to 4);
|
|
signal sprite_coll_q : boolean;
|
|
signal int_n_q : std_logic;
|
|
|
|
|
|
type read_mux_t is (RDMUX_STATUS, RDMUX_READAHEAD);
|
|
signal read_mux_s : read_mux_t;
|
|
|
|
begin
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Process seq
|
|
--
|
|
-- Purpose:
|
|
-- Implements the sequential elements.
|
|
--
|
|
seq: process (clk_i, reset_i)
|
|
variable incr_addr_v : boolean;
|
|
begin
|
|
if reset_i then
|
|
state_q <= ST_IDLE;
|
|
buffer_q <= (others => '0');
|
|
addr_q <= (others => '0');
|
|
rdvram_sched_q <= false;
|
|
rdvram_q <= false;
|
|
wrvram_sched_q <= false;
|
|
wrvram_q <= false;
|
|
|
|
elsif clk_i'event and clk_i = '1' then
|
|
-- default assignments
|
|
incr_addr_v := incr_addr_s;
|
|
|
|
if clk_en_10m7_i then
|
|
-- update state vector ------------------------------------------------
|
|
state_q <= state_s;
|
|
|
|
-- buffer and flag control --------------------------------------------
|
|
if wrbuf_cpu_s then
|
|
-- write read-ahead buffer from CPU bus
|
|
buffer_q <= cd_i;
|
|
-- immediately stop read-ahead
|
|
rdvram_sched_q <= false;
|
|
rdvram_q <= false;
|
|
elsif clk_en_acc_i and
|
|
rdvram_q and
|
|
access_type_i = AC_CPU then
|
|
-- write read-ahead buffer from VRAM during CPU access slot
|
|
buffer_q <= vram_d_i;
|
|
-- stop scanning for CPU data
|
|
rdvram_q <= false;
|
|
-- increment read-ahead address
|
|
incr_addr_v := true;
|
|
end if;
|
|
|
|
if sched_rdvram_s then
|
|
-- immediately stop write-back
|
|
wrvram_sched_q <= false;
|
|
wrvram_q <= false;
|
|
-- schedule read-ahead
|
|
rdvram_sched_q <= true;
|
|
end if;
|
|
|
|
if sched_wrvram_s then
|
|
-- schedule write-back
|
|
wrvram_sched_q <= true;
|
|
end if;
|
|
|
|
if abort_wrvram_s then
|
|
-- stop scanning for write-back
|
|
wrvram_q <= false;
|
|
end if;
|
|
|
|
if rdvram_sched_q and clk_en_acc_i then
|
|
-- align scheduled read-ahead with access slot phase
|
|
rdvram_sched_q <= false;
|
|
rdvram_q <= true;
|
|
end if;
|
|
if wrvram_sched_q and clk_en_acc_i then
|
|
-- align scheduled write-back with access slot phase
|
|
wrvram_sched_q <= false;
|
|
wrvram_q <= true;
|
|
end if;
|
|
|
|
-- manage address -----------------------------------------------------
|
|
if load_addr_s then
|
|
addr_q(6 to 13) <= unsigned(tmp_q);
|
|
addr_q(0 to 5) <= unsigned(cd_i(2 to 7));
|
|
elsif incr_addr_v then
|
|
addr_q <= addr_q + 1;
|
|
end if;
|
|
|
|
end if;
|
|
end if;
|
|
end process seq;
|
|
--
|
|
-----------------------------------------------------------------------------
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Process wback_ctrl
|
|
--
|
|
-- Purpose:
|
|
-- Write-back control.
|
|
--
|
|
wback_ctrl: process (clk_en_acc_i,
|
|
access_type_i,
|
|
wrvram_q)
|
|
begin
|
|
-- default assignments
|
|
abort_wrvram_s <= false;
|
|
incr_addr_s <= false;
|
|
vram_we_o <= '0';
|
|
|
|
if wrvram_q then
|
|
if access_type_i = AC_CPU then
|
|
-- signal write access to VRAM
|
|
vram_we_o <= '1';
|
|
|
|
if clk_en_acc_i then
|
|
-- clear write-back flag and increment address
|
|
abort_wrvram_s <= true;
|
|
incr_addr_s <= true;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process wback_ctrl;
|
|
--
|
|
-----------------------------------------------------------------------------
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Process reg_if
|
|
--
|
|
-- Purpose:
|
|
-- Implements the register interface.
|
|
--
|
|
reg_if: process (clk_i, reset_i)
|
|
variable reg_addr_v : unsigned(0 to 2);
|
|
begin
|
|
if reset_i then
|
|
tmp_q <= (others => '0');
|
|
ctrl_reg_q <= (others => (others => '0'));
|
|
sprite_coll_q <= false;
|
|
sprite_5th_q <= false;
|
|
sprite_5th_num_q <= (others => '0');
|
|
int_n_q <= '1';
|
|
|
|
elsif clk_i'event and clk_i = '1' then
|
|
if clk_en_10m7_i then
|
|
-- Temporary register -------------------------------------------------
|
|
if write_tmp_s then
|
|
tmp_q <= cd_i;
|
|
end if;
|
|
|
|
-- Registers 0 to 7 ---------------------------------------------------
|
|
if write_reg_s then
|
|
reg_addr_v := unsigned(cd_i(5 to 7));
|
|
ctrl_reg_q(to_integer(reg_addr_v)) <= tmp_q;
|
|
end if;
|
|
|
|
end if;
|
|
|
|
-- Fifth sprite handling ------------------------------------------------
|
|
if spr_5th_i and not sprite_5th_q then
|
|
sprite_5th_q <= true;
|
|
sprite_5th_num_q <= spr_5th_num_i;
|
|
elsif destr_rd_status_s then
|
|
sprite_5th_q <= false;
|
|
end if;
|
|
|
|
-- Sprite collision flag ------------------------------------------------
|
|
if spr_coll_i then
|
|
sprite_coll_q <= true;
|
|
elsif destr_rd_status_s then
|
|
sprite_coll_q <= false;
|
|
end if;
|
|
|
|
-- Interrupt ------------------------------------------------------------
|
|
if irq_i then
|
|
int_n_q <= '0';
|
|
elsif destr_rd_status_s then
|
|
int_n_q <= '1';
|
|
end if;
|
|
end if;
|
|
end process reg_if;
|
|
--
|
|
-----------------------------------------------------------------------------
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Process access_ctrl
|
|
--
|
|
-- Purpose:
|
|
-- Implements the combinational logic for the CPU I/F FSM.
|
|
-- Decodes the CPU I/F FSM state and generates the control signals for the
|
|
-- register and VRAM logic.
|
|
--
|
|
access_ctrl: process (state_q,
|
|
rd_i, wr_i,
|
|
mode_i,
|
|
cd_i)
|
|
type transfer_mode_t is (TM_NONE,
|
|
TM_RD_MODE0, TM_WR_MODE0,
|
|
TM_RD_MODE1, TM_WR_MODE1);
|
|
variable transfer_mode_v : transfer_mode_t;
|
|
begin
|
|
-- default assignments
|
|
state_s <= state_q;
|
|
sched_rdvram_s <= false;
|
|
sched_wrvram_s <= false;
|
|
wrbuf_cpu_s <= false;
|
|
write_tmp_s <= false;
|
|
write_reg_s <= false;
|
|
load_addr_s <= false;
|
|
read_mux_s <= RDMUX_STATUS;
|
|
destr_rd_status_s <= false;
|
|
|
|
-- determine transfer mode
|
|
transfer_mode_v := TM_NONE;
|
|
if mode_i = '0' then
|
|
if rd_i then
|
|
transfer_mode_v := TM_RD_MODE0;
|
|
end if;
|
|
if wr_i then
|
|
transfer_mode_v := TM_WR_MODE0;
|
|
end if;
|
|
else
|
|
if rd_i then
|
|
transfer_mode_v := TM_RD_MODE1;
|
|
end if;
|
|
if wr_i then
|
|
transfer_mode_v := TM_WR_MODE1;
|
|
end if;
|
|
end if;
|
|
|
|
-- FSM state transitions
|
|
case state_q is
|
|
-- ST_IDLE: waiting for CPU access --------------------------------------
|
|
when ST_IDLE =>
|
|
case transfer_mode_v is
|
|
when TM_RD_MODE0 =>
|
|
state_s <= ST_RD_MODE0;
|
|
when TM_WR_MODE0 =>
|
|
state_s <= ST_WR_MODE0;
|
|
when TM_RD_MODE1 =>
|
|
state_s <= ST_RD_MODE1;
|
|
when TM_WR_MODE1 =>
|
|
state_s <= ST_WR_MODE1_1ST;
|
|
when others =>
|
|
null;
|
|
end case;
|
|
|
|
-- ST_RD_MODE0: read from VRAM ------------------------------------------
|
|
when ST_RD_MODE0 =>
|
|
-- set read mux
|
|
read_mux_s <= RDMUX_READAHEAD;
|
|
|
|
if transfer_mode_v = TM_NONE then
|
|
-- CPU finished read access:
|
|
-- schedule new read-ahead and return to idle
|
|
state_s <= ST_IDLE;
|
|
sched_rdvram_s <= true;
|
|
end if;
|
|
|
|
-- ST_WR_MODE0: write to VRAM -------------------------------------------
|
|
when ST_WR_MODE0 =>
|
|
-- write data from CPU to write-back/read-ahead buffer
|
|
wrbuf_cpu_s <= true;
|
|
|
|
if transfer_mode_v = TM_NONE then
|
|
-- CPU finished write access:
|
|
-- schedule new write-back and return to idle
|
|
state_s <= ST_IDLE;
|
|
sched_wrvram_s <= true;
|
|
end if;
|
|
|
|
-- ST_RD_MODE1: read from status register -------------------------------
|
|
when ST_RD_MODE1 =>
|
|
-- set read mux
|
|
read_mux_s <= RDMUX_STATUS;
|
|
|
|
if transfer_mode_v = TM_NONE then
|
|
-- CPU finished read access:
|
|
-- destructive read of status register and return to IDLE
|
|
destr_rd_status_s <= true;
|
|
state_s <= ST_IDLE;
|
|
end if;
|
|
|
|
-- ST_WR_MODE1_1ST: save first byte -------------------------------------
|
|
when ST_WR_MODE1_1ST =>
|
|
-- update temp register
|
|
write_tmp_s <= true;
|
|
|
|
if transfer_mode_v = TM_NONE then
|
|
-- CPU finished write access:
|
|
-- become idle but remember that the first byte of a paired write
|
|
-- has been written
|
|
state_s <= ST_WR_MODE1_1ST_IDLE;
|
|
end if;
|
|
|
|
-- ST_WR_MODE1_1ST_IDLE: wait for next access ---------------------------
|
|
when ST_WR_MODE1_1ST_IDLE =>
|
|
-- determine type of next access
|
|
case transfer_mode_v is
|
|
when TM_RD_MODE0 =>
|
|
state_s <= ST_RD_MODE0;
|
|
when TM_WR_MODE0 =>
|
|
state_s <= ST_WR_MODE0;
|
|
when TM_RD_MODE1 =>
|
|
state_s <= ST_RD_MODE1;
|
|
when TM_WR_MODE1 =>
|
|
case cd_i(0 to 1) is
|
|
when "00" =>
|
|
state_s <= ST_WR_MODE1_2ND_VREAD;
|
|
when "01" =>
|
|
state_s <= ST_WR_MODE1_2ND_VWRITE;
|
|
when "10" | "11" =>
|
|
state_s <= ST_WR_MODE1_2ND_RWRITE;
|
|
when others =>
|
|
null;
|
|
end case;
|
|
when others =>
|
|
null;
|
|
end case;
|
|
|
|
-- ST_WR_MODE1_2ND_VREAD: write second byte of address, then read ahead -
|
|
when ST_WR_MODE1_2ND_VREAD =>
|
|
load_addr_s <= true;
|
|
|
|
if transfer_mode_v = TM_NONE then
|
|
-- CPU finished write access:
|
|
-- schedule new read-ahead and return to idle
|
|
sched_rdvram_s <= true;
|
|
state_s <= ST_IDLE;
|
|
end if;
|
|
|
|
-- ST_WR_MODE1_2ND_VWRITE: write second byte of address
|
|
when ST_WR_MODE1_2ND_VWRITE =>
|
|
load_addr_s <= true;
|
|
|
|
if transfer_mode_v = TM_NONE then
|
|
-- CPU finished write access:
|
|
-- return to idle
|
|
state_s <= ST_IDLE;
|
|
end if;
|
|
|
|
-- ST_WR_MODE1_2ND_RWRITE: write to register ----------------------------
|
|
when ST_WR_MODE1_2ND_RWRITE =>
|
|
write_reg_s <= true;
|
|
|
|
if transfer_mode_v = TM_NONE then
|
|
-- CPU finished write access:
|
|
-- return to idle
|
|
state_s <= ST_IDLE;
|
|
end if;
|
|
|
|
when others =>
|
|
null;
|
|
|
|
end case;
|
|
|
|
end process access_ctrl;
|
|
--
|
|
-----------------------------------------------------------------------------
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Process mode_decode
|
|
--
|
|
-- Purpose:
|
|
-- Decodes the display mode from the M1, M2, M3 bits.
|
|
--
|
|
mode_decode: process (ctrl_reg_q)
|
|
variable mode_v : std_logic_vector(0 to 2);
|
|
begin
|
|
mode_v := ctrl_reg_q(1)(3) & -- M1
|
|
ctrl_reg_q(1)(4) & -- M2
|
|
ctrl_reg_q(0)(6); -- M3
|
|
|
|
case mode_v is
|
|
when "000" =>
|
|
opmode_o <= OPMODE_GRAPH1;
|
|
when "001" =>
|
|
opmode_o <= OPMODE_GRAPH2;
|
|
when "010" =>
|
|
opmode_o <= OPMODE_MULTIC;
|
|
when "100" =>
|
|
opmode_o <= OPMODE_TEXTM;
|
|
when others =>
|
|
opmode_o <= OPMODE_TEXTM;
|
|
end case;
|
|
end process mode_decode;
|
|
--
|
|
-----------------------------------------------------------------------------
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Build status register
|
|
-----------------------------------------------------------------------------
|
|
status_reg_s <= not int_n_q &
|
|
to_std_logic_f(sprite_5th_q) &
|
|
to_std_logic_f(sprite_coll_q) &
|
|
sprite_5th_num_q;
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Output mapping
|
|
-----------------------------------------------------------------------------
|
|
vram_a_o <= std_logic_vector(addr_q);
|
|
vram_d_o <= buffer_q;
|
|
|
|
cd_o <= buffer_q
|
|
when read_mux_s = RDMUX_READAHEAD else
|
|
status_reg_s;
|
|
cd_oe_o <= '1'
|
|
when rd_i else
|
|
'0';
|
|
|
|
reg_ev_o <= to_boolean_f(ctrl_reg_q(0)(7));
|
|
reg_16k_o <= to_boolean_f(ctrl_reg_q(1)(0));
|
|
reg_blank_o <= not to_boolean_f(ctrl_reg_q(1)(1));
|
|
reg_size1_o <= to_boolean_f(ctrl_reg_q(1)(6));
|
|
reg_mag1_o <= to_boolean_f(ctrl_reg_q(1)(7));
|
|
reg_ntb_o <= ctrl_reg_q(2)(4 to 7);
|
|
reg_ctb_o <= ctrl_reg_q(3);
|
|
reg_pgb_o <= ctrl_reg_q(4)(5 to 7);
|
|
reg_satb_o <= ctrl_reg_q(5)(1 to 7);
|
|
reg_spgb_o <= ctrl_reg_q(6)(5 to 7);
|
|
reg_col1_o <= ctrl_reg_q(7)(0 to 3);
|
|
reg_col0_o <= ctrl_reg_q(7)(4 to 7);
|
|
int_n_o <= int_n_q or not ctrl_reg_q(1)(2);
|
|
|
|
end rtl;
|