Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db9a4552e7 | ||
|
|
2cbb73cc5b | ||
|
|
efcba04379 | ||
|
|
4863dd122f | ||
|
|
7cf8db9aca |
295
CPLD/v1.2/sfd700.vhd
vendored
295
CPLD/v1.2/sfd700.vhd
vendored
@@ -12,7 +12,7 @@
|
||||
-- Copyright: (c) 2018-23 Philip Smart <philip.smart@net2net.org>
|
||||
--
|
||||
-- History: July 2023 - v1.0 - Initial write.
|
||||
|
||||
-- Apr 2026 - v1.1 - Synchroniser bug fixes.
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
-- 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
|
||||
@@ -112,8 +112,6 @@ architecture rtl of cpld128 is
|
||||
signal EXXX_WR_SELni : std_logic;
|
||||
signal FXXX_RD_SELni : std_logic;
|
||||
signal FXXX_WR_SELni : std_logic;
|
||||
signal RAMEN_RD_SELni : std_logic;
|
||||
signal RAMEN_WR_SELni : std_logic;
|
||||
signal MODE_RD_SELni : std_logic;
|
||||
signal ROMINHSET_WR_SELni : std_logic;
|
||||
signal ROMINHCLR_WR_SELni : std_logic;
|
||||
@@ -131,15 +129,44 @@ architecture rtl of cpld128 is
|
||||
signal REG_SIDE : std_logic;
|
||||
signal REG_DDEN : std_logic;
|
||||
signal REG_INT : std_logic;
|
||||
signal REG_EXXX_PAGE : std_logic_vector(6 downto 0);
|
||||
signal REG_FXXX_PAGE : std_logic_vector(6 downto 0);
|
||||
signal REG_EXXX_PAGE : std_logic_vector(7 downto 0); -- D7 = RAM enable, D6:0 = page address.
|
||||
signal REG_FXXX_PAGE : std_logic_vector(7 downto 0); -- D7 = RAM enable, D6:0 = page address.
|
||||
signal REG_ROMDIS : std_logic;
|
||||
signal REG_ROMINH : std_logic;
|
||||
signal REG_RAMEN : std_logic;
|
||||
|
||||
-- Selected mode of the interface, ie. which machine it will be plugged into.
|
||||
signal IFMODE : integer range 0 to 7 := 0;
|
||||
|
||||
-- Synchronised bus select signals (1-stage registered to eliminate glitches from combinational decode).
|
||||
-- The existing *_LAST variable in each process provides the second synchronisation stage,
|
||||
-- giving a full 2-stage pipeline for edge detection.
|
||||
signal SIDE_WR_SYNC : std_logic := '1';
|
||||
signal DDEN_WR_SYNC : std_logic := '1';
|
||||
signal DRIVE_WR_SYNC : std_logic := '1';
|
||||
signal INTEN_SYNC : std_logic := '1';
|
||||
signal EXXX_WR_SYNC : std_logic := '1';
|
||||
signal FXXX_WR_SYNC : std_logic := '1';
|
||||
signal ROMINHSET_WR_SYNC : std_logic := '1';
|
||||
signal ROMINHCLR_WR_SYNC : std_logic := '1';
|
||||
signal ROMDISSET_WR_SYNC : std_logic := '1';
|
||||
signal ROMDISCLR_WR_SYNC : std_logic := '1';
|
||||
|
||||
-- Registered Z80 data bus and RDn for clean capture in write processes.
|
||||
signal Z80_DATA_SYNC : std_logic_vector(7 downto 0) := (others => '0');
|
||||
signal Z80_RDn_SYNC : std_logic := '1';
|
||||
|
||||
-- Registered memory chip selects — eliminates combinational glitches from address bus
|
||||
-- skew propagating through MEM_EXXX/FXXX decode into ROM_SELni/RAM_SELni.
|
||||
-- One CLK_16M cycle (62.5ns) of added latency; the Z80 at 3.54MHz has ~200ns of read
|
||||
-- access window so this is well within margin for typical FlashROM/RAM (55-70ns).
|
||||
signal ROM_CSn_REG : std_logic := '1';
|
||||
signal RAM_CSn_REG : std_logic := '1';
|
||||
|
||||
-- Registered ID bus output for page address — prevents brief high-Z glitch on
|
||||
-- ID during address transitions from corrupting the ROM/RAM upper address.
|
||||
signal ID_REG : std_logic_vector(7 downto 0) := (others => '0');
|
||||
signal ID_OEn : std_logic := '1';
|
||||
|
||||
-- Clocks.
|
||||
signal CLK_8Mi : std_logic := '0';
|
||||
|
||||
@@ -162,53 +189,93 @@ begin
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- Process to register configuration mode on reset.
|
||||
SETMODE: process( Z80_RESETn, MODE )
|
||||
-- Synchronise asynchronous Z80 bus-derived select signals into the CLK_16M domain.
|
||||
-- This eliminates glitches caused by address/control bus skew on the combinational decodes.
|
||||
-- Each signal is registered once here; the *_LAST variable in each consumer process provides
|
||||
-- the second stage, giving a full 2-stage synchronisation pipeline for edge detection.
|
||||
SYNCBUS: process( Z80_RESETn, CLK_16M )
|
||||
begin
|
||||
if(Z80_RESETn = '0') then
|
||||
-- Configuration jumpers specify the machine the SFD700 card will be used with.
|
||||
IFMODE <= to_integer(unsigned(MODE));
|
||||
SIDE_WR_SYNC <= '1';
|
||||
DDEN_WR_SYNC <= '1';
|
||||
DRIVE_WR_SYNC <= '1';
|
||||
INTEN_SYNC <= '1';
|
||||
EXXX_WR_SYNC <= '1';
|
||||
FXXX_WR_SYNC <= '1';
|
||||
ROMINHSET_WR_SYNC <= '1';
|
||||
ROMINHCLR_WR_SYNC <= '1';
|
||||
ROMDISSET_WR_SYNC <= '1';
|
||||
ROMDISCLR_WR_SYNC <= '1';
|
||||
Z80_DATA_SYNC <= (others => '0');
|
||||
Z80_RDn_SYNC <= '1';
|
||||
|
||||
elsif(rising_edge(CLK_16M)) then
|
||||
SIDE_WR_SYNC <= SIDE_WR_SELni;
|
||||
DDEN_WR_SYNC <= DDEN_WR_SELni;
|
||||
DRIVE_WR_SYNC <= DRIVE_WR_SELni;
|
||||
INTEN_SYNC <= INTEN_SELni;
|
||||
EXXX_WR_SYNC <= EXXX_WR_SELni;
|
||||
FXXX_WR_SYNC <= FXXX_WR_SELni;
|
||||
ROMINHSET_WR_SYNC <= ROMINHSET_WR_SELni;
|
||||
ROMINHCLR_WR_SYNC <= ROMINHCLR_WR_SELni;
|
||||
ROMDISSET_WR_SYNC <= ROMDISSET_WR_SELni;
|
||||
ROMDISCLR_WR_SYNC <= ROMDISCLR_WR_SELni;
|
||||
Z80_DATA_SYNC <= Z80_DATA;
|
||||
Z80_RDn_SYNC <= Z80_RDn;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- Process to register configuration mode on reset.
|
||||
-- Synchronous reset captures MODE jumpers cleanly on CLK_16M; once reset deasserts the
|
||||
-- register holds its value. Avoids latch inference from the original combinational process.
|
||||
SETMODE: process( CLK_16M )
|
||||
begin
|
||||
if(rising_edge(CLK_16M)) then
|
||||
if(Z80_RESETn = '0') then
|
||||
-- Configuration jumpers specify the machine the SFD700 card will be used with.
|
||||
IFMODE <= to_integer(unsigned(MODE));
|
||||
end if;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- Set Floppy Disk Side.
|
||||
-- A write to 0xDD bit D0 = low sets Floppy Disk side 1, high sets side 0.
|
||||
SETSIDE: process( Z80_RESETn, CLK_16M, SIDE_WR_SELni )
|
||||
-- A write to 0xDD: D0 = 0 selects side 0, D0 = 1 selects side 1 (active-high on SIDE1 output).
|
||||
SETSIDE: process( Z80_RESETn, CLK_16M )
|
||||
variable SIDE_SEL_LASTni : std_logic;
|
||||
begin
|
||||
if(Z80_RESETn = '0') then
|
||||
REG_SIDE <= '0';
|
||||
SIDE_SEL_LASTni := '0';
|
||||
SIDE_SEL_LASTni := '1';
|
||||
|
||||
elsif(rising_edge(CLK_16M)) then
|
||||
if(SIDE_WR_SELni = '0' and SIDE_SEL_LASTni = '1') then
|
||||
REG_SIDE <= not Z80_DATA(0);
|
||||
if(SIDE_WR_SYNC = '0' and SIDE_SEL_LASTni = '1') then
|
||||
REG_SIDE <= not Z80_DATA_SYNC(0);
|
||||
end if;
|
||||
SIDE_SEL_LASTni := SIDE_WR_SELni;
|
||||
SIDE_SEL_LASTni := SIDE_WR_SYNC;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- Set Double Data Rate Enable.
|
||||
-- A write to 0xDE bit D0 = low enables double data rate, high enables single data rate.
|
||||
SETDDEN: process( Z80_RESETn, CLK_16M, DDEN_WR_SELni )
|
||||
SETDDEN: process( Z80_RESETn, CLK_16M )
|
||||
variable DDEN_SEL_LASTni : std_logic;
|
||||
begin
|
||||
if(Z80_RESETn = '0') then
|
||||
REG_DDEN <= '1';
|
||||
DDEN_SEL_LASTni := '0';
|
||||
DDEN_SEL_LASTni := '1';
|
||||
|
||||
elsif(rising_edge(CLK_16M)) then
|
||||
if(DDEN_WR_SELni = '0' and DDEN_SEL_LASTni = '1') then
|
||||
REG_DDEN <= not Z80_DATA(0);
|
||||
if(DDEN_WR_SYNC = '0' and DDEN_SEL_LASTni = '1') then
|
||||
REG_DDEN <= not Z80_DATA_SYNC(0);
|
||||
end if;
|
||||
DDEN_SEL_LASTni := DDEN_WR_SELni;
|
||||
DDEN_SEL_LASTni := DDEN_WR_SYNC;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- Set Drive Number and Motor
|
||||
-- A write to 0xDC, bits D2:0 sets the active drive. Bit 2 high enables the active drive, low disables all drives.
|
||||
-- Bit D7 enables the motor.
|
||||
SETDRIVE: process( Z80_RESETn, CLK_16M, DRIVE_WR_SELni )
|
||||
SETDRIVE: process( Z80_RESETn, CLK_16M )
|
||||
variable DRIVE_SEL_LASTni: std_logic;
|
||||
begin
|
||||
if(Z80_RESETn = '0') then
|
||||
@@ -217,16 +284,16 @@ begin
|
||||
REG_DRIVEC <= '0';
|
||||
REG_DRIVED <= '0';
|
||||
REG_MOTOR <= '0';
|
||||
DRIVE_SEL_LASTni := '0';
|
||||
DRIVE_SEL_LASTni := '1';
|
||||
|
||||
elsif(rising_edge(CLK_16M)) then
|
||||
if(DRIVE_WR_SELni = '0' and DRIVE_SEL_LASTni = '1') then
|
||||
if(DRIVE_WR_SYNC = '0' and DRIVE_SEL_LASTni = '1') then
|
||||
REG_DRIVEA <= '0';
|
||||
REG_DRIVEB <= '0';
|
||||
REG_DRIVEC <= '0';
|
||||
REG_DRIVED <= '0';
|
||||
REG_MOTOR <= Z80_DATA(7);
|
||||
case(to_integer(unsigned(Z80_DATA(2 downto 0)))) is
|
||||
REG_MOTOR <= Z80_DATA_SYNC(7);
|
||||
case(to_integer(unsigned(Z80_DATA_SYNC(2 downto 0)))) is
|
||||
when 0 =>
|
||||
|
||||
when 4 =>
|
||||
@@ -241,86 +308,77 @@ begin
|
||||
when others =>
|
||||
end case;
|
||||
end if;
|
||||
DRIVE_SEL_LASTni := DRIVE_WR_SELni;
|
||||
DRIVE_SEL_LASTni := DRIVE_WR_SYNC;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- Set Interrupt Enable
|
||||
-- A write to 0xDF enables WD1773 interrupts, a read from 0xDF disables WD1773 interrupts.
|
||||
SETINT: process( Z80_RESETn, CLK_16M, INTEN_SELni )
|
||||
SETINT: process( Z80_RESETn, CLK_16M )
|
||||
variable INTEN_SEL_LASTni: std_logic;
|
||||
begin
|
||||
if(Z80_RESETn = '0') then
|
||||
REG_INT <= '0';
|
||||
INTEN_SEL_LASTni := '0';
|
||||
INTEN_SEL_LASTni := '1';
|
||||
|
||||
elsif(rising_edge(CLK_16M)) then
|
||||
if(INTEN_SELni = '0' and INTEN_SEL_LASTni = '1') then
|
||||
REG_INT <= Z80_RDn;
|
||||
if(INTEN_SYNC = '0' and INTEN_SEL_LASTni = '1') then
|
||||
REG_INT <= Z80_RDn_SYNC;
|
||||
end if;
|
||||
INTEN_SEL_LASTni := INTEN_SELni;
|
||||
INTEN_SEL_LASTni := INTEN_SYNC;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- Set FlashROM/RAM EXXX (E300:EFFF) page address. Each bank is 4K but due to the memory mapped I/O, only
|
||||
-- E300:EFFF is useable in RFS.
|
||||
-- A write t0 0x60 copies D6:0 into the EXXX page address register which selects a required 4K page in region E300:EFFF
|
||||
SETEXXXPAGE: process( Z80_RESETn, CLK_16M, IFMODE, EXXX_WR_SELni )
|
||||
-- Set FlashROM/RAM EXXX (E300:EFFF) page address and ROM/RAM select. Each bank is 4K but due to the
|
||||
-- memory mapped I/O, only E300:EFFF is useable in RFS.
|
||||
-- A write to 0x60: D7 = 1 selects RAM, D7 = 0 selects FlashROM; D6:0 = 4K page address.
|
||||
SETEXXXPAGE: process( Z80_RESETn, CLK_16M )
|
||||
variable EXXX_SEL_LASTni : std_logic;
|
||||
begin
|
||||
if(Z80_RESETn = '0') then
|
||||
-- Customised UROM containing RFS start bank.
|
||||
REG_EXXX_PAGE <= "0000010";
|
||||
EXXX_SEL_LASTni := '0';
|
||||
|
||||
-- Customised UROM containing RFS start bank (page 2, ROM).
|
||||
REG_EXXX_PAGE <= "00000010";
|
||||
EXXX_SEL_LASTni := '1';
|
||||
|
||||
elsif(rising_edge(CLK_16M)) then
|
||||
if(EXXX_WR_SELni = '0' and EXXX_SEL_LASTni = '1') then
|
||||
REG_EXXX_PAGE <= Z80_DATA(6 downto 0);
|
||||
if(EXXX_WR_SYNC = '0' and EXXX_SEL_LASTni = '1') then
|
||||
REG_EXXX_PAGE <= Z80_DATA_SYNC;
|
||||
end if;
|
||||
EXXX_SEL_LASTni := EXXX_WR_SELni;
|
||||
EXXX_SEL_LASTni := EXXX_WR_SYNC;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- Set FlashROM/RAM FXXX (F000:FFFF) page address.
|
||||
-- A write to 0x61 copies D6:0 into the FXXX page address register which selects a 4K page
|
||||
-- window of ROM/RAM accessible in the FXXX window.
|
||||
SETFXXXPAGE: process( Z80_RESETn, CLK_16M, IFMODE, FXXX_WR_SELni )
|
||||
-- Set FlashROM/RAM FXXX (F000:FFFF) page address and ROM/RAM select.
|
||||
-- A write to 0x61: D7 = 1 selects RAM, D7 = 0 selects FlashROM; D6:0 = 4K page address.
|
||||
SETFXXXPAGE: process( Z80_RESETn, CLK_16M )
|
||||
variable FXXX_SEL_LASTni : std_logic;
|
||||
variable RESET_DONE : std_logic;
|
||||
begin
|
||||
if(Z80_RESETn = '0') then
|
||||
-- Initial page is set to 0, which is the MZ-80A AFI ROM. The original ROM is 2K so potential to add additional code in the upper block.
|
||||
-- Initial page is set to 0 (ROM), which is the MZ-80A AFI ROM.
|
||||
REG_FXXX_PAGE <= (others => '0');
|
||||
|
||||
-- MZ-700, starting at the second 4k page, set banking registers to 4K block which stores the MZ-700 AFI ROM.
|
||||
if(IFMODE = MODE_MZ700) then
|
||||
REG_FXXX_PAGE(1 downto 0) <= "01";
|
||||
end if;
|
||||
FXXX_SEL_LASTni := '1';
|
||||
RESET_DONE := '0';
|
||||
|
||||
elsif(rising_edge(CLK_16M)) then
|
||||
|
||||
-- Set 4k F000:FFFF page address.
|
||||
if(FXXX_WR_SELni = '0' and FXXX_SEL_LASTni = '1') then
|
||||
REG_FXXX_PAGE <= Z80_DATA(6 downto 0);
|
||||
-- On the first clock after reset deasserts, apply mode-specific default.
|
||||
-- IFMODE is a synchronous register captured during reset, so it is stable here.
|
||||
if(RESET_DONE = '0') then
|
||||
RESET_DONE := '1';
|
||||
-- MZ-700, starting at the second 4K page (ROM), set banking register to the block which stores the MZ-700 AFI ROM.
|
||||
if(IFMODE = MODE_MZ700) then
|
||||
REG_FXXX_PAGE(1 downto 0) <= "01";
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- Set 4k F000:FFFF page address and ROM/RAM select.
|
||||
if(FXXX_WR_SYNC = '0' and FXXX_SEL_LASTni = '1') then
|
||||
REG_FXXX_PAGE <= Z80_DATA_SYNC;
|
||||
end if;
|
||||
|
||||
-- Edge detection.
|
||||
FXXX_SEL_LASTni := FXXX_WR_SELni;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- Enable FlashROM or RAM.
|
||||
-- A write to 0x62 with D0 = low enables FlashROM, D0 = high enables RAM.
|
||||
SETRAMEN: process( Z80_RESETn, CLK_16M, RAMEN_WR_SELni )
|
||||
variable RAMEN_SEL_LASTni: std_logic;
|
||||
begin
|
||||
if(Z80_RESETn = '0') then
|
||||
REG_RAMEN <= '0';
|
||||
|
||||
elsif(rising_edge(CLK_16M)) then
|
||||
if(RAMEN_WR_SELni = '0' and RAMEN_SEL_LASTni = '1') then
|
||||
REG_RAMEN <= Z80_DATA(0);
|
||||
end if;
|
||||
RAMEN_SEL_LASTni := RAMEN_WR_SELni;
|
||||
FXXX_SEL_LASTni := FXXX_WR_SYNC;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
@@ -339,7 +397,7 @@ begin
|
||||
--
|
||||
-- A write to 0xE1 enables RAM in the region 0xD000:FFFF so the onboard FlashROM/RAM should be disabled. A write to 0xE3/0xE4 re-enables
|
||||
-- FlashROM/RAM. A write to 0xE5 inhibits all access to region 0xD000:FFFF so the onboard FlashROM/RAM is disabled, write to 0xE6 re-enables it.
|
||||
SETHIMEM: process( Z80_RESETn, CLK_16M, ROMINHSET_WR_SELni, ROMINHCLR_WR_SELni, ROMDISSET_WR_SELni, ROMDISCLR_WR_SELni )
|
||||
SETHIMEM: process( Z80_RESETn, CLK_16M )
|
||||
variable ROMINHSET_LAST : std_logic;
|
||||
variable ROMINHCLR_LAST : std_logic;
|
||||
variable ROMDISSET_LAST : std_logic;
|
||||
@@ -348,28 +406,28 @@ begin
|
||||
if(Z80_RESETn = '0') then
|
||||
REG_ROMINH <= '0';
|
||||
REG_ROMDIS <= '0';
|
||||
ROMINHSET_LAST := '0';
|
||||
ROMINHCLR_LAST := '0';
|
||||
ROMDISSET_LAST := '0';
|
||||
ROMDISCLR_LAST := '0';
|
||||
ROMINHSET_LAST := '1';
|
||||
ROMINHCLR_LAST := '1';
|
||||
ROMDISSET_LAST := '1';
|
||||
ROMDISCLR_LAST := '1';
|
||||
|
||||
elsif(rising_edge(CLK_16M)) then
|
||||
if(ROMINHSET_WR_SELni = '0' and ROMINHSET_LAST = '1') then
|
||||
if(ROMINHSET_WR_SYNC = '0' and ROMINHSET_LAST = '1') then
|
||||
REG_ROMINH <= '1';
|
||||
end if;
|
||||
if(ROMINHCLR_WR_SELni = '0' and ROMINHCLR_LAST = '1') then
|
||||
if(ROMINHCLR_WR_SYNC = '0' and ROMINHCLR_LAST = '1') then
|
||||
REG_ROMINH <= '0';
|
||||
end if;
|
||||
if(ROMDISSET_WR_SELni = '0' and ROMDISSET_LAST = '1') then
|
||||
if(ROMDISSET_WR_SYNC = '0' and ROMDISSET_LAST = '1') then
|
||||
REG_ROMDIS <= '1';
|
||||
end if;
|
||||
if(ROMDISCLR_WR_SELni = '0' and ROMDISCLR_LAST = '1') then
|
||||
if(ROMDISCLR_WR_SYNC = '0' and ROMDISCLR_LAST = '1') then
|
||||
REG_ROMDIS <= '0';
|
||||
end if;
|
||||
ROMINHSET_LAST := ROMINHSET_WR_SELni;
|
||||
ROMINHCLR_LAST := ROMINHCLR_WR_SELni;
|
||||
ROMDISSET_LAST := ROMDISSET_WR_SELni;
|
||||
ROMDISCLR_LAST := ROMDISCLR_WR_SELni;
|
||||
ROMINHSET_LAST := ROMINHSET_WR_SYNC;
|
||||
ROMINHCLR_LAST := ROMINHCLR_WR_SYNC;
|
||||
ROMDISSET_LAST := ROMDISSET_WR_SYNC;
|
||||
ROMDISCLR_LAST := ROMDISCLR_WR_SYNC;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
@@ -378,13 +436,19 @@ begin
|
||||
else '1';
|
||||
MEM_FXXX_SELni <= '0' when Z80_MREQn = '0' and unsigned(Z80_ADDR(15 downto 8)) >= X"F0" and unsigned(Z80_ADDR(15 downto 8)) <= X"FF"
|
||||
else '1';
|
||||
ROM_SELni <= '0' when (IFMODE = MODE_MZ700 or IFMODE = MODE_MZ1500) and REG_ROMINH = '0' and REG_ROMDIS = '0' and REG_RAMEN = '0' and (MEM_EXXX_SELni = '0' or MEM_FXXX_SELni = '0')
|
||||
-- ROM/RAM select is now per-region: bit 7 of REG_EXXX_PAGE/REG_FXXX_PAGE determines whether that
|
||||
-- region maps to FlashROM (bit 7 = 0) or RAM (bit 7 = 1). Each region is independent.
|
||||
ROM_SELni <= '0' when (IFMODE = MODE_MZ700 or IFMODE = MODE_MZ1500) and REG_ROMINH = '0' and REG_ROMDIS = '0'
|
||||
and ((MEM_EXXX_SELni = '0' and REG_EXXX_PAGE(7) = '0') or (MEM_FXXX_SELni = '0' and REG_FXXX_PAGE(7) = '0'))
|
||||
else
|
||||
'0' when (IFMODE = MODE_MZ1200 or IFMODE = MODE_MZ80A) and REG_RAMEN = '0' and (MEM_EXXX_SELni = '0' or MEM_FXXX_SELni = '0')
|
||||
'0' when (IFMODE = MODE_MZ1200 or IFMODE = MODE_MZ80A)
|
||||
and ((MEM_EXXX_SELni = '0' and REG_EXXX_PAGE(7) = '0') or (MEM_FXXX_SELni = '0' and REG_FXXX_PAGE(7) = '0'))
|
||||
else '1';
|
||||
RAM_SELni <= '0' when (IFMODE = MODE_MZ700 or IFMODE = MODE_MZ1500) and REG_ROMINH = '0' and REG_ROMDIS = '0' and REG_RAMEN = '1' and (MEM_EXXX_SELni = '0' or MEM_FXXX_SELni = '0')
|
||||
RAM_SELni <= '0' when (IFMODE = MODE_MZ700 or IFMODE = MODE_MZ1500) and REG_ROMINH = '0' and REG_ROMDIS = '0'
|
||||
and ((MEM_EXXX_SELni = '0' and REG_EXXX_PAGE(7) = '1') or (MEM_FXXX_SELni = '0' and REG_FXXX_PAGE(7) = '1'))
|
||||
else
|
||||
'0' when (IFMODE = MODE_MZ1200 or IFMODE = MODE_MZ80A) and REG_RAMEN = '1' and (MEM_EXXX_SELni = '0' or MEM_FXXX_SELni = '0')
|
||||
'0' when (IFMODE = MODE_MZ1200 or IFMODE = MODE_MZ80A)
|
||||
and ((MEM_EXXX_SELni = '0' and REG_EXXX_PAGE(7) = '1') or (MEM_FXXX_SELni = '0' and REG_FXXX_PAGE(7) = '1'))
|
||||
else '1';
|
||||
|
||||
-- I/O Port select.
|
||||
@@ -406,10 +470,6 @@ begin
|
||||
else '1';
|
||||
FXXX_WR_SELni <= '0' when Z80_IORQn = '0' and Z80_WRn = '0' and unsigned(Z80_ADDR(7 downto 0)) = X"61"
|
||||
else '1';
|
||||
RAMEN_RD_SELni <= '0' when Z80_IORQn = '0' and Z80_RDn = '0' and unsigned(Z80_ADDR(7 downto 0)) = X"62"
|
||||
else '1';
|
||||
RAMEN_WR_SELni <= '0' when Z80_IORQn = '0' and Z80_WRn = '0' and unsigned(Z80_ADDR(7 downto 0)) = X"62"
|
||||
else '1';
|
||||
MODE_RD_SELni <= '0' when Z80_IORQn = '0' and Z80_RDn = '0' and unsigned(Z80_ADDR(7 downto 0)) = X"63"
|
||||
else '1';
|
||||
ROMINHSET_WR_SELni <= '0' when Z80_IORQn = '0' and Z80_WRn = '0' and unsigned(Z80_ADDR(7 downto 0)) = X"E5"
|
||||
@@ -421,27 +481,54 @@ begin
|
||||
ROMDISCLR_WR_SELni <= '0' when Z80_IORQn = '0' and Z80_WRn = '0' and (unsigned(Z80_ADDR(7 downto 0)) = X"E3" or unsigned(Z80_ADDR(7 downto 0)) = X"E4")
|
||||
else '1';
|
||||
|
||||
-- Register memory chip selects and ID bus page address on CLK_16M to prevent
|
||||
-- combinational glitches during Z80 address transitions from reaching the FlashROM/RAM.
|
||||
-- The FDC data path on ID remains combinational (WD1773 needs immediate response).
|
||||
MEMCS: process( Z80_RESETn, CLK_16M )
|
||||
begin
|
||||
if(Z80_RESETn = '0') then
|
||||
ROM_CSn_REG <= '1';
|
||||
RAM_CSn_REG <= '1';
|
||||
ID_REG <= (others => '0');
|
||||
ID_OEn <= '1';
|
||||
|
||||
elsif(rising_edge(CLK_16M)) then
|
||||
ROM_CSn_REG <= ROM_SELni;
|
||||
RAM_CSn_REG <= RAM_SELni;
|
||||
|
||||
-- Register the page address (D6:0) and output enable for non-FDC memory accesses.
|
||||
-- Bit 7 is the ROM/RAM select and is not part of the address.
|
||||
if(MEM_EXXX_SELni = '0') then
|
||||
ID_REG <= REG_EXXX_PAGE(6 downto 0) & Z80_ADDR(11);
|
||||
ID_OEn <= '0';
|
||||
elsif(MEM_FXXX_SELni = '0') then
|
||||
ID_REG <= REG_FXXX_PAGE(6 downto 0) & Z80_ADDR(11);
|
||||
ID_OEn <= '0';
|
||||
else
|
||||
ID_OEn <= '1';
|
||||
end if;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- WD1773 master clock.
|
||||
CLK_FDC <= CLK_8Mi;
|
||||
|
||||
-- Data bus control.
|
||||
Z80_DATA <= not ID when FDC_SELni = '0' and Z80_WRn = '1' -- WD1773
|
||||
else
|
||||
'0' & REG_EXXX_PAGE when EXXX_RD_SELni = '0' -- EXXX Register contents.
|
||||
REG_EXXX_PAGE when EXXX_RD_SELni = '0' -- EXXX Register: D7=RAM, D6:0=page.
|
||||
else
|
||||
'0' & REG_FXXX_PAGE when FXXX_RD_SELni = '0' -- FXXX Register contents.
|
||||
else
|
||||
"0000000" & REG_RAMEN when RAMEN_RD_SELni = '0' -- ROM/RAM Enable register contents.
|
||||
REG_FXXX_PAGE when FXXX_RD_SELni = '0' -- FXXX Register: D7=RAM, D6:0=page.
|
||||
else
|
||||
"00000" & MODE when MODE_RD_SELni = '0' -- Configured mode.
|
||||
else
|
||||
(others => 'Z');
|
||||
-- ID is the inverted data bus, the WD1773 uses inverted data. ID doubles up as the FlashROM/RAM upper address bits when not being used for the WD1773.
|
||||
-- FDC data path remains combinational (WD1773 CSn is also combinational and needs immediate data).
|
||||
-- Memory page address uses the registered path to prevent glitches during address transitions.
|
||||
ID <= not Z80_DATA when Z80_WRn = '0' and FDC_SELni = '0'
|
||||
else
|
||||
REG_EXXX_PAGE & Z80_ADDR(11) when MEM_EXXX_SELni = '0'
|
||||
else
|
||||
REG_FXXX_PAGE & Z80_ADDR(11) when MEM_FXXX_SELni = '0'
|
||||
ID_REG when ID_OEn = '0'
|
||||
else
|
||||
(others => 'Z');
|
||||
|
||||
@@ -456,9 +543,9 @@ begin
|
||||
else Z80_ADDR(10);
|
||||
RAM_A10 <= Z80_ADDR(10);
|
||||
|
||||
-- FlashROM/RAM Chip Select.
|
||||
ROM_CSn <= ROM_SELni;
|
||||
RAM_CSn <= RAM_SELni;
|
||||
-- FlashROM/RAM Chip Select (registered to prevent combinational glitches).
|
||||
ROM_CSn <= ROM_CSn_REG;
|
||||
RAM_CSn <= RAM_CSn_REG;
|
||||
|
||||
-- Floppy Disk Interface Signals.
|
||||
FDCn <= FDC_SELni;
|
||||
|
||||
9
CPLD/v1.2/sfd700_pkg.vhd
vendored
9
CPLD/v1.2/sfd700_pkg.vhd
vendored
@@ -12,6 +12,7 @@
|
||||
-- Copyright: (c) 2018-23 Philip Smart <philip.smart@net2net.org>
|
||||
--
|
||||
-- History: July 2023 - Initial write.
|
||||
-- Apr 2026 - Synchroniser bug fixes.
|
||||
--
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
-- This source file is free software: you can redistribute it and-or modify
|
||||
@@ -112,7 +113,6 @@ package body sfd700_pkg is
|
||||
else
|
||||
return b;
|
||||
end if;
|
||||
return a;
|
||||
end function IntMax;
|
||||
|
||||
-- Find the number of bits required to represent an integer.
|
||||
@@ -132,16 +132,17 @@ package body sfd700_pkg is
|
||||
end function;
|
||||
|
||||
-- Function to calculate the number of whole 'clock' cycles in a given time period, the period being in ns.
|
||||
-- Returns CEIL(period * clock / 1e9), i.e. the minimum number of clock ticks that span the period.
|
||||
function clockTicks(period : in integer; clock : in integer) return integer is
|
||||
variable ticks : real;
|
||||
variable fracTicks : real;
|
||||
begin
|
||||
ticks := (Real(period) * Real(clock)) / 1000000000.0;
|
||||
fracTicks := ticks - CEIL(ticks);
|
||||
fracTicks := ticks - FLOOR(ticks);
|
||||
if fracTicks > 0.0001 then
|
||||
return Integer(CEIL(ticks + 1.0));
|
||||
return Integer(FLOOR(ticks)) + 1;
|
||||
else
|
||||
return Integer(CEIL(ticks));
|
||||
return Integer(FLOOR(ticks));
|
||||
end if;
|
||||
end function;
|
||||
|
||||
|
||||
2
README.md
vendored
2
README.md
vendored
@@ -6,7 +6,7 @@
|
||||
|
||||
<div style="text-align: justify">
|
||||
|
||||
<font style="color: green;" size="4"> <b>Update February 2024</b> - The v1.2 hardware is fully functional and has been tested on my MZ-80A, MZ-700 and MZ-1500 using the EXT700 adapter and in the MZ-2000 expansion unit. Issues have been reported using the MZ-1U06 which needs to be investigated, seems a CPLD timing issue. The CPLD logic (VHDL) is complete and all ROM/RAM paging and host-mode logic works correctly. The Rom Filing System software ported to the SFD-700 is functional for basic FDC operations (floppy boot via 'F' command) but does not yet include the full suite of floppy disk monitor tools; this is under ongoing development. Client workload has slowed progress — please check the <a href="https://git.eaw.app/eaw/SFD700">Gitea repository</a> for the latest state. The board hasnt been tested in other MZ series machines, hopefully I will do this in due course.</font>
|
||||
<font style="color: green;" size="4"> <b>Update April 2026</b> - A CPLD synchronisation bug was removed and the logic hardened making it more stable. It is advised to put a wire between one of the boards mounting holes and the MZ700 metal chassis as the grounding via the expansion port and joystick 5v pickup is insufficient. The v1.2 hardware is fully functional and has been tested on my MZ-80A, MZ-700 and MZ-1500 using the EXT700 adapter and in the MZ-2000 expansion unit. The CPLD logic (VHDL) is complete and all ROM/RAM paging and host-mode logic works correctly. The Rom Filing System software ported to the SFD-700 is functional for FDC operations (floppy boot via 'FL' command, directory via 'FD' command and a FC command to save memory to a floppy file). The floppy disk code for an MZ700 and MZ80A has been built into the RFS firmware and auto selects based on the jumper configuration. The board hasnt been tested in other MZ series machines, hopefully I will do this in due course.</font>
|
||||
|
||||
<div style="padding-top: 0.8em;"></div>
|
||||
One of the most needed accessories for the Sharp MZ series is the venerable Floppy Disk Controller. These are in relative short supply as not many people used them, mainly due to cost during the Sharp MZ era.
|
||||
|
||||
Reference in New Issue
Block a user