----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
-- Company: None
|
-- Company: None
|
-- Engineer: Yann Vernier
|
-- Engineer: Yann Vernier
|
--
|
--
|
-- Create Date: 13:29:13 02/09/2009
|
-- Create Date: 13:29:13 02/09/2009
|
-- Design Name:
|
-- Design Name:
|
-- Module Name: pdp1cpu - Behavioral
|
-- Module Name: pdp1cpu - Behavioral
|
-- Project Name: PDP-1
|
-- Project Name: PDP-1
|
-- Target Devices: Xilinx Spartan 3A
|
-- Target Devices: Xilinx Spartan 3A
|
-- Tool versions: WebPack 10.1
|
-- Tool versions: WebPack 10.1
|
-- Description: PDP-1 CPU (main logic) module, executes instructions.
|
-- Description: PDP-1 CPU (main logic) module, executes instructions.
|
--
|
--
|
-- Dependencies: Requires a RAM and a clock source. RAM is accessed in alternating read and write.
|
-- Dependencies: Requires a RAM and a clock source. RAM is accessed in alternating read and write.
|
-- Original clock is 200kHz memory cycle, where each cycle both
|
-- Original clock is 200kHz memory cycle, where each cycle both
|
-- reads and writes (core memory). CPU itself must be clocked 10
|
-- reads and writes (core memory). CPU itself must be clocked 10
|
-- times faster (original logic modules could keep up with 4MHz).
|
-- times faster (original logic modules could keep up with 4MHz).
|
--
|
--
|
-- Revision:
|
-- Revision:
|
-- Revision 0.01 - File Created
|
-- Revision 0.01 - File Created
|
-- Additional Comments:
|
-- Additional Comments:
|
-- Aim is currently a PDP-1B level.
|
-- Aim is currently a PDP-1B level.
|
-- B version had multiply and divide Step to accelerate the subroutines,
|
-- B version had multiply and divide Step to accelerate the subroutines,
|
-- and the first had pure software subroutines. C had full hardware multiply(?).
|
-- and the first had pure software subroutines. C had full hardware multiply(?).
|
-- PDP-1D implements several more instructions not included here.
|
-- PDP-1D implements several more instructions not included here.
|
-- Extensions (including sequence break and extended memory) are not implemented.
|
-- Extensions (including sequence break and extended memory) are not implemented.
|
--
|
--
|
-- Goal: Run Spacewar! in hardware. Initial target version is that used by Java
|
-- Goal: Run Spacewar! in hardware. Initial target version is that used by Java
|
-- emulator, which is a PDP-1B, with multiply and divide steps.
|
-- emulator, which is a PDP-1B, with multiply and divide steps.
|
-- That emulator doesn't have DIP.
|
-- That emulator doesn't have DIP.
|
-- PDP-1C had full multiply and divide instructions of variable time.
|
-- PDP-1C had full multiply and divide instructions of variable time.
|
--
|
--
|
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
library IEEE;
|
library IEEE;
|
use IEEE.STD_LOGIC_1164.ALL;
|
use IEEE.STD_LOGIC_1164.ALL;
|
|
-- Why does isim behave like unsigned+natural doesn't exist?
|
use IEEE.NUMERIC_STD.ALL;
|
use IEEE.NUMERIC_STD.ALL;
|
|
|
|
|
entity pdp1cpu is
|
entity pdp1cpu is
|
Port (
|
Port (
|
-- memory interface
|
-- memory interface
|
M_DI : out STD_LOGIC_VECTOR(0 to 17) := (others=>'0');
|
M_DI : out STD_LOGIC_VECTOR(0 to 17) := (others=>'0');
|
M_DO : in STD_LOGIC_VECTOR(0 to 17);
|
M_DO : in STD_LOGIC_VECTOR(0 to 17);
|
MW : inout STD_LOGIC := '0';
|
MW : inout STD_LOGIC := '0';
|
MA : out std_logic_vector(0 to 11) := (others=>'0');
|
MA : out std_logic_vector(0 to 11) := (others=>'0');
|
|
|
CLK : in STD_LOGIC; -- in progress: adapt to 10x clock
|
CLK : in STD_LOGIC; -- in progress: adapt to 10x clock
|
|
|
-- CPU status
|
-- CPU status
|
AWAKE : out STD_LOGIC;
|
AWAKE : out STD_LOGIC;
|
|
|
-- user visible registers
|
-- user visible registers
|
AC : inout STD_LOGIC_VECTOR(0 to 17) := (others=>'0'); -- accumulator
|
AC : inout STD_LOGIC_VECTOR(0 to 17) := (others=>'0'); -- accumulator
|
IO : inout STD_LOGIC_VECTOR(0 to 17) := (others=>'0'); -- I/O
|
IO : inout STD_LOGIC_VECTOR(0 to 17) := (others=>'0'); -- I/O
|
PC : inout unsigned(0 to 11) := (others=>'0'); -- program counter
|
PC : inout STD_LOGIC_VECTOR(0 to 11) := (others=>'0'); -- program counter
|
PF : inout STD_LOGIC_VECTOR(1 to 6) := (others=>'0'); -- program flags
|
PF : inout STD_LOGIC_VECTOR(1 to 6) := (others=>'0'); -- program flags
|
OV : inout STD_LOGIC := '0'; -- overflow flag
|
OV : inout STD_LOGIC := '0'; -- overflow flag
|
|
|
-- user settable switches
|
-- user settable switches
|
SW_TESTA : in std_logic_vector(0 to 11) := (others => '0'); -- test address
|
SW_TESTA : in std_logic_vector(0 to 11) := (others => '0'); -- test address
|
SW_TESTW : in std_logic_vector(0 to 17) := (others => '0'); -- test word
|
SW_TESTW : in std_logic_vector(0 to 17) := (others => '0'); -- test word
|
SW_SENSE : in std_logic_vector(1 to 6) := (others => '0'); -- sense switches
|
SW_SENSE : in std_logic_vector(1 to 6) := (others => '0'); -- sense switches
|
|
|
-- I/O interface
|
-- I/O interface
|
IOT : out STD_LOGIC_VECTOR(0 to 63) := (others=>'0'); -- I/O transfer pulse lines
|
IOT : out STD_LOGIC_VECTOR(0 to 63) := (others=>'0'); -- I/O transfer pulse lines
|
IODOPULSE : out STD_LOGIC := '0'; -- signal to I/O device to send a
|
IODOPULSE : out STD_LOGIC := '0'; -- signal to I/O device to send a
|
-- pulse when done
|
-- pulse when done
|
IODONE : in STD_LOGIC := '0'; -- I/O device done signal
|
IODONE : in STD_LOGIC := '0'; -- I/O device done signal
|
IO_SET : in STD_ULOGIC := '0'; -- used to set I/O register to IO_IN value (synchronous!)
|
IO_SET : in STD_ULOGIC := '0'; -- used to set I/O register to IO_IN value (synchronous!)
|
IO_IN : in STD_LOGIC_VECTOR(0 to 17) := o"000000"; -- bus for I/O devices to report values
|
IO_IN : in STD_LOGIC_VECTOR(0 to 17) := o"000000"; -- bus for I/O devices to report values
|
|
|
RESET : in STD_LOGIC
|
RESET : in STD_LOGIC
|
);
|
);
|
end pdp1cpu;
|
end pdp1cpu;
|
|
|
architecture Behavioral of pdp1cpu is
|
architecture Behavioral of pdp1cpu is
|
subtype word is STD_LOGIC_VECTOR(0 to 17);
|
subtype word is STD_LOGIC_VECTOR(0 to 17);
|
subtype opcode is std_logic_vector(0 to 5);
|
subtype opcode is std_logic_vector(0 to 5);
|
-- Formally the opcode is 5 bits; I've included the indirection bit.
|
-- Formally the opcode is 5 bits; I've included the indirection bit.
|
|
|
signal MB: word; -- memory buffer
|
signal MB: word; -- memory buffer
|
signal op: opcode := o"00"; -- current operation (user visible originally)
|
signal op: opcode := o"00"; -- current operation (user visible originally)
|
|
|
signal IOWAIT, HALT : boolean := false;
|
signal IOWAIT, HALT : boolean := false;
|
|
|
alias ib : std_logic is op(5); -- indirection bit
|
alias ib : std_logic is op(5); -- indirection bit
|
alias y : std_logic_vector(0 to 11) is MB(6 to 17); -- address/operand
|
alias y : std_logic_vector(0 to 11) is MB(6 to 17); -- address/operand
|
|
|
-- operations - note that "load" here is OR, for some reason.
|
-- operations - note that "load" here is OR, for some reason.
|
alias cli : std_logic is MB(6); -- clear IO
|
alias cli : std_logic is MB(6); -- clear IO
|
alias lat : std_logic is MB(7); -- load AC with Test.Switches
|
alias lat : std_logic is MB(7); -- load AC with Test.Switches
|
alias cma : std_logic is MB(8); -- complement AC
|
alias cma : std_logic is MB(8); -- complement AC
|
alias hlt : std_logic is MB(9); -- halt
|
alias hlt : std_logic is MB(9); -- halt
|
alias cla : std_logic is MB(10); -- clear AC
|
alias cla : std_logic is MB(10); -- clear AC
|
alias lap : std_logic is MB(11); -- load AC from PC
|
alias lap : std_logic is MB(11); -- load AC from PC
|
alias flag_setto : std_logic is MB(14); -- set program flag(s) - value
|
alias flag_setto : std_logic is MB(14); -- set program flag(s) - value
|
alias flag_which : std_logic_vector(2 downto 0) is MB(15 to 17);
|
alias flag_which : std_logic_vector(2 downto 0) is MB(15 to 17);
|
-- skip conditions
|
-- skip conditions
|
alias spi : std_logic is MB(7); -- Skip if Positive IO
|
alias spi : std_logic is MB(7); -- Skip if Positive IO
|
alias szo : std_logic is MB(8); -- skip if zero OV
|
alias szo : std_logic is MB(8); -- skip if zero OV
|
alias sza : std_logic is MB(9); -- skip if zero AC
|
alias sza : std_logic is MB(9); -- skip if zero AC
|
alias spa : std_logic is MB(10); -- skip if positive AC
|
alias spa : std_logic is MB(10); -- skip if positive AC
|
alias sma : std_logic is MB(11); -- skip if negative AC
|
alias sma : std_logic is MB(11); -- skip if negative AC
|
alias szs : std_logic_vector(0 to 2) is MB(12 to 14); -- skip if Zero Switches
|
alias szs : std_logic_vector(0 to 2) is MB(12 to 14); -- skip if Zero Switches
|
alias szf : std_logic_vector(0 to 2) is MB(15 to 17); -- skip if Zero Flags
|
alias szf : std_logic_vector(0 to 2) is MB(15 to 17); -- skip if Zero Flags
|
|
|
-- Opcodes -- loading group
|
-- Opcodes -- loading group
|
constant op_and : opcode := o"02"; -- AC&=M
|
constant op_and : opcode := o"02"; -- AC&=M
|
constant op_ior : opcode := o"04"; -- AC|=M
|
constant op_ior : opcode := o"04"; -- AC|=M
|
constant op_xor : opcode := o"06"; -- AC^=M
|
constant op_xor : opcode := o"06"; -- AC^=M
|
constant op_add : opcode := o"40"; -- AC+=M
|
constant op_add : opcode := o"40"; -- AC+=M
|
constant op_sub : opcode := o"42"; -- AC-=M
|
constant op_sub : opcode := o"42"; -- AC-=M
|
constant op_lac : opcode := o"20"; -- load AC
|
constant op_lac : opcode := o"20"; -- load AC
|
constant op_lio : opcode := o"22"; -- load IO
|
constant op_lio : opcode := o"22"; -- load IO
|
constant op_sad : opcode := o"50"; -- skip if AC/=M(y)
|
constant op_sad : opcode := o"50"; -- skip if AC/=M(y)
|
constant op_sas : opcode := o"52"; -- skip if AC=M(y)
|
constant op_sas : opcode := o"52"; -- skip if AC=M(y)
|
-- storing group
|
-- storing group
|
constant op_dac : opcode := o"24"; -- store AC
|
constant op_dac : opcode := o"24"; -- store AC
|
constant op_dap : opcode := o"26"; -- deposit address part of AC
|
constant op_dap : opcode := o"26"; -- deposit address part of AC
|
constant op_dip : opcode := o"30"; -- deposit instruction part -- missing in Java emulator
|
constant op_dip : opcode := o"30"; -- deposit instruction part -- missing in Java emulator
|
constant op_dio : opcode := o"32"; -- deposit IO
|
constant op_dio : opcode := o"32"; -- deposit IO
|
constant op_dzm : opcode := o"34"; -- deposit zero
|
constant op_dzm : opcode := o"34"; -- deposit zero
|
-- jumping group
|
-- jumping group
|
constant op_skip: opcode := o"64"; -- adds 1 to IP; SAD and SAS, load group, also do this
|
constant op_skip: opcode := o"64"; -- adds 1 to IP; SAD and SAS, load group, also do this
|
constant op_skipi: opcode := o"65"; -- as above, but inverts condition
|
constant op_skipi: opcode := o"65"; -- as above, but inverts condition
|
constant op_jmp : opcode := o"60"; -- jump
|
constant op_jmp : opcode := o"60"; -- jump
|
constant op_jsp : opcode := o"62"; -- jump and save PC
|
constant op_jsp : opcode := o"62"; -- jump and save PC
|
constant op_cal : opcode := o"16"; -- call subroutine
|
constant op_cal : opcode := o"16"; -- call subroutine
|
constant op_jda : opcode := o"17"; -- jump and deposit AC
|
constant op_jda : opcode := o"17"; -- jump and deposit AC
|
-- immediate group
|
-- immediate group
|
constant op_rotshiftl: opcode := o"66"; -- rotate/shift (IB is direction)
|
constant op_rotshiftl: opcode := o"66"; -- rotate/shift (IB is direction)
|
constant op_rotshiftr: opcode := o"67";
|
constant op_rotshiftr: opcode := o"67";
|
constant op_law : opcode := o"70"; -- load accumulator immediate
|
constant op_law : opcode := o"70"; -- load accumulator immediate
|
constant op_lawm: opcode := o"71";
|
constant op_lawm: opcode := o"71";
|
constant op_opr : opcode := o"76"; -- operate group
|
constant op_opr : opcode := o"76"; -- operate group
|
-- miscellaneous
|
-- miscellaneous
|
constant op_idx : opcode := o"44"; -- index - AC=++M[y]
|
constant op_idx : opcode := o"44"; -- index - AC=++M[y]
|
constant op_isp : opcode := o"46"; -- same, and skip if positive
|
constant op_isp : opcode := o"46"; -- same, and skip if positive
|
-- constant op_mul : opcode := o"54"; -- full multiply (PDP-1C)
|
-- constant op_mul : opcode := o"54"; -- full multiply (PDP-1C)
|
-- constant op_div : opcode := o"56"; -- full divide (PDP-1C)
|
-- constant op_div : opcode := o"56"; -- full divide (PDP-1C)
|
constant op_mus : opcode := o"54"; -- multiply step (PDP-1B)
|
constant op_mus : opcode := o"54"; -- multiply step (PDP-1B)
|
constant op_dis : opcode := o"56"; -- divide step (PDP-1B)
|
constant op_dis : opcode := o"56"; -- divide step (PDP-1B)
|
constant op_xct : opcode := o"10"; -- execute
|
constant op_xct : opcode := o"10"; -- execute
|
constant op_iot : opcode := o"73"; -- I/O transfer group, ib is wait for completion
|
constant op_iot : opcode := o"73"; -- I/O transfer group, ib is wait for completion
|
constant op_iot_nw : opcode := o"72";
|
constant op_iot_nw : opcode := o"72";
|
|
|
-- cycletype tracks the memory cycle reason
|
-- cycletype tracks the memory cycle reason
|
type cycle_type is (load_instruction, load_indirect, load_data, store_data);
|
type cycle_type is (load_instruction, load_indirect, load_data, store_data);
|
signal cycletype : cycle_type;
|
signal cycletype : cycle_type;
|
signal cycle : integer range 0 to 9 := 0; -- 10 cycles per memory access cycle
|
signal cycle : integer range 0 to 9 := 0; -- 10 cycles per memory access cycle
|
-- NOTE: rotshift relies on this range!
|
-- NOTE: rotshift relies on this range!
|
constant cycle_setup_read: integer := 0;
|
constant cycle_setup_read: integer := 0;
|
constant cycle_read: integer := 2; -- memory is over-registered
|
constant cycle_read: integer := 2; -- memory is over-registered
|
constant cycle_execute: integer := 3;
|
constant cycle_execute: integer := 3;
|
constant cycle_setup_write: integer := 5;
|
constant cycle_setup_write: integer := 5;
|
constant cycle_wrote: integer := 6; -- not actually used
|
constant cycle_wrote: integer := 6; -- not actually used
|
constant cycle_skip: integer := 9;
|
constant cycle_skip: integer := 9;
|
|
|
COMPONENT onecomplement_adder
|
COMPONENT onecomplement_adder
|
PORT(
|
PORT(
|
A : IN std_logic_vector(0 to 17);
|
A : IN std_logic_vector(0 to 17);
|
B : IN std_logic_vector(0 to 17);
|
B : IN std_logic_vector(0 to 17);
|
CI : IN std_logic;
|
CI : IN std_logic;
|
SUM : OUT std_logic_vector(0 to 17);
|
SUM : OUT std_logic_vector(0 to 17);
|
OV : OUT std_logic;
|
OV : OUT std_logic;
|
CSUM : OUT std_logic_vector(0 to 17)
|
CSUM : OUT std_logic_vector(0 to 17)
|
);
|
);
|
END COMPONENT;
|
END COMPONENT;
|
|
|
COMPONENT pdp1rotshift
|
COMPONENT pdp1rotshift
|
PORT(
|
PORT(
|
ac : IN std_logic_vector(0 to 17);
|
ac : IN std_logic_vector(0 to 17);
|
io : IN std_logic_vector(0 to 17);
|
io : IN std_logic_vector(0 to 17);
|
right : IN std_logic;
|
right : IN std_logic;
|
shift : IN std_logic;
|
shift : IN std_logic;
|
words : IN std_logic_vector(0 to 1);
|
words : IN std_logic_vector(0 to 1);
|
acout : OUT std_logic_vector(0 to 17);
|
acout : OUT std_logic_vector(0 to 17);
|
ioout : OUT std_logic_vector(0 to 17)
|
ioout : OUT std_logic_vector(0 to 17)
|
);
|
);
|
END COMPONENT;
|
END COMPONENT;
|
signal rotshift_ac, rotshift_io: word;
|
signal rotshift_ac, rotshift_io: word;
|
signal rotshift_right, rotshift_shift: std_logic;
|
signal rotshift_right, rotshift_shift: std_logic;
|
signal rotshift_words: std_logic_vector(0 to 1);
|
signal rotshift_words: std_logic_vector(0 to 1);
|
signal add_a, add_b, add_sum, add_csum, dis_term: word;
|
signal add_a, add_b, add_sum, add_csum, dis_term: word;
|
signal add_ci, add_ov: std_logic;
|
signal add_ci, add_ov: std_logic;
|
signal skipcond: std_logic; -- skip condition for op_skip
|
signal skipcond: std_logic; -- skip condition for op_skip
|
signal ac_eq_mb : boolean;
|
signal ac_eq_mb : boolean;
|
-- purpose: value of a sense switch if n valid, else '1'
|
-- purpose: value of a sense switch if n valid, else '1'
|
function sense_or_one (
|
function sense_or_one (
|
n : integer; -- which sense flag
|
n : integer; -- which sense flag
|
sense_sw : std_logic_vector(1 to 6)) -- sense switches
|
sense_sw : std_logic_vector(1 to 6)) -- sense switches
|
return std_logic is
|
return std_logic is
|
begin -- sense_or_one
|
begin -- sense_or_one
|
for i in 1 to 6 loop
|
for i in 1 to 6 loop
|
if n=i then
|
if n=i then
|
return sense_sw(n);
|
return sense_sw(n);
|
end if;
|
end if;
|
end loop; -- i
|
end loop; -- i
|
return '1';
|
return '1';
|
end sense_or_one;
|
end sense_or_one;
|
begin
|
begin
|
AWAKE <= '0' when IOWAIT or RESET='1' or HALT else '1';
|
AWAKE <= '0' when IOWAIT or RESET='1' or HALT else '1';
|
|
|
Inst_pdp1rotshift: pdp1rotshift PORT MAP(
|
Inst_pdp1rotshift: pdp1rotshift PORT MAP(
|
ac => AC, -- shift operation is read from memory
|
ac => AC, -- shift operation is read from memory
|
io => IO,
|
io => IO,
|
right => rotshift_right,
|
right => rotshift_right,
|
shift => rotshift_shift,
|
shift => rotshift_shift,
|
words => rotshift_words,
|
words => rotshift_words,
|
acout => rotshift_ac,
|
acout => rotshift_ac,
|
ioout => rotshift_io
|
ioout => rotshift_io
|
);
|
);
|
with op select
|
with op select
|
rotshift_right <= '1' when op_mus,
|
rotshift_right <= '1' when op_mus,
|
'0' when op_dis,
|
'0' when op_dis,
|
M_DO(5) when others;
|
M_DO(5) when others;
|
with op select
|
with op select
|
rotshift_shift <= '1' when op_mus,
|
rotshift_shift <= '1' when op_mus,
|
'-' when op_dis,
|
'-' when op_dis,
|
M_DO(6) when others;
|
M_DO(6) when others;
|
with op select
|
with op select
|
rotshift_words <= "11" when op_mus,
|
rotshift_words <= "11" when op_mus,
|
"11" when op_dis,
|
"11" when op_dis,
|
M_DO(7 to 8) when others;
|
M_DO(7 to 8) when others;
|
|
|
Inst_onecomplement_adder: onecomplement_adder PORT MAP(
|
Inst_onecomplement_adder: onecomplement_adder PORT MAP(
|
A => add_a,
|
A => add_a,
|
B => add_b,
|
B => add_b,
|
CI => add_ci,
|
CI => add_ci,
|
SUM => add_sum,
|
SUM => add_sum,
|
OV => add_ov,
|
OV => add_ov,
|
CSUM => add_csum
|
CSUM => add_csum
|
);
|
);
|
-- we use this same adder for addition, subtraction, indexing and multiply/divide step
|
-- we use this same adder for addition, subtraction, indexing and multiply/divide step
|
with io(17) select
|
with io(17) select
|
dis_term <= not MB when '1',
|
dis_term <= not MB when '1',
|
MB when '0',
|
MB when '0',
|
(others=>'-') when others;
|
(others=>'-') when others;
|
with op select
|
with op select
|
add_a <= o"000001" when op_idx | op_isp,
|
add_a <= o"000001" when op_idx | op_isp,
|
AC when op_add|op_mus|op_dis,
|
AC when op_add|op_mus|op_dis,
|
(others=>'-') when others;
|
(others=>'-') when others;
|
with op select
|
with op select
|
add_b <= MB when op_add|op_idx|op_isp|op_mus,
|
add_b <= MB when op_add|op_idx|op_isp|op_mus,
|
not MB when op_sub,
|
not MB when op_sub,
|
dis_term when op_dis,
|
dis_term when op_dis,
|
(others=>'-') when others;
|
(others=>'-') when others;
|
add_ci <= '1' when op=op_dis and io(17)='0' else
|
add_ci <= '1' when op=op_dis and io(17)='0' else
|
'0';
|
'0';
|
|
|
M_DI <= MB;
|
M_DI <= MB;
|
ac_eq_mb <= AC=MB;
|
ac_eq_mb <= AC=MB;
|
|
|
skipcond <= '1' when (
|
skipcond <= '1' when (
|
(sza='1' and AC=o"00_0000") or -- accumulator zero
|
(sza='1' and AC=o"00_0000") or -- accumulator zero
|
(spa='1' and AC(0)='0') or -- accumulator positive
|
(spa='1' and AC(0)='0') or -- accumulator positive
|
(sma='1' and AC(0)='1') or -- accumulator negative
|
(sma='1' and AC(0)='1') or -- accumulator negative
|
(szo='1' and OV='0') or -- zero overflow
|
(szo='1' and OV='0') or -- zero overflow
|
(spi='1' and IO(0)='0') or -- positive IO register
|
(spi='1' and IO(0)='0') or -- positive IO register
|
(MB(12 to 14)=o"7" and sw_sense=o"00") or -- all sense switches 0
|
(MB(12 to 14)=o"7" and sw_sense=o"00") or -- all sense switches 0
|
(sense_or_one(to_integer(unsigned(MB(12 to 14))),sw_sense)='0') or
|
(sense_or_one(to_integer(unsigned(MB(12 to 14))),sw_sense)='0') or
|
false -- so all lines above end with or
|
false -- so all lines above end with or
|
) else '0';
|
) else '0';
|
|
|
process (CLK, RESET)
|
process (CLK, RESET)
|
variable idx: unsigned(0 to 17);
|
variable idx: unsigned(0 to 17);
|
variable tmp_w: word;
|
variable tmp_w: word;
|
begin
|
begin
|
if RESET='1' then -- asynchronous reset
|
if RESET='1' then -- asynchronous reset
|
AC <= (others => '0');
|
AC <= (others => '0');
|
IO <= (others => '0');
|
IO <= (others => '0');
|
PC <= o"0000";
|
PC <= o"0000";
|
OV <= '0';
|
OV <= '0';
|
PF <= (others => '0');
|
PF <= (others => '0');
|
-- memory control
|
-- memory control
|
MW <= '0';
|
MW <= '0';
|
MA <= o"0000";
|
MA <= o"0000";
|
-- reset our internal state
|
-- reset our internal state
|
op <= o"00";
|
op <= o"00";
|
IOWAIT <= false;
|
IOWAIT <= false;
|
IOT <= (others => '0');
|
IOT <= (others => '0');
|
cycletype <= load_instruction;
|
cycletype <= load_instruction;
|
cycle <= cycle_setup_read;
|
cycle <= cycle_setup_read;
|
elsif rising_edge(CLK) then
|
elsif rising_edge(CLK) then
|
if IO_set='1' then
|
if IO_set='1' then
|
IO <= IO_in;
|
IO <= IO_in;
|
end if;
|
end if;
|
IOT <= (others => '0'); -- ordinarily no io trigger pulse
|
IOT <= (others => '0'); -- ordinarily no io trigger pulse
|
-- Advance the cycle, unless we're halted
|
-- Advance the cycle, unless we're halted
|
if IOWAIT then
|
if IOWAIT then
|
if IODONE='1' then
|
if IODONE='1' then
|
IOWAIT <= false;
|
IOWAIT <= false;
|
end if;
|
end if;
|
end if;
|
end if;
|
-- pause during setup_read cycle for halt or iowait
|
-- pause during setup_read cycle for halt or iowait
|
if not ((IOWAIT or HALT) and (cycle=cycle_setup_read)) then
|
if not ((IOWAIT or HALT) and (cycle=cycle_setup_read)) then
|
if cycle=9 then
|
if cycle=9 then
|
cycle <= 0;
|
cycle <= 0;
|
else
|
else
|
cycle <= cycle+1;
|
cycle <= cycle+1;
|
end if;
|
end if;
|
|
|
-- common logic for signals
|
-- common logic for signals
|
if cycle=cycle_setup_write then
|
if cycle=cycle_setup_write then
|
MW <= '1';
|
MW <= '1';
|
else
|
else
|
MW <= '0';
|
MW <= '0';
|
end if;
|
end if;
|
if (op=op_rotshiftr or op=op_rotshiftl) then
|
if (op=op_rotshiftr or op=op_rotshiftl) then
|
case cycle is
|
case cycle is
|
when 9 => -- don't shift on cycle 9
|
when 9 => -- don't shift on cycle 9
|
when others =>
|
when others =>
|
if MB(9+cycle)='1' then
|
if MB(9+cycle)='1' then
|
AC <= rotshift_ac; -- perform rotate/shift instructions
|
AC <= rotshift_ac; -- perform rotate/shift instructions
|
if IO_set/='1' then
|
if IO_set/='1' then
|
IO <= rotshift_io;
|
IO <= rotshift_io;
|
end if;
|
end if;
|
end if;
|
end if;
|
end case;
|
end case;
|
end if;
|
end if;
|
|
|
case cycle is
|
case cycle is
|
when cycle_read => -- have read something from memory
|
when cycle_read => -- have read something from memory
|
MB <= M_DO;
|
MB <= M_DO;
|
case cycletype is
|
case cycletype is
|
when load_instruction => -- it's our next instruction
|
when load_instruction => -- it's our next instruction
|
op <= M_DO(0 to 5);
|
op <= M_DO(0 to 5);
|
if op/=op_xct then -- indirect execution
|
if op/=op_xct then -- indirect execution
|
PC<=PC+1;
|
PC<=std_logic_vector(unsigned(PC)+1);
|
end if;
|
end if;
|
when load_indirect => -- completing an indirect instruction
|
when load_indirect => -- completing an indirect instruction
|
ib <= M_DO(5); -- update indirection bit
|
ib <= M_DO(5); -- update indirection bit
|
when others => -- data access cycle
|
when others => -- data access cycle
|
end case;
|
end case;
|
when cycle_skip =>
|
when cycle_skip =>
|
if ((op=op_skip or op=op_skipi) and (skipcond xor ib)='1') or
|
if ((op=op_skip or op=op_skipi) and (skipcond xor ib)='1') or
|
(op=op_isp and cycletype=store_data and AC(0)='0') or
|
(op=op_isp and cycletype=store_data and AC(0)='0') or
|
(op=op_sas and cycletype=load_data and ac_eq_mb) or
|
(op=op_sas and cycletype=load_data and ac_eq_mb) or
|
(op=op_sad and cycletype=load_data and not ac_eq_mb) or
|
(op=op_sad and cycletype=load_data and not ac_eq_mb) or
|
FALSE then -- increase PC an extra time
|
FALSE then -- increase PC an extra time
|
PC <= PC+1;
|
PC <= std_logic_vector(unsigned(PC)+1);
|
end if;
|
end if;
|
if (op=op_skip or op=op_skipi) and szo='1' then
|
if (op=op_skip or op=op_skipi) and szo='1' then
|
OV <= '0'; -- clear overflow after checking it
|
OV <= '0'; -- clear overflow after checking it
|
end if;
|
end if;
|
when cycle_setup_read => -- set up the memory address
|
when cycle_setup_read => -- set up the memory address
|
case cycletype is
|
case cycletype is
|
when load_instruction|load_indirect =>
|
when load_instruction|load_indirect =>
|
case (op) is
|
case (op) is
|
-- memory loading instructions - will execute after loading data
|
-- memory loading instructions - will execute after loading data
|
when op_sas|op_sad|op_lac|op_lio|op_and|op_xor|op_ior|op_add|
|
when op_sas|op_sad|op_lac|op_lio|op_and|op_xor|op_ior|op_add|
|
op_sub|op_idx|op_isp|op_mus|op_dis =>
|
op_sub|op_idx|op_isp|op_mus|op_dis =>
|
cycletype <= load_data;
|
cycletype <= load_data;
|
MA <= y;
|
MA <= y;
|
when op_xct => -- load specified instruction, do not change PC
|
when op_xct => -- load specified instruction, do not change PC
|
cycletype <= load_instruction;
|
cycletype <= load_instruction;
|
MA <= y;
|
MA <= y;
|
when op_dac|op_dap|op_dip|op_dio|op_dzm => -- deposit instructions
|
when op_dac|op_dap|op_dip|op_dio|op_dzm => -- deposit instructions
|
cycletype <= store_data;
|
cycletype <= store_data;
|
MA <= y;
|
MA <= y;
|
when op_jmp|op_jsp|op_cal|op_jda => -- jumping instructions
|
when op_jmp|op_jsp|op_cal|op_jda => -- jumping instructions
|
if op/=op_jmp then
|
if op/=op_jmp then
|
AC(0) <= OV;
|
AC(0) <= OV;
|
AC(1 to 5) <= (others => '0'); -- extended PC
|
AC(1 to 5) <= (others => '0'); -- extended PC
|
AC(6 to 17) <= std_logic_vector(PC);
|
AC(6 to 17) <= std_logic_vector(PC);
|
end if;
|
end if;
|
if op=op_cal or op=op_jda then
|
if op=op_cal or op=op_jda then
|
if op=op_cal then
|
if op=op_cal then
|
PC <= o"0101";
|
PC <= o"0101";
|
MA <= o"0100";
|
MA <= o"0100";
|
else
|
else
|
PC <= unsigned(y)+1;
|
PC <= std_logic_vector(unsigned(y)+1);
|
MA <= y;
|
MA <= y;
|
end if;
|
end if;
|
cycletype <= store_data;
|
cycletype <= store_data;
|
else
|
else
|
MA <= y;
|
MA <= y;
|
PC <= unsigned(y);
|
PC <= y;
|
cycletype <= load_instruction;
|
cycletype <= load_instruction;
|
end if;
|
end if;
|
when op_skipi|op_rotshiftr|op_lawm|op_iot_nw =>
|
when op_skipi|op_rotshiftr|op_lawm|op_iot_nw =>
|
-- instructions with IB set, yet are immediate
|
-- instructions with IB set, yet are immediate
|
MA <= std_logic_vector(PC);
|
MA <= std_logic_vector(PC);
|
cycletype <= load_instruction;
|
cycletype <= load_instruction;
|
when others => -- most instructions are followed by the next instruction
|
when others => -- most instructions are followed by the next instruction
|
if ib='1' then -- handle indirection bit
|
if ib='1' then -- handle indirection bit
|
MA <= y;
|
MA <= y;
|
cycletype <= load_indirect;
|
cycletype <= load_indirect;
|
else
|
else
|
MA <= std_logic_vector(PC);
|
MA <= std_logic_vector(PC);
|
cycletype <= load_instruction;
|
cycletype <= load_instruction;
|
end if;
|
end if;
|
end case; -- end of by-instruction memory setup
|
end case; -- end of by-instruction memory setup
|
when others => -- have done data load/store
|
when others => -- have done data load/store
|
MA <= std_logic_vector(PC);
|
MA <= std_logic_vector(PC);
|
cycletype <= load_instruction;
|
cycletype <= load_instruction;
|
end case;
|
end case;
|
when cycle_execute =>
|
when cycle_execute =>
|
-- execute common instructions - instr or operand has been read
|
-- execute common instructions - instr or operand has been read
|
case cycletype is
|
case cycletype is
|
when load_instruction|load_indirect => -- new instr,
|
when load_instruction|load_indirect => -- new instr,
|
case op is
|
case op is
|
when op_law => -- load accumulator immediate
|
when op_law => -- load accumulator immediate
|
AC(0 to 5) <= (others => '0');
|
AC(0 to 5) <= (others => '0');
|
AC(6 to 17) <= y;
|
AC(6 to 17) <= y;
|
when op_lawm => -- load accumulator immediate negative
|
when op_lawm => -- load accumulator immediate negative
|
AC(0 to 5) <= (others => '1');
|
AC(0 to 5) <= (others => '1');
|
AC(6 to 17) <= not y;
|
AC(6 to 17) <= not y;
|
when op_opr => -- operate group
|
when op_opr => -- operate group
|
if cli='1' and IO_set/='1' then IO <= o"00_0000"; end if;
|
if cli='1' and IO_set/='1' then IO <= o"00_0000"; end if;
|
if hlt='1' then HALT <= TRUE; end if; -- HALT
|
if hlt='1' then HALT <= TRUE; end if; -- HALT
|
if cla='1' then
|
if cla='1' then
|
tmp_w := (others => '0');
|
tmp_w := (others => '0');
|
else
|
else
|
tmp_w := AC;
|
tmp_w := AC;
|
end if;
|
end if;
|
if lat='1' then tmp_w := tmp_w or sw_testw; end if;
|
if lat='1' then tmp_w := tmp_w or sw_testw; end if;
|
if lap='1' then -- or AC with PC and OV
|
if lap='1' then -- or AC with PC and OV
|
tmp_w(6 to 17) := tmp_w(6 to 17) or std_logic_vector(PC);
|
tmp_w(6 to 17) := tmp_w(6 to 17) or std_logic_vector(PC);
|
tmp_w(0) := tmp_w(0) or OV;
|
tmp_w(0) := tmp_w(0) or OV;
|
end if;
|
end if;
|
if cma='1' then tmp_w := not tmp_w; end if;
|
if cma='1' then tmp_w := not tmp_w; end if;
|
AC <= tmp_w;
|
AC <= tmp_w;
|
for j in 1 to 6 loop
|
for j in 1 to 6 loop
|
if unsigned(flag_which)=j or unsigned(flag_which)=7 then
|
if unsigned(flag_which)=j or unsigned(flag_which)=7 then
|
PF(j) <= flag_setto; -- set or clear program flags
|
PF(j) <= flag_setto; -- set or clear program flags
|
end if;
|
end if;
|
end loop;
|
end loop;
|
when op_rotshiftl|op_rotshiftr =>
|
when op_rotshiftl|op_rotshiftr =>
|
-- handled in a separate block
|
-- handled in a separate block
|
when op_skip|op_skipi =>
|
when op_skip|op_skipi =>
|
-- inverted skip is not documented in 1960 manual.
|
-- inverted skip is not documented in 1960 manual.
|
-- it does occur in PDP-1B emulator and 1963 manual.
|
-- it does occur in PDP-1B emulator and 1963 manual.
|
-- all skips are handled in skip phase, for no
|
-- all skips are handled in skip phase, for no
|
-- real reason
|
-- real reason
|
when op_iot|op_iot_nw => -- I/O transfer
|
when op_iot|op_iot_nw => -- I/O transfer
|
-- Java emulator Spacewar! binary supports:
|
-- Java emulator Spacewar! binary supports:
|
-- typewriter sequence break input,
|
-- typewriter sequence break input,
|
-- display on ordinary display (7),
|
-- display on ordinary display (7),
|
-- reading of controls using undocumented device 11,
|
-- reading of controls using undocumented device 11,
|
-- should load 4 bits of button controls in low and
|
-- should load 4 bits of button controls in low and
|
-- high ends of the word
|
-- high ends of the word
|
-- and does display 3 display commands (disabled point?).
|
-- and does display 3 display commands (disabled point?).
|
-- It uses nowait+pulse, nowait, and wait+noop modes, so waiting
|
-- It uses nowait+pulse, nowait, and wait+noop modes, so waiting
|
-- has to be implemented.
|
-- has to be implemented.
|
IODOPULSE <= MB(5) xor MB(6); -- generate an IODONE for this event
|
IODOPULSE <= MB(5) xor MB(6); -- generate an IODONE for this event
|
IOWAIT <= IB='1';
|
IOWAIT <= IB='1';
|
IOT(to_integer(unsigned(MB(12 to 17)))) <= '1';
|
IOT(to_integer(unsigned(MB(12 to 17)))) <= '1';
|
|
|
when others =>
|
when others =>
|
if ib='1' then -- likely turns into a valid op once IB is cleared
|
if ib='1' then -- likely turns into a valid op once IB is cleared
|
cycletype <= load_indirect;
|
cycletype <= load_indirect;
|
end if;
|
end if;
|
end case; -- end of instruction check in execute phase
|
end case; -- end of instruction check in execute phase
|
when load_data => -- loaded data for loading instruction
|
when load_data => -- loaded data for loading instruction
|
case (op) is
|
case (op) is
|
when op_sas|op_sad => -- handled in skip phase
|
when op_sas|op_sad => -- handled in skip phase
|
when op_lac =>
|
when op_lac =>
|
AC <= M_DO;
|
AC <= M_DO;
|
when op_idx|op_isp =>
|
when op_idx|op_isp =>
|
AC <= add_sum;
|
AC <= add_sum;
|
MB <= add_sum;
|
MB <= add_sum;
|
when op_lio =>
|
when op_lio =>
|
if IO_set/='1' then
|
if IO_set/='1' then
|
IO <= MB;
|
IO <= MB;
|
end if;
|
end if;
|
when op_and =>
|
when op_and =>
|
AC <= AC and MB;
|
AC <= AC and MB;
|
when op_xor =>
|
when op_xor =>
|
AC <= AC xor MB;
|
AC <= AC xor MB;
|
when op_ior =>
|
when op_ior =>
|
AC <= AC or MB;
|
AC <= AC or MB;
|
when op_add =>
|
when op_add =>
|
AC <= add_csum; -- no negative 0
|
AC <= add_csum; -- no negative 0
|
OV <= OV or add_ov;
|
OV <= OV or add_ov;
|
when op_sub =>
|
when op_sub =>
|
AC <= add_sum;
|
AC <= add_sum;
|
OV <= OV or add_ov;
|
OV <= OV or add_ov;
|
-- Multiply Step and Divide Step are the same opcode as Multiply and Divide.
|
-- Multiply Step and Divide Step are the same opcode as Multiply and Divide.
|
-- There's no reasonable way for the CPU to determine which a program wants.
|
-- There's no reasonable way for the CPU to determine which a program wants.
|
-- when op_mul => -- multiply originally takes 3-5 cycles. this one takes 2.
|
-- when op_mul => -- multiply originally takes 3-5 cycles. this one takes 2.
|
-- tmp_w := AC;
|
-- tmp_w := AC;
|
-- tmp_w2 := M_DO;
|
-- tmp_w2 := M_DO;
|
-- if tmp_w(0)='1' then tmp_w:=not tmp_w; end if;
|
-- if tmp_w(0)='1' then tmp_w:=not tmp_w; end if;
|
-- if tmp_w2(0)='1' then tmp_w2:=not tmp_w2; end if;
|
-- if tmp_w2(0)='1' then tmp_w2:=not tmp_w2; end if;
|
-- product := tmp_w(1 to 17)*tmp_w2(1 to 17);
|
-- product := tmp_w(1 to 17)*tmp_w2(1 to 17);
|
-- if AC(0)/=M_DO(0) then
|
-- if AC(0)/=M_DO(0) then
|
-- product:=not product; -- preserve sign
|
-- product:=not product; -- preserve sign
|
-- AC(0)<='1';
|
-- AC(0)<='1';
|
-- IO(0)<='1';
|
-- IO(0)<='1';
|
-- else
|
-- else
|
-- AC(0)<='0';
|
-- AC(0)<='0';
|
-- IO(0)<='0';
|
-- IO(0)<='0';
|
-- end if;
|
-- end if;
|
-- AC(1 to 17)<=product(0 to 16);
|
-- AC(1 to 17)<=product(0 to 16);
|
-- IO(1 to 17)<=product(17 to 33);
|
-- IO(1 to 17)<=product(17 to 33);
|
when op_mus => -- PDP-1B multiply step
|
when op_mus => -- PDP-1B multiply step
|
if IO(17)='1' then
|
if IO(17)='1' then
|
AC <= add_csum;
|
AC <= add_csum;
|
end if;
|
end if;
|
-- continued in next cycle
|
-- continued in next cycle
|
when op_dis => -- PDP-1B divide step
|
when op_dis => -- PDP-1B divide step
|
AC <= rotshift_ac;
|
AC <= rotshift_ac;
|
IO(0 to 16) <= rotshift_io(0 to 16);
|
IO(0 to 16) <= rotshift_io(0 to 16);
|
IO(17) <= not AC(0);
|
IO(17) <= not AC(0);
|
-- continued in next cycle
|
-- continued in next cycle
|
when others =>
|
when others =>
|
-- can't happen, see cases leading to load_data.
|
-- can't happen, see cases leading to load_data.
|
end case; -- end of load ops, execute cycle
|
end case; -- end of load ops, execute cycle
|
when store_data =>
|
when store_data =>
|
case op is
|
case op is
|
when op_dac =>
|
when op_dac =>
|
MB <= AC;
|
MB <= AC;
|
when op_dap =>
|
when op_dap =>
|
--MB(0 to 5) <= MB(0 to 5);
|
--MB(0 to 5) <= MB(0 to 5);
|
MB(6 to 17) <= AC(6 to 17);
|
MB(6 to 17) <= AC(6 to 17);
|
when op_dip =>
|
when op_dip =>
|
MB(0 to 5) <= AC(0 to 5);
|
MB(0 to 5) <= AC(0 to 5);
|
--MB(6 to 17) <= MB(6 to 17);
|
--MB(6 to 17) <= MB(6 to 17);
|
when op_dio =>
|
when op_dio =>
|
MB <= IO;
|
MB <= IO;
|
when op_dzm =>
|
when op_dzm =>
|
MB <= o"00_0000";
|
MB <= o"00_0000";
|
when others => -- others should not occur
|
when others => -- others should not occur
|
end case;
|
end case;
|
end case; -- end of cycletype cases for execute cycle
|
end case; -- end of cycletype cases for execute cycle
|
-- FIXME more to fix here
|
-- FIXME more to fix here
|
when cycle_execute+1 =>
|
when cycle_execute+1 =>
|
case op is -- multiply, divide and I/O use two stages
|
case op is -- multiply, divide and I/O use two stages
|
when op_mus =>
|
when op_mus =>
|
AC <= rotshift_ac;
|
AC <= rotshift_ac;
|
if IO_set/='1' then
|
if IO_set/='1' then
|
IO <= rotshift_io;
|
IO <= rotshift_io;
|
end if;
|
end if;
|
when op_dis =>
|
when op_dis =>
|
AC <= add_csum;
|
AC <= add_csum;
|
when others => -- note: rotshift uses 9 cycles
|
when others => -- note: rotshift uses 9 cycles
|
end case;
|
end case;
|
when others =>
|
when others =>
|
end case;
|
end case;
|
end if;
|
end if;
|
end if;
|
end if;
|
end process;
|
end process;
|
end Behavioral;
|
end Behavioral;
|
|
|
|
|