added basic cpu; simulates correctly
This commit is contained in:
36
cpu/Makefile
Normal file
36
cpu/Makefile
Normal file
@@ -0,0 +1,36 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
IVERILOG=iverilog
|
||||
VERILOG=cver
|
||||
|
||||
#all: igo
|
||||
all: go
|
||||
|
||||
pdp8.o: pdp8.v
|
||||
$(VERILOG) -o pdp8.o $(PARTS)
|
||||
|
||||
irun: $(PARTS) run.v pdp8.v
|
||||
$(IVERILOG) -o run run.v
|
||||
|
||||
crun: $(PARTS) run.v pdp8.v
|
||||
$(VERILOG) +change_port_type run.v
|
||||
echo "exit 0" > run
|
||||
chmod +x run
|
||||
|
||||
go: crun
|
||||
./run
|
||||
|
||||
igo: irun
|
||||
./run
|
||||
|
||||
display:
|
||||
./maketraces.sh >traces
|
||||
gtkwave caddr.vcd traces
|
||||
|
||||
snapshot:
|
||||
(suffix=`date +%Y%m%d`; \
|
||||
cd ..; \
|
||||
tar cfz pdp8_verilog_$$suffix.tar.gz pdp8/*.v pdp8/Makefile ; \
|
||||
mv pdp8_verilog* ~/html/heeltoe/html/download/pdp8)
|
||||
75
cpu/NOTES.txt
Normal file
75
cpu/NOTES.txt
Normal file
@@ -0,0 +1,75 @@
|
||||
DEC-8I-H8NA-D KT8/I Time-Sharing Option Functional Description
|
||||
DEC-8I-HOCA-D KE8/I Extended Arithmetic Element
|
||||
|
||||
interrupt occurs here
|
||||
|
||||
510 00516 7141 CLL CIA
|
||||
511 00517 1155 TAD LASTV
|
||||
512 00520 7630 SZL CLA
|
||||
513 00521 4526 ERROR /STORAGE FILLED BY PUSHDOWN LIST
|
||||
|
||||
|
||||
6000 PWS The contents of the AC register are loaded into the SW
|
||||
register. This instruction performs no operation if the IM
|
||||
flag is 1.
|
||||
|
||||
6001 ION The IE flag is set to 1, enabling interrupts, after a one
|
||||
instruction delay.
|
||||
|
||||
6002 IOF The IE flag is set to 0, disabling interrupts.
|
||||
|
||||
6003 PSI Skip if the IE flag is 1. This instruction performs no
|
||||
operation if the IM flag is 1.
|
||||
|
||||
6004 PAI The contents of the IB register are loaded into the IF
|
||||
register, and the II flag is set to 0, just as if a JMP or
|
||||
JMS was executed when the IM flag is 1. This instruction
|
||||
performs no operation if the IM flag is 1.
|
||||
|
||||
6005 PAS The contents of the DF register are loaded into bits
|
||||
[03..05] of the SF register, and the contents of the IF
|
||||
register are loaded into bits [00..02] of the SF register,
|
||||
just as they would be loaded on an interrupt. This
|
||||
instruction performs no operation of the IM flag is 1.
|
||||
|
||||
6006 PDX The DM flag is set to 1 for the execution of the next
|
||||
instruction. This instruction executes normally if IM is 1,
|
||||
but since DM is 1 anytime IM is 1, it effectively performs
|
||||
no operation.
|
||||
|
||||
6007 PEX The IM flag is set to 1, switching the CPU into normal mode,
|
||||
after a one instruction delay. This instruction executes
|
||||
notmally if IM is 1, but effectively performs no operation.
|
||||
|
||||
62x1 CDF Bits [06..08] of the instruction are loaded into the DF
|
||||
register.
|
||||
62x2 CIF Bits [06..08] of the instruction are loaded into the IB
|
||||
register, and the II flag is set to 1. The contents of the
|
||||
IB register will be loaded into the IF register, and the II
|
||||
flag will be set to 0, when the next JMP or JMS instruction
|
||||
is executed. The reload happens after any instruction and/or
|
||||
indirect address words are read, but before the JMS writes
|
||||
its return address; the return address is written to the new
|
||||
instruction field. The II flag blocks interrupts between the
|
||||
CIF and the JMP/JMS (page 53 of the 1970 small computer
|
||||
handbook describes the effect of the II flag, even though
|
||||
the flag itself is not described clearly).
|
||||
|
||||
6214 RDF The contents of the DF register are logically ORed into
|
||||
bits [06..08] of the AC register.
|
||||
|
||||
6224 RIF The contents of the IF register are logically ORed into
|
||||
bits [06..08] of the AC register.
|
||||
|
||||
6234 RIB The contents of the SF register are logically ORed into
|
||||
bits [06..11] of the AC register; if the AC register is
|
||||
initially 0, then bits [09..11] of the AC register get the
|
||||
value which was in the DF register at the time of the last
|
||||
interrupt, and bits [06..08] of the AC register get the
|
||||
value which was in the IF register at the time of the last
|
||||
interrupt.
|
||||
|
||||
6244 RMF Bits [03..05] of the SF register are loaded into the DF
|
||||
register, bits [00..02] of the SF register are loaded into
|
||||
the IB register, and the II flag is set to 1. The next JMP
|
||||
or JMS instruction completes the restore.
|
||||
1
cpu/README
Normal file
1
cpu/README
Normal file
@@ -0,0 +1 @@
|
||||
focal.v -cd win
|
||||
2740
cpu/focal.v
Normal file
2740
cpu/focal.v
Normal file
File diff suppressed because it is too large
Load Diff
1
cpu/maketraces.sh
Executable file
1
cpu/maketraces.sh
Executable file
@@ -0,0 +1 @@
|
||||
#!/bin/sh
|
||||
728
cpu/pdp8.v
Normal file
728
cpu/pdp8.v
Normal file
@@ -0,0 +1,728 @@
|
||||
// PDP-8
|
||||
// Based on descriptions in "Computer Engineering"
|
||||
// Nov 2005 Brad Parker brad@heeltoe.com
|
||||
// initial work; runs focal a bit
|
||||
// Dec 2006
|
||||
// cleaned up a little; now runs focal to prompt
|
||||
// moved i/o out to pdp8_io.v
|
||||
// added IF, DF, user mode
|
||||
//
|
||||
|
||||
// TODO:
|
||||
// fully implement extended memory (IF & DF), user mode (KT8/I)
|
||||
// add df32/rf08
|
||||
// 6000 pws ac <= switches
|
||||
//
|
||||
|
||||
//
|
||||
// Instruction format:
|
||||
//
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11
|
||||
// 11 10 9 8 7 6 5 4 3 2 1 0
|
||||
// |--op--|
|
||||
// 0 and
|
||||
// 1 tad
|
||||
// 2 isz
|
||||
// 3 dca
|
||||
// 4 jms
|
||||
// 5 jmp
|
||||
// 6 iot
|
||||
// 7 opr
|
||||
// 11 10 9 8 7 6 5 4 3 2 1 0
|
||||
// group 1
|
||||
// 0
|
||||
// |cla|clf| | |
|
||||
// | | |cma cml| |
|
||||
// |bsw 001 |
|
||||
// |ral 010 |
|
||||
// |rtl 011 |
|
||||
// |rar 100 |
|
||||
// |rtr 101 |
|
||||
// | |iac
|
||||
//
|
||||
// 11 10 9 8 7 6 5 4 3 2 1 0
|
||||
// group 2
|
||||
// 1 0
|
||||
// |sma|sza|snl|skp| |
|
||||
// |cla|
|
||||
// |osr|hlt
|
||||
//
|
||||
// group 3
|
||||
// 11 10 9 8 7 6 5 4 3 2 1 0
|
||||
// eae
|
||||
// 1 1
|
||||
// |cla|
|
||||
// |mqa|sca|mql|
|
||||
// |isn |
|
||||
//
|
||||
//
|
||||
|
||||
/*
|
||||
cpu states
|
||||
|
||||
F0 fetch
|
||||
F1 incr pc
|
||||
F2 write
|
||||
F3 dispatch
|
||||
|
||||
E0 read
|
||||
E1 decode
|
||||
E2 write
|
||||
E3 load
|
||||
|
||||
or
|
||||
|
||||
D0 read
|
||||
D1 wait
|
||||
D2 write
|
||||
D3 load
|
||||
|
||||
------
|
||||
|
||||
F0 fetch
|
||||
check for interrupt
|
||||
F1 incr pc
|
||||
if opr
|
||||
group 1 processing
|
||||
group 2 processing
|
||||
|
||||
if iot
|
||||
|
||||
incr pc or skip (incr pc by 2)
|
||||
|
||||
F2 write
|
||||
ma <= pc
|
||||
|
||||
F3 dispatch
|
||||
if opr
|
||||
group1 processing
|
||||
|
||||
if !opr && !iot
|
||||
possible defer
|
||||
|
||||
|
||||
D0
|
||||
mb <= memory
|
||||
D1
|
||||
*/
|
||||
|
||||
// extended memory
|
||||
//
|
||||
// 62n1 cdf change data field; df <= mb[5:3]
|
||||
// 62n2 cif change instruction field; if <= mb[5:3], after next jmp or jms
|
||||
// 6214 rdf read df into ac[5:3]
|
||||
// 6224 rif read if into ac[5:3]
|
||||
// 6234 rib read sf into ac[5:0], which is {if,df}
|
||||
// 6244 rmf restore memory field, sf => ib, df
|
||||
// (remember that on interrupt, sf <= {if,df})
|
||||
//
|
||||
|
||||
`include "pdp8_io.v"
|
||||
|
||||
module pdp8(clk, reset_n, switches);
|
||||
|
||||
input clk, reset_n;
|
||||
input[11:0] switches;
|
||||
|
||||
// memory buffer, holds data, instructions
|
||||
reg[11:0] mb;
|
||||
|
||||
// hold address of work in memory being accessed
|
||||
reg[14:0] ma;
|
||||
|
||||
// accumulator & link
|
||||
reg[11:0] ac;
|
||||
reg l;
|
||||
|
||||
// MQ
|
||||
reg [11:0] mq;
|
||||
|
||||
// program counter
|
||||
reg[11:0] pc;
|
||||
wire pc_incr, pc_skip;
|
||||
|
||||
// instruction register
|
||||
reg[2:0] ir;
|
||||
|
||||
// extended memory - instruction field & data field
|
||||
reg [2:0] IF;
|
||||
reg [2:0] DF;
|
||||
reg [2:0] IB;
|
||||
reg [5:0] SF;
|
||||
reg ib_pending;
|
||||
|
||||
// user mode
|
||||
reg UB;
|
||||
reg UF;
|
||||
|
||||
//
|
||||
wire[11:0] memory_bus;
|
||||
reg ram_we_n;
|
||||
|
||||
// processor state
|
||||
reg[3:0] state, next_state;
|
||||
|
||||
reg run;
|
||||
reg interrupt_enable;
|
||||
reg interrupt_cycle;
|
||||
reg interrupt_inhibit;
|
||||
reg interrupt_skip;
|
||||
|
||||
reg interrupt;
|
||||
reg user_interrupt;
|
||||
|
||||
reg io_pulse_1, io_pulse_2, io_pulse_4;
|
||||
|
||||
wire [11:0] io_data;
|
||||
wire io_data_avail;
|
||||
wire io_interrupt;
|
||||
wire io_skip;
|
||||
|
||||
wire[5:0] io_select;
|
||||
assign io_select = mb[8:3];
|
||||
|
||||
wire skip_condition;
|
||||
|
||||
wire fetch; // memory cycle to fetch instruction
|
||||
wire deferred; // memory cycle to get address of operand
|
||||
wire execute; // memory cycle to getch (store) operand and execute isn
|
||||
|
||||
assign {fetch, deferred, execute} =
|
||||
(state[3:2] == 2'b00) ? 3'b100 :
|
||||
(state[3:2] == 2'b01) ? 3'b010 :
|
||||
(state[3:2] == 2'b10) ? 3'b001 :
|
||||
3'b000 ;
|
||||
|
||||
wire and,tad,isz,dca,jms,jmp,iot,opr;
|
||||
|
||||
assign {and,tad,isz,dca,jms,jmp,iot,opr} =
|
||||
(ir == 3'b000) ? 8'b10000000 :
|
||||
(ir == 3'b001) ? 8'b01000000 :
|
||||
(ir == 3'b010) ? 8'b00100000 :
|
||||
(ir == 3'b011) ? 8'b00010000 :
|
||||
(ir == 3'b100) ? 8'b00001000 :
|
||||
(ir == 3'b101) ? 8'b00000100 :
|
||||
(ir == 3'b110) ? 8'b00000010 :
|
||||
8'b00000001 ;
|
||||
|
||||
|
||||
//-------------
|
||||
|
||||
parameter F0 = 4'b0000;
|
||||
parameter F1 = 4'b0001;
|
||||
parameter F2 = 4'b0010;
|
||||
parameter F3 = 4'b0011;
|
||||
|
||||
parameter D0 = 4'b0100;
|
||||
parameter D1 = 4'b0101;
|
||||
parameter D2 = 4'b0110;
|
||||
parameter D3 = 4'b0111;
|
||||
|
||||
parameter E0 = 4'b1000;
|
||||
parameter E1 = 4'b1001;
|
||||
parameter E2 = 4'b1010;
|
||||
parameter E3 = 4'b1011;
|
||||
|
||||
ram_4kx12 ram(
|
||||
.A(ma),
|
||||
.DI(mb),
|
||||
.DO(memory_bus),
|
||||
.CE_N(1'b0),
|
||||
.WE_N(ram_we_n));
|
||||
|
||||
/*
|
||||
* note: bit numbering is opposite that used in "Computer Engineering"
|
||||
*
|
||||
* F1
|
||||
* if opr
|
||||
* if MB[8] and !MB[0]
|
||||
* begin
|
||||
* if skip.conditions ^ MB[3]
|
||||
* pc <= pc + 2
|
||||
* if skip.conditions == MB[3]
|
||||
* pc <= pc + 1 next
|
||||
* if MB[7]
|
||||
* ac <= 0
|
||||
*/
|
||||
assign skip_condition =
|
||||
(mb[6] && ac[11]) ||
|
||||
(mb[5] && ac == 0) ||
|
||||
(mb[4] && l == 1);
|
||||
|
||||
assign pc_incr =
|
||||
(opr & !mb[8]) ||
|
||||
(opr && (mb[8] && !mb[0]) && (skip_condition == mb[3])) ||
|
||||
iot ||
|
||||
(!(opr || iot) && !interrupt_cycle);
|
||||
|
||||
assign pc_skip =
|
||||
(opr && (mb[8] && !mb[0]) && (skip_condition ^ mb[3])) ||
|
||||
(iot && (io_skip || interrupt_skip));
|
||||
// (iot && mb[0] && io_skip);
|
||||
|
||||
|
||||
pdp8_io io(.clk(clk), .reset_n(reset_n),
|
||||
.iot(iot), .state(state), .pc(pc), .ac(ac), .mb(mb),
|
||||
.io_select(io_select),
|
||||
.io_data_out(io_data),
|
||||
.io_data_avail(io_data_avail),
|
||||
.io_interrupt(io_interrupt),
|
||||
.io_skip(io_skip));
|
||||
|
||||
always @(reset_n)
|
||||
if (reset_n == 0)
|
||||
begin
|
||||
pc <= 0;
|
||||
ma <= 0;
|
||||
mb <= 0;
|
||||
ac <= 0;
|
||||
mq <= 0;
|
||||
l <= 0;
|
||||
ir <= 0;
|
||||
ram_we_n <= 1;
|
||||
state <= 0;
|
||||
next_state <= 0;
|
||||
run <= 1;
|
||||
interrupt_enable <= 0;
|
||||
interrupt_cycle <= 0;
|
||||
interrupt_inhibit <= 0;
|
||||
interrupt_skip <= 0;
|
||||
interrupt <= 0;
|
||||
user_interrupt <= 0;
|
||||
io_pulse_1 <= 0;
|
||||
io_pulse_2 <= 0;
|
||||
io_pulse_4 <= 0;
|
||||
IF <= 0;
|
||||
DF <= 0;
|
||||
IB <= 0;
|
||||
SF <= 0;
|
||||
UF <= 0;
|
||||
UB <= 0;
|
||||
ib_pending <= 0;
|
||||
end
|
||||
|
||||
initial
|
||||
begin
|
||||
ram_we_n = 1;
|
||||
end
|
||||
|
||||
/*
|
||||
* cpu state state machine
|
||||
*
|
||||
* clock next cpu state at rising edge of clock
|
||||
*/
|
||||
|
||||
always @(posedge clk)
|
||||
state <= #1 next_state;
|
||||
|
||||
always @(state)
|
||||
begin
|
||||
case (state)
|
||||
|
||||
// FETCH
|
||||
F0:
|
||||
begin
|
||||
interrupt_skip <= 0;
|
||||
|
||||
if (interrupt && interrupt_enable &&
|
||||
!interrupt_inhibit && !interrupt_cycle)
|
||||
begin
|
||||
$display("interrupt; %b %b %b",
|
||||
interrupt, interrupt_enable, interrupt_cycle);
|
||||
interrupt_cycle <= 1;
|
||||
interrupt <= 0;
|
||||
mb <= 12'o4000;
|
||||
ir <= 3'o4;
|
||||
SF <= {IF,DF};
|
||||
IF <= #1 3'b000;
|
||||
DF <= #1 3'b000;
|
||||
end
|
||||
else
|
||||
begin
|
||||
interrupt_cycle <= 0;
|
||||
mb <= memory_bus;
|
||||
ir <= memory_bus[11:9];
|
||||
end
|
||||
|
||||
next_state <= F1;
|
||||
end
|
||||
|
||||
F1:
|
||||
begin
|
||||
//$display("f1: io_skip %b", io_skip);
|
||||
//$display("f1 - ma %o, mb %o, ir %o", ma, mb, ir);
|
||||
if (opr)
|
||||
begin
|
||||
// group 1
|
||||
if (!mb[8])
|
||||
begin
|
||||
// $display("f1/g1 - ma %o, mb %o, ac %o l %o", ma, mb, ac,l);
|
||||
|
||||
if (mb[7]) ac <= 0;
|
||||
if (mb[6]) l <= 0;
|
||||
if (mb[5]) ac <= ~ac;
|
||||
if (mb[4]) l <= ~l;
|
||||
end
|
||||
|
||||
// group 2
|
||||
if (mb[8] & !mb[0])
|
||||
begin
|
||||
// $display("f1/g2 - ma %o, mb %o, sc %o, i %o s %o, ac %o l %o",
|
||||
// ma, mb, skip_condition, pc_incr, pc_skip, ac, l);
|
||||
|
||||
if (mb[7])
|
||||
ac <= 0;
|
||||
end
|
||||
|
||||
// group 3
|
||||
if (mb[8] & mb[0])
|
||||
begin
|
||||
if (mb[7]) ac <= 0;
|
||||
end
|
||||
end
|
||||
|
||||
if (iot)
|
||||
begin
|
||||
|
||||
$display("iot %t, run %b, state %b, pc %o, ir %o, mb %o, io_select %o",
|
||||
$time, run, state, pc, ir, mb, io_select);
|
||||
|
||||
if (mb[0]) io_pulse_1 <= 1;
|
||||
if (mb[1]) io_pulse_2 <= 1;
|
||||
if (mb[2]) io_pulse_4 <= 1;
|
||||
|
||||
case (io_select)
|
||||
6'b000000: // ION, IOF
|
||||
case (mb[2:0])
|
||||
3'b001: interrupt_enable <= 1;
|
||||
3'b010: interrupt_enable <= 0;
|
||||
3'b011: if (interrupt_enable) interrupt_skip <= 1;
|
||||
endcase // case(mb[2:0])
|
||||
|
||||
6'b010xxx: // CDF..RMF
|
||||
begin
|
||||
case (mb[2:0])
|
||||
3'b001: DF <= mb[5:3]; // CDF
|
||||
3'b010: // CIF
|
||||
begin
|
||||
IB <= mb[5:3];
|
||||
ib_pending <= 1;
|
||||
interrupt_inhibit <= 1;
|
||||
end
|
||||
3'b100:
|
||||
case (io_select[2:0])
|
||||
3'b001: ac <= { 6'b0, DF, 3'b0 }; // RDF
|
||||
3'b010: ac <= { 6'b0, IF, 3'b0 }; // RIF
|
||||
3'b011: ac <= { 6'b0, SF }; // RIB
|
||||
3'b100: begin // RMF
|
||||
IB <= SF[5:3];
|
||||
DF <= SF[2:0];
|
||||
end
|
||||
endcase // case(io_select[2:0])
|
||||
endcase // case(mb[2:0])
|
||||
end
|
||||
endcase
|
||||
|
||||
if (io_data_avail)
|
||||
begin
|
||||
ac <= io_data;
|
||||
end
|
||||
end
|
||||
|
||||
if (io_interrupt)
|
||||
interrupt <= 1;
|
||||
|
||||
//$display("f1 io_skip %b skip %b, incr %b", io_skip, pc_skip, pc_incr);
|
||||
if (pc_skip)
|
||||
pc <= pc + 2;
|
||||
else
|
||||
if (pc_incr)
|
||||
pc <= pc + 1;
|
||||
|
||||
next_state <= F2;
|
||||
end
|
||||
|
||||
F2:
|
||||
begin
|
||||
io_pulse_1 <= 0;
|
||||
io_pulse_2 <= 0;
|
||||
io_pulse_4 <= 0;
|
||||
|
||||
if (opr)
|
||||
begin
|
||||
ma <= {IF,pc};
|
||||
|
||||
// group 3
|
||||
if (mb[8] & mb[0])
|
||||
begin
|
||||
$display("f2/g3 - ma %o, mb %o, ac %o l %o", ma, mb, ac,l);
|
||||
|
||||
case ({mb[6:4]})
|
||||
3'b001: mq <= ac;
|
||||
3'b100: ac <= ac | mq;
|
||||
// 3'b101: tmq <= mq;
|
||||
3'b100: ac <= mq;
|
||||
3'b101: ac <= mq;
|
||||
endcase // case({mb[6:4]})
|
||||
end // if (mb[8] & mb[0])
|
||||
end // if (opr)
|
||||
|
||||
if (iot)
|
||||
ma <= {IF,pc};
|
||||
|
||||
if (!(opr || iot))
|
||||
begin
|
||||
ma[6:0] <= mb[6:0];
|
||||
if (!mb[7])
|
||||
ma[11:7] <= 0;
|
||||
end
|
||||
|
||||
next_state <= F3;
|
||||
end
|
||||
|
||||
F3:
|
||||
begin
|
||||
if (opr)
|
||||
begin
|
||||
// group 1
|
||||
if (!mb[8])
|
||||
begin
|
||||
if (mb[0]) // IAC
|
||||
{l,ac} <= {l,ac} + 1'b1;
|
||||
if (mb[3:1] == 3'b001) // BSW
|
||||
{l,ac} <= {l,ac[5:0],ac[11:6]};
|
||||
if (mb[3] && !mb[1]) // RAR
|
||||
{l,ac} <= {ac[0],l,ac[11:1]};
|
||||
if (mb[3] && mb[1]) // RTR
|
||||
{l,ac} <= {ac[1:0],l,ac[11:2]};
|
||||
if (mb[2] && !mb[1]) // RAL
|
||||
{l,ac} <= {ac[11:0],l};
|
||||
if (mb[2] && mb[1]) // RTL
|
||||
{l,ac} <= {ac[10:0],l,ac[11]};
|
||||
end
|
||||
|
||||
if (!UF)
|
||||
begin
|
||||
// group 2
|
||||
if (mb[8] & !mb[0])
|
||||
if (mb[2])
|
||||
ac <= ac | switches;
|
||||
if (mb[1])
|
||||
run <= 0;
|
||||
end
|
||||
|
||||
if (UF)
|
||||
begin
|
||||
// group 2 - user mode (halt & osr)
|
||||
if (mb[8] & !mb[0])
|
||||
if (mb[2])
|
||||
user_interrupt <= 1;
|
||||
if (mb[1])
|
||||
user_interrupt <= 1;
|
||||
end
|
||||
|
||||
// group 3
|
||||
if (mb[8] & mb[0])
|
||||
begin
|
||||
if (mb[7:4] == 4'b1101) mq <= 0;
|
||||
end
|
||||
|
||||
ir <= 0;
|
||||
mb <= 0;
|
||||
next_state <= F0;
|
||||
end
|
||||
|
||||
if (iot)
|
||||
begin
|
||||
ir <= 0;
|
||||
mb <= 0;
|
||||
next_state <= F0;
|
||||
end
|
||||
|
||||
if (!(opr || iot))
|
||||
begin
|
||||
if (!mb[8] & jmp)
|
||||
begin
|
||||
pc <= ma;
|
||||
ir <= 0;
|
||||
mb <= 0;
|
||||
next_state <= F0;
|
||||
end
|
||||
|
||||
if (mb[8])
|
||||
begin
|
||||
mb <= 0;
|
||||
next_state <= D0; /* defer */
|
||||
end
|
||||
|
||||
if (!mb[8] & !jmp)
|
||||
begin
|
||||
mb <= 0;
|
||||
next_state <= E0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// DEFER
|
||||
|
||||
D0:
|
||||
begin
|
||||
mb <= memory_bus;
|
||||
next_state <= D1;
|
||||
end
|
||||
|
||||
D1:
|
||||
begin
|
||||
// auto increment regs
|
||||
if (ma[11:3] == 8'h01)
|
||||
mb <= mb + 1;
|
||||
|
||||
next_state <= D2;
|
||||
end
|
||||
|
||||
D2:
|
||||
begin
|
||||
// write back
|
||||
if (ma[11:3] == 8'h01)
|
||||
ram_we_n <= 0;
|
||||
|
||||
ma <= #1 {DF,mb};
|
||||
next_state <= D3;
|
||||
end
|
||||
|
||||
D3:
|
||||
begin
|
||||
ram_we_n <= 1;
|
||||
|
||||
if (jmp)
|
||||
begin
|
||||
pc <= mb;
|
||||
ir <= 0;
|
||||
mb <= 0;
|
||||
next_state <= F0;
|
||||
end
|
||||
|
||||
if (!jmp)
|
||||
begin
|
||||
mb <= 0;
|
||||
next_state <= E0;
|
||||
end
|
||||
end
|
||||
|
||||
// EXECUTE
|
||||
|
||||
E0:
|
||||
begin
|
||||
mb <= memory_bus;
|
||||
next_state <= E1;
|
||||
end
|
||||
|
||||
E1:
|
||||
begin
|
||||
if (and)
|
||||
;
|
||||
|
||||
if (isz)
|
||||
begin
|
||||
mb <= mb + 1;
|
||||
if (mb == 12'b111111111111)
|
||||
pc <= pc + 1;
|
||||
end
|
||||
|
||||
if (dca)
|
||||
mb <= ac;
|
||||
|
||||
if (jms)
|
||||
mb <= pc;
|
||||
|
||||
next_state <= E2;
|
||||
end
|
||||
|
||||
E2:
|
||||
begin
|
||||
if (isz || dca || jms)
|
||||
ram_we_n <= 0;
|
||||
|
||||
if (~jms)
|
||||
ma <= #1 {IF,pc};
|
||||
|
||||
if (jms)
|
||||
ma <= #1 {ma[14:12], ma[11:0] + 1};
|
||||
|
||||
next_state <= E3;
|
||||
end
|
||||
|
||||
E3:
|
||||
begin
|
||||
ram_we_n <= 1;
|
||||
|
||||
if (and)
|
||||
ac <= ac & mb;
|
||||
|
||||
if (tad)
|
||||
{l,ac} <= ac + mb;
|
||||
|
||||
//if (tad) $display("tad - mb %o, ac %o l %o", mb, ac, l);
|
||||
|
||||
if (dca)
|
||||
ac <= 0;
|
||||
|
||||
if (jms)
|
||||
pc <= ma;
|
||||
|
||||
ir <= 0;
|
||||
next_state <= F0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
/* 4kx12 static ram */
|
||||
module ram_4kx12(A, DI, DO, CE_N, WE_N);
|
||||
|
||||
input[14:0] A;
|
||||
input[11:0] DI;
|
||||
input CE_N, WE_N;
|
||||
output[11:0] DO;
|
||||
|
||||
reg[11:0] ram [0:32767];
|
||||
integer i;
|
||||
|
||||
initial
|
||||
begin
|
||||
for (i = 0; i < 32768; i=i+1)
|
||||
ram[i] = 12'b0;
|
||||
|
||||
ram[15'o0000] = 12'o5177;
|
||||
ram[15'o0200] = 12'o7300;
|
||||
ram[15'o0201] = 12'o1300;
|
||||
ram[15'o0202] = 12'o1301;
|
||||
ram[15'o0203] = 12'o3302;
|
||||
ram[15'o0204] = 12'o7402;
|
||||
ram[15'o0205] = 12'o5200;
|
||||
|
||||
`include "focal.v"
|
||||
ram[15'o0000] = 12'o5404;
|
||||
ram[15'o0004] = 12'o0200;
|
||||
end
|
||||
|
||||
always @(negedge WE_N)
|
||||
begin
|
||||
if (CE_N == 0)
|
||||
begin
|
||||
$display("ram: write [%o] <- %o", A, DI);
|
||||
ram[ A ] = DI;
|
||||
end
|
||||
end
|
||||
|
||||
//always @(A)
|
||||
// begin
|
||||
// $display("ram: ce %b, we %b [%o] -> %o", CE_N, WE_N, A, ram[A]);
|
||||
// end
|
||||
|
||||
// assign DO = ram[ A ];
|
||||
assign DO = (^A === 1'bX || A === 1'bz) ? ram[0] : ram[A];
|
||||
|
||||
endmodule
|
||||
|
||||
419
cpu/pdp8_io.v
Normal file
419
cpu/pdp8_io.v
Normal file
@@ -0,0 +1,419 @@
|
||||
// PDP-8 i/o
|
||||
// Based on descriptions in "Computer Engineering"
|
||||
// Dev 2006 Brad Parker brad@heeltoe.com
|
||||
//
|
||||
|
||||
/*
|
||||
iot's touched by focal
|
||||
|
||||
6022 PCF
|
||||
6203 CDF CIF 00
|
||||
6402 PT08
|
||||
6412
|
||||
6422
|
||||
6432
|
||||
6442
|
||||
6452
|
||||
6462
|
||||
6472
|
||||
6764 DECTAPE
|
||||
6772
|
||||
*/
|
||||
|
||||
/*
|
||||
RF08
|
||||
7750 word count
|
||||
7751 current address
|
||||
|
||||
2048 words/track
|
||||
|
||||
660x
|
||||
661x
|
||||
662x
|
||||
664x
|
||||
|
||||
6601 DCMA Generates System Clear Pulse (SCLP) at IOP time 1.
|
||||
Clears disk memory eaddress(DMA), Parity Eror Flag (PEF),
|
||||
Data Request Late Flag (DRL), and sets logic to
|
||||
initial state for read or write. Does not clear
|
||||
interrupt enable or extended address register.
|
||||
|
||||
6603 DMAR Generate SCLP at IOP time 1. At IOP time 2, loads DMA
|
||||
with AC and clears AC. Read continues for number words
|
||||
in WC register (7750)
|
||||
|
||||
6605 DMAW Generate SCLP at IOP time 1. At IOP time 4, loads DMA
|
||||
with AC and clears AC. When the disk word address is located
|
||||
writing begins, disk address is incremented for each
|
||||
word written
|
||||
|
||||
6611 DCIM clears disk interrupt enable and the extended address
|
||||
registers at IOP time 1.
|
||||
|
||||
6612 DSAC At IOP time 2, skip if Address Confirmed (ADC) set indicating
|
||||
the DMA address and disk word address compare. AC is then
|
||||
cleared.
|
||||
|
||||
6615 DIML At IOP time 1, clear interrupt enable and memory address
|
||||
extension registers. At IOP time 4, load interrupt enable
|
||||
and memory address extension register with AC, clear AC.
|
||||
|
||||
6616 DIMA Clear ??? at IOP time 2. At IOP time 4 load AC with status
|
||||
register.
|
||||
|
||||
6621 DFSE skip on error skip if DRL, PER WLS or NXD set
|
||||
|
||||
6622 ??? skip if data completion DCF set
|
||||
|
||||
6623 DISK skip on error or data completion; enabled at IOP 2
|
||||
|
||||
6626 DMAC clear ac at IOP time 2 load AC from DMA at IOP time 4.
|
||||
|
||||
6641 DCXA Clears EMA
|
||||
|
||||
6643 DXAL Clears and loads EMA from AC. At IOP time 1, clear EMA, at
|
||||
IOP time 2, load EMA with AC. Clear AC
|
||||
|
||||
6645 DXAC Clears AC and loads EMA into AC
|
||||
|
||||
6646 DMMT Maintenance
|
||||
|
||||
uses 3 cycle data break
|
||||
|
||||
ac 7:0, ac 11:0 => 20 bit {EMA,DMA}
|
||||
20 bit {EMA,DMA} = { disk-select, track-select 6:0, word-select 11:0 }
|
||||
|
||||
status
|
||||
*/
|
||||
|
||||
// EIE = WLS | DRL | NXD | PER
|
||||
|
||||
/*
|
||||
3 cycle data break
|
||||
|
||||
1. An address is read from the device to indicate the location of the
|
||||
word count register. This location specifies the number of words in
|
||||
the block yet to be transferred. The address is always the same for a
|
||||
given device.
|
||||
|
||||
2. The content of the specficified word count register is read from
|
||||
memory and incremented by one. To transfer a block of n words, the
|
||||
word count is set to -n during the programmed initialization of the
|
||||
device. When this register is incremented to 0, a pulse is sent to
|
||||
the device to terminate the transfer.
|
||||
|
||||
3. The location after the word count register contains the current
|
||||
address register for the device transfer. The content of thise
|
||||
register is set to 1 less than the location to be affected by the next
|
||||
transfer. To transfer a block beginning at location A, the register is
|
||||
originally set to A-1.
|
||||
|
||||
4. The content of the current address register is incremented by 1
|
||||
and then used to specify the location affected by the transfer.
|
||||
|
||||
After the transfer of information has been accomplished through the
|
||||
data break factility, input data (or new output data) is processed,
|
||||
usually through the program interrupt facility. An interrupt is
|
||||
requested when the data transfer is completed and the service routine
|
||||
will process the information.
|
||||
|
||||
xxx:
|
||||
if (databreak_req)
|
||||
begin
|
||||
databreak_done <= 0;
|
||||
next_state <= DB0;
|
||||
end
|
||||
|
||||
// read word count
|
||||
DB0:
|
||||
ma <= wc-address;
|
||||
next_state <= DB1;
|
||||
|
||||
// write word count - 1
|
||||
DB1:
|
||||
mb <= memory_bus - 1;
|
||||
ram_we_n <= 0;
|
||||
if (mb == 0) databreak_done <= 1;
|
||||
next_state <= DB2;
|
||||
|
||||
// finish write
|
||||
DB2:
|
||||
ram_we_n <= 1;
|
||||
next_state <= DB3;
|
||||
|
||||
// read current address
|
||||
DB3:
|
||||
ma <= ma | 1;
|
||||
next_state <= DB4;
|
||||
|
||||
// write current address - 1
|
||||
DB4:
|
||||
mb <= memory_bus + 1;
|
||||
ram_we_n <= 0;
|
||||
next_state <= DB5;
|
||||
|
||||
// finish write
|
||||
DB5:
|
||||
ram_we_n <= 1;
|
||||
next_state <= DB6;
|
||||
|
||||
// set up read/write address
|
||||
DB6:
|
||||
ma <= mb;
|
||||
next_state <= DB7;
|
||||
|
||||
// do read or start write
|
||||
DB7:
|
||||
if (databreak_write)
|
||||
begin
|
||||
data <= memory_bus;
|
||||
next_state <= F0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
mb <= data;
|
||||
ram_we_n <= 0;
|
||||
next_state <= DB8;
|
||||
end
|
||||
|
||||
// finish write
|
||||
DB8:
|
||||
ram_we_n < = 1;
|
||||
next_state <= F0;
|
||||
|
||||
*/
|
||||
|
||||
|
||||
module pdp8_io(clk, reset_n, iot, state, pc, ac, mb,
|
||||
io_select,
|
||||
io_data_out, io_data_avail, io_interrupt, io_skip, io_clear_ac);
|
||||
|
||||
input clk, reset_n, iot;
|
||||
input [11:0] pc;
|
||||
input [11:0] ac;
|
||||
input [11:0] mb;
|
||||
input [3:0] state;
|
||||
input [5:0] io_select;
|
||||
|
||||
output reg [11:0] io_data_out;
|
||||
output reg io_data_avail;
|
||||
output reg io_interrupt;
|
||||
output reg io_skip;
|
||||
output reg io_clear_ac;
|
||||
|
||||
|
||||
reg rx_int, tx_int;
|
||||
reg [12:0] rx_data, tx_data;
|
||||
reg tx_delaying;
|
||||
reg [3:0] tx_delay;
|
||||
|
||||
parameter F0 = 4'b0000;
|
||||
parameter F1 = 4'b0001;
|
||||
parameter F2 = 4'b0010;
|
||||
parameter F3 = 4'b0011;
|
||||
|
||||
parameter D0 = 4'b0100;
|
||||
parameter D1 = 4'b0101;
|
||||
parameter D2 = 4'b0110;
|
||||
parameter D3 = 4'b0111;
|
||||
|
||||
parameter E0 = 4'b1000;
|
||||
parameter E1 = 4'b1001;
|
||||
parameter E2 = 4'b1010;
|
||||
parameter E3 = 4'b1011;
|
||||
|
||||
|
||||
parameter PCA = 12'o4000; // photocell status
|
||||
parameter DRE = 12'o2000; // data req enable
|
||||
parameter WLS = 12'o1000; // write lock status
|
||||
parameter EIE = 12'o0400; // error int enable
|
||||
parameter PIE = 12'o0200; // photocell int enb
|
||||
parameter CIE = 12'o0100; // done int enable
|
||||
parameter MEX = 12'o0070; // memory extension
|
||||
parameter DRL = 12'o0004; // data late error
|
||||
parameter NXD = 12'o0002; // non-existent disk
|
||||
parameter PER = 12'o0001; // parity error
|
||||
|
||||
|
||||
always @(state)
|
||||
begin
|
||||
case (state)
|
||||
F0:
|
||||
begin
|
||||
// sampled during f1
|
||||
io_skip <= 0;
|
||||
io_data_avail <= 0;
|
||||
io_clear_ac <= 0;
|
||||
|
||||
if (iot)
|
||||
case (io_select)
|
||||
6'o03:
|
||||
if (mb[0])
|
||||
io_skip <= rx_int;
|
||||
|
||||
6'o04:
|
||||
if (mb[0])
|
||||
begin
|
||||
$display("tsf; tx_int %b, pc %o", tx_int, pc);
|
||||
io_skip <= tx_int;
|
||||
end
|
||||
|
||||
6'o60: // DCMA
|
||||
if (mb[2:0] == 3'b001)
|
||||
begin
|
||||
DMA <= 0;
|
||||
PEF < = 0;
|
||||
DRL <= 0;
|
||||
end
|
||||
6'o61:
|
||||
case (mb[2:0])
|
||||
n 3'o1: // DCIM
|
||||
begin
|
||||
CIE <= 0;
|
||||
EMA <= 0;
|
||||
end
|
||||
3'o2: // DSAC
|
||||
// xxx
|
||||
assign ADC = DMA == DWA;
|
||||
|
||||
begin
|
||||
if (ADC)
|
||||
begin
|
||||
io_skip <= 1;
|
||||
io_clear_ac <= 1;
|
||||
end
|
||||
end
|
||||
3'o5: // DIML
|
||||
begin
|
||||
CIE <= AC[8];
|
||||
EMA <= AC[7:0];
|
||||
end
|
||||
3'o6: // DIMA
|
||||
begin
|
||||
AC <= { PCA,DRE,WLS,EIE,PIE,CIE, MEX, DRL,NXD,PER };
|
||||
end
|
||||
endcase // case(mb[2:0])
|
||||
|
||||
6'o62:
|
||||
case (mb[2:0])
|
||||
3'o1: // DFSE
|
||||
if (DRL | PER | WLS | NXD)
|
||||
io_skip <= 1;
|
||||
3'o2: // ???
|
||||
if (DCF)
|
||||
io_skip <= 1;
|
||||
3'o3: // DISK
|
||||
if (DRL | PER | WLS | NXD | DCF)
|
||||
io_skip <= 1;
|
||||
endcase
|
||||
endcase
|
||||
end
|
||||
|
||||
F1:
|
||||
if (iot)
|
||||
begin
|
||||
$display("iot2 %t, state %b, mb %o, io_select %o",
|
||||
$time, state, mb, io_select);
|
||||
|
||||
case (io_select)
|
||||
6'o03:
|
||||
begin
|
||||
if (mb[1])
|
||||
rx_int <= 0;
|
||||
if (mb[2])
|
||||
begin
|
||||
io_data_out <= rx_data;
|
||||
io_data_avail <= 1;
|
||||
end
|
||||
end
|
||||
|
||||
6'o04:
|
||||
begin
|
||||
if (mb[1])
|
||||
tx_int <= 0;
|
||||
if (mb[2])
|
||||
begin
|
||||
$display("tls; %o", ac);
|
||||
tx_data <= ac;
|
||||
tx_int <= 1;
|
||||
tx_delaying <= 1;
|
||||
tx_delay <= 4'b1111;
|
||||
$display("set tx_int");
|
||||
end
|
||||
end // case: 6'o04
|
||||
|
||||
6'o60:
|
||||
case (mb[2:0])
|
||||
3'o03: // DMAR
|
||||
begin
|
||||
DMA <= AC;
|
||||
io_clear_ac <= 0;
|
||||
rf08_start_io <= 1;
|
||||
rf08_rw <= 0;
|
||||
end
|
||||
|
||||
3'o03: // DMAW
|
||||
begin
|
||||
DMA <= AC;
|
||||
io_clear_ac <= 0;
|
||||
rf08_start_io <= 1;
|
||||
rf08_rw <= 1;
|
||||
end
|
||||
endcase // case(mb[2:0])
|
||||
|
||||
6'o62:
|
||||
case (mb[2:0])
|
||||
6: // DMAC
|
||||
begin
|
||||
// io_clear_ac <= 1;
|
||||
io_ac <= DMA;
|
||||
end
|
||||
endcase
|
||||
|
||||
6'o64:
|
||||
case (mb[2:0])
|
||||
1: // DCXA
|
||||
EMA <= 0;
|
||||
3: // DXAL
|
||||
begin
|
||||
EMA <= AC;
|
||||
io_clean_ac <= 1;
|
||||
end
|
||||
5: // DXAC
|
||||
begin
|
||||
AC <= EMA;
|
||||
end
|
||||
endcase
|
||||
|
||||
endcase
|
||||
|
||||
end // if (iot)
|
||||
|
||||
F2:
|
||||
begin
|
||||
if (io_interrupt)
|
||||
$display("iot2 %t, reset io_interrupt", $time);
|
||||
|
||||
// sampled during f0
|
||||
io_interrupt <= 0;
|
||||
end
|
||||
|
||||
F3:
|
||||
begin
|
||||
if (tx_delaying)
|
||||
begin
|
||||
tx_delay <= tx_delay - 1;
|
||||
if (tx_delay == 4'b0)
|
||||
begin
|
||||
$display("iot2 %t, set io_interrupt", $time);
|
||||
tx_delaying <= 0;
|
||||
io_interrupt <= 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
endcase // case(state)
|
||||
end
|
||||
|
||||
endmodule
|
||||
68
cpu/run.v
Normal file
68
cpu/run.v
Normal file
@@ -0,0 +1,68 @@
|
||||
|
||||
`include "pdp8.v"
|
||||
|
||||
`timescale 1ns / 1ns
|
||||
|
||||
module test;
|
||||
|
||||
reg clk, reset_n;
|
||||
reg[11:0] switches;
|
||||
|
||||
pdp8 cpu(clk, reset_n, switches);
|
||||
|
||||
initial
|
||||
begin
|
||||
$timeformat(-9, 0, "ns", 7);
|
||||
|
||||
$dumpfile("pdp8.vcd");
|
||||
$dumpvars(0, test.cpu);
|
||||
end
|
||||
|
||||
initial
|
||||
begin
|
||||
clk = 0;
|
||||
reset_n = 1;
|
||||
|
||||
#1 begin
|
||||
reset_n = 0;
|
||||
end
|
||||
|
||||
#100 begin
|
||||
reset_n = 1;
|
||||
end
|
||||
|
||||
// #1500000 $finish;
|
||||
#3000000 $finish;
|
||||
end
|
||||
|
||||
always
|
||||
begin
|
||||
#100 clk = 0;
|
||||
#100 clk = 1;
|
||||
end
|
||||
|
||||
//----
|
||||
integer cycle;
|
||||
|
||||
initial
|
||||
cycle = 0;
|
||||
|
||||
always @(posedge cpu.clk)
|
||||
if (cpu.state == 4'b0000)
|
||||
begin
|
||||
cycle = cycle + 1;
|
||||
#1 $display("cycle %d, r%b, pc %o, ir%o, ma %o, mb %o, jmp %b, l%b ac %o, i%b/%b",
|
||||
cycle, cpu.run, cpu.pc,
|
||||
cpu.ir, cpu.ma, cpu.mb, cpu.jmp, cpu.l, cpu.ac,
|
||||
cpu.interrupt_enable, cpu.interrupt);
|
||||
end
|
||||
|
||||
// always @(posedge cpu.clk)
|
||||
// begin
|
||||
// #1 $display("state %b, runs %b, pc %o, ir %o, ma %o mb %o, jmp %b, l %b ac %o",
|
||||
// cpu.state, cpu.run, cpu.pc,
|
||||
// cpu.ir, cpu.ma, cpu.mb, cpu.jmp, cpu.l, cpu.ac);
|
||||
// end
|
||||
|
||||
endmodule
|
||||
|
||||
Reference in New Issue
Block a user