1
0
mirror of https://github.com/ibm2030/IBM2030.git synced 2026-01-11 23:52:47 +00:00
ibm2030.IBM2030/ibm2030-storage.vhd
2015-11-25 10:01:15 +01:00

329 lines
14 KiB
VHDL

---------------------------------------------------------------------------
-- Copyright 2010 Lawrence Wilkinson lawrence@ljw.me.uk
--
-- This file is part of LJW2030, a VHDL implementation of the IBM
-- System/360 Model 30.
--
-- LJW2030 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.
--
-- LJW2030 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 LJW2030 . If not, see <http://www.gnu.org/licenses/>.
--
---------------------------------------------------------------------------
--
-- File: ibm2030-storage.vhd
-- Creation Date: 19:55:00 20/07/10
-- Description:
-- 360/30 Storage Handling - Main and Local (Bump) Storage
-- Page references like "5-01A" refer to the IBM Maintenance Diagram Manual (MDM)
-- for the 360/30 R25-5103-1
-- References like "02AE6" refer to coordinate "E6" on page "5-02A"
-- Logic references like "AB3D5" refer to card "D5" in board "B3" in gate "A"
-- Gate A is the main logic gate, B is the second (optional) logic gate,
-- C is the core storage and X is the CCROS unit
--
-- Revision History:
-- Revision 1.0 2010-07-20 Initial Release
-- Revision 1.1 2012-03-06 Modified to parse PCH files from Hercules (ESD/TXT/TXT/TXT/RLD/RLD/END)
--
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use work.Buses_package.all;
use work.Gates_package.all;
---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity storage is
Port ( -- Physical storage I/O from FPGA
phys_address : out std_logic_vector(16 downto 0);
phys_data : inout std_logic_vector(8 downto 0);
phys_CE : out std_logic;
phys_OE : out std_logic;
phys_WE : out std_logic;
phys_UB,phys_LB : out std_logic;
-- Other inputs
clk : in STD_LOGIC; -- 50MHz
-- Interface to config ROM
din : in STD_LOGIC;
reset_prom : out STD_LOGIC;
cclk : out STD_LOGIC;
-- Storage interface to CPU
StorageIn : out STORAGE_IN_INTERFACE;
StorageOut : in STORAGE_OUT_INTERFACE;
debug : out STD_LOGIC
);
end storage;
architecture Behavioral of storage is
--
-- declaration of serial configuration PROM reading interface
--
component prom_reader_serial
generic( length : integer := 5; --sync pattern 2^length
frequency : integer := 50 ); --system clock speed in MHz
port( clock : in std_logic;
reset : in std_logic; --active high
read : in std_logic; --active low single cycle pulse
next_sync : in std_logic; --active low single cycle pulse
din : in std_logic;
sync_pattern : in std_logic_vector((2**length) - 1 downto 0);
cclk : out std_logic;
sync : out std_logic; --active low single cycle pulse
data_ready : out std_logic; --active low single cycle pulse
reset_prom : out std_logic; --active high to /OE of PROM (reset when high)
dout : out std_logic_vector(7 downto 0));
end component;
-- Signals for RAM clearing and initialisation purposes (at startup)
signal drive_out : std_logic;
signal addr : std_logic_vector(15 downto 0) := "0000000000000000";
signal len : std_logic_vector(15 downto 0) := "0000000000000000";
signal init_CE : std_logic;
signal init_WE : std_logic;
signal init_OE : std_logic;
signal init_drive_out, clear_data_out, clear_local_data_out, init_data_out: std_logic;
signal clear_data : std_logic_vector(7 downto 0) := "00000000"; -- Value written into storage locations when clearing
signal init_data : std_logic_vector(7 downto 0);
type init_state is (
initClearMainStorage,initClearLocalStorage,
resetProm,resetProm2,
wait_for_first_high_length_byte,wait_for_high_length_byte,got_high_length_byte,wait_for_low_length_byte,got_low_length_byte,
wait_for_high_address_byte,got_high_address_byte,wait_for_low_address_byte,got_low_address_byte,
wait_for_data_byte, write_byte, written_byte, finished);
signal state : init_state := initClearMainStorage;
--
-- Signals for serial PROM reader
--
signal reset_prom_reader : std_logic;
signal prom_read_pulse : std_logic;
signal prom_sync_pulse : std_logic;
signal prom_data_ready_pulse : std_logic;
begin
-- See Xilinx XAPP694 for how to store user data in the platform flash
-- Input file format is
-- Header 8F9FAFBF (see XAPP694)
-- LL High byte of data segment length
-- LL Low byte of data segment length
-- AA High byte of destination address
-- AA Low byte of destination address
-- DD Data byte (repeated a total of LLLL times)
-- LL LL AA AA DD DD ... DD repeated as required
-- 00 00 Length of 0000 to terminate
Initialise: process(clk) is
begin
-- Initialise storage
if clk'event and clk='1' then -- Wait for rising edge of 50MHz clock
case state is
-- Clear the 64k of main storage space
when initClearMainStorage =>
init_data_out <= '0'; -- '1' if we're initialising RAM from PROM
clear_data_out <= '1'; -- '1' if we're clearing the 64k main storage space
clear_local_data_out <= '0'; -- '1' if we're clearing the local storage space
addr <= (others=>'0'); -- Start clearing at 0000
len <= (others=>'0'); -- Clear 64k
state <= write_byte; -- Will come back to initClearLocalStorage
-- Clear 64k of local storage space, though only a small portion is actually used
when initClearLocalStorage =>
clear_data_out <= '0'; -- Done with clearing main storage...
clear_local_data_out <= '1'; -- ... so on to clearing local storage
addr <= (others=>'0'); -- Start clearing at 0000
len <= (others=>'0'); -- Clear 64k
state <= write_byte; -- Will come back to resetProm
when resetProm =>
clear_data_out <= '0'; -- Done with clearing main storage...
clear_local_data_out <= '0'; -- ...and local storage...
init_data_out <= '1'; -- ...so on to initialising storage from PROM
state <= resetProm2;
when resetProm2 =>
state <= wait_for_first_high_length_byte;
when wait_for_first_high_length_byte =>
-- Wait until we get the first data byte, which is the high byte of the length
-- Note we don't need to assert the PROM read pulse in this state
-- as the first byte following the sync pattern is automatically read
if (prom_data_ready_pulse = '0') then
len(15 downto 8) <= init_data;
state <= got_high_length_byte;
else
state <= wait_for_high_length_byte;
end if;
when wait_for_high_length_byte =>
-- Wait until we get a high length byte
if (prom_data_ready_pulse = '0') then
-- Store it in len (high)
len(15 downto 8) <= init_data;
state <= got_high_length_byte;
else
state <= wait_for_high_length_byte;
end if;
when got_high_length_byte =>
state <= wait_for_low_length_byte;
when wait_for_low_length_byte =>
-- Wait until we get a low length byte
if (prom_data_ready_pulse = '0') then
-- Store it in len (low)
len(7 downto 0) <= init_data;
-- Check if both bytes are 00, finish if so
-- Note: Can't check len(7 downto 0) as it isn't in there yet
if len(15 downto 8)="00000000" and init_data="00000000" then
state <= finished;
else
-- Not 0 length, go on to getting address & data bytes
state <= got_low_length_byte;
end if;
else
state <= wait_for_low_length_byte;
end if;
when got_low_length_byte =>
state <= wait_for_high_address_byte;
when wait_for_high_address_byte =>
-- Wait until we get the high address byte
if (prom_data_ready_pulse = '0') then
-- Store it in addr (high)
addr(15 downto 8) <= init_data;
state <= got_high_address_byte;
else
state <= wait_for_high_address_byte;
end if;
when got_high_address_byte =>
-- prom_read_pulse <= '0';
state <= wait_for_low_address_byte;
when wait_for_low_address_byte =>
-- Wait until we get the low address byte
if (prom_data_ready_pulse = '0') then
-- Store it in addr (low)
addr(7 downto 0) <= init_data;
state <= got_low_address_byte;
else
state <= wait_for_low_address_byte;
end if;
when got_low_address_byte =>
state <= wait_for_data_byte;
when wait_for_data_byte =>
-- Wait until we get one of our data bytes from the PROM
if (prom_data_ready_pulse = '0') then
state <= write_byte;
else
state <= wait_for_data_byte;
end if;
when write_byte =>
-- WE* is asserted during this state and does the actual write
state <= written_byte;
when written_byte =>
-- Bump address and count
addr <= addr + "0000000000000001";
len <= len - "0000000000000001";
-- Compare length to 1 (not 0) as it is about to be decremented
if (len="0000000000000001") then
-- Ok, have done all the bytes now
if clear_data_out='1' then
-- If we were clearing main storage, go on to clearing local storage
state <= initClearLocalStorage;
else if clear_local_data_out='1' then
-- If we were clearing local storage, go on to initialising storage
state <= resetProm;
else
-- Doing initialisation, so look for a further length value
state <= wait_for_high_length_byte;
end if;
end if;
else
-- Not finished yet
if clear_data_out='1' or clear_local_data_out='1' then
-- Clearing storage can go straight back and do another byte
state <= write_byte;
else
-- Initialising storage needs to fetch a byte from PROM
state <= wait_for_data_byte;
end if;
end if;
when finished =>
-- Make sure we can't interfere with CPU operation
init_data_out <= '0';
when others =>
end case;
end if;
end process;
-- Outputs generated as a function of the initialisation machine state:
init_drive_out <= '0' when state=finished else '1';
init_ce <= '0' when state=wait_for_data_byte or state=write_byte or state=written_byte else '1';
init_oe <= '1';
init_we <= '0' when state=write_byte else '1'; -- Only assert WE* from the one state
reset_prom_reader <= '1' when state=resetProm or state=resetProm2 else '0';
-- reset_prom_reader <= '1' when state=resetProm else '0';
prom_read_pulse <= '0' when state=wait_for_high_length_byte
or state=wait_for_low_length_byte
or state=wait_for_high_address_byte
or state=wait_for_low_address_byte
or state=wait_for_data_byte; -- Trigger a further PROM read when in these states
phys_CE <= init_ce when init_drive_out='1' else '0' when StorageOut.ReadPulse='1' or StorageOut.WritePulse='1' else '1'; -- Select which CE* to use
phys_WE <= init_we when init_drive_out='1' else '0' when StorageOut.WritePulse='1' else '1'; -- Select which WE* to use
phys_UB <= '0'; -- Always select upper byte
phys_LB <= '0'; -- Always select lower byte
phys_OE <= init_oe when init_drive_out='1' else '0' when StorageOut.ReadPulse='1' or StorageOut.WritePulse='0' else '1'; -- Assert OE* if reading
drive_out <= '1' when init_drive_out='1' else '0' when StorageOut.ReadPulse='1' else '1'; -- Whether data bus is driving out, or tristated for input
-- Read in and latch data when doing a real memory read (note - this does not erase the memory as real core would)
StorageIn.ReadData <= phys_data when StorageOut.ReadPulse='1';
-- Select initialisation data or real (R reg) data to go out when writing
phys_data <= init_data & evenParity(init_data) when init_drive_out='1' and init_data_out='1'
else clear_data & evenParity(clear_data) when init_drive_out='1' and (clear_data_out='1' or clear_local_data_out='1')
else StorageOut.WriteData when drive_out='1'
else "ZZZZZZZZZ";
-- Select initialisation address or real (MN reg) address to go out
-- Top bit is 0 for Local Storage and 1 for Main Storage
phys_address <= (not clear_local_data_out) & addr when init_drive_out='1' else StorageOut.MainStorage & StorageOut.MSAR;
-- This turns the debug light on during initialisation
-- (if configured in the higher-level blocks)
debug <= init_drive_out;
--
----------------------------------------------------------------------------------------------------------------------------------
-- Serial configuration PROM reader
----------------------------------------------------------------------------------------------------------------------------------
--
-- This macro enables data stored afater the Spartan-3 configuration data to be located and then read
-- sequentially.
--
prom_access: prom_reader_serial
generic map( length => 5, --Synchronisation pattern is 2^5 = 32 bits
frequency => 50) --System clock rate is 50MHz
port map( clock => clk,
reset => reset_prom_reader, --reset reader and initiates search for sync pattern
read => prom_read_pulse, --active low pulse initiates retrieval of next byte
next_sync => '1', --would be used to find another sync pattern
din => din, --from XCF04S device
sync_pattern => X"8F9FAFBF", --32bit synchronisation pattern is constant in this application
cclk => cclk, --to XCF04S device
sync => prom_sync_pulse, --active low pulse indicates sync pattern located
data_ready => prom_data_ready_pulse, --active low pulse indicates data byte received
reset_prom => reset_prom, --to XCF04S device
dout => init_data); --byte received from serial prom
end behavioral;