mirror of
https://github.com/ibm2030/IBM2030.git
synced 2026-01-11 23:52:47 +00:00
329 lines
14 KiB
VHDL
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;
|