// PDP-8/I in verilog // copyright Brad Parker 2005-2010 // // Based on descriptions in "Computer Engineering" and various PDP-8/I manuals // fully implements extended memory (IF & DF) and user mode (KT8/I) // // Mar 2010 // co-simulation with simh & behavioral model to boot TSS/8 // passes 8/I instruction and extended memory diags // split out peripherals, added external dma // Apr 2009 // major revamp for synthesis, removed latches, added muxes, new top // Jan 2007 // cleaned up state machines for synthesis // finished extended memory (IF & DF), user mode (KT8/I) // added rf08 registers // Dec 2006 // cleaned up a little; now runs focal to prompt // moved i/o out to pdp8_io.v // added IF, DF, user mode // Nov 2005 Brad Parker brad@heeltoe.com // initial work; runs focal a bit // // // 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 read ram[IF,pc] // F1 incr pc // F2 ? // F3 dispatch // // then // // E0 read ram[ea] // E1 decode // E2 write ram // E3 load // // or // // D0 read ram[ea] // D1 wait // D2 write ram // D3 load // // H0 halted // // ------ // Rules for address calculation // 0 and // 1 tad // 2 isz // 3 dca // // // 11 // 109876543210 // cccIZooooooo // // bit 8 - indirect // bit 7 - page 0 // bits 6:0 offset // // if 8:7 == 2'b00 // ea = IF, pc[11:7], offset[6:0] ;; current page // if 8:7 == 2'b01 // ea = IF, 5'b0, offset[6:0] ;; page 0 // if 8:7 == 2'b10 // ea = (DF, MA( IF, pc[11:7], offset[6:0] )) ;; *(current page) // if 8:7 == 2'b11 // ea = (DF, MA( IF, 5'b0, offset[6:0] )) ;; *(page 0) // 4 jms // 5 jmp // // ------ // // ea <= if Z // if I // {DF, 5'b0, mb[6:0]}; // else // {IF, 5'b0, mb[6:0]}; // else // if I // {DF, pc[11:7], mb[6:0]}; // else // {IF, pc[11:7], mb[6:0]}; // // ------ // // Actions take during each state: // // F0 fetch // ma = {IF,pc} // check for interrupt // // F1 incr pc // ma = 0 // ea <= { IF, ir_z_flag ? pc[11:7] : 5'b0, mb[6:0] }; // // if opr // group 1 processing // group 2 processing // // if iot // // incr pc or skip (incr pc by 2) // // F2 ?? // ma = pc // // F3 dispatch // ma = ea // if opr // group1 processing // if !opr && !iot // possible defer // // D0 // ma = ea // mb <= ram[ma] // D1 // ma = 0 // D2 (write index reg) // ma = index reg ? ea : {DF,mb} // ram_wr = 1 // D3 // ea <= mb // ma = 0 // // E0 // ma = ea // mb <= ram[ma] // E1 // ma = 0 // E2 (write isz value, dca value, jms return) // ma = ea // ram_wr = 1 // E3 // ma = ea + 1 (only bottom 12 bits) // // // 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}) // module pdp8(clk, reset, ram_addr, ram_data_out, ram_data_in, ram_rd, ram_wr, io_select, io_data_out, io_data_in, io_data_avail, io_interrupt, io_skip, io_clear_ac, switches, iot, state, mb, ext_ram_read_req, ext_ram_write_req, ext_ram_done, ext_ram_ma, ext_ram_in, ext_ram_out); input clk, reset; input [11:0] ram_data_in; output ram_rd; output ram_wr; output [11:0] ram_data_out; output [14:0] ram_addr; output [5:0] io_select; input [11:0] io_data_in; output [11:0] io_data_out; input io_data_avail; input io_interrupt; input io_skip; input io_clear_ac; output iot; output [3:0] state; output [11:0] mb; input [11:0] switches; input ext_ram_read_req; input ext_ram_write_req; input [14:0] ext_ram_ma; input [11:0] ext_ram_in; output ext_ram_done; output [11:0] ext_ram_out; // memory buffer, holds data, instructions reg [11:0] mb; // generate address of work in memory being accessed wire [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; reg ir_z_flag; reg ir_i_flag; // extended memory - instruction field & data field reg [2:0] IF; reg [2:0] DF; reg [2:0] IB; reg [6:0] SF; // { UF, IF[2:0], DF[2:0] } reg IB_pending; // user mode reg UB; // user_buffer reg UF; // user_flag reg UI; // user_interrupt reg UB_pending; // processor state reg [3:0] state; wire [3:0] next_state; reg run; reg interrupt_enable; reg interrupt_cycle; reg [1:0] interrupt_inhibit_delay; reg interrupt_skip; reg interrupt; wire interrupt_inhibit; 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 ; // instruction op decode wire i_and,tad,isz,dca,jms,jmp,iot,opr; assign {i_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 ; //------------- /* * 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 * * skip conditions are only valid during F1 * */ assign skip_condition = (mb[6] && ac[11]) || (mb[5] && (ac == 12'b0)) || (mb[4] && l); assign pc_incr = /* group 1 */ (opr & !mb[8]) || /* group 2 */ (opr && (mb[8] && !mb[0]) && (skip_condition == mb[3])) || /* group 3? */ (opr && (mb[8] && mb[0])) || iot || (!(opr || iot) && !interrupt_cycle); assign pc_skip = (opr && (mb[8] && !mb[0]) && (skip_condition ^ mb[3])) || (iot && (io_skip || interrupt_skip)); // cpu states parameter [3:0] F0 = 4'b0000, F1 = 4'b0001, F2 = 4'b0010, F3 = 4'b0011, D0 = 4'b0100, D1 = 4'b0101, D2 = 4'b0110, D3 = 4'b0111, E0 = 4'b1000, E1 = 4'b1001, E2 = 4'b1010, E3 = 4'b1011, H0 = 4'b1100; // // cpu state state machine // // clock next cpu state at rising edge of clock // always @(posedge clk) if (reset) state <= 0; else state <= next_state; wire next_is_F0; wire next_is_E0; assign next_is_F0 = opr | iot | (!mb[8] & jmp); assign next_is_E0 = !mb[8] & !jmp; assign next_state = state == F0 ? F1 : state == F1 && (~iot | (iot & io_data_avail)) ? F2 : // state == F1 && (iot & ~io_data_avail) ? F1 : state == F2 ? F3 : state == F3 ? (~run ? H0 : next_is_F0 ? F0 : next_is_E0 ? E0 : D0) : state == D0 ? D1 : state == D1 ? D2 : state == D2 ? D3 : state == D3 ? (jmp ? F0 : E0) : state == E0 ? E1 : state == E1 ? E2 : state == E2 ? E3 : state == E3 ? F0 : state == H0 ? H0 : F0; // // pc // wire [11:0] pc_mux; always @(posedge clk) if (reset) pc <= 0; else begin pc <= pc_mux; end assign pc_mux = (state == F1 && pc_skip) ? (pc + 12'd2) : (state == F1 && pc_incr) ? (pc + 12'd1) : (state == F3 && !(opr || iot) && (!mb[8] & jmp)) ? ma : (state == D3 && jmp) ? mb : (state == E3 && jms) ? ma : (state == E3 && isz && mb == 12'b0) ? (pc + 12'd1) : pc; // // ram // assign ram_rd = (state == F0) || (state == D0) || (state == E0) || (state == F2 && ext_ram_read_req); // // ea calculation // reg [14:0] ea; always @(posedge clk) if (reset) ea <= 0; else if (state == F1) ea <= { IF, ir_z_flag ? pc[11:7] : 5'b0, mb[6:0] }; else if (state == D3) ea <= { (ir_i_flag && (!jmp && !jms)) ? DF : IF, mb }; wire is_index_reg; assign is_index_reg = ea[11:3] == 9'o001; assign ram_wr = (state == D2 && is_index_reg) || (state == E2 && (isz || dca || jms)) || (state == F2 && ext_ram_write_req); /* peripherals get ram access during F2 */ wire ext_ram_req; wire ext_ram_grant; assign ext_ram_req = ext_ram_read_req | ext_ram_write_req; assign ext_ram_done = state == F2 && ext_ram_req; assign ext_ram_grant = state == F2 && ext_ram_req; assign ext_ram_out = ext_ram_req ? ram_data_in : 12'b0; assign ram_addr = ext_ram_grant ? ext_ram_ma : ma; assign ram_data_out = ext_ram_grant ? ext_ram_in : mb; assign io_select = mb[8:3]; assign io_data_out = ac; // // ma // assign ma = (state == F0) ? {IF,pc} : (state == F2 && (opr || iot)) ? {IF,pc} : ((state == F3 || state == D0 || state == E0) && (!opr && !iot)) ? ea : (state == D2) ? (is_index_reg ? ea : {DF,mb}) : (state == E2 ) ? ea : (state == E3 && jms) ? {ea[14:12], ea[11:0] + 12'b1} : 15'b0; // // interrupt defer logic // reg interrupt_inhibit_clear; reg interrupt_inhibit_ib; reg interrupt_inhibit_ub; reg interrupt_inhibit_ion; assign interrupt_inhibit = interrupt_inhibit_delay[0] | interrupt_inhibit_delay[1] | interrupt_inhibit_ion; always @(posedge clk) if (reset) begin interrupt_inhibit_delay <= 2'b00; IB_pending <= 0; UB_pending <= 0; end else if (interrupt_inhibit_clear) begin interrupt_inhibit_delay <= 2'b00; IB_pending <= 1'b0; UB_pending <= 1'b0; end else if (interrupt_inhibit_ib) begin IB_pending <= 1; interrupt_inhibit_delay <= 2'b10; end else if (interrupt_inhibit_ub) begin UB_pending <= 1; interrupt_inhibit_delay <= 2'b10; end else if (~IB_pending && ~UB_pending) begin interrupt_inhibit_delay[1] <= interrupt_inhibit_delay[0]; interrupt_inhibit_delay[0] <= interrupt_inhibit_ion; end // // registers // always @(posedge clk) if (reset) begin mb <= 0; ac <= 0; mq <= 0; l <= 0; ir <= 3'b000; ir_z_flag <= 1'b0; ir_i_flag <= 1'b0; run <= 1; interrupt_enable <= 0; interrupt_cycle <= 0; interrupt_skip <= 0; interrupt <= 0; UI <= 0; IF <= 0; DF <= 0; IB <= 0; SF <= 0; UF <= 0; UB <= 0; end else case (state) // // FETCH // F0: begin interrupt_skip <= 0; interrupt_inhibit_ion = 1'b0; if (interrupt && interrupt_enable && !interrupt_inhibit && !interrupt_cycle) begin if (0) $display("xxx interrupt, pc %o; %b %b %b; %b %b", pc, interrupt, interrupt_enable, interrupt_cycle, interrupt_inhibit, interrupt_inhibit_delay); interrupt_cycle <= 1; interrupt <= 0; interrupt_enable <= 0; // simulate a jsr to 0 mb <= 12'o4000; ir <= 3'o4; ir_i_flag <= 1'b0; ir_z_flag <= 1'b0; SF <= {UF,IF,DF}; IF <= 3'b000; DF <= 3'b000; end else begin interrupt_cycle <= 0; if (0) $display("read ram [%o] -> %o", ram_addr, ram_data_in); mb <= ram_data_in; ir <= ram_data_in[11:9]; ir_i_flag <= ram_data_in[8]; ir_z_flag <= ram_data_in[7]; end end F1: begin /* defaults - these should be comb logic */ interrupt_inhibit_clear = 1'b0; interrupt_inhibit_ion = 1'b0; interrupt_inhibit_ib = 1'b0; interrupt_inhibit_ub = 1'b0; /* defered loading of IF from IB at next jmp/jms */ if ((jmp || jms) && IB_pending) begin //$display("loading IF %o", IB); IF <= IB; interrupt_inhibit_clear = 1'b1; end if ((jmp || jms) && UB_pending) begin UF <= UB; interrupt_inhibit_clear = 1'b1; end if (opr) casex ({mb[8],mb[0]}) 2'b0x: // group 1 begin case ({mb[7],mb[5]}) 2'b01: ac <= ~ac; 2'b10: ac <= 12'o0; 2'b11: ac <= 12'o7777; endcase case ({mb[6],mb[4]}) 2'b01: l <= ~l; 2'b10: l <= 1'b0; 2'b11: l <= 1'b1; endcase end 2'b10: // group 2 begin if (mb[7]) ac <= 0; end 2'b11: // group 3 begin if (mb[7]) ac <= 0; end default: ; endcase if (iot) begin casex (io_select) 6'b000000: // ION, IOF case (mb[2:0]) 3'b001: begin interrupt_enable <= 1; interrupt_inhibit_ion = 1'b1; end 3'b010: interrupt_enable <= 0; 3'b011: if (interrupt_enable) interrupt_skip <= 1; endcase 6'b010xxx: // CDF..RMF begin if (mb[0]) DF <= mb[5:3]; // CDF if (mb[1]) begin // CIF IB <= mb[5:3]; interrupt_inhibit_ib = 1'b1; end if (mb[2:0] == 3'b100) begin case (io_select[2:0]) 3'b000: UI <= 0; // CINT 3'b001: ac <= ac | { 6'b0, DF, 3'b0 }; // RDF 3'b010: ac <= ac | { 6'b0, IF, 3'b0 }; // RIF 3'b011: ac <= ac | { 5'b0, SF }; // RIB 3'b100: begin // RMF UB <= SF[6]; IB <= SF[5:3]; DF <= SF[2:0]; end 3'b101: // SINT if (UI) interrupt_skip <= 1; 3'b110: // CUF begin UB <= 0; interrupt_inhibit_ub = 1'b1; end 3'b111: // SUF begin UB <= 1; interrupt_inhibit_ub = 1'b1; end endcase end // if (mb[2:0] == 3'b100) end endcase // case(io_select) if (io_data_avail) begin //$display("io_data clock %o", io_data_in); ac <= io_data_in; end if (io_clear_ac) begin ac <= 0; end end // if (iot) if (io_interrupt) begin if (0) $display("F1 - set interrupt; (%b %b %b, %b %b %b)", interrupt_enable, interrupt_inhibit, interrupt_cycle, IB_pending, UB_pending, interrupt_inhibit_delay); interrupt <= 1; end else interrupt <= 0; end // case: F1 F2: begin if (opr) begin // group 1 if (!mb[8] && mb[0]) /* IAC */ {l,ac} <= {l,ac} + 13'o00001; // group 3 if (mb[8] & mb[0]) case ({mb[6:4]}) 3'b001: /* MQL */ begin mq <= ac; ac <= 0; end 3'b100: ac <= ac | mq; //3'b101: tmq <= mq; 3'b101: ac <= mq; /* MQA */ endcase end end F3: begin if (opr) begin // group 1 if (!mb[8]) begin case (mb[3:1]) 3'b001: // BSW {l,ac} <= {l,ac[5:0],ac[11:6]}; 3'b010: // RAL {l,ac} <= {ac[11:0],l}; 3'b011: // RTL {l,ac} <= {ac[10:0],l,ac[11]}; 3'b100: // RAR {l,ac} <= {ac[0],l,ac[11:1]}; 3'b101: // RTR {l,ac} <= {ac[1:0],l,ac[11:2]}; endcase end if (!UF) begin // group 2 if (mb[8] & !mb[0]) begin if (mb[2]) ac <= ac | switches; if (mb[1]) begin $display("HLT! %o", mb); run <= 0; end end end if (UF) begin // group 2 - user mode (halt & osr) if (mb[8] & !mb[0]) begin if (mb[2]) UI <= 1; if (mb[1]) UI <= 1; end end // group 3 if (mb[8] & mb[0]) begin if (mb[7:4] == 4'b1101) mq <= 0; end end // if (opr) end // case: F3 // // DEFER // D0: begin if (0) $display("read ram [%o] -> %o", ram_addr, ram_data_in); mb <= ram_data_in; end D1: begin // auto increment locations if (is_index_reg) mb <= mb + 1; end D2: begin // write ram if (ram_wr) if (0) $display("write ram [%o] <- %o", ram_addr, ram_data_out); end D3: begin end // // EXECUTE // E0: begin if (0) $display("read ram [%o] -> %o", ram_addr, ram_data_in); mb <= ram_data_in; end E1: begin if (i_and) begin end if (isz) mb <= mb + 1; else if (dca) mb <= ac; else if (jms) mb <= pc; end E2: begin // write ram if (ram_wr) if (0) $display("write ram [%o] <- %o (pc %o)", ram_addr, ram_data_out, pc); end E3: begin if (i_and) ac <= ac & mb; else if (tad) {l,ac} <= {l,ac} + {1'b0,mb}; else if (dca) ac <= 0; end endcase // case(state) endmodule