z80: major rewrite of memory access state machine

Change-Id: Icc5c7c991120ed155691c1e74517ac02f8ea2ada
This commit is contained in:
David Banks 2019-10-14 13:35:13 +01:00
parent 984ac1a2d3
commit 4c746994cb
6 changed files with 231 additions and 81 deletions

View File

@ -1,5 +1,5 @@
--------------------------------------------------------------------------------
-- Copyright (c) 2015 David Banks
-- Copyright (c) 2019 David Banks
--
--------------------------------------------------------------------------------
-- ____ ____
@ -8,18 +8,17 @@
-- \ \ \/
-- \ \
-- / / Filename : Z80CpuMon.vhd
-- /___/ /\ Timestamp : 22/06/2015
-- /___/ /\ Timestamp : 14/10/2018
-- \ \ / \
-- \___\/\___\
--
--Design Name: Z80CpuMon
--Device: XC3S250E
--Device: multiple
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use work.OhoPack.all ;
entity Z80CpuMon is
generic (
@ -51,6 +50,7 @@ entity Z80CpuMon is
BUSAK_n : out std_logic;
Addr : out std_logic_vector(15 downto 0);
Data : inout std_logic_vector(7 downto 0);
DOE_n : out std_logic;
-- External trigger inputs
trig : in std_logic_vector(1 downto 0);
@ -84,7 +84,7 @@ end Z80CpuMon;
architecture behavioral of Z80CpuMon is
type state_type is (idle, rd_init, rd_setup, rd, rd_hold, wr_init, wr_setup, wr, wr_hold, release);
type state_type is (idle, nop_t1, nop_t2, nop_t3, nop_t4, rd_t1, rd_wa, rd_t2, rd_t3, wr_t1, wr_wa, wr_t2, wr_t3);
signal state : state_type;
@ -105,6 +105,7 @@ type state_type is (idle, rd_init, rd_setup, rd, rd_hold, wr_init, wr_setup, wr,
signal SS_Single : std_logic;
signal SS_Step : std_logic;
signal SS_Step_held : std_logic;
signal SS_Running : std_logic;
signal CountCycle : std_logic;
signal skipNextOpcode : std_logic;
@ -119,6 +120,16 @@ type state_type is (idle, rd_init, rd_setup, rd, rd_hold, wr_init, wr_setup, wr,
signal memory_din : std_logic_vector(7 downto 0);
signal memory_done : std_logic;
signal io_rd1 : std_logic;
signal io_wr1 : std_logic;
signal memory_rd1 : std_logic;
signal memory_wr1 : std_logic;
signal mon_mreq_n : std_logic;
signal mon_iorq_n : std_logic;
signal mon_rd_n : std_logic;
signal mon_wr_n : std_logic;
signal mon_wait_n : std_logic;
signal INT_n_sync : std_logic;
signal NMI_n_sync : std_logic;
@ -158,6 +169,8 @@ type state_type is (idle, rd_init, rd_setup, rd, rd_hold, wr_init, wr_setup, wr,
signal clock_49_ctr : std_logic_vector(23 downto 0);
signal clock_avr_ctr : std_logic_vector(23 downto 0);
signal rfsh_addr : std_logic_vector(15 downto 0);
begin
-- Generics allows polarity of switches/LEDs to be tweaked from the project file
@ -167,6 +180,10 @@ begin
led6 <= not led6_n when LEDsActiveHigh else led6_n;
led8 <= not led8_n when LEDsActiveHigh else led8_n;
--------------------------------------------------------
-- Clocking
--------------------------------------------------------
inst_dcm0 : entity work.DCM0
generic map (
ClkMult => ClkMult,
@ -178,6 +195,13 @@ begin
CLKFX_OUT => clock_avr
);
cpu_clk <= CLK_n;
busmon_clk <= CLK_n;
--------------------------------------------------------
-- BusMonCore
--------------------------------------------------------
mon : entity work.BusMonCore
generic map (
num_comparators => 4,
@ -228,6 +252,10 @@ begin
SS_Step => SS_Step
);
--------------------------------------------------------
-- T80
--------------------------------------------------------
GenT80Core: if UseT80Core generate
inst_t80: entity work.T80a port map (
TS => TState,
@ -253,31 +281,39 @@ begin
);
end generate;
WAIT_n_int <= WAIT_n when SS_Single = '0' else
WAIT_n and SS_Step_held;
--------------------------------------------------------
-- Z80 specific single step / breakpoint logic
--------------------------------------------------------
CountCycle <= '1' when SS_Single = '0' or SS_Step_held = '1' else '0';
WAIT_n_int <= WAIT_n and SS_Running;
CountCycle <= '1' when SS_Single = '0' or SS_Running = '1' else '0';
sync_gen : process(CLK_n, RESET_n_int)
begin
if RESET_n_int = '0' then
NMI_n_sync <= '1';
INT_n_sync <= '1';
SS_Step_held <= '1';
SS_Running <= '1';
SS_Step_held <= '0';
elsif rising_edge(CLK_n) then
NMI_n_sync <= NMI_n;
INT_n_sync <= INT_n;
if (Sync0 = '1') then
if Sync0 = '1' and SS_Single = '1' then
-- stop at the end of T1 instruction fetch
SS_Step_held <= '0';
elsif (SS_Step = '1') then
-- start again when the single step command is issues
SS_Running <= '0';
elsif SS_Step_held = '1' and state = nop_t4 then
-- start again when the single step command is issued
SS_Running <= '1';
end if;
if SS_Step = '1' then
SS_Step_held <= '1';
elsif SS_Running = '1' then
SS_Step_held <= '0';
end if;
end if;
end process;
-- Logic to ignore the second M1 in multi-byte opcodes
skip_opcode_latch : process(CLK_n)
begin
@ -345,75 +381,181 @@ begin
-- Mux the data seen by the bus monitor appropriately
mon_data <= rd_data when Read_n <= '0' or ReadIO_n = '0' else ex_data;
-- Memory access
Addr <= memory_addr when (state /= idle) else Addr_int;
-- Mark the memory access as done when t3 is reached
memory_done <= '1' when state = rd_t3 or state = wr_t3 else '0';
MREQ_n <= '1' when (state = rd_init or state = wr_init or state = release) else
'0' when (state /= idle and io_not_mem = '0') else MREQ_n_int;
IORQ_n <= '1' when (state = rd_init or state = wr_init or state = release) else
'0' when (state /= idle and io_not_mem = '1') else IORQ_n_int;
WR_n <= '0' when (state = wr) else
'1' when (state /= idle) else WR_n_int;
RD_n <= '0' when (state = rd_setup or state = rd or state = rd_hold) else
'1' when (state /= idle) else RD_n_int;
M1_n <= '1' when (state /= idle) else M1_n_int;
memory_done <= '1' when (state = rd_hold or state = wr_hold) else '0';
-- Multiplex the bus control signals
-- The _int versions come from the T80
-- The mon_ versions come from the state machine below
-- TODO: Also need to take account of BUSRQ_n/BUSAK_n
Data <= memory_dout when state = wr_setup or state = wr or state = wr_hold else
MREQ_n <= MREQ_n_int when state = idle else mon_mreq_n;
IORQ_n <= IORQ_n_int when state = idle else mon_iorq_n;
WR_n <= WR_n_int when state = idle else mon_wr_n;
RD_n <= RD_n_int when state = idle else mon_rd_n;
M1_n <= M1_n_int when state = idle else '1';
Addr <= Addr_int when state = idle else
x"0000" when state = nop_t1 or state = nop_t2 else
rfsh_addr when state = nop_t3 or state = nop_t4 else
memory_addr;
Data <= memory_dout when state = wr_wa or state = wr_t2 or state = wr_t3 else
Dout when state = idle and Den = '1' else
(others => 'Z');
(others => 'Z');
DOE_n <= '0' when state = wr_wa or state = wr_t2 or state = wr_t3 else
'0' when state = idle and Den = '1' else
'1';
Din <= Data;
-- TODO: Add refresh generation into idle loop
men_access_machine : process(CLK_n)
men_access_machine_rising : process(CLK_n, RESET_n)
begin
if (RESET_n = '0') then
state <= idle;
elsif falling_edge(CLK_n) then
case state IS
when idle =>
if (memory_wr = '1' or io_wr = '1') then
state <= wr_init;
io_not_mem <= io_wr;
elsif (memory_rd = '1' or io_rd = '1') then
state <= rd_init;
io_not_mem <= io_rd;
end if;
when rd_init =>
state <= rd_setup;
when rd_setup =>
if (WAIT_n = '1') then
state <= rd;
end if;
when rd =>
state <= rd_hold;
when rd_hold =>
state <= idle;
when wr_init =>
state <= wr_setup;
when wr_setup =>
if (WAIT_n = '1') then
state <= wr;
end if;
when wr =>
state <= wr_hold;
when wr_hold =>
state <= release;
when release =>
state <= idle;
memory_rd1 <= '0';
memory_wr1 <= '0';
io_rd1 <= '0';
io_wr1 <= '0';
elsif rising_edge(CLK_n) then
-- Extend the 1-cycle long request strobes from BusMonCore
-- until we are ready to generate a bus cycle
if memory_rd = '1' then
memory_rd1 <= '1';
elsif state = rd_t1 then
memory_rd1 <= '0';
end if;
if memory_wr = '1' then
memory_wr1 <= '1';
elsif state = wr_t1 then
memory_wr1 <= '0';
end if;
if io_rd = '1' then
io_rd1 <= '1';
elsif state = rd_t1 then
io_rd1 <= '0';
end if;
if io_wr = '1' then
io_wr1 <= '1';
elsif state = wr_t1 then
io_wr1 <= '0';
end if;
-- Main state machine, generating refresh, read and write cycles
-- (the timing should exactly match those of the Z80)
case state is
-- Idle is when T80 is running
when idle =>
if SS_Running = '0' then
-- If the T80 is stopped, start genering refresh cycles
state <= nop_t1;
-- Load the initial refresh address from I/R in the T80
rfsh_addr <= Regs(199 downto 192) & Regs(207 downto 200);
end if;
-- Refresh cycle
when nop_t1 =>
state <= nop_t2;
-- Increment the refresh address (7 bits, just like the Z80)
rfsh_addr(6 downto 0) <= rfsh_addr(6 downto 0) + 1;
when nop_t2 =>
state <= nop_t3;
when nop_t3 =>
state <= nop_t4;
when nop_t4 =>
if memory_wr1 = '1' or io_wr1 = '1' then
state <= wr_t1;
io_not_mem <= io_wr1;
elsif memory_rd1 = '1' or io_rd1 = '1' then
state <= rd_t1;
io_not_mem <= io_rd1;
elsif SS_Step_held = '1' or SS_Single = '0' then
state <= idle;
else
state <= nop_t1;
end if;
-- Read cycle
when rd_t1 =>
if io_not_mem = '1' then
state <= rd_wa;
else
state <= rd_t2;
end if;
when rd_wa =>
state <= rd_t2;
when rd_t2 =>
if mon_wait_n = '1' then
state <= rd_t3;
end if;
when rd_t3 =>
state <= nop_t1;
-- Write cycle
when wr_t1 =>
if io_not_mem = '1' then
state <= wr_wa;
else
state <= wr_t2;
end if;
when wr_wa =>
state <= wr_t2;
when wr_t2 =>
if mon_wait_n = '1' then
state <= wr_t3;
end if;
when wr_t3 =>
state <= nop_t1;
end case;
end if;
end process;
men_access_machine_falling : process(RESET_n)
begin
if falling_edge(CLK_n) then
-- For memory access cycles, mreq/iorq/rd/wr all change in the middle of
-- the t state, so retime these on the falling edge of clock
if state = rd_t1 or state = rd_wa or state = rd_t2 or state = wr_t1 or state = wr_wa or state = wr_t2 then
if io_not_mem = '0' then
-- Memory cycle
mon_mreq_n <= '0';
mon_iorq_n <= '1';
else
-- IO cycle
mon_mreq_n <= '1';
mon_iorq_n <= '0';
end if;
elsif state = nop_t3 then
-- Refresh cycle
mon_mreq_n <= '0';
mon_iorq_n <= '1';
else
-- Idle cycle
mon_mreq_n <= '1';
mon_iorq_n <= '1';
end if;
-- Read strobe
if state = rd_t1 or state = rd_wa or state = rd_t2 then
mon_rd_n <= '0';
else
mon_rd_n <= '1';
end if;
-- Write strobe
if state = wr_wa or state = wr_t2 then
mon_wr_n <= '0';
else
mon_wr_n <= '1';
end if;
-- Sample wait on the falling edge of the clock
mon_wait_n <= WAIT_n;
end if;
end process;
RESET_n_int <= RESET_n and sw_interrupt_n and nRST;
avr_TxD <= avr_Txd_int;
@ -443,7 +585,5 @@ begin
--test3 <= TState(2);
--test4 <= CLK_n;
cpu_clk <= CLK_n;
busmon_clk <= CLK_n;
end behavioral;

View File

@ -87,6 +87,7 @@ architecture behavioral of Z80CpuMonALS is
signal BUSAK_n_int : std_logic;
signal WR_n_int : std_logic;
signal DOE_n : std_logic;
begin
@ -97,8 +98,8 @@ begin
OEA1_n <= not BUSAK_n_int;
OEA2_n <= not BUSAK_n_int;
OED_n <= not BUSAK_n_int; -- TODO: This needs to come from the Z80 core
DIRD <= WR_n_int; -- TODO: This needs to come from the Z80 core
OED_n <= not BUSAK_n_int;
DIRD <= DOE_n;
wrapper : entity work.Z80CpuMon
generic map (
@ -130,6 +131,7 @@ begin
BUSAK_n => BUSAK_n_int,
Addr => Addr,
Data => Data,
DOE_n => DOE_n,
-- External trigger inputs
trig => trig,

View File

@ -70,14 +70,14 @@ NET "test2" LOC="P66" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 4 ;
NET "test3" LOC="P12" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 4 ;
NET "test4" LOC="P91" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 4 ;
# This output is only used in the lx9_dave builds
# so we connect it to an unused pin
NET "DOE_n" LOC="P99" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 4 ;
# NET "" LOC="P48" | IOSTANDARD = LVCMOS33 ; # connector pin E2
# NET "" LOC="P48" | IOSTANDARD = LVCMOS33 ; # connector pin E2
# NET "" LOC="P49" | IOSTANDARD = LVCMOS33 ; # connector pin E3
# NET "" LOC="P27" | IOSTANDARD = LVCMOS33 ; # connector pin E4
# NET "" LOC="P44" | IOSTANDARD = LVCMOS33 ; # connector pin E5
# NET "" LOC="P50" | IOSTANDARD = LVCMOS33 ; # connector pin E6
# NET "" LOC="P42" | IOSTANDARD = LVCMOS33 ; # connector pin E7
# NET "" LOC="P99" | IOSTANDARD = LVCMOS33 ; # connector pin E8

View File

@ -70,14 +70,14 @@ NET "test2" LOC="P66" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 4 ;
NET "test3" LOC="P12" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 4 ;
NET "test4" LOC="P91" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 4 ;
# This output is only used in the lx9_dave builds
# so we connect it to an unused pin
NET "DOE_n" LOC="P99" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 4 ;
# NET "" LOC="P48" | IOSTANDARD = LVCMOS33 ; # connector pin E2
# NET "" LOC="P48" | IOSTANDARD = LVCMOS33 ; # connector pin E2
# NET "" LOC="P49" | IOSTANDARD = LVCMOS33 ; # connector pin E3
# NET "" LOC="P27" | IOSTANDARD = LVCMOS33 ; # connector pin E4
# NET "" LOC="P44" | IOSTANDARD = LVCMOS33 ; # connector pin E5
# NET "" LOC="P50" | IOSTANDARD = LVCMOS33 ; # connector pin E6
# NET "" LOC="P42" | IOSTANDARD = LVCMOS33 ; # connector pin E7
# NET "" LOC="P99" | IOSTANDARD = LVCMOS33 ; # connector pin E8

View File

@ -74,3 +74,7 @@ NET "test2" LOC="P137" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 8 ; #
NET "test3" LOC="P133" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 8 ; # led4
NET "test4" LOC="P120" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 8 ; # led5
#NET "test5" LOC="P118" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 8 ; # led7
# This output is only used in the lx9_dave builds
# so we connect it to an unused pin
NET "DOE_n" LOC="P118" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 4 ;

View File

@ -74,3 +74,7 @@ NET "test2" LOC="P137" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 8 ; #
NET "test3" LOC="P133" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 8 ; # led4
NET "test4" LOC="P120" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 8 ; # led5
#NET "test5" LOC="P118" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 8 ; # led7
# This output is only used in the lx9_dave builds
# so we connect it to an unused pin
NET "DOE_n" LOC="P118" | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 4 ;