729 lines
13 KiB
Verilog
729 lines
13 KiB
Verilog
// 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
|
|
|