`include "defs.svh" `define DATA_BITS 12 module core #( JTAG_INSTANCE = 1 ) ( input bit clk , input bit reset , input bit uart_tx_ready , output bit uart_tx_valid , output uart_byte_t uart_tx_data , output bit uart_rx_ready , input bit uart_rx_valid , input uart_byte_t uart_rx_data , input bit mem_command_ready , output bit mem_command_valid , output pdp_command_t mem_command , output bit mem_read_ready , input bit mem_read_valid , input pdp_read_response_t mem_read , input bit [2:0] switch_df , input bit [2:0] switch_if , input bit [`PDP_ADDRESS_BITS-3-1:0] switch_sr , input bit switch_start , input bit switch_load_add , input bit switch_dep , input bit switch_exam , input bit switch_cont , input bit switch_stop , input bit switch_sing_step , input bit switch_sing_inst // verilator lint_off UNDRIVEN , output bit [`PDP_ADDRESS_BITS-3-1:0] led_pc , output bit [`PDP_ADDRESS_BITS-3-1:0] led_memaddr , output bit [`DATA_BITS-1:0] led_memdata , output bit [`DATA_BITS-1:0] led_acc , output bit [`DATA_BITS-1:0] led_mq , output bit led_and , output bit led_tad , output bit led_isz , output bit led_dca , output bit led_jms , output bit led_jmp , output bit led_iot , output bit led_opr , output bit led_fetch , output bit led_execute , output bit led_defer , output bit led_word_count , output bit led_current_address , output bit led_break , output bit led_ion , output bit led_pause , output bit led_run , output bit [4:0] led_step_counter , output bit [2:0] led_df , output bit [2:0] led_if , output bit led_link // verilator lint_on UNDRIVEN ); `ifndef SYNTHESIS integer pctrace; initial pctrace = $fopen("trace.pcs", "w"); integer eventtrace; initial eventtrace = $fopen("trace.events", "r"); // Event trace file format: lines of the form // instruction-count skip interrupt // Events are triggered before the corresponding instruction // Skip opcodes: // 0 Do not override skip behaviour of this instruction // 1 Force this instruction to "not skip" // 2 Force this instruction to "skip" // Interrupt opcodes: // 0 Do not force an interrupt this instruction // 1 Force an interrupt this instruction // The first instruction has instruction count 1 integer unsigned nextevent = ~0; integer force_skip = 0; bit force_interrupt = 0; initial if (eventtrace != 0) $fscanf(eventtrace, "%d %d %d", nextevent, force_skip, force_interrupt); initial $display("nextevent=%d force_skip=%d force_interrupt=%d", nextevent, force_skip, force_interrupt); `endif bit run; bit int_enable; bit int_delay; bit int_request; bit switch_start_observed; bit switch_load_add_observed; bit switch_dep_observed; bit switch_exam_observed; bit switch_cont_observed; assign led_run = run; assign led_ion = int_enable; assign led_current_address = mem_command_valid; assign led_memaddr = mem_command.address[`PDP_ADDRESS_BITS-3-1:0]; assign mem_command.mask = ~0; bit mem_hold_valid; pdp_read_response_t mem_hold; bit rx_ready; bit rx_valid; bit [7:0] rx_data; bit tx_ready; bit tx_valid; bit [7:0] tx_data; assign tx_ready = uart_tx_ready; assign uart_tx_valid = tx_valid; assign uart_tx_data = tx_data; assign uart_rx_ready = rx_ready; assign rx_valid = uart_rx_valid; assign rx_data = uart_rx_data; bit [`PDP_ADDRESS_BITS-3-1:7] page; bit [2:0] data_field; bit [2:0] data_field_saved; bit [2:0] inst_field; bit [2:0] inst_field_buffer; bit [2:0] inst_field_saved; bit [`PDP_ADDRESS_BITS-3-1:0] pc; bit [`PDP_ADDRESS_BITS-3-1:0] next_pc; assign led_pc = pc; bit [2:0] opcode; bit [8:0] operand; bit [`DATA_BITS-1:0] acc; bit link; assign led_df = data_field; assign led_if = inst_field; assign led_acc = acc; assign led_link = link; assign led_and = opcode == 0; assign led_tad = opcode == 1; assign led_isz = opcode == 2; assign led_dca = opcode == 3; assign led_jms = opcode == 4; assign led_jmp = opcode == 5; assign led_iot = opcode == 6; assign led_opr = opcode == 7; bit tti_int_enable; bit tti_valid; bit [`DATA_BITS-1:0] tti_data; bit [15:0] tto_delay; bit tto_int_enable; bit tto_flag; bit tto_flag_old; bit i; bit z; bit [6:0] wip; bit [`PDP_ADDRESS_BITS-1:0] address; bit can_skip; bit skip; bit injected_instruction; `ifndef SYNTHESIS integer unsigned instcount = 1; `endif assign int_request = 0 || (tti_int_enable && tti_valid) || (tto_int_enable && tto_flag && !tto_flag_old) ; enum { FETCH , DECODE , INDIRECT , INDIRECTED , PREINC , AGEN , EXEC , HALT , DEPOSIT , RETIRE } state; assign led_fetch = state == FETCH; assign led_execute = state == DECODE || state == AGEN || state == EXEC; assign led_defer = state == INDIRECT || state == INDIRECTED || state == PREINC; assign led_pause = state == HALT; always_ff @(posedge clk) begin if (reset) begin `ifdef SYNTHESIS run = 0; `else run = 1; `endif int_enable = 0; int_delay = 0; switch_start_observed = 0; switch_load_add_observed = 0; switch_dep_observed = 0; switch_exam_observed = 0; switch_cont_observed = 0; mem_command_valid = 0; mem_hold_valid = 0; rx_ready = 0; tx_valid = 0; tto_delay = 0; tto_flag = 1; tto_flag_old = 1; tx_data = 0; data_field = 0; data_field_saved = 0; inst_field = 0; inst_field_buffer = 0; inst_field_saved = 0; pc = 'o200; acc = 0; link = 1; tti_int_enable = 1; tto_int_enable = 1; tti_valid = 0; state = state.first; end else begin if (mem_command_ready && mem_command_valid) begin mem_command_valid = 0; end if (mem_read_ready && mem_read_valid) begin if (mem_read.address == mem_command.address) begin mem_hold_valid = 1; mem_hold = mem_read; mem_hold.data &= 'hfff; led_memdata = mem_hold.data; end end if (switch_start && !switch_start_observed) begin switch_start_observed = 1; run = 1; int_enable = 0; int_delay = 0; mem_command_valid = 0; acc = 0; link = 1; state = state.first; end if (!switch_start) switch_start_observed = 0; if (switch_load_add && !switch_load_add_observed) begin switch_load_add_observed = 1; data_field = switch_df; inst_field = switch_if; inst_field_buffer = switch_if; pc = switch_sr; end if (!switch_load_add) switch_load_add_observed = 0; `ifdef HISTORIC_SWITCH_BEHAVIOUR if (switch_dep) switch_dep_observed = 1; if (!switch_dep && switch_dep_observed) begin `else if (switch_dep && !switch_dep_observed) begin switch_dep_observed = 1; `endif state = DEPOSIT; mem_command_valid = 1; mem_command.address = {inst_field, pc}; mem_command.write = 1; mem_command.data = switch_sr; led_memdata = mem_command.data; run = 1; end if (!switch_dep) switch_dep_observed = 0; if (switch_exam && !switch_exam_observed) begin if (!run) begin switch_exam_observed = 1; state = FETCH; run = 1; end end if (!switch_exam) switch_exam_observed = 0; if (switch_cont && !switch_cont_observed) begin switch_cont_observed = 1; run = 1; end if (!switch_cont) switch_cont_observed = 0; if (switch_stop) run = 0; if (tx_ready) begin if (tx_valid) tto_flag = 1; tx_valid = 0; end if (tto_delay == 1) tx_valid = 1; if (tto_delay != 0) --tto_delay; if (rx_ready && rx_valid) begin tti_valid = 1; tti_data = {4'b0, 1'b1, rx_data[6:0]}; if (tti_data[6:0] == 7'h0a) tti_data[6:0] = 7'h0d; else if (tti_data[6:0] == 7'h1b) tti_data[6:0] = 7'h03; end if (run) begin case (state) FETCH: begin can_skip = 0; skip = 0; injected_instruction = 0; if (!mem_command_valid) begin mem_command_valid = 1; mem_command.address = {inst_field, pc}; mem_command.write = 0; mem_hold_valid = 0; state = DECODE; page = pc[`PDP_ADDRESS_BITS-3-1:7]; next_pc = pc + 1; end end DECODE: begin automatic bit go; go = 0; `ifdef SYNTHESIS if (!switch_exam_observed && (int_enable && int_request)) begin `else if (!switch_exam_observed && ((nextevent == ~0 && int_enable && int_request) || (instcount == nextevent && force_interrupt))) begin if (! int_enable) $display("ERROR: forced interrupt at a point where architecturally interrupts are disabled"); `endif $display("taking interrupt at instcount=%d", instcount); int_enable = 0; int_delay = 0; data_field_saved = data_field; inst_field_saved = inst_field; data_field = 0; inst_field = 0; inst_field_buffer = 0; tto_flag_old = tto_flag; opcode = 'o4; operand = 'o000; next_pc = pc; injected_instruction = 1; go = 1; end else if (mem_hold_valid) begin state = RETIRE; {opcode, operand} = mem_hold.data; if (switch_exam_observed) begin run = 0; end else begin go = 1; end end if (go) begin int_enable = int_delay; {i, z, wip} = operand; if (z) address = {inst_field, page, wip}; else address = {inst_field, 5'b0, wip}; $display("%d %o: %o%o", instcount, pc, opcode, operand); case (opcode) 'o0, 'o1, 'o2: state = i ? INDIRECT : AGEN; 'o3, 'o4: state = i ? INDIRECT : EXEC; 'o5: begin if (i) begin state = INDIRECT; end else begin next_pc = address[`PDP_ADDRESS_BITS-3-1:0]; inst_field = inst_field_buffer; end end 'o7: begin casez (operand) 'b0????????: begin automatic bit cla, cll, cma, cml, rar, ral, bsw, iac; {cla, cll, cma, cml, rar, ral, bsw, iac} = operand[7:0]; if (cla) acc = 0; if (cll) link = 0; if (cma) acc = ~acc; if (cml) link = ~link; if (iac) {link, acc} += 1; if (rar && !ral) begin {link, acc} = {acc[0], link, acc[11:1]}; if (bsw) {link, acc} = {acc[0], link, acc[11:1]}; end if (ral && !rar) begin {link, acc} = {acc, link}; if (bsw) {link, acc} = {acc, link}; end if (bsw && !(rar || ral)) acc = {acc[5:0], acc[11:6]}; end 'b1????0??0: begin automatic bit cla, sma, sza, snl, osr, hlt; can_skip = 1; {cla, sma, sza, snl, osr, hlt} = {operand[7:4], operand[2:1]}; if (sma && acc[11]) skip = 1; if (sza && acc == 0) skip = 1; if (snl && link != 0) skip = 1; if (cla) acc = 0; if (osr) begin $display("%o: unsupported front panel switch test", pc); $finish; end if (hlt) state = HALT; end 'b1????1??0: begin automatic bit cla, spa, sna, szl, osr, hlt; can_skip = 1; skip = 1; {cla, spa, sna, szl, osr, hlt} = {operand[7:4], operand[2:1]}; if (spa && acc[11]) skip = 0; if (sna && acc == 0) skip = 0; if (szl && link != 0) skip = 0; if (cla) acc = 0; if (osr) begin $display("%o: unsupported front panel switch test", pc); $finish; end if (hlt) state = HALT; end default: begin $display("%o: decoded unknown opcode %o %o", pc, opcode, operand); $finish; end endcase end 'o6: begin case (operand[8:6]) 'o0: begin case (operand[5:3]) 'o0: begin case (operand[2:0]) 'o0: begin can_skip = 1; if (int_enable) skip = 1; int_enable = 0; int_delay = 0; end 'o1: int_delay = 1; 'o2: begin int_enable = 0; int_delay = 0; end 'o3: begin can_skip = 1; if (int_request) skip = 1; end 'o4: acc = {link, 1'b0/*gt*/, int_request, 1'b0/*ii*/, int_enable, 1'b0/*u*/, 3'b0/*if*/, 3'b0/*df*/}; 'o5: begin link = acc[11]; if (acc[7]) begin int_delay = 1; end else begin int_enable = 0; int_delay = 0; end end 'o7: begin int_enable = 0; int_delay = 0; acc = 0; link = 1; tx_valid = 0; tto_flag = 1; tto_flag_old = 1; tti_valid = 0; end default: $display("%o: unsupported 600%o op", pc, operand[2:0]); endcase end 'o3: begin case (operand[2:0]) 'o0: tti_valid = 0; 'o1: begin can_skip = 1; if (tti_valid) skip = 1; end 'o2: begin acc = 0; tti_valid = 0; end 'o4: begin acc |= tti_data; end 'o5: begin tti_int_enable = acc[0]; end 'o6: begin acc = tti_data; tti_valid = 0; end default: begin $display("%o: unsupported keyboard op %o", pc, operand[2:0]); $finish; end endcase end 'o4: begin case (operand[2:0]) 'o0: begin tto_flag = 1; tto_flag_old = 1; end 'o1: begin can_skip = 1; if (tto_flag) skip = 1; end 'o2: begin tto_flag = 0; tto_flag_old = 0; end 'o4: begin tto_delay = 100; $display("[%o] write %o", pc, acc[6:0]); tx_data = {1'b0, acc[6:0]}; if (tx_data == 'h8d) tx_data = 'h8a; end 'o5: begin can_skip = 1; if (tto_flag || tti_valid) skip = 1; end 'o6: begin tto_delay =100; tto_flag = 0; tto_flag_old = 0; $display("[%o] write %o", pc, acc[6:0]); tx_data = {1'b0, acc[6:0]}; if (tx_data == 'h8d) tx_data = 'h8a; end default: begin $display("%o: unsupported tty op %o", pc, operand[2:0]); $finish; end endcase end default: begin $display("%o: unsupported device %o (operation %o)", pc, operand[8:3], operand[2:0]); can_skip = 1; end endcase end 'o2: begin if (operand[0]) data_field = operand[5:3]; if (operand[1]) inst_field_buffer = operand[5:3]; if (operand[2]) begin if (operand[3] && operand[4]) acc = acc | {6'b0, inst_field_saved, data_field_saved}; else if (operand[3]) acc = acc | {6'b0, data_field, 3'b0}; else if (operand[4]) acc = acc | {6'b0, inst_field, 3'b0}; if (operand[5]) begin data_field = data_field_saved; inst_field_buffer = inst_field_saved; end end end default: begin $display("%o: unsupported device %o (operation %o)", pc, operand[8:3], operand[2:0]); can_skip = 1; end endcase end default: begin $display("%o: decoded unknown opcode %o %o", pc, opcode, operand); $finish; end endcase end end INDIRECT: begin if (!mem_command_valid) begin mem_command_valid = 1; mem_command.write = 0; mem_command.address = address; mem_hold_valid = 0; state = INDIRECTED; end end INDIRECTED: begin if (mem_hold_valid) begin if (opcode != 'o5 && address[7:3] == 5'b00001) begin address = {3'b0, mem_hold.data}; address += 1; address[`PDP_ADDRESS_BITS-1:`PDP_ADDRESS_BITS-3] = data_field; state = PREINC; end else begin address = {data_field, mem_hold.data}; case (opcode) 'o0, 'o1, 'o2: state = AGEN; 'o3, 'o4: state = EXEC; 'o5: begin next_pc = address[`PDP_ADDRESS_BITS-3-1:0]; $display("indirect jump to %o", next_pc); inst_field = inst_field_buffer; state = RETIRE; end endcase end end end PREINC: begin if (!mem_command_valid) begin mem_command_valid = 1; mem_command.write = 1; mem_command.data = address[`DATA_BITS-1:0]; mem_hold_valid = 0; led_memdata = mem_command.data; $display("preinc [%o] <- %o", mem_command.address, mem_command.data); case (opcode) 'o0, 'o1, 'o2: state = AGEN; 'o3, 'o4, 'o5: state = EXEC; endcase end end AGEN: begin if (!mem_command_valid) begin mem_command_valid = 1; case (opcode) 'o0, 'o1, 'o2: mem_command.write = 0; endcase mem_command.address = address; mem_hold_valid = 0; state = EXEC; end end EXEC: begin automatic bit stall = mem_command_valid; case (opcode) 'o0, 'o1, 'o2: if (! mem_hold_valid) stall = 1; endcase if (! stall) begin state = RETIRE; case (opcode) 'o0: begin acc &= mem_hold.data; end 'o1: begin {link, acc} += {1'b0, mem_hold.data}; end 'o2: begin mem_command_valid = 1; mem_command.address = address; mem_command.write = 1; mem_command.data = (mem_hold.data + 1) & 'hfff; mem_hold_valid = 0; led_memdata = mem_command.data; $display("store [%o] <- %o", mem_command.address, mem_command.data); can_skip = 1; if (mem_command.data == 0) skip = 1; end 'o3: begin mem_command_valid = 1; mem_command.address = address; mem_command.write = 1; mem_command.data = acc; mem_hold_valid = 0; led_memdata = mem_command.data; $display("store [%o] <- %o", mem_command.address, mem_command.data); acc = 0; end 'o4: begin mem_command_valid = 1; mem_command.address = address; mem_command.write = 1; mem_command.data = next_pc[`DATA_BITS-1:0]; mem_hold_valid = 0; led_memdata = mem_command.data; $display("store [%o] <- %o", mem_command.address, mem_command.data); next_pc = address[`PDP_ADDRESS_BITS-3-1:0] + 1; inst_field = inst_field_buffer; end 'o5: begin next_pc = address[`PDP_ADDRESS_BITS-3-1:0]; inst_field = inst_field_buffer; end endcase end end HALT: begin run = 0; $display("\nhalt state reached"); $finish; end DEPOSIT: begin state = FETCH; // Not a retired instruction; go directly to decode page = pc[`PDP_ADDRESS_BITS-3-1:7]; ++pc; // Not a skip; normal part of panel deposit (because we are skipping RETIRE) run = 0; end endcase if (state == RETIRE) begin `ifndef SYNTHESIS if (!injected_instruction) begin $display("retiring pc=%o%o", inst_field, pc); $fdisplay(pctrace, "%o%o", inst_field, pc); end `endif state = FETCH; pc = next_pc; if (can_skip) begin `ifndef SYNTHESIS if (instcount == nextevent) begin $display("checking instcount=%d force_skip=%d against inferred skip=%d", instcount, force_skip, skip); if (force_skip == 1) skip = 0; else if (force_skip == 2) skip = 1; end `endif if (skip) begin $display("skip at instcount=%d pc=%o", instcount, pc); ++pc; end end `ifndef SYNTHESIS else if (instcount == nextevent && force_skip != 0) begin $display("Error: forced skip at impossible boundary instcount=%d nextpc=%o", instcount, pc); $finish; end `endif `ifndef SYNTHESIS if (instcount == nextevent) begin nextevent = ~0; if (eventtrace != 0) $fscanf(eventtrace, "%d %d %d", nextevent, force_skip, force_interrupt); $display("nextevent=%d force_skip=%d force_interrupt=%d", nextevent, force_skip, force_interrupt); end if (!injected_instruction) begin ++instcount; if (instcount >= 26000) $finish; end `endif end if (switch_sing_step) run = 0; if (state == FETCH && switch_sing_inst) run = 0; rx_ready = !tti_valid; end mem_read_ready = !mem_hold_valid; end end endmodule