AtomBusMon/src/MC6809CpuMon.vhd

407 lines
13 KiB
VHDL

-------------------------------------------------------------------------------
-- Copyright (c) 2019 David Banks
--
--------------------------------------------------------------------------------
-- ____ ____
-- / /\/ /
-- /___/ \ /
-- \ \ \/
-- \ \
-- / / Filename : MC6808CpuMon.vhd
-- /___/ /\ Timestamp : 24/10/2019
-- \ \ / \
-- \___\/\___\
--
--Design Name: MC6808CpuMon
--Device: multiple
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity MC6809CpuMon is
generic (
ClkMult : integer;
ClkDiv : integer;
ClkPer : real;
num_comparators : integer;
avr_prog_mem_size : integer
);
port (
-- Fast clock
clock : in std_logic;
-- Quadrature clocks
E : in std_logic;
Q : in std_logic;
--6809 Signals
DMA_n_BREQ_n : in std_logic;
-- 6809E Sig
TSC : in std_logic;
LIC : out std_logic;
AVMA : out std_logic;
BUSY : out std_logic;
-- Signals common to both 6809 and 6809E
RES_n : in std_logic;
NMI_n : in std_logic;
IRQ_n : in std_logic;
FIRQ_n : in std_logic;
HALT_n : in std_logic;
BS : out std_logic;
BA : out std_logic;
R_W_n : out std_logic;
Addr : out std_logic_vector(15 downto 0);
Data : inout std_logic_vector(7 downto 0);
-- External trigger inputs
trig : in std_logic_vector(1 downto 0);
-- Serial Console
avr_RxD : in std_logic;
avr_TxD : out std_logic;
-- Switches
sw_reset_cpu : in std_logic;
sw_reset_avr : in std_logic;
-- LEDs
led_bkpt : out std_logic;
led_trig0 : out std_logic;
led_trig1 : out std_logic;
-- OHO_DY1 connected to test connector
tmosi : out std_logic;
tdin : out std_logic;
tcclk : out std_logic;
-- Debugging signals
test1 : out std_logic;
test2 : out std_logic
);
end MC6809CpuMon;
architecture behavioral of MC6809CpuMon is
signal clock_avr : std_logic;
signal cpu_clk : std_logic;
signal cpu_reset_n : std_logic;
signal busmon_clk : std_logic;
signal R_W_n_int : std_logic;
signal NMI_sync : std_logic;
signal IRQ_sync : std_logic;
signal FIRQ_sync : std_logic;
signal HALT_sync : std_logic;
signal Addr_int : std_logic_vector(15 downto 0);
signal Din : std_logic_vector(7 downto 0);
signal Dout : std_logic_vector(7 downto 0);
signal Dbusmon : std_logic_vector(7 downto 0);
signal Sync_int : std_logic;
signal hold : std_logic;
signal memory_rd : std_logic;
signal memory_wr : std_logic;
signal memory_rd1 : std_logic;
signal memory_wr1 : std_logic;
signal memory_addr : std_logic_vector(15 downto 0);
signal memory_addr1 : std_logic_vector(15 downto 0);
signal memory_dout : std_logic_vector(7 downto 0);
signal memory_din : std_logic_vector(7 downto 0);
signal memory_done : std_logic;
signal Regs : std_logic_vector(111 downto 0);
signal Regs1 : std_logic_vector(255 downto 0);
signal last_PC : std_logic_vector(15 downto 0);
signal ifetch : std_logic;
signal ifetch1 : std_logic;
signal SS_Single : std_logic;
signal SS_Step : std_logic;
signal CountCycle : std_logic;
signal int_ctrl : std_logic_vector(7 downto 0);
signal LIC_int : std_logic;
signal E_a : std_logic; -- E delayed by 0..20ns
signal E_b : std_logic; -- E delayed by 20..40ns
signal E_c : std_logic; -- E delayed by 40..60ns
signal E_d : std_logic; -- E delayed by 60..80ns
signal E_e : std_logic; -- E delayed by 80..100ns
signal E_f : std_logic; -- E delayed by 100..120ns
signal E_g : std_logic; -- E delayed by 120..140ns
signal E_h : std_logic; -- E delayed by 120..140ns
signal E_i : std_logic; -- E delayed by 120..140ns
signal data_wr : std_logic;
signal nRSTout : std_logic;
signal FIRQ_n_masked : std_logic;
signal IRQ_n_masked : std_logic;
signal NMI_n_masked : std_logic;
signal RES_n_masked : std_logic;
begin
LIC <= LIC_int;
-- The following outputs are not implemented
-- BUSY (6809E mode)
BUSY <= '0';
-- The following inputs are not implemented
-- DMA_n_BREQ_n (6809 mode)
inst_dcm0 : entity work.DCM0
generic map (
ClkMult => ClkMult,
ClkDiv => ClkDiv,
ClkPer => ClkPer
)
port map(
CLKIN_IN => clock,
CLKFX_OUT => clock_avr
);
mon : entity work.BusMonCore
generic map (
num_comparators => num_comparators,
avr_prog_mem_size => avr_prog_mem_size
)
port map (
clock_avr => clock_avr,
busmon_clk => busmon_clk,
busmon_clken => '1',
cpu_clk => cpu_clk,
cpu_clken => '1',
Addr => Addr_int,
Data => Dbusmon,
Rd_n => not R_W_n_int,
Wr_n => R_W_n_int,
RdIO_n => '1',
WrIO_n => '1',
Sync => Sync_int,
Rdy => open,
nRSTin => RES_n_masked,
nRSTout => cpu_reset_n,
CountCycle => CountCycle,
trig => trig,
avr_RxD => avr_RxD,
avr_TxD => avr_TxD,
sw_reset_cpu => sw_reset_cpu,
sw_reset_avr => sw_reset_avr,
led_bkpt => led_bkpt,
led_trig0 => led_trig0,
led_trig1 => led_trig1,
tmosi => tmosi,
tdin => tdin,
tcclk => tcclk,
Regs => Regs1,
RdMemOut => memory_rd,
WrMemOut => memory_wr,
RdIOOut => open,
WrIOOut => open,
AddrOut => memory_addr,
DataOut => memory_dout,
DataIn => memory_din,
Done => memory_done,
int_ctrl => int_ctrl,
SS_Step => SS_Step,
SS_Single => SS_Single
);
-- The two int control bits work as follows
-- 00 -> IRQ_n (enabled)
-- 01 -> IRQ_n or SS_Single (enabled when free-running)
-- 10 -> 0 (forced)
-- 11 -> 1 (disabled)
FIRQ_n_masked <= int_ctrl(0) when int_ctrl(1) = '1' else
FIRQ_n or (int_ctrl(0) and SS_single);
IRQ_n_masked <= int_ctrl(2) when int_ctrl(3) = '1' else
IRQ_n or (int_ctrl(2) and SS_single);
NMI_n_masked <= int_ctrl(4) when int_ctrl(5) = '1' else
NMI_n or (int_ctrl(4) and SS_single);
RES_n_masked <= int_ctrl(6) when int_ctrl(7) = '1' else
RES_n or (int_ctrl(6) and SS_single);
-- The CPU is slightly pipelined and the register update of the last
-- instruction overlaps with the opcode fetch of the next instruction.
--
-- If the single stepping stopped on the opcode fetch cycle, then the registers
-- valued would not accurately reflect the previous instruction.
--
-- To work around this, when single stepping, we stop on the cycle after
-- the opcode fetch, which means the program counter has advanced.
--
-- To hide this from the user single stepping, all we need to do is to
-- also pipeline the value of the program counter by one stage to compensate.
last_pc_gen : process(cpu_clk)
begin
if rising_edge(cpu_clk) then
if (hold = '0') then
last_PC <= Regs(95 downto 80);
end if;
end if;
end process;
Regs1( 79 downto 0) <= Regs( 79 downto 0);
Regs1( 95 downto 80) <= last_PC;
Regs1(111 downto 96) <= Regs(111 downto 96);
Regs1(255 downto 112) <= (others => '0');
inst_cpu09: entity work.cpu09 port map (
clk => cpu_clk,
rst => not cpu_reset_n,
vma => AVMA,
lic_out => LIC_int,
ifetch => ifetch,
opfetch => open,
ba => BA,
bs => BS,
addr => Addr_int,
rw => R_W_n_int,
data_out => Dout,
data_in => Din,
irq => IRQ_sync,
firq => FIRQ_sync,
nmi => NMI_sync,
halt => HALT_sync,
hold => hold,
Regs => Regs
);
-- Synchronize all external inputs, to avoid subtle bugs like missed interrupts
irq_gen : process(cpu_clk)
begin
if falling_edge(cpu_clk) then
NMI_sync <= not NMI_n_masked;
IRQ_sync <= not IRQ_n_masked;
FIRQ_sync <= not FIRQ_n_masked;
HALT_sync <= not HALT_n;
end if;
end process;
-- This block generates a sync signal that has the same characteristic as
-- a 6502 sync, i.e. asserted during the fetching the first byte of each instruction.
-- The below logic copes ifetch being active for all bytes of the instruction.
sync_gen : process(cpu_clk)
begin
if rising_edge(cpu_clk) then
if (hold = '0') then
ifetch1 <= ifetch and not LIC_int;
end if;
end if;
end process;
Sync_int <= ifetch and not ifetch1;
-- This block generates a hold signal that acts as the inverse of a clock enable
-- for the 6809. See comments above for why this is a cycle later than the way
-- we would do if for the 6502.
hold_gen : process(cpu_clk)
begin
if rising_edge(cpu_clk) then
if (Sync_int = '1') then
-- stop after the opcode has been fetched
hold <= SS_Single;
elsif (SS_Step = '1') then
-- start again when the single step command is issues
hold <= '0';
end if;
end if;
end process;
-- Only count cycles when the 6809 is actually running
CountCycle <= not hold;
-- this block delays memory_rd, memory_wr, memory_addr until the start of the next cpu clk cycle
-- necessary because the cpu mon block is clocked of the opposite edge of the clock
-- this allows a full cpu clk cycle for cpu mon reads and writes
mem_gen : process(cpu_clk)
begin
if rising_edge(cpu_clk) then
memory_rd1 <= memory_rd;
memory_wr1 <= memory_wr;
memory_addr1 <= memory_addr;
end if;
end process;
R_W_n <= 'Z' when TSC = '1' else
'1' when memory_rd1 = '1' else
'0' when memory_wr1 = '1' else
R_W_n_int;
Addr <= (others => 'Z') when TSC = '1' else
memory_addr1 when (memory_rd1 = '1' or memory_wr1 = '1') else
Addr_int;
data_latch : process(E)
begin
if falling_edge(E) then
Din <= Data;
memory_din <= Data;
end if;
end process;
Data <= memory_dout when TSC = '0' and data_wr = '1' and memory_wr1 = '1' else
Dout when TSC = '0' and data_wr = '1' and R_W_n_int = '0' and memory_rd1 = '0' else
(others => 'Z');
-- Version of data seen by the Bus Mon need to use Din rather than the
-- external bus value as by the rising edge of cpu_clk we will have stopped driving
-- the external bus. On the ALS version we get away way this, but on the GODIL
-- version, due to the pullups, we don't. So all write watch breakpoints see
-- the data bus as 0xFF.
Dbusmon <= Din when R_W_n_int = '1' else Dout;
memory_done <= memory_rd1 or memory_wr1;
-- Delayed/Deglitched version of the E clock
e_gen : process(clock)
begin
if rising_edge(clock) then
E_a <= E;
E_b <= E_a;
if E_b /= E_i then
E_c <= E_b;
end if;
E_d <= E_c;
E_e <= E_d;
E_f <= E_e;
E_g <= E_f;
E_h <= E_g;
E_i <= E_h;
end if;
end process;
-- Main clock timing control
-- E_c is delayed by 40-60ns
-- On a real 6809 the output delay (to ADDR, RNW, BA, BS) is 65ns (measured)
cpu_clk <= not E_c;
busmon_clk <= E_c;
-- Data bus write timing control
--
-- When data_wr is 0 the bus is high impedence
--
-- This avoids bus conflicts when the direction of the data bus
-- changes from read to write (or visa versa).
--
-- Note: on the dragon this is not critical; setting to '1' seemed to work
data_wr <= Q or E;
-- Spare pins used for testing
test1 <= E_a;
test2 <= E_c;
end behavioral;