module dataController_top( // clocks: input clk32, // 32.5 MHz pixel clock input clk8_en_p, input clk8_en_n, input E_rising, input E_falling, // system control: input machineType, // 0 - Mac Plus, 1 - Mac SE input _systemReset, // 68000 CPU control: output _cpuReset, output [2:0] _cpuIPL, // 68000 CPU memory interface: input [15:0] cpuDataIn, input [3:0] cpuAddrRegHi, // A12-A9 input [2:0] cpuAddrRegMid, // A6-A4 input [1:0] cpuAddrRegLo, // A2-A1 input _cpuUDS, input _cpuLDS, input _cpuRW, output [15:0] cpuDataOut, // peripherals: input selectSCSI, input selectSCC, input selectIWM, input selectVIA, input selectSEOverlay, input _cpuVMA, // RAM/ROM: input videoBusControl, input cpuBusControl, input [15:0] memoryDataIn, output [15:0] memoryDataOut, input memoryLatch, // keyboard: input [10:0] ps2_key, output capslock, // mouse: input [24:0] ps2_mouse, // serial: input serialIn, output serialOut, input serialCTS, output serialRTS, // RTC input [32:0] timestamp, // video: output pixelOut, input _hblank, input _vblank, input loadPixels, output vid_alt, // audio output [10:0] audioOut, // 8 bit audio + 3 bit volume output snd_alt, input loadSound, // misc output memoryOverlayOn, input [1:0] insertDisk, input [1:0] diskSides, output [1:0] diskEject, output [1:0] diskMotor, output [1:0] diskAct, output [21:0] dskReadAddrInt, input dskReadAckInt, output [21:0] dskReadAddrExt, input dskReadAckExt, // connections to io controller input [SCSI_DEVS-1:0] img_mounted, input [31:0] img_size, output [31:0] io_lba[SCSI_DEVS], output [SCSI_DEVS-1:0] io_rd, output [SCSI_DEVS-1:0] io_wr, input [SCSI_DEVS-1:0] io_ack, input [7:0] sd_buff_addr, input [15:0] sd_buff_dout, output [15:0] sd_buff_din[SCSI_DEVS], input sd_buff_wr ); parameter SCSI_DEVS = 2; // add binary volume levels according to volume setting assign audioOut = (snd_vol[0]?audio_x1:11'd0) + (snd_vol[1]?audio_x2:11'd0) + (snd_vol[2]?audio_x4:11'd0); // three binary volume levels *1, *2 and *4, sign expanded wire [10:0] audio_x1 = { {3{audio_latch[7]}}, audio_latch }; wire [10:0] audio_x2 = { {2{audio_latch[7]}}, audio_latch, 1'b0 }; wire [10:0] audio_x4 = { audio_latch[7] , audio_latch, 2'b00}; reg loadSoundD; always @(posedge clk32) if (clk8_en_n) loadSoundD <= loadSound; // read audio data and convert to signed for further volume adjustment reg [7:0] audio_latch; always @(posedge clk32) begin if(clk8_en_p && loadSoundD) begin if(snd_ena) audio_latch <= 8'h00; else audio_latch <= memoryDataIn[15:8] - 8'd128; end end // CPU reset generation // For initial CPU reset, RESET and HALT must be asserted for at least 100ms = 800,000 clocks of clk8 reg [19:0] resetDelay; // 20 bits = 1 million wire isResetting = resetDelay != 0; initial begin // force a reset when the FPGA configuration is completed resetDelay <= 20'hFFFFF; end always @(posedge clk32 or negedge _systemReset) begin if (_systemReset == 1'b0) begin resetDelay <= 20'hFFFFF; end else if (clk8_en_p && isResetting) begin resetDelay <= resetDelay - 1'b1; end end assign _cpuReset = isResetting ? 1'b0 : 1'b1; // interconnects wire SEL; wire _viaIrq, _sccIrq, sccWReq; wire [15:0] viaDataOut; wire [15:0] iwmDataOut; wire [7:0] sccDataOut; wire [7:0] scsiDataOut; wire mouseX1, mouseX2, mouseY1, mouseY2, mouseButton; // interrupt control assign _cpuIPL = !_viaIrq?3'b110: !_sccIrq?3'b101: 3'b111; reg [15:0] cpu_data; always @(posedge clk32) if (cpuBusControl && memoryLatch) cpu_data <= memoryDataIn; // CPU-side data output mux assign cpuDataOut = selectIWM ? iwmDataOut : selectVIA ? viaDataOut : selectSCC ? { sccDataOut, 8'hEF } : selectSCSI ? { scsiDataOut, 8'hEF } : (cpuBusControl && memoryLatch) ? memoryDataIn : cpu_data; // Memory-side assign memoryDataOut = cpuDataIn; // SCSI ncr5380 #(SCSI_DEVS) scsi( .clk(clk32), .reset(!_cpuReset), .bus_cs(selectSCSI), .bus_rs(cpuAddrRegMid), .ior(!_cpuUDS), .iow(!_cpuLDS), .dack(cpuAddrRegHi[0]), // A9 .wdata(cpuDataIn[15:8]), .rdata(scsiDataOut), // connections to io controller .img_mounted( img_mounted ), .img_size( img_size ), .io_lba ( io_lba ), .io_rd ( io_rd ), .io_wr ( io_wr ), .io_ack ( io_ack ), .sd_buff_addr(sd_buff_addr), .sd_buff_dout(sd_buff_dout), .sd_buff_din(sd_buff_din), .sd_buff_wr(sd_buff_wr) ); // count vblanks, and set 1 second interrupt after 60 vblanks reg [5:0] vblankCount; reg _lastVblank; always @(posedge clk32) begin if (clk8_en_n) begin _lastVblank <= _vblank; if (_vblank == 1'b0 && _lastVblank == 1'b1) begin if (vblankCount != 59) begin vblankCount <= vblankCount + 1'b1; end else begin vblankCount <= 6'h0; end end end end wire onesec = vblankCount == 59; // Mac SE ROM overlay switch reg SEOverlay; always @(posedge clk32) begin if (!_cpuReset) SEOverlay <= 1; else if (selectSEOverlay) SEOverlay <= 0; end // VIA wire [2:0] snd_vol; wire snd_ena; wire driveSel; // internal drive select, 0 - upper, 1 - lower wire [7:0] via_pa_i, via_pa_o, via_pa_oe; wire [7:0] via_pb_i, via_pb_o, via_pb_oe; wire viaIrq; assign _viaIrq = ~viaIrq; //port A assign via_pa_i = {sccWReq, ~via_pa_oe[6:0] | via_pa_o[6:0]}; assign snd_vol = ~via_pa_oe[2:0] | via_pa_o[2:0]; assign snd_alt = machineType ? 1'b0 : ~(~via_pa_oe[3] | via_pa_o[3]); assign driveSel = machineType ? ~via_pa_oe[4] | via_pa_o[4] : 1'b1; assign memoryOverlayOn = machineType ? SEOverlay : ~via_pa_oe[4] | via_pa_o[4]; assign SEL = ~via_pa_oe[5] | via_pa_o[5]; assign vid_alt = ~via_pa_oe[6] | via_pa_o[6]; //port B assign via_pb_i = {1'b1, {3{machineType}} | {_hblank, mouseY2, mouseX2}, machineType ? _ADBint : mouseButton, 2'b11, rtcdat_o}; assign snd_ena = ~via_pb_oe[7] | via_pb_o[7]; assign viaDataOut[7:0] = 8'hEF; via6522 via( .clock (clk32), .rising (E_rising), .falling (E_falling), .reset (!_cpuReset), .addr (cpuAddrRegHi), .wen (selectVIA && !_cpuVMA && !_cpuRW), .ren (selectVIA && !_cpuVMA && _cpuRW), .data_in (cpuDataIn[15:8]), .data_out (viaDataOut[15:8]), .phi2_ref (), //-- pio -- .port_a_o (via_pa_o), .port_a_t (via_pa_oe), .port_a_i (via_pa_i), .port_b_o (via_pb_o), .port_b_t (via_pb_oe), .port_b_i (via_pb_i), //-- handshake pins .ca1_i (_vblank), .ca2_i (onesec), .cb1_i (kbdclk), .cb2_i (cb2_i), .cb2_o (cb2_o), .cb2_t (cb2_t), .irq (viaIrq) ); wire _rtccs = ~via_pb_oe[2] | via_pb_o[2]; wire rtcck = ~via_pb_oe[1] | via_pb_o[1]; wire rtcdat_i = ~via_pb_oe[0] | via_pb_o[0]; wire rtcdat_o; rtc pram ( .clk (clk32), .reset (!_cpuReset), .timestamp (timestamp), ._cs (_rtccs), .ck (rtcck), .dat_i (rtcdat_i), .dat_o (rtcdat_o) ); wire _ADBint; wire ADBST0 = ~via_pb_oe[4] | via_pb_o[4]; wire ADBST1 = ~via_pb_oe[5] | via_pb_o[5]; wire ADBListen; reg kbdclk; reg [10:0] kbdclk_count; reg kbd_transmitting, kbd_wait_receiving, kbd_receiving; reg [2:0] kbd_bitcnt; wire cb2_i = kbddata_o; wire cb2_o, cb2_t; wire kbddat_i = ~cb2_t | cb2_o; reg kbddata_o; reg [7:0] kbd_to_mac; reg kbd_data_valid; // Keyboard transmitter-receiver always @(posedge clk32) begin if (clk8_en_p) begin if ((kbd_transmitting && !kbd_wait_receiving) || kbd_receiving) begin kbdclk_count <= kbdclk_count + 1'd1; if (kbdclk_count == (machineType ? 8'd80 : 12'd1300)) begin // ~165usec - Mac Plus / faster - ADB kbdclk <= ~kbdclk; kbdclk_count <= 0; if (kbdclk) begin // shift before the falling edge if (kbd_transmitting) kbd_out_data <= { kbd_out_data[6:0], kbddat_i }; if (kbd_receiving) kbddata_o <= kbd_to_mac[7-kbd_bitcnt]; end end end else begin kbdclk_count <= 0; kbdclk <= 1; end end end // Keyboard control always @(posedge clk32) begin reg kbdclk_d; reg ADBListenD; if (!_cpuReset) begin kbd_bitcnt <= 0; kbd_transmitting <= 0; kbd_wait_receiving <= 0; kbd_data_valid <= 0; ADBListenD <= 0; end else if (clk8_en_p) begin if (kbd_in_strobe && !machineType) begin kbd_to_mac <= kbd_in_data; kbd_data_valid <= 1; end if (adb_dout_strobe && machineType) begin kbd_to_mac <= adb_dout; kbd_receiving <= 1; end kbd_out_strobe <= 0; adb_din_strobe <= 0; kbdclk_d <= kbdclk; // Only the Macintosh can initiate communication over the keyboard lines. On // power-up of either the Macintosh or the keyboard, the Macintosh is in // charge, and the external device is passive. The Macintosh signals that it's // ready to begin communication by pulling the keyboard data line low. if (!machineType && !kbd_transmitting && !kbd_receiving && !kbddat_i) begin kbd_transmitting <= 1; kbd_bitcnt <= 0; end // ADB transmission start if (machineType && !kbd_transmitting && !kbd_receiving) begin ADBListenD <= ADBListen; if (!ADBListenD && ADBListen) begin kbd_transmitting <= 1; kbd_bitcnt <= 0; end end // The last bit of the command leaves the keyboard data line low; the // Macintosh then indicates it's ready to receive the keyboard's response by // setting the data line high. if (kbd_wait_receiving && kbddat_i && kbd_data_valid) begin kbd_wait_receiving <= 0; kbd_receiving <= 1; kbd_transmitting <= 0; end // send/receive bits at rising edge of the keyboard clock if (~kbdclk_d & kbdclk) begin kbd_bitcnt <= kbd_bitcnt + 1'd1; if (kbd_bitcnt == 3'd7) begin if (kbd_transmitting) begin if (!machineType) begin kbd_out_strobe <= 1; kbd_wait_receiving <= 1; end else begin adb_din_strobe <= 1; adb_din <= kbd_out_data; kbd_transmitting <= 0; end end if (kbd_receiving) begin kbd_receiving <= 0; kbd_data_valid <= 0; end end end end end // IWM iwm i( .clk(clk32), .cep(clk8_en_p), .cen(clk8_en_n), ._reset(_cpuReset), .selectIWM(selectIWM), ._cpuRW(_cpuRW), ._cpuLDS(_cpuLDS), .dataIn(cpuDataIn), .cpuAddrRegHi(cpuAddrRegHi), .SEL(SEL), .driveSel(driveSel), .dataOut(iwmDataOut), .insertDisk(insertDisk), .diskSides(diskSides), .diskEject(diskEject), .diskMotor(diskMotor), .diskAct(diskAct), .dskReadAddrInt(dskReadAddrInt), .dskReadAckInt(dskReadAckInt), .dskReadAddrExt(dskReadAddrExt), .dskReadAckExt(dskReadAckExt), .dskReadData(memoryDataIn[7:0]) ); // SCC scc s( .clk(clk32), .cep(clk8_en_p), .cen(clk8_en_n), .reset_hw(~_cpuReset), .cs(selectSCC && (_cpuLDS == 1'b0 || _cpuUDS == 1'b0)), // .cs(selectSCC && (_cpuLDS == 1'b0 || _cpuUDS == 1'b0) && cpuBusControl), // .we(!_cpuRW), .we(!_cpuLDS), .rs(cpuAddrRegLo), .wdata(cpuDataIn[15:8]), .rdata(sccDataOut), ._irq(_sccIrq), .dcd_a(mouseX1), .dcd_b(mouseY1), .wreq(sccWReq), .txd(serialOut), .rxd(serialIn), .cts(serialCTS), .rts(serialRTS) ); // Video videoShifter vs( .clk32(clk32), .memoryLatch(memoryLatch), .dataIn(memoryDataIn), .loadPixels(loadPixels), .pixelOut(pixelOut)); // Mouse ps2_mouse mouse( .clk(clk32), .ce(clk8_en_p), .reset(~_cpuReset), .ps2_mouse(ps2_mouse), .x1(mouseX1), .y1(mouseY1), .x2(mouseX2), .y2(mouseY2), .button(mouseButton)); wire [7:0] kbd_in_data; wire kbd_in_strobe; reg [7:0] kbd_out_data; reg kbd_out_strobe; // Keyboard ps2_kbd kbd( .clk(clk32), .ce(clk8_en_p), .reset(~_cpuReset), .ps2_key(ps2_key), .data_out(kbd_out_data), // data from mac .strobe_out(kbd_out_strobe), .data_in(kbd_in_data), // data to mac .strobe_in(kbd_in_strobe), .capslock(capslock) ); reg [7:0] adb_din; reg adb_din_strobe; wire [7:0] adb_dout; wire adb_dout_strobe; adb adb( .clk(clk32), .clk_en(clk8_en_p), .reset(~_cpuReset), .st({ADBST1, ADBST0}), ._int(_ADBint), .viaBusy(kbd_transmitting || kbd_receiving), .listen(ADBListen), .adb_din(adb_din), .adb_din_strobe(adb_din_strobe), .adb_dout(adb_dout), .adb_dout_strobe(adb_dout_strobe), .ps2_mouse(ps2_mouse), .ps2_key(ps2_key) ); endmodule