Files
SharpMZ_MiSTer/rtl/mz80c/cmt.vhd
birdybro b32c9615e4 Restructure folders
Proposal to restructure all the files and folders to keep things organized and similar to other cores.
2022-06-24 14:04:27 -06:00

1402 lines
68 KiB
VHDL

---------------------------------------------------------------------------------------------------------
--
-- Name: cmt.vhd
-- Created: July 2018
-- Author(s): Philip Smart
-- Description: Sharp MZ series PWM Tape Interface.
-- This module fully emulates the Sharp PWM tape interface. It uses cache ram
-- to simulate the tape. Data is played out to the Sharp or read from the Sharp
-- and stored in the ram.
-- For reading of data from Tape to the Sharp, the HPS or other controller loads a
-- complete tape into ram and should the Play/Auto function be enabled, playback
-- starts immediately.
-- For writing of data from the Sharp to Taoe, the data is stored in ram and the
-- HPS or other controller, when it gets the completed signal, can read out the ram
-- and store the data onto a local filesystem.
-- Credits:
-- Copyright: (c) 2018 Philip Smart <philip.smart@net2net.org>
--
-- History: July 2018 - Initial module written and playback mode tested and debugged.
-- August 2018 - Record mode written but not yet debugged/completed.
--
---------------------------------------------------------------------------------------------------------
-- 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;
library pkgs;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use pkgs.clkgen_pkg.all;
use pkgs.mctrl_pkg.all;
use ieee.numeric_std.all;
entity cmt is
Port (
RST : in std_logic;
-- Clock signals needed by this module.
CLKBUS : in std_logic_vector(CLKBUS_WIDTH);
-- Different operations modes.
CONFIG : in std_logic_vector(CONFIG_WIDTH);
-- Cassette magnetic tape signals.
CMTBUS : out std_logic_vector(CMTBUS_WIDTH);
CMT_READBIT : in std_logic;
CMT_MOTOR : in std_logic;
-- HPS Interface
IOCTL_DOWNLOAD : in std_logic; -- HPS Downloading to FPGA.
IOCTL_UPLOAD : in std_logic; -- HPS Uploading from FPGA.
IOCTL_CLK : in std_logic; -- HPS I/O Clock.
IOCTL_WR : in std_logic; -- HPS Write Enable to FPGA.
IOCTL_RD : in std_logic; -- HPS Read Enable from FPGA.
IOCTL_ADDR : in std_logic_vector(24 downto 0); -- HPS Address in FPGA to write into.
IOCTL_DOUT : in std_logic_vector(15 downto 0); -- HPS Data to be written into FPGA.
IOCTL_DIN : out std_logic_vector(15 downto 0); -- HPS Data to be read into HPS.
-- Debug Status Leds
DEBUG_STATUS_LEDS : out std_logic_vector(23 downto 0) -- 8 leds to display cmt internal status.
);
end cmt;
architecture RTL of cmt is
--
-- Components
--
component dpram
generic (
init_file : string;
widthad_a : natural;
width_a : natural;
widthad_b : natural;
width_b : natural;
outdata_reg_a : string := "UNREGISTERED";
outdata_reg_b : string := "UNREGISTERED"
);
Port (
clock_a : in std_logic := '1';
clocken_a : in std_logic := '1';
address_a : in std_logic_vector (widthad_a-1 downto 0);
data_a : in std_logic_vector (width_a-1 downto 0);
wren_a : in std_logic := '0';
q_a : out std_logic_vector (width_a-1 downto 0);
clock_b : in std_logic;
clocken_b : in std_logic := '1';
address_b : in std_logic_vector (widthad_b-1 downto 0);
data_b : in std_logic_vector (width_b-1 downto 0);
wren_b : in std_logic := '0';
q_b : out std_logic_vector (width_b-1 downto 0)
);
end component;
-- HPS Control signals.
signal IOCTL_CS_HDR_n : std_logic;
signal IOCTL_CS_DATA_n : std_logic;
signal IOCTL_TAPEHDR_WEN : std_logic;
signal IOCTL_TAPEDATA_WEN : std_logic;
signal IOCTL_DIN_HDR : std_logic_vector(7 downto 0);
signal IOCTL_DIN_DATA : std_logic_vector(7 downto 0);
--
signal CMTBUSi : std_logic_vector(CMTBUS_WIDTH);
signal BUTTONS_LAST : std_logic_vector(1 downto 0);
signal PLAY_READY_CNT : integer range 0 to 224000000 := 0; -- 2 second timer.
signal PLAY_READY : std_logic;
signal PLAY_READY_CLR : std_logic;
signal PLAY_READY_SEQ : std_logic_vector(1 downto 0); -- Setup and hold sequencer.
signal PLAY_BUTTON : std_logic;
signal PLAYING : std_logic_vector(2 downto 0);
signal RECORD_READY : std_logic;
signal RECORD_READY_SET : std_logic;
signal RECORD_READY_SEQ : std_logic_vector(1 downto 0); -- Setup and hold sequencer.
signal RECORD_BUTTON : std_logic;
signal RECORDING : std_logic_vector(2 downto 0);
signal MOTOR_CLK : std_logic_vector(1 downto 0);
signal MOTOR_SENSE : std_logic;
signal WRITEBIT : std_logic;
signal READBIT : std_logic;
signal TAPE_MOTOR_ON_n : std_logic;
-- Bit receiver signals.
signal RCV_BIT : std_logic; -- Received bit after PWM demodulation coming from MZ.
signal RCV_AVAIL : std_logic; -- Received bit is available and valid.
signal RCV_SEQ : std_logic_vector(2 downto 0); -- Setup and hold sequencer.
signal RCV_COUNT : integer range -3095 to 3095 := 0;
-- Bit transmitter signals.
signal XMIT_DONE : std_logic; -- Transmit of bit complete.
signal XMIT_SEQ : std_logic_vector(2 downto 0); -- Setup and hold sequencer.
signal XMIT_LOAD_1 : std_logic; -- Load bit and start transmission selector 1.
signal XMIT_LOAD_2 : std_logic; -- Load bit and start transmission selector 2.
signal XMIT_BIT_1 : std_logic; -- Transmit bit for XMIT_LOAD_2 to be PWM modulated and sent to MZ.
signal XMIT_BIT_2 : std_logic; -- Working bit for active XMIT_BIT.
signal XMIT_COUNT : integer range -7999 to 8000 := 0;
signal XMIT_LIMIT : integer range -7999 to 8000 := 0;
-- Bit padding transmitter signals.
signal XMIT_PADDING_LOAD : std_logic;
signal XMIT_PADDING_BIT : std_logic;
signal XMIT_PADDING_DONE : std_logic;
signal XMIT_PADDING_CNT1 : integer range 0 to 32767 := 0;
signal XMIT_PADDING_CNT2 : integer range 0 to 32767 := 0;
signal XMIT_PADDING_LEVEL1 : std_logic;
signal XMIT_PADDING_LEVEL2 : std_logic;
signal PADDING_SEQ : std_logic_vector(2 downto 0); -- Setup and hold sequencer.
signal PADDING_CNT1 : integer range 0 to 32767 := 0;
signal PADDING_CNT2 : integer range 0 to 32767 := 0;
signal PADDING_LEVEL1 : std_logic;
signal PADDING_LEVEL2 : std_logic;
-- Cache RAM header/data transmitter signals.
signal XMIT_RAM_LOAD : std_logic;
signal XMIT_RAM_DONE : std_logic;
signal XMIT_RAM_SEQ : std_logic_vector(2 downto 0); -- Setup and hold sequencer.
signal XMIT_RAM_ADDR : std_logic_vector(15 downto 0);
signal XMIT_RAM_COUNT : unsigned(15 downto 0);
signal XMIT_RAM_CHKSUM_CNT : unsigned(1 downto 0);
signal XMIT_RAM_CHECKSUM : std_logic_vector(15 downto 0);
signal XMIT_RAM_TYPE : std_logic;
signal XMIT_RAM_STATE : integer range 0 to 7 := 0;
signal XMIT_RAM_SR : std_logic_vector(8 downto 0);
signal XMIT_RAM_BITCNT : integer range 0 to 8 := 0;
signal XMIT_TAPE_SIZE : unsigned(15 downto 0);
-- RAM control signals.
signal RAM_ADDR : std_logic_vector(15 downto 0);
signal RAM_DATAIN : std_logic_vector(7 downto 0);
signal HDR_RAM_DATAOUT : std_logic_vector(7 downto 0);
signal HDR_RAM_WEN : std_logic;
signal DATA_RAM_DATAOUT : std_logic_vector(7 downto 0);
signal DATA_RAM_WEN : std_logic;
--
signal RCV_PADDING_LOAD : std_logic;
signal RCV_PADDING_DONE : std_logic;
signal RCV_PADDING_CNT : integer range 0 to 30000 := 0;
signal RCV_PADDING_LEVEL : std_logic;
signal RCV_PADDING_SEQ : std_logic_vector(2 downto 0); -- Setup and hold sequencer.
signal PADDING_CNT : integer range 0 to 30000 := 0;
signal PADDING_LEVEL : std_logic;
--
signal RCV_RAM_LOAD : std_logic;
signal RCV_RAM_DONE : std_logic;
signal RCV_RAM_SUCCESS : std_logic;
signal RCV_RAM_ADDR : std_logic_vector(15 downto 0);
signal RCV_RAM_STATE : integer range 0 to 10 := 0;
signal RCV_RAM_TYPE : std_logic;
signal RCV_RAM_RETRIES : std_logic;
signal RCV_RAM_COUNT : unsigned(15 downto 0);
signal RCV_RAM_CHECKSUM : std_logic_vector(15 downto 0);
signal RCV_RAM_CNT_CKSUM : std_logic;
signal RCV_RAM_CALC_CKSUM : std_logic_vector(15 downto 0);
signal RCV_RAM_SEQ : std_logic_vector(2 downto 0); -- Setup and hold sequencer.
signal RCV_RAM_SR : std_logic_vector(8 downto 0);
signal RCV_RAM_BITCNT : integer range 0 to 8 := 0;
signal RCV_TAPE_SIZE : unsigned(15 downto 0);
-- Main process Finite State Machine variables.
signal TAPE_READ_STATE : integer range 0 to 15 := 0;
signal TAPE_READ_SEQ : std_logic_vector(2 downto 0); -- Setup and hold sequencer.
signal TAPE_WRITE_STATE : integer range 0 to 15 := 0;
signal TAPE_WRITE_SEQ : std_logic_vector(2 downto 0); -- Setup and hold sequencer.
--
begin
-- Wired signals between this CMT unit and the MZ/MCtrl bus.
--
CMTBUSi(pkgs.mctrl_pkg.WRITEBIT) <= WRITEBIT; -- Write a bit to the MZ PIO.
CMTBUSi(pkgs.mctrl_pkg.READBIT) <= CMT_READBIT; -- Read a bit from the MZ PIO.
CMTBUSi(pkgs.mctrl_pkg.SENSE) <= MOTOR_SENSE; -- Indiate current state of Motor, 0 if not running, 1 if running.
CMTBUSi(pkgs.mctrl_pkg.ACTIVE) <= PLAYING(2) or RECORDING(2);
CMTBUSi(pkgs.mctrl_pkg.PLAY_READY) <= PLAY_READY;
CMTBUSi(pkgs.mctrl_pkg.PLAYING) <= PLAYING(2);
CMTBUSi(pkgs.mctrl_pkg.RECORD_READY) <= RECORD_READY;
CMTBUSi(pkgs.mctrl_pkg.RECORDING) <= RECORDING(1);
CMTBUSi(pkgs.mctrl_pkg.MOTOR) <= TAPE_MOTOR_ON_n;
--
READBIT <= CMT_READBIT;
MOTOR_CLK(1) <= CMT_MOTOR;
CMTBUS <= CMTBUSi;
-- Mux Signals from different sources.
RAM_ADDR <= RCV_RAM_ADDR when RECORDING(1) = '1' else XMIT_RAM_ADDR;
IOCTL_CS_HDR_n <= '0' when IOCTL_ADDR(24 downto 8) = "00000010100000000" else '1';
IOCTL_CS_DATA_n <= '0' when IOCTL_ADDR(24 downto 16) = "000000110" else '1';
IOCTL_TAPEHDR_WEN <= '1' when IOCTL_CS_HDR_n = '0' and IOCTL_WR = '1' else '0';
IOCTL_TAPEDATA_WEN <= '1' when IOCTL_CS_DATA_n = '0' and IOCTL_WR = '1' else '0';
IOCTL_DIN <= X"00" & IOCTL_DIN_HDR when IOCTL_CS_HDR_n = '0'
else
X"00" & IOCTL_DIN_DATA when IOCTL_CS_DATA_n = '0'
else
(others => '0');
TAPEHDR : dpram
GENERIC MAP (
init_file => null,
widthad_a => 7,
width_a => 8,
widthad_b => 7,
width_b => 8
)
PORT MAP (
clock_a => CLKBUS(CKMEM),
clocken_a => '1',
address_a => RAM_ADDR(6 downto 0),
data_a => RAM_DATAIN,
wren_a => HDR_RAM_WEN,
q_a => HDR_RAM_DATAOUT,
clock_b => IOCTL_CLK,
address_b => IOCTL_ADDR(6 downto 0),
data_b => IOCTL_DOUT(7 downto 0),
wren_b => IOCTL_TAPEHDR_WEN,
q_b => IOCTL_DIN_HDR
);
TAPEDATA : dpram
GENERIC MAP (
init_file => null,
widthad_a => 16,
width_a => 8,
widthad_b => 16,
width_b => 8
)
PORT MAP (
clock_a => CLKBUS(CKMEM),
clocken_a => '1',
address_a => RAM_ADDR,
data_a => RAM_DATAIN,
wren_a => DATA_RAM_WEN,
q_a => DATA_RAM_DATAOUT,
clock_b => IOCTL_CLK,
address_b => IOCTL_ADDR(15 downto 0),
data_b => IOCTL_DOUT(7 downto 0),
wren_b => IOCTL_TAPEDATA_WEN,
q_b => IOCTL_DIN_DATA
);
--------------------------------------------------------------------
-- TAPE HEADER FORMAT
--------------------------------------------------------------------
-- LGAP | LTM | L | HDR | CHKH | L | 256S | HDRC | CHKH | L
-- SGAP | STM | L | FILE | CHKF | L | 256S | FILEC | CHKF | L
-- LGAP is a long GAP
-- SGAP is a short GAP
-- LTM is a long tapemark - 40 long pulses then 40 short pulses
-- STM is a short tapemark - 20 long pulses then 20 short puses
-- HDR is the tapeheader
-- HDRC is a copy of the tapeheader
-- FILE is the file
-- FILEC is a copy of the file
-- CHKH is a 2 byte checksum of the tape header or its copy
-- CHKF is a 2 byte checksum of the file or its copy
-- L is 1 long pulse
-- 256S contains 256 short pulses
--------------------------------------------------------------------
-- Process to determine CMT state according to inputs. ie. If we are STOPPED, PLAYING or RECORDING.
-- This is determined by the input switches in CONFIG(BUTTOS), 00 = Off, 01 = Play, 02 = Record and 03 = Auto.
-- Auto mode indicates the CMT logic has to determine wether it is PLAYING or RECORDING. The default is PLAYING
-- but if a bit is received from the MZ then we switch to RECORDING until a full tape dump has been received.
--
process( RST, CLKBUS(CKCPU), CONFIG(BUTTONS), MOTOR_CLK ) begin
if RST='1' then
TAPE_MOTOR_ON_n <= '1';
MOTOR_SENSE <= '0';
PLAY_BUTTON <= '0';
RECORD_BUTTON <= '0';
BUTTONS_LAST <= "00";
PLAYING <= "000";
RECORDING <= "000";
MOTOR_CLK(0) <= '0';
elsif CLKBUS(CKCPU)'event and CLKBUS(CKCPU)='1' then
-- Store last state so we detect change.
BUTTONS_LAST <= CONFIG(BUTTONS);
MOTOR_CLK(0) <= MOTOR_CLK(1);
-- Store last state so we can detect a switch to recording or play mode.
RECORDING(1 downto 0) <= RECORDING(2 downto 1);
PLAYING(1 downto 0) <= PLAYING(2 downto 1);
-- Process the buttons and adapt signals accordingly.
--
if BUTTONS_LAST /= CONFIG(BUTTONS) then
case CONFIG(BUTTONS) is
when "00" => -- Off
PLAY_BUTTON <= '0';
RECORD_BUTTON <= '0';
TAPE_MOTOR_ON_n <= '1';
when "10" => -- Record
PLAY_BUTTON <= '0';
RECORD_BUTTON <= '1';
TAPE_MOTOR_ON_n <= '0';
when "01"|"11" => -- Play/Auto
-- Assume playback mode for Auto unless activity is detected from the MZ,
-- in which case switch to Recording.
PLAY_BUTTON <= '1';
RECORD_BUTTON <= '0';
TAPE_MOTOR_ON_n <= '0';
end case;
end if;
-- If in auto mode and data starts being received from the MZ, enter record mode.
--
if CONFIG(BUTTONS) = "11" and RCV_AVAIL = '1' then
PLAY_BUTTON <= '0';
RECORD_BUTTON <= '1';
TAPE_MOTOR_ON_n <= '0';
end if;
-- If the motor is running then setup the state according to the buttons pressed and the data availability.
--
if TAPE_MOTOR_ON_n = '0' and PLAY_BUTTON = '1' and PLAY_READY = '1' then
PLAYING(2) <= '1';
RECORDING(2) <= '0';
MOTOR_SENSE <= '1';
elsif TAPE_MOTOR_ON_n = '0' and RECORD_BUTTON = '1' then
PLAYING(2) <= '0';
RECORDING(2) <= '1';
MOTOR_SENSE <= '1';
elsif CONFIG(BUTTONS) /= "11" then
PLAYING(2) <= '0';
RECORDING(2) <= '0';
MOTOR_SENSE <= not TAPE_MOTOR_ON_n;
end if;
-- MZ motor on/off toggle. A high pulse on MOTOR_CLK will toggle the motor state.
--
if MOTOR_CLK = "10" then
TAPE_MOTOR_ON_n <= not TAPE_MOTOR_ON_n;
end if;
end if;
end process;
----------------------------------------------------------------------------------------------------------------------
-- Write To Tape logic.
--
-- This block concentrates all the logic required to receive data from the MZ into RAM (virtual tape).
-- Viewed from the CMT, it is the reception of data from computer onto tape.
----------------------------------------------------------------------------------------------------------------------
-- Trigger, when a write occurs to ram, start a counter. Each write resets the counter. After 1 second of
-- no further writes, then the ram data is ready to play.
-- Clear funtionality allows the logic to clear the ready signal to indicate data has been processed.
--
process( RST, IOCTL_CLK, IOCTL_TAPEHDR_WEN, IOCTL_TAPEDATA_WEN, IOCTL_CS_HDR_n, IOCTL_CS_DATA_n )
begin
if RST = '1' then
PLAY_READY <= '0';
PLAY_READY_CNT <= 0;
PLAY_READY_SEQ <= "00";
RECORD_READY <= '0';
RECORD_READY_SEQ <= "00";
elsif IOCTL_CLK'event and IOCTL_CLK = '1' then
-- Sample clear signal and hold. Shift right 2 bits, msb = latest value.
PLAY_READY_SEQ(0) <= PLAY_READY_SEQ(1);
PLAY_READY_SEQ(1) <= PLAY_READY_CLR;
-- Sample record complete signal and hold. Shift righ 2 bits, msb = latest value.
RECORD_READY_SEQ(0) <= RECORD_READY_SEQ(1);
RECORD_READY_SEQ(1) <= RECORD_READY_SET;
-- If the external clear is triggered, reset ready signal.
if PLAY_READY_SEQ = "10" then
PLAY_READY <= '0';
PLAY_READY_CNT <= 0;
-- Every write to ram resets the counter.
elsif IOCTL_TAPEHDR_WEN = '1' or IOCTL_TAPEDATA_WEN = '1' then
PLAY_READY <= '0';
PLAY_READY_CNT <= 1;
-- 1 second timer, if no new writes have occurred to RAM, then set the ready flag.
elsif PLAY_READY_CNT >= 112000000 then
PLAY_READY_CNT <= 0;
PLAY_READY <= '1';
end if;
-- Set RECORD_READY if fsm determines a full tape message received.
if RECORD_READY_SEQ = "10" then
RECORD_READY <= '1';
-- HPS access resets signal.
elsif IOCTL_CS_HDR_n = '0' or IOCTL_CS_DATA_n = '0' then
RECORD_READY <= '0';
-- If the fsm resets the signal then clear the flag as it will be receiving a new tape message.
elsif RECORD_READY_SEQ = "00" then
RECORD_READY <= '0';
end if;
-- Increment counters if enabled.
if PLAY_READY_CNT >= 1 then
PLAY_READY_CNT <= PLAY_READY_CNT + 1;
end if;
end if;
end process;
-- State machine to represent the tape drive WRITE mode (MZ -> RAM) using cache memory as the tape, which is read by the HPS after data is stored.
--
process( RST, CLKBUS(CKCPU), RECORDING ) begin
-- For reset, hold machine in reset.
if RST = '1' then
RECORD_READY_SET <= '0';
TAPE_WRITE_STATE <= 15;
TAPE_WRITE_SEQ <= "000";
RCV_PADDING_LOAD <= '0';
RCV_PADDING_LEVEL<= '0';
RCV_PADDING_CNT <= 0;
RCV_RAM_LOAD <= '0';
RCV_RAM_TYPE <= '0';
elsif CLKBUS(CKCPU)'event and CLKBUS(CKCPU) = '1' then
-- If recording is inactive, hold state variables in reset.
if RECORDING = "00" then
RECORD_READY_SET <= '0';
TAPE_WRITE_STATE <= 15;
TAPE_WRITE_SEQ <= "000";
RCV_PADDING_LOAD <= '0';
RCV_PADDING_LEVEL<= '0';
RCV_PADDING_CNT <= 0;
RCV_RAM_LOAD <= '0';
RCV_RAM_TYPE <= '0';
-- Change in recording state, start fsm to receive data from the MZ and store in the cache RAM ready for HPS upload.
elsif RECORDING = "10" then
if TAPE_WRITE_STATE = 15 then
TAPE_WRITE_STATE <= 0;
RCV_RAM_TYPE <= '0';
end if;
elsif RECORDING = "11" then
-- Sample the done signal, when setup and stable, we can continue.
TAPE_WRITE_SEQ(1 downto 0)<= TAPE_WRITE_SEQ(2 downto 1);
TAPE_WRITE_SEQ(2) <= (RCV_PADDING_LOAD or RCV_RAM_LOAD) and (RCV_PADDING_DONE and RCV_RAM_DONE);
-- If reception has just been started and acknowledged by the DONE flag being reset, reset the activation strobe.
--
if TAPE_WRITE_SEQ = "111" then
RCV_PADDING_LOAD <= '0';
RCV_RAM_LOAD <= '0';
end if;
-- If reception is in progress, run the FSM.
--
if RCV_PADDING_LOAD = '0' and RCV_RAM_LOAD = '0' then
-- Default is to move onto next state per clock cycle, unless modified by the state action.
TAPE_WRITE_STATE <= TAPE_WRITE_STATE + 1;
-- Execute current state.
case TAPE_WRITE_STATE is
-- As the Gap (Short/Long) is a continuous stream of '0' pulses, we can wait for 1000 '0' pulses then move onto the tapemark.
--
when 0 =>
RCV_PADDING_CNT <= 1000;
RCV_PADDING_LEVEL <= '0';
RCV_PADDING_LOAD <= '1'; -- Wait for 100 '1' pulses from MZ.
when 1 =>
if RCV_PADDING_DONE = '0' then
TAPE_WRITE_STATE <= 1;
end if;
-- Wait for Tapemark, 40 long/short pulses for Header, 20 long/short pulses for Data.
when 2 =>
-- Header = 0, Data = 1
if RCV_RAM_TYPE = '0' then
-- Setup to receive a Long Tape Mark.
RCV_PADDING_CNT <= 40;
RCV_PADDING_LEVEL<= '1';
else
-- Setup to receive a Short Tape Mark.
RCV_PADDING_CNT <= 20;
RCV_PADDING_LEVEL<= '1';
end if;
RCV_PADDING_LOAD <= '1';
when 3 =>
if RCV_PADDING_DONE = '0' then
TAPE_WRITE_STATE <= 3;
end if;
when 4 =>
-- Header = 0, Data = 1
if RCV_RAM_TYPE = '0' then
-- Setup to receive a Long Tape Mark.
RCV_PADDING_CNT <= 40;
RCV_PADDING_LEVEL<= '0';
else
-- Setup to receive a Short Tape Mark.
RCV_PADDING_CNT <= 20;
RCV_PADDING_LEVEL<= '0';
end if;
RCV_PADDING_LOAD <= '1';
when 5 =>
if RCV_PADDING_DONE = '0' then
TAPE_WRITE_STATE <= 5;
end if;
-- Wait for single long pulse.
when 6 =>
RCV_PADDING_LEVEL <= '1';
RCV_PADDING_CNT <= 1;
RCV_PADDING_LOAD <= '1';
when 7 =>
if RCV_PADDING_DONE = '0' then
TAPE_WRITE_STATE <= 7;
end if;
-- Now read in the header/data (MZ writing to RAM tape).
when 8 =>
RECORD_READY_SET <= '0';
RCV_RAM_RETRIES <= '1';
when 9 =>
RCV_RAM_LOAD <= '1';
when 10 =>
if RCV_RAM_DONE = '0' then
TAPE_WRITE_STATE <= 10;
end if;
when 11 =>
if RCV_RAM_SUCCESS = '0' then
-- In the event the data block read checksum fails, perform 1 retry before pausing the FSM.
if RCV_RAM_RETRIES = '1' then
RCV_RAM_RETRIES <= '0';
TAPE_WRITE_STATE <= 9;
else
-- Nothing can be done, checksum errors shouldnt occur, so just exit.
TAPE_WRITE_STATE <= 15;
end if;
end if;
-- Wait for single long pulse.
when 12 =>
RCV_PADDING_LEVEL <= '1';
RCV_PADDING_CNT <= 1;
RCV_PADDING_LOAD <= '1';
when 13 =>
if RCV_PADDING_DONE = '0' then
TAPE_WRITE_STATE <= 13;
end if;
-- If we have received the header, switch to receive the data.
-- On completion, signal hps to read memory by setting status flag and start recording fsm again.
when 14 =>
if RCV_RAM_TYPE = '0' then
RCV_RAM_TYPE <= '1';
TAPE_WRITE_STATE <= 0;
else
RECORD_READY_SET <= '1';
TAPE_WRITE_STATE <= 15;
end if;
-- Final state, stay until reset.
when 15 =>
TAPE_WRITE_STATE <= 15;
end case;
end if;
end if;
end if;
end process;
-- Process to recieve the header/data blocks and checksum from the MZ and store it into the cache RAM. ie. MZ -> RAM.
-- The bit stream is assumed to be at the correct point and the data is serialised and loaded into the RAM.
--
-- RCV_RAM_DONE is high when the tape header and checksum have been received.
--
-- RCV_RAM_LOAD = = Start receiving the tape data and checksum.
-- RCV_RAM_DONE = = Tape data and checksum recieved
-- RCV_RAM_SUCCESS = = Tape data and checksum recieved, checksums match.
-- CLKBUS(CKCPU) = = Base clock for encoding/decoding of pwm pulse.
--
process( RST, RCV_RAM_LOAD, CLKBUS(CKCPU) ) begin
if RST = '1' then
RCV_RAM_DONE <= '0';
RCV_RAM_SUCCESS <= '0';
RCV_RAM_ADDR <= std_logic_vector(to_unsigned(0, 16));
RCV_RAM_COUNT <= to_unsigned(0, 16); -- Count of bytes to transmit, excludes checksum.
RCV_RAM_CHECKSUM <= std_logic_vector(to_unsigned(0, 16));
RCV_RAM_STATE <= 0;
RCV_RAM_SEQ <= "000";
elsif CLKBUS(CKCPU)'event and CLKBUS(CKCPU) = '1' then
-- Sample load signal and hold. Shift right 3 bits, msb = latest value.
RCV_RAM_SEQ(1 downto 0) <= RCV_RAM_SEQ(2 downto 1);
RCV_RAM_SEQ(2) <= RCV_RAM_LOAD;
-- If load is stable, acknowledge by bringing DONE low and start process.
if RCV_RAM_SEQ = "111" then
RCV_RAM_DONE <= '0';
RCV_RAM_SUCCESS <= '0';
-- When RCV_RAM_LOAD is asserted and setled, sample parameters, set address and count for the given ram block and commence de-serialisation.
--
elsif RCV_RAM_SEQ = "110" then
if RCV_RAM_TYPE = '0' then
RCV_RAM_COUNT <= to_unsigned(128, 16);
else
RCV_RAM_COUNT <= RCV_TAPE_SIZE;
end if;
RCV_RAM_ADDR <= std_logic_vector(to_unsigned(0, 16));
HDR_RAM_WEN <= '0';
DATA_RAM_WEN <= '0';
RCV_RAM_CNT_CKSUM <= '1';
RCV_RAM_CALC_CKSUM <= std_logic_vector(to_unsigned(0, 16));
RCV_RAM_STATE <= 0;
-- If the DONE signal is low, then run the actual process, raising DONE when complete.
elsif RCV_RAM_DONE = '0' then
-- FSM to implement the receiption of data from MZ and storage in cache RAM>
case(RCV_RAM_STATE) is
when 0 =>
when 1 =>
RCV_RAM_BITCNT <= 8;
-- Store 1 bit.
when 2 =>
if RCV_AVAIL = '1' then
-- Only store the data bits, 1st bit is just padding.
if RCV_RAM_BITCNT < 8 then
if RCV_BIT = '1' then
RCV_RAM_CALC_CKSUM <= RCV_RAM_CALC_CKSUM + 1;
end if;
RCV_RAM_SR <= RCV_RAM_SR(7 downto 0) & RCV_BIT;
end if;
RCV_RAM_STATE <= 3;
end if;
when 3 =>
-- At end of transmission, load the full byte into RAM and make ready for next incoming byte.
if RCV_RAM_BITCNT = 0 then
RCV_RAM_COUNT <= RCV_RAM_COUNT - 1;
RCV_RAM_STATE <= 4;
-- For the header, extract the size of the tape data block and the load address.
--
if RCV_RAM_TYPE = '0' then
if RCV_RAM_ADDR = 18 then
RCV_TAPE_SIZE(7 downto 0) <= unsigned(RCV_RAM_SR(7 downto 0));
elsif RCV_RAM_ADDR = 19 then
RCV_TAPE_SIZE(15 downto 8) <= unsigned(RCV_RAM_SR(7 downto 0));
end if;
end if;
else
RCV_RAM_BITCNT <= RCV_RAM_BITCNT - 1;
RCV_RAM_STATE <= 2;
end if;
when 4 =>
if RCV_RAM_COUNT = 0 and RCV_RAM_CNT_CKSUM = '1' then
RCV_RAM_CHECKSUM(15 downto 8) <= RCV_RAM_SR(7 downto 0);
RCV_RAM_CNT_CKSUM <= '0';
RCV_RAM_STATE <= 1;
elsif RCV_RAM_COUNT = 0 and RCV_RAM_CNT_CKSUM = '0' then
RCV_RAM_CHECKSUM(7 downto 0) <= RCV_RAM_SR(7 downto 0);
RCV_RAM_STATE <= 8;
else
RAM_DATAIN <= RCV_RAM_SR(7 downto 0);
RCV_RAM_STATE <= 5;
end if;
when 5 =>
-- Assert Write to load data into RAM.
if RCV_RAM_TYPE = '0' then
HDR_RAM_WEN <= '1';
else
DATA_RAM_WEN <= '1';
end if;
RCV_RAM_STATE <= 6;
when 6 =>
-- Deassert write.
if RCV_RAM_TYPE = '0' then
HDR_RAM_WEN <= '0';
else
DATA_RAM_WEN <= '0';
end if;
RCV_RAM_STATE <= 7;
when 7 =>
-- Once write transaction has completed, update the RAM address.
RCV_RAM_ADDR <= RCV_RAM_ADDR + 1;
-- Receive the next byte which will be Checksum MSB.
RCV_RAM_STATE <= 1;
when 8 =>
-- Compare checksums, raise SUCCESS flag if they match.
if RCV_RAM_CHECKSUM = RCV_RAM_CALC_CKSUM then
RCV_RAM_SUCCESS <= '1';
end if;
RCV_RAM_STATE <= 9;
when 9 =>
RCV_RAM_DONE <= '1';
when others =>
end case;
end if;
end if;
end process;
-- Process to read a sequence of identical bits. This is for GAP, Tape Marks and Pulse Sequences which seperate the
-- meaningful data blocks in the tape stream.
--
-- RCV_PADDING_LOAD = = Load the counter and level and commence counting required number of bits.
-- RCV_PADDING_LEVEL = = Level of the bit to detect, 0 = 0 (low), 1 = 1 (high). A bit received opposite to what we are detecting resets the count.
-- RCV_PADDING_CNT = = Number of bits of RCV_PADDING_LEVEL to receive.
-- CLKBUS(CKMEM) = = Base clock for encoding/decoding of pwm pulse.
-- RCV_PADDING_DONE = = Status, 0 = counting or idle, 1 = done, number of required bits received.
--
process( RST, CLKBUS(CKMEM), RCV_PADDING_LOAD ) begin
if RST = '1' then
PADDING_CNT <= 0;
PADDING_LEVEL <= '0';
RCV_PADDING_DONE <= '0';
RCV_PADDING_SEQ <= "000";
elsif CLKBUS(CKCPU)'event and CLKBUS(CKCPU) = '1' then
-- Sample incoming LOAD and hold. Detect when a sample is required.
RCV_PADDING_SEQ(1 downto 00) <= RCV_PADDING_SEQ(2 downto 1);
RCV_PADDING_SEQ(2) <= RCV_PADDING_LOAD;
-- If load is stable, acknowldge by bringing DONE low and start the process.
if RCV_PADDING_SEQ = "111" then
RCV_PADDING_DONE <= '0';
-- When the load signal goes high, sample the count and level and prepare for counting.
--
elsif RCV_PADDING_SEQ = "110" then
PADDING_CNT <= RCV_PADDING_CNT;
PADDING_LEVEL <= RCV_PADDING_LEVEL;
-- If DONE is low, we are processing.
elsif RCV_PADDING_DONE = '0' then
-- On activation of received data available, sample bit. If sampled bit is same as level, decrement counter otherwise reset counter.
--
if RCV_AVAIL = '1' then
if RCV_BIT = PADDING_LEVEL then
PADDING_CNT <= PADDING_CNT - 1;
else
PADDING_CNT <= RCV_PADDING_CNT;
end if;
-- When counter reaches zero, raise the DONE flag as operation is complete.
--
if PADDING_CNT = 0 then
RCV_PADDING_DONE <= '1';
end if;
end if;
end if;
end if;
end process;
-- Process to read a bit (PWM decode) from the MZ output.
-- Basically we detect when the bit rises then start counting a fixed period of time. Once the period has
-- elapsed, we sample the data and indicate it is available.
--
-- RCV_BIT = TO TAPE = Decoded bit received from MZ.
-- RCV_AVAIL = TO TAPE = RCV_BIT = 1 received and valid.
-- READBIT = FROM MZ = Encoded bit transmitted by MZ.
-- CLKBUS(CKMEM) = = Base clock for encoding/decoding of pwm pulse.
--
-- Machine Time uS Description N
-- MZ80KCA/700 368.00 Reading point 736
-- Machine Time uS Description N-80K N-700
-- MZ80KCA/700 464.00 Long Pulse Start 1856 3248
-- 494.00 Long Pulse End 1976 3458
-- 240.00 Short Pulse Start 960 1680
-- 264.00 Short Pulse End 1056 1848
-- 368.00 Reading point. 1472 2576
-- MZ80B 333.00 Long Pulse Start 2664
-- 334.00 Long Pulse End 2672
-- 166.75 Short Pulse Start 1334
-- 166.00 Short Pulse End 1328
-- 255.00 Reading point. 2040
--
process( RST, CLKBUS(CKMEM), READBIT) begin
if RST = '1' then
RCV_AVAIL <= '0';
RCV_COUNT <= 1;
RCV_BIT <= '0';
RCV_SEQ <= "000";
elsif CLKBUS(CKMEM)'event and CLKBUS(CKMEM) = '1' then
-- Sample incoming bit and hold. Detect when a valid transmission starts.
RCV_SEQ(1 downto 0) <= RCV_SEQ(2 downto 1);
RCV_SEQ(2) <= READBIT;
-- A rising edge indicates the start of the data. We measure in from this edge the following
-- amount of time, then sample the bit as the 'read' value.
--
if RCV_SEQ = "100" then
-- Indicate data is being sampled, when = 1 data available.
RCV_AVAIL <= '0';
-- Pulse periods for MZ80C type machines
if CONFIG(MZ_80C) = '1' then
RCV_COUNT <= -1472;
elsif CONFIG(MZ700) = '1' then
RCV_COUNT <= -2576;
else
RCV_COUNT <= -2040;
end if;
elsif RCV_COUNT <= 0 then
if RCV_AVAIL = '0' and RCV_COUNT = 0 then
RCV_BIT <= READBIT;
RCV_AVAIL <= '1';
else
RCV_COUNT <= RCV_COUNT + 1;
end if;
end if;
end if;
end process;
----------------------------------------------------------------------------------------------------------------------
-- Read From Tape logic (write to MZ).
-- Definitions: Read = Read from virtual tape (RAM).
-- Write = Write into virtual tape (RAM).
-- Xmit = Transmit from CMT to MZ.
-- Rcv = Receive from MZ into CMT.
-- Thus you read Read from tape and transmit to MZ, or Receive from MZ and write onto virtual tape.
-- Playing is when the CMT is reading from tape, Recording is when the CMT is writing to tape.
--
-- This block concentrates all the logic required to deliver data from RAM (virtual tape) to the MZ.
-- Viewed from the CMT, it is the transmission of data from tape to computer.
----------------------------------------------------------------------------------------------------------------------
-- State machine to represent the tape drive READ mode (RAM -> MZ) using cache memory as the tape, which is populated by the HPS.
--
process( RST, CLKBUS(CKCPU), PLAYING ) begin
-- For reset, hold machine in reset.
if RST = '1' then
PLAY_READY_CLR <= '0';
TAPE_READ_STATE <= 0;
TAPE_READ_SEQ <= "000";
XMIT_PADDING_LOAD <= '0';
XMIT_RAM_LOAD <= '0';
XMIT_RAM_TYPE <= '0';
elsif CLKBUS(CKCPU)'event and CLKBUS(CKCPU) = '1' then
-- If not in playing mode, clear necessary signals and wait.
if PLAYING = "000" then
PLAY_READY_CLR <= '0';
-- If playing has been suspended, on 3rd clock determine the next state, setup and clear necessary signals.
elsif PLAYING = "001" then
XMIT_PADDING_LOAD <= '0';
XMIT_RAM_LOAD <= '0';
-- If the data block was received on first attempt, MZ will stop the motor, so skip the second block.
if XMIT_RAM_TYPE = '0' and TAPE_READ_STATE > 6 and TAPE_READ_STATE < 15 then
TAPE_READ_STATE <= 14;
else
PLAY_READY_CLR <= '1';
TAPE_READ_STATE <= 15;
end if;
-- Change in play state, start fsm to play out the ram contents when the HPS upload has completed.
elsif PLAYING = "110" then
if TAPE_READ_STATE = 15 then
TAPE_READ_STATE <= 0;
XMIT_RAM_TYPE <= '0';
PLAY_READY_CLR <= '0';
end if;
-- If playing, run the FSM.
elsif PLAYING = "111" then
-- Sample the done signal, when setup and stable, we can continue.
TAPE_READ_SEQ(1 downto 0) <= TAPE_READ_SEQ(2 downto 1);
TAPE_READ_SEQ(2) <= (XMIT_PADDING_LOAD or XMIT_RAM_LOAD) and (XMIT_PADDING_DONE and XMIT_RAM_DONE);
-- If a transmission has just been started and acknowledged by the DONE flag being reset, reset the activation strobe.
--
if TAPE_READ_SEQ(0) = '1' then
XMIT_PADDING_LOAD <= '0';
XMIT_RAM_LOAD <= '0';
end if;
-- If a transmission is in progress, run the FSM.
--
if XMIT_PADDING_LOAD = '0' and XMIT_RAM_LOAD = '0' then
-- Default is to move onto next state per clock cycle, unless modified by the state action.
TAPE_READ_STATE <= TAPE_READ_STATE + 1;
-- Execute current state.
case TAPE_READ_STATE is
-- Section 1 - Header
--
when 0 =>
-- Header = 0, Data = 1
if XMIT_RAM_TYPE = '0' then
-- Setup to send a Long Gap.
if CONFIG(MZ_80C) = '1' or CONFIG(MZ700) = '1' then
XMIT_PADDING_CNT1 <= 22000;
else
XMIT_PADDING_CNT1 <= 10000;
end if;
else
if CONFIG(MZ_80C) = '1' or CONFIG(MZ700) = '1' then
XMIT_PADDING_CNT1 <= 11000;
else
XMIT_PADDING_CNT1 <= 10000;
end if;
end if;
XMIT_PADDING_LEVEL1 <= '0'; -- Short Pulses
XMIT_PADDING_CNT2 <= 0;
XMIT_PADDING_LEVEL2 <= '0';
XMIT_PADDING_LOAD <= '1';
when 1 =>
-- Wait for the padding transmission to complete.
if XMIT_PADDING_DONE = '0' then
TAPE_READ_STATE <= 1;
end if;
when 2 =>
-- Header = 0, Data = 1
if XMIT_RAM_TYPE = '0' then
-- Setup to send a Long Tape Mark.
XMIT_PADDING_CNT1 <= 40;
XMIT_PADDING_CNT2 <= 40;
else
-- Setup to send a Short Tape Mark.
XMIT_PADDING_CNT1 <= 20;
XMIT_PADDING_CNT2 <= 20;
end if;
XMIT_PADDING_LEVEL1 <= '1'; -- Long Pulses
XMIT_PADDING_LEVEL2 <= '0'; -- Short Pulses
XMIT_PADDING_LOAD <= '1';
when 3 =>
if XMIT_PADDING_DONE = '0' then
TAPE_READ_STATE <= 3;
end if;
when 4 =>
-- Setup to send a Long Pulse.
XMIT_PADDING_CNT1 <= 1;
XMIT_PADDING_LEVEL1 <= '1'; -- Long Pulse
XMIT_PADDING_CNT2 <= 0;
XMIT_PADDING_LEVEL2 <= '0';
XMIT_PADDING_LOAD <= '1';
when 5 =>
if XMIT_PADDING_DONE = '0' then
TAPE_READ_STATE <= 5;
end if;
-- Send the header and checksum for header.
when 6 =>
XMIT_RAM_LOAD <= '1'; -- Send First copy of header/data.
when 7 =>
if XMIT_RAM_DONE = '0' then -- If first copy successfully received, MZ will issue a motor stop.
TAPE_READ_STATE <= 7;
end if;
when 8 =>
-- Setup to send 256 short pulse padding.
XMIT_PADDING_CNT1 <= 256;
XMIT_PADDING_LEVEL1 <= '0';
XMIT_PADDING_CNT2 <= 0;
XMIT_PADDING_LEVEL2 <= '0';
XMIT_PADDING_LOAD <= '1';
when 9 =>
if XMIT_PADDING_DONE = '0' then
TAPE_READ_STATE <= 9;
end if;
-- Resend the header/data as backup copy.
when 10 =>
XMIT_RAM_LOAD <= '1'; -- If required, send second copy of header/data.
when 11 =>
if XMIT_RAM_DONE = '0' then
TAPE_READ_STATE <= 11;
end if;
when 12 =>
-- Setup to send a Long Pulse.
XMIT_PADDING_CNT1 <= 1;
XMIT_PADDING_LEVEL1 <= '1';
XMIT_PADDING_CNT2 <= 0;
XMIT_PADDING_LEVEL2 <= '0';
XMIT_PADDING_LOAD <= '1';
when 13 =>
if XMIT_PADDING_DONE = '0' then
TAPE_READ_STATE <= 13;
end if;
-- Switch to data if we have just transmitted the header, else terminate the process.
when 14 =>
if XMIT_RAM_TYPE = '0' then
XMIT_RAM_TYPE <= '1';
TAPE_READ_STATE <= 0;
else
PLAY_READY_CLR <= '1';
end if;
-- Clear the Play Ready strobe and wait at this state until external actions reset the state.
when 15 =>
PLAY_READY_CLR <= '0';
TAPE_READ_STATE <= 15;
end case;
end if;
end if;
end if;
end process;
-- Process to read the tape data blocks and checksum from RAM and transmit it to the MZ.
--
-- The ram is serialised and written to the MZ. A checksum (count of 1's) is calculated and transmitted
-- immediately after the data.
-- XMIT_READ_DONE is high when the tape header and checksum transmission are complete.
-- Normally, XMIT_RAM_LOAD is asserted high and then wait until XMIT_DONE goes high, finally deassert XMIT_RAM_LOAD to low.
--
-- XMIT_LOAD_2 = = Load signal to commence bit transmission.
-- XMIT_BIT_2 = = Input into bit transmitter of bit value to be sent.
-- XMIT_RAM_DONE = = Transmission of RAM block complete (= 1).
-- XMIT_RAM_TYPE = = 0 - Header, 1 = Data
-- XMIT_RAM_ADDR = = Address of the RAM to be transmitted. RAM can be header or data ram block
-- XMIT_RAM_COUNT = = Count of bytes to be sent, 0 = end.
-- XMIT_RAM_CHECKSUM = = Sum of number of 1's transmitted.
-- XMIT_RAM_STATE = = State machine current state.
-- CLKBUS(CKCPU) = = Base clock for encoding/decoding of pwm pulse.
--
process( RST, CLKBUS(CKCPU), XMIT_RAM_LOAD, XMIT_RAM_TYPE ) begin
if RST = '1' then
XMIT_RAM_DONE <= '1'; -- Default state is DONE, data transmitted. Set to 0 when transmission in progress.
XMIT_LOAD_2 <= '0'; -- LOAD signal to the bit writer. 1 = start bit transmission.
--
XMIT_BIT_2 <= '0'; -- Level of bit to transmit.
XMIT_RAM_ADDR <= std_logic_vector(to_unsigned(0, 16)); -- Address of cache memory for next byte.
XMIT_RAM_COUNT <= to_unsigned(0, 16); -- Count of bytes to transmit, excludes checksum.
XMIT_RAM_CHKSUM_CNT <= to_unsigned(0, 2); -- Count of checksum bytes to transmit.
XMIT_RAM_CHECKSUM <= std_logic_vector(to_unsigned(0, 16)); -- Calculated checksum, count of all 1's in data bytes.
XMIT_RAM_STATE <= 0; -- FSM state.
XMIT_RAM_SEQ <= "000";
elsif CLKBUS(CKCPU)'event and CLKBUS(CKCPU) = '1' then
-- Sample load signal and hold. Shift right 3 bits, msb = latest value.
XMIT_RAM_SEQ(1 downto 0) <= XMIT_RAM_SEQ(2 downto 1);
XMIT_RAM_SEQ(2) <= XMIT_RAM_LOAD;
-- If load is stable, acknowledge by bringing DONE low and start process.
if XMIT_RAM_SEQ = "111" then
XMIT_RAM_DONE <= '0';
-- When XMIT_RAM_LOAD is asserted and setled, sample parameters, set address and count for the given ram block and commence serialisation.
--
elsif XMIT_RAM_SEQ = "110" then
if XMIT_RAM_TYPE = '0' then
XMIT_RAM_COUNT <= to_unsigned(128, 16);
else
XMIT_RAM_COUNT <= XMIT_TAPE_SIZE;
end if;
XMIT_RAM_CHKSUM_CNT <= to_unsigned(1, 2);
XMIT_RAM_ADDR <= std_logic_vector(to_unsigned(0, 16));
XMIT_RAM_CHECKSUM <= std_logic_vector(to_unsigned(0, 16));
XMIT_RAM_STATE <= 1;
XMIT_LOAD_2 <= '0';
-- If the DONE signal is low, then run the actual process, raising DONE when complete.
elsif XMIT_RAM_DONE = '0' then
-- Simple FSM to implement transmission of RAM contents according to MZ Tape Protocol.
case(XMIT_RAM_STATE) is
when 0 =>
when 1 =>
if XMIT_RAM_COUNT = 0 and XMIT_RAM_CHKSUM_CNT = 1 then
XMIT_RAM_SR <= '1' & XMIT_RAM_CHECKSUM(15 downto 8);
elsif XMIT_RAM_COUNT = 0 and XMIT_RAM_CHKSUM_CNT = 0 then
XMIT_RAM_SR <= '1' & XMIT_RAM_CHECKSUM(7 downto 0);
else
-- Extract the size of the tape data block and the load address if this is the header.
--
if XMIT_RAM_TYPE = '0' then
if XMIT_RAM_ADDR = 18 then
XMIT_TAPE_SIZE(7 downto 0) <= unsigned(HDR_RAM_DATAOUT);
elsif XMIT_RAM_ADDR = 19 then
XMIT_TAPE_SIZE(15 downto 8) <= unsigned(HDR_RAM_DATAOUT);
end if;
XMIT_RAM_SR <= '1' & HDR_RAM_DATAOUT;
else
XMIT_RAM_SR <= '1' & DATA_RAM_DATAOUT;
end if;
end if;
XMIT_RAM_BITCNT <= 8; -- 9 bits to transmit, pre 1 + 8 bits of data byte.
XMIT_RAM_STATE <= 2;
when 2 =>
XMIT_BIT_2 <= XMIT_RAM_SR(8);
XMIT_LOAD_2 <= '1';
if XMIT_RAM_SR(8) = '1' and XMIT_RAM_BITCNT < 8 and XMIT_RAM_COUNT > 0 then
XMIT_RAM_CHECKSUM <= XMIT_RAM_CHECKSUM + 1;
end if;
XMIT_RAM_SR <= XMIT_RAM_SR(7 downto 0) & '0';
XMIT_RAM_STATE <= 3;
when 3 =>
-- As we are using the same clock freq, need to wait until XMIT_DONE is set to 0, indicating transmission in progress.
if XMIT_LOAD_2 = '1' and XMIT_DONE = '0' then
XMIT_LOAD_2 <= '0';
XMIT_RAM_STATE <= 4;
end if;
when 4 =>
-- Wait until the DONE signal is asserted before continuing.
if XMIT_DONE = '1' then
XMIT_RAM_STATE <= 5;
end if;
when 5 =>
XMIT_BIT_2 <= '0'; -- Reset bit..
if XMIT_RAM_BITCNT = 0 then
if XMIT_RAM_COUNT = 0 and XMIT_RAM_CHKSUM_CNT = 0 then
XMIT_RAM_STATE <= 6;
else
if XMIT_RAM_COUNT > 0 then
XMIT_RAM_COUNT <= XMIT_RAM_COUNT - 1;
XMIT_RAM_ADDR <= XMIT_RAM_ADDR + 1;
elsif XMIT_RAM_COUNT = 0 and XMIT_RAM_CHKSUM_CNT > 0 then
XMIT_RAM_CHKSUM_CNT <= XMIT_RAM_CHKSUM_CNT - 1;
end if;
XMIT_RAM_STATE <= 1;
end if;
else
XMIT_RAM_BITCNT <= XMIT_RAM_BITCNT - 1;
XMIT_RAM_STATE <= 2;
end if;
when others => XMIT_RAM_DONE <= '1';
end case;
end if;
end if;
end process;
-- Process to send padding from CMT to MZ.
--
-- This process transmits a set of pulses to represent the Gap, Tape Mark, Short Seperator or Long pulse of an MZ tape
-- message. XMIT_PADDING_LOAD when high starts the generation, XMIT_PADDING_DONE is set high when generation completes.
-- Normally, the invoker process sets up the number of bits and level in the parameters:
-- XMIT_PADDING_CNT1 = Internal = If > 0, then transmit this number of bits first.
-- XMIT PADDING_LEVEL1 = Internal = Level of the bit to transmit CNT1 times.
-- XMIT PADDING_CNT2 = Internal = If > 0, then tramsit this number of bits second.
-- XMIT_PADDING_LEVEL2 = Internal = Level of the bit to transmit CNT2 times.
--
-- After completion of transmission, the Done signal is asserted high:
-- XMIT_PADDING_DONE = Internal = 0 when transmission in progress, 1 when transmission completed.
--
-- Clocks:
-- CLKBUS(CKCPU) = Internal = Base clock for encoding/decoding of pwm pulse.
--
process( RST, CLKBUS(CKCPU), XMIT_PADDING_LOAD ) begin
if RST = '1' then
XMIT_PADDING_DONE <= '1'; -- PADDING transmission complete signal, DONE = 1 when complete, 0 during transmit.
XMIT_LOAD_1 <= '0'; -- LOAD signal to bit transmitted, loads required bit when = 1 for 1 cycle.
PADDING_CNT1 <= 0;
PADDING_LEVEL1 <= '0';
PADDING_CNT2 <= 0;
PADDING_LEVEL2 <= '0';
PADDING_SEQ <= "000";
elsif CLKBUS(CKCPU)'event and CLKBUS(CKCPU) = '1' then
-- Sample load signal and hold. Shift right 3 bits, msb = latest value.
PADDING_SEQ(1 downto 0) <= PADDING_SEQ(2 downto 1);
PADDING_SEQ(2) <= XMIT_PADDING_LOAD;
-- If LOAD active for 3 periods, bring DONE low to acknowledge LOAD signal and start processing.
--
if PADDING_SEQ = "111" then
XMIT_PADDING_DONE <= '0';
end if;
-- If LOAD active for 2 periods, sample and store the provided parameters.
--
if PADDING_SEQ = "110" then
-- Sample the parameters XMIT_PADDING_CNT1, XMIT_PADDING_CNT2, XMIT_PADDING_LEVEL1, XMIT_PADDING_LEVEL2 and
-- write out the number of Level1 @ Cnt1, Level2 @ Cnt2 bits.
PADDING_CNT1 <= XMIT_PADDING_CNT1;
PADDING_LEVEL1 <= XMIT_PADDING_LEVEL1;
PADDING_CNT2 <= XMIT_PADDING_CNT2;
PADDING_LEVEL2 <= XMIT_PADDING_LEVEL2;
XMIT_LOAD_1 <= '0';
end if;
-- If DONE is low, we are processing.
if XMIT_PADDING_DONE = '0' then
-- Reset strobe when acknowledged by XMIT_DONE going low.
if XMIT_LOAD_1 = '1' and XMIT_DONE = '0' then
XMIT_LOAD_1 <= '0';
-- If we arent loading a padding sequence, then we are either waiting for a Done signal
-- or need to commence a new transmission.
--
elsif XMIT_LOAD_1 = '0' then
-- If transmission buffer empty, setup next bit to transmit.
--
if XMIT_DONE = '1' then
-- Set the completion flag if the counters expire or PLAYING is disabled.
--.
if PLAYING = "000" or (PADDING_CNT1 = 0 and PADDING_CNT2 = 0) then
-- Final wait for done on the last bit before setting our done flag.
XMIT_PADDING_DONE <= '1';
-- First, transmit the nummber of Counter 1 bits defined in Level 1.
elsif PADDING_CNT1 > 0 then
XMIT_BIT_1 <= PADDING_LEVEL1; -- Set the mux input bit according to input level,
XMIT_LOAD_1 <= '1'; -- Set the mux input to commence xmit.
PADDING_CNT1 <= PADDING_CNT1 - 1; -- Decrement counter as this bit is now being transmitted.
-- Then transmit the number of Counter 2 bits defined in Level 2.
elsif PADDING_CNT2 > 0 then
XMIT_BIT_1 <= PADDING_LEVEL2;
XMIT_LOAD_1 <= '1';
PADDING_CNT2 <= PADDING_CNT2 - 1;
end if;
end if;
end if;
end if;
end if;
end process;
-- Process to write a bit (PWM encode) to the MZ input.
-- The timings are as follows with a default SCLK of 2MHz. For faster operation, MZ clock is boosted
-- and the SCLK is also boosted on a 1:1 relationship, thus the dividers are halved per boost.
--
-- XMIT_LOAD_1 = FROM TAPE = When high, Bit available on XMIT_BIT_1 to encode and transmit to MZ.
-- XMIT_LOAD_2 = FROM TAPE = When high, Bit available on XMIT_BIT_2 to encode and transmit to MZ.
-- XMIT_DONE = TO TAPE = When high, transmission of bit complete. Resets to 0 on active XMIT_LOAD signal.
-- WRITEBIT = FROM MZ = Encoded bit tranmitted to MZ.
-- CLKBUS(CKCPU) = = Base clock for encoding/decoding of pwm pulse.
--
-- Machine Time uS Description N-80K(CKMEM) N-80K(CKCPU) N-700(CKCPU) N-700(CKMEM)
-- MZ80KCA/700 464.00 Long Pulse Start 1856 928 1624 3248
-- 494.00 Long Pulse End 1976 988 1729 3458
-- 240.00 Short Pulse Start 960 480 840 1680
-- 264.00 Short Pulse End 1056 528 924 1848
-- 368.00 Read point. 1472 736 1288 2576
-- MZ80B 333.00 Long Pulse Start 2664 1332
-- 334.00 Long Pulse End 2672 1336
-- 166.75 Short Pulse Start 1334 667
-- 166.00 Short Pulse End 1328 664
-- 255.00 Read point. 2040 1020
--
process( RST, CLKBUS(CKCPU), XMIT_LOAD_1, XMIT_LOAD_2 ) begin
-- When RESET is high, hold in reset mode.
if RST = '1' then
XMIT_DONE <= '1'; -- Completion signal, 0 when transmitting, 1 when done.
WRITEBIT <= '0'; -- Bit facing towards MZ input.
XMIT_LIMIT <= 0; -- End of pulse.
XMIT_COUNT <= 0; -- Pulse start, bit set to 1, reset to 0 on counter = 0
XMIT_SEQ <= "000";
elsif CLKBUS(CKCPU)'event and CLKBUS(CKCPU)='1' then
-- Sample load signal and hold. Shift right 3 bits, msb = latest value.
XMIT_SEQ(1 downto 0) <= XMIT_SEQ(2 downto 1);
XMIT_SEQ(2) <= XMIT_LOAD_1 or XMIT_LOAD_2;
-- If load is stable, acknowledge by bringing DONE low and start process.
if XMIT_SEQ = "111" then
XMIT_DONE <= '0';
WRITEBIT <= '1';
-- Store run values on 2nd clock cycle of LOAD being active.
elsif XMIT_SEQ = "110" then
-- Pulse periods for MZ80C type machines
if CONFIG(MZ_KC) = '1' or CONFIG(MZ_A) = '1' then
if (XMIT_LOAD_1 = '1' and XMIT_BIT_1 = '1') or (XMIT_LOAD_2 = '1' and XMIT_BIT_2 = '1') then
XMIT_LIMIT <= 988; -- 1976;
XMIT_COUNT <= -928; -- -1856;
else
XMIT_LIMIT <= 528; -- 1056;
XMIT_COUNT <= -480; -- -960;
end if;
elsif CONFIG(MZ700) = '1' then
-- Pulse periods for MZ700 type machines
if (XMIT_LOAD_1 = '1' and XMIT_BIT_1 = '1') or (XMIT_LOAD_2 = '1' and XMIT_BIT_2 = '1') then
XMIT_LIMIT <= 1729; -- 3458;
XMIT_COUNT <= -1624; -- -3248;
else
XMIT_LIMIT <= 924; -- 1848;
XMIT_COUNT <= -840; -- -1680;
end if;
else
-- Pulse periods for MZ80B type machines
if (XMIT_LOAD_1 = '1' and XMIT_BIT_1 = '1') or (XMIT_LOAD_2 = '1' and XMIT_BIT_2 = '1') then
XMIT_LIMIT <= 1336; -- 2672;
XMIT_COUNT <= -1332; -- -2664;
else
XMIT_LIMIT <= 664; -- 1328;
XMIT_COUNT <= -667; -- -1334;
end if;
end if;
-- On expiration of timer, signal completion.
elsif XMIT_COUNT = XMIT_LIMIT then
XMIT_DONE <= '1';
-- If the counter is running, format the output pulse.
elsif XMIT_COUNT /= XMIT_LIMIT then
-- At zero, we have elapsed the correct high period for the write bit, now bring it low for the remaining period.
if XMIT_COUNT = 0 then
WRITEBIT <= '0';
end if;
XMIT_COUNT <= XMIT_COUNT + 1;
end if;
end if;
end process;
DEBUG_STATUS_LEDS(0) <= WRITEBIT;
DEBUG_STATUS_LEDS(1) <= XMIT_DONE;
DEBUG_STATUS_LEDS(2) <= XMIT_LOAD_1;
DEBUG_STATUS_LEDS(3) <= XMIT_LOAD_2;
DEBUG_STATUS_LEDS(4) <= XMIT_PADDING_LOAD;
DEBUG_STATUS_LEDS(5) <= XMIT_PADDING_DONE;
DEBUG_STATUS_LEDS(6) <= XMIT_RAM_LOAD;
DEBUG_STATUS_LEDS(7) <= XMIT_RAM_DONE;
DEBUG_STATUS_LEDS(8) <= PLAY_READY;
DEBUG_STATUS_LEDS(9) <= PLAY_READY_CLR;
DEBUG_STATUS_LEDS(10) <= PLAYING(2);
DEBUG_STATUS_LEDS(11) <= PLAYING(1);
DEBUG_STATUS_LEDS(12) <= PLAYING(0);
DEBUG_STATUS_LEDS(13) <= RECORDING(2);
DEBUG_STATUS_LEDS(14) <= RECORDING(1);
DEBUG_STATUS_LEDS(15) <= RECORDING(0);
DEBUG_STATUS_LEDS(16) <= READBIT;
DEBUG_STATUS_LEDS(17) <= RCV_AVAIL;
DEBUG_STATUS_LEDS(18) <= RECORD_READY;
DEBUG_STATUS_LEDS(19) <= RCV_RAM_TYPE;
DEBUG_STATUS_LEDS(20) <= RCV_PADDING_LOAD;
DEBUG_STATUS_LEDS(21) <= RCV_PADDING_DONE;
DEBUG_STATUS_LEDS(22) <= RCV_RAM_LOAD;
DEBUG_STATUS_LEDS(23) <= RCV_RAM_DONE;
--DEBUG_STATUS_LEDS(19 downto 16) <= std_logic_vector(to_unsigned(TAPE_READ_STATE, 4));
--DEBUG_STATUS_LEDS(22 downto 20) <= TAPE_READ_SEQ; --PADDING_SEQ;
--DEBUG_STATUS_LEDS(23 downto 19) <= XMIT_RAM_SEQ;
end RTL;