From f1d328611f41c8337e1ff2d6ce9eb2ac104cd42f Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sun, 19 Jun 2011 22:00:10 +1000 Subject: [PATCH 1/1] Initial commit Signed-off-by: Benjamin Herrenschmidt --- .gitignore | 25 ++ README | 100 +++++ TODO | 22 + fpga/Makefile | 25 ++ fpga/addr_decode.v | 154 +++++++ fpga/cpu_intf.v | 526 +++++++++++++++++++++++ fpga/ctrl.v | 68 +++ fpga/fpga_clocks.v | 71 +++ fpga/fpga_clocks_dll.v | 102 +++++ fpga/iwm.v | 221 ++++++++++ fpga/mem_intf.v | 391 +++++++++++++++++ fpga/minimigmac.ucf | 199 +++++++++ fpga/minimigmac.v | 560 ++++++++++++++++++++++++ fpga/ncr5380.v | 530 +++++++++++++++++++++++ fpga/ps2.v | 170 ++++++++ fpga/ps2_kbd.v | 800 ++++++++++++++++++++++++++++++++++ fpga/ps2_mouse.v | 239 +++++++++++ fpga/rtc.v | 180 ++++++++ fpga/scc.v | 688 +++++++++++++++++++++++++++++ fpga/scope.v | 185 ++++++++ fpga/sim_bench.v | 236 ++++++++++ fpga/sim_clocks.v | 62 +++ fpga/sim_kbd.v | 147 +++++++ fpga/sim_mouse.v | 139 ++++++ fpga/sim_pic.v | 746 ++++++++++++++++++++++++++++++++ fpga/sim_sram.v | 72 ++++ fpga/spi_backbus.v | 110 +++++ fpga/spi_slave.v | 159 +++++++ fpga/via6522.v | 559 ++++++++++++++++++++++++ fpga/video.v | 196 +++++++++ fpga/xilinx/minimigmac.xise | 405 ++++++++++++++++++ pic/18f252_mod.lkr | 23 + pic/Makefile | 28 ++ pic/fat16.c | 542 +++++++++++++++++++++++ pic/fat16.h | 30 ++ pic/fat16_defs.h | 171 ++++++++ pic/hardware.c | 247 +++++++++++ pic/hardware.h | 91 ++++ pic/main.c | 833 ++++++++++++++++++++++++++++++++++++ pic/minimigmac.piklab | 74 ++++ pic/mmc.c | 402 +++++++++++++++++ pic/mmc.h | 8 + pic/mmc_cmds.h | 99 +++++ pic/scsi.c | 392 +++++++++++++++++ pic/scsi.h | 7 + 45 files changed, 11034 insertions(+) create mode 100644 .gitignore create mode 100644 README create mode 100644 TODO create mode 100644 fpga/Makefile create mode 100644 fpga/addr_decode.v create mode 100644 fpga/cpu_intf.v create mode 100644 fpga/ctrl.v create mode 100644 fpga/fpga_clocks.v create mode 100644 fpga/fpga_clocks_dll.v create mode 100644 fpga/iwm.v create mode 100644 fpga/mem_intf.v create mode 100644 fpga/minimigmac.ucf create mode 100644 fpga/minimigmac.v create mode 100644 fpga/ncr5380.v create mode 100644 fpga/ps2.v create mode 100644 fpga/ps2_kbd.v create mode 100644 fpga/ps2_mouse.v create mode 100644 fpga/rtc.v create mode 100644 fpga/scc.v create mode 100644 fpga/scope.v create mode 100644 fpga/sim_bench.v create mode 100644 fpga/sim_clocks.v create mode 100644 fpga/sim_kbd.v create mode 100644 fpga/sim_mouse.v create mode 100644 fpga/sim_pic.v create mode 100644 fpga/sim_sram.v create mode 100644 fpga/spi_backbus.v create mode 100644 fpga/spi_slave.v create mode 100644 fpga/via6522.v create mode 100644 fpga/video.v create mode 100644 fpga/xilinx/minimigmac.xise create mode 100644 pic/18f252_mod.lkr create mode 100644 pic/Makefile create mode 100644 pic/fat16.c create mode 100644 pic/fat16.h create mode 100644 pic/fat16_defs.h create mode 100644 pic/hardware.c create mode 100644 pic/hardware.h create mode 100644 pic/main.c create mode 100644 pic/minimigmac.piklab create mode 100644 pic/mmc.c create mode 100644 pic/mmc.h create mode 100644 pic/mmc_cmds.h create mode 100644 pic/scsi.c create mode 100644 pic/scsi.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a65a45b --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +*.o +*.d +fpga/xilinx/* +!fpga/xilinx/minimigmac.xise +*.vpi +*~ +*.bin +*.elf +fpga/testrom/testrom +*.vcd +*.vvp +*.bak +*.adb +*.lst +*.map +*.hex +*.cof +*.cof +*.cof +*.cof +*.cof +*.cof +*.cof +*.cof + diff --git a/README b/README new file mode 100644 index 0000000..5e8b33c --- /dev/null +++ b/README @@ -0,0 +1,100 @@ +Minimigmac v0.1 +--------------- + +Copyright 2011 Ben Herrenschmidt + +Licenced under the GPL, see COPYING file. + +! Warning ! + +A bunch of stuff aren't quite working yet, the whole thing actually +has some stability issues when "hot", see the TODO file and help +is welcome :-) + +This currently requires a 4M minimig to emulate a 2M MacPlus. I +will probably try to make it work on a 2M minimig at some point. + +Build/Install instructions +-------------------------- + +* Build the FPGA using Xilinx tools, project file not included, + and put the result in a file named "MIMIGMAC.BIN" on the sd-card. + + The toplevel module is "minimigmac" in minimigmac. The sim_* + modules and the Makefile are for simulation with iverilog. + This requires m68k_vpi.vpi amd m68k.v (my VERY CRUDE) VPI + module containing Hampa Hug's 68k emulator. It can be found + at http://ozlabs.org/~benh/m68k-vpi-0.1.tar.bz2 + + Or use my prebuilt binaries + +* Build the PIC code using sdcc (I use piklab as a front-end) and + download that to the PIC + + Or use my prebuild binaries + +* Get your (legitimate) copy of the MacPlus ROM onto the sd-card + in a file named "MIMIGMAC.ROM" + +* Make a 20M (I used 21M just in case, see below) file to use as a + SCSI disk image on the sd-card, name it "MACHD.IMG" + +Hard disk image notes: +---------------------- + +Since we don't emulate floppies yet, you need to have a pre-installed +hard disk image. + +This is not a raw filesystem image as used by most Mac emulators out +there, but instead needs to be a raw image of a Mac formatted SCSI +disk, more specifically a Seagate ST 225N :-) I've built such an image +using the "pce" emulator from Hampa Hug, I'll put some instructions +below. Eventually, when floppy emulation is implemented, things will +be easier to install. + +The reason for that is that the Macintosh ROM doesn't contain a proper +SCSI disk driver. Instead, it uses the low level SCSI Manager routines +in "polled" mode at boot to load such a driver from the disk itself. + +Since I don't have at this point a free driver I can make visible there +(by simulating the partition map etc...) we need to make things look +good enough for Apple's own driver to work. Unfortunately, Apple's +SCSI formatting/partitioning tool only works with "known" disks (well +there are versions that work with any disk but not the one I've used +so far). + +Note also that SCSI emulation is incomplete. It works with the driver +coming with System 4.2 Finder 5.5, which doesn't do anything but +READ6/WRITE6 (not even a GET_CAPACITY or INQUIRY) but you may have +problems with other variants. I will fix that eventually. + +So to create the disk image, first I've downloaded & built PCE which +you can find at http://www.hampa.ch/ + +I've then modified the default MacPlus config file so that its scsi +device section looks like: + + device { + # The SCSI ID + id = 6 + + # The drive number. This number is used to identify + # a "disk" section. The number itself is meaningless. + drive = 128 + + # The vendor and product strings are returned by + # the SCSI Inquiry command. + vendor = " SEAGATE" + product = " ST225N" + } + +(This is the same vendor/product strings that our PIC code passes +to the Macintosh upon INQUIRY commands). + +I've created a 21MB disk image (in case the disk is a bit more than +20MB though it's probably a bit less, ie, the Apple tool afaik uses +a hard wired size from a table) + +I've then used System 4.2 floppy images to install a MacOS onto +that disk image & copied it to my sd-card. + diff --git a/TODO b/TODO new file mode 100644 index 0000000..1f858fa --- /dev/null +++ b/TODO @@ -0,0 +1,22 @@ +- Instability.... it tends to crash in odd ways when used + for a while (ie. when "hot"). Either a bad timing or + maybe a charge buildup on a hi-z line... I don't have + chipscope so that's going to be a bitch to debug, tho + maybe it will go away once I sort out how to do the timing + constraints properly (Help welcome !) +- Sort out timings & DCM config +- Keypad, capslock & other keyboard sim improvements. + Add a fifo from ps2 to mac, always keep first slot empty + unless fed a keypad cmd which fills two first slots +- Add support for 2M variant of Minimig (1M MacPlus) +- Simulate a 2.5M MacPlus on a 4M Minimig instead of a 2M one +- SCSI Errors (try removing the SD-card !) +- More SCSI commands (try Apple SD format tool...) +- Embed an m68k SCSI driver to generate partitions from raw FS images ? +- Sound +- Floppy +- OSD (to swap floppies) +- RTC/nvram (how ? nvram file ? wire an i2c RTC to the PIC ?) +- Can I fit more stuff in PIC anyways ? +- Reset ? reboot not working... +- Some amount of serial emulation for the SCC (not just mouse) diff --git a/fpga/Makefile b/fpga/Makefile new file mode 100644 index 0000000..691566b --- /dev/null +++ b/fpga/Makefile @@ -0,0 +1,25 @@ +default,all: sim_bench.vcd + +SRC := sim_bench.v sim_clocks.v sim_sram.v sim_pic.v sim_mouse.v sim_kbd.v +SRC += m68k.v cpu_intf.v addr_decode.v +SRC += mem_intf.v via6522.v iwm.v ncr5380.v scc.v +SRC += spi_backbus.v spi_slave.v scope.v ctrl.v +SRC += video.v rtc.v ps2.v ps2_mouse.v ps2_kbd.v +SRC += minimigmac.v + +sim_bench.vvp: $(SRC) + iverilog -g2 -Wall -tvvp -D__IVERILOG__ -o $@ $^ + +sim_bench.vcd: sim_bench.vvp m68k_vpi.vpi + vvp -M. -mm68k_vpi sim_bench.vvp + +wave: sim_bench.vcd + gtkwave ./sim_bench.vcd + +clean: + rm -f *.vvp + rm -f *.vcd + rm -f *.vpi + rm -f *.o +distclean: clean + rm -f *~ diff --git a/fpga/addr_decode.v b/fpga/addr_decode.v new file mode 100644 index 0000000..74e721f --- /dev/null +++ b/fpga/addr_decode.v @@ -0,0 +1,154 @@ +`timescale 1ns / 100ps + +/* Mac Plus address space + * + * - 000000 RAM (or ROM when overlay enabled) + * + * - 400000 ROM + * WARNING: The ROM does a fun test to decide whether SCSI is availble + * or not (MacPlus vs. Mac512KE ?). It basically tests if + * (0x420000) == (0x440000) and if it -is- then it assumes + * SCSI is -not- there. So we must not "replicate" the ROM + * in our address decoding. + * + * + * - 580000 SCSI + * 580drn + * ||\ + * |\ \ b0000=Rd b0001=Wr + * | \- reg (b0000...b0111) + * \----b00d0 where d=dack + * (repeated to 5fffff) + * + * - 600000 RAM copy when overlay enabled (max 2M) + * (should we just always decode that ?) + * - 800000 + * + * - 9FFFF7 ?? SCC Reset ? (*) + * + * - 9FFFF8 SCC Rd (LDS=read UDS=write) + * - BFFFF9 SCC Wr + * +0 Ch B control + * +2 Ch A control + * +4 Ch B data + * +6 Ch B data + * + * - DFE1FF IWM + * + * + 0000 = DFE1FF = ph0L : CA0 = 0 + * + 0200 = DFE3FF = ph0H : CA0 = 1 + * + 0400 = DFE5FF = ph1L : CA1 = 0 + * + 0600 = DFE7FF = ph1H : CA1 = 1 + * + 0800 = DFE9FF = ph2L : CA2 = 0 + * + 0A00 = DFEBFF = ph2H : CA2 = 1 + * + 0C00 = DFEDFF = ph3L : LSTRB = 0 + * + 0E00 = DFEFFF = ph3H : LSTRB = 1 + * + 1000 = DFF1FF = mtrOff : ENABLE = 0 + * + 1200 = DFF3FF = mtrOn : ENABLE = 1 + * + 1400 = DFF5FF = intDrv : SELECT = 0 + * + 1600 = DFF7FF = extDrv : SELECT = 1 + * + 1800 = DFF9FF = q6L : Q6 = 0 + * + 1A00 = DFFBFF = q6H : Q6 = 1 + * + 1C00 = DFFDFF = q7L : Q7 = 0 + * + 1E00 = DFFFFF = q7H : Q7 = 1 + * + * - EFE1FE VIA + * Ex[xxxr][rrr1]xx - rrrr is RS3..RS0 + * + * Port A (reg 0x1 or 0xf) + * 0x07 O Sound volume + * 0x08 O 1=Main snd buf 0=Alt snd buf + * 0x10 O 1=ROM overlay (pullup, so set at boot) + * 0x20 O Disk SEL line + * 0x40 O 1=Main video 0=Alt video + * 0x80 I SCC WReq + * + * Port B (reg 0x0) + * 0x01 IO RTC data + * 0x02 O RTC clock + * 0x04 O RTC enable + * 0x08 I Mouse switch + * 0x10 I Mouse X2 + * 0x20 I Mouse Y2 + * 0x40 I HBlank + * 0x80 O 1=Sound disable 0=Enable + * + * - F00000 PhaseRead (according FDiasm) (*) + * + * - F80000 Expansion ROM + * + * - FFFFF? Autovector interrupt sim (we only check for top 6 bits 1) + * + * I am not certain how that "phase" setting works. Essentially we just + * return all 1's and the ROM seems happy enough. What happens is that + * very early at boot, the ROM does: + * + * movem.w (0xf0000000), d0-d2 + * + * Loading thus the 3 half words from f0000000, f0000002 and f0000004 + * into d0, d1 and d2 respectively. + * + * It then does a byte read of 9ffff7 which I believe resets the SCC. + * + * It then extracts the low order bit of the 3 reads above, adds them + * up, substracts one, and check if the results is 0 or -1 (ie check + * if at least 2 of those reads had bit 0 set). + * + * If yes, it then does a "phase adjust" by reading a word from 9ffff8, + * and that's about it. + */ + +/* + * Combinational address decoder, returns a chip select for each + * device. Handles the special casing of the ROM overlay and major + * devices. + * + * Returns cs_nack for a non-existing address region + * + * We are quite lax with decoding to save logic, I haven't verified + * how "precise" a real macplus is but I haven't been anal at beeing + * as lax as possible neither... + */ +module addr_decode(addr, req, rom_ovl, + cs_ram, cs_rom, cs_scsi, cs_scc, cs_iwm, cs_via, + cs_ivec, cs_nack); + input [23:1] addr; + input req; + input rom_ovl; + output reg cs_ram; + output reg cs_rom; + output reg cs_scsi; + output reg cs_scc; + output reg cs_iwm; + output reg cs_via; + output reg cs_ivec; + output reg cs_nack; + + always@(addr or req or rom_ovl) begin + cs_ram = 0; + cs_rom = 0; + cs_scsi = 0; + cs_scc = 0; + cs_iwm = 0; + cs_via = 0; + cs_ivec = 0; + cs_nack = 0; + + casez({req,addr[23:18],rom_ovl}) + 8'b100????0: cs_ram = 1; + 8'b100????1: cs_rom = 1; + 8'b1010000?: cs_rom = 1; + 8'b101011??: cs_scsi = 1; + 8'b1011????: cs_ram = 1; /* only ovl=1 ? */ + 8'b110?1???: cs_scc = 1; + 8'b11101???: cs_iwm = 1; + 8'b11110???: cs_via = 1; + 8'b1111111?: cs_ivec =1; + default: cs_nack = req; + endcase + end +endmodule + + + + \ No newline at end of file diff --git a/fpga/cpu_intf.v b/fpga/cpu_intf.v new file mode 100644 index 0000000..bc76d49 --- /dev/null +++ b/fpga/cpu_intf.v @@ -0,0 +1,526 @@ +`timescale 1ns / 100ps + +/* + * m68k CPU to internal bus interface + * + * Devices & Memory bus protocol: + * + * We implement a two phase bus protocol to the outside world + * + * - Address phase (phase 0) corresponds to CPU S4 & S5 + * + * - Data phase (phase 1) corrsponds to CPU S6 & S7 + * + * Devices shall typically act on the clock edge at the begining of each + * phase, tho phase 0 can generally be ignored for simple devices. Phase 0 + * shall not cause any action to be committed, but the phase 0 edge is + * when we sample the "ack" signal which allows transition to phase 1. + * + * Thus phase 0 is only here to allow devices to delay/hold the bus, which + * is used by SCSI to make blind transfer reliable and our memory interface + * because I'm an idiot and didn't properly interleave CPU and video. I will + * probably use it from the SWIM interface too when transfering raw data. + * + * Here are the signal that are valid at phase 0 and phase 1 clock + * edges. I also noted whether those signals remain valid in the clock + * cycle following phase 1 clock edge (ie, during phase 1 / S6/S7) since + * some devices such as memory rely on these. + * + * Signal Phase 0 clk Phase 1 clk Phase 1 + * + * bus_cs_xxx yes yes no (*) + * bus_we yes yes yes + * bus_ube/lbe no (**) yes no (**) + * bus_addr yes yes yes + * bus_wdata yes yes no (***) + * bus_phase 0 1 0 + * + * from device: + * + * bus_ack no yes no (****) + * bus_rdata no yes n/a + * + * (*) CS is lowered during phase 1 so that the next clock edge doesn't + * get mistaken for a new phase 0 cycle. + * + * (**) For a CPU initiated cycle, bus_ube/lbe are directly sourced from + * the m68k's _UDS and _LDS lines, which for a write are only asserted + * in S4, so right -after- phase 0 clock edge, and are released in S7 + * right after the negative edge following phase 1 clock edge. The + * memory interface routes those signal directly to the SRAM, the + * timing should be just right (with something like 15ns margin), but + * we might want to establish constraints in the FPGA design to ensure + * that. Alternatively, we could latch them on the clock negative edge + * and maintain them all the way during phase 1 but that isnt necessary + * for now I believe. + * + * (***) For a CPU initiate cycle, bus_wdata is sources directly from the + * m68k's data bus, which becomes invalid in S7, after the negedge + * of the clock. There's a 15ns valid time between UDS/LDS going + * up and data becoming invalid on a 16Mhz CPU, but here too we + * might want to add constraints to the synthesis tool to guarantee + * we stay within the margin. + * + * (****) The CPU DTACK is set from bus_ack and sampled asynchronously + * during phase 0 on the negative edge of the clock (end of S4), + * and by our CPU interface logic on phase 1 clk to validate + * actually going into phase 1 (else we remain in phase 0). + * Thus dumb devices can just wire CS to ACK and devices that + * want to hold the bus can do so by delaying ACK keeping the + * interface in phase 0. + * + * WARNING: While in phase 0, the CPU interface might decide to switch to + * another master (the SPI interface is the only one for now), + * so devices shouldn't commit/latch anything with the assumption + * that a subsequent phase 0 will provide the same cs/address/... + * signals. However, if a device does ack, then going into phase 1 + * on the next cycle is guaranteed. + */ + +/* Backbus interface registers + * + * WARNING: Writing to the ADDR, DATA and MISC latches should only be + * done while the bus is in stopped state. If an attempt is made to + * write a latch that the CPU tries to update in the same cycle, the CPU + * wins and the SPI write is lost. + * + * Similarily, reading from those latches may result in inconsistent + * values if done while the CPU interface is runnig as the SPI + * interface will latch the data at some "random" time after the + * strobe + */ +`define CPUINTF_REG_ADDR0 0 +`define CPUINTF_REG_ADDR1 1 +`define CPUINTF_REG_ADDR2 2 +`define CPUINTF_REG_DATA0 3 +`define CPUINTF_REG_DATA1 4 +`define CPUINTF_REG_MISC 5 /* bit 2: _UDS, bit 1: _LDS, bit 0: R_W */ +`define CPUINTF_REG_CTRL 6 +`define CPUINTF_CTRL_STOP_BIT 0 /* Stop CPU */ +`define CPUINTF_CTRL_RUN_BIT 1 /* Run CPU */ +`define CPUINTF_CTRL_CYCLE_BIT 2 /* Issue an SPI generated bus cycle */ +`define CPUINTF_CTRL_BKEN_BIT 6 /* Sticky, set with RUN to enable bkpt */ +`define CPUINTF_CTRL_STEP_BIT 7 /* Sticky, set with RUN to single step */ +`define CPUINTF_REG_STAT 7 /* bus state machine */ +`define CPUINTF_REG_BKPT0 8 +`define CPUINTF_REG_BKPT1 9 +`define CPUINTF_REG_BKPT2 10 + +module cpu_intf(input sysclk, + input reset, + + /* m68k CPU pins */ + inout [15:0] cpu_data, + input [23:1] cpu_addr, + input _cpu_as, + input _cpu_uds, + input _cpu_lds, + input cpu_r_w, + output _cpu_dtack, + /*inout _cpu_reset,*/ + + /* Backbus interface */ + input [5:0] bb_addr, + input [7:0] bb_wdata, + output[7:0] bb_rdata, + input bb_strobe, + input bb_wr, + + /* Common device bus interface */ + output [23:1] bus_addr, + output [15:0] bus_wdata, + output bus_ube, + output bus_lbe, + output bus_we, + output bus_phase, + + /* Per-device CS, ACK and data input */ + output bus_cs_ram, + output bus_cs_rom, + input bus_ack_mem, + input [15:0] bus_rdata_mem, /* RAM and ROM */ + output bus_cs_scsi, + input bus_ack_scsi, + input [7:0] bus_rdata_scsi, + output bus_cs_scc, + input bus_ack_scc, + input [7:0] bus_rdata_scc, + output bus_cs_via, + input bus_ack_via, + input [7:0] bus_rdata_via, + output bus_cs_iwm, + input bus_ack_iwm, + input [7:0] bus_rdata_iwm, + + /* Global control signals */ + input rom_ovl /* from VIA */ +); + + /* For autovector simulation since we have no _avec pin */ + wire cs_ivec; + + /* No target for a bus request */ + wire cs_nack; + + /* Bus request signal, activates chip selects */ + wire bus_req; + + /* Cumulative ack result from devices */ + wire bus_ack; + + /* Address data and control latches (for spy and SPI ops) */ + reg [23:1] addr_latch; + reg [15:0] data_latch; + reg _uds_latch; + reg _lds_latch; + reg r_w_latch; + + /* SPI control register */ + reg [7:0] spi_ctrl; + + /* Data bus mux */ + wire [15:0] bus_rdata; /* Demuxed from devices */ + + /* Breakpoint */ + reg [23:1] addr_bkpt; + wire addr_bphit; + + /* Signals from the SPI interface */ + wire spi_wreg; + wire spi_reg_addr0; + wire spi_reg_addr1; + wire spi_reg_addr2; + wire spi_reg_data0; + wire spi_reg_data1; + wire spi_reg_misc; + wire spi_reg_ctrl; + wire spi_reg_stat; + wire spi_reg_bkpt0; + wire spi_reg_bkpt1; + wire spi_reg_bkpt2; + + /* State machine */ + localparam bi_state_idle = 8'h84; /* idle, addr & r_w from CPU */ + localparam bi_state_cpu0 = 8'h85; /* cpu S4,S5 */ + localparam bi_state_cpu1 = 8'h82; /* cpu S6,S7 */ + localparam bi_state_spip = 8'h44; /* spi control, prep addr & r_w */ + localparam bi_state_spi0 = 8'h45; /* spi control, pseudo S4,S5 */ + localparam bi_state_spi1 = 8'h42; /* spi control, psuedo S6,S7 */ + localparam bi_state_stop = 8'h48; /* spi control, stopped */ + + /* Note on individual state bits:: + - 7 : CPU cycle + - 6 : SPI cycle + - 5 : unused + - 4 : unused + - 3 : stopped + - 2 : CS set based on _AS (or 1 for SPI) + - 1 : allow cpu data input and force dtack + - 0 : bus phase at next clock + + You may notice that we drop CS on CPU phase S6,S7. We must do + that for SPI as we must not have more than 2 phases with CS set + in one cycle, but we could leave bit 2 set for the CPU in that + stage as _AS is supposed to go down... doesn't matter much tho + */ + reg [7:0] state; + + /* Instanciate address decoder */ + addr_decode decode0(.addr(bus_addr), + .req(bus_req), + .rom_ovl(rom_ovl), + .cs_ram(bus_cs_ram), + .cs_rom(bus_cs_rom), + .cs_scsi(bus_cs_scsi), + .cs_scc(bus_cs_scc), + .cs_iwm(bus_cs_iwm), + .cs_via(bus_cs_via), + .cs_ivec(cs_ivec), + .cs_nack(cs_nack)); + + /* Generate bus address. Avoid propagating z state from the + * CPU to the FPGA by gating with _cpu_as + */ + assign bus_addr = (!state[7]) ? addr_latch : + _cpu_as ? 22'h3fffff : cpu_addr; + + /* Data bus to CPU is hi-z unless CPU has r_w up -and- we are + * in phase 1. Should keep us safe from shorts. We always feed + * the CPU from the data latch + */ + assign cpu_data = (cpu_r_w & state[1]) ? data_latch : 16'bz; + + /* Data bus and control signals to devices */ + assign bus_wdata = state[7] ? cpu_data : data_latch; + assign bus_ube = ~(state[7] ? _cpu_uds : _uds_latch); + assign bus_lbe = ~(state[7] ? _cpu_lds : _lds_latch); + assign bus_we = ~(state[7] ? cpu_r_w : r_w_latch); + + /* Address decoder's enable signal which controls CS signals */ + assign bus_req = state[2] & (state[6] | ~_cpu_as); + + /* Bus phase output straight off the state machine */ + assign bus_phase = state[0]; + + /* Data from devices demux (and generate autovector value) */ + assign bus_rdata = (bus_cs_ram | bus_cs_rom) ? bus_rdata_mem : + bus_cs_scsi ? { bus_rdata_scsi, 8'h0 } : + bus_cs_scc ? { bus_rdata_scc, 8'h0 } : + bus_cs_via ? { bus_rdata_via, 8'h0 } : + bus_cs_iwm ? { 8'h0, bus_rdata_iwm } : + cs_ivec ? { 11'b0, 2'b11, bus_addr[3:1] } : +`ifdef __IVERILOG__ + 16'hbeef; +`else + 16'b1111_1111_1111_1111; +`endif + + /* We OR all acks, devices shall not set ack if their CS isn't set + * additionally, we ack for ivec and nack signals from the decoder + * as those are handled locally + */ + assign bus_ack = bus_ack_mem | + bus_ack_scsi | + bus_ack_scc | + bus_ack_via | + bus_ack_iwm | + cs_ivec | cs_nack; +`ifdef __IVERILOG__ + /* Debug stuff, sim only */ + always@(posedge sysclk or posedge reset) begin + if (cs_nack && bus_we && bus_phase) begin + $write("%c", bus_wdata[15:8]); + end + end +`endif + + + /* Breakpoint check */ + assign addr_bphit = (cpu_addr == addr_bkpt) & + spi_ctrl[`CPUINTF_CTRL_BKEN_BIT]; + + /* Generate _DTACK for CPU cycles only. Note that the doc is + * unlcear as to whether we must keep it asserted until + * _AS goes back up or whether we can take it off as soon as + * state S5 or or later is reached. + * Better safe than sorry, we keep it down all the way for now + * at least until I have more than my crude sim to test with. + */ + assign _cpu_dtack = _cpu_as | ~state[7] | /* CPU cycle and AS asserted */ + (~state[1] & ~(state[0] & bus_ack)); + /* Bus state machine */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + state <= bi_state_stop; + end else + case(state) + bi_state_idle: begin + /* SPI takeover */ + if (spi_ctrl[`CPUINTF_CTRL_STOP_BIT]) + state <= bi_state_stop; + + /* If the CPU is trying to start a cycle, + * move up. CS will already have been generated + * asynchronously on the bus interface. We also + * detect breakpoints there + */ + else if (!_cpu_as) begin + if (addr_bphit) + state <= bi_state_stop; + else + state <= bi_state_cpu0; + end + + /* If the CPU is idle and we are stepping, + * we stop again + */ + else if (spi_ctrl[`CPUINTF_CTRL_STEP_BIT]) + state <= bi_state_stop; + end + bi_state_cpu0: begin + /* We got an ack from the device, move to + * phase 1 + */ + if (bus_ack) + state <= bi_state_cpu1; + + /* No ack, another try at SPI takeover */ + else if (spi_ctrl[`CPUINTF_CTRL_STOP_BIT]) + state <= bi_state_stop; + end + bi_state_cpu1: begin + /* If stepping, stop now */ + if (spi_ctrl[`CPUINTF_CTRL_STEP_BIT]) + state <= bi_state_stop; + + /* Back to idle */ + else + state <= bi_state_idle; + end + bi_state_spip: begin + state <= bi_state_spi0; + end + bi_state_spi0: begin + if (bus_ack) + state <= bi_state_spi1; + end + bi_state_spi1: begin + state <= bi_state_stop; + end + bi_state_stop: begin + /* Restart CPU */ + if (spi_ctrl[`CPUINTF_CTRL_RUN_BIT]) + state <= bi_state_idle; + + /* Start a simulated cycle */ + else if (spi_ctrl[`CPUINTF_CTRL_CYCLE_BIT]) + state <= bi_state_spip; + end + endcase + end + + /* Some SPI backbus interface combo decode logic */ + assign spi_wreg = bb_strobe && bb_wr; + assign spi_reg_addr0 = bb_addr[3:0] == `CPUINTF_REG_ADDR0; + assign spi_reg_addr1 = bb_addr[3:0] == `CPUINTF_REG_ADDR1; + assign spi_reg_addr2 = bb_addr[3:0] == `CPUINTF_REG_ADDR2; + assign spi_reg_data0 = bb_addr[3:0] == `CPUINTF_REG_DATA0; + assign spi_reg_data1 = bb_addr[3:0] == `CPUINTF_REG_DATA1; + assign spi_reg_misc = bb_addr[3:0] == `CPUINTF_REG_MISC; + assign spi_reg_ctrl = bb_addr[3:0] == `CPUINTF_REG_CTRL; + assign spi_reg_stat = bb_addr[3:0] == `CPUINTF_REG_STAT; + assign spi_reg_bkpt0 = bb_addr[3:0] == `CPUINTF_REG_BKPT0; + assign spi_reg_bkpt1 = bb_addr[3:0] == `CPUINTF_REG_BKPT1; + assign spi_reg_bkpt2 = bb_addr[3:0] == `CPUINTF_REG_BKPT2; + + /* Latches used by SPI interface to latch address/data + * when generating cycles. When under CPU control, they + * latch the last CPU cycle for single step mode + */ + + /* Address latch */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + addr_latch <= 0; + end else begin + /* Latch the CPU address for any CPU cycle in phase 0 */ + if (state[7]) begin + if (state[0]) + addr_latch <= cpu_addr; + end else if (spi_wreg) begin + /* Or fill from SPI */ + if (spi_reg_addr0) + addr_latch[23:16] <= bb_wdata; + else if (spi_reg_addr1) + addr_latch[15:8] <= bb_wdata; + else if (spi_reg_addr2) + addr_latch[7:1] <= bb_wdata[7:1]; + end + end + end + + /* Data latch. The CPU always feed from this, we latch the data + * from the device at phase 1 clock edge + */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + data_latch <= 0; + end else begin + /* Latch the bus data in phase 1 clock. Don't + * bother with ack, we'll latch a fresh value + * later if not set + */ + if (state[0]) begin + if (bus_we) + data_latch <= bus_wdata; + else + data_latch <= bus_rdata; + /* Else handle write from SPI */ + end else if (spi_wreg) begin + if (spi_reg_data0) + data_latch[15:8] <= bb_wdata; + else if (spi_reg_data1) + data_latch[7:0] <= bb_wdata; + end + end + end + + /* Misc latches (uds/lds/rw) */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + _uds_latch <= 0; + _lds_latch <= 0; + r_w_latch <= 0; + end else begin + /* Latch the CPU misc for any CPU cycle in phase 0 */ + if (state[7]) begin + if (state[0]) begin + _uds_latch <= _cpu_uds; + _lds_latch <= _cpu_lds; + r_w_latch <= cpu_r_w; + end + end else if (spi_wreg) begin + /* Or fill from SPI */ + if (spi_reg_misc) begin + _uds_latch <= bb_wdata[2]; + _lds_latch <= bb_wdata[1]; + r_w_latch <= bb_wdata[0]; + end + end + end + end + + /* SPI control register */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + spi_ctrl <= 0; + end else begin + /* Stop bit cleared whenever we are stopped */ + if (state[3]) + spi_ctrl[`CPUINTF_CTRL_STOP_BIT] <= 0; + /* Run bit cleared if we are in any CPU cycle */ + if (state[7]) + spi_ctrl[`CPUINTF_CTRL_RUN_BIT] <= 0; + /* Cycle bit cleared when we are in an SPI cycle */ + if (state[6]) + spi_ctrl[`CPUINTF_CTRL_CYCLE_BIT] <= 0; + + /* Now write logic from SPI. All bits are OR except + * for step and bken that is sticky + */ + if (spi_wreg && spi_reg_ctrl) begin + spi_ctrl[2:0] <= spi_ctrl[2:0] | bb_wdata[2:0]; + spi_ctrl[7:6] <= bb_wdata[7:6]; + end + end + end + + /* Breakpoint register */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + addr_bkpt <= 0; + end else begin + if (spi_wreg) begin + /* Or fill from SPI */ + if (spi_reg_bkpt0) + addr_bkpt[23:16] <= bb_wdata; + else if (spi_reg_bkpt1) + addr_bkpt[15:8] <= bb_wdata; + else if (spi_reg_bkpt2) + addr_bkpt[7:1] <= bb_wdata[7:1]; + end + end + end + + /* SPI backbus register read logic */ + assign bb_rdata = spi_reg_addr0 ? addr_latch[23:16] : + spi_reg_addr1 ? addr_latch[15:8] : + spi_reg_addr2 ? { addr_latch[7:1], 1'b0 } : + spi_reg_data0 ? data_latch[15:8] : + spi_reg_data1 ? data_latch[7:0] : + spi_reg_misc ? { 5'b0, _uds_latch, _lds_latch, + r_w_latch } : + spi_reg_ctrl ? spi_ctrl : + spi_reg_stat ? state : + 0; +endmodule diff --git a/fpga/ctrl.v b/fpga/ctrl.v new file mode 100644 index 0000000..2c1110e --- /dev/null +++ b/fpga/ctrl.v @@ -0,0 +1,68 @@ +/* RW: ID registers */ +`define CTRL_REG_FPGA_ID 0 +`define CTRL_REG_FPGA_VERSION 1 + +/* WO: Global control register */ +`define CTRL_REG_GCTRL 2 +`define GCTRL_CPU_UNRESET_BIT 0 + +/* RO: PS2 Mouse Debug */ +`define CTRL_REG_PS2M_DBG0 3 +`define CTRL_REG_PS2M_DBG1 4 + +/* RO: PS2 Kbd Debug */ +`define CTRL_REG_PS2K_DBG0 5 +`define CTRL_REG_PS2K_DBG1 6 + +module ctrl +( + input sysclk, + input reset, + + /* Backbus interface */ + input [5:0] bb_addr, + input [7:0] bb_wdata, + output [7:0] bb_rdata, + input bb_strobe, + input bb_wr, + + /* Debug stuff */ + input [15:0] ps2m_dbg, + input [15:0] ps2k_dbg, + + /* Generated control signals */ + output cpu_reset +); + /* Latched registers */ + reg [7:0] gctrl; + + assign cpu_reset = ~gctrl[`GCTRL_CPU_UNRESET_BIT]; + + assign bb_rdata = (bb_addr[2:0] == `CTRL_REG_FPGA_ID) ? 8'h42 : + (bb_addr[2:0] == `CTRL_REG_FPGA_VERSION) ? 8'h01 : + (bb_addr[2:0] == `CTRL_REG_GCTRL) ? gctrl : + (bb_addr[2:0] == `CTRL_REG_PS2M_DBG0) ? ps2m_dbg[15:8] : + (bb_addr[2:0] == `CTRL_REG_PS2M_DBG1) ? ps2m_dbg[7:0] : + (bb_addr[2:0] == `CTRL_REG_PS2K_DBG0) ? ps2k_dbg[15:8] : + (bb_addr[2:0] == `CTRL_REG_PS2K_DBG1) ? ps2k_dbg[7:0] : + 0; + + /* Register interface */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + gctrl <= 0; + end else begin + if (bb_strobe && bb_wr) begin + case(bb_addr[2:0]) + `CTRL_REG_GCTRL: + gctrl <= bb_wdata; + endcase + end + end + end +endmodule + + + + + diff --git a/fpga/fpga_clocks.v b/fpga/fpga_clocks.v new file mode 100644 index 0000000..74e62f7 --- /dev/null +++ b/fpga/fpga_clocks.v @@ -0,0 +1,71 @@ +module clockgen +( + input mclk, /* 4.433619 Mhz input */ + output sysclk16, /* System clock outputs */ + output sysclk33, /* System clock outputs NOT IMPLEMENTED, running at 16.6 */ + output pixclk /* Pixel clock */ +); + wire pll_mclk; + wire pll_c25; + wire pll_c16; + + IBUFG mclk_buf( .I(mclk), .O(pll_mclk)); + + /* Generate 16.6 for the DLL cpu*/ + DCM # + ( + .CLKDV_DIVIDE(2.0), // Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5, + // 7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0 + .CLKFX_DIVIDE(4), // Can be any integer from 1 to 32 + .CLKFX_MULTIPLY(15), // Can be any integer from 2 to 32 + .CLKIN_DIVIDE_BY_2("FALSE"), // TRUE/FALSE to enable CLKIN divide by two feature + .CLKIN_PERIOD(225.0), // Specify period of input clock + .CLKOUT_PHASE_SHIFT("NONE"), // Specify phase shift of NONE, FIXED or VARIABLE + .CLK_FEEDBACK("NONE"), // Specify clock feedback of NONE, 1X or 2X + .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or an integer from 0 to 15 + .DFS_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for frequency synthesis + .DLL_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for DLL + .DUTY_CYCLE_CORRECTION("TRUE"), // Duty cycle correction, TRUE or FALSE + .FACTORY_JF(16'hC080), // FACTORY JF values + .PHASE_SHIFT(0), // Amount of fixed phase shift from -255 to 255 + .STARTUP_WAIT("FALSE") // Delay configuration DONE until DCM LOCK, TRUE/FALSE + ) + pllmain + ( + .CLKFX(pll_c16), // DCM CLK synthesis out (M/D) + .CLKIN(pll_mclk) // Clock input (from IBUFG, BUFG or DCM) + ); + + /* Generate a rough 25.1Mhz for the video */ + DCM # + ( + .CLKDV_DIVIDE(3.0), // Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5, + // 7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0 + .CLKFX_DIVIDE(3), // Can be any integer from 1 to 32 +// .CLKFX_MULTIPLY(16), // Can be any integer from 2 to 32 + .CLKFX_MULTIPLY(17), // Can be any integer from 2 to 32 + .CLKIN_DIVIDE_BY_2("FALSE"), // TRUE/FALSE to enable CLKIN divide by two feature + .CLKIN_PERIOD(225.0), // Specify period of input clock + .CLKOUT_PHASE_SHIFT("NONE"), // Specify phase shift of NONE, FIXED or VARIABLE + .CLK_FEEDBACK("NONE"), // Specify clock feedback of NONE, 1X or 2X + .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or an integer from 0 to 15 + .DFS_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for frequency synthesis + .DLL_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for DLL + .DUTY_CYCLE_CORRECTION("TRUE"), // Duty cycle correction, TRUE or FALSE + .FACTORY_JF(16'hC080), // FACTORY JF values + .PHASE_SHIFT(0), // Amount of fixed phase shift from -255 to 255 + .STARTUP_WAIT("FALSE") // Delay configuration DONE until DCM LOCK, TRUE/FALSE + ) + pllpix + ( + .CLKFX(pll_c25), // DCM CLK synthesis out (M/D) + .CLKIN(pll_mclk) // Clock input (from IBUFG, BUFG or DCM) + ); + + /* Global clock buffers */ + BUFG clk33_buf( .I(pll_c16), .O(sysclk33)); /* NOT IMPL. RUNNING AT 16Mhz */ + BUFG clk16_buf( .I(pll_c16), .O(sysclk16)); + + /* Temporarily assign pixclock to 33Mhz, will fix later */ + BUFG pixclk_buf( .I(pll_c25), .O(pixclk)); +endmodule // clocks diff --git a/fpga/fpga_clocks_dll.v b/fpga/fpga_clocks_dll.v new file mode 100644 index 0000000..29194f5 --- /dev/null +++ b/fpga/fpga_clocks_dll.v @@ -0,0 +1,102 @@ +module clockgen +( + input mclk, /* 4.433619 Mhz input */ + output sysclk16, /* System clock outputs */ + output sysclk33, /* System clock outputs */ + output pixclk /* Pixel clock */ +); + wire pll_mclk; + wire pll_c33; + wire pll_c25; + wire dll_c33; + wire dll_c16; + + IBUFG mclk_buf( .I(mclk), .O(pll_mclk)); + + /* Generate 33.25 for the DLL cpu*/ + DCM # + ( + .CLKDV_DIVIDE(2.0), // Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5, + // 7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0 + .CLKFX_DIVIDE(2), // Can be any integer from 1 to 32 + .CLKFX_MULTIPLY(15), // Can be any integer from 2 to 32 + .CLKIN_DIVIDE_BY_2("FALSE"), // TRUE/FALSE to enable CLKIN divide by two feature + .CLKIN_PERIOD(225.0), // Specify period of input clock + .CLKOUT_PHASE_SHIFT("NONE"), // Specify phase shift of NONE, FIXED or VARIABLE + .CLK_FEEDBACK("NONE"), // Specify clock feedback of NONE, 1X or 2X + .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or an integer from 0 to 15 + .DFS_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for frequency synthesis + .DLL_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for DLL + .DUTY_CYCLE_CORRECTION("TRUE"), // Duty cycle correction, TRUE or FALSE + .FACTORY_JF(16'hC080), // FACTORY JF values + .PHASE_SHIFT(0), // Amount of fixed phase shift from -255 to 255 + .STARTUP_WAIT("FALSE") // Delay configuration DONE until DCM LOCK, TRUE/FALSE + ) + pllmain + ( + .CLKFX(pll_c33), // DCM CLK synthesis out (M/D) + .CLKIN(pll_mclk) // Clock input (from IBUFG, BUFG or DCM) + ); + + /* Generate a rough 25.1Mhz for the video */ + DCM # + ( + .CLKDV_DIVIDE(3.0), // Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5, + // 7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0 + .CLKFX_DIVIDE(3), // Can be any integer from 1 to 32 + .CLKFX_MULTIPLY(17), // Can be any integer from 2 to 32 + .CLKIN_DIVIDE_BY_2("FALSE"), // TRUE/FALSE to enable CLKIN divide by two feature + .CLKIN_PERIOD(225.0), // Specify period of input clock + .CLKOUT_PHASE_SHIFT("NONE"), // Specify phase shift of NONE, FIXED or VARIABLE + .CLK_FEEDBACK("NONE"), // Specify clock feedback of NONE, 1X or 2X + .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or an integer from 0 to 15 + .DFS_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for frequency synthesis + .DLL_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for DLL + .DUTY_CYCLE_CORRECTION("TRUE"), // Duty cycle correction, TRUE or FALSE + .FACTORY_JF(16'hC080), // FACTORY JF values + .PHASE_SHIFT(0), // Amount of fixed phase shift from -255 to 255 + .STARTUP_WAIT("FALSE") // Delay configuration DONE until DCM LOCK, TRUE/FALSE + ) + pllpix + ( + .CLKFX(pll_c25), // DCM CLK synthesis out (M/D) + .CLKIN(pll_mclk) // Clock input (from IBUFG, BUFG or DCM) + ); + + /* DLL that 33.25Mhz input and generate the 16.6 from it. Note: The Xilinx tools don't + * seem to like when you do that ;-) Looks like we should really have put a faster + * crystal on the board + */ + DCM # + ( + .CLKDV_DIVIDE(2.0), // Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5, + // 7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0 + .CLKFX_DIVIDE(2), // Can be any integer from 1 to 32 + .CLKFX_MULTIPLY(2), // Can be any integer from 2 to 32 + .CLKIN_DIVIDE_BY_2("FALSE"), // TRUE/FALSE to enable CLKIN divide by two feature + .CLKIN_PERIOD(30.0), // Specify period of input clock + .CLKOUT_PHASE_SHIFT("NONE"), // Specify phase shift of NONE, FIXED or VARIABLE + .CLK_FEEDBACK("1X"), // Specify clock feedback of NONE, 1X or 2X + .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or an integer from 0 to 15 + .DFS_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for frequency synthesis + .DLL_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for DLL + .DUTY_CYCLE_CORRECTION("TRUE"), // Duty cycle correction, TRUE or FALSE + .FACTORY_JF(16'hC080), // FACTORY JF values + .PHASE_SHIFT(0), // Amount of fixed phase shift from -255 to 255 + .STARTUP_WAIT("FALSE") // Delay configuration DONE until DCM LOCK, TRUE/FALSE + ) + dll + ( + .CLKIN(pll_c33), // Clock input (from IBUFG, BUFG or DCM) + .CLK0(dll_c33), // 0 degree DCM CLK output + .CLKDV(dll_c16), // Divided DCM CLK out (CLKDV_DIVIDE) + .CLKFB(sysclk33) // DCM clock feedback + ); + + /* Global clock buffers */ + BUFG clk33_buf( .I(dll_c33), .O(sysclk33)); + BUFG clk16_buf( .I(dll_c16), .O(sysclk16)); + + /* Temporarily assign pixclock to 33Mhz, will fix later */ + BUFG pixclk_buf( .I(pll_c25), .O(pixclk)); +endmodule // clocks diff --git a/fpga/iwm.v b/fpga/iwm.v new file mode 100644 index 0000000..52ee8aa --- /dev/null +++ b/fpga/iwm.v @@ -0,0 +1,221 @@ +`timescale 1ns / 100ps + +/* + * IWM module for minimigmac. + * + * Location to Location to + * IWM line turn line on turn line off + * + * Disk state-control lines: + * CA0 dBase+ph0H dBase+ph0L + * CA1 dBase+ph1H dBase+ph1L + * CA2 dBase+ph2H dBase+ph2L + * LSTRB dBase+ph3H dBase+ph3L + * + * Disk enable line: + * ENABLE dBase+motorOn dBase+motorOff + * + * IWM internal states: + * SELECT dBase+extDrive dBase+intDrive + * Q6 dBase+q6H dBase+q6L + * Q7 dBase+q7H dBase+q7L + * + * ph0L .EQU 512*0 ;CA0 off (0) + * ph0H .EQU 512*1 ;CA0 on (1) + * ph1L .EQU 512*2 ;CA1 off (0) + * ph1H .EQU 512*3 ;CA1 on (1) + * ph2L .EQU 512*4 ;CA2 off (0) + * ph2H .EQU 512*5 ;CA2 on (1) + * ph3L .EQU 512*6 ;LSTRB off (low) + * ph3H .EQU 512*7 ;LSTRB on (high) + * mtrOff .EQU 512*8 ;disk enable off + * mtrOn .EQU 512*9 ;disk enable on + * intDrive .EQU 512*10 ;select internal drive + * extDrive .EQU 512*11 ;select external drive + * q6L .EQU 512*12 ;Q6 off + * q6H .EQU 512*13 ;Q6 on + * q7L .EQU 512*14 ;Q7 off + * q7H .EQU 512*15 ;Q7 on + */ + +`define IWM_REG_ph0L 0 +`define IWM_REG_ph0H 1 +`define IWM_REG_ph1L 2 +`define IWM_REG_ph1H 3 +`define IWM_REG_ph2L 4 +`define IWM_REG_ph2H 5 +`define IWM_REG_ph3L 6 +`define IWM_REG_ph3H 7 +`define IWM_REG_mtrOff 8 +`define IWM_REG_mtrOn 9 +`define IWM_REG_intDrive 10 +`define IWM_REG_extDrive 11 +`define IWM_REG_q6L 12 +`define IWM_REG_q6H 13 +`define IWM_REG_q7L 14 +`define IWM_REG_q7H 15 + + +module iwm(input sysclk, + input reset, + + /* Bus interface. 4-bit address, to be wired + * appropriately upstream (to A9..A12) + */ + input cs, + input we, + output ack, + input phase, + input [3:0] rs, + input [7:0] wdata, + output [7:0] rdata, + + /* Backbus interface */ + input [5:0] bb_addr, + input [7:0] bb_wdata, + output reg [7:0] bb_rdata, + input bb_strobe, + input bb_wr, + + /* SEL line from VIA */ + input via_sel + ); + + /* IWM state register broken up by name */ + reg CA0; + reg CA1; + reg CA2; + reg LSTRB; + reg ENABLE; + reg SELECT; + reg Q6; + reg Q7; + + /* Internal wires */ + wire new_Q6; + wire new_Q7; + wire new_EN; + wire reg_wr; + + /* Internal data & mode registers */ + reg [7:0] data_reg; + reg [4:0] mode_reg; + + /* Status and write handshake are just wires */ + wire [7:0] stat_reg; + wire [7:0] whsk_reg; + + /* Sense line from disk */ + wire sense; + + /* enbl1 and enbl2 lines (not inverted here unlike real HW) */ + reg enbl1; + reg enbl2; + + /* Disk register definitions */ + wire [3:0] diskreg_sel; +`define DISK_REG_DIRTN 0 /* R/W: step direction */ +`define DISK_REG_CSTIN 1 /* R: disk in place (1 = no disk) */ + /* W: ?? reset disk switch flag ? */ +`define DISK_REG_STEP 2 /* R: disk stepping (1 = complete) */ + /* W: 0 = step disk */ +`define DISK_REG_WRTPRT 3 /* R: 0 = disk is write-protected */ +`define DISK_REG_MOTORON 4 /* R/W: 0 = motor on */ +`define DISK_REG_TK0 5 /* R: 0 = head at track 0 */ +`define DISK_REG_EJECT 6 /* R: disk switched */ + /* W: 1 = eject disk */ +`define DISK_REG_TACH 7 /* R: tach-o-meter */ +`define DISK_REG_RDDATA0 8 /* R: lower head activate */ +`define DISK_REG_RDDATA9 9 /* R: upper head activate */ +`define DISK_REG_SIDES 12 /* R: number of sides (0=single, 1=dbl) */ +`define DISK_REG_READY 13 /* R: disk ready (head loaded) (0=ready) */ +`define DISK_REG_INSTALLED 14 /* R: drive present (0 = yes) */ +`define DISK_REG_DRVIN 15 /* R: drive present (0=yes, 1=no) XXX HD ?*/ + + /* Toggle control lines */ + always@(posedge reset or posedge sysclk) begin + if (reset) begin + CA0 <= 0; + CA1 <= 0; + CA2 <= 0; + LSTRB <= 0; + ENABLE <= 0; + SELECT <= 0; + Q6 <= 0; + Q7 <= 0; + end else if (cs && phase) begin + case(rs[3:1]) + 0: CA0 <= rs[0]; + 1: CA1 <= rs[0]; + 2: CA2 <= rs[0]; + 3: LSTRB <= rs[0]; + 4: ENABLE <= rs[0]; + 5: SELECT <= rs[0]; + 6: Q6 <= rs[0]; + 7: Q7 <= rs[0]; + endcase + end + end + + /* ENBL1/2 lines. XXX We don't do the 1 second timer yet + * but I don't think the Mac ROM uses it anyways + */ + always@(posedge reset or posedge sysclk) begin + if (reset) begin + enbl1 <= 0; + enbl2 <= 0; + end else begin + if (cs && phase && rs[3:1] == 4) begin + if (SELECT) + enbl2 <= rs[0]; + else + enbl1 <= rs[0]; + end + end + end + + /* Now for register read/writes, we need to use + * the "new" value of Q6, Q7 and ENABLE in the same + * cycle. We hack that up here. + */ + assign new_Q6 = (cs && rs[3:1] == 6) ? rs[0] : Q6; + assign new_Q7 = (cs && rs[3:1] == 7) ? rs[0] : Q7; + assign new_EN = (cs && rs[3:1] == 4) ? rs[0] : ENABLE; + assign reg_wr = cs & phase & new_Q6 & new_Q7 & rs[0]; + + /* Internal register reads */ + assign rdata = (!new_Q7 && !new_Q6 && new_EN) ? data_reg : + (!new_Q7 && new_Q6) ? stat_reg : + ( new_Q7 && !new_Q6) ? whsk_reg : 8'hff; + + /* Internal write to mode register */ + always@(posedge reset or posedge sysclk) begin + if (reset) + mode_reg <= 0; + else if (reg_wr && !ENABLE) + mode_reg <= wdata[4:0]; + end + + /* Make up status and write handshake register wires */ + assign stat_reg = { sense, 1'b0, enbl1 | enbl2, mode_reg[4:0] }; + assign whsk_reg = 8'b11000000; /* XXX fixme */ + + /* Disk register reads */ +// assign diskreg_idx = { CA2, CA1, CA0, via_sel }; +// assign sense = diskregs = SELECT ? diskreg_b[diskreg_idx] : +// diskreg_a[diskreg_idx]; + assign sense = 1'b1; + + /* Test values for disk regs */ + always@(posedge reset or posedge sysclk) begin + if (reset) begin +// diskregs[0] <= 16'b1001_1100_0001_0110; +// diskregs[1] <= 16'b1111_1111_1111_1111; + end + end + + /* For now always ack immediately, we'll change that + * eventually when we have something with real data + */ + assign ack = cs; +endmodule diff --git a/fpga/mem_intf.v b/fpga/mem_intf.v new file mode 100644 index 0000000..5f0d106 --- /dev/null +++ b/fpga/mem_intf.v @@ -0,0 +1,391 @@ +/* + * Memory interface for Minimigmac + * + * Wraps the SRAM chips on the Minimig board and provides + * support for RAM and ROM access from the processor, + * backbus access via SPI, and "DMA" sound and video buffers + * + * Notes about video: We use a simple req edge based asynchronous + * protocol since we are running the main logic separately from + * the video timing generator. On each handshake, the memory + * interface will fetch the next 16 pixels. Video addresses are + * calculated internally based on the buffer select VIA bit for + * a fixed resolution of 512x342. + * + * Starting addresses of the video buffers depend on the model + * and amount of RAM. We emulate a 2Mb configuration but for + * completeness, here are the address for all supported + * configurations: + * + * System Main Screen Alternate + * + * Macintosh Plus, 1Mb $FA700 $F2700 + * Macintosh Plus, 2Mb $1FA700 $1F2700 + * Macintosh Plus, 2.5Mb $27A700 $272700 + * Macintosh Plus, 4Mb $3FA700 $3F2700 + * + * The above addresses look confusing but basically it's really + * just hard wired chip select I beleive on the real mac tho for + * now we just use a full blown latch to hold the address since + * we have silicon to spare :-) + * + * The total size of the video buffer is 0x5580 bytes + * + * For audio, we have: + * + * System Main Sound Alternate + * Macintosh Plus, 1Mb $FFD00 $FA100 + * Macintosh Plus, 2Mb $1FFD00 $1FA100 + * Macintosh Plus, 2.5Mb $27FD00 $27A100 + * Macintosh Plus, 4Mb $3FFD00 $3FA100 + * + * Implementation note: + * + * This is sub-optimal. We have a 3 cycle round trip, which could + * probably go down to one or two (we have a mandatory idle cycle + * between 2-cycle accesses). Works for now and is easy tho. + * + * The memory doesn't seem to require any hold times, on synthetized + * cycles (video, SPI, ...) we establish everything at once and leave + * the signals "on" for 2 cycles. On CPU initiated cycles, some + * signals are established later, see comments in the CPU interface + * module, but overall it fits the timings. The idle cycle should give + * us the necessary resting time to avoid cases of both the memory and + * the FPGA trying to drive the data bus. + */ + +module mem_intf(input sysclk, + input reset, + + /* SRAM pins */ + inout [15:0] ram_data, + output [19:1] ram_address, + output [3:0] _ram_ce, + output _ram_bhe, + output _ram_ble, + output _ram_we, + output _ram_oe, + + /* Bus interface */ + input bus_cs_ram, + input bus_cs_rom, + input bus_we, + output bus_ack, + input bus_ube, + input bus_lbe, + input bus_phase, + input [15:0] bus_wdata, + output [15:0] bus_rdata, + input [22:1] bus_addr, + + /* Backbus interface */ + input [5:0] bb_addr, + input [7:0] bb_wdata, + output [7:0] bb_rdata, + input bb_strobe, + input bb_wr, + + /* Asynchronous video interface */ + input vid_bufsel, + input vid_req, + output reg vid_ack, + output reg [15:0] vid_pixels + + /* TODO: Audio */ + ); + + /* Owner of interface */ + wire own_bus; + wire own_vid; + wire own_spi; + + /* Cooked bus address (ram/rom selection) */ + wire [21:1] cooked_address; + /* Latch of cs_rom vs. cs_ram */ + reg rom_select; + + /* SPI byte write latch */ + reg [21:0] spi_addr; + reg [7:0] spi_data; + reg spi_pending; + reg spi_we; + + /* SPI decode signals */ + wire spi_wreg; + wire spi_reg_addr0; + wire spi_reg_addr1; + wire spi_reg_addr2; + wire spi_reg_data; + + /* Muxed/cooked RAM control signals */ + wire [15:0] ram_data_out; + wire [21:1] ram_addr; + + /* Main memory enable */ + wire ram_en; + + /* Video address generator. Buffer select bit is + * inserted in there at bit 15. We cound down tho + * eventually I may replace that with a reset input + * from the video circuitry + */ + parameter vid_buf_base = 'h1F2700 / 2; + parameter vid_buf_size = 'h2ac0; + reg [21:1] vid_addr; + reg [13:0] vid_words; + wire vid_latch_pix; + + /* vid_req/vid_ack logic */ + reg [2:0] vid_req_sync; + reg vid_req_pending; + wire vid_ack_reset; + + /* State machine */ + localparam state_idle = 8'h10; /* idle, ram off, owner is bus */ + localparam state_bus0 = 8'h11; /* bus access phase 0 */ + localparam state_bus1 = 8'h13; /* bus access phase 1 */ + localparam state_spi0 = 8'h21; /* spi access phase 0 */ + localparam state_spi1 = 8'h23; /* spi access phase 1 */ + localparam state_vid0 = 8'h41; + localparam state_vid1 = 8'h47; + + /* State bits breakout: + * + * 7: + * 6: own bit video + * 5: own bit spi + * 4: own bit bus + * 3: + * 2: inc address (vid/audio) + * 1: bus phase + * 0: ram active + */ + reg [7:0] state; + + /* Own lines off the state machine */ + assign own_bus = state[4]; + assign own_spi = state[5]; + assign own_vid = state[6]; + + /* First setup ram_data_out, _ram_we and ram_addr based + * on owner. ram_data_out is then applied to ram_data or + * not based on _ram_we. + * + * XX. Remove the "default" case, make it SPI + */ + assign ram_data_out = own_bus ? bus_wdata : + own_spi ? { spi_data, spi_data } : + 16'b1; + + assign ram_en = state[0]; + assign _ram_we = (own_bus ? (bus_cs_rom | ~bus_we) : + own_spi ? ~spi_we : 1'b1) | ~ram_en; + assign _ram_bhe = own_bus ? ~bus_ube : + own_spi ? spi_addr[0] : + own_vid ? 1'b0 : + 1'b1; + assign _ram_ble = own_bus ? ~bus_lbe : + own_spi ? ~spi_addr[0] : + own_vid ? 1'b0 : + 1'b1; + + /* Cook RAM address based on RAM/ROM selection + * We have 2M of RAM and 128k of ROM repeated + */ + assign cooked_address = rom_select ? + { 5'b10000, bus_addr[16:1] } : + { 1'b0, bus_addr[20:1] }; + + + /* Mux appropriate source of RAM address */ + assign ram_addr = own_bus ? cooked_address : + own_spi ? spi_addr[21:1] : + own_vid ? vid_addr : 0; + + /* Do ram_data input based on _ram_we */ + assign ram_data = _ram_we ? 16'bz : ram_data_out; + + /* And set ram output enable accordingly */ + assign _ram_oe = ~_ram_we | ~ram_en; + + /* Split ram_addr into banks */ + assign ram_address = ram_addr[19:1]; + assign _ram_ce[0] = ~(ram_addr[21:20] == 2'b00) | ~state[0]; + assign _ram_ce[1] = ~(ram_addr[21:20] == 2'b01) | ~state[0]; + assign _ram_ce[2] = ~(ram_addr[21:20] == 2'b10) | ~state[0]; + assign _ram_ce[3] = ~(ram_addr[21:20] == 2'b11) | ~state[0]; + + /* RAM to bus data */ + assign bus_rdata = own_bus ? ram_data : 16'b1; + + /* Ack for bus request in phase 0. WARNING: Must reproduce + * the state machine logic in state_idle + */ + assign bus_ack = state == state_bus0; + + /* Latch rom_select on cs transitions */ + always@(posedge sysclk or posedge reset) begin + if (reset) + rom_select <= 0; + else begin + if (bus_cs_ram) + rom_select <= 0; + else if (bus_cs_rom) + rom_select <= 1; + end + end + + /* State machine */ + always@(posedge sysclk or posedge reset) begin + if (reset) + state <= state_idle; + else + case(state) + state_idle: begin + /* Video request */ + if (vid_req_pending) + state <= state_vid0; + /* SPI request */ + else if (spi_pending) + state <= state_spi0; + /* CPU request */ + else if (bus_cs_ram || bus_cs_rom) + state <= state_bus0; + end + state_bus0: begin + /* There should be no possible abort here + * since we have ack out + */ + state <= state_bus1; + end + state_bus1: state <= state_idle; + state_spi0: state <= state_spi1; + state_spi1: state <= state_idle; + state_vid0: state <= state_vid1; + state_vid1: state <= state_idle; + endcase + end + + /* vid_req input synchronizer and edge detect */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + vid_req_sync <= 3'b000; + vid_req_pending <= 0; + end else begin + vid_req_sync[2] <= vid_req_sync[1]; + vid_req_sync[1] <= vid_req_sync[0]; + vid_req_sync[0] <= vid_req; + if (vid_req_sync[2] == 0 && vid_req_sync[1]) + vid_req_pending <= 1; + else if (own_vid) + vid_req_pending <= 0; + end + end + + assign vid_ack_reset = reset | ~vid_req_sync[1]; + assign vid_latch_pix = state == state_vid0; + + /* vid_ack generation, synchronous set, asynchronous reset */ + always@(posedge sysclk or posedge vid_ack_reset) begin + if (vid_ack_reset) + vid_ack <= 1'b0; + else if (vid_latch_pix) + vid_ack <= 1'b1; + end + + /* vid address calculation */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + vid_addr <= vid_buf_base; + vid_addr[15] <= vid_bufsel; + vid_words <= vid_buf_size-1; + end else begin + if (state[2]) begin + if (vid_words == 0) begin + vid_addr <= vid_buf_base; + vid_addr[15] <= vid_bufsel; + vid_words <= vid_buf_size-1; + end else begin + vid_addr <= vid_addr + 1; + vid_words <= vid_words - 1; + end + end + end + end + + /* latch pixels */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + vid_pixels <= 0; + end else begin + if (vid_latch_pix) begin + vid_pixels <= ram_data; + end + end + end + + /* SPI register interface */ +`define MEMINTF_REG_ADDR0 0 +`define MEMINTF_REG_ADDR1 1 +`define MEMINTF_REG_ADDR2 2 +`define MEMINTF_REG_DATA 3 + assign spi_wreg = bb_strobe && bb_wr; + assign spi_reg_addr0 = bb_addr[2:0] == `MEMINTF_REG_ADDR0; + assign spi_reg_addr1 = bb_addr[2:0] == `MEMINTF_REG_ADDR1; + assign spi_reg_addr2 = bb_addr[2:0] == `MEMINTF_REG_ADDR2; + assign spi_reg_data = bb_addr[2:0] == `MEMINTF_REG_DATA; + + /* SPI control */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + spi_pending <= 0; + spi_we <= 0; + end else begin + if (bb_strobe && spi_reg_data) begin + spi_pending <= 1; + spi_we <= bb_wr; + end else if (own_spi) begin + spi_pending <= 0; + end + end + end + + /* SPI data latch */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + spi_data <= 0; + end else begin + if (spi_wreg && spi_reg_data) begin + spi_data <= bb_wdata; + end else if (own_spi && !spi_we && state[1]) begin + spi_data <= spi_addr[0] ? ram_data[7:0] : + ram_data[15:8]; + end + end + end + + /* SPI address latch write */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + spi_addr <= 0; + end else begin + if (own_spi && state[1]) begin + spi_addr <= spi_addr + 1; + end else if (spi_wreg) begin + if (spi_reg_addr0) + spi_addr[21:16] <= bb_wdata[5:0]; + else if (spi_reg_addr1) + spi_addr[15:8] <= bb_wdata; + else if (spi_reg_addr2) + spi_addr[7:0] <= bb_wdata; + end + end + end + + /* SPI backbus register read logic */ + assign bb_rdata = spi_reg_addr0 ? { 2'b0, spi_addr[21:16] } : + spi_reg_addr1 ? spi_addr[15:8] : + spi_reg_addr2 ? spi_addr[7:0] : + spi_reg_data ? spi_data : + 0; +endmodule \ No newline at end of file diff --git a/fpga/minimigmac.ucf b/fpga/minimigmac.ucf new file mode 100644 index 0000000..dc87492 --- /dev/null +++ b/fpga/minimigmac.ucf @@ -0,0 +1,199 @@ +INST "ram_data<0>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<1>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<2>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<3>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<4>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<5>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<6>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<7>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<8>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<9>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<10>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<11>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<12>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<13>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<14>" TNM = "RAMDAT"; #SRAM data +INST "ram_data<15>" TNM = "RAMDAT"; #SRAM data +INST "ram_address<1>" TNM = "RAMADD"; #SRAM address +INST "ram_address<2>" TNM = "RAMADD"; #SRAM address +INST "ram_address<3>" TNM = "RAMADD"; #SRAM address +INST "ram_address<4>" TNM = "RAMADD"; #SRAM address +INST "ram_address<5>" TNM = "RAMADD"; #SRAM address +INST "ram_address<6>" TNM = "RAMADD"; #SRAM address +INST "ram_address<7>" TNM = "RAMADD"; #SRAM address +INST "ram_address<8>" TNM = "RAMADD"; #SRAM address +INST "ram_address<9>" TNM = "RAMADD"; #SRAM address +INST "ram_address<10>" TNM = "RAMADD"; #SRAM address +INST "ram_address<11>" TNM = "RAMADD"; #SRAM address +INST "ram_address<12>" TNM = "RAMADD"; #SRAM address +INST "ram_address<13>" TNM = "RAMADD"; #SRAM address +INST "ram_address<14>" TNM = "RAMADD"; #SRAM address +INST "ram_address<15>" TNM = "RAMADD"; #SRAM address +INST "ram_address<16>" TNM = "RAMADD"; #SRAM address +INST "ram_address<17>" TNM = "RAMADD"; #SRAM address +INST "ram_address<18>" TNM = "RAMADD"; #SRAM address +INST "ram_address<19>" TNM = "RAMADD"; #SRAM address +INST "_ram_bhe" TNM = "RAMCTL"; #SRAM address and control +INST "_ram_ble" TNM = "RAMCTL"; #SRAM address and control +INST "_ram_we" TNM = "RAMCTL"; #SRAM address and control +INST "_ram_oe" TNM = "RAMCTL"; #SRAM address and control +INST "_ram_ce[0]" TNM = "RAMCTL"; #SRAM address and control +INST "_ram_ce[1]" TNM = "RAMCTL"; #SRAM address and control +INST "_ram_ce[2]" TNM = "RAMCTL"; #SRAM address and control +INST "_ram_ce[3]" TNM = "RAMCTL"; #SRAM address and control +INST "_cpu_as" TNM = "CPU_AS"; +INST "_cpu_uds" TNM = "CPU_DS"; +INST "_cpu_lds" TNM = "CPU_DS"; +INST "_cpu_dtack" TNM = "CPU_DTACK"; +#TIMESPEC TS11 = FROM "FFS" TO "RAMCTL" 33 ns; +#TIMESPEC TS12 = FROM "RAMDAT" TO "FFS" 33 ns; +#TIMESPEC TS13 = FROM "FFS" TO "RAMDAT" 33 ns; +#TIMESPEC TS14 = FROM "FFS" TO "RAMADD" 33 ns; +TIMESPEC TS14 = FROM "CPU_DS" TO "RAMCTL" 20 ns; +TIMESPEC TS14 = FROM "CPU_AS" TO "RAMCTL" 20 ns; +TIMESPEC TS15 = FROM "CPU_AS" TO "CPU_DTACK" 20 ns; +#TIMESPEC TS16 = FROM "RAMDAT" TO "CPU_DTACK" 20 ns; +#TIMESPEC TS17 = FROM "CPU_DS" TO "CPU_DTACK" 20 ns; + +#PACE: Start of Constraints generated by PACE +#PACE: Start of PACE I/O Pin Assignments +NET "mclk" LOC = "P80" |IOSTANDARD = LVCMOS33 |PERIOD = 225; +NET "_cpu_reset" LOC = "P95" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |PULLUP; +NET "cpu_clk" LOC = "P101" |IOSTANDARD = LVCMOS33 |SLEW = FAST |DRIVE = 24; +NET "_cpu_dtack" LOC = "P102" |IOSTANDARD = LVCMOS33 |SLEW = FAST |DRIVE = 12; +NET "cpu_r_w" LOC = "P106" |IOSTANDARD = LVCMOS33 ; +NET "_cpu_as" LOC = "P109" |IOSTANDARD = LVCMOS33 ; +NET "_cpu_uds" LOC = "P108" |IOSTANDARD = LVCMOS33 ; +NET "_cpu_lds" LOC = "P107" |IOSTANDARD = LVCMOS33 ; +NET "_cpu_ipl[0]" LOC = "P96" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "_cpu_ipl[1]" LOC = "P97" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "_cpu_ipl[2]" LOC = "P100" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "cpu_address[10]" LOC = "P149" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[11]" LOC = "P148" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[12]" LOC = "P147" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[13]" LOC = "P146" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[14]" LOC = "P144" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[15]" LOC = "P143" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[16]" LOC = "P141" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[17]" LOC = "P140" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[18]" LOC = "P139" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[19]" LOC = "P138" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[1]" LOC = "P166" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[20]" LOC = "P137" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[21]" LOC = "P135" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[22]" LOC = "P133" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[23]" LOC = "P132" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[2]" LOC = "P165" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[3]" LOC = "P162" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[4]" LOC = "P161" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[5]" LOC = "P156" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[6]" LOC = "P155" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[7]" LOC = "P154" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[8]" LOC = "P152" |IOSTANDARD = LVCMOS33 ; +NET "cpu_address[9]" LOC = "P150" |IOSTANDARD = LVCMOS33 ; +NET "cpu_data[0]" LOC = "P111" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[10]" LOC = "P124" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[11]" LOC = "P125" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[12]" LOC = "P126" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[13]" LOC = "P128" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[14]" LOC = "P130" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[15]" LOC = "P131" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[1]" LOC = "P113" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[2]" LOC = "P114" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[3]" LOC = "P115" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[4]" LOC = "P116" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[5]" LOC = "P117" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[6]" LOC = "P119" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[7]" LOC = "P120" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[8]" LOC = "P122" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "cpu_data[9]" LOC = "P123" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "_ram_ce[0]" LOC = "P28" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "_ram_ce[1]" LOC = "P20" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "_ram_ce[2]" LOC = "P15" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "_ram_ce[3]" LOC = "P18" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "_ram_oe" LOC = "P39" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "_ram_we" LOC = "P72" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "_ram_bhe" LOC = "P40" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "_ram_ble" LOC = "P42" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[10]" LOC = "P58" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[11]" LOC = "P61" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[12]" LOC = "P62" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[13]" LOC = "P63" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[14]" LOC = "P64" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[15]" LOC = "P79" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[16]" LOC = "P78" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[17]" LOC = "P77" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[18]" LOC = "P76" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[19]" LOC = "P74" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[1]" LOC = "P27" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[2]" LOC = "P26" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[3]" LOC = "P24" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[4]" LOC = "P22" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[5]" LOC = "P21" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[6]" LOC = "P35" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[7]" LOC = "P36" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[8]" LOC = "P37" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_address[9]" LOC = "P57" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "ram_data[0]" LOC = "P29" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[10]" LOC = "P50" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[11]" LOC = "P48" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[12]" LOC = "P46" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[13]" LOC = "P45" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[14]" LOC = "P44" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[15]" LOC = "P43" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[1]" LOC = "P31" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[2]" LOC = "P33" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[3]" LOC = "P34" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[4]" LOC = "P65" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[5]" LOC = "P67" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[6]" LOC = "P68" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[7]" LOC = "P71" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[8]" LOC = "P52" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "ram_data[9]" LOC = "P51" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; +NET "red[0]" LOC = "P4" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "red[1]" LOC = "P5" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "red[2]" LOC = "P7" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "red[3]" LOC = "P9" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "green[0]" LOC = "P204" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "green[1]" LOC = "P205" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "green[2]" LOC = "P2" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "green[3]" LOC = "P3" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "blue[0]" LOC = "P198" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "blue[1]" LOC = "P199" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "blue[2]" LOC = "P200" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "blue[3]" LOC = "P203" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "_hsync" LOC = "P196" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "_vsync" LOC = "P197" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "_joy1[0]" LOC = "P172" |IOSTANDARD = LVCMOS33 |PULLUP; +NET "_joy1[1]" LOC = "P171" |IOSTANDARD = LVCMOS33 |PULLUP; +NET "_joy1[2]" LOC = "P169" |IOSTANDARD = LVCMOS33 |PULLUP; +NET "_joy1[3]" LOC = "P167" |IOSTANDARD = LVCMOS33 |PULLUP; +NET "_joy1[4]" LOC = "P168" |IOSTANDARD = LVCMOS33 |PULLUP; +NET "_joy1[5]" LOC = "P175" |IOSTANDARD = LVCMOS33 |PULLUP; +NET "_joy2[0]" LOC = "P182" |IOSTANDARD = LVCMOS33 |PULLUP; +NET "_joy2[1]" LOC = "P181" |IOSTANDARD = LVCMOS33 |PULLUP; +NET "_joy2[2]" LOC = "P180" |IOSTANDARD = LVCMOS33 |PULLUP; +NET "_joy2[3]" LOC = "P176" |IOSTANDARD = LVCMOS33 |PULLUP; +NET "_joy2[4]" LOC = "P178" |IOSTANDARD = LVCMOS33 |PULLUP; +NET "_joy2[5]" LOC = "P183" |IOSTANDARD = LVCMOS33 |PULLUP; +NET "kbdclk" LOC = "P11" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; +NET "kbddat" LOC = "P10" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; +NET "msclk" LOC = "P13" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; +NET "msdat" LOC = "P12" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; +NET "left" LOC = "P191" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "right" LOC = "P190" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; +NET "sdi" LOC = "P85" |IOSTANDARD = LVCMOS33 ; +NET "sdo" LOC = "P19" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "sck" LOC = "P90" |IOSTANDARD = LVCMOS33 |CLOCK_DEDICATED_ROUTE = FALSE; +NET "_spi_cs" LOC = "P93" |IOSTANDARD = LVCMOS33 ; +NET "scsi_hshake" LOC = "P86" |IOSTANDARD = LVCMOS33 ; +NET "nmi" LOC = "P87" |IOSTANDARD = LVCMOS33 ; +NET "rxd" LOC = "P185" |IOSTANDARD = LVCMOS33 ; +NET "txd" LOC = "P184" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "rts" LOC = "P187" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "cts" LOC = "P189" |IOSTANDARD = LVCMOS33 ; +NET "pwrled" LOC = "P94" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; +NET "_15khz" LOC = "P194" |IOSTANDARD = LVCMOS33 |PULLUP ; +NET "gpio" LOC = "P16" |IOSTANDARD = LVCMOS33 |PULLUP ; +NET "init_b" LOC = "P83" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; +NET "_cpu_as" CLOCK_DEDICATED_ROUTE = FALSE; diff --git a/fpga/minimigmac.v b/fpga/minimigmac.v new file mode 100644 index 0000000..84caef3 --- /dev/null +++ b/fpga/minimigmac.v @@ -0,0 +1,560 @@ +`timescale 1ns / 100ps + +/* + * Note about interrupt wiring: + * + * Level IPL 0 <- VIA + * IPL 1 <- SCC + * IPL 2 <- xxx + * + * With debug switch clamping all 3 down + */ + +module minimigmac +( + /* m68k CPU pins */ + inout [15:0] cpu_data, // m68k data bus + input [23:1] cpu_address, // m68k address bus + output [2:0] _cpu_ipl, // m68k interrupt request + input _cpu_as, // m68k address strobe + input _cpu_uds, // m68k upper data strobe + input _cpu_lds, // m68k lower data strobe + input cpu_r_w, // m68k read / write + output _cpu_dtack, // m68k data acknowledge + inout _cpu_reset, // m68k reset + output cpu_clk, // m68k clock + + /* SRAM pins */ + inout [15:0] ram_data, // sram data bus + output [19:1] ram_address, // sram address bus + output [3:0] _ram_ce, // sram chip enable + output _ram_bhe, // sram upper byte select + output _ram_ble, // sram lower byte select + output _ram_we, // sram write enable + output _ram_oe, // sram output enable + + /* RS232 pins */ + input rxd, // rs232 receive + output txd, // rs232 send + input cts, // rs232 clear to send + output rts, // rs232 request to send + + /* Joystick ports */ + input [5:0]_joy1, // joystick 1 [fire2,fire,up,down,left,right] + input [5:0]_joy2, // joystick 2 [fire2,fire,up,down,left,right] + + /* PS2 ports */ + inout msdat, // PS2 mouse data + inout msclk, // PS2 mouse clk + inout kbddat, // PS2 keyboard data + inout kbdclk, // PS2 keyboard clk + + /* Video pins */ + output _hsync, // horizontal sync + output _vsync, // vertical sync + output [3:0] red, // red + output [3:0] green, // green + output [3:0] blue, // blue + + /* Audio pins */ + output left, // audio bitstream left + output right, // audio bitstream right + + /* System pins */ + input mclk, // master system clock (4.433619 Mhz) + output pwrled, // power led + output init_b, + input _15khz, // scandoubler disable (jumper, unused) + output gpio, + output scsi_hshake, // handshake for scsi fifo + input nmi, // macsbug ! + + /* SPI pins */ + input _spi_cs, // SPI chip select + input sdi, // SPI data input + inout sdo, // SPI data output + input sck // SPI clock +); + /* Internal clocks */ + wire sysclk16; + wire sysclk33; + wire pixclk; + + /* Backbus SPI interface */ + wire [5:0] bb_addr; + wire [7:0] bb_wdata; + wire [7:0] bb_rdata; + wire bb_strobe; + wire bb_wr; + + /* Chip-selects & rdata for backbus clients */ + wire bb_cs_scope; + wire [7:0] bb_rdata_scope; + wire bb_cs_ctrl; + wire [7:0] bb_rdata_ctrl; + wire bb_cs_cpu; + wire [7:0] bb_rdata_cpu; + wire bb_cs_iwm; + wire [7:0] bb_rdata_iwm; + wire bb_cs_scsi; + wire [7:0] bb_rdata_scsi; + wire bb_cs_mem; + wire [7:0] bb_rdata_mem; + + /* Signals for the scope module */ + wire [63:0] scoped_sigs; + + /* Main bus between CPU and devices */ + wire [23:1] bus_addr; + wire [15:0] bus_wdata; + wire bus_ube; + wire bus_lbe; + wire bus_we; + wire bus_phase; + wire bus_cs_ram; + wire bus_cs_rom; + wire bus_ack_mem; + wire [15:0] bus_rdata_mem; + wire bus_cs_scsi; + wire bus_ack_scsi; + wire [7:0] bus_rdata_scsi; + wire bus_cs_scc; + wire bus_ack_scc; + wire [7:0] bus_rdata_scc; + wire bus_cs_via; + wire bus_ack_via; + wire [7:0] bus_rdata_via; + wire bus_cs_iwm; + wire bus_ack_iwm; + wire [7:0] bus_rdata_iwm; + + /* VIA wires */ + wire [2:0] via_pa_sndvol; + wire via_pa_sndbuf_sel; /* 1=main, 0=alt */ + wire via_pa_rom_ovl; /* 1=overlay */ + wire via_pa_disksel; + wire via_pa_vidbuf_sel; /* 1=main, 0=alt */ + wire via_pa_scc_wreq; + wire via_pb_mouse_switch; + wire via_pb_mouse_x2; + wire via_pb_mouse_y2; + wire via_pb_snddis; /* 1=disabled */ + wire [7:0] via_kbd_out; + wire via_kbd_out_strobe; + wire [7:0] via_kbd_in; + wire via_kbd_in_strobe; + + /* Video wires */ + wire vid_req; + wire vid_ack; + wire [15:0] vid_pixels; + wire vid_hblank; + + /* SCC additional wires */ + wire scc_mouse_x1; + wire scc_mouse_y1; + + /* RTC wires */ + wire rtc_onesec; + wire rtc_data_in; + wire rtc_data_out; + wire rtc_data_clock; + wire _rtc_data_enable; + + /* IRQs */ + wire _via_irq; + wire _scc_irq; + reg _prg_irq; + reg nmi_sync; + + /* Control lines */ + reg led; + + /* Reset hack */ + reg [3:0] reset_ctl = 4'b0000;/* Xilinx tool should cope */ + wire reset; + wire ctrl_cpu_reset; + + /* Led test hack */ + reg [31:0] led_cnt; + + /* Scope test hack */ + reg [7:0] scope_test; + + /* PS2 mouse & kbd port debug */ + wire [15:0] ps2m_dbg; + wire [15:0] ps2k_dbg; + + /* Not real synchronizers but will do */ + reg _vblank_sync; + reg hblank_sync; + + /* Instanciate clock generator */ + clockgen clocks(.mclk(mclk), + .sysclk16(sysclk16), + .sysclk33(sysclk33), + .pixclk(pixclk)); + + /* Low skew clock out trick from Matt */ + OFDDRCPE OFDDRCPE_inst ( + .Q(cpu_clk), // Data output (connect directly to + // top-level port) + .C0(sysclk16), // 0 degree clock input + .C1(~sysclk16), // 180 degree clock input + .CE(1'b1), // Clock enable input + .CLR(1'b0), // Asynchronous reset input + .D0(1'b1), // Posedge data input + .D1(1'b0), // Negedge data input + .PRE(1'b0) // Asynchronous preset input + ); + + /* Instanciate backbus/SPI interface. Use 16Mhz clock for now */ + spi_backbus spi_bb(.sysclk(sysclk16), + .reset(reset), + ._scs(_spi_cs), + .sdi(sdi), + .sdo(sdo), + .sck(sck), + .bb_addr(bb_addr), + .bb_wdata(bb_wdata), + .bb_rdata(bb_rdata), + .bb_strobe(bb_strobe), + .bb_wr(bb_wr)); + + /* Our reset currently comes reset_ctl, this will deassert it + * after 4clk of config (gives time to slower components like + * video to reset) + */ + assign reset = ~reset_ctl[0]; + always@(posedge sysclk16) + if (!ctrl_cpu_reset && !_cpu_reset) + reset_ctl <= 0; + else + reset_ctl[3:0] <= { 1'b1, reset_ctl[3:1] }; + assign _cpu_reset = ctrl_cpu_reset ? 1'b0 : 1'bz; + + /* Instanciate scope module */ + scope scop0(.sysclk(sysclk16), + .capclk(sysclk16), + .reset(reset), + .sigs(scoped_sigs), + .bb_addr(bb_addr), + .bb_wdata(bb_wdata), + .bb_rdata(bb_rdata_scope), + .bb_strobe(bb_strobe && bb_cs_scope), + .bb_wr(bb_wr)); + +`ifdef __disabled__ + assign scoped_sigs = { msclk, /* 63 */ + msdat, /* 62 */ + cpu_data, /* 61..46 */ + cpu_address, /* 45..23 */ + _cpu_ipl, /* 22..20 */ + _cpu_as, /* 19 */ + _cpu_uds, /* 18 */ + _cpu_lds, /* 17 */ + cpu_r_w, /* 16 */ + _cpu_dtack, /* 15 */ + _cpu_reset, /* 14 */ + 6'b111111, + scope_test}; +`else + assign scoped_sigs = 0; +`endif + always@(posedge reset or posedge sysclk16) begin + if (reset) begin + scope_test <= 0; + end else begin + scope_test <= scope_test + 1; + end + end + + /* Instanciate the CPU interface */ + cpu_intf cpu0(.sysclk(sysclk16), + .reset(reset), + .cpu_data(cpu_data), + .cpu_addr(cpu_address), + ._cpu_as(_cpu_as), + ._cpu_uds(_cpu_uds), + ._cpu_lds(_cpu_lds), + .cpu_r_w(cpu_r_w), + ._cpu_dtack(_cpu_dtack), + .bb_addr(bb_addr), + .bb_wdata(bb_wdata), + .bb_rdata(bb_rdata_cpu), + .bb_strobe(bb_strobe && bb_cs_cpu), + .bb_wr(bb_wr), + .bus_addr(bus_addr), + .bus_wdata(bus_wdata), + .bus_ube(bus_ube), + .bus_lbe(bus_lbe), + .bus_we(bus_we), + .bus_phase(bus_phase), + .bus_cs_ram(bus_cs_ram), + .bus_cs_rom(bus_cs_rom), + .bus_ack_mem(bus_ack_mem), + .bus_rdata_mem(bus_rdata_mem), + .bus_cs_scsi(bus_cs_scsi), + .bus_ack_scsi(bus_ack_scsi), + .bus_rdata_scsi(bus_rdata_scsi), + .bus_cs_scc(bus_cs_scc), + .bus_ack_scc(bus_ack_scc), + .bus_rdata_scc(bus_rdata_scc), + .bus_cs_via(bus_cs_via), + .bus_ack_via(bus_ack_via), + .bus_rdata_via(bus_rdata_via), + .bus_cs_iwm(bus_cs_iwm), + .bus_ack_iwm(bus_ack_iwm), + .bus_rdata_iwm(bus_rdata_iwm), + .rom_ovl(via_pa_rom_ovl)); + + /* CPU interrupt inputs */ + /* + * Don't assert _via_irq if _scc_irq is asserted + * a temporary spurrious is fine but the ROM will not + * handle otherwise a level 3 (it just RTEs). + */ + assign _cpu_ipl = _prg_irq ? + {1'b1, _scc_irq, _via_irq | ~_scc_irq } + : 3'b000; + + /* Instanciate memory interface */ + mem_intf mem0(.sysclk(sysclk16), + .reset(reset), + .ram_data(ram_data), + .ram_address(ram_address), + ._ram_ce(_ram_ce), + ._ram_bhe(_ram_bhe), + ._ram_ble(_ram_ble), + ._ram_we(_ram_we), + ._ram_oe(_ram_oe), + .bus_cs_ram(bus_cs_ram), + .bus_cs_rom(bus_cs_rom), + .bus_we(bus_we), + .bus_ack(bus_ack_mem), + .bus_addr(bus_addr[22:1]), + .bus_ube(bus_ube), + .bus_lbe(bus_lbe), + .bus_phase(bus_phase), + .bus_wdata(bus_wdata), + .bus_rdata(bus_rdata_mem), + .bb_addr(bb_addr), + .bb_wdata(bb_wdata), + .bb_rdata(bb_rdata_mem), + .bb_strobe(bb_strobe & bb_cs_mem), + .bb_wr(bb_wr), + .vid_bufsel(via_pa_vidbuf_sel), + .vid_req(vid_req), + .vid_ack(vid_ack), + .vid_pixels(vid_pixels)); + + /* Instanciate VIA */ + via6522 via0(.sysclk(sysclk16), + .reset(reset), + .cs(bus_cs_via & bus_phase), + .we(bus_we), + .rs(bus_addr[12:9]), + .wdata(bus_wdata[15:8]), + .rdata(bus_rdata_via), + ._irq(_via_irq), + .pa_in7(via_pa_scc_wreq), + .pa_out({via_pa_vidbuf_sel, + via_pa_disksel, + via_pa_rom_ovl, + via_pa_sndbuf_sel, + via_pa_sndvol}), + .pb_out2_0({_rtc_data_enable, + rtc_data_clock, + rtc_data_out}), + .pb_out7(via_pb_snddis), + .pb_in6_3({hblank_sync, + via_pb_mouse_y2, + via_pb_mouse_x2, + via_pb_mouse_switch}), + .pb_in0(rtc_data_in), + .sr_in(via_kbd_in), + .sr_in_strobe(via_kbd_in_strobe), + .sr_out(via_kbd_out), + .sr_out_strobe(via_kbd_out_strobe), + .ca1(_vblank_sync), + .ca2(rtc_onesec)); + + /* Simplified one-phase interface */ + assign bus_ack_via = bus_cs_via; + + /* Instanciate the RTC */ + rtc rtc0(.sysclk(sysclk16), + .reset(reset), + .onesec(rtc_onesec), + .data_in(rtc_data_in), + .data_out(rtc_data_out), + .data_clock(rtc_data_clock), + ._data_enable(_rtc_data_enable)); + + /* Instanciate scc */ + scc scc0(.sysclk(sysclk16), + .reset_hw(reset), + .cs(bus_cs_scc & bus_phase), + .we(bus_we), + .rs(bus_addr[2:1]), + .wdata(bus_wdata[15:8]), + .rdata(bus_rdata_scc), + ._irq(_scc_irq), + .rxd(rxd), + .txd(txd), + .cts(cts), + .rts(rts), + .dcd_a(scc_mouse_x1), + .dcd_b(scc_mouse_y1), + .wreq(via_pa_scc_wreq)); + /* Simplified one-phase interface */ + assign bus_ack_scc = bus_cs_scc; + + /* Instanciate iwm */ + iwm iwm0(.sysclk(sysclk16), + .reset(reset), + .cs(bus_cs_iwm), + .we(bus_we), + .ack(bus_ack_iwm), + .phase(bus_phase), + .rs(bus_addr[12:9]), + .wdata(bus_wdata[7:0]), + .rdata(bus_rdata_iwm), + .bb_addr(bb_addr), + .bb_wdata(bb_wdata), + .bb_rdata(bb_rdata_iwm), + .bb_strobe(bb_strobe && bb_cs_iwm), + .bb_wr(bb_wr), + .via_sel(via_pa_disksel)); + + /* Instanciate scsi */ + ncr5380 scsi0(.sysclk(sysclk16), + .reset(reset), + .bus_cs(bus_cs_scsi), + .bus_we(bus_we), + .bus_ack(bus_ack_scsi), + .bus_phase(bus_phase), + .bus_rs(bus_addr[6:4]), + .dack(bus_addr[9]), + .wdata(bus_wdata[15:8]), + .rdata(bus_rdata_scsi), + .scsi_hshake(scsi_hshake), + .bb_addr(bb_addr), + .bb_wdata(bb_wdata), + .bb_rdata(bb_rdata_scsi), + .bb_strobe(bb_strobe && bb_cs_scsi), + .bb_wr(bb_wr)); + + /* Instanciate video output module */ + video video0(.pixclk(pixclk), + .reset(reset), + .pixels(vid_pixels), + .req_pix(vid_req), + .ack_pix(vid_ack), + ._hsync(_hsync), + ._vsync(_vsync), + .red(red), + .green(green), + .blue(blue), + .hblank(vid_hblank)); + + /* Instanciate the PS/2 mouse interface */ + ps2_mouse mouse0(.sysclk(sysclk16), + .reset(reset), + .ps2dat(msdat), + .ps2clk(msclk), + .x1(scc_mouse_x1), + .y1(scc_mouse_y1), + .x2(via_pb_mouse_x2), + .y2(via_pb_mouse_y2), + .button(via_pb_mouse_switch), + .debug(ps2m_dbg)); + + /* Instanciate the PS/2 keyboard interface */ + ps2_kbd kbd0(.sysclk(sysclk16), + .reset(reset), + .ps2dat(kbddat), + .ps2clk(kbdclk), + .data_out(via_kbd_out), + .strobe_out(via_kbd_out_strobe), + .data_in(via_kbd_in), + .strobe_in(via_kbd_in_strobe), + .debug(ps2k_dbg)); + + /* Instanciate global backbus control registers */ + ctrl ctrl0(.sysclk(sysclk16), + .reset(reset), + .bb_addr(bb_addr), + .bb_wdata(bb_wdata), + .bb_rdata(bb_rdata_ctrl), + .bb_strobe(bb_strobe && bb_cs_ctrl), + .bb_wr(bb_wr), + .ps2m_dbg(ps2m_dbg), + .ps2k_dbg(ps2k_dbg), + .cpu_reset(ctrl_cpu_reset)); + + /* Generate backbus chip selects and mux the backbus read data */ + assign bb_cs_ctrl = bb_addr[5:3] == 3'b000; + assign bb_cs_cpu = bb_addr[5:4] == 3'b001; + assign bb_cs_mem = bb_addr[5:3] == 3'b100; + assign bb_cs_iwm = bb_addr[5:3] == 3'b101; + assign bb_cs_scsi = bb_addr[5:3] == 3'b110; + assign bb_cs_scope = bb_addr[5:3] == 3'b111; + + assign bb_rdata = bb_cs_ctrl ? bb_rdata_ctrl : + bb_cs_cpu ? bb_rdata_cpu : + bb_cs_iwm ? bb_rdata_iwm : + bb_cs_scsi ? bb_rdata_scsi : + bb_cs_mem ? bb_rdata_mem : + bb_cs_scope ? bb_rdata_scope : 8'hff; + + /* Test LED */ + always@(posedge sysclk16) begin + if (reset) begin + led <= 0; + led_cnt <= 0; + end else begin + led_cnt <= led_cnt + 1; + if (led_cnt == 1000000) begin + led_cnt <= 0; + led <= ~led; + end + end + end + + /* Programmer switch, latch the button */ + always@(posedge sysclk16) begin + if (reset) begin + nmi_sync <= 0; + _prg_irq <= 1; + end else begin + nmi_sync <= nmi; + _prg_irq <= ~nmi_sync; + end + end + + /* Not real synchronizers but will do */ + always@(posedge sysclk16 or posedge reset) begin + if (reset) begin + _vblank_sync <= 1; + hblank_sync <= 0; + end else begin + _vblank_sync <= _vsync; + hblank_sync <= vid_hblank; + end + end + + /* LED output powered by a weak pullup */ + assign pwrled = led ? 1'b1 : 1'bz; + + /* Unused output signals */ + assign kbddat = 1'bz; + assign kbdclk = 1'bz; + + assign left = 0; + assign right = 0; + + assign init_b = 0; + assign gpio = 1'bz; + +endmodule // mimigmac diff --git a/fpga/ncr5380.v b/fpga/ncr5380.v new file mode 100644 index 0000000..7ad7b46 --- /dev/null +++ b/fpga/ncr5380.v @@ -0,0 +1,530 @@ +`timescale 1ns / 100ps + +/* + * NCR5380 SCSI module for minimigmac. + * + * Located on high data bus, but writes are done at odd addresses as + * LDS is used as WR signals or something like that on a Mac Plus. + * + * We don't care here and just ignore which side was used. We also don't + * implement the interrupt logic (well at least we don't route it to the + * CPU like a real Mac) and we do all sort of simplifications such as only + * supporting initiator mode + * + * This is implementation is really just a gateway to the backbus, the + * microcontroller can observe SCSI line states and exchange data. + * + * Notes: + * + * - Arbitration - + * + * We do not support external arbitration & reselection, thus we always + * win it. Thus our arbitration logic is as simple as reflecting the + * arbitration bit into ICR:AIP and never setting ICR:LA, no timing + * to care much about. + * + * Note: We also don't wait for a bus free phase, so we assume SW will + * note try to arbitrate while BSY is still asserted by the target, if + * that was to happen, I could use a latch to delay arbitration to + * finding that BSY is false and checking SEL. + * + * - Bus reset - + * + * We don't do anything special, just report the state of the line. We + * might end up needing to latch it to give a chance for the uC to pick + * it up if it's asserted for tfoo short (for ex. the uC is in the middle + * of a data transfer). + * + * - Busy monitoring - + * + * Not implemented... might need to add, ROM doesn't ues it but MacOS + * might + * + * - Interrupts - + * + * Nothing implemented, BSR:IRQ never set, ROM doesn't use it, but + * MacOS might ... Select Enable Register is not implemented + * + * - Data transfers + * + * In dumb mode, it's all manual assertion of the various lines on + * both side. However, there's a problem with the Mac ROM when doing + * that. + * + * When doing a read, the ROM does not wait for REQ to go down after the + * last byte has been read. If the PIC is slow enough to react, that menans + * that the subsequent call to SCSIComplete, will incorrectly match that REQ + * for the one of the status phase, and get a phase mismatch. This can happen + * with my SPI protocol which is somewhat sub optimal for alternating reads + * and writes + * + * So the PIC has to be fast enough at lowering REQ when ACK gets set ... + * or always use automatic mode: + * + * Automatic mode: when in that mode, REQ/ACK is handled in HW and the + * separate _scsi_dreq line is used to pace the PIC as follow: + * + * - On reads (target -> initiator) + * + * * Signal din_full set set when PIC writes to IDATA, + * causes REQ to be asserted + * * ACK (either manual or auto-generated by DMA mode) + * clears din_full (and thus de-asserts REQ) + * * _scsi_dreq is asserted based on a flip flop that + * gets set when data is read by host (DACK) and set + * when REQ is asserted (IDATA is written). This only + * happens while automatic mode is enabled. + * + * - On writes (initiator -> target) + * * + * + +/* Read registers */ +`define RREG_CDR 3'h0 /* Current SCSI data */ +`define RREG_ICR 3'h1 /* Initiator Command */ +`define RREG_MR 3'h2 /* Mode register */ +`define RREG_TCR 3'h3 /* Target Command */ +`define RREG_CSR 3'h4 /* SCSI bus status */ +`define RREG_BSR 3'h5 /* Bus and status */ +`define RREG_IDR 3'h6 /* Input data */ +`define RREG_RST 3'h7 /* Reset */ + +/* Write registers */ +`define WREG_ODR 3'h0 /* Ouptut data */ +`define WREG_ICR 3'h1 /* Initiator Command */ +`define WREG_MR 3'h2 /* Mode register */ +`define WREG_TCR 3'h3 /* Target Command */ +`define WREG_SER 3'h4 /* Select Enable */ +`define WREG_DMAS 3'h5 /* Start DMA Send */ +`define WREG_DMATR 3'h6 /* Start DMA Target receive */ +`define WREG_IDMAR 3'h7 /* Start DMA Initiator receive */ + +/* Backbus interface */ +`define BB_REG_LINES 0 /* Read only, line states set by initiator */ +`define BB_LINES_BSY 7 +`define BB_LINES_SEL 6 +`define BB_LINES_ACK 5 +`define BB_LINES_ATN 4 +`define BB_LINES_RST 3 + +`define BB_REG_ASSERT 1 /* RW, assert lines by target */ +`define BB_ASSERT_BSY 7 +`define BB_ASSERT_SEL 6 +`define BB_ASSERT_CD 5 +`define BB_ASSERT_IO 4 +`define BB_ASSERT_MSG 3 +`define BB_ASSERT_REQ 2 +`define BB_ASSERT_RST 1 +`define BB_ASSERT_AUTO 0 /* Automatic mode */ + +`define BB_REG_ODATA 2 +`define BB_REG_IDATA 3 +`define BB_REG_ACNT_HI 4 +`define BB_REG_ACNT_LO 5 + +/* MR bit numbers */ +`define MR_DMA_MODE 1 +`define MR_ARB 0 + +/* ICR bit numbers */ +`define ICR_A_RST 7 +`define ICR_TEST_MODE 6 +`define ICR_DIFF_ENBL 5 +`define ICR_A_ACK 4 +`define ICR_A_BSY 3 +`define ICR_A_SEL 2 +`define ICR_A_ATN 1 +`define ICR_A_DATA 0 + +/* TCR bit numbers */ +`define TCR_A_REQ 3 +`define TCR_A_MSG 2 +`define TCR_A_CD 1 +`define TCR_A_IO 0 + +module ncr5380(input sysclk, + input reset, + + /* Bus interface. 3-bit address, to be wired + * appropriately upstream (to A4..A6) plus one + * more bit (A9) wired as dack. + */ + input bus_cs, + input bus_we, + output bus_ack, + input bus_phase, + input [2:0] bus_rs, + input dack, + input [7:0] wdata, + output [7:0] rdata, + + /* Backbus interface */ + input [5:0] bb_addr, + input [7:0] bb_wdata, + output [7:0] bb_rdata, + input bb_strobe, + input bb_wr, + + /* External request line */ + output scsi_hshake + ); + + /* Bus interface signals */ + wire reg_rd; + wire reg_wr; + wire dma_rd; + wire dma_wr; + wire bus_hold; + + /* Mode Register */ + reg [7:0] mr; + + /* Initiator Command Register */ + wire icr_aip; + wire icr_la; + wire [7:0] icr_read; + reg [7:0] icr; + + /* Target Command Register */ + reg [3:0] tcr; + + /* Backbus asserts */ + reg [7:0] bb_assert; + + /* These are the simulated SCSI bus lines, we use positive + * polarity to simplify things + */ + wire scsi_bsy; + wire scsi_sel; + wire scsi_ack; + wire scsi_atn; + wire scsi_cd; + wire scsi_io; + wire scsi_msg; + wire scsi_req; + wire scsi_rst; + + /* What backbus sees of the above */ + wire [7:0] bb_lines; + wire bb_reg_rd; + wire bb_reg_wr; + wire bb_reg_lines; + wire bb_reg_assert; + wire bb_reg_odata; + wire bb_reg_idata; + wire bb_reg_acnt_hi; + wire bb_reg_acnt_lo; + + /* SCSI bus status register */ + wire [7:0] csr; + + /* Bus and Status register */ + wire bsr_eodma; + wire bsr_dmarq; + wire bsr_perr; + wire bsr_irq; + wire bsr_pmatch; + wire bsr_berr; + wire [7:0] bsr; + + /* Current data register and logic */ + wire [7:0] cur_data; + wire out_en; + + /* Data in and out latches and associated + * control logic for DMA + */ + reg [7:0] din; + reg [7:0] dout; + reg dphase; + reg dma_en; + + /* BB automatic mode */ + reg autoreq; + reg [15:0] autocnt; + wire autoreg; + reg autodrop; + reg [7:0] dout_latch; + + /* --- Main host-side interface --- */ + + /* Register & DMA accesses decodes */ + assign dma_rd = bus_cs & bus_phase & dack & ~bus_we; + assign dma_wr = bus_cs & bus_phase & dack & bus_we; + assign reg_rd = bus_cs & bus_phase & ~dack & ~bus_we; + assign reg_wr = bus_cs & bus_phase & ~dack & bus_we; + + /* Hold bus to phase 0 on DMA accesses if necessary, this + * makes "blind" transfers 100% reliable since I really don't + * know if my SPI interface is fast enough + */ + assign bus_hold = dack & (scsi_ack | ~scsi_req) & dma_en; + assign bus_ack = bus_cs & ~bus_hold; + + /* System bus reads + * + * Q. to HW dudes... I don't need to test reg_rd everywhere, I can + * just let the mux toggle all the time, but would that consume + * more power or it's so ridiculous we don't care ? + */ + assign rdata = dma_rd ? cur_data : + reg_rd && bus_rs == `RREG_CDR ? cur_data : + reg_rd && bus_rs == `RREG_ICR ? icr_read : + reg_rd && bus_rs == `RREG_MR ? mr : + reg_rd && bus_rs == `RREG_TCR ? { 4'h0 | tcr } : + reg_rd && bus_rs == `RREG_CSR ? csr : + reg_rd && bus_rs == `RREG_BSR ? bsr : + reg_rd && bus_rs == `RREG_IDR ? cur_data : + reg_rd && bus_rs == `RREG_RST ? 8'hff : + 8'hff; + + /* DMA handhsaking logic. Two phase logic, in phase 0 + * DRQ follows SCSI _REQ until we see DACK. In phase 1 + * we just wait for SCSI _REQ to go down and go back to + * phase 0. We assert SCSI _ACK in phase 1. + */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + dphase <= 0; + end else begin + if (!dma_en) begin + dphase <= 0; + end else if (dphase == 0) begin + /* Be careful to do that in bus phase 1, + * not phase 0, or we would incorrectly + * assert bus_hold and lock up the system + */ + if ((dma_rd || dma_wr) && scsi_req) begin + dphase <= 1; + end + end else if (!scsi_req) begin + dphase <= 0; + end + end + end + + /* Data out latch (in DMA mode, this is one cycle after we've + * asserted ACK) + */ + always@(posedge sysclk or posedge reset) begin + if (reset) + dout <= 8'haa; + else if ((reg_wr && bus_rs == `WREG_ODR) || dma_wr) + dout <= wdata; + end + + /* Current data register. Simplified logic: We loop back the + * output data if we are asserting the bus, else we get the + * input latch + */ + assign cur_data = out_en ? dout : din; + + /* Logic for "asserting the bus" simplified */ + assign out_en = icr[`ICR_A_DATA] | mr[`MR_ARB]; + + /* ICR read wires */ + assign icr_read = { icr[`ICR_A_RST], + icr_aip, + icr_la, + icr[`ICR_A_ACK], + icr[`ICR_A_BSY], + icr[`ICR_A_SEL], + icr[`ICR_A_ATN], + icr[`ICR_A_DATA] }; + /* ICR write */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + icr <= 0; + end else if (reg_wr && (bus_rs == `WREG_ICR)) begin + icr <= wdata; + end + end + + /* MR write */ + always@(posedge sysclk or posedge reset) begin + if (reset) + mr <= 8'b0; + else if (reg_wr && (bus_rs == `WREG_MR)) + mr <= wdata; + end + + /* TCR write */ + always@(posedge sysclk or posedge reset) begin + if (reset) + tcr <= 4'b0; + else if (reg_wr && (bus_rs == `WREG_TCR)) + tcr <= wdata[3:0]; + end + + /* DMA start send & receive registers. We currently ignore + * the direction. + */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + dma_en <= 0; + end else begin + if (!mr[`MR_DMA_MODE]) begin + dma_en <= 0; + end else if (reg_wr && (bus_rs == `WREG_DMAS)) begin + dma_en <= 1; + end else if (reg_wr && (bus_rs == `WREG_IDMAR)) begin + dma_en <= 1; + end + end + end + + /* CSR (read only). We don't do parity */ + assign csr = { scsi_rst, scsi_bsy, scsi_req, scsi_msg, + scsi_cd, scsi_io, scsi_sel, 1'b0 }; + + /* BSR (read only). We don't do a few things... */ + assign bsr_eodma = 0; /* We don't do EOP */ + assign bsr_dmarq = scsi_req & ~dphase & dma_en; + assign bsr_perr = 0; /* We don't do parity */ + assign bsr_irq = 0; /* XXX ? Does MacOS use this ? */ + assign bsr_pmatch = tcr[`TCR_A_MSG] == scsi_msg && + tcr[`TCR_A_CD ] == scsi_cd && + tcr[`TCR_A_IO ] == scsi_io; + assign bsr_berr = 0; /* XXX ? Does MacOS use this ? */ + assign bsr = { bsr_eodma, bsr_dmarq, bsr_perr, bsr_irq, + bsr_pmatch, bsr_berr, scsi_atn, scsi_ack }; + + /* --- Simulated SCSI Signals --- */ + + /* BSY logic (simplified arbitration, see notes) */ + assign scsi_bsy = icr[`ICR_A_BSY] | + bb_assert[`BB_ASSERT_BSY] | + mr[`MR_ARB]; + /* Remains of simplified arbitration logic */ + assign icr_aip = mr[`MR_ARB]; + assign icr_la = 0; + + /* Other ORed SCSI signals */ + assign scsi_sel = icr[`ICR_A_SEL] | bb_assert[`BB_ASSERT_SEL]; + assign scsi_rst = icr[`ICR_A_RST] | bb_assert[`BB_ASSERT_RST]; + assign scsi_ack = icr[`ICR_A_ACK] | dphase; + assign scsi_atn = icr[`ICR_A_ATN]; + + /* Other trivial lines set by target */ + assign scsi_cd = bb_assert[`BB_ASSERT_CD]; + assign scsi_io = bb_assert[`BB_ASSERT_IO]; + assign scsi_msg = bb_assert[`BB_ASSERT_MSG]; + assign scsi_req = bb_assert[`BB_ASSERT_REQ] | autoreq; + + /* --- Backbus interface --- */ + + assign bb_reg_rd = bb_strobe & ~bb_wr; + assign bb_reg_wr = bb_strobe & bb_wr; + assign bb_reg_lines = bb_addr[3:0] == `BB_REG_LINES; + assign bb_reg_assert = bb_addr[3:0] == `BB_REG_ASSERT; + assign bb_reg_odata = bb_addr[3:0] == `BB_REG_ODATA; + assign bb_reg_idata = bb_addr[3:0] == `BB_REG_IDATA; + assign bb_reg_acnt_hi = bb_addr[3:0] == `BB_REG_ACNT_HI; + assign bb_reg_acnt_lo = bb_addr[3:0] == `BB_REG_ACNT_LO; + + /* Backbus lines, ie, pseudo SCSI bus state seen by the + * microcontroller. Note that they are positive logic unlike + * the real SCSI lines. We only care about the lines that are + * not asserted exclusively by the target + */ + assign bb_lines = { scsi_bsy, scsi_sel, scsi_ack, scsi_atn, + scsi_rst, 3'b0 }; + + /* Backbus register reads */ + assign bb_rdata = bb_reg_lines ? bb_lines : + bb_reg_assert ? bb_assert : + bb_reg_odata ? (bb_assert[`BB_ASSERT_AUTO] ? dout_latch : dout) : + bb_reg_idata ? din : + 0; + + /* External handshake logic. We assert it for now when SEL is + * asserted in ICR. Or in auto mode, when both req and ack are + * low + */ + assign scsi_hshake = icr[`ICR_A_SEL] | icr[`ICR_A_RST] | + (bb_assert[`BB_ASSERT_AUTO] & ~(scsi_ack | scsi_req)); + + /* Data in latch */ + always@(posedge sysclk or posedge reset) begin + if (reset) + din <= 8'h55; + else if (bb_reg_wr && bb_reg_idata) + din <= bb_wdata; + end + + /* BB assert register write */ + always@(posedge sysclk or posedge reset) begin + if (reset) + bb_assert <= 0; + else if (bb_reg_wr && bb_reg_assert) + bb_assert <= bb_wdata; + end + + /* --- Backbus "automatic" mode logic crackpot --- */ + + /* autocnt register write & decrement */ + always@(posedge sysclk or posedge reset) begin + if (reset) + autocnt <= 0; + else if (bb_reg_wr && bb_reg_acnt_hi) + autocnt[15:8] <= bb_wdata; + else if (bb_reg_wr && bb_reg_acnt_lo) + autocnt[7:0] <= bb_wdata; + else if (autoreg) + autocnt <= autocnt - 1; + end + + /* Hack to workaround SPI interface giving us two strobes + * on the first byte of reads. We use this latch to drop + * the first one. + */ + always@(posedge sysclk or posedge reset) begin + if (reset) + autodrop <= 0; + else begin + if (bb_reg_wr && bb_reg_acnt_lo) + autodrop <= 1; + else if (bb_strobe) + autodrop <= 0; + end + end + + /* An automatic register access happens when auto mode is + * enabled, autocnt is non-0 and we access the idata or + * odata register + */ + assign autoreg = bb_assert[`BB_ASSERT_AUTO] & bb_strobe & + (bb_reg_idata | (bb_reg_odata & ~autodrop)) & + (autocnt != 0); + + /* Automatic mode, toggle REQ automagically during + * data phases if there are still bytes to go + */ + always@(posedge sysclk or posedge reset) begin + if (reset) + autoreq <= 0; + else begin + if (!bb_assert[`BB_ASSERT_AUTO]) + autoreq <= 0; + else if (autoreg) + autoreq <= 1; + else if (scsi_ack) + autoreq <= 0; + end + end + + /* In auto mode writes, we are slightly off as REQ is + * asserted by a read done by the PIC so we need to + * latch the data when ack is asserted so it's still + * valid by the time the PIC reads it. To simplify + * things we always latch on ACK + */ + always@(posedge sysclk or posedge reset) begin + if (reset) + dout_latch <= 0; + else begin + if (scsi_ack) + dout_latch <= dout; + end + end +endmodule diff --git a/fpga/ps2.v b/fpga/ps2.v new file mode 100644 index 0000000..31f7fb4 --- /dev/null +++ b/fpga/ps2.v @@ -0,0 +1,170 @@ +`timescale 1ns / 100ps + +/* + * Generic PS2 interface module + * + * istrobe, oreq, oack and timeout are all 1-clk strobes, + * ibyte must be latched on that strobe, obyte is latched + * as oreq is detected, oreq is ignore while already + * sending. + * + * we ignore bad parity on input for now + */ + +module ps2(input sysclk, + input reset, + + inout ps2dat, + inout ps2clk, + + output istrobe, + output [7:0] ibyte, + + input oreq, + input [7:0] obyte, + output oack, + + output timeout, + + output[1:0] dbg_state + ); + + reg [7:0] clkbuf; + reg [7:0] datbuf; + reg clksync; + reg clkprev; + reg datsync; + reg [10:0] shiftreg; + reg [3:0] shiftcnt; + wire shiftend; + reg [1:0] state; + wire datout; + reg [23:0] timecnt; + wire clkdown; + wire opar; + + /* State machine */ + localparam ps2_state_idle = 0; + localparam ps2_state_ring = 1; + localparam ps2_state_send = 2; + localparam ps2_state_recv = 3; + + always@(posedge sysclk or posedge reset) begin + if (reset) + state <= ps2_state_idle; + else begin + if (timeout && !oreq) + state <= ps2_state_idle; + else + case(state) + ps2_state_idle: begin + if (oreq) + state <= ps2_state_ring; + else if (clkdown) + state <= ps2_state_recv; + end + ps2_state_ring: begin + if (timecnt[12]) + state <= ps2_state_send; + end + ps2_state_send: begin + if (shiftend) + state <= ps2_state_idle; + end + ps2_state_recv: begin + if (oreq) + state <= ps2_state_ring; + else if (shiftend) + state <= ps2_state_idle; + end + endcase + end + end + assign dbg_state = state; + + /* Tristate control of clk & data */ + assign datout = state == ps2_state_ring || state == ps2_state_send; + assign ps2dat = (datout & ~shiftreg[0]) ? 1'b0 : 1'bz; + assign ps2clk = (state == ps2_state_ring) ? 1'b0 : 1'bz; + + /* Bit counter */ + always@(posedge sysclk or posedge reset) begin + if (reset) + shiftcnt <= 10; + else begin + if (state == ps2_state_idle) + shiftcnt <= 10; + else if (state == ps2_state_ring) + shiftcnt <= 11; + else if (clkdown && state != ps2_state_ring) + shiftcnt <= shiftcnt - 1; + end + end + + /* Shift register, ticks on falling edge of ps2 clock */ + always@(posedge sysclk or posedge reset) begin + if (reset) + shiftreg <= 0; + else begin + if (oreq) + shiftreg <= { 1'b1, opar, obyte, 1'b0 }; + else if (clkdown && state != ps2_state_ring) + shiftreg <= { datsync, shiftreg[10:1] }; + end + end + + + /* Ack/strobe logic */ + assign shiftend = shiftcnt == 0; + assign oack = (state == ps2_state_send && shiftend); + assign istrobe = (state == ps2_state_recv && shiftend); + assign ibyte = shiftreg[8:1]; + + /* Filters/synchronizers on PS/2 clock */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + clkbuf <= 0; + clksync <= 0; + clkprev <= 0; + end else begin + clkprev <= clksync; + clkbuf <= { clkbuf[6:0], ps2clk }; + if (clkbuf[7:2] == 6'b000000) + clksync <= 0; + if (clkbuf[7:2] == 6'b111111) + clksync <= 1; + end + end + assign clkdown = clkprev & ~clksync; + + /* Filters/synchronizers on PS/2 data */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + datbuf <= 0; + datsync <= 0; + end else begin + datbuf <= { datbuf[6:0], ps2dat }; + if (datbuf[7:2] == 6'b000000) + datsync <= 0; + if (datbuf[7:2] == 6'b111111) + datsync <= 1; + end + end + + /* Parity for output byte */ + assign opar = ~(obyte[0] ^ obyte[1] ^ obyte[2] ^ obyte[3] ^ + obyte[4] ^ obyte[5] ^ obyte[6] ^ obyte[7]); + + /* Timeout logic */ + always@(posedge sysclk or posedge reset) begin + if (reset) + timecnt <= 0; + else begin + if (clkdown | oreq) + timecnt <= 0; + else + timecnt <= timecnt + 1; + end + end + assign timeout = (timecnt == 24'hff_ffff); +endmodule diff --git a/fpga/ps2_kbd.v b/fpga/ps2_kbd.v new file mode 100644 index 0000000..3049745 --- /dev/null +++ b/fpga/ps2_kbd.v @@ -0,0 +1,800 @@ +`timescale 1ns / 100ps + +/* + * PS2 Keyboard to Mac interface module + */ +module ps2_kbd( input sysclk, + input reset, + + inout ps2dat, + inout ps2clk, + + input [7:0] data_out, + input strobe_out, + + output [7:0] data_in, + output strobe_in, + + output reg [15:0] debug +); + + reg [8:0] keymac; + reg key_pending; + reg [21:0] pacetimer; + reg inquiry_active; + reg extended; + reg keybreak; + reg capslock; + reg haskey; + wire got_key; + wire got_break; + wire got_extend; + wire tick_short; + wire tick_long; + wire pop_key; + reg cmd_inquiry; + reg cmd_instant; + reg cmd_model; + reg cmd_test; + reg[1:0] state; + reg[1:0] next; + reg nreq; + reg[7:0] nbyte; + + /* PS2 interface signals */ + wire istrobe; + wire [7:0] ibyte; + reg oreq; + reg [7:0] obyte; + wire oack; + wire timeout; + wire [1:0] dbg_lowstate; + + ps2 ps20(.sysclk(sysclk), + .reset(reset), + .ps2dat(ps2dat), + .ps2clk(ps2clk), + .istrobe(istrobe), + .ibyte(ibyte), + .oreq(oreq), + .obyte(obyte), + .oack(oack), + .timeout(timeout), + .dbg_state(dbg_lowstate)); + + /* --- PS2 side State machine --- + * + * - at state_init: wait for BAT reply + * * 0xaa -> send 0xed -> state_led1 + * * bad reply -> send 0xff -> state_init + * * timeout -> send 0xff -> state_init + * + * - at state_led1: wait for ack, send LED state + * * 0xfa -> send 0xYY -> state_led2 YY=LED + * * bad reply -> send 0xff -> state_init + * * timeout -> send 0xff -> state_init + * + * - at state_led2: wait for ack, go wait data + * * 0xfa -> state_wait + * * bad reply -> send 0xff -> state_init + * * timeout -> send 0xff -> state_init + * + * - at state_wait: wait for data byte + * * capslock -> send 0xed -> state_led1 + * * other -> state_wait + * + * Works fine with my logitech USB/PS2 keyboard, but fails + * miserably with an old HP PS2 keyboard (which also behaves + * oddly with minimig as an amiga). Will eventually investigate... + */ + localparam ps2k_state_init = 0; + localparam ps2k_state_led1 = 1; + localparam ps2k_state_led2 = 2; + localparam ps2k_state_wait = 3; + + + /* Unlike my other modules, here I'll play with a big fat + * combo logic. The outputs are: + * - oreq : triggers sending of a byte. Set based on either + * timeout or istrobe, and as such only set for a + * clock. + * - next : next state + * - obyte : next byte to send + */ + always@(timeout or state or istrobe or ibyte or capslock) begin + nreq = 0; + next = state; + nbyte = 8'hff; + + if (istrobe || timeout) + case(state) + ps2k_state_init: begin + if (istrobe && ibyte == 8'haa) begin + nbyte = 8'hed; + nreq = 1; + next = ps2k_state_led1; + end else if (ibyte != 8'hfa) + nreq = 1; + end + ps2k_state_led1: begin + nreq = 1; + if (istrobe && ibyte == 8'hfa) begin + nbyte = { 5'b00000, capslock, 1'b1, 1'b0 }; + next = ps2k_state_led2; + end else + next = ps2k_state_init; + end + ps2k_state_led2: begin + if (istrobe && ibyte == 8'hfa) + next = ps2k_state_wait; + else begin + nreq = 1; + next = ps2k_state_init; + end + end + ps2k_state_wait: begin + /* Update LEDs */ + if (istrobe && ibyte == 8'h58) begin + nbyte = 8'hed; + nreq = 1; + next = ps2k_state_led1; + end + end + endcase + end + + /* State related latches. We latch oreq and obyte, we don't + * necessarily have to but that avoids back to back + * receive/send at the low level which can upset things + */ + always@(posedge sysclk or posedge reset) + if (reset) + state <= ps2k_state_init; + else + state <= next; + always@(posedge sysclk or posedge reset) + if (reset) + oreq <= 0; + else + oreq <= nreq; + always@(posedge sysclk or posedge reset) + if (reset) + obyte <= 0; + else + obyte <= nbyte; + + assign got_key = (state == ps2k_state_wait) && istrobe; + assign got_break = { ibyte[7:1], 1'b0 } == 8'hf0; + assign got_extend = { ibyte[7:1], 1'b0 } == 8'he0; + + /* Latch key info from PS2, handle capslock state */ + always@(posedge sysclk or posedge reset) + if (reset) begin + extended <= 0; + keybreak <= 0; + capslock <= 0; + end else if (got_key) begin + if (got_break) + keybreak <= 1; + else if (got_extend) + extended <= 1; + else begin + keybreak <= 0; + extended <= 0; + + /* Capslock handling */ + if (ibyte == 8'h58 && !keybreak) + capslock <= ~capslock; + end + end + + /* --- Mac side --- */ + + /* Latch commands from Mac */ + always@(posedge sysclk or posedge reset) + if (reset) begin + cmd_inquiry <= 0; + cmd_instant <= 0; + cmd_model <= 0; + cmd_test <= 0; + end else begin + if (strobe_out) begin + cmd_inquiry <= 0; + cmd_instant <= 0; + cmd_model <= 0; + cmd_test <= 0; + case(data_out) + 8'h10: cmd_inquiry <= 1; + 8'h14: cmd_instant <= 1; + 8'h16: cmd_model <= 1; + 8'h36: cmd_test <= 1; + endcase + end + end + + /* Divide our clock to pace our responses to the Mac. tick_short ticks + * when we can respond to a command, and tick_long ticks when an inquiry + * command shall timeout + */ + always@(posedge sysclk or posedge reset) + if (reset) + pacetimer <= 0; + else begin + /* reset counter on command from Mac */ + if (strobe_out) + pacetimer <= 0; + else if (!tick_long) + pacetimer <= pacetimer + 1; + end + assign tick_long = pacetimer == 22'h3fffff; + assign tick_short = pacetimer == 22'h000fff; + + /* Delay inquiry responses to after tick_short */ + always@(posedge sysclk or posedge reset) + if (reset) + inquiry_active <= 0; + else begin + if (strobe_out | strobe_in) + inquiry_active <= 0; + else if (tick_short) + inquiry_active <= cmd_inquiry; + end + + /* Key answer to the mac XXX FIXME: keypad */ + assign pop_key = (cmd_instant & tick_short) | + (inquiry_active & tick_long) | + (inquiry_active & key_pending); + + /* Reply to Mac */ + assign strobe_in = ((cmd_model | cmd_test) & tick_short) | pop_key; + + /* Latch key_pending */ + always @(posedge sysclk or posedge reset) + if (reset) + key_pending <= 0; + else begin + if (pop_key | cmd_model | cmd_test) + key_pending <= 0; + else if (!key_pending & got_key && !got_break && !got_extend) + key_pending <= 1; + end + + /* Data to Mac ... XXX: Handle keypad and special case capslock */ + assign data_in = cmd_test ? 8'h7d : + cmd_model ? 8'h03 : + key_pending ? keymac[7:0] : 8'h7b; + + /* Keymap. XXX add option to assign ctrl/alt/windows to cmd/option + * differently + */ + always @(posedge sysclk) + if (got_key && !key_pending) begin + case({extended,ibyte}) // Scan Code Set 2 + 9'h000: keymac[8:0] <= 9'h07b; + 9'h001: keymac[8:0] <= 9'h07b; //F9 + 9'h002: keymac[8:0] <= 9'h07b; + 9'h003: keymac[8:0] <= 9'h07b; //F5 + 9'h004: keymac[8:0] <= 9'h07b; //F3 + 9'h005: keymac[8:0] <= 9'h07b; //F1 + 9'h006: keymac[8:0] <= 9'h07b; //F2 + 9'h007: keymac[8:0] <= 9'h07b; //F12 + 9'h008: keymac[8:0] <= 9'h07b; + 9'h009: keymac[8:0] <= 9'h07b; //F10 + 9'h00a: keymac[8:0] <= 9'h07b; //F8 + 9'h00b: keymac[8:0] <= 9'h07b; //F6 + 9'h00c: keymac[8:0] <= 9'h07b; //F4 + 9'h00d: keymac[8:0] <= 9'h061; //TAB + 9'h00e: keymac[8:0] <= 9'h065; //~ (`) + 9'h00f: keymac[8:0] <= 9'h07b; + 9'h010: keymac[8:0] <= 9'h07b; + 9'h011: keymac[8:0] <= 9'h075; //LEFT ALT (option) + 9'h012: keymac[8:0] <= 9'h071; //LEFT SHIFT + 9'h013: keymac[8:0] <= 9'h07b; + 9'h014: keymac[8:0] <= 9'h06f; //CTRL (command) + 9'h015: keymac[8:0] <= 9'h019; //q + 9'h016: keymac[8:0] <= 9'h025; //1 + 9'h017: keymac[8:0] <= 9'h07b; + 9'h018: keymac[8:0] <= 9'h07b; + 9'h019: keymac[8:0] <= 9'h07b; + 9'h01a: keymac[8:0] <= 9'h00d; //z + 9'h01b: keymac[8:0] <= 9'h003; //s + 9'h01c: keymac[8:0] <= 9'h001; //a + 9'h01d: keymac[8:0] <= 9'h01b; //w + 9'h01e: keymac[8:0] <= 9'h027; //2 + 9'h01f: keymac[8:0] <= 9'h07b; + 9'h020: keymac[8:0] <= 9'h07b; + 9'h021: keymac[8:0] <= 9'h011; //c + 9'h022: keymac[8:0] <= 9'h00f; //x + 9'h023: keymac[8:0] <= 9'h005; //d + 9'h024: keymac[8:0] <= 9'h01d; //e + 9'h025: keymac[8:0] <= 9'h02b; //4 + 9'h026: keymac[8:0] <= 9'h029; //3 + 9'h027: keymac[8:0] <= 9'h07b; + 9'h028: keymac[8:0] <= 9'h07b; + 9'h029: keymac[8:0] <= 9'h063; //SPACE + 9'h02a: keymac[8:0] <= 9'h013; //v + 9'h02b: keymac[8:0] <= 9'h007; //f + 9'h02c: keymac[8:0] <= 9'h023; //t + 9'h02d: keymac[8:0] <= 9'h01f; //r + 9'h02e: keymac[8:0] <= 9'h02f; //5 + 9'h02f: keymac[8:0] <= 9'h07b; + 9'h030: keymac[8:0] <= 9'h07b; + 9'h031: keymac[8:0] <= 9'h05b; //n + 9'h032: keymac[8:0] <= 9'h017; //b + 9'h033: keymac[8:0] <= 9'h009; //h + 9'h034: keymac[8:0] <= 9'h00b; //g + 9'h035: keymac[8:0] <= 9'h021; //y + 9'h036: keymac[8:0] <= 9'h02d; //6 + 9'h037: keymac[8:0] <= 9'h07b; + 9'h038: keymac[8:0] <= 9'h07b; + 9'h039: keymac[8:0] <= 9'h07b; + 9'h03a: keymac[8:0] <= 9'h05d; //m + 9'h03b: keymac[8:0] <= 9'h04d; //j + 9'h03c: keymac[8:0] <= 9'h041; //u + 9'h03d: keymac[8:0] <= 9'h035; //7 + 9'h03e: keymac[8:0] <= 9'h039; //8 + 9'h03f: keymac[8:0] <= 9'h07b; + 9'h040: keymac[8:0] <= 9'h07b; + 9'h041: keymac[8:0] <= 9'h057; //<, + 9'h042: keymac[8:0] <= 9'h051; //k + 9'h043: keymac[8:0] <= 9'h045; //i + 9'h044: keymac[8:0] <= 9'h03f; //o + 9'h045: keymac[8:0] <= 9'h03b; //0 + 9'h046: keymac[8:0] <= 9'h033; //9 + 9'h047: keymac[8:0] <= 9'h07b; + 9'h048: keymac[8:0] <= 9'h07b; + 9'h049: keymac[8:0] <= 9'h05f; //>. + 9'h04a: keymac[8:0] <= 9'h059; //FORWARD SLASH + 9'h04b: keymac[8:0] <= 9'h04b; //l + 9'h04c: keymac[8:0] <= 9'h053; //; + 9'h04d: keymac[8:0] <= 9'h047; //p + 9'h04e: keymac[8:0] <= 9'h037; //- + 9'h04f: keymac[8:0] <= 9'h07b; + 9'h050: keymac[8:0] <= 9'h07b; + 9'h051: keymac[8:0] <= 9'h07b; + 9'h052: keymac[8:0] <= 9'h04f; //'" + 9'h053: keymac[8:0] <= 9'h07b; + 9'h054: keymac[8:0] <= 9'h043; //[ + 9'h055: keymac[8:0] <= 9'h031; // = + 9'h056: keymac[8:0] <= 9'h07b; + 9'h057: keymac[8:0] <= 9'h07b; + 9'h058: keymac[8:0] <= 9'h073; //CAPSLOCK + 9'h059: keymac[8:0] <= 9'h071; //RIGHT SHIFT + 9'h05a: keymac[8:0] <= 9'h049; //ENTER + 9'h05b: keymac[8:0] <= 9'h03d; //] + 9'h05c: keymac[8:0] <= 9'h07b; + 9'h05d: keymac[8:0] <= 9'h055; //BACKSLASH + 9'h05e: keymac[8:0] <= 9'h07b; + 9'h05f: keymac[8:0] <= 9'h07b; + 9'h060: keymac[8:0] <= 9'h07b; + 9'h061: keymac[8:0] <= 9'h071; //international left shift cut out (German '<>' key), 0x56 Set#1 code + 9'h062: keymac[8:0] <= 9'h07b; + 9'h063: keymac[8:0] <= 9'h07b; + 9'h064: keymac[8:0] <= 9'h07b; + 9'h065: keymac[8:0] <= 9'h07b; + 9'h066: keymac[8:0] <= 9'h067; //BACKSPACE + 9'h067: keymac[8:0] <= 9'h07b; + 9'h068: keymac[8:0] <= 9'h07b; + 9'h069: keymac[8:0] <= 9'h127; //KP 1 + 9'h06a: keymac[8:0] <= 9'h07b; + 9'h06b: keymac[8:0] <= 9'h12d; //KP 4 + 9'h06c: keymac[8:0] <= 9'h133; //KP 7 + 9'h06d: keymac[8:0] <= 9'h07b; + 9'h06e: keymac[8:0] <= 9'h07b; + 9'h06f: keymac[8:0] <= 9'h07b; + 9'h070: keymac[8:0] <= 9'h125; //KP 0 + 9'h071: keymac[8:0] <= 9'h103; //KP . + 9'h072: keymac[8:0] <= 9'h129; //KP 2 + 9'h073: keymac[8:0] <= 9'h12f; //KP 5 + 9'h074: keymac[8:0] <= 9'h131; //KP 6 + 9'h075: keymac[8:0] <= 9'h137; //KP 8 + 9'h076: keymac[8:0] <= 9'b000; //ESCAPE + 9'h077: keymac[8:0] <= 9'h07b; //NUMLOCK + 9'h078: keymac[8:0] <= 9'h07b; //F11 + 9'h079: keymac[8:0] <= 9'h07b; //KP + + 9'h07a: keymac[8:0] <= 9'h12b; //KP 3 + 9'h07b: keymac[8:0] <= 9'h11d; //KP - + 9'h07c: keymac[8:0] <= 9'h07b; //KP * + 9'h07d: keymac[8:0] <= 9'h139; //KP 9 + 9'h07e: keymac[8:0] <= 9'h07b; //SCROLL LOCK / KP ) + 9'h07f: keymac[8:0] <= 9'h07b; + 9'h080: keymac[8:0] <= 9'h07b; + 9'h081: keymac[8:0] <= 9'h07b; + 9'h082: keymac[8:0] <= 9'h07b; + 9'h083: keymac[8:0] <= 9'h07b; //F7 + 9'h084: keymac[8:0] <= 9'h07b; + 9'h085: keymac[8:0] <= 9'h07b; + 9'h086: keymac[8:0] <= 9'h07b; + 9'h087: keymac[8:0] <= 9'h07b; + 9'h088: keymac[8:0] <= 9'h07b; + 9'h089: keymac[8:0] <= 9'h07b; + 9'h08a: keymac[8:0] <= 9'h07b; + 9'h08b: keymac[8:0] <= 9'h07b; + 9'h08c: keymac[8:0] <= 9'h07b; + 9'h08d: keymac[8:0] <= 9'h07b; + 9'h08e: keymac[8:0] <= 9'h07b; + 9'h08f: keymac[8:0] <= 9'h07b; + 9'h090: keymac[8:0] <= 9'h07b; + 9'h091: keymac[8:0] <= 9'h07b; + 9'h092: keymac[8:0] <= 9'h07b; + 9'h093: keymac[8:0] <= 9'h07b; + 9'h094: keymac[8:0] <= 9'h07b; + 9'h095: keymac[8:0] <= 9'h07b; + 9'h096: keymac[8:0] <= 9'h07b; + 9'h097: keymac[8:0] <= 9'h07b; + 9'h098: keymac[8:0] <= 9'h07b; + 9'h099: keymac[8:0] <= 9'h07b; + 9'h09a: keymac[8:0] <= 9'h07b; + 9'h09b: keymac[8:0] <= 9'h07b; + 9'h09c: keymac[8:0] <= 9'h07b; + 9'h09d: keymac[8:0] <= 9'h07b; + 9'h09e: keymac[8:0] <= 9'h07b; + 9'h09f: keymac[8:0] <= 9'h07b; + 9'h0a0: keymac[8:0] <= 9'h07b; + 9'h0a1: keymac[8:0] <= 9'h07b; + 9'h0a2: keymac[8:0] <= 9'h07b; + 9'h0a3: keymac[8:0] <= 9'h07b; + 9'h0a4: keymac[8:0] <= 9'h07b; + 9'h0a5: keymac[8:0] <= 9'h07b; + 9'h0a6: keymac[8:0] <= 9'h07b; + 9'h0a7: keymac[8:0] <= 9'h07b; + 9'h0a8: keymac[8:0] <= 9'h07b; + 9'h0a9: keymac[8:0] <= 9'h07b; + 9'h0aa: keymac[8:0] <= 9'h07b; + 9'h0ab: keymac[8:0] <= 9'h07b; + 9'h0ac: keymac[8:0] <= 9'h07b; + 9'h0ad: keymac[8:0] <= 9'h07b; + 9'h0ae: keymac[8:0] <= 9'h07b; + 9'h0af: keymac[8:0] <= 9'h07b; + 9'h0b0: keymac[8:0] <= 9'h07b; + 9'h0b1: keymac[8:0] <= 9'h07b; + 9'h0b2: keymac[8:0] <= 9'h07b; + 9'h0b3: keymac[8:0] <= 9'h07b; + 9'h0b4: keymac[8:0] <= 9'h07b; + 9'h0b5: keymac[8:0] <= 9'h07b; + 9'h0b6: keymac[8:0] <= 9'h07b; + 9'h0b7: keymac[8:0] <= 9'h07b; + 9'h0b8: keymac[8:0] <= 9'h07b; + 9'h0b9: keymac[8:0] <= 9'h07b; + 9'h0ba: keymac[8:0] <= 9'h07b; + 9'h0bb: keymac[8:0] <= 9'h07b; + 9'h0bc: keymac[8:0] <= 9'h07b; + 9'h0bd: keymac[8:0] <= 9'h07b; + 9'h0be: keymac[8:0] <= 9'h07b; + 9'h0bf: keymac[8:0] <= 9'h07b; + 9'h0c0: keymac[8:0] <= 9'h07b; + 9'h0c1: keymac[8:0] <= 9'h07b; + 9'h0c2: keymac[8:0] <= 9'h07b; + 9'h0c3: keymac[8:0] <= 9'h07b; + 9'h0c4: keymac[8:0] <= 9'h07b; + 9'h0c5: keymac[8:0] <= 9'h07b; + 9'h0c6: keymac[8:0] <= 9'h07b; + 9'h0c7: keymac[8:0] <= 9'h07b; + 9'h0c8: keymac[8:0] <= 9'h07b; + 9'h0c9: keymac[8:0] <= 9'h07b; + 9'h0ca: keymac[8:0] <= 9'h07b; + 9'h0cb: keymac[8:0] <= 9'h07b; + 9'h0cc: keymac[8:0] <= 9'h07b; + 9'h0cd: keymac[8:0] <= 9'h07b; + 9'h0ce: keymac[8:0] <= 9'h07b; + 9'h0cf: keymac[8:0] <= 9'h07b; + 9'h0d0: keymac[8:0] <= 9'h07b; + 9'h0d1: keymac[8:0] <= 9'h07b; + 9'h0d2: keymac[8:0] <= 9'h07b; + 9'h0d3: keymac[8:0] <= 9'h07b; + 9'h0d4: keymac[8:0] <= 9'h07b; + 9'h0d5: keymac[8:0] <= 9'h07b; + 9'h0d6: keymac[8:0] <= 9'h07b; + 9'h0d7: keymac[8:0] <= 9'h07b; + 9'h0d8: keymac[8:0] <= 9'h07b; + 9'h0d9: keymac[8:0] <= 9'h07b; + 9'h0da: keymac[8:0] <= 9'h07b; + 9'h0db: keymac[8:0] <= 9'h07b; + 9'h0dc: keymac[8:0] <= 9'h07b; + 9'h0dd: keymac[8:0] <= 9'h07b; + 9'h0de: keymac[8:0] <= 9'h07b; + 9'h0df: keymac[8:0] <= 9'h07b; + 9'h0e0: keymac[8:0] <= 9'h07b; //ps2 extended key + 9'h0e1: keymac[8:0] <= 9'h07b; + 9'h0e2: keymac[8:0] <= 9'h07b; + 9'h0e3: keymac[8:0] <= 9'h07b; + 9'h0e4: keymac[8:0] <= 9'h07b; + 9'h0e5: keymac[8:0] <= 9'h07b; + 9'h0e6: keymac[8:0] <= 9'h07b; + 9'h0e7: keymac[8:0] <= 9'h07b; + 9'h0e8: keymac[8:0] <= 9'h07b; + 9'h0e9: keymac[8:0] <= 9'h07b; + 9'h0ea: keymac[8:0] <= 9'h07b; + 9'h0eb: keymac[8:0] <= 9'h07b; + 9'h0ec: keymac[8:0] <= 9'h07b; + 9'h0ed: keymac[8:0] <= 9'h07b; + 9'h0ee: keymac[8:0] <= 9'h07b; + 9'h0ef: keymac[8:0] <= 9'h07b; + 9'h0f0: keymac[8:0] <= 9'h07b; //ps2 release code + 9'h0f1: keymac[8:0] <= 9'h07b; + 9'h0f2: keymac[8:0] <= 9'h07b; + 9'h0f3: keymac[8:0] <= 9'h07b; + 9'h0f4: keymac[8:0] <= 9'h07b; + 9'h0f5: keymac[8:0] <= 9'h07b; + 9'h0f6: keymac[8:0] <= 9'h07b; + 9'h0f7: keymac[8:0] <= 9'h07b; + 9'h0f8: keymac[8:0] <= 9'h07b; + 9'h0f9: keymac[8:0] <= 9'h07b; + 9'h0fa: keymac[8:0] <= 9'h07b; //ps2 ack code + 9'h0fb: keymac[8:0] <= 9'h07b; + 9'h0fc: keymac[8:0] <= 9'h07b; + 9'h0fd: keymac[8:0] <= 9'h07b; + 9'h0fe: keymac[8:0] <= 9'h07b; + 9'h0ff: keymac[8:0] <= 9'h07b; + 9'h100: keymac[8:0] <= 9'h07b; + 9'h101: keymac[8:0] <= 9'h07b; + 9'h102: keymac[8:0] <= 9'h07b; + 9'h103: keymac[8:0] <= 9'h07b; + 9'h104: keymac[8:0] <= 9'h07b; + 9'h105: keymac[8:0] <= 9'h07b; + 9'h106: keymac[8:0] <= 9'h07b; + 9'h107: keymac[8:0] <= 9'h07b; + 9'h108: keymac[8:0] <= 9'h07b; + 9'h109: keymac[8:0] <= 9'h07b; + 9'h10a: keymac[8:0] <= 9'h07b; + 9'h10b: keymac[8:0] <= 9'h07b; + 9'h10c: keymac[8:0] <= 9'h07b; + 9'h10d: keymac[8:0] <= 9'h07b; + 9'h10e: keymac[8:0] <= 9'h07b; + 9'h10f: keymac[8:0] <= 9'h07b; + 9'h110: keymac[8:0] <= 9'h07b; + 9'h111: keymac[8:0] <= 9'h075; //RIGHT ALT + 9'h112: keymac[8:0] <= 9'h07b; + 9'h113: keymac[8:0] <= 9'h07b; + 9'h114: keymac[8:0] <= 9'h07b; + 9'h115: keymac[8:0] <= 9'h07b; + 9'h116: keymac[8:0] <= 9'h07b; + 9'h117: keymac[8:0] <= 9'h07b; + 9'h118: keymac[8:0] <= 9'h07b; + 9'h119: keymac[8:0] <= 9'h07b; + 9'h11a: keymac[8:0] <= 9'h07b; + 9'h11b: keymac[8:0] <= 9'h07b; + 9'h11c: keymac[8:0] <= 9'h07b; + 9'h11d: keymac[8:0] <= 9'h07b; + 9'h11e: keymac[8:0] <= 9'h07b; + 9'h11f: keymac[8:0] <= 9'h06f; //LEFT COMMAND (LEFT GUI) + 9'h120: keymac[8:0] <= 9'h07b; + 9'h121: keymac[8:0] <= 9'h07b; + 9'h122: keymac[8:0] <= 9'h07b; + 9'h123: keymac[8:0] <= 9'h07b; + 9'h124: keymac[8:0] <= 9'h07b; + 9'h125: keymac[8:0] <= 9'h07b; + 9'h126: keymac[8:0] <= 9'h07b; + 9'h127: keymac[8:0] <= 9'h06f; //RIGHT AMIGA (RIGHT GUI) + 9'h128: keymac[8:0] <= 9'h07b; + 9'h129: keymac[8:0] <= 9'h07b; + 9'h12a: keymac[8:0] <= 9'h07b; + 9'h12b: keymac[8:0] <= 9'h07b; + 9'h12c: keymac[8:0] <= 9'h07b; + 9'h12d: keymac[8:0] <= 9'h07b; + 9'h12e: keymac[8:0] <= 9'h07b; + 9'h12f: keymac[8:0] <= 9'h06f; //RIGHT AMIGA (APPS) + 9'h130: keymac[8:0] <= 9'h07b; + 9'h131: keymac[8:0] <= 9'h07b; + 9'h132: keymac[8:0] <= 9'h07b; + 9'h133: keymac[8:0] <= 9'h07b; + 9'h134: keymac[8:0] <= 9'h07b; + 9'h135: keymac[8:0] <= 9'h07b; + 9'h136: keymac[8:0] <= 9'h07b; + 9'h137: keymac[8:0] <= 9'h07b; + 9'h138: keymac[8:0] <= 9'h07b; + 9'h139: keymac[8:0] <= 9'h07b; + 9'h13a: keymac[8:0] <= 9'h07b; + 9'h13b: keymac[8:0] <= 9'h07b; + 9'h13c: keymac[8:0] <= 9'h07b; + 9'h13d: keymac[8:0] <= 9'h07b; + 9'h13e: keymac[8:0] <= 9'h07b; + 9'h13f: keymac[8:0] <= 9'h07b; + 9'h140: keymac[8:0] <= 9'h07b; + 9'h141: keymac[8:0] <= 9'h07b; + 9'h142: keymac[8:0] <= 9'h07b; + 9'h143: keymac[8:0] <= 9'h07b; + 9'h144: keymac[8:0] <= 9'h07b; + 9'h145: keymac[8:0] <= 9'h07b; + 9'h146: keymac[8:0] <= 9'h07b; + 9'h147: keymac[8:0] <= 9'h07b; + 9'h148: keymac[8:0] <= 9'h07b; + 9'h149: keymac[8:0] <= 9'h07b; + 9'h14a: keymac[8:0] <= 9'h07b; //KP / + 9'h14b: keymac[8:0] <= 9'h07b; + 9'h14c: keymac[8:0] <= 9'h07b; + 9'h14d: keymac[8:0] <= 9'h07b; + 9'h14e: keymac[8:0] <= 9'h07b; + 9'h14f: keymac[8:0] <= 9'h07b; + 9'h150: keymac[8:0] <= 9'h07b; + 9'h151: keymac[8:0] <= 9'h07b; + 9'h152: keymac[8:0] <= 9'h07b; + 9'h153: keymac[8:0] <= 9'h07b; + 9'h154: keymac[8:0] <= 9'h07b; + 9'h155: keymac[8:0] <= 9'h07b; + 9'h156: keymac[8:0] <= 9'h07b; + 9'h157: keymac[8:0] <= 9'h07b; + 9'h158: keymac[8:0] <= 9'h07b; + 9'h159: keymac[8:0] <= 9'h07b; + 9'h15a: keymac[8:0] <= 9'h119; //KP ENTER + 9'h15b: keymac[8:0] <= 9'h07b; + 9'h15c: keymac[8:0] <= 9'h07b; + 9'h15d: keymac[8:0] <= 9'h07b; + 9'h15e: keymac[8:0] <= 9'h07b; + 9'h15f: keymac[8:0] <= 9'h07b; + 9'h160: keymac[8:0] <= 9'h07b; + 9'h161: keymac[8:0] <= 9'h07b; + 9'h162: keymac[8:0] <= 9'h07b; + 9'h163: keymac[8:0] <= 9'h07b; + 9'h164: keymac[8:0] <= 9'h07b; + 9'h165: keymac[8:0] <= 9'h07b; + 9'h166: keymac[8:0] <= 9'h07b; + 9'h167: keymac[8:0] <= 9'h07b; + 9'h168: keymac[8:0] <= 9'h07b; + 9'h169: keymac[8:0] <= 9'h07b; //END + 9'h16a: keymac[8:0] <= 9'h07b; + 9'h16b: keymac[8:0] <= 9'h10d; //ARROW LEFT + 9'h16c: keymac[8:0] <= 9'h07b; //HOME + 9'h16d: keymac[8:0] <= 9'h07b; + 9'h16e: keymac[8:0] <= 9'h07b; + 9'h16f: keymac[8:0] <= 9'h07b; + 9'h170: keymac[8:0] <= 9'h07b; //INSERT = HELP + 9'h171: keymac[8:0] <= 9'h10f; //DELETE + 9'h172: keymac[8:0] <= 9'h111; //ARROW DOWN + 9'h173: keymac[8:0] <= 9'h07b; + 9'h174: keymac[8:0] <= 9'h105; //ARROW RIGHT + 9'h175: keymac[8:0] <= 9'h10b; //ARROW UP + 9'h176: keymac[8:0] <= 9'h07b; + 9'h177: keymac[8:0] <= 9'h07b; + 9'h178: keymac[8:0] <= 9'h07b; + 9'h179: keymac[8:0] <= 9'h07b; + 9'h17a: keymac[8:0] <= 9'h07b; //PGDN + 9'h17b: keymac[8:0] <= 9'h07b; + 9'h17c: keymac[8:0] <= 9'h07b; //PRTSCR + 9'h17d: keymac[8:0] <= 9'h07b; //PGUP + 9'h17e: keymac[8:0] <= 9'h07b; //ctrl+break + 9'h17f: keymac[8:0] <= 9'h07b; + 9'h180: keymac[8:0] <= 9'h07b; + 9'h181: keymac[8:0] <= 9'h07b; + 9'h182: keymac[8:0] <= 9'h07b; + 9'h183: keymac[8:0] <= 9'h07b; + 9'h184: keymac[8:0] <= 9'h07b; + 9'h185: keymac[8:0] <= 9'h07b; + 9'h186: keymac[8:0] <= 9'h07b; + 9'h187: keymac[8:0] <= 9'h07b; + 9'h188: keymac[8:0] <= 9'h07b; + 9'h189: keymac[8:0] <= 9'h07b; + 9'h18a: keymac[8:0] <= 9'h07b; + 9'h18b: keymac[8:0] <= 9'h07b; + 9'h18c: keymac[8:0] <= 9'h07b; + 9'h18d: keymac[8:0] <= 9'h07b; + 9'h18e: keymac[8:0] <= 9'h07b; + 9'h18f: keymac[8:0] <= 9'h07b; + 9'h190: keymac[8:0] <= 9'h07b; + 9'h191: keymac[8:0] <= 9'h07b; + 9'h192: keymac[8:0] <= 9'h07b; + 9'h193: keymac[8:0] <= 9'h07b; + 9'h194: keymac[8:0] <= 9'h07b; + 9'h195: keymac[8:0] <= 9'h07b; + 9'h196: keymac[8:0] <= 9'h07b; + 9'h197: keymac[8:0] <= 9'h07b; + 9'h198: keymac[8:0] <= 9'h07b; + 9'h199: keymac[8:0] <= 9'h07b; + 9'h19a: keymac[8:0] <= 9'h07b; + 9'h19b: keymac[8:0] <= 9'h07b; + 9'h19c: keymac[8:0] <= 9'h07b; + 9'h19d: keymac[8:0] <= 9'h07b; + 9'h19e: keymac[8:0] <= 9'h07b; + 9'h19f: keymac[8:0] <= 9'h07b; + 9'h1a0: keymac[8:0] <= 9'h07b; + 9'h1a1: keymac[8:0] <= 9'h07b; + 9'h1a2: keymac[8:0] <= 9'h07b; + 9'h1a3: keymac[8:0] <= 9'h07b; + 9'h1a4: keymac[8:0] <= 9'h07b; + 9'h1a5: keymac[8:0] <= 9'h07b; + 9'h1a6: keymac[8:0] <= 9'h07b; + 9'h1a7: keymac[8:0] <= 9'h07b; + 9'h1a8: keymac[8:0] <= 9'h07b; + 9'h1a9: keymac[8:0] <= 9'h07b; + 9'h1aa: keymac[8:0] <= 9'h07b; + 9'h1ab: keymac[8:0] <= 9'h07b; + 9'h1ac: keymac[8:0] <= 9'h07b; + 9'h1ad: keymac[8:0] <= 9'h07b; + 9'h1ae: keymac[8:0] <= 9'h07b; + 9'h1af: keymac[8:0] <= 9'h07b; + 9'h1b0: keymac[8:0] <= 9'h07b; + 9'h1b1: keymac[8:0] <= 9'h07b; + 9'h1b2: keymac[8:0] <= 9'h07b; + 9'h1b3: keymac[8:0] <= 9'h07b; + 9'h1b4: keymac[8:0] <= 9'h07b; + 9'h1b5: keymac[8:0] <= 9'h07b; + 9'h1b6: keymac[8:0] <= 9'h07b; + 9'h1b7: keymac[8:0] <= 9'h07b; + 9'h1b8: keymac[8:0] <= 9'h07b; + 9'h1b9: keymac[8:0] <= 9'h07b; + 9'h1ba: keymac[8:0] <= 9'h07b; + 9'h1bb: keymac[8:0] <= 9'h07b; + 9'h1bc: keymac[8:0] <= 9'h07b; + 9'h1bd: keymac[8:0] <= 9'h07b; + 9'h1be: keymac[8:0] <= 9'h07b; + 9'h1bf: keymac[8:0] <= 9'h07b; + 9'h1c0: keymac[8:0] <= 9'h07b; + 9'h1c1: keymac[8:0] <= 9'h07b; + 9'h1c2: keymac[8:0] <= 9'h07b; + 9'h1c3: keymac[8:0] <= 9'h07b; + 9'h1c4: keymac[8:0] <= 9'h07b; + 9'h1c5: keymac[8:0] <= 9'h07b; + 9'h1c6: keymac[8:0] <= 9'h07b; + 9'h1c7: keymac[8:0] <= 9'h07b; + 9'h1c8: keymac[8:0] <= 9'h07b; + 9'h1c9: keymac[8:0] <= 9'h07b; + 9'h1ca: keymac[8:0] <= 9'h07b; + 9'h1cb: keymac[8:0] <= 9'h07b; + 9'h1cc: keymac[8:0] <= 9'h07b; + 9'h1cd: keymac[8:0] <= 9'h07b; + 9'h1ce: keymac[8:0] <= 9'h07b; + 9'h1cf: keymac[8:0] <= 9'h07b; + 9'h1d0: keymac[8:0] <= 9'h07b; + 9'h1d1: keymac[8:0] <= 9'h07b; + 9'h1d2: keymac[8:0] <= 9'h07b; + 9'h1d3: keymac[8:0] <= 9'h07b; + 9'h1d4: keymac[8:0] <= 9'h07b; + 9'h1d5: keymac[8:0] <= 9'h07b; + 9'h1d6: keymac[8:0] <= 9'h07b; + 9'h1d7: keymac[8:0] <= 9'h07b; + 9'h1d8: keymac[8:0] <= 9'h07b; + 9'h1d9: keymac[8:0] <= 9'h07b; + 9'h1da: keymac[8:0] <= 9'h07b; + 9'h1db: keymac[8:0] <= 9'h07b; + 9'h1dc: keymac[8:0] <= 9'h07b; + 9'h1dd: keymac[8:0] <= 9'h07b; + 9'h1de: keymac[8:0] <= 9'h07b; + 9'h1df: keymac[8:0] <= 9'h07b; + 9'h1e0: keymac[8:0] <= 9'h07b; //ps2 extended key(duplicate, see $e0) + 9'h1e1: keymac[8:0] <= 9'h07b; + 9'h1e2: keymac[8:0] <= 9'h07b; + 9'h1e3: keymac[8:0] <= 9'h07b; + 9'h1e4: keymac[8:0] <= 9'h07b; + 9'h1e5: keymac[8:0] <= 9'h07b; + 9'h1e6: keymac[8:0] <= 9'h07b; + 9'h1e7: keymac[8:0] <= 9'h07b; + 9'h1e8: keymac[8:0] <= 9'h07b; + 9'h1e9: keymac[8:0] <= 9'h07b; + 9'h1ea: keymac[8:0] <= 9'h07b; + 9'h1eb: keymac[8:0] <= 9'h07b; + 9'h1ec: keymac[8:0] <= 9'h07b; + 9'h1ed: keymac[8:0] <= 9'h07b; + 9'h1ee: keymac[8:0] <= 9'h07b; + 9'h1ef: keymac[8:0] <= 9'h07b; + 9'h1f0: keymac[8:0] <= 9'h07b; //ps2 release code(duplicate, see $f0) + 9'h1f1: keymac[8:0] <= 9'h07b; + 9'h1f2: keymac[8:0] <= 9'h07b; + 9'h1f3: keymac[8:0] <= 9'h07b; + 9'h1f4: keymac[8:0] <= 9'h07b; + 9'h1f5: keymac[8:0] <= 9'h07b; + 9'h1f6: keymac[8:0] <= 9'h07b; + 9'h1f7: keymac[8:0] <= 9'h07b; + 9'h1f8: keymac[8:0] <= 9'h07b; + 9'h1f9: keymac[8:0] <= 9'h07b; + 9'h1fa: keymac[8:0] <= 9'h07b; //ps2 ack code(duplicate see $fa) + 9'h1fb: keymac[8:0] <= 9'h07b; + 9'h1fc: keymac[8:0] <= 9'h07b; + 9'h1fd: keymac[8:0] <= 9'h07b; + 9'h1fe: keymac[8:0] <= 9'h07b; + 9'h1ff: keymac[8:0] <= 9'h07b; + endcase // case ({extended,ps2key[7:0]}) + keymac[7] <= keybreak; + end + + /* Some debug signals for my own sanity */ + always@(posedge sysclk or posedge reset) begin + if (reset) + debug <= 0; + else begin + if (istrobe) + debug[15:8] <= ibyte; + debug[7:0] <= { ps2clk, ps2dat, dbg_lowstate, 2'b0, + state }; + end + end +endmodule diff --git a/fpga/ps2_mouse.v b/fpga/ps2_mouse.v new file mode 100644 index 0000000..0a04f0a --- /dev/null +++ b/fpga/ps2_mouse.v @@ -0,0 +1,239 @@ +`timescale 1ns / 100ps + +/* + * PS2 Mouse to Mac interface module + */ +module ps2_mouse(input sysclk, + input reset, + + inout ps2dat, + inout ps2clk, + + output reg x1, + output reg y1, + output reg x2, + output reg y2, + output reg button, + + output reg [15:0] debug +); + wire istrobe; + wire [7:0] ibyte; + wire timeout; + wire oack; + reg [7:0] obyte; + reg oreq; + reg [2:0] state; + reg [2:0] next; + reg [7:0] nbyte; + reg nreq; + reg [9:0] xacc; + reg [9:0] yacc; + reg xsign; + reg ysign; + reg [11:0] clkdiv; + wire tick; + wire[1:0] dbg_lowstate; + + ps2 ps20(.sysclk(sysclk), + .reset(reset), + .ps2dat(ps2dat), + .ps2clk(ps2clk), + .istrobe(istrobe), + .ibyte(ibyte), + .oreq(oreq), + .obyte(obyte), + .oack(oack), + .timeout(timeout), + .dbg_state(dbg_lowstate)); + + /* State machine: + * + * - at state_init: wait for BAT reply + * * 0xaa -> state_id + * * 0xfa -> send 0xff -> state_init + * * bad reply -> send 0xff -> state_init + * * timeout -> send 0xff -> state_init + * + * - at state_id: wait for device_id + * * 0x00 -> send 0xf4 -> state_setup + * * bad reply -> send 0xff -> state_init + * * timeout -> send 0xff -> state_init + * + * - at state_setup: wait for enable data reporting ack + * * 0xfa -> state_byte0 + * * bad reply -> send 0xff -> state_init + * * timeout -> send 0xff -> state_init + * + * - at state_byte0: wait for data byte 0 + * * data -> state_byte1 + * * data -> state_byte1 + * * timeout -> send 0xff -> state_init + * + * - at state_byte1: wait for data byte 1 + * * data -> state_byte2 + * * timeout -> send 0xff -> state_init + * + * - at state_byte2: wait for data byte 2 + * * data -> state_byte0 + * * timeout -> send 0xff -> state_init + */ + localparam ps2m_state_init = 0; + localparam ps2m_state_id = 1; + localparam ps2m_state_setup = 2; + localparam ps2m_state_byte0 = 3; + localparam ps2m_state_byte1 = 4; + localparam ps2m_state_byte2 = 5; + + /* Unlike my other modules, here I'll play with a big fat + * combo logic. The outputs are: + * - oreq : triggers sending of a byte. Set based on either + * timeout or istrobe, and as such only set for a + * clock. + * - next : next state + * - obyte : next byte to send + */ + always@(timeout or state or istrobe or ibyte) begin + nreq = 0; + next = state; + nbyte = 8'hff; + if (timeout) begin + next = ps2m_state_init; + nreq = 1; + end else if (istrobe) + case(state) + ps2m_state_init: begin + if (ibyte == 8'haa) + next = ps2m_state_id; + else if (ibyte != 8'hfa) + nreq = 1; + end + ps2m_state_id: begin + nreq = 1; + if (ibyte == 8'h00) begin + nbyte = 8'hf4; + next = ps2m_state_setup; + end else + next = ps2m_state_init; + end + ps2m_state_setup: begin + if (ibyte == 8'hfa) + next = ps2m_state_byte0; + else begin + nreq = 1; + next = ps2m_state_init; + end + end + ps2m_state_byte0: next = ps2m_state_byte1; + ps2m_state_byte1: next = ps2m_state_byte2; + ps2m_state_byte2: next = ps2m_state_byte0; + endcase + end + + /* State related latches. We latch oreq and obyte, we don't + * necessarily have to but that avoids back to back + * receive/send at the low level which can upset things + */ + always@(posedge sysclk or posedge reset) + if (reset) + state <= ps2m_state_init; + else + state <= next; + always@(posedge sysclk or posedge reset) + if (reset) + oreq <= 0; + else + oreq <= nreq; + always@(posedge sysclk or posedge reset) + if (reset) + obyte <= 0; + else + obyte <= nbyte; + + /* Capture button state */ + always@(posedge sysclk or posedge reset) + if (reset) + button <= 1; + else if (istrobe && state == ps2m_state_byte0) + button <= ~ibyte[0]; + + /* Clock divider to flush accumulators */ + always@(posedge sysclk or posedge reset) + if (reset) + clkdiv <= 0; + else + clkdiv <= clkdiv + 1; + assign tick = clkdiv == 0; + + /* Toggle output lines base on accumulator */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + x1 <= 0; + x2 <= 0; + end else if (tick && xacc != 0) begin + x1 <= ~x1; + x2 <= ~x1 ^ ~xacc[9]; + end + end + always@(posedge sysclk or posedge reset) begin + if (reset) begin + y1 <= 0; + y2 <= 0; + end else if (tick && yacc != 0) begin + y1 <= ~y1; + y2 <= ~y1 ^ ~yacc[9]; + end + end + + /* Capture sign bits */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + xsign <= 0; + ysign <= 0; + end else if (istrobe && state == ps2m_state_byte0) begin + xsign <= ibyte[4]; + ysign <= ibyte[5]; + end + end + + /* Movement accumulators. Needs tuning ! */ + always@(posedge sysclk or posedge reset) begin + if (reset) + xacc <= 0; + else begin + /* Add movement, convert to a 10-bit number if not over */ + if (istrobe && state == ps2m_state_byte1 && xacc[8] == xacc[9]) + xacc <= xacc + { xsign, xsign, ibyte }; + else + /* Decrement */ + if (tick && xacc != 0) + xacc <= xacc + { {9{~xsign}}, 1'b1 }; + end + end + + always@(posedge sysclk or posedge reset) begin + if (reset) + yacc <= 0; + else begin + /* Add movement, convert to a 10-bit number if not over*/ + if (istrobe && state == ps2m_state_byte2 && yacc[8] == yacc[9]) + yacc <= yacc + { ysign, ysign, ibyte }; + else + /* Decrement */ + if (tick && yacc != 0) + yacc <= yacc + { {9{~ysign}}, 1'b1 }; + end + end + + /* Some debug signals for my own sanity */ + always@(posedge sysclk or posedge reset) begin + if (reset) + debug <= 0; + else begin + if (istrobe) + debug[15:8] <= ibyte; + debug[7:0] <= { ps2clk, ps2dat, dbg_lowstate, 1'b0, + state }; + end + end +endmodule diff --git a/fpga/rtc.v b/fpga/rtc.v new file mode 100644 index 0000000..1928562 --- /dev/null +++ b/fpga/rtc.v @@ -0,0 +1,180 @@ +/* Mac Plus real time clock + * + * Doesn't do much yet ... eventually needs a backbus interface + * to save/restore PRAM to the PIC. Not much I can do about the + * actual RTC tho. + * + * + * NOTE: + * + * The HW ref manual seems to indicate that the RTC chip latches + * on raising clock but SW should latch on falling clock. However + * that isn't what the ROM does. + * + * The actual transfer done by the ROM looks like: + * + * * Byte send: + * - Direction set to output, value 111 (disabled, clock & data high) + * - Set enabled and clock to 0 + * 8 times: - Set value bit and clock to 0 + * - Set clock to 1 + * + * So the transfer "completes" with clock high after sending the last bit, + * the next transfer is then either a read or a write. In the later case, + * the ROM goes back to the same routine above, which means it continues + * clocking bits without a significant "hickup". + * + * For a read however, what happens next is: + * + * - Set direction to input (at this point clock is still high) + * - Clear data (no effect as direction is input) and clock + * 8 times: - Set clock to 0 + * - Set clock to 1 + * - Read bit + * - Set direction to output + * + * Finally disable chip. + * + * You will notice that this is quite bogus since there's no way + * for the RTC chip to know after a read when to stop driving the + * data line before the Mac sets the direction back to output, + * so there -will- be some contention... oops. This isn't a problem + * for us though as we use separate signals. + */ + +module rtc(input sysclk, + input reset, + output reg onesec, + input data_in, + output data_out, + input data_clock, + input _data_enable); + + parameter divider = 23'h7a1200; + + /* Sysclk is 16Mhz, to generate a 1-sec pulse, we need + * a 23-bit counter + */ + reg [22:0] cnt; + reg oldclk; + wire dstrobe; + wire gotbyte; + reg [7:0] rdata; + reg [7:0] shift; + reg [7:0] addr; + reg dir; + reg ckaddr; + reg [2:0] bitcnt; + + localparam rtc_state_cmd = 0; + localparam rtc_state_read = 1; + localparam rtc_state_write = 2; + localparam rtc_state_cmd2 = 3; + reg [1:0] state; + + /* Generate one second tick */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + cnt <= divider; + onesec <= 0; + end else begin + if (cnt == 0) begin + onesec <= ~onesec; + cnt <= divider; + end else + cnt <= cnt - 1; + end + end + + /* Data clock edge detect buffer */ + always@(posedge sysclk or posedge reset) begin + if (reset) + oldclk <= 1; + else + oldclk <= data_clock; + end + + assign dstrobe = data_clock & ~oldclk & ~_data_enable; + +`ifdef __foo__ + /* Shift bit counter */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + bitcnt <= 7; + end else begin + if (_data_enable) + bitcnt <= 7; + else if (dstrobe) + bitcnt <= bitcnt - 1; + end + end + assign gotbyte = (bitcnt == 0) && dstrobe; + + /* Shift register */ + always@(posedge sysclk or posedge reset) begin + if (reset) + shift <= 0; + else begin + if (_data_enable) + shift <= 0; + else if (dstrobe) begin + if (state == rtc_state_read && bitcnt == 3'b111) + shift <= rdata; + else + shift <= { shift[6:0], data_in }; + end + end + end + assign data_out = shift[7]; + + /* Latch/decode command byte into address, ckaddr and dir */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + addr <= 0; + dir <= 0; + ckaddr <= 0; + end else if (gotbyte) begin + if (state == rtc_state_cmd) begin + dir <= shift[7]; + addr <= shift; + end else if (state == rtc_state_cmd2) + addr <= { addr[2:0], shift[6:2] }; + end + end + + /* State machine */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + state <= rtc_state_cmd; + end else begin + if (_data_enable) + state <= rtc_state_cmd; + else if (gotbyte) begin + if (state == rtc_state_cmd) begin + if (shift[6:3] == 4'b1111) + state <= rtc_state_cmd2; + else if (shift[7]) + state <= rtc_state_read; + else + state <= rtc_state_write; + end else if (state == rtc_state_cmd2) begin + if (addr[7]) + state <= rtc_state_read; + else + state <= rtc_state_write; + end else + state <= rtc_state_cmd; + end + end + end + + /* Read machine */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + rdata <= 0; + end else begin + end + end +`endif + +endmodule diff --git a/fpga/scc.v b/fpga/scc.v new file mode 100644 index 0000000..6984af9 --- /dev/null +++ b/fpga/scc.v @@ -0,0 +1,688 @@ +`timescale 1ns / 100ps + +/* + * Zilog 8530 SCC module for minimigmac. + * + * Located on high data bus, but writes are done at odd addresses as + * LDS is used as WR signals or something like that on a Mac Plus. + * + * We don't care here and just ignore which side was used. + * + * NOTE: We don't implement the 85C30 or ESCC additions such as WR7' + * for now, it's all very simplified + */ + +module scc(input sysclk, + input reset_hw, + + /* Bus interface. 2-bit address, to be wired + * appropriately upstream (to A1..A2). + */ + input cs, + input we, + input [1:0] rs, /* [1] = data(1)/ctl [0] = a_side(1)/b_side */ + input [7:0] wdata, + output [7:0] rdata, + output _irq, + + /* A single serial port on Minimig */ + input rxd, + output txd, + input cts, /* normally wired to device DTR output + * on Mac cables. That same line is also + * connected to the TRxC input of the SCC + * to do fast clocking but we don't do that + * here + */ + output rts, /* on a real mac this activates line + * drivers when low */ + + /* DCD for both ports are hijacked by mouse interface */ + input dcd_a, /* We don't synchronize those inputs */ + input dcd_b, + + /* Write request */ + output wreq + ); + + /* Register access is semi-insane */ + reg [3:0] rindex; + wire wreg_a; + wire wreg_b; + wire wdata_a; + wire wdata_b; + wire rdata_a; + wire rdata_b; + + /* Resets via WR9, one clk pulses */ + wire reset_a; + wire reset_b; + wire reset; + + /* Data registers */ + reg [7:0] data_a; + reg [7:0] data_b; + + /* Read registers */ + wire [7:0] rr0_a; + wire [7:0] rr0_b; + wire [7:0] rr1_a; + wire [7:0] rr1_b; + wire [7:0] rr2_b; + wire [7:0] rr3_a; + wire [7:0] rr10_a; + wire [7:0] rr10_b; + wire [7:0] rr15_a; + wire [7:0] rr15_b; + + /* Write registers. Only some are implemented, + * some result in actions on write and don't + * store anything + */ + reg [7:0] wr1_a; + reg [7:0] wr1_b; + reg [7:0] wr2; + reg [7:0] wr3_a; + reg [7:0] wr3_b; + reg [7:0] wr4_a; + reg [7:0] wr4_b; + reg [7:0] wr5_a; + reg [7:0] wr5_b; + reg [7:0] wr6_a; + reg [7:0] wr6_b; + reg [7:0] wr7_a; + reg [7:0] wr7_b; + reg [7:0] wr8_a; + reg [7:0] wr8_b; + reg [5:0] wr9; + reg [7:0] wr10_a; + reg [7:0] wr10_b; + reg [7:0] wr11_a; + reg [7:0] wr11_b; + reg [7:0] wr12_a; + reg [7:0] wr12_b; + reg [7:0] wr13_a; + reg [7:0] wr13_b; + reg [7:0] wr14_a; + reg [7:0] wr14_b; + reg [7:0] wr15_a; + reg [7:0] wr15_b; + + /* Status latches */ + reg latch_open_a; + reg latch_open_b; + reg dcd_latch_a; + reg dcd_latch_b; + wire dcd_ip_a; + wire dcd_ip_b; + wire do_latch_a; + wire do_latch_b; + wire do_extreset_a; + wire do_extreset_b; + + /* IRQ stuff */ + wire rx_irq_pend_a; + wire rx_irq_pend_b; + wire tx_irq_pend_a; + wire tx_irq_pend_b; + wire ex_irq_pend_a; + wire ex_irq_pend_b; + reg ex_irq_ip_a; + reg ex_irq_ip_b; + wire [2:0] rr2_vec_stat; + + /* Register/Data access helpers */ + assign wreg_a = cs & we & (~rs[1]) & rs[0]; + assign wreg_b = cs & we & (~rs[1]) & ~rs[0]; + assign wdata_a = cs & we & (rs[1] | (rindex == 8)) & rs[0]; + assign wdata_b = cs & we & (rs[1] | (rindex == 8)) & ~rs[0]; + assign rdata_a = cs & (~we) & (rs[1] | (rindex == 8)) & rs[0]; + assign rdata_b = cs & (~we) & (rs[1] | (rindex == 8)) & ~rs[0]; + + /* Register index is set by a write to WR0 and reset + * after any subsequent write. We ignore the side + */ + always@(posedge sysclk or posedge reset) begin + if (reset) + rindex <= 0; + else if (cs && !rs[1]) begin + /* Default, reset index */ + rindex <= 0; + + /* Write to WR0 */ + if (we && rindex == 0) begin + /* Get low index bits */ + rindex[2:0] <= wdata[2:0]; + + /* Add point high */ + rindex[3] <= (wdata[5:3] == 3'b001); + end + end + end + + /* Reset logic (write to WR9 cmd) + * + * Note about resets: Some bits are documented as unchanged/undefined on + * HW reset by the doc. We apply this to channel and soft resets, however + * we _do_ reset every bit on an external HW reset in this implementation + * to make the FPGA & synthesis tools happy. + */ + assign reset = ((wreg_a | wreg_b) & (rindex == 9) & (wdata[7:6] == 2'b11)) | reset_hw; + assign reset_a = ((wreg_a | wreg_b) & (rindex == 9) & (wdata[7:6] == 2'b10)) | reset; + assign reset_b = ((wreg_a | wreg_b) & (rindex == 9) & (wdata[7:6] == 2'b01)) | reset; + + /* WR1 + * Reset: bit 5 and 2 unchanged */ + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr1_a <= 0; + else begin + if (reset_a) + wr1_a <= { 2'b00, wr1_a[5], 2'b00, wr1_a[2], 2'b00 }; + else if (wreg_a && rindex == 1) + wr1_a <= wdata; + end + end + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr1_b <= 0; + else begin + if (reset_b) + wr1_b <= { 2'b00, wr1_b[5], 2'b00, wr1_b[2], 2'b00 }; + else if (wreg_b && rindex == 1) + wr1_b <= wdata; + end + end + + /* WR2 + * Reset: unchanged + */ + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr2 <= 0; + else if ((wreg_a || wreg_b) && rindex == 2) + wr2 <= wdata; + end + + /* WR3 + * Reset: bit 0 to 0, otherwise unchanged. + */ + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr3_a <= 0; + else begin + if (reset_a) + wr3_a[0] <= 0; + else if (wreg_a && rindex == 3) + wr3_a <= wdata; + end + end + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr3_b <= 0; + else begin + if (reset_b) + wr3_b[0] <= 0; + else if (wreg_b && rindex == 3) + wr3_b <= wdata; + end + end + + /* WR4 + * Reset: Bit 2 to 1, otherwise unchanged + */ + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr4_a <= 0; + else begin + if (reset_a) + wr4_a[2] <= 1; + else if (wreg_a && rindex == 4) + wr4_a <= wdata; + end + end + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr4_b <= 0; + else begin + if (reset_b) + wr4_b[2] <= 1; + else if (wreg_b && rindex == 4) + wr4_b <= wdata; + end + end + + /* WR5 + * Reset: Bits 7,4,3,2,1 to 0 + */ + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr5_a <= 0; + else begin + if (reset_a) + wr5_a <= { 1'b0, wr5_a[6:5], 4'b0000, wr5_a[0] }; + else if (wreg_a && rindex == 5) + wr5_a <= wdata; + end + end + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr5_b <= 0; + else begin + if (reset_b) + wr5_b <= { 1'b0, wr5_b[6:5], 4'b0000, wr5_b[0] }; + else if (wreg_b && rindex == 5) + wr5_b <= wdata; + end + end + + /* WR6 + * Reset: Unchanged. + */ + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr6_a <= 0; + else if (wreg_a && rindex == 6) + wr6_a <= wdata; + end + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr6_b <= 0; + else if (wreg_b && rindex == 6) + wr6_b <= wdata; + end + + /* WR7 + * Reset: Unchanged. + */ + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr7_a <= 0; + else if (wreg_a && rindex == 7) + wr7_a <= wdata; + end + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr7_b <= 0; + else if (wreg_b && rindex == 7) + wr7_b <= wdata; + end + + /* WR9. Special: top bits are reset, handled separately, bottom + * bits are only reset by a hw reset + */ + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr9 <= 0; + else if ((wreg_a || wreg_b) && rindex == 9) + wr9 <= wdata[5:0]; + end + + /* WR10 + * Reset: all 0, except chanel reset retains 6 and 5 + */ + always@(posedge sysclk or posedge reset) begin + if (reset) + wr10_a <= 0; + else begin + if (reset_a) + wr10_a <= { 1'b0, wr10_a[6:5], 5'b00000 }; + else if (wreg_a && rindex == 10) + wr10_a <= wdata; + end + end + always@(posedge sysclk or posedge reset) begin + if (reset) + wr10_b <= 0; + else begin + if (reset_b) + wr10_b <= { 1'b0, wr10_b[6:5], 5'b00000 }; + else if (wreg_b && rindex == 10) + wr10_b <= wdata; + end + end + + /* WR11 + * Reset: On full reset only, not channel reset + */ + always@(posedge sysclk or posedge reset) begin + if (reset) + wr11_a <= 8'b00001000; + else if (wreg_a && rindex == 11) + wr11_a <= wdata; + end + always@(posedge sysclk or posedge reset) begin + if (reset) + wr11_b <= 8'b00001000; + else if (wreg_b && rindex == 11) + wr11_b <= wdata; + end + + /* WR12 + * Reset: Unchanged + */ + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr12_a <= 0; + else if (wreg_a && rindex == 12) + wr12_a <= wdata; + end + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr12_b <= 0; + else if (wreg_b && rindex == 12) + wr12_b <= wdata; + end + + /* WR13 + * Reset: Unchanged + */ + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr13_a <= 0; + else if (wreg_a && rindex == 13) + wr13_a <= wdata; + end + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr13_b <= 0; + else if (wreg_b && rindex == 13) + wr13_b <= wdata; + end + + /* WR14 + * Reset: Full reset maintains top 2 bits, + * Chan reset also maitains bottom 2 bits, bit 4 also + * reset to a different value + */ + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr14_a <= 0; + else begin + if (reset) + wr14_a <= { wr14_a[7:6], 6'b110000 }; + else if (reset_a) + wr14_a <= { wr14_a[7:6], 4'b1000, wr14_a[1:0] }; + else if (wreg_a && rindex == 14) + wr14_a <= wdata; + end + end + always@(posedge sysclk or posedge reset_hw) begin + if (reset_hw) + wr14_b <= 0; + else begin + if (reset) + wr14_b <= { wr14_b[7:6], 6'b110000 }; + else if (reset_b) + wr14_b <= { wr14_b[7:6], 4'b1000, wr14_b[1:0] }; + else if (wreg_b && rindex == 14) + wr14_b <= wdata; + end + end + + /* WR15 */ + always@(posedge sysclk or posedge reset) begin + if (reset) + wr15_a <= 8'b11111000; + else if (wreg_a && rindex == 15) + wr15_a <= wdata; + end + always@(posedge sysclk or posedge reset) begin + if (reset) + wr15_b <= 8'b11111000; + else if (wreg_b && rindex == 15) + wr15_b <= wdata; + end + + /* Read data mux */ + assign rdata = rs[1] && rs[0] ? data_a : + rs[1] ? data_b : + rindex == 0 && rs[0] ? rr0_a : + rindex == 0 ? rr0_b : + rindex == 1 && rs[0] ? rr1_a : + rindex == 1 ? rr1_b : + rindex == 2 && rs[0] ? wr2 : + rindex == 2 ? rr2_b : + rindex == 3 && rs[0] ? rr3_a : + rindex == 3 ? 0 : + rindex == 4 && rs[0] ? rr0_a : + rindex == 4 ? rr0_b : + rindex == 5 && rs[0] ? rr1_a : + rindex == 5 ? rr1_b : + rindex == 6 && rs[0] ? wr2 : + rindex == 6 ? rr2_b : + rindex == 7 && rs[0] ? rr3_a : + rindex == 7 ? 0 : + + rindex == 8 && rs[0] ? data_a : + rindex == 8 ? data_b : + rindex == 9 && rs[0] ? wr13_a : + rindex == 9 ? wr13_b : + rindex == 10 && rs[0] ? rr10_a : + rindex == 10 ? rr10_b : + rindex == 11 && rs[0] ? rr15_a : + rindex == 11 ? rr15_b : + rindex == 12 && rs[0] ? wr12_a : + rindex == 12 ? wr12_b : + rindex == 13 && rs[0] ? wr13_a : + rindex == 13 ? wr13_b : + rindex == 14 && rs[0] ? rr10_a : + rindex == 14 ? rr10_b : + rindex == 15 && rs[0] ? rr15_a : + rindex == 15 ? rr15_b : 8'hff; + + /* RR0 */ + assign rr0_a = { 1'b0, /* Break */ + 1'b1, /* Tx Underrun/EOM */ + 1'b0, /* CTS */ + 1'b0, /* Sync/Hunt */ + wr15_a[3] ? dcd_latch_a : dcd_a, /* DCD */ + 1'b1, /* Tx Empty */ + 1'b0, /* Zero Count */ + 1'b0 /* Rx Available */ + }; + assign rr0_b = { 1'b0, /* Break */ + 1'b1, /* Tx Underrun/EOM */ + 1'b0, /* CTS */ + 1'b0, /* Sync/Hunt */ + wr15_b[3] ? dcd_latch_b : dcd_b, /* DCD */ + 1'b1, /* Tx Empty */ + 1'b0, /* Zero Count */ + 1'b0 /* Rx Available */ + }; + + /* RR1 */ + assign rr1_a = { 1'b0, /* End of frame */ + 1'b0, /* CRC/Framing error */ + 1'b0, /* Rx Overrun error */ + 1'b0, /* Parity error */ + 1'b0, /* Residue code 0 */ + 1'b1, /* Residue code 1 */ + 1'b1, /* Residue code 2 */ + 1'b1 /* All sent */ + }; + + assign rr1_b = { 1'b0, /* End of frame */ + 1'b0, /* CRC/Framing error */ + 1'b0, /* Rx Overrun error */ + 1'b0, /* Parity error */ + 1'b0, /* Residue code 0 */ + 1'b1, /* Residue code 1 */ + 1'b1, /* Residue code 2 */ + 1'b1 /* All sent */ + }; + + /* RR2 (Chan B only, A is just WR2) */ + assign rr2_b = { wr2[7], + wr9[4] ? rr2_vec_stat[0] : wr2[6], + wr9[4] ? rr2_vec_stat[1] : wr2[5], + wr9[4] ? rr2_vec_stat[2] : wr2[4], + wr9[4] ? wr2[3] : rr2_vec_stat[2], + wr9[4] ? wr2[2] : rr2_vec_stat[1], + wr9[4] ? wr2[1] : rr2_vec_stat[0], + wr2[0] + }; + + + /* RR3 (Chan A only) */ + assign rr3_a = { 2'b0, + rx_irq_pend_a, /* Rx interrupt pending */ + tx_irq_pend_a, /* Tx interrupt pending */ + ex_irq_pend_a, /* Status/Ext interrupt pending */ + rx_irq_pend_b, + tx_irq_pend_b, + ex_irq_pend_b + }; + + /* RR10 */ + assign rr10_a = { 1'b0, /* One clock missing */ + 1'b0, /* Two clocks missing */ + 1'b0, + 1'b0, /* Loop sending */ + 1'b0, + 1'b0, + 1'b0, /* On Loop */ + 1'b0 + }; + assign rr10_b = { 1'b0, /* One clock missing */ + 1'b0, /* Two clocks missing */ + 1'b0, + 1'b0, /* Loop sending */ + 1'b0, + 1'b0, + 1'b0, /* On Loop */ + 1'b0 + }; + + /* RR15 */ + assign rr15_a = { wr15_a[7], + wr15_a[6], + wr15_a[5], + wr15_a[4], + wr15_a[3], + 1'b0, + wr15_a[1], + 1'b0 + }; + + assign rr15_b = { wr15_b[7], + wr15_b[6], + wr15_b[5], + wr15_b[4], + wr15_b[3], + 1'b0, + wr15_b[1], + 1'b0 + }; + + /* Interrupts. Simplified for now + * + * Need to add latches. Tx irq is latched when buffer goes from full->empty, + * it's not a permanent state. For now keep it clear. Will have to fix that. + */ + assign rx_irq_pend_a = 0; + assign tx_irq_pend_a = 0 /*& wr1_a[1]*/; /* Tx always empty for now */ + assign ex_irq_pend_a = ex_irq_ip_a; + assign rx_irq_pend_b = 0; + assign tx_irq_pend_b = 0 /*& wr1_b[1]*/; /* Tx always empty for now */ + assign ex_irq_pend_b = ex_irq_ip_b; + + assign _irq = ~(wr9[3] & (rx_irq_pend_a | + rx_irq_pend_b | + tx_irq_pend_a | + tx_irq_pend_b | + ex_irq_pend_a | + ex_irq_pend_b)); + + /* XXX Verify that... also missing special receive condition */ + assign rr2_vec_stat = rx_irq_pend_a ? 3'b110 : + tx_irq_pend_a ? 3'b100 : + ex_irq_pend_a ? 3'b101 : + rx_irq_pend_b ? 3'b010 : + tx_irq_pend_b ? 3'b000 : + ex_irq_pend_b ? 3'b001 : 3'b011; + + /* External/Status interrupt & latch logic */ + assign do_extreset_a = wreg_a & (rindex == 0) & (wdata[5:3] == 3'b010); + assign do_extreset_b = wreg_b & (rindex == 0) & (wdata[5:3] == 3'b010); + + /* Internal IP bit set if latch different from source and + * corresponding interrupt is enabled in WR15 + */ + assign dcd_ip_a = (dcd_a != dcd_latch_a) & wr15_a[3]; + assign dcd_ip_b = (dcd_b != dcd_latch_b) & wr15_b[3]; + + /* Latches close when an enabled IP bit is set and latches + * are currently open + */ + assign do_latch_a = latch_open_a & (dcd_ip_a /* | cts... */); + assign do_latch_b = latch_open_b & (dcd_ip_b /* | cts... */); + + /* "Master" interrupt, set when latch close & WR1[0] is set */ + always@(posedge sysclk or posedge reset) begin + if (reset) + ex_irq_ip_a <= 0; + else if (do_extreset_a) + ex_irq_ip_a <= 0; + else if (do_latch_a && wr1_a[0]) + ex_irq_ip_a <= 1; + end + always@(posedge sysclk or posedge reset) begin + if (reset) + ex_irq_ip_b <= 0; + else if (do_extreset_b) + ex_irq_ip_b <= 0; + else if (do_latch_b && wr1_b[0]) + ex_irq_ip_b <= 1; + end + + /* Latch open/close control */ + always@(posedge sysclk or posedge reset) begin + if (reset) + latch_open_a <= 1; + else begin + if (do_extreset_a) + latch_open_a <= 1; + else if (do_latch_a) + latch_open_a <= 0; + end + end + always@(posedge sysclk or posedge reset) begin + if (reset) + latch_open_b <= 1; + else begin + if (do_extreset_b) + latch_open_b <= 1; + else if (do_latch_b) + latch_open_b <= 0; + end + end + + /* Latches proper */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + dcd_latch_a <= 0; + /* cts ... */ + end else begin + if (do_latch_a) + dcd_latch_a <= dcd_a; + /* cts ... */ + end + end + always@(posedge sysclk or posedge reset) begin + if (reset) begin + dcd_latch_b <= 0; + /* cts ... */ + end else begin + if (do_latch_b) + dcd_latch_b <= dcd_b; + /* cts ... */ + end + end + + /* NYI */ + assign txd = 1; + assign rts = 1; + + assign wreq = 0; +endmodule diff --git a/fpga/scope.v b/fpga/scope.v new file mode 100644 index 0000000..c219f90 --- /dev/null +++ b/fpga/scope.v @@ -0,0 +1,185 @@ +/* + * Internal signal capture module + */ + +`define SIGCAP_WIDTH 64 +`define SIGCAP_DEPTH 2048 +`define SIGCAP_ADDRBITS 16 + +/* Capture RAM module, should infer a block RAM in ISE + * + * Based on XST example "Dual-Port Block RAM with Different Clocks" + */ +module capram +( + input clka, + input clkb, + input wea, + input [`SIGCAP_ADDRBITS-1:0] addra, + input [`SIGCAP_ADDRBITS-1:0] addrb, + input [`SIGCAP_WIDTH-1:0] dia, + output [`SIGCAP_WIDTH-1:0] doa, + output [`SIGCAP_WIDTH-1:0] dob +); + reg [`SIGCAP_WIDTH-1:0] ram [`SIGCAP_DEPTH-1:0]; + reg [`SIGCAP_ADDRBITS-1:0] addr_rega; + reg [`SIGCAP_ADDRBITS-1:0] addr_regb; + + always @(posedge clka) begin + if (wea) + ram[addra] <= dia; + addr_rega <= addra; + end + always @(posedge clkb) begin + addr_regb <= addrb; + end + assign doa = ram[addr_rega]; + assign dob = ram[addr_regb]; +endmodule + +/* WO: Capture control register */ +`define SIGCAP_REG_CTRL 0 +`define SIGCAP_CTRL_RESET 0 /* 8'b00000001 */ +`define SIGCAP_CTRL_RUN 1 /* 8'b00000002 */ + +/* RO: Total sample count in buffer */ +`define SIGCAP_REG_COUNT_0 2 +`define SIGCAP_REG_COUNT_1 3 + +/* RW: Read next sample. Write reset read address */ +`define SIGCAP_REG_DATA 4 + +/* + * Note about clock domains: + * + * sysclk is used for the register interface, capclk for the actual + * capture. However, we don't use synchronizers when those domains + * collide, as we know that both clocks derive from the same DLL + * and shouldn't go metastable. Typically capclk is a multiple of + * sysclk + */ +module scope +( + /* System clock */ + input sysclk, + /* Capture clock */ + input capclk, + /* System reset */ + input reset, + /* Signals to capture */ + input [`SIGCAP_WIDTH-1:0] sigs, + + /* Backbus interface */ + input [5:0] bb_addr, + input [7:0] bb_wdata, + output [7:0] bb_rdata, + input bb_strobe, + input bb_wr + ); + reg capturing; + + reg [`SIGCAP_ADDRBITS-1:0] cap_addr; + reg [`SIGCAP_ADDRBITS-1:0] read_addr; + reg [2:0] read_byte; + + wire [`SIGCAP_WIDTH-1:0] cap_data; + reg [7:0] cap_byte; + wire [`SIGCAP_WIDTH-1:0] ram_ignore; + + reg do_reset; + reg do_run; + + wire cap_full; + wire do_reset_combo; + + + /* Instanciate capture memory */ + capram capram0(.clka(capclk), + .clkb(sysclk), + .wea(capturing && !cap_full), + .addra(cap_addr), + .addrb(read_addr), + .dia(sigs), + .doa(ram_ignore), + .dob(cap_data)); + + /* Capture control logic */ + always@(posedge capclk or posedge reset) begin + if (reset) begin + cap_addr <= 0; + end else begin + if (do_reset) begin + capturing <= 0; + cap_addr <= 0; + end else if (!do_run) begin + capturing <= 0; + end else begin + capturing <= 1; + if (capturing && !cap_full) begin + cap_addr <= cap_addr + 1; + end + end + end + end + assign cap_full = cap_addr == (`SIGCAP_DEPTH - 1); + + /* Mux memory output */ + always@(read_byte or cap_data) begin + case (read_byte) + 0: cap_byte = cap_data[63:56]; + 1: cap_byte = cap_data[55:48]; + 2: cap_byte = cap_data[47:40]; + 3: cap_byte = cap_data[39:32]; + 4: cap_byte = cap_data[31:24]; + 5: cap_byte = cap_data[23:16]; + 6: cap_byte = cap_data[15:8]; + 7: cap_byte = cap_data[7:0]; + default: cap_byte = cap_data[7:0]; + endcase + end + + /* Register read */ + assign bb_rdata = (bb_addr[2:0] == `SIGCAP_REG_COUNT_0) ? + cap_addr[`SIGCAP_ADDRBITS-1:8] : + (bb_addr[2:0] == `SIGCAP_REG_COUNT_1) ? cap_addr[7:0] : + cap_byte; + + /* Fifo address counter */ + assign do_reset_combo = reset | do_reset; + + always@(posedge sysclk or posedge do_reset_combo) begin + if (do_reset_combo) begin + read_addr <= 0; + read_byte <= 0; + end else begin + if (bb_strobe && bb_addr[2:0] == `SIGCAP_REG_DATA) begin + if (bb_wr) begin + read_byte <= 0; + read_addr <= 0; + end else begin + read_byte <= read_byte + 1; + if (read_byte == 3'b111) + read_addr <= read_addr + 1; + end + end + end + end + + /* Control register write interface */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + do_reset <= 1; + do_run <= 0; + end else begin + if (bb_strobe && bb_wr && bb_addr[2:0] == `SIGCAP_REG_CTRL) begin + do_reset <= bb_wdata[`SIGCAP_CTRL_RESET]; + do_run <= bb_wdata[`SIGCAP_CTRL_RUN]; + end + end + end +endmodule + + + + + \ No newline at end of file diff --git a/fpga/sim_bench.v b/fpga/sim_bench.v new file mode 100644 index 0000000..e632ba5 --- /dev/null +++ b/fpga/sim_bench.v @@ -0,0 +1,236 @@ +`timescale 1ns / 100ps + +module main(); + + /* Interface between FPGA and other siumulated components */ + + /* m68k interface */ + wire [15:0] cpu_data; + wire [23:1] cpu_address; + wire [2:0] _cpu_ipl; + wire _cpu_as; + wire _cpu_uds; + wire _cpu_lds; + wire cpu_r_w; + wire _cpu_dtack; + wire _cpu_reset; + wire cpu_clk; + + /* SRAM interface */ + wire [15:0] ram_data; + wire [19:1] ram_address; + wire [3:0] _ram_ce; + wire _ram_bhe; + wire _ram_ble; + wire _ram_we; + wire _ram_oe; + + /* RS232 pins */ + wire rxd; + wire txd; + wire cts; + wire rts; + + /* Joystick ports */ + wire [5:0] _joy1; + wire [5:0] _joy2; + + /* PS2 ports */ + wire msdat; + wire msclk; + wire kbddat; + wire kbdclk; + + /* Video pins */ + wire _hsync; + wire _vsync; + wire [3:0] red; + wire [3:0] green; + wire [3:0] blue; + + /* Audio pins */ + wire left; + wire right; + + /* System pins */ + wire mclk; /* Note: Ignored in sim */ + wire pwrled; + wire init_b; + wire _15khz; + wire gpio; + wire scsi_hshake; + wire nmi; + + /* SPI pins */ + wire _spi_cs; + wire sdi; + wire sdo; + wire sck; + + /* Unused CPU output pins */ + wire [2:0] cpu_fc; + wire _cpu_bg; + + /* Instanciate 4 SRAM modules */ + sim_sram512x16 ram0(.addr(ram_address), + .data(ram_data), + ._cs(_ram_ce[0]), + ._oe(_ram_oe), + ._we(_ram_we), + ._ub(_ram_bhe), + ._lb(_ram_ble)); + + sim_sram512x16 ram1(.addr(ram_address), + .data(ram_data), + ._cs(_ram_ce[1]), + ._oe(_ram_oe), + ._we(_ram_we), + ._ub(_ram_bhe), + ._lb(_ram_ble)); + + sim_sram512x16 ram2(.addr(ram_address), + .data(ram_data), + ._cs(_ram_ce[2]), + ._oe(_ram_oe), + ._we(_ram_we), + ._ub(_ram_bhe), + ._lb(_ram_ble)); + + sim_sram512x16 ram3(.addr(ram_address), + .data(ram_data), + ._cs(_ram_ce[3]), + ._oe(_ram_oe), + ._we(_ram_we), + ._ub(_ram_bhe), + ._lb(_ram_ble)); + + /* Instanciate CPU. Lotsa stuff not wired on Minimig */ + m68k cpu0(.clk(cpu_clk), + .fc(cpu_fc), + .a(cpu_address), + ._as(_cpu_as), + ._uds(_cpu_uds), + ._lds(_cpu_lds), + .r_w(cpu_r_w), + ._dtack(_cpu_dtack), + .d(cpu_data), + ._ipl(_cpu_ipl), + ._avec(1'b1), + ._br(1'b1), + ._bg(_cpu_bg), + ._berr(1'b1), + ._reset(_cpu_reset), + ._halt(1'b1)); + + pullup(_cpu_reset); + + defparam cpu.turbo = 1; +// defparam cpu.log_flags = 0; +// defparam cpu.log_flags = 'hff; + + /* Instanciate the FPGA */ + minimigmac minimigmac0(.cpu_data(cpu_data), + .cpu_address(cpu_address), + ._cpu_ipl(_cpu_ipl), + ._cpu_uds(_cpu_uds), + ._cpu_lds(_cpu_lds), + ._cpu_as(_cpu_as), + .cpu_r_w(cpu_r_w), + ._cpu_dtack(_cpu_dtack), + ._cpu_reset(_cpu_reset), + .cpu_clk(cpu_clk), + .ram_data(ram_data), + .ram_address(ram_address), + ._ram_ce(_ram_ce), + ._ram_bhe(_ram_bhe), + ._ram_ble(_ram_ble), + ._ram_we(_ram_we), + ._ram_oe(_ram_oe), + .rxd(rxd), + .txd(txd), + .cts(cts), + .rts(rts), + ._joy1(_joy1), + ._joy2(_joy2), + .msdat(msdat), + .msclk(msclk), + .kbddat(kbddat), + .kbdclk(kbdclk), + ._hsync(_hsync), + ._vsync(_vsync), + .red(red), + .green(green), + .blue(blue), + .left(left), + .right(right), + .mclk(mclk), + .pwrled(pwrled), + .init_b(init_b), + ._15khz(_15khz), + .gpio(gpio), + .nmi(nmi), + .scsi_hshake(scsi_hshake), + ._spi_cs(_spi_cs), + .sdi(sdi), + .sdo(sdo), + .sck(sck)); + + sim_pic pic0(.sdo(sdi), + .sdi(sdo), + .sck(sck), + ._spi_cs(_spi_cs), + .scsi_hshake(scsi_hshake), + .nmi(nmi)); + + sim_mouse mouse0(.clk(msclk), .dat(msdat)); + sim_kbd kbd0(.clk(kbdclk), .dat(kbddat)); + + integer rom_file; + integer rom_size; + + initial begin + $dumpfile("sim_bench.vcd"); + $dumpvars(0,minimigmac0); + $dumpvars(0,ram0); + $dumpvars(0,ram1); + $dumpvars(0,ram2); + $dumpvars(0,ram3); + $dumpvars(0,pic0); + $dumpvars(0,cpu0); + $dumpvars(0,mouse0); + + $display("Loading ROM..."); + rom_file = $fopenr("rom.bin"); + rom_size = $fread(ram2.mem, rom_file, 0); + $fclose(rom_file); + $display("Loaded 0x%h bytes", rom_size); + $display("ram[841]=%h", ram2.mem['h841]); + $display("Disabling mem test"); + ram0.mem['h2a2/2] = 'h0040; + ram0.mem['h2a4/2] = 'h0000; + $display("Disabling boot beep & clr screen etc..."); + ram2.mem['hf4/2] = 'h4e71; + ram2.mem['hf6/2] = 'h4e71; + ram2.mem['hea/2] = 'h4e71; + ram2.mem['hec/2] = 'h4e71; + ram2.mem['h34/2] = 'h4e71; + ram2.mem['h36/2] = 'h4e71; + ram2.mem['h38/2] = 'h4e71; + ram2.mem['h3a/2] = 'h4e71; + ram2.mem['h3c/2] = 'h4e71; + ram2.mem['h3e/2] = 'h4e71; + ram2.mem['h40/2] = 'h4e71; + /* checksum & test memory replaced with + * movea.l #memsize, a5 and beq->bra + */ + ram2.mem['h12a/2] = 'h2a7c; + ram2.mem['h12c/2] = 'h0020; + ram2.mem['h12e/2] = 'h0000; + ram2.mem['h130/2] = 'h4e71; + ram2.mem['h132/2] = 'h6000; + ram0.mem['h108/2] = 'h0020; /* memtop */ + ram0.mem['h10a/2] = 'h0000; + #50000000; + $finish; + end +endmodule \ No newline at end of file diff --git a/fpga/sim_clocks.v b/fpga/sim_clocks.v new file mode 100644 index 0000000..3fa0d32 --- /dev/null +++ b/fpga/sim_clocks.v @@ -0,0 +1,62 @@ +`timescale 1ns / 100ps + +/* + * Generate clocks for simulation + * + * We generate: + * + * - sysclk16 at 16.6666Mhz (60ns) + * - sysclk33 at 33.3333Mhz (30ns) + * + */ +module clockgen(input mclk, /* ignored */ + output reg sysclk16, + output reg sysclk33, + output reg pixclk); + + initial begin + sysclk16 = 0; + sysclk33 = 0; + pixclk = 0; + end + + always begin + #30 sysclk16 = ~sysclk16; + end + always begin + #15 sysclk33 = ~sysclk33; + end + always begin + #20 pixclk = ~pixclk; + end +endmodule + +/* The xilinx OFDDRCPE IO module is simulated here for iverilog */ +module OFDDRCPE +( + output Q, + input C0, + input C1, + input CE, + input CLR, + input D0, + input D1, + input PRE +); + reg c = 0; + + assign Q = c == 0 ? D0 : D1; + + always@(posedge C0 or posedge C1 or posedge CLR or posedge PRE) begin + if (CLR) + c = 0; + else if (PRE) + c = 1; + else if (CE) begin + if (C0) + c = 0; + else if (C1) + c = 1; + end + end +endmodule diff --git a/fpga/sim_kbd.v b/fpga/sim_kbd.v new file mode 100644 index 0000000..d36a77c --- /dev/null +++ b/fpga/sim_kbd.v @@ -0,0 +1,147 @@ +`timescale 1ns / 100ps + +module sim_kbd(inout clk, inout dat); +`define CLK_HALF 5000 /* faster sim, should be more like 50000 */ + + reg aclk; + reg adat; + reg [7:0] cmd; + reg wait_led; + + + task do_send; + input [7:0] d; + output ok; + reg [10:0] sr; + reg p; + integer i; + begin + $display("sim_kbd: Send %x", d); + + p = ~(d[0] ^ d[1] ^ d[2] ^ d[3] ^ + d[4] ^ d[5] ^ d[6] ^ d[7]); + sr = { 1'b1, p, d, 1'b0 }; + aclk = 0; + adat = 0; + ok = 1; + i = 11; + while(i > 0 && ok) begin + if (clk == 0) begin + $display("sim_kbd: Clock low, aborting send"); + ok = 0; + end else begin + adat = ~sr[0]; + #`CLK_HALF aclk = 1; + #`CLK_HALF aclk = 0; + #1; + sr = { 1'b0, sr[10:1] }; + i = i - 1; + end + end + aclk = 0; + adat = 0; + #`CLK_HALF; + #`CLK_HALF; + end + endtask + + task do_receive; + output [7:0] d; + output ok; + reg [10:0] sr; + reg p; + integer i; + begin + aclk = 0; + adat = 0; + sr = 0; + wait(dat == 0); + wait(clk != 0); + ok = 1; + i = 10; + while(i > 0 && ok) begin + if (clk == 0) begin + $display("sim_kbd: Clock low, aborting receive"); + ok = 0; + end else begin + #`CLK_HALF aclk = 1; + #`CLK_HALF aclk = 0; + #1; + sr = { dat != 0, sr[10:1] }; + i = i - 1; + end + end // while (i > 0 && ok) + adat = 1; + #`CLK_HALF aclk = 1; + #`CLK_HALF aclk = 0; + adat = 0; + // $display("rx shift final: %b", sr); + aclk = 0; + adat = 0; + d = sr[8:1]; + #`CLK_HALF; + #`CLK_HALF; + $display("sim_kbd: Got %x ok=%b", d, ok); + end + endtask + + pullup(clk); + pullup(dat); + + assign clk = aclk ? 1'b0 : 1'bz; + assign dat = adat ? 1'b0 : 1'bz; + + reg ok; + + always@(clk, dat) begin + while (clk == 0) begin + #`CLK_HALF; + if (clk == 0) begin + #`CLK_HALF; + if (clk == 0) begin + do_receive(cmd, ok); + if (ok) begin + if (wait_led) begin + do_send(8'hfa, ok); + $display("sim_kbd: LEDs set to %x\n", cmd); + end else begin + if (cmd == 8'hed) begin + wait_led = 1; + do_send(8'hfa, ok); + end else if (cmd == 8'hff) begin + if (ok) + do_send(8'hfa, ok); + if (ok) + do_send(8'haa, ok); + end + end + end + end + end + end + end + + initial begin + aclk = 0; + adat = 0; + wait_led = 0; + #`CLK_HALF; + #`CLK_HALF; + #`CLK_HALF; + #`CLK_HALF; + do_send(8'haa, ok); + #10000000; + do_send(8'h1c, ok); + #100000; + do_send(8'hf0, ok); + do_send(8'h1c, ok); + #1000000; + do_send(8'he0, ok); + do_send(8'h75, ok); + #100000; + do_send(8'he0, ok); + do_send(8'hf0, ok); + do_send(8'h75, ok); + end +endmodule + diff --git a/fpga/sim_mouse.v b/fpga/sim_mouse.v new file mode 100644 index 0000000..c2c8e03 --- /dev/null +++ b/fpga/sim_mouse.v @@ -0,0 +1,139 @@ +`timescale 1ns / 100ps + +module sim_mouse(inout clk, inout dat); +`define CLK_HALF 5000 /* faster sim, should be more like 50000 */ + + reg aclk; + reg adat; + reg [7:0] cmd; + + task do_send; + input [7:0] d; + output ok; + reg [10:0] sr; + reg p; + integer i; + begin + $display("sim_mouse: send %x", d); + + p = ~(d[0] ^ d[1] ^ d[2] ^ d[3] ^ + d[4] ^ d[5] ^ d[6] ^ d[7]); + sr = { 1'b1, p, d, 1'b0 }; + aclk = 0; + adat = 0; + ok = 1; + i = 11; + while(i > 0 && ok) begin + if (clk == 0) begin + $display("Mouse: Clock low, aborting send"); + ok = 0; + end else begin + adat = ~sr[0]; + #`CLK_HALF aclk = 1; + #`CLK_HALF aclk = 0; + #1; + sr = { 1'b0, sr[10:1] }; + i = i - 1; + end + end + aclk = 0; + adat = 0; + #`CLK_HALF; + #`CLK_HALF; + end + endtask + + task do_receive; + output [7:0] d; + output ok; + reg [10:0] sr; + reg p; + integer i; + begin + aclk = 0; + adat = 0; + sr = 0; + wait(dat == 0); + wait(clk != 0); + ok = 1; + i = 10; + while(i > 0 && ok) begin + if (clk == 0) begin + $display("Mouse: Clock low, aborting receive"); + ok = 0; + end else begin + #`CLK_HALF aclk = 1; + #`CLK_HALF aclk = 0; + #1; + sr = { dat != 0, sr[10:1] }; + i = i - 1; + end + end // while (i > 0 && ok) + adat = 1; + #`CLK_HALF aclk = 1; + #`CLK_HALF aclk = 0; + adat = 0; +// $display("rx shift final: %b", sr); + aclk = 0; + adat = 0; + d = sr[8:1]; + #`CLK_HALF; + #`CLK_HALF; + $display("sim_mouse: got %x ok=%b", d, ok); + end + endtask + + pullup(clk); + pullup(dat); + + assign clk = aclk ? 1'b0 : 1'bz; + assign dat = adat ? 1'b0 : 1'bz; + + reg ok; + + always@(clk, dat) begin + while (clk == 0) begin + #`CLK_HALF; + if (clk == 0) begin + #`CLK_HALF; + if (clk == 0) begin + do_receive(cmd, ok); + if (ok) begin + if (cmd == 8'hf4) begin + do_send(8'hfa, ok); + end else if (cmd == 8'hff) begin + if (ok) + do_send(8'haa, ok); + if (ok) + do_send(8'h00, ok); + end + end + end + end + end + end + + initial begin + aclk = 0; + adat = 0; + #`CLK_HALF; + #`CLK_HALF; + #`CLK_HALF; + #`CLK_HALF; + do_send(8'haa, ok); + do_send(8'h00, ok); + #6000000; + do_send(8'h00, ok); + do_send(8'h02, ok); + do_send(8'h00, ok); + #1000000; + do_send(8'h00, ok); + do_send(8'h02, ok); + do_send(8'h00, ok); + #1000000; + do_send(8'h00, ok); + do_send(8'h00, ok); + do_send(8'h02, ok); + end +endmodule + diff --git a/fpga/sim_pic.v b/fpga/sim_pic.v new file mode 100644 index 0000000..de82055 --- /dev/null +++ b/fpga/sim_pic.v @@ -0,0 +1,746 @@ +`timescale 1ns / 100ps + +`define ROM_MAX 'h20000 + +`define BB_WRITE 8'h80 +`define BB_AUTOINC 8'h40 + +`define BB_REG_CTRL_BASE 6'h00 +`define BB_REG_CPU_BASE 6'h10 +`define BB_REG_MEM_BASE 6'h20 +`define BB_REG_IWM_BASE 6'h28 +`define BB_REG_SCSI_BASE 6'h30 +`define BB_REG_SCOPE_BASE 6'h38 + +/* SCSI Backbus interface */ +`define BB_REG_LINES 0 +`define BB_LINES_BSY 7 +`define BB_LINES_SEL 6 +`define BB_LINES_ACK 5 +`define BB_LINES_ATN 4 +`define BB_LINES_RST 3 + +`define BB_REG_ASSERT 1 +`define BB_ASSERT_BSY 7 +`define BB_ASSERT_SEL 6 +`define BB_ASSERT_CD 5 +`define BB_ASSERT_IO 4 +`define BB_ASSERT_MSG 3 +`define BB_ASSERT_REQ 2 +`define BB_ASSERT_RST 1 +`define BB_ASSERT_AUTO 0 /* Automatic mode */ + +`define BB_REG_ODATA 2 +`define BB_REG_IDATA 3 +`define BB_REG_ACNT_HI 4 +`define BB_REG_ACNT_LO 5 + +module sim_pic +( + input sdi, + output reg sdo, + output reg sck, + output reg _spi_cs, + input scsi_hshake, + output reg nmi); + integer rom_file; + integer rom_size; + reg [7:0] rom[0:`ROM_MAX]; + reg [7:0] scsi_asserts; + reg [7:0] scsi_cdb[0:11]; + reg [7:0] scsi_buf[0:2047]; + integer dbg_foo; + + /* SPI half clock period */ + parameter SPI_HCLOCK = 40; + /* Setup time of data vs rising clock edge */ + parameter SPI_HEAD = 4; + + task do_spi; + input [7:0] out_byte; /* Out to FPGA */ + output reg [7:0] in_byte; /* In from FPGA */ + reg [7:0] in_sr; + reg [7:0] out_sr; + begin + in_sr = 8'hff; + out_sr = out_byte; + repeat(8) begin + sck = 0; + #(SPI_HCLOCK-SPI_HEAD); + sdo = out_sr[7]; + out_sr = { out_sr[6:0], 1'b0 }; + in_sr = { in_sr[6:0], sdi }; + #SPI_HEAD sck = 1; + #SPI_HCLOCK; + end + sck = 0; + in_byte = in_sr; + end + endtask // do_spi + + task run_to; + input [23:0] addr; + reg [7:0] stat; + begin + /* Set breakpoint */ + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_AUTOINC | `BB_REG_CPU_BASE | 8, val); + do_spi(addr[23:16], val); + do_spi(addr[15:8], val); + do_spi(addr[7:0], val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + + /* Run CPU interface */ + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val); + do_spi(8'h42, val); /* RUN + BKEN */ + _spi_cs = 1; + #(SPI_HCLOCK*4); + + stat = 0; + while(!stat[3]) begin + /* Get CPU status */ + _spi_cs = 0; + do_spi(`BB_REG_CPU_BASE | 7, val); + do_spi(8'hff, val); + do_spi(8'hff, stat); + _spi_cs = 1; +// $display("stat: %h", stat); +// #(SPI_HCLOCK*40); + end + end + endtask // run_to + + task scsi_set_lines; + input [7:0] lines; + begin + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_REG_SCSI_BASE | `BB_REG_ASSERT, val); + do_spi(lines, val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + end + endtask + + task scsi_set; + input integer line; + begin + scsi_asserts[line] = 1'b1; + scsi_set_lines(scsi_asserts); + end + endtask + + task scsi_clear; + input integer line; + begin + scsi_asserts[line] = 1'b0; + scsi_set_lines(scsi_asserts); + end + endtask + + task scsi_set_autocnt; + input integer cnt; + begin + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_AUTOINC | + `BB_REG_SCSI_BASE | `BB_REG_ACNT_HI, val); + do_spi(cnt[15:8], val); + do_spi(cnt[7:0], val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + end + endtask + + task scsi_set_data; + input [7:0] data; + begin + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_REG_SCSI_BASE | `BB_REG_IDATA, val); + do_spi(data, val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + end + endtask + + task scsi_get_data; + output [7:0] val; + reg [7:0] dummy; + begin + _spi_cs = 0; + do_spi(`BB_REG_SCSI_BASE | `BB_REG_ODATA, dummy); + do_spi(8'hff, dummy); + do_spi(8'hff, val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + end + endtask + + task scsi_get_lines; + output [7:0] val; + reg [7:0] dummy; + begin + _spi_cs = 0; + do_spi(`BB_REG_SCSI_BASE | `BB_REG_LINES, dummy); + do_spi(8'hff, dummy); + do_spi(8'hff, val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + end + endtask + + task scsi_do_cmd; + output integer len; + output [7:0] stat; + output direction; + integer i; + + begin + stat = 0; + len = 0; + direction = 0; + case(scsi_cdb[0]) + 'h12: begin /* INQUIRY */ + direction = 1; + for (i=0; i < 256; i = i + 1) + scsi_buf[i] = 0; + scsi_buf[4] = 32; + scsi_buf[8] = 'h46; + scsi_buf[9] = 'h4f; + scsi_buf[10] = 'h4f; + scsi_buf[16] = 'h42; + scsi_buf[17] = 'h41; + scsi_buf[18] = 'h52; + len = scsi_cdb[4]; + $display("CMD: INQUIRY 0x%h bytes", len); + end + 'h08: begin /* READ6 */ + direction = 1; + len = 512 * scsi_cdb[4]; + if (len == 0) + len = 512 * 256; + $display("CMD: READ6 0x%h bytes", len); + end + 'h0a: begin /* WRITE6 */ + direction = 0; + len = 512 * scsi_cdb[4]; + if (len == 0) + len = 512 * 256; + $display("CMD: WRITE6 0x%h bytes", len); + end + 'h28: begin /* READ10 */ + direction = 1; + len = 512 * (scsi_cdb[7] * 256 + scsi_cdb[8]); + $display("CMD: READ10 0x%h bytes", len); + end + 'h2a: begin /* WRITE10 */ + direction = 0; + len = 512 * (scsi_cdb[7] * 256 + scsi_cdb[8]); + $display("CMD: WRITE10 0x%h bytes", len); + end + default: begin + stat = 1; /* CHECK CONDITION */ + end + endcase + end + endtask // scsi_init_cmd + + task do_scsi; + reg [7:0] lines; + reg [7:0] data; + integer len, llen; + reg [7:0] stat; + reg direction; + + begin + scsi_get_lines(lines); + $display("Waiting selection... Lines: %h", lines); + while (!lines[`BB_LINES_SEL] || lines[`BB_LINES_BSY]) + scsi_get_lines(lines); + scsi_get_data(data); + $display("Select ID: %h lines %h", data, lines); + stat = 0; + if (!data[6]) + $display("Wrong ID !"); + else begin + $display("Setting BSY"); + scsi_set(`BB_ASSERT_BSY); + $display("Waiting !SEL..."); + while(lines[`BB_LINES_SEL]) + scsi_get_lines(lines); + $display("Setting CMD, starting cmd transfer"); + scsi_set(`BB_ASSERT_CD); + len = 1; + i = 0; + stat = 0; + while(len) begin + scsi_set(`BB_ASSERT_REQ); + while(!lines[`BB_LINES_ACK]) + scsi_get_lines(lines); + scsi_get_data(data); + $display("CMD %h = %h", i, data); + scsi_cdb[i] = data; + len = len - 1; + i = i + 1; + if (i == 1) begin + /* get command size */ + case (data) + 'h12: len = 6 - 1; + 'h08: len = 6 - 1; + 'h0a: len = 6 - 1; + 'h28: len = 10 - 1; + 'h2a: len = 10 - 1; + default: begin + len = 0; + stat = 1; + end + endcase + $display("CMD size adjust %h", len); + end + scsi_clear(`BB_ASSERT_REQ); + while(lines[`BB_LINES_ACK]) + scsi_get_lines(lines); + end + + scsi_do_cmd(len, stat, direction); + scsi_clear(`BB_ASSERT_CD); + if (direction) + scsi_set(`BB_ASSERT_IO); + i = 0; + while (len) begin + scsi_set(`BB_ASSERT_AUTO); + /* Break up transfer */ + if (len > 256) + llen = 256; + else + llen = len; + len = len - llen; + $display("Chunk of %h, remaining %h...", llen, len); + scsi_set_autocnt(llen); + _spi_cs = 0; + if (direction) begin + do_spi(`BB_WRITE | `BB_REG_SCSI_BASE | `BB_REG_IDATA, val); + while(llen != 0) begin + $display("Writing byte %h", i); + do_spi(scsi_buf[i], val); + wait (!scsi_hshake); + wait (scsi_hshake); + llen = llen - 1; + i = i + 1; + + end + end else begin + do_spi(`BB_REG_SCSI_BASE | `BB_REG_ODATA, val); + #160; + wait (scsi_hshake); + do_spi(8'hff, val); + wait (!scsi_hshake); + $display("dummy1: %h", val); + wait (scsi_hshake); + do_spi(8'hff, val); + wait (!scsi_hshake); + $display("dummy2: %h", val); + wait (scsi_hshake); + do_spi(8'hff, val); + wait (!scsi_hshake); + $display("dummy3: %h", val); + while(llen != 0) begin + wait (scsi_hshake) ; + do_spi(8'hff, data); + if (llen > 3) + wait (!scsi_hshake); + scsi_buf[i] = data; + $display("DATA %h: %h", i, data); + llen = llen - 1; + i = i + 1; + end + end + _spi_cs = 1; + #(SPI_HCLOCK*4); + scsi_clear(`BB_ASSERT_AUTO); + end + $display("sending status %h", stat); + scsi_set(`BB_ASSERT_CD); + scsi_set(`BB_ASSERT_IO); + scsi_set_data(stat); + scsi_set(`BB_ASSERT_REQ); + while(!lines[`BB_LINES_ACK]) + scsi_get_lines(lines); + scsi_clear(`BB_ASSERT_REQ); + while(lines[`BB_LINES_ACK]) + scsi_get_lines(lines); + scsi_set(`BB_ASSERT_MSG); + $display("Sending message"); + scsi_set_data(0); + scsi_set(`BB_ASSERT_REQ); + while(!lines[`BB_LINES_ACK]) + scsi_get_lines(lines); + scsi_clear(`BB_ASSERT_REQ); + while(lines[`BB_LINES_ACK]) + scsi_get_lines(lines); + $display("Clearing phase & BSY"); + scsi_clear(`BB_ASSERT_CD); + scsi_clear(`BB_ASSERT_IO); + scsi_clear(`BB_ASSERT_MSG); + scsi_clear(`BB_ASSERT_BSY); + end // else: !if(!data[6]) + end + endtask // do_scsi + + task run_scsi; + begin + while(1) begin + wait (scsi_hshake) + do_scsi(); + end + end + endtask // run_scsi + + task dump_latches; + reg [7:0] sval[0:7]; + begin + /* Read latches */ + _spi_cs = 0; + do_spi(`BB_AUTOINC | `BB_REG_CPU_BASE, val); + do_spi(8'hff, val); + do_spi(8'hff, sval[0]); + do_spi(8'hff, sval[1]); + do_spi(8'hff, sval[2]); + do_spi(8'hff, sval[3]); + do_spi(8'hff, sval[4]); + do_spi(8'hff, sval[5]); + _spi_cs = 1; + #(SPI_HCLOCK*4); + $display("RW:%b U:%b L:%b ADDR: %h DATA: %h", + sval[5][0], sval[5][1], sval[5][2], + { sval[0], sval[1], sval[2] }, + { sval[3], sval[4] }); + end + endtask // dump_latches + + task step; + input integer count; + begin + repeat(count) begin + /* Step CPU interface */ + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val); + do_spi(8'h82, val); /* RUN + STEP */ + _spi_cs = 1; + #(SPI_HCLOCK*4); + + /* Should wait for completion... not useful + * in practice as SPI is too slow + */ + end // repeat (count) + end + endtask // step + + + reg [7:0] val; +// reg [7:0] scopedump[0:(2048 * 8)-1]; + integer i; + + initial begin + nmi = 0; + _spi_cs = 1; + sdo = 1'bz; + sck = 0; + scsi_asserts = 0; + + #100 _spi_cs = 0; + do_spi(`BB_AUTOINC | `BB_REG_CTRL_BASE, val); + do_spi(8'hff, val); + do_spi(8'hff, val); + $display("FPGA ID = 0x%h", val); + do_spi(8'hff, val); + $display("FPGA Version = 0x%h", val); + do_spi(8'hff, val); + $display("FPGA Control = 0x%h", val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + + /* Un-reset CPU */ + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_REG_CTRL_BASE | 2, val); + do_spi(8'h01, val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + +`ifdef foo + $display("Running to puts..."); + run_to('h40326c); + dump_latches(); + repeat(1000) begin + step(1); + dump_latches(); + end +`endif + + $display("Run...."); + /* Run CPU interface */ + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val); + do_spi(8'h02, val); /* RUN */ + _spi_cs = 1; + #(SPI_HCLOCK*4); + run_scsi(); +`ifdef foo + $display("Running to iwm_test1..."); + run_to('h4010f0); + dump_latches(); + repeat(100) begin + step(1); + dump_latches(); + end +`endif + +`ifdef foo + $display("Running to P_mBootBeep..."); + run_to('h40028a); + dump_latches(); + step(1); + dump_latches(); + $display("Running to IWM init..."); + run_to('h4000fc); + dump_latches(); + step(1); + dump_latches(); + $display("Running to P_ChecksumRomAndTestMemory..."); + run_to('h400d76); + dump_latches(); + step(1); + dump_latches(); + $display("Running to P_BootPart2 (fake hit)..."); + run_to('h400352); + dump_latches(); + step(1); + dump_latches(); + $display("Running to P_BootPart2..."); + run_to('h400352); + dump_latches(); + step(1); + dump_latches(); + + repeat(1000) begin + step(1); + dump_latches(); + end +`endif + +`ifdef __disabled__ + /* Write memory from 0x201000 using mem SPI (ROM) */ + $display("Loading ROM..."); + rom_file = $fopenr("rom.bin"); + rom_size = $fread(rom, rom_file, 0); + $fclose(rom_file); + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_AUTOINC | `BB_REG_MEM_BSAE, val); + do_spi(8'h20, val); /* ROM base */ + do_spi(8'h00, val); + do_spi(8'h00, val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_REG_MEM_BASE | 3, val);/ + i = 0; + repeat(rom_size) begin + do_spi(rom[i], val); + i = i+1; + if ((i % 1024) == 0) + $display("%h", i); + end + _spi_cs = 1; + #(SPI_HCLOCK*4); + + $display("RAM dump..."); + $display("%h %h %h %h %h %h %h %h", + sval[0], sval[1], sval[2], sval[3], + sval[4], sval[5], sval[6], sval[7]); + + /* Start the scope */ + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_REG_SCOPE_BASE, val); + do_spi(8'h02, val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + + /* Un-reset CPU */ + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_REG_CTRL_BASE | 2, val); + do_spi(8'h01, val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + + /* Set breakpoint */ + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_AUTOINC | `BB_REG_CPU_BASE | 8, val); + do_spi(8'h00, val); + do_spi(8'h10, val); + do_spi(8'h22, val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + + /* Run CPU interface */ + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val); + do_spi(8'h42, val); /* RUN + BKEN */ + _spi_cs = 1; + #(SPI_HCLOCK*4); + + repeat(10) begin + /* Get CPU status */ + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 7, val); + do_spi(8'hff, val); + do_spi(8'hff, val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + $display("CPU Stat: %h", val); + end +`endif + +`ifdef foo + $display("Steps:"); + repeat(100) begin + /* Step CPU interface */ + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val); + do_spi(8'h82, val); /* RUN + STEP */ + _spi_cs = 1; + #(SPI_HCLOCK*4); + + /* We shall wait for cycle to complete but we know + * how slow our SPI is ... + */ + /* Read latches */ + _spi_cs = 0; + do_spi(`BB_AUTOINC | `BB_REG_CPU_BASE, val); + do_spi(8'hff, val); + do_spi(8'hff, sval[0]); + do_spi(8'hff, sval[1]); + do_spi(8'hff, sval[2]); + do_spi(8'hff, sval[3]); + do_spi(8'hff, sval[4]); + do_spi(8'hff, sval[5]); + _spi_cs = 1; + #(SPI_HCLOCK*4); + $display("RW:%b U:%b L:%b ADDR: %h DATA: %h", + sval[5][0], sval[5][1], sval[5][2], + { sval[0], sval[1], sval[2] }, + { sval[3], sval[4] }); + end +`endif + +`ifdef foo + + $display("Reading 0x1020 via SPI initiaed read cycle:"); + + /* Read value at 0x1020 */ + _spi_cs = 0; + do_spi(8'hc8, val); /* b001_000 */ + do_spi(8'h00, val); + do_spi(8'h10, val); + do_spi(8'h20, val); + do_spi(8'haa, val); + do_spi(8'h55, val); + do_spi(8'h01, val); /* _uds = 0, _lds = 0, r_w = 1 */ + do_spi(8'h04, val); /* ctrl: cycle */ + _spi_cs = 1; + #(SPI_HCLOCK*4); + + /* Here too, shall wait but heh ! */ + + /* Read latches */ + _spi_cs = 0; + do_spi(8'h48, val); /* b001_000 */ + do_spi(8'hff, val); + do_spi(8'hff, sval[0]); + do_spi(8'hff, sval[1]); + do_spi(8'hff, sval[2]); + do_spi(8'hff, sval[3]); + do_spi(8'hff, sval[4]); + do_spi(8'hff, sval[5]); + _spi_cs = 1; + #(SPI_HCLOCK*4); + $display("RW:%b U:%b L:%b ADDR: %h DATA: %h", + sval[5][0], sval[5][1], sval[5][2], + { sval[0], sval[1], sval[2] }, + { sval[3], sval[4] }); + + /* Read memory from 0x1000 using mem SPI */ + _spi_cs = 0; + do_spi(8'he0, val); /* b100_000 */ + do_spi(8'h00, val); + do_spi(8'h10, val); + do_spi(8'h00, val); + _spi_cs = 1; + #(SPI_HCLOCK*4); + _spi_cs = 0; + do_spi(8'h23, val); /* b100_000 */ + do_spi(8'hff, val); + do_spi(8'hff, val); /* add. dummy cycle for reads */ + do_spi(8'hff, sval[0]); + do_spi(8'hff, sval[1]); + do_spi(8'hff, sval[2]); + do_spi(8'hff, sval[3]); + do_spi(8'hff, sval[4]); + do_spi(8'hff, sval[5]); + do_spi(8'hff, sval[6]); + do_spi(8'hff, sval[7]); + _spi_cs = 1; + #(SPI_HCLOCK*4); + + $display("RAM dump..."); + $display("%h %h %h %h %h %h %h %h", + sval[0], sval[1], sval[2], sval[3], + sval[4], sval[5], sval[6], sval[7]); + +`endif + +`ifdef foo + $display("Run...."); + + /* Run CPU interface */ + _spi_cs = 0; + do_spi(8'h8e, val); /* b001_110 */ + do_spi(8'h02, val); /* RUN */ + _spi_cs = 1; + #(SPI_HCLOCK*4); +`endif + /* Wait a bit and stop the scope */ + #4000000; + +`ifdef foo + _spi_cs = 0; + do_spi(`BB_WRITE | `BB_REG_SCOPE_BASE, val); + do_spi(8'h00, val); + _spi_cs = 1; + #(SPI_HCLOCK*4); +`endif + +`ifdef foo + /* Dump scope output */ + _spi_cs = 0; + do_spi(8'h3c, val); + do_spi(8'hff, val); + i = 0; + repeat(2048) begin + do_spi(8'hff, sval[0]); + do_spi(8'hff, sval[1]); + do_spi(8'hff, sval[2]); + do_spi(8'hff, sval[3]); + do_spi(8'hff, sval[4]); + do_spi(8'hff, sval[5]); + do_spi(8'hff, sval[6]); + do_spi(8'hff, sval[7]); + $display("%h %h %h %h %h %h %h %h", + sval[0], sval[1], sval[2], sval[3], + sval[4], sval[5], sval[6], sval[7]); + end + _spi_cs = 1; + #(SPI_HCLOCK*4); +`endif + end + +endmodule diff --git a/fpga/sim_sram.v b/fpga/sim_sram.v new file mode 100644 index 0000000..377c56f --- /dev/null +++ b/fpga/sim_sram.v @@ -0,0 +1,72 @@ +/* Behavioural of our SRAM chips */ +module sim_sram512x16(input [18:0] addr, + input _cs, + input _oe, + input _we, + input _ub, + input _lb, + inout [15:0] data); + + + reg sel; + reg [15:0] mem[0:'h7ffff]; + reg [15:0] dlatch; + wire [15:0] dout; + + /* Simplified model, roughly based on a IS64LV6416L model adapted + * to look like our AS6C8016 + */ + parameter Taa = 55; + parameter Toh = 10; + parameter Tchz = 20; + parameter Tas = 0; + parameter Twhz = 20; + + wire r_en = _we & (~_cs) & (~_oe); + wire w_en = (~_we) & (~_cs) & ((~_ub) | (~_lb)); + assign #(r_en ? Taa : Tchz) data = r_en ? dout : 16'bz; + assign dout[ 7:0] = _lb ? 8'bz : mem[addr][ 7:0]; + assign dout[15:8] = _ub ? 8'bz : mem[addr][15:8]; + + always@(addr or w_en) begin + #Tas + if (w_en) + #Twhz begin + /* XXX Something's wrong with iverilog. It appears that + * the if (w_en) above isn't enough, if I don't also test + * w_en in the assignment belwow, it -will- write spuriously + * to the RAMs even when w_en has never kicked for a while + * and I really have no idea why. --BenH + */ + mem[addr][ 7:0] = _lb | ~w_en ?mem[addr][ 7:0] : data[ 7:0]; + mem[addr][15:8] = _ub | ~w_en ?mem[addr][15:8] : data[15:8]; + end + end + +`ifdef foo + initial begin + + $display("initializing RAM..."); + mem[0] = 0; + mem[1] = 'h2000 - 16; + mem[2] = 0; + mem[3] = 'h1000; + // 1000: 303c 000a movew #10,%d0 + mem['h1000/2] = 'h303c; + mem['h1002/2] = 'h000a; + // 1004: 41fa 001a lea %pc@(1020 <_buf>),%a0 + mem['h1004/2] = 'h41fa; + mem['h1006/2] = 'h001a; + // 1008: 3080 movew %d0,%a0@ + mem['h1008/2] = 'h3080; + // 100a: 0640 0001 addiw #1,%d0 + mem['h100a/2] = 'h0640; + mem['h100c/2] = 'h0001; + // 100e: 6000 fff4 braw 1004 + mem['h100e/2] = 'h6000; + mem['h1010/2] = 'hfff4; + $display("done"); + end // initial begin +`endif + +endmodule diff --git a/fpga/spi_backbus.v b/fpga/spi_backbus.v new file mode 100644 index 0000000..9597474 --- /dev/null +++ b/fpga/spi_backbus.v @@ -0,0 +1,110 @@ +`timescale 1ns / 100ps + +/* SPI to backbus interface for minimigmac + * + * This layers on top of the spi_slave module. It provides a protocol + * which allows to read and write from of 8-bit wide, 6 addr bit bus + * which is connected to various modules in the system. + * + * The basic SPI protocol starts with one byte received by the FPGA, + * which is has the format + * + * RW : Read (0) / Write (1) + * AI : Auto-increment + * ADDR : Register address + * + * For writes, all subsequent bytes are directly relevant. For reads, + * the next byte after the read command shall be ignored, then subsequent + * bytes are relevant. + * + * The backbus side protocol is: + * + * - Writes: at clock with strobe high and wr high, data and address + * are stable and shall be latched. + * - Reads : at clock with strobe high and wr low, address stable, + * we latch data immediately. + */ + +module spi_backbus +( + /* Clock & Reset */ + input sysclk, + input reset, + + /* SPI signals (through to the SPI slave core) */ + input sdi, + output sdo, + input sck, + input _scs, + + /* Backbus interface */ + output reg [5:0] bb_addr, + output [7:0] bb_wdata, + input [7:0] bb_rdata, + output reg bb_strobe, + output reg bb_wr + ); + + /* SPI slave wires */ + wire spi_rx; + wire spi_cmd; + + /* Protocol internals */ + reg ainc; + reg delayed_ainc; + reg write; + + /* Data read latch */ + reg [7:0] data; + + /* Instanciate SPI slave interface */ + spi_slave spi0(.sysclk(sysclk), + .reset(reset), + ._scs(_scs), + .sdi(sdi), + .sdo(sdo), + .sck(sck), + .wdata(data), + .rdata(bb_wdata), + .rx(spi_rx), + .cmd(spi_cmd)); + + /* Data latch */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + data <= 0; + end else begin + /* Latch read data at read strobe */ + if (bb_strobe && !bb_wr) + data <= bb_rdata; + end + end + + /* One state machine to rule them all coz I'm lazy */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + bb_addr <= 0; + end else begin + /* Command byte. Establish addr, wr and set strobe on read */ + if (spi_rx && spi_cmd) begin + bb_addr <= bb_wdata[5:0]; + delayed_ainc <= bb_wdata[6]; + ainc <= bb_wdata[7] ? 0 : bb_wdata[6]; + bb_wr <= bb_wdata[7]; + bb_strobe <= ~bb_wdata[7]; + end else if (spi_rx) begin + /* Subsequent bytes */ + ainc <= delayed_ainc; + if (ainc) begin + bb_addr <= bb_addr + 1; + end + /* New strobe */ + bb_strobe <= 1; + end else begin + bb_strobe <= 0; + end + end + end +endmodule + + \ No newline at end of file diff --git a/fpga/spi_slave.v b/fpga/spi_slave.v new file mode 100644 index 0000000..9f91725 --- /dev/null +++ b/fpga/spi_slave.v @@ -0,0 +1,159 @@ +`timescale 1ns / 100ps + +/* SPI slave module + * + * Expected configuration on the master is SPI mode 0 + * (CPOL 0 CPHA 0) + * - SCK idle state is 0 + * - We latch input on positive edges of SCK + * - We setup output on negative edge of SCK + * + * IE. For a PIC, this means setting: + * CKP=0 CKE=1 SMP=0 + */ +module spi_slave +( + input sysclk, + input reset, /* Async system reset */ + input _scs, + input sdi, + output sdo, + input sck, + input [7:0] wdata, /* Output data */ + output reg [7:0] rdata, /* Input data */ + output rx, /* One-sysclk signal of a new byte */ + output reg cmd /* Indicate first byte of rx, variable len */ +); + + reg [6:0] in_sr; + reg [7:0] out_sr; + reg [2:0] cnt; + reg first; + reg new_byte; + reg rx1; + reg rx2; + reg rx3; + reg tx; + + /* Bit counter */ + always@(posedge sck or posedge _scs) begin + if (_scs) begin + cnt <= 0; + end else begin + cnt <= cnt + 1; + end + end + + /* Shift input, latch on positive edge of sck. We only shift in 7 + * bits as we'll take sdi directly into the final latch. + */ + always@(posedge sck) begin + if (!_scs) begin + in_sr[6:0] <= { in_sr[5:0], sdi }; + end + end + + /* We latch the input byte so it remains stable for a while byte for + * consumption by sysclk domain. + */ + always@(posedge sck) begin + if (!_scs && cnt == 3'b111) begin + rdata[7:0] <= { in_sr[6:0], sdi }; + end + end + + /* Shift output. We latch "data" on bit 7 falling edge (so before we + * set new_byte and thus before rx catches up sysclk domain). + * + * That means we are ahead by one bit, so we add a one bit delay latch + * on sdo + * + */ + always@(negedge sck or posedge _scs) begin + if (_scs) begin + out_sr <= 8'hff; + end else begin + if (cnt == 3'b111) begin + out_sr <= wdata; + end else begin + out_sr[7:0] <= { out_sr[6:0], 1'b0 }; + end + end + end + + always@(negedge sck or posedge _scs) begin + if (_scs) begin + tx <= 1; + end else begin + tx <= out_sr[7]; + end + end + + /* We keep output hi-z if chip select not set. We do have a transcient + * undefined state between _scs assertion and the first clock, which + * I'm happy to ignore for now + */ + assign sdo = _scs ? 1'bz : tx; + + /* Now we need to generate rx. It's tricky because sck can/will stop + * immediately after the last bit. + * + * What we do is we generate a signal "new_byte" at the same time as + * latching the input byte, which we clear half way through the next + * byte. We then double flip-flop synchronize that into sysclk domain + * where we do an edge detection. If we lose sck, this signal will + * remains set until we start a new transmission, but since the delay + * between two transmissions is unpredictable, we must make sure we + * don't clear it until half way through the new byte (ie, we keep it + * set even when SCS is gone). For the same reason we don't + * clear our input latch either when SCS is gone. + * + * Now, the nasty thing is we need to reset that dude asynchronously + * from sysclk domain, which could probably use some sychronisation + * too. For now, we assume the sysclk reset happens long enough before + * sck toggles. This will do for us, but in a more complex system, + * some logic might be needed to ensure sck is effectively masked + * out until a few clocks after reset completes to avoid metastability + * + * Note: FPGAs are nice, we could probably just rely on new_byte being + * 0 at powerup time :-) + */ + always@(posedge sck or posedge reset) begin + if (reset) begin + new_byte <= 0; + end else begin + if (cnt == 3'b111) begin + new_byte <= 1; + end else if (cnt == 3'b011) begin + new_byte <= 0; + end + end + end + + always@(posedge sysclk or posedge reset) begin + if (reset) begin + rx1 <= 0; + rx2 <= 0; + rx3 <= 0; + end else begin + rx1 <= new_byte; + rx2 <= rx1; + rx3 <= rx2; + end + end + + assign rx = rx2 & !rx3; + + /* "first" is set in sck domain during clocking of first byte, + * and flushed into "cmd" at bit 7, thus cmd moves along with + * the data itself and will be sampled with the data by the user. + */ + always@(posedge sck or posedge _scs) begin + if (_scs) begin + first <= 1; + end else if (cnt == 3'b111) begin + first <= 0; + cmd <= first; + end + end +endmodule diff --git a/fpga/via6522.v b/fpga/via6522.v new file mode 100644 index 0000000..dfc54ef --- /dev/null +++ b/fpga/via6522.v @@ -0,0 +1,559 @@ +`timescale 1ns / 100ps + +/* + * VIA6522 module for minimigmac. + * + * The processor interface part of the VIA modified to use my + * home made bus interface. The rest is hugely simplified to + * only operate in the modes used by a Macintosh. The shift + * register is not implemented as such, instead, we have a + * direct 8 bit bus to the keyboard interface. + * + * My understanding of the original Mac design is that the VIA + * two phase clock is fed from the original m68k's E line, thus + * ticks at 1/10 of the 7.82 Mhz CPU frequency. + * + * In this variant, the register interface is clocked at the full + * internal system clock, and we generate artificially a 782 kHz + * enable signal from it to feed the timers. We don't aim for + * great precision here, so for now we simply divide 16.666 Mhz + * by 21 which gives us an approximate 793kHz. + * + * In addition, to simplify the logic, the direction of the + * port A and B bits is hard wired and the reset values adapted + * to enable the ROM overlay at startup. + * + */ + +/* Register numbers */ +`define VIA_REG_ORB 4'h0 +`define VIA_REG_ORA 4'h1 +`define VIA_REG_DDRB 4'h2 +`define VIA_REG_DDRA 4'h3 +`define VIA_REG_T1CL 4'h4 +`define VIA_REG_T1CH 4'h5 +`define VIA_REG_T1LL 4'h6 +`define VIA_REG_T1LH 4'h7 +`define VIA_REG_T2CL 4'h8 +`define VIA_REG_T2CH 4'h9 +`define VIA_REG_SR 4'ha +`define VIA_REG_ACR 4'hb +`define VIA_REG_PCR 4'hc +`define VIA_REG_IFR 4'hd +`define VIA_REG_IER 4'he +`define VIA_REG_ORA2 4'hf + +/* Interrupt bits */ +`define CA2_IRQ 0 +`define CA1_IRQ 1 +`define SR_IRQ 2 +`define CB2_IRQ 3 +`define CB1_IRQ 4 +`define T2_IRQ 5 +`define T1_IRQ 6 + +module via6522(input sysclk, + input reset, + + /* Bus interface. 4-bit address, to be wired + * appropriately upstream (to A9..A12) + */ + input cs, + input we, + input [3:0] rs, + input [7:0] wdata, + output [7:0] rdata, + output _irq, + + /* Port A and B signals. Fixed direction. Port B bit + * 0 (RTC data) is duplicated for in/out. + */ + input [7:7] pa_in7, + output [6:0] pa_out, + output [2:0] pb_out2_0, + output pb_out7, + input [6:3] pb_in6_3, + input pb_in0, + + /* CA1 and CA2 are inputs always */ + input ca1, + input ca2, + + /* Pseudo shift-register keyboard interface */ + input [7:0] sr_in, + input sr_in_strobe, + output [7:0] sr_out, + output reg sr_out_strobe); + + + /* Registers */ + reg [7:0] pa; + reg [7:0] pb; + reg [7:0] sr; + reg [7:0] acr; + reg [6:0] ier; + reg [6:0] ifr; + reg [7:0] pcr; + reg [15:0] t1; + reg [15:0] t1l; + reg [15:0] t2; + reg [7:0] t2l; + reg ddr_b0; + wire pb0_v; + wire ifr7; + reg ca1_old; + reg ca2_old; + reg pb6_old; + reg t1_armed; + reg t2_armed; + reg t1_fired; + reg t2_fired; + reg [4:0] ckdiv; + wire ckhalf; + reg t1pb7; + wire tstrobe; + wire t2strobe; + wire t1irq; + wire t2irq; + + /* Register selects */ + wire rs_orb; + wire rs_ora; + wire rs_ddrb; + wire rs_ddra; + wire rs_t1cl; + wire rs_t1ch; + wire rs_t1ll; + wire rs_t1lh; + wire rs_t2cl; + wire rs_t2ch; + wire rs_sr; + wire rs_acr; + wire rs_pcr; + wire rs_ifr; + wire rs_ier; + wire rs_ora2; + + assign rs_orb = cs && rs == `VIA_REG_ORB; + assign rs_ora = cs && rs == `VIA_REG_ORA; + assign rs_ddrb = cs && rs == `VIA_REG_DDRB; + assign rs_ddra = cs && rs == `VIA_REG_DDRA; + assign rs_t1cl = cs && rs == `VIA_REG_T1CL; + assign rs_t1ch = cs && rs == `VIA_REG_T1CH; + assign rs_t1ll = cs && rs == `VIA_REG_T1LL; + assign rs_t1lh = cs && rs == `VIA_REG_T1LH; + assign rs_t2cl = cs && rs == `VIA_REG_T2CL; + assign rs_t2ch = cs && rs == `VIA_REG_T2CH; + assign rs_sr = cs && rs == `VIA_REG_SR; + assign rs_acr = cs && rs == `VIA_REG_ACR; + assign rs_pcr = cs && rs == `VIA_REG_PCR; + assign rs_ifr = cs && rs == `VIA_REG_IFR; + assign rs_ier = cs && rs == `VIA_REG_IER; + assign rs_ora2 = cs && rs == `VIA_REG_ORA2; + + /* Outputs & interrupt generation */ + assign pa_out = pa[6:0] ; + assign pb_out2_0 = pb[2:0]; + assign pb_out7 = acr[7] ? t1pb7 : pb[7]; + assign sr_out = sr; + assign ifr7 = (ifr & ier) != 7'b0000000; + assign _irq = ~ifr7; + + /* PB[0] value */ + assign pb0_v = ddr_b0 ? pb[0] : pb_in0; + + /* Register reads */ + assign rdata = rs_ora ? { pa_in7, pa[6:0] } : + rs_ora2 ? { pa_in7, pa[6:0] } : + rs_orb ? { pb[7], pb_in6_3, pb[2:1], pb0_v } : + rs_ddrb ? { 1'b1, 4'b0000, 2'b11, ddr_b0 } : + rs_ddra ? { 1'b0, 7'b1111111 } : + rs_t1cl ? t1[7:0] : + rs_t1ch ? t1[15:8] : + rs_t1ll ? t1l[7:0] : + rs_t1lh ? t1l[15:8] : + rs_t2cl ? t2[7:0] : + rs_t2ch ? t2[15:8] : + rs_sr ? sr : + rs_acr ? acr : + rs_pcr ? pcr : + rs_ifr ? { ifr7, ifr } : + rs_ier ? { 1'b1, ier } : + 8'b11111111; + + /* Write to ORA */ + always@(posedge sysclk or posedge reset) begin + if (reset) + pa <= 8'b11111111; + else if (we && (rs_ora || rs_ora2)) + pa <= wdata; + end + + /* Write to ORB */ + always@(posedge sysclk or posedge reset) begin + if (reset) + pb <= 8'b11111111; + else if (we && rs_orb) + pb <= wdata; + end + + /* Write to DDRA ignored */ + /* Write to DDRB */ + always@(posedge sysclk or posedge reset) begin + if (reset) + ddr_b0 <= 1'b0; + else if (we && rs_orb) + ddr_b0 <= wdata[0]; + end + + /* Write to ACR */ + always@(posedge sysclk or posedge reset) begin + if (reset) + acr <= 8'b0; + else if (we && rs_acr) + acr <= wdata; + end + + /* Write to PCR */ + always@(posedge sysclk or posedge reset) begin + if (reset) + pcr <= 8'b0; + else if (we && rs_pcr) + pcr <= wdata; + end + + /* Timers + * + * Note: When writing to TnCH, we load the timer immediately + * and start counting on the next tstrobe (ie. P2 clock), which + * means that depending on when the CPU hits, the first P2 period + * might be reduced to one sysclk ... this could be "fixed" by causing + * all VIA accesses to wait for a P2 clock to complete but I'm happy + * to ignore the "problem" for now. + * + * Also, to keep things simple, PB7 goes up right after writing to + * T1CH, tho then follows proper periods (hopefully) + */ + + /* Clock division. We divide by 21 to get approx 793kHz */ + always@(posedge sysclk or posedge reset) begin + if (reset) + ckdiv <= 20; + else begin + if (ckdiv == 0) + ckdiv <= 20; + else + ckdiv <= ckdiv - 1; + end + end + assign tstrobe = (ckdiv == 0); + assign ckhalf = (ckdiv == 10); + + /* Timer 1 latches */ + always@(posedge sysclk or posedge reset) begin + if (reset) + t1l <= 0; + else begin + if (we && (rs_t1cl || rs_t1ll)) + t1l[7:0] <= wdata; + if (we && (rs_t1ch || rs_t1lh)) + t1l[15:8] <= wdata; + end + end + + /* Timer 1 counter */ + always@(posedge sysclk or posedge reset) begin + if (reset) + t1 <= 0; + else begin + /* Handle transfer from latch on write */ + if (we && rs_t1ch) begin + t1[7:0] <= t1l[7:0]; + t1[15:8] <= wdata; + end else if (tstrobe) begin + /* In "free run", reload from latch + * + * XXX There's an inconsistency in the doco + * as to whether we shall just roll over and + * count in one-shot mode or whether we shall + * -also- reload from the latch on T1 (it's + * clear for T2). For now I just let it roll + * over, we'll see if any SW gets upset + */ + if (acr[6] && t1_fired) + t1 <= t1l; + else + t1 <= t1 - 1; + end + end + end + + /* "armed" latch. This is set on a write and cleared when + * running out in "one shot" mode + */ + always@(posedge sysclk or posedge reset) begin + if (reset) + t1_armed <= 0; + else begin + /* Always armed when in continuous mode, else + * arm when writing to T1CH + */ + if ((we && rs_t1ch) || acr[6]) + t1_armed <= 1; + else if (t1_fired && !acr[6]) + t1_armed <= 0; + end + end + + /* "fired" latch. This is set on tstrobe with counter == 0 + * and is used to do whatever has to be done (irq, pb7, ...) + * on the next half phase 2 clock. It's set for the duration + * of the next P2 period. + */ + always@(posedge sysclk or posedge reset) begin + if (reset) + t1_fired <= 0; + else begin + if (tstrobe) + t1_fired <= t1_armed && t1 == 0; + end + end + + /* Generate PB7 output */ + always@(posedge sysclk or posedge reset) begin + if (reset) + t1pb7 <= 1; + else begin + /* We are 0 when timer is armed and not fired yet */ + if (we && rs_t1ch) + t1pb7 <= 0; + /* We invert when fired */ + if (ckhalf && t1_fired) + t1pb7 <= ~t1pb7; + end + end + + /* T1 interrupt */ + assign t1irq = ckhalf & t1_fired; + + /* Timer 2 + * + * Either one shot or count PB6 pulses (ie. hblank) + */ + always@(posedge sysclk or posedge reset) begin + if (reset) + pb6_old <= 1'b0; + else + pb6_old <= pb_in6_3[6]; + end + assign t2strobe = acr[5] ? (pb6_old & ~pb_in6_3[6]) : tstrobe; + + /* Timer 2 latche (low only) */ + always@(posedge sysclk or posedge reset) begin + if (reset) + t2l <= 0; + else begin + if (we && rs_t2cl) + t2l <= wdata; + end + end + + /* Timer 2 counter */ + always@(posedge sysclk or posedge reset) begin + if (reset) + t2 <= 0; + else begin + /* Handle transfer from latch on write */ + if (we && rs_t2ch) begin + t2[7:0] <= t2l; + t2[15:8] <= wdata; + end else if (t2strobe) + t2 <= t2 - 1; + end + end + + /* T2 "armed" latch (see T1) */ + always@(posedge sysclk or posedge reset) begin + if (reset) + t2_armed <= 0; + else begin + if (we && rs_t2ch) + t2_armed <= 1; + else if (t2_fired) + t2_armed <= 0; + end + end + + /* "fired" latch. This is set on tstrobe with counter == 0 + * or in pulse counting right when hitting the 0 -> -1 + * transitionand. It's set for the duration of the next P2 period + * in the former case, and for one sysclk in the later case. + */ + always@(posedge sysclk or posedge reset) begin + if (reset) + t2_fired <= 0; + else begin + if (t2strobe) + t2_fired <= t2_armed && t2 == 0; + if (acr[5] && t2_fired) + t2_fired <= 0; + end + end + + /* T2 Interrupt */ + assign t2irq = acr[5] ? t2_fired : (t2_fired & ckhalf); + + /* Shift register + * + * CB1 is the kbd clock, CB2 the shift register, + * we don't implement them that way, instead we + * have a direct 8-bit bus to the PS2 conversion + * module. The Mac only uses these ACR modes: + * 111 used when sending to the keyboard + * 110 used to assert the data line to 0 to start comm + * 011 used when receiving from the keyboard + * 000 used when transitioning + * + * We ignore the 110 mode as we don't need the + * keyboard to clock us. Thus we only care about + * mode 111 and 011. In the former, any write to + * sr results in an sr_out_strobe pulse to signal + * the keyboard. In mode 011, a pulse on sr_in_strobe + * results in updating the SR and eventually signaling + * an interrupt + */ + + /* Write to SR (including external input) */ + always@(posedge sysclk or posedge reset) begin + if (reset) + sr <= 8'b0; + else begin + if (we && rs_sr) + sr <= wdata; + if (acr[4:2] == 3'b011 && sr_in_strobe) + sr <= sr_in; + end + end + + /* Generate sr_out_strobe */ + always@(posedge sysclk or posedge reset) begin + if (reset) + sr_out_strobe <= 1'b0; + else begin + if (we && rs_sr && acr[4:2] == 3'b111) + sr_out_strobe <= 1; + else + sr_out_strobe <= 0; + end + end + + /* CA1 / CA2 edge detection */ + always@(posedge sysclk or posedge reset) begin + if (reset) begin + ca1_old <= 0; + ca2_old <= 0; + end else begin + ca1_old <= ca1; + ca2_old <= ca2; + end + end + + + /* Interrupts */ + + + /* Write to IER */ + always@(posedge sysclk or posedge reset) begin + if (reset) + ier <= 1'b0; + else if (we && rs_ier) begin + if (wdata[7]) + ier <= ier | wdata[6:0]; + else + ier <= ier & ~wdata[6:0]; + end + end + + /* Handle IFR */ + always@(posedge sysclk or posedge reset) begin + if (reset) + ifr <= 8'b0; + else begin + /* First, write-1-to-clear */ + if (we && rs_ifr) + ifr <= ifr & ~wdata[6:0]; + + /* Now clear individual sources based on + * register accesses + */ + if (rs_ora && (pcr[3:1] == 3'b000 || + pcr[3:1] == 3'b010)) + ifr[`CA2_IRQ] <= 0; + if (rs_ora) + ifr[`CA1_IRQ] <= 0; + if (rs_sr) + ifr[`SR_IRQ] <= 0; + if (rs_orb && (pcr[7:5] == 3'b000 || + pcr[7:5] == 3'b010)) + ifr[`CB2_IRQ] <= 0; + if (rs_orb) + ifr[`CB1_IRQ] <= 0; + if (we && rs_t2ch) + ifr[`T2_IRQ] <= 0; + if ((!we) && rs_t2cl) + ifr[`T2_IRQ] <= 0; + if (we && (rs_t1ch || rs_t1lh)) + ifr[`T1_IRQ] <= 0; + if ((!we) && rs_t1cl) + ifr[`T1_IRQ] <= 0; + + /* Finally set sources based on relevant + * events + */ + + /* -- SR interrupt -- + * + /* Note that when shifting out, we strobe and + * thus interrupt immediately when ACR is set + * to 111 (shift under external clock). That is + * sending commands to the keyboard. + * + * When ACR is set to 110 (sending a 0 pulse to + * the keyboard to wake it up), we just swallow + * everything here and don't generate an interrupt, + * the Mac ROM seems to be happy enough that way. + */ + + /* Shift out under control of external clock */ + if (we && rs_sr && acr[4:2] == 3'b111) + ifr[`SR_IRQ] <= 1; + + /* Shift in under control of external clock */ + if (acr[4:2] == 3'b011 && sr_in_strobe) + ifr[`SR_IRQ] <= 1; + + /* CA2 */ + if (pcr[3] == 0 && + ((!pcr[2] && ca2_old && !ca2) || + ( pcr[2] && !ca2_old && ca2))) + ifr[`CA2_IRQ] <= 1; + + /* CA1 */ + if ((!pcr[0] && ca1_old && !ca1) || + ( pcr[0] && !ca1_old && ca1)) + ifr[`CA1_IRQ] <= 1; + + /* No CB1/CB2 on the mac ? Well they are kbd data + * and clock, I don't think they are ever used, but + * we may want to check... + */ + + /* Timer IRQs */ + if (t1irq) + ifr[`T1_IRQ] <= 1; + if (t2irq) + ifr[`T2_IRQ] <= 1; + end + end +endmodule + diff --git a/fpga/video.v b/fpga/video.v new file mode 100644 index 0000000..bb4790c --- /dev/null +++ b/fpga/video.v @@ -0,0 +1,196 @@ +module video(input pixclk, + input reset, + input [15:0] pixels, + output reg req_pix, + input ack_pix, + output _hsync, + output _vsync, + output [3:0] red, + output [3:0] green, + output [3:0] blue, + output hblank); + + /* Max coordinate size in bits - 1 */ + parameter csize = 9; + + /* Fixed CRTC params for now. Based on a 640x480 VGA signal with + * added borders to center the Mac's 512x342. + * + * The added border is 64 pixels left and right and 69 pixels top + * and bottom. + * + * The constants are used as count-down and thus include a -1 to + * account for the 0 state. + */ + parameter hdisp_frontp = 8 - 1; + parameter hdisp_hsync = 96 - 1; + parameter hdisp_backp = 40 - 1; + parameter hdisp_lbord = 8 + 64 - 1; + parameter hdisp_display = 512 - 1; + parameter hdisp_rbord = 8 + 64 - 1; + + parameter vdisp_frontp = 2 - 1; + parameter vdisp_vsync = 2 - 1; + parameter vdisp_backp = 25 - 1; + parameter vdisp_tbord = 8 + 69 - 1; + parameter vdisp_display = 342 - 1; + parameter vdisp_bbord = 8 + 69 - 1; + + /* Corresponding stages */ + localparam hstage_frontp = 0; + localparam hstage_hsync = 1; + localparam hstage_backp = 2; + localparam hstage_lbord = 3; + localparam hstage_display = 4; + localparam hstage_rbord = 5; + + localparam vstage_frontp = 0; + localparam vstage_vsync = 1; + localparam vstage_backp = 2; + localparam vstage_tbord = 3; + localparam vstage_display = 4; + localparam vstage_bbord = 5; + + /* Running h/v counters */ + reg [csize:0] hcnt; + reg [csize:0] vcnt; + reg [csize:0] hnextcnt; + reg [csize:0] vnextcnt; + + /* State machines */ + reg [2:0] hstage; + reg [2:0] vstage; + + /* Output latches */ + reg _hsync_r; + reg _vsync_r; + reg pix_r; + + /* Pixel shift reg */ + reg [15:0] pix_shift; + wire vid_on; + + /* Request/ack protocol helpers */ + wire pix_req_reset; + + /* Obtain the new count for the next stage */ + always@(hstage) begin + case(hstage) + hstage_frontp: hnextcnt = hdisp_hsync; + hstage_hsync: hnextcnt = hdisp_backp; + hstage_backp: hnextcnt = hdisp_lbord; + hstage_lbord: hnextcnt = hdisp_display; + hstage_display: hnextcnt = hdisp_rbord; + hstage_rbord: hnextcnt = hdisp_frontp; + default: hnextcnt = hdisp_frontp; + endcase + end + always@(vstage) begin + case(vstage) + vstage_frontp: vnextcnt = vdisp_vsync; + vstage_vsync: vnextcnt = vdisp_backp; + vstage_backp: vnextcnt = vdisp_tbord; + vstage_tbord: vnextcnt = vdisp_display; + vstage_display: vnextcnt = vdisp_bbord; + vstage_bbord: vnextcnt = vdisp_frontp; + default: vnextcnt = vdisp_frontp; + endcase + end + + /* H counter */ + always@(posedge pixclk or posedge reset) begin + if (reset) begin + hcnt <= 0; + hstage <= hstage_frontp; + end else begin + if (hcnt == 0) begin + hcnt <= hnextcnt; + if (hstage == hstage_rbord) + hstage <= hstage_frontp; + else + hstage <= hstage + 1; + end else + hcnt <= hcnt - 1; + end + end + + /* V counter */ + always@(posedge pixclk or posedge reset) begin + if (reset) begin + vcnt <= 0; + vstage <= vstage_frontp; + end else begin + if (hstage == hstage_rbord && hcnt == 0) begin + if (vcnt == 0) begin + vcnt <= vnextcnt; + if (vstage == vstage_bbord) + vstage <= vstage_frontp; + else + vstage <= vstage + 1; + end else begin + vcnt <= vcnt - 1; + end + end + end + end + + /* Visible signal */ + assign vid_on = hstage == hstage_display && + vstage == vstage_display; + + /* Pixel request, async clear */ + assign pix_req_reset = reset | ack_pix; + + /* Generate pixel request. NOTE: This relies on the display + * width being a multiple of 16 + */ + always@(posedge pixclk or posedge pix_req_reset) begin + if (pix_req_reset) begin + req_pix <= 0; + end else begin + if (vstage == vstage_display) begin + if (hstage == hstage_backp && hcnt == 0) begin + req_pix <= 1; + end else if (hstage == hstage_display && + hcnt[3:0] == 4'b1111 && + hcnt[csize:4]) begin + req_pix <= 1; + end + end + + end + end + + /* Shift register */ + always@(posedge pixclk or posedge reset) begin + if (reset) begin + pix_shift <= 0; + end else begin + if (hcnt[3:0] == 0) + pix_shift <= pixels; + else + pix_shift[15:1] <= pix_shift[14:0]; + end + end + + /* Output latches */ + always@(posedge pixclk or posedge reset) begin + if (reset) begin + _hsync_r <= 1; + _vsync_r <= 1; + pix_r <= 0; + end else begin + _hsync_r <= ~(hstage == hstage_hsync); + _vsync_r <= ~(vstage == vstage_vsync); + pix_r <= vid_on & ~pix_shift[15]; + end + end + + /* Output wires */ + assign _hsync = _hsync_r; + assign _vsync = _vsync_r; + assign red = { pix_r, pix_r, pix_r, pix_r }; + assign green = { pix_r, pix_r, pix_r, pix_r }; + assign blue = { pix_r, pix_r, pix_r, pix_r }; + assign hblank = hstage != hstage_display; +endmodule diff --git a/fpga/xilinx/minimigmac.xise b/fpga/xilinx/minimigmac.xise new file mode 100644 index 0000000..45a8591 --- /dev/null +++ b/fpga/xilinx/minimigmac.xise @@ -0,0 +1,405 @@ + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/pic/18f252_mod.lkr b/pic/18f252_mod.lkr new file mode 100644 index 0000000..d5ace8b --- /dev/null +++ b/pic/18f252_mod.lkr @@ -0,0 +1,23 @@ +// File: 18f252.lkr +// Sample linker script for the PIC18F252 processor + +// Not intended for use with MPLAB C18. For C18 projects, +// use the linker scripts provided with that product. + +// Modified for larger data bank to fit secbuf + +LIBPATH . + +CODEPAGE NAME=page START=0x0 END=0x7FFF +CODEPAGE NAME=idlocs START=0x200000 END=0x200007 PROTECTED +CODEPAGE NAME=config START=0x300000 END=0x30000D PROTECTED +CODEPAGE NAME=devid START=0x3FFFFE END=0x3FFFFF PROTECTED +CODEPAGE NAME=eedata START=0xF00000 END=0xF000FF PROTECTED + +ACCESSBANK NAME=accessram START=0x0 END=0x7F +DATABANK NAME=gpr0 START=0x80 END=0xFF +DATABANK NAME=gpr1 START=0x100 END=0x1FF +DATABANK NAME=gpr2 START=0x200 END=0x2FF +DATABANK NAME=gpr3 START=0x300 END=0x3FF +DATABANK NAME=gpr45 START=0x400 END=0x5FF +ACCESSBANK NAME=accesssfr START=0xF80 END=0xFFF PROTECTED diff --git a/pic/Makefile b/pic/Makefile new file mode 100644 index 0000000..9ad42b6 --- /dev/null +++ b/pic/Makefile @@ -0,0 +1,28 @@ +OBJS = main.o hardware.o mmc.o fat16.o scsi.o + +LIBS = /usr/share/sdcc/lib/pic16/libc18f.lib +LIBS += /usr/share/sdcc/lib/pic16/libio18f252.lib + +SDCC_BASE_FLAGS = -mpic16 -p18f252 -V --debug +SDCC_CFLAGS = $(SDCC_BASE_FLAGS) --obanksel=2 --denable-peeps --optimize-cmp --optimize-df +SDCC_LDFLAGS = $(SDCC_BASE_FLAGS) -Wl-c -Wl-m -Wl-s18f252_mod.lkr + + +%.o : %.c + sdcc $(SDCC_CFLAGS) -c $^ + +minimigmac.hex: $(OBJS) 18f252_mod.lkr + sdcc $(SDCC_LDFLAGS) -o$@ $(OBJS) $(LIBS) + +all: minimigmac.hex + +clean: + rm -rf *.o + rm -rf *.asm + rm -rf *.lst + rm -rf *.adb + +distclean: clean + rm -rf *~ + + diff --git a/pic/fat16.c b/pic/fat16.c new file mode 100644 index 0000000..a47a71c --- /dev/null +++ b/pic/fat16.c @@ -0,0 +1,542 @@ +/* +Copyright 2005, 2006, 2007 Dennis van Weeren + +This file is part of Minimig + +Minimig is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +Minimig is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +This is a simple FAT16 handler. It works on a sector basis to allow fastest acces on disk +images. + +Simplified from Minimig version + +*/ + +#include +#include +#include + +#include "mmc.h" +#include "fat16.h" +#include "fat16_defs.h" + +/* Enable / Disable Debug info */ +#undef DEBUG_FAT + +/* Structure containing selected partition data */ +struct partition_info { + unsigned char fatType; // 0x10=FAT16, 0x20=FAT32 + unsigned long partStart; // start LBA of partition + unsigned long fatStart; // start LBA of first FAT table + unsigned long dataStart; // start LBA of data field + unsigned long rootDirCluster; // root directory cluster + unsigned long rootDirStart; // start LBA of root directory table + unsigned short rootDirEntries; // start LBA of root directory table + unsigned char fatNo; // number of FAT tables + unsigned char clusterSize; // size of a cluster in blocks + unsigned long clusterMask; // binary mask of cluster number +}; + + +/* Open file info */ +struct file_info { + unsigned char name[12]; // Short file name + unsigned char attributes; // File attributes + unsigned short entry; // File-entry index in directory table + unsigned long sector; // Sector index in file + unsigned long len; // File size + unsigned long cluster; // Current cluster + unsigned long firstCluster; // First file cluster +}; + + +#define MAX_LFN_SIZE 128 // Maximum supported long file name, default is 256 + +/* Directory Seek Modes */ +#define DIRECTORY_BROWSE_START 0 // start search from beginning of directory +#define DIRECTORY_BROWSE_NEXT 2 // find next file in directory + +/* Sector buffer. Shared globally to save space on PIC */ +static unsigned long cached_cur_lba; + +/* Selected Partition */ +static struct partition_info fat_partition; + +static unsigned char longFilename[MAX_LFN_SIZE]; // Default long filename entry + +static struct file_info fat_cur_file; // global file handle +static struct file_info fat_cur_dir; // global directory file handle + +// Long filename entry char pos +static const unsigned char charLFNPos[] = { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 }; + + +#ifdef DEBUG_FAT +#define pr_debug(args...) printf_tiny(args) +#else +#define pr_debug(args...) +#endif + +/* Caching functions for MMC */ +static unsigned char cached_read(unsigned long lba) +{ + if (cached_cur_lba == lba) + return true; + + if (!mmc_read(lba, secbuf)) + return false; + + cached_cur_lba = lba; + return true; +} + +static unsigned char cached_write(unsigned long lba) +{ + cached_cur_lba = lba; + return mmc_write(lba, secbuf); +} + +/* Calculate LBA from current file position cluster and sector */ +static unsigned long fat_get_lba(struct file_info near *file) +{ + unsigned long lba; + + /* Calc sector to read, when first cluster is 0 it is root directory of FAT16 */ + if (file->firstCluster == 0) { + lba = fat_partition.rootDirStart; + lba += file->sector; + } else { + lba = fat_partition.dataStart; // start of data in partition + lba += (file->cluster-2) * fat_partition.clusterSize; // cluster offset + lba += file->sector & ~fat_partition.clusterMask; // sector offset in cluster + } + return lba; +} + +/* Find next file cluster from the FAT */ +static unsigned char fat_get_next_cluster(struct file_info near *file) +{ + unsigned short index; + unsigned long fatSectorLba; + + /* calculate sector that contains FAT-link */ + fatSectorLba = fat_partition.fatStart; + + if (fat_partition.fatType & 0x10) { + /* FAT16 256 cluster infos in sector on disk */ + fatSectorLba += (file->cluster) >> 8; + + /* calculate offset */ + index = (file->cluster) & 0xFF; + index <<= 1; + } else { + /* FAT32 128 cluster infos in sector on disk */ + fatSectorLba += (file->cluster) >> 7; + + /* FAT32 128 cluster infos in sector */ + index = (file->cluster) & 0x7F; + index <<= 2; + } + + /* Read sector of FAT */ + if (!cached_read(fatSectorLba)) + return false; + + if (fat_partition.fatType & 0x10) { + /* get FAT-link */ + file->cluster = *((unsigned short*)&secbuf[index]); + + /* Exit if end of chain */ + if (file->cluster == 0xffff) + return false; + } else { + /* For FAT32 Read long instead short */ + file->cluster = *((unsigned long*)&secbuf[index]); + file->cluster &= 0x0FFFFFFF; + + /* Exit if end of chain */ + if (file->cluster == 0x0fffffff) + return false; + } + return true; +} + +/* Point to next sector in file */ +static unsigned char fat_do_next_sector(struct file_info near *file) +{ + /* increment sector index */ + file->sector++; + + /* if we are now in another cluster, look up cluster */ + if ((file->sector & ~fat_partition.clusterMask) == 0) + if (!fat_get_next_cluster(file)) + return false; + + pr_debug(" n s=0x%lx c=%lx\r\n", file->sector, file->cluster); + + return true; +} + +/* Seek within a file (or directory) entry */ +static unsigned char fat_file_do_seek(struct file_info near *file, unsigned long sector) +{ + long currentClusterNo; + long clusterNo; + + /* Calculate Current ClusterNo to avoid seek if new secor is on + * same cluster + */ + currentClusterNo = (long)file->sector; + currentClusterNo /= (long)fat_partition.clusterSize; + + /* Sector in file to read */ + file->sector = sector; + + /* Calculate new ClusterNO in file */ + clusterNo = (long)file->sector; + clusterNo /= (long)fat_partition.clusterSize; + + /* Calc ClusterNo Difference */ + currentClusterNo -= clusterNo; + + /* Check if we are on same cluster */ + if (currentClusterNo == 0) { + pr_debug(" S s=0x%lx c=%lx\r\n", file->sector, file->cluster); + return true; + } + + /* Reset current cluster in file to first */ + file->cluster = file->firstCluster; + + /* If first cluster in file exit */ + if (clusterNo == 0) + return true; + + /* Loop through cluster links */ + while(--clusterNo >= 0) { + if (!fat_get_next_cluster(file)) + return false; + // pr_debug(" -seek: clusterNo: 0x%lx", clusterNo); + // pr_debug(" , cluster: 0x%lx\r\n", file->cluster); + } + + pr_debug(" s s=0x%lx c=%lx\r\n", file->sector, file->cluster); + return true; +} + +static unsigned char fat_process_dir_entry(struct file_info near *file, union FAT_directoryEntry * dirEntry) +{ + short i; + short char_offset; + + /* Check if we are at directory end */ + if (dirEntry->entry.shortName.name[0] == FAT_ENTRY_FREE) { + pr_debug("Entry free %d\r\n", file->entry); + return 0; + } + + /* Check if file deleted */ + if (FAT_ENTRY_DELETED != dirEntry->entry.shortName.name[0]) { + /* Check if longfilename */ + if(FAT_ATTRIB_LFN_TEXT == dirEntry->entry.attributes) { + /* Calc pos to copy part of LFN */ + char_offset = ((dirEntry->LFN.sequenceNo & 0x3f)-1) * 13; + + /* Copy part of LFN */ + for(i = 0; i < 13 && (char_offset + i) < (MAX_LFN_SIZE - 1); i++) + longFilename[char_offset + i] = dirEntry->bytes[charLFNPos[i]]; + } else if (dirEntry->entry.attributes & + (FAT_ATTRIB_HIDDEN | FAT_ATTRIB_SYSTEM | FAT_ATTRIB_VOLUME)) { + pr_debug("FAT: Skiping entry no:%d attr:0x%02X\n", + file->entry, dirEntry->entry.attributes); + /* Clear longfile name just in case */ + memset(longFilename,0,MAX_LFN_SIZE); + } else { + /* Copy name */ + memcpy(&file->name, &dirEntry->entry.shortName, 11); + file->name[11] = 0x00; /* In theory not needed since never trashed */ + file->attributes = dirEntry->entry.attributes; + file->len = dirEntry->entry.length; /* get length of file */ + file->firstCluster = (unsigned long)dirEntry->entry.firstClusterLow; // get first cluster of file + /* FAT32 high bytes of cluster */ + if (fat_partition.fatType & 0x20) + file->firstCluster |= ((unsigned long)dirEntry->entry.firstClusterHigh) << 16; + file->cluster = file->firstCluster; /* Copy current cluster to first cluster */ + file->sector = 0; /* Reset current sector index */ + + /* Check if there is no LFN than copy short name as LFN */ + /* Skip entries that have LFN and entries starting with '.' (Parent and Current dir) */ + if(!longFilename[0] && (file->name[0] != '.')) { + char_offset = 0; + for(i=0; i < 11; i++) { + if(file->name[i] != ' ') + longFilename[char_offset++] = file->name[i]; + if(7 == i) + longFilename[char_offset++] = '.'; + } + /* Delete Last dot */ + if(longFilename[char_offset-1]=='.') + char_offset--; + longFilename[char_offset] = 0x00; + } + return 1; + } + } + return 2; +} + +/* Iterate directory entries */ +static unsigned char fat_get_dir_entry(struct file_info near *file, struct file_info near *dir, + unsigned char mode) +{ + short entryOffset; + char rc; + + /* Clear long file name */ + memset(longFilename,0,MAX_LFN_SIZE); + + /* Check for iteration mode */ + switch(mode) { + case DIRECTORY_BROWSE_NEXT: + file->entry++; + break; + case DIRECTORY_BROWSE_START: + default: + file->entry = 0; + break; + } + + /* Seek directory to selected file entry */ + if(!fat_file_do_seek(dir, file->entry >> 4)) + return false; + + /* Infinite loop for search */ + while(1) { + /* Read sector in buffer */ + if (!cached_read(fat_get_lba(dir))) + return false; + + /* Calculate file entry offset in directory sector, 16 entries in sector 32 byte big */ + entryOffset = (file->entry & 0xF) << 5; + + /* Process directory entry */ + rc = fat_process_dir_entry(file, (union FAT_directoryEntry *)(secbuf + entryOffset)); + if (!rc) + return false; + if (rc == 1) + return true; + + /* Go to next Fat Entry */ + file->entry++; + + /* Check should we seek directory to next sector */ + if (!(file->entry & 0xF)) + if (!fat_do_next_sector(dir)) + return false; + } +} + + +/* Move to root directory */ +static void fat_cd_root_dir(struct file_info near *dir) +{ + /* Clear Entry */ + memset(dir, 0, sizeof(struct file_info)); + dir->name[0] = '/'; + dir->attributes = FAT_ATTRIB_DIR; + dir->firstCluster = fat_partition.rootDirCluster; + dir->cluster = dir->firstCluster; +} + +/* Find file by name and open it, return len or -1 */ +long fat_open(const unsigned char *name) +{ + struct file_info near *file = &fat_cur_file; + unsigned char mode = DIRECTORY_BROWSE_START; + unsigned char i; + + pr_debug("FAT: Open file: %s\n",name); + + while (fat_get_dir_entry(file, &fat_cur_dir, mode)) { + pr_debug(" found: %s...",file->name); + for(i=0; i<11; i++) + if (file->name[i]!=name[i]) + break; + if (i==11) { + pr_debug(" GOOD !\n"); + return file->len; + } + pr_debug("\n"); + mode = DIRECTORY_BROWSE_NEXT; + } + + pr_debug("FAT: File not found\n"); + + return -1; +} + +unsigned char fat_file_read(void) +{ + return cached_read(fat_get_lba(&fat_cur_file)); +} + +unsigned char fat_file_write(void) +{ + return cached_write(fat_get_lba(&fat_cur_file)); +} + +unsigned char fat_file_seek(unsigned long sector) +{ + return fat_file_do_seek(&fat_cur_file, sector); +} + +unsigned char fat_file_next_sector(void) +{ + return fat_do_next_sector(&fat_cur_file); +} + +void fat_inval_cache(void) +{ + cached_cur_lba = 0xffffffff; +} + +/* fat_init() + * + * Checks if a card is present. if a card is present it will check for + * a valid FAT16 or FAT32 primary partition + */ +unsigned char fat_init(void) +{ + unsigned long fatsize; // size of fat + unsigned long dirsize; // size of directory region in sectors + + struct MBR_Disk *mbr = (struct MBR_Disk *)secbuf; + struct FAT_Boot_Sector *boot = (struct FAT_Boot_Sector *)secbuf; + + /* Invalidate cache */ + cached_cur_lba = 0xffffffff; + + /* Read partition sector */ + if (!cached_read(0)) { + pr_debug("FAT: Error Unable to read partition sector\n"); + return false; + } + + /* Check for signature */ + if (mbr->signature != 0xAA55) { + pr_debug("FAT: Error invalid FAT signature\n"); + return false; + } + + /* Check first partition type */ + switch (mbr->partitions[0].Type) { + case PARTITION_TYPE_FAT16_32MB: + case PARTITION_TYPE_FAT16: + /* First partition filesystem type: FAT16 */ + fat_partition.fatType = 0x10; + /* Get start of first partition */ + fat_partition.partStart = mbr->partitions[0].LBAFirst; + break; + + case PARTITION_TYPE_FAT32: + case PARTITION_TYPE_FAT32_LBA: + /* First partition filesystem type: FAT32 */ + fat_partition.fatType = 0x20; + /* get start of first partition */ + fat_partition.partStart = mbr->partitions[0].LBAFirst; + break; + + default: + pr_debug("FAT: Error no supported partition found\n"); + return false; + } + + /* Read boot sector */ + if (!cached_read(fat_partition.partStart)) { + pr_debug("FAT: Error unable to read boot sector\n"); + return false; + } + + /* Check for near-jump or short-jump opcode */ + if (0xE9 != boot->jumpInstruction[0] && 0xEB != boot->jumpInstruction[0]) { + pr_debug("FAT: Error invalid 0x%x boot sector jump\n", + boot->jumpInstruction[0]); + return false; + } + + /* Check if blocksize is really 512 bytes */ + if (0x200 != boot->bytesPerSector) { + pr_debug("FAT: Error block size not 512 bytes it is 0x%x\n", + boot->bytesPerSector); + return false; + } + + /* Check medium descriptor byte, must be 0xf8 for hard drive */ + if (0xF8 != boot->mediaDescriptor) { + pr_debug("Error invalid media descriptor\n"); + return false; + } + + /* Get cluster size */ + fat_partition.clusterSize = boot->sectorsPerCluster; + /* calculate cluster mask */ + fat_partition.clusterMask = ~(fat_partition.clusterSize-1); + + + /* Calculate drive's parameters from bootsector, first up is size of directory */ + /* Get Max ROOT Dir entries FAT16 only !!*/ + fat_partition.rootDirEntries = boot->maxRootEntries; + + /* Calculate start of FAT, size of FAT and number of FAT's */ + fat_partition.fatStart = fat_partition.partStart + boot->reservedSectorCount; + fat_partition.fatNo = boot->noOfFATs; + + /* When FAT16 */ + if (0x10 == fat_partition.fatType) { + fatsize = boot->sectorsPerFAT; + // Fat 16 Root dir cluster + fat_partition.rootDirCluster = 0; + // calculate start of FAT16 ROOT directory + fat_partition.rootDirStart = fat_partition.fatStart + (fat_partition.fatNo * fatsize); + // Calculate dire size in sectors + dirsize = ((fat_partition.rootDirEntries<<5)+511)>>9; + // calculate start of data + fat_partition.dataStart = fat_partition.rootDirStart + dirsize; + } else { + fatsize = boot->extParams.fat32Ext.sectorsPerFAT; + // calculate data start + fat_partition.dataStart = fat_partition.fatStart + (fat_partition.fatNo * fatsize); + // Fat 32 Root dir cluster + fat_partition.rootDirCluster = boot->extParams.fat32Ext.rootDirCluster; + // Get FAT32 root dir start + fat_partition.rootDirStart = fat_partition.dataStart + + (fat_partition.rootDirCluster-2) * fat_partition.clusterSize; + } + + /* Open Root Directory */ + fat_cd_root_dir(&fat_cur_dir); + + pr_debug("FAT%d partition found.\n", fat_partition.fatType); + pr_debug("Fat Size: %ld\n",fatsize); + pr_debug("No of fats: %d\n",fat_partition.fatNo); + pr_debug("Fat start: %ld\n", fat_partition.fatStart); + pr_debug("Direntries: %d\n",fat_partition.rootDirEntries); + pr_debug("Dir start: %ld\n", fat_partition.rootDirStart); + pr_debug("Data start: %ld\n", fat_partition.dataStart); + pr_debug("Cluster size: %d\n",fat_partition.clusterSize); + pr_debug("Cluster mask: %lx\n",fat_partition.clusterMask); + + return true; +} + diff --git a/pic/fat16.h b/pic/fat16.h new file mode 100644 index 0000000..2d4fea1 --- /dev/null +++ b/pic/fat16.h @@ -0,0 +1,30 @@ +#ifndef __FAT16_H__ +#define __FAT16_H__ + +/* Global sector buffer, data for read/write actions is stored here + * + * Any operation (init, open, seek and read) will clobber it. If you + * modify this buffer content, you _MUST_ either follow with a + * fat_file_write or a fat_inval_cache _BEFORE_ any other open, read + * or seek operation + */ +extern unsigned char secbuf[512]; + +/* Initialize, find first partition */ +unsigned char fat_init(void); + +/* Open a file by name, one open file at a time, replace the previous one */ +long fat_open(const unsigned char *name); + +/* Seek into file WARNING: Can clobber secbuf ! */ +unsigned char fat_file_seek(unsigned long sector); +unsigned char fat_file_next_sector(void); + +/* Read and write current file sector */ +unsigned char fat_file_read(void); +unsigned char fat_file_write(void); + +/* Invalidate sector cache */ +void fat_inval_cache(void); + +#endif /* __FAT16_H__ */ diff --git a/pic/fat16_defs.h b/pic/fat16_defs.h new file mode 100644 index 0000000..7c4827b --- /dev/null +++ b/pic/fat16_defs.h @@ -0,0 +1,171 @@ +#ifndef __FAT_DEFS_H__ +#define __FAT_DEFS_H__ + +/* Partition Type Defninition */ +#define PARTITION_TYPE_FREE 0x00 /* The partition table entry is not used. */ +#define PARTITION_TYPE_FAT12 0x01 /* The partition contains a FAT12 filesystem. */ +#define PARTITION_TYPE_FAT16_32MB 0x04 /* The partition contains a FAT16 filesystem with + 32MB maximum. */ +#define PARTITION_TYPE_EXTENDED 0x05 /* The partition is an extended partition with its own + partition table. */ +#define PARTITION_TYPE_FAT16 0x06 /* The partition contains a FAT16 filesystem. */ +#define PARTITION_TYPE_FAT32 0x0b /* The partition contains a FAT32 filesystem. */ +#define PARTITION_TYPE_FAT32_LBA 0x0c /* The partition contains a FAT32 filesystem with LBA. */ +#define PARTITION_TYPE_FAT16_LBA 0x0e /* The partition contains a FAT16 filesystem with LBA. */ +#define PARTITION_TYPE_EXTENDED_LBA 0x0f /* The partition is an extended partition with LBA. */ +#define PARTITION_TYPE_UNKNOWN 0xff /* The partition has an unknown type. */ + + +/* File Attributes */ +#define FAT_ATTRIB_READONLY (1 << 0) // The file is read-only. +#define FAT_ATTRIB_HIDDEN (1 << 1) // The file is hidden. +#define FAT_ATTRIB_SYSTEM (1 << 2) // The file is a system file. +#define FAT_ATTRIB_VOLUME (1 << 3) // The file is empty and has the volume label as its name. +#define FAT_ATTRIB_DIR (1 << 4) // The file is a directory. +#define FAT_ATTRIB_ARCHIVE (1 << 5) // The file has to be archived. +#define FAT_ATTRIB_LFN_TEXT (FAT_ATTRIB_VOLUME | FAT_ATTRIB_SYSTEM | FAT_ATTRIB_HIDDEN |\ + FAT_ATTRIB_READONLY) // 0x0F Long filename entry + +#define FAT_LFN_LAST_MASK 0x40 // Last long entry flag + +/* First Characters in Fat name */ +#define FAT_ENTRY_FREE 0x00 /* Fat entry is free */ +#define FAT_ENTRY_KANJI 0x05 /* Fat entry is kanji */ +#define FAT_ENTRY_PARENT_DIRECTORY 0x2E /* Fat entry is directory or parent direcotry */ +#define FAT_ENTRY_DELETED 0xE5 /* Fat entry is deleted */ + + +/* Define partition structure */ +struct PRIMARY_Partition +{ + unsigned char Status; // Partition status + unsigned char CHSFirstBlock[3]; + unsigned char Type; // Partition type + unsigned char CHSLastBlock[3]; + unsigned long LBAFirst; // First LBA block + unsigned long LBABlocks; // Number of LBA blocks in partition +}; + + +/* Define Master Boot Record */ +struct MBR_Disk +{ + unsigned char bootCode[440]; // Code Area + unsigned long diskSignature; // Optional Disk signature + unsigned short reserved; // Usually Nulls; 0x0000 + struct PRIMARY_Partition partitions[4]; // Table of primary partitions + // (Four 16-byte entries, IBM Partition Table scheme) + unsigned short signature; // MBR signature; 0xAA55 +}; + + +/* FAT 16 Extened bios parametars block */ +struct FAT16_ExtBiosParams +{ + unsigned char physicalDriveNo; // Physical drive number + unsigned char reserved; // Reserved ("current head") In Windows NT bit 0 is + // a dirty flag to request chkdsk at boot time. + // bit 1 requests surface scan too.[28] + unsigned char extBootSignature; // Extended boot signature.Value is 0x29[27] or 0x28. + unsigned long ID; // ID (serial number) + unsigned char volumeLabel[11]; // Volume Label, padded with blanks (0x20). + unsigned char FATFileSystemType[8]; // FAT file system type, padded with blanks (0x20), + // e.g.: "FAT12 ", "FAT16 ". This is not meant + // to be used to determine drive type, however, + // some utilities use it in this way. + unsigned char OsBootCode[448]; // Operating system boot code +}; + + +/* FAT32 Extended parametars block */ +struct FAT32_ExtBiosParams +{ + unsigned long sectorsPerFAT; // Sectors per file allocation table + unsigned short flags; // FAT Flags + unsigned short version; // Version + unsigned long rootDirCluster; // Cluster number of root directory start + unsigned short fsInformationSector; // Sector number of FS Information Sector + unsigned short bootSectorCopy; // Sector number of a copy of this boot sector + unsigned char reserved1[12]; // Reserved + unsigned char physicalDriveNo; // Physical drive number + unsigned char reserved2; // Reserved + unsigned char extBootSignature; // Extended boot signature. + unsigned long ID; // ID (serial number) + unsigned char volumeLabel[11]; // Volume Label + unsigned char FATFileSystemType[8]; // FAT file system type: "FAT32 " + unsigned char OsBootCode[420]; // Operating system boot code +}; + + +/* Fat Boot Sector */ +struct FAT_Boot_Sector +{ + unsigned char jumpInstruction[3]; // Jump instruction. This instruction will be executed and will skip past the rest of the (non-executable) header if the partition is booted from. See Volume Boot Record. If the jump is two-byte near jmp it is followed by a NOP instruction. + unsigned char oemName[8]; // OEM Name (padded with spaces). This value determines in which system disk was formatted. MS-DOS checks this field to determine which other parts of the boot record can be relied on.[25][26] Common values are IBM 3.3 (with two spaces between the "IBM" and the "3.3"), MSDOS5.0 and MSWIN4.1 and mkdosfs. + unsigned short bytesPerSector; // Bytes per sector. A common value is 512, especially for file systems on IDE (or compatible) disks. The BIOS Parameter Block starts here. + unsigned char sectorsPerCluster; // Sectors per cluster. Allowed values are powers of two from 1 to 128. However, the value must not be such that the number of bytes per cluster becomes greater than 32 KB. + unsigned short reservedSectorCount; // Reserved sector count. The number of sectors before the first FAT in the file system image. Should be 1 for FAT12/FAT16. Usually 32 for FAT32. + unsigned char noOfFATs; // Number of file allocation tables. Almost always 2. + unsigned short maxRootEntries; // Maximum number of root directory entries. Only used on FAT12 and FAT16, where the root directory is handled specially. Should be 0 for FAT32. This value should always be such that the root directory ends on a sector boundary (i.e. such that its size becomes a multiple of the sector size). 224 is typical for floppy disks. + unsigned short totalSectorsFAT16; // Total sectors (if zero, use 4 byte value at offset totalSectorsFAT32) + unsigned char mediaDescriptor; // Media descriptor 0xF8 Fixed disk (i.e. Hard disk). + unsigned short sectorsPerFAT; // Sectors per File Allocation Table for FAT12/FAT16 + unsigned short sectorsPerTrack; // Sectors per track + unsigned short noHeads; // Number of heads + unsigned long hiddenSectors; // Hidden sectors + unsigned long totalSectorsFAT32; // Total sectors (if greater than 65535; otherwise, see offset totalSectorsFAT16) + union + { + struct FAT16_ExtBiosParams fat16Ext; + struct FAT32_ExtBiosParams fat32Ext; + } extParams; + unsigned short signature; // Boot sector signature (0x55 0xAA) +}; + + +// FAT Directory Entry +struct FAT_dirEntryDefault +{ + struct + { + unsigned char name[8]; // File name + unsigned char ext[3]; // File name extension + } shortName; // Short file name + unsigned char attributes; // File attributes + unsigned char reserved; // Reserved + unsigned char createTimeFineRes; // Create time fine resolution 10ms + unsigned short createTime; // Create time + unsigned short createDate; // Create date + unsigned short lastAccessDate; // Last access date + unsigned short firstClusterHigh; // First cluster high two bytes (FAT32 only) + unsigned short modifiedTime; // Last modified time + unsigned short modifiedDate; // Last modified date + unsigned short firstClusterLow; // First cluster low two bytes + unsigned long length; // File length +}; + + +// FAT LFN Directory entry structure +struct FAT_dirEntryLFN +{ + unsigned char sequenceNo; // Sequence number for long file name entry + unsigned short fiveUTF16[5]; // Five UTF16 characters + unsigned char attributes; // Attributes always 0x0F + unsigned char type; // LFN entry type always 0x00 + unsigned char checksum; // Dos file name checksum + unsigned short sixUTF16[6]; // Six UTF16 characters + unsigned short firstCluster; // First cluster always 0x0000 + unsigned short twoUTF16[2]; // Two UTF16 characters +}; + + +// FAT file entry structure all possible structures combined +union FAT_directoryEntry +{ + struct FAT_dirEntryDefault entry; // Default File Entry + struct FAT_dirEntryLFN LFN; // FAT LFN file entry structure + unsigned char bytes[32]; // Simple Byte Buffer +}; + + +#endif /* __FAT_DEFS_H__ */ diff --git a/pic/hardware.c b/pic/hardware.c new file mode 100644 index 0000000..714b5b1 --- /dev/null +++ b/pic/hardware.c @@ -0,0 +1,247 @@ +/* +Copyright 2005, 2006, 2007 Dennis van Weeren + +This file is part of Minimig + +Minimig is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +Minimig is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Hardware control routines + +27-11-2005 -started coding +29-01-2006 -done a lot of work +31-01-2006 -added key repeat +06-02-2006 -took out all button handling stuff + +-- Goran Ljubojevic -- +2009-11-13 - OsdCommand added +2009-11-21 - small cleanup +2009-12-20 - systimer reset on every hardware init to support propper timings on reset +2009-12-30 - Support for new FPGA firmware added in header file + - GetFPGAStatus function added +2010-01-29 - ResetFPGA() macro added to header file. +2010-08-21 - YQ100818 FPGA core support +2010-08-26 - Added firmwareConfiguration.h +2010-09-09 - Added _SPI macro to help save space on multiple SPI calls +2010-10-15 - Moved over to minimigmac, coding style changes, etc... +*/ + +#include + +#include "hardware.h" + +static volatile unsigned short systimer; + +/*initialize hardware*/ +void hardware_init(void) +{ + /* Disable analog inputs */ + ADCON1 = 0b00000110; + + /* Initalize output register */ + PORTA = 0b00100011; + PORTB = 0b00000000; + PORTC = 0b00010001; + + /* Enable PORTB weak pullup */ + INTCON2bits.RBPU = 0; + + /* Initialize SPI to master mode 0, sample on posedge, 5Mhz */ + SSPSTAT = 0x40; + SSPCON1 = 0x20; + + /* Initialize input/ouput configuration */ + TRISA = 0b11001100; + TRISB = 0b00101011; + TRISC = 0b10010000; + + /* Initialize serial port */ + SPBRG = 10; /*115200 BAUD @ 20MHz*/ + TXSTA = 0x24; + RCSTA = 0x90; + + /* Init timer0, internal clk, prescaler 1:256 */ + T0CON = 0xc7; + TMR0H = 0; + TMR0L = 0; + + /* Enable interrupt for timer 0 */ + INTCONbits.TMR0IE = 1; + INTCONbits.GIE = 1; +} + +/* interrupt service routine */ +static void isr(void) interrupt 1 +{ + unsigned short t; + static unsigned char button_state; + + /* Clear timer 0 interrupt flag */ + INTCONbits.TMR0IF = 0; + + /* Set timer to timeout every 10ms + * @20Mhz --> instruction = 200ns + * 200ns * 256 * 195 = 10ms + * + * Not sure we can just cast, so for now be gross + * since SDCC doesn't give us a 16-bit TMR0 + */ + t = TMR0L; + t |= ((unsigned short)TMR0H) << 8; + t -= 195; + TMR0H = t >> 8; + TMR0L = t & 0xff; + + /* Increment system timer */ + systimer++; + + /* Debounce button */ + if (check_button()) { + if (button_state < 10) { + button_state++; + if (button_state == 10) { + putchar('['); + putchar('N'); + putchar('M'); + putchar('I'); + putchar(']'); + NMI = 1; + } + } + } else { + button_state = 0; + NMI = 0; + } +} + +/* Get system timer + offset (handy for lots of things)*/ +unsigned short get_timer(unsigned short offset) +{ + unsigned short r; + + /* Get system time SAFELY */ + INTCONbits.GIE = 0; + r = systimer; + INTCONbits.GIE = 1; + + /*add offset*/ + r += offset; + + return r; +} + +/* Check if timer is past given time in + * it may be maximum 30000 ticks in the future + */ +char check_timer(unsigned short t) +{ + /* calculate difference */ + INTCONbits.GIE = 0; + t -= systimer; + INTCONbits.GIE = 1; + + /* check if has passed */ + return t > 30000; +} + +void wait_timer(unsigned short time) +{ + time = get_timer(time); + while (!check_timer(time)) + ; +} + +/* SPI-bus. Should inline ? */ +unsigned char do_spi(unsigned char d) +{ + SSPBUF = d; + while (!SSPSTATbits.BF); /*Wait untill controller is ready*/ + return(SSPBUF); /*Return with received value*/ +} + +/* FPAG configuration serial interface */ +void shift_fpga(unsigned char byte) +{ + /*bit 0*/ + DIN = 0; + CCLK = 0; + if (byte & 0x80) + DIN = 1; + CCLK = 1; + + /*bit 1*/ + DIN = 0; + CCLK = 0; + if (byte & 0x40) + DIN = 1; + CCLK = 1; + + /*bit 2*/ + DIN = 0; + CCLK = 0; + if (byte & 0x20) + DIN = 1; + CCLK = 1; + + /*bit 3*/ + DIN = 0; + CCLK = 0; + if (byte & 0x10) + DIN = 1; + CCLK = 1; + + /*bit 4*/ + DIN = 0; + CCLK = 0; + if (byte & 0x08) + DIN = 1; + CCLK = 1; + + /*bit 5*/ + DIN = 0; + CCLK = 0; + if (byte & 0x04) + DIN = 1; + CCLK = 1; + + /*bit 6*/ + DIN = 0; + CCLK = 0; + if (byte & 0x02) + DIN = 1; + CCLK = 1; + + /*bit 7*/ + DIN = 0; + CCLK = 0; + if (byte & 0x01) + DIN = 1; + CCLK = 1; +} + +/* Put out a chacter to the serial port */ +void putchar(char ch) __wparam +{ + while(TXSTAbits.TRMT == 0); + TXREG = ch; + if (ch == 10) { + while(TXSTAbits.TRMT == 0); + TXREG = 13; + } +} + + + + + + diff --git a/pic/hardware.h b/pic/hardware.h new file mode 100644 index 0000000..60a40c4 --- /dev/null +++ b/pic/hardware.h @@ -0,0 +1,91 @@ +#ifndef __HARDWARE_H__ +#define __HARDWARE_H__ + +#include +#include + +/* IO defines*/ +#define PROG_B TRISAbits.TRISA2 /* FPGA config */ +#define PROG_B_VAL PORTAbits.RA2 /* FPGA config */ +#define DONE PORTBbits.RB3 /* FPGA config */ +#define INIT_B PORTAbits.RA3 /* FPGA config */ +#define CCLK LATAbits.LATA5 /* FPGA config */ +#define DIN LATBbits.LATB7 /* FPGA config */ +#define _SW0 PORTBbits.RB0 /* Switch input */ +#define _SW1 PORTBbits.RB1 /* Switch input */ +#define _M_CD TRISAbits.TRISA0 /* mmc card clock disable */ +#define _M_CS PORTCbits.RC0 /* mmc card spi select */ +#define _F_CS0 PORTAbits.RA1 /* FGPA spi0 select */ +#define SCSI_HSHAKE PORTBbits.RB5 /* SCSI data request */ +#define NMI PORTBbits.RB6 /* Programmer switch */ + +/* Some IO accessors. static inline somewhat broken with sdcc so + * I use macros... sad + */ +#define spi_enable_fpga() do { \ + _F_CS0 = 0; \ +} while(0) + +#define spi_disable_fpga() do { \ + _F_CS0 = 1; \ +} while(0) + +#define spi_enable_osd() do { \ + _F_CS1 = 0; \ +} while(0) + +#define spi_disable_osd() do { \ + _F_CS1 = 1; \ +} while(0) + +#define spi_enable_sdcard() do { \ + _M_CD = 1; \ + _M_CS = 0; \ +} while(0) + +#define spi_disable_sdcard() do { \ + _M_CS = 1; \ + do_spi(0xff); \ + _M_CD = 0; \ +} while(0) + +#define check_button(void) (!_SW0) + +#define reset_fpga() do { \ + PROG_B_VAL = 0; \ + PROG_B = 0; \ + PROG_B = 1; \ +} while(0) + +#define diskled_on() do { \ + PORTBbits.RB4 = 1; \ +} while(0) + +#define diskled_off() do { \ + PORTBbits.RB4 = 0; \ +} while(0) + +/* Functions */ +extern void hardware_init(void); + +extern unsigned short get_timer(unsigned short offset); +extern char check_timer(unsigned short t); +extern void wait_timer(unsigned short time); + +extern unsigned char do_spi(unsigned char data); +extern void shift_fpga(unsigned char data); + +/* Backbus interface defines */ +#define BB_WRITE 0x80 +#define BB_AUTOINC 0x40 + +#define BB_REG_CTRL_BASE 0x00 +#define BB_REG_CPU_BASE 0x10 +#define BB_REG_MEM_BASE 0x20 +#define BB_REG_IWM_BASE 0x28 +#define BB_REG_SCSI_BASE 0x30 +#define BB_REG_SCOPE_BASE 0x38 + + +#endif /* __HARDWARE_H__ */ + diff --git a/pic/main.c b/pic/main.c new file mode 100644 index 0000000..93d33fa --- /dev/null +++ b/pic/main.c @@ -0,0 +1,833 @@ +#include +#include +#include +#include + +#include "hardware.h" +#include "mmc.h" +#include "fat16.h" +#include "scsi.h" + +#define printf printf_tiny + +/* Sector buffer. Shared globally to save space on PIC + * + * XXX In this file to work around a missing bank select problem + */ +unsigned char secbuf[512]; + +static char hex[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c', + 'd','e','f'}; + +/* Temp buffer used for tests */ +static unsigned char c[10]; + +static unsigned char load_file(char *name, unsigned long addr) +{ + unsigned short t; + unsigned short n; + unsigned char *ptr = secbuf; + long size; + + size = fat_open(name); + if (size < 0) { + printf("File %s not found !\n", name); + return false; + } + fat_file_seek(0); + + spi_enable_fpga(); + do_spi(BB_WRITE | BB_AUTOINC | BB_REG_MEM_BASE); + do_spi((addr >> 16) & 0xff); + do_spi((addr >> 8) & 0xff); + do_spi((addr ) & 0xff); + spi_disable_fpga(); + + t = 0; + n = (size + 7) >> 3; + do { + /* read sector if 512 (64*8) bytes done */ + if ((t & 0x3f) == 0) { + if ((t>>9)&1) + diskled_on(); + else + diskled_off(); + putchar('*'); + if (!fat_file_read()) { + printf("Reading failed !\n"); + return false; + } + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_MEM_BASE | 3); + ptr = secbuf; + } + + /* send data in packets of 8 bytes */ + do_spi(*(ptr++)); + do_spi(*(ptr++)); + do_spi(*(ptr++)); + do_spi(*(ptr++)); + do_spi(*(ptr++)); + do_spi(*(ptr++)); + do_spi(*(ptr++)); + do_spi(*(ptr++)); + t++; + + /* read next sector if 512 (64*8) bytes done */ + if ((t & 0x3f) == 0) { + spi_disable_fpga(); + fat_file_next_sector(); + } + if ((t & 0x7ff) == 0) + putchar(10); + } while (t < n); + spi_disable_fpga(); + + putchar(10); + + return true; +} + +static unsigned char configure_fpga(void) +{ + unsigned short t; + unsigned short n; + unsigned char *ptr = secbuf; + long size; + + /* Reset FGPA configuration sequence */ + reset_fpga(); + + // now wait for INIT to go high + t = 50000; + while (!INIT_B) { + if (--t==0) { + printf("FPGA init is NOT high!\n"); + return false; + } + } + if (DONE) { + printf("FPGA done is high before configuration!\n"); + return false; + } + + /* Open bitstream file */ + size = fat_open("MIMIGMACBIN"); + if (size < 0) { + printf("No FPGA configuration file found!\n"); + return false; + } + fat_file_seek(0); + + /* Send all bytes to FPGA in loop */ + t = 0; + n = size >> 3; + do { + /* read sector if 512 (64*8) bytes done */ + if ((t & 0x3f) == 0) { + if ((t>>9)&1) + diskled_on(); + else + diskled_off(); + putchar('*'); + if (!fat_file_read()) { + printf("Reading failed !\n"); + return false; + } + ptr = secbuf; + } + + /* send data in packets of 8 bytes */ + shift_fpga(*(ptr++)); + shift_fpga(*(ptr++)); + shift_fpga(*(ptr++)); + shift_fpga(*(ptr++)); + shift_fpga(*(ptr++)); + shift_fpga(*(ptr++)); + shift_fpga(*(ptr++)); + shift_fpga(*(ptr++)); + t++; + + /* read next sector if 512 (64*8) bytes done */ + if ((t & 0x3f) == 0) + fat_file_next_sector(); + if ((t & 0x7ff) == 0) + putchar(10); + } while (t < n); + + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + + diskled_off(); + + /* check if DONE is high */ + if (DONE) { + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + shift_fpga(0xff); + putchar(10); + return true; + } + + printf("FPGA done is NOT high!\n"); + return false; +} + +static void print_latches(void) +{ + printf(" RW:%d U:%d L:%d ADDR: %x %x %x DATA: %x %x\n", + !!(c[5] & 0x01), + !!(c[5] & 0x02), + !!(c[5] & 0x04), + c[0], c[1], c[2], c[3], c[4]); +} + +#if 1 +static void dump_scope(void) +{ + unsigned short i; + + /* Dump scope output */ + spi_enable_fpga(); + do_spi(BB_AUTOINC | BB_REG_SCOPE_BASE | 2); + do_spi(0xff); + c[0] = do_spi(0xff); + c[1] = do_spi(0xff); + printf("sample count: %x %x\n", c[0], c[1]); + spi_disable_fpga(); + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_SCOPE_BASE | 4); + do_spi(0xff); + spi_disable_fpga(); + + spi_enable_fpga(); + do_spi(BB_REG_SCOPE_BASE | 4); + do_spi(0xff); + // for (i = 0; i < 0x200; i++) { + for (i = 0; i < (32768/8); i++) { + c[0] = do_spi(0xff); + c[1] = do_spi(0xff); + c[2] = do_spi(0xff); + c[3] = do_spi(0xff); + c[4] = do_spi(0xff); + c[5] = do_spi(0xff); + c[6] = do_spi(0xff); + c[7] = do_spi(0xff); + printf("%x: %x %x %x %x %x %x %x %x\n", i * 8, + c[0], c[1], c[2], c[3], + c[4], c[5], c[6], c[7]); + } + spi_disable_fpga(); +} +#endif + +static unsigned char check_fpga(void) +{ + spi_enable_fpga(); + do_spi(BB_AUTOINC | BB_REG_CTRL_BASE); + do_spi(0xff); /* pad */ + c[0] = do_spi(0xff); /* Read data 0 : ID */ + c[1] = do_spi(0xff); /* Read data 1 : Version */ + spi_disable_fpga(); + + printf("FPGA ID %x Version %d\n", c[0], c[1]); + if (c[0] != 0x42 && c[1] != 1) { + printf("Unsupported !\n"); + return false; + } + + /* Test CPU inteface latches */ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_AUTOINC | BB_REG_CPU_BASE); + do_spi(0xde); + do_spi(0xad); + do_spi(0xbf); + do_spi(0xf0); + do_spi(0x0d); + do_spi(0xff); + spi_disable_fpga(); + + spi_enable_fpga(); + do_spi(BB_AUTOINC | BB_REG_CPU_BASE); + do_spi(0xff); + c[0] = do_spi(0xff); + c[1] = do_spi(0xff); + c[2] = do_spi(0xff); + c[3] = do_spi(0xff); + c[4] = do_spi(0xff); + c[5] = do_spi(0xff); + spi_disable_fpga(); + + if (c[0] != 0xde || c[1] != 0xad || c[2] != 0xbe || + c[3] != 0xf0 || c[4] != 0x0d) { + printf("Latches test FAILED want:\n"); + printf(" RW:1 U:1 L:1 ADDR: de ad be DATA: f0 0d\n"); + printf("Got:\n"); + print_latches(); + return false; + } + printf("CPU interface latch test passed !\n"); + + return true; +} + +/* Bits inverted from what goes into the SPI request */ +#define SIM_CYCLE_READ 0x00 +#define SIM_CYCLE_WRITE 0x01 +#define SIM_CYCLE_UPPER 0x02 +#define SIM_CYCLE_LOWER 0x04 +#define SIM_CYCLE_WORD (SIM_CYCLE_UPPER | SIM_CYCLE_LOWER) + +static unsigned short sim_bus_cycle(unsigned long addr, + unsigned short dat, + unsigned char flags) +{ + unsigned char stat; + + /* Read value at 0x1020 */ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_AUTOINC | BB_REG_CPU_BASE); + do_spi((addr >> 16) & 0xff); + do_spi((addr >> 8) & 0xff); + do_spi((addr ) & 0xff); + do_spi((dat >> 8 ) & 0xff); + do_spi((dat ) & 0xff); + do_spi(~flags); + do_spi(0x04); /* ctrl: cycle */ + spi_disable_fpga(); + + /* Wait for stop state */ + spi_enable_fpga(); + do_spi(BB_REG_CPU_BASE + 0x7); + do_spi(0xff); + do + stat = do_spi(0xff); + while(!(stat & 0x08)); + spi_disable_fpga(); + + /* Read latches */ + spi_enable_fpga(); + do_spi(BB_AUTOINC | BB_REG_CPU_BASE | 3); + do_spi(0xff); + dat = do_spi(0xff); + dat <<= 8; + dat |= do_spi(0xff); + spi_disable_fpga(); + + return dat ; +} + +static void dump_latches(void) +{ + /* Read latches */ + spi_enable_fpga(); + do_spi(BB_AUTOINC | BB_REG_CPU_BASE); + do_spi(0xff); + c[0] = do_spi(0xff); + c[1] = do_spi(0xff); + c[2] = do_spi(0xff); + c[3] = do_spi(0xff); + c[4] = do_spi(0xff); + c[5] = do_spi(0xff); + spi_disable_fpga(); + printf(" RW:%d U:%d L:%d ADDR: %x %x %x DATA: %x %x %s\n", + !!(c[5] & 0x01), + !!(c[5] & 0x02), + !!(c[5] & 0x04), + c[0], c[1], c[2], c[3], c[4], + SCSI_HSHAKE ? " [SCSI]" : ""); +} + +static void run_to(unsigned long addr) +{ + unsigned char stat; + + /* Set breakpoint */ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_AUTOINC | BB_REG_CPU_BASE | 8); + do_spi((addr >> 16) & 0xff); + do_spi((addr >> 8) & 0xff); + do_spi((addr ) & 0xff); + spi_disable_fpga(); + + /* Start CPU with BP */ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_CPU_BASE | 6); + do_spi(0x42); /* RUN + BKEN */ + spi_disable_fpga(); + + /* Wait for stop state */ + spi_enable_fpga(); + do_spi(BB_REG_CPU_BASE + 0x7); + do_spi(0xff); + do { + if (SCSI_HSHAKE) { + spi_disable_fpga(); + do_scsi(); + spi_enable_fpga(); + do_spi(BB_REG_CPU_BASE + 0x7); + do_spi(0xff); + } + stat = do_spi(0xff); + } while(!(stat & 0x08)); + spi_disable_fpga(); +} + +static void step(unsigned short n) +{ + unsigned short i; + + for (i = 0; i < n; i++) { + /* Step CPU */ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_CPU_BASE | 6); + do_spi(0x82); /* RUN + STEP */ + spi_disable_fpga(); + +#if 0 /* Need in theory but CPU never fast enough */ + /* Wait for stop state */ + spi_enable_fpga(); + do_spi(BB_REG_CPU_BASE + 0x7); + do_spi(0xff); + do + stat = do_spi(0xff); + while(!(stat & 0x08)); + spi_disable_fpga(); +#endif + } +} + +void main(void) +{ + unsigned char rc; + unsigned short i; + unsigned short v, v2; + + hardware_init(); + + printf("Initializing MMC...\n"); + + rc = mmc_init(); + if (!rc) + return; + + printf("Initializing FAT...\n"); + + rc = fat_init(); + if (!rc) + return; + + printf("Configuring FPGA...\n"); + + if (!configure_fpga()) + return; + + // printf("Checking FPGA...\n"); + if (!check_fpga()) + return; + + printf("Loading ROM file...\n"); + if (!load_file("MIMIGMACROM", 0x200000)) + return; +#if 0 + printf("Loading Screenshot...\n"); + if (!load_file("SCRNSHOTRAW", 0x1fa700)) + return; +#endif + + printf("Opening SCSI disk image...\n"); + scsi_open(); + +#if 0 + /* Start scope */ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_SCOPE_BASE); + do_spi(0x02); + spi_disable_fpga(); + + /* Read ROM first words */ + v = sim_bus_cycle(0, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + v2 = sim_bus_cycle(2, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + printf("Mem 0: %x %x\n", v, v2); + v = sim_bus_cycle(4, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + v2 = sim_bus_cycle(6, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + printf("Mem 4: %x %x\n", v, v2); + + /* Read ROM las words */ + v = sim_bus_cycle(0x41fff8, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + v2 = sim_bus_cycle(0x41fffa, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + printf("ROM end 0: %x %x\n", v, v2); + v = sim_bus_cycle(0x41fffc, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + v2 = sim_bus_cycle(0x41fffe, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + printf("ROM end 4: %x %x\n", v, v2); +#endif + + /* Un-reset CPU */ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_CTRL_BASE | 2); + do_spi(0x01); + spi_disable_fpga(); + +#if 0 + { + unsigned char b,oldb,t; + + spi_enable_fpga(); + do_spi(BB_REG_CTRL_BASE | 6); + do_spi(0xff); + oldb = 0; + t = 0, i = 0; + while (i < 512) { + oldb = b; + b = do_spi(0xff); + if (b == oldb && t < 0xff) + t++; + else { + secbuf[i++] = b; + secbuf[i++] = t; + t = 0; + } + } + spi_disable_fpga(); + for (i = 0; i < 512; i+=2) + printf("%x %x\n", secbuf[i], secbuf[i+1]); + } +#endif + +#if 0 + + + // printf("Running to main()\n"); + // run_to(0x4010c8); + printf("Stepping...()\n"); + dump_latches(); + step(1); + dump_latches(); + step(1); + + for (i = 0; i < 1000; i++) { + step(1); + dump_latches(); + } + + for (;;) + ; +#endif +#if 0 + for (;;) { + printf("Running to exception \n"); + run_to(0x401336); + dump_latches(); + step(1); + } +#endif + +#if 0 + printf("Running to P_mBootBeep...\n"); + run_to(0x40028a); + dump_latches(); + step(1); + dump_latches(); + printf("Running to IWM init...\n"); + run_to(0x4000fc); + dump_latches(); + step(1); + dump_latches(); + printf("Running to P_ChecksumRomAndTestMemory...\n"); + run_to(0x400d76); + dump_latches(); + step(1); + dump_latches(); + printf("Running to P_BootPart2 (fake hit)...\n"); + run_to(0x400352); + dump_latches(); + step(1); + dump_latches(); + printf("Running to P_BootPart2...\n"); + run_to(0x400352); + dump_latches(); + step(1); + dump_latches(); +#endif + +#if 0 + /* Read video first words */ + v = sim_bus_cycle(0x1fa700, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + v2 = sim_bus_cycle(0x1fa702, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + printf("Vid 0: %x %x\n", v, v2); + v = sim_bus_cycle(0x1fa704, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + v2 = sim_bus_cycle(0x1fa706, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + printf("Vid 4: %x %x\n", v, v2); + printf("Running to setting MemTop...\n"); + run_to(0x40037e); + dump_latches(); + step(1); + dump_latches(); + step(1); + dump_latches(); + step(1); + dump_latches(); + step(1); + dump_latches(); + v = sim_bus_cycle(0x108, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + printf("Detected memory size: 0x%x0000 bytes\n", v); + printf("Running to L53 (CPUflag test)...\n"); + run_to(0x400584); + dump_latches(); + for (i = 0; i < 50; i++) { + step(1); + dump_latches(); + } + printf("Running to Bsr P5...\n"); + run_to(0x4005fa); + dump_latches(); + printf("Running to call to _InitResources...\n"); + run_to(0x400600); + dump_latches(); + step(1); + dump_latches(); + printf("Running to P_mInitQueue...\n"); + run_to(0x401a38); + dump_latches(); + step(1); + dump_latches(); + printf("Running to P_InitSCC...\n"); + run_to(0x40082c); + dump_latches(); + step(1); + dump_latches(); + printf("Running to P_InitKeyboard...\n"); + run_to(0x402568); + dump_latches(); + step(1); + dump_latches(); + printf("Running to P_mInitIOMgr...\n"); + run_to(0x40087c); + dump_latches(); + step(1); + dump_latches(); +#endif +#if 0 + + printf("Running to P252...\n"); + run_to(0x407d40); + dump_latches(); + + /* HWCfgFlags at 0xb22 bit 7 is SCSI */ + v = sim_bus_cycle(0xb22, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + printf("HWCfgFlags=%x\n", v >> 8); + v = sim_bus_cycle(0xb2e, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + printf("SCSIDrvrs =%x\n", v >> 8); + + printf("Running to SCSI driver jump...\n"); + run_to(0x407dc4); + dump_latches(); + printf("Running to SCSI driver csum...\n"); + run_to(0x1d36); + dump_latches(); +#endif + +#if 0 + printf("Running to after plot disk...\n"); + run_to(0x4006e4); + dump_latches(); + v = sim_bus_cycle(0x30a, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + v2 = sim_bus_cycle(0x30c, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + printf("DrvQHdr.Head=%x %x\n", v, v2); +#endif + +#if 0 + printf("Running to P_mInitIOMgr..x1...\n"); + run_to(0x40089c); + dump_latches(); + step(1); + dump_latches(); + printf("Running to P_mInitIOMgr..x2...\n"); + run_to(0x4008a6); + dump_latches(); + step(1); + dump_latches(); + printf("Running to P_mInitIOMgr..x3...\n"); + run_to(0x4008ac); + dump_latches(); +#endif + +#if 0 + for (i = 0; i < 1000; i++) { + step(1); + dump_latches(); + } +#endif + /* Run CPU */ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_CPU_BASE | 6); + do_spi(0x02); + spi_disable_fpga(); + + i = 0; + for (;;) { + /* This is VERY timing sensitive, the ROM selection + * timeout is very short and the fact that we run at + * twice the speed doesn't help. + * We could improve that by pre-setting the SPI to point + * to the SCSI lines register and just read from them, + * or we could have some of the selection logic moved to + * the FPGA + * + * Better HACK: The FPGA could delay CPU read cycles + * from the bus status register when SEL + * is set by a significant amount in the + * FPGA thus allowing a lot more time for + * the selection process + */ + if (SCSI_HSHAKE) + do_scsi(); + } + +#if 0 + printf("Running to E_Sony_Open...\n"); + run_to(0x417d9e); + dump_latches(); + step(1); + dump_latches(); + printf("Running to E_Sony_Open:17DFA (_VInstall)...\n"); + run_to(0x417dfa); + dump_latches(); + step(1); + dump_latches(); + printf("Running to E_Sony_Open:17E12 (bsr P721)...\n"); + run_to(0x417e12); + dump_latches(); + for (i = 0; i < 1000; i++) { + step(1); + dump_latches(); + } + printf("Running to E_Sony_Open:17E16 (bsr P720)...\n"); + run_to(0x417e12); + dump_latches(); + for (i = 0; i < 100; i++) { + step(1); + dump_latches(); + } + printf("Running to E_Sony_Open:17E24 (bsr P715)...\n"); + run_to(0x417e12); + dump_latches(); + step(1); + dump_latches(); + step(1); + dump_latches(); + for (i = 0; i < 1000; i++) { + step(1); + dump_latches(); + } + printf("Running to L58...\n"); + run_to(0x400692); + dump_latches(); + step(1); + dump_latches(); + for (i = 0; i < 1000; i++) { + step(1); + dump_latches(); + } +#endif + + +#if 0 + for (i = 0; i < 100; i++) { + wait_timer(100); + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_CPU_BASE | 6); + do_spi(0x01); /* RUN + STEP */ + spi_disable_fpga(); + + /* We shall wait for cycle to complete but we know + * how slow our SPI is ... + */ + dump_latches(); + + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_CPU_BASE | 6); + do_spi(0x02); /* RUN + STEP */ + spi_disable_fpga(); + } + + /* Stop CPU */ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_CPU_BASE | 6); + do_spi(0x01); /* RUN + STEP */ + spi_disable_fpga(); +#endif + +#if 0 + step(10000); + step(10000); + step(10000); + step(10000); + step(10000); + step(10000); + step(10000); + step(10000); + step(10000); + step(10000); + step(10000); +#endif + +#if 0 + /* Stop scope */ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_SCOPE_BASE); + do_spi(0x00); + spi_disable_fpga(); + + printf("Reading 0x1020 via SPI initiated read cycle: "); + v = sim_bus_cycle(0x1020, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD); + printf("%x\n", v); + + /* Reading memory via RAM fifo */ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_AUTOINC | BB_REG_MEM_BASE); + do_spi(0x20); /* ROM base */ + do_spi(0x10); + do_spi(0x00); + spi_disable_fpga(); + + spi_enable_fpga(); + do_spi(BB_REG_MEM_BASE); + do_spi(0xff); + do_spi(0xff); /* one more dummy cycle with RAM fifo */ + for (i = 0; i < 0x40; i++) { + printf(" %x", do_spi(0xff)); + if ((i & 0xf) == 0xf) + printf("\n"); + } + printf("\n"); +#endif + +#if 0 + /* Start CPU */ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_CPU_BASE | 6); + do_spi(0x02); /* RUN */ + spi_disable_fpga(); +#endif + +#if 0 + for (;;) + ; +#endif +} diff --git a/pic/minimigmac.piklab b/pic/minimigmac.piklab new file mode 100644 index 0000000..a7e46ff --- /dev/null +++ b/pic/minimigmac.piklab @@ -0,0 +1,74 @@ + + + + 18F252 + sdcc + custom + + main.c + hardware.c + /usr/share/sdcc/lib/pic16/libc18f.lib + /usr/share/sdcc/lib/pic16/libio18f252.lib + mmc.c + fat16.c + 18f252_mod.lkr + scsi.c + + /home/benh/hackplace/minimigmac/pic/18f252_mod.lkr + + 0.1 + executable + + + + true + + -m%FAMILY + -%DEVICE + -V + --debug + -I$(SRCPATH) + -c + %I + --obanksel=2 + --denable-peeps + --optimize-cmp + --optimize-df + + + $(SRCPATH) + + + + + false + + -m%FAMILY + -%DEVICE + -V + --debug + -Wl-c + -Wl-m + $LKR(-Wl-s%LKR) + -I$(SRCPATH) + -o%O + %OBJS + %LIBS + + + $(SRCPATH) + + + inhx32 + + + false + + -c + %O + %OBJS + %LIBS + + + + diff --git a/pic/mmc.c b/pic/mmc.c new file mode 100644 index 0000000..9c1b300 --- /dev/null +++ b/pic/mmc.c @@ -0,0 +1,402 @@ +/* + * MMC/SD-Card driver + * + * Essentially copied from Minimig + * + * Current implementation is polled & synchronous + */ + +#include + +#include "hardware.h" +#include "mmc.h" +#include "mmc_cmds.h" + +/* Enable / Disable Debug info */ +#undef DEBUG_SDMMC + +/* MMC, or SD Card Specifification V1, V2, SDHC */ +#define MMC_CARD_NONE 0x00 +#define MMC_CARD_MMC 0x01 +#define MMC_CARD_SD_RAW_SPEC_1 0x02 +#define MMC_CARD_SD_RAW_SPEC_2 0x04 +#define MMC_CARD_SD_RAW_SPEC_SDHC 0x08 + +static unsigned char mmc_card; +static unsigned char mmc_crc_7; +static unsigned int mmc_timeout; +static unsigned char mmc_resp[5]; + +#ifdef DEBUG_SDMMC +#define pr_debug(args...) printf_tiny(args) +#else +#define pr_debug(args...) +#endif + +/* Calculate CRC7 checksum */ +static void mmc_add_crc7(unsigned char c) +{ + unsigned char i; + + for (i = 0; i < 8; i++) { + mmc_crc_7 <<= 1; + if (c & 0x80) + mmc_crc_7 ^= 0x09; + if (mmc_crc_7 & 0x80) + mmc_crc_7 ^= 0x09; + c <<= 1; + } +} + +/* Send a byte and calculate CRC */ +static void mmc_spi_crc(unsigned char cmd) +{ + do_spi(cmd); + mmc_add_crc7(cmd); +} + +/* Send a command to the SDcard */ +static void mmc_cmd_r0(char cmd, unsigned short addr_h, unsigned short addr_l) +{ + mmc_crc_7 = 0; + + /* Flush SPI-bus */ + do_spi(0xff); + + /* Send command */ + mmc_spi_crc(cmd); + + /* Send address */ + mmc_spi_crc(addr_h >> 8); + mmc_spi_crc(addr_h & 0xff); + mmc_spi_crc(addr_l >> 8); + mmc_spi_crc(addr_l & 0xff); + + /* Cook and send CRC */ + mmc_crc_7 <<= 1; + mmc_crc_7++; + do_spi(mmc_crc_7); +} + +/* Send a command to the SDcard, a one byte response is expected */ +static void mmc_cmd_r1(char cmd, unsigned short addr_h, unsigned short addr_l) +{ + unsigned char i = 100; + + mmc_cmd_r0(cmd, addr_h, addr_l); + do + mmc_resp[0] = do_spi(0xff); + while (mmc_resp[0] == 0xff && --i); +} + +/* Send a command to the SDcard, a two byte response is expected */ +static void mmc_cmd_r2(char cmd, unsigned short addr_h, unsigned short addr_l) +{ + unsigned char i = 100; + + mmc_cmd_r0(cmd, addr_h, addr_l); + do + mmc_resp[0] = do_spi(0xff); + while (mmc_resp[0] == 0xff && --i); + mmc_resp[1] = do_spi(0xff); +} + +/* Send a command to the SDcard, a five byte response is expected */ +static void mmc_cmd_r3(char cmd, unsigned short addr_h, unsigned short addr_l) +{ + unsigned char i = 100; + + mmc_cmd_r0(cmd, addr_h, addr_l); + do + mmc_resp[0] = do_spi(0xff); + while (mmc_resp[0] == 0xff && --i); + mmc_resp[1] = do_spi(0xff); + mmc_resp[2] = do_spi(0xff); + mmc_resp[3] = do_spi(0xff); + mmc_resp[4] = do_spi(0xff); +} + +/* Enable the MMC/SD card correctly */ +unsigned char mmc_init(void) +{ + unsigned short lp; + + /* Set SPI clock .. Might need to be smarter at changing the + * SPI clock based on what device is enabled... + */ + SSPCON1 = 0x22; //spiclk = Fosc/64 (init clock 100-400 kHz) + + _M_CD = 0; + _M_CS = 1; + wait_timer(10); + + _M_CD = 1; /* Enable clock*/ + _M_CS = 1; /* SDcard Disabled */ + + /* Set SDcard in SPI-Mode, Reset*/ + /* (10 * 8bits = 80 clockpulses) */ + for (lp = 0; lp < 10; lp++) + do_spi(0xff); + + /* Delay for a lot of milliseconds (least 16 bus clock cycles) */ + for (lp=0; lp < 56000; lp++) + ; /* PRAY sdcc doesn't optimize that out ! + Should use timer instead ! */ + wait_timer(10); + + /* SDcard Enabled */ + _M_CS = 0; + + /* No card detected by default */ + mmc_card = MMC_CARD_NONE; + + /* CMD0: Reset all cards to IDLE state */ + mmc_cmd_r1(CMD0, 0, 0); + if (mmc_resp[0] != 0x01) { + pr_debug("No card detected ! (resp=%x)\n", mmc_resp[0]); + goto fail; + } + + /* Default to MMC */ + mmc_card = MMC_CARD_MMC; + + /* Check for SD Card V2 */ + /* (Voltage 2.7V - 3.6V, 0xaa test pattern) */ + mmc_cmd_r3(CMD8, 0x0000, 0x01aa); + if (!(mmc_resp[0] & 0x04)) { + if (mmc_resp[3] == 0x01 && mmc_resp[4] == 0xaa) { + pr_debug("SD card V2 detected, possible SDHC\n"); + mmc_card |= MMC_CARD_SD_RAW_SPEC_2; + } else { + pr_debug("Error detecting SD V2 card\n"); + goto fail; + } + } else { + /* Check for SD card V1 */ + mmc_cmd_r1(CMD55, 0, 0); + mmc_cmd_r1(CMD41, 0, 0); + if (!(mmc_resp[0] & 0x04)) { + pr_debug("SD card V1 detected\n"); + mmc_card |= MMC_CARD_SD_RAW_SPEC_1; + } else + pr_debug("MMC-card detected\n"); + } + + /* Wait for card to get ready */ + mmc_timeout = 0; + while (1) { + if (mmc_card & (MMC_CARD_SD_RAW_SPEC_1 | MMC_CARD_SD_RAW_SPEC_2)) { + /* When SD Card detected */ + lp = 0x0; + if (mmc_card & MMC_CARD_SD_RAW_SPEC_2) + lp = 0x4000; + /* Activate SD card init process */ + mmc_cmd_r1(CMD55, 0, 0); + mmc_cmd_r1(CMD41, lp, 0); + } else { + /* Activate the MMC cards init process */ + mmc_cmd_r1(CMD1, 0, 0); + } + + /* Check if card is idle */ + if (mmc_resp[0] == 0x00) + break; + mmc_timeout++; + if (mmc_timeout == 1000) { + pr_debug("SD/MMC ACMD41/CMD1 response timeout...\n"); + goto fail; + } + } + + /* Check for SDHC card */ + if (mmc_card & MMC_CARD_SD_RAW_SPEC_2) { + /* Get operating conditions */ + mmc_cmd_r3(CMD58, 0, 0); + if (mmc_resp[0]) { + pr_debug("SD/MMC: Failed to get operating conditions\n"); + goto fail; + } + + if (mmc_resp[1] & 0x40) { + pr_debug("SDHC Card Detected\n"); + mmc_card |= MMC_CARD_SD_RAW_SPEC_SDHC; + } + } + + /* Set block size to 512 bytes */ + mmc_timeout = 0; + while(1) { + mmc_cmd_r1(CMD16, 0x0000, 0x0200); + if (!mmc_resp[0]) + break; + mmc_timeout++; + if(mmc_timeout == 100) { + pr_debug("Set block size to 512 timeout\n"); + goto fail; + } + } + + spi_disable_sdcard(); + + /* XXX Stuff below from minimig... it's strange... it sets SSPCON1 to a + * different speed from what is done in hardware_init.c for everybody + */ + // TODO: Change speed for card access + SSPCON1 = 0x20; //spiclk = Fosc/4 (5 MHz) + return true; + fail: + mmc_card = MMC_CARD_NONE; + spi_disable_sdcard(); + return false; +} + +/* Read single block (with block-size set by CMD16 to 512 by default) */ +unsigned char mmc_read(unsigned long lba, unsigned char *out_data) +{ + unsigned short upper_lba, lower_lba; + unsigned char i; + unsigned char *p; + + /* !SDHC uses byte address instead of LBA */ + if (!(mmc_card & MMC_CARD_SD_RAW_SPEC_SDHC)) + lba <<= 9; + + pr_debug("SD/MMC: Reading LBA 0x%lx\n", lba); + + upper_lba = (unsigned short)(lba>>16); + lower_lba = (unsigned short)lba; + + spi_enable_sdcard(); + + mmc_cmd_r1(CMD17, upper_lba, lower_lba); + + /* Exit if invalid response*/ + if (mmc_resp[0]) { + pr_debug("SD/MMC CMD17: invalid response %x\n", mmc_resp[0]); + goto fail; + } + + /* Wait for start of data transfer with timeout */ + mmc_timeout = 0; + while(do_spi(0xff) != 0xfe) { + if (mmc_timeout++ >= 50000) { + pr_debug("SD/MMC CMD17: no data token\n"); + goto fail; + } + } + + /* Read data and exit OK */ + p = out_data; + for (i = 0; i < 128; i++) { + SSPBUF = 0xff; + while (!SSPSTATbits.BF) + ; + *(p++) = SSPBUF; + SSPBUF = 0xff; + while (!SSPSTATbits.BF) + ; + *(p++) = SSPBUF; + SSPBUF = 0xff; + while (!SSPSTATbits.BF) + ; + *(p++) = SSPBUF; + SSPBUF = 0xff; + while (!SSPSTATbits.BF) + ; + *(p++) = SSPBUF; + } + + /* XXX Todo: check CRC */ + do_spi(0xff); //Read CRC lo byte + do_spi(0xff); //Read CRC hi byte + + spi_disable_sdcard(); + return true; + fail: + spi_disable_sdcard(); + return false; +} + + + +/* Write: 512 Byte-Mode, this will not work (read MMC and SD-card specs) with any other + * sector/block size + */ +unsigned char mmc_write(unsigned long lba, unsigned char *in_data) +{ + unsigned short upper_lba, lower_lba; + unsigned char i; + unsigned char *p; + + /* !SDHC uses byte address instead of LBA */ + if (!(mmc_card & MMC_CARD_SD_RAW_SPEC_SDHC)) + lba <<= 9; + + pr_debug("SD/MMC: Writing LBA 0x%lx\n", lba); + + upper_lba = (unsigned short)(lba>>16); + lower_lba = (unsigned short)lba; + + spi_enable_sdcard(); + + mmc_cmd_r1(CMD24, upper_lba, lower_lba); + + /* Exit if invalid response*/ + if (mmc_resp[0]) { + pr_debug("SD/MMC CMD24: invalid response %x\n", mmc_resp[0]); + goto fail; + } + + do_spi(0xff); //One byte gap + do_spi(0xfe); //Send Data token + + /* Send bytes for sector */ + p = in_data; + for (i = 0; i < 128; i++) { + SSPBUF = *(p++); + while (!SSPSTATbits.BF) + ; + SSPBUF = *(p++); + while (!SSPSTATbits.BF) + ; + SSPBUF = *(p++); + while (!SSPSTATbits.BF) + ; + SSPBUF = *(p++); + while (!SSPSTATbits.BF) + ; + } + + /* XXX Todo: Send proper CRC ? */ + do_spi(0xff); //Send CRC lo byte + do_spi(0xff); //Send CRC hi byte + + /* Read packet response */ + i = do_spi(0xff); + + /* Status codes + * 010 = Data accepted + * 101 = Data rejected due to CRC error + * 110 = Data rejected due to write error + */ + i &= 0b00011111; + if (i != 0b00000101) { + pr_debug("SD/MMC CMD24: write error %x\n", i); + goto fail; + } + + mmc_timeout = 0; + /* Wait until the card has finished writing the data */ + while (do_spi(0xff) == 0x00) { + if (mmc_timeout++ >= 50000) { + pr_debug("SD/MMC CMD24: busy wait timeout\n"); + goto fail; + } + } + spi_disable_sdcard(); + return true; + fail: + spi_disable_sdcard(); + return false; +} + diff --git a/pic/mmc.h b/pic/mmc.h new file mode 100644 index 0000000..611005e --- /dev/null +++ b/pic/mmc.h @@ -0,0 +1,8 @@ +#ifndef __MMC_H__ +#define __MMC_H__ + +unsigned char mmc_init(void); +unsigned char mmc_read(unsigned long lba, unsigned char *out_data); +unsigned char mmc_write(unsigned long lba, unsigned char *in_data); + +#endif diff --git a/pic/mmc_cmds.h b/pic/mmc_cmds.h new file mode 100644 index 0000000..00678a5 --- /dev/null +++ b/pic/mmc_cmds.h @@ -0,0 +1,99 @@ +#ifndef __MMC_CMDS_H__ +#define __MMC_CMDS_H__ + +/* MMC command set */ +#define CMD0 0x40 /*Resets the multimedia card*/ +#define CMD1 0x41 /*Activates the card's initialization process*/ +#define CMD2 0x42 /*--*/ +#define CMD3 0x43 /*--*/ +#define CMD4 0x44 /*--*/ +#define CMD5 0x45 /*reseved*/ +#define CMD6 0x46 /*reserved*/ +#define CMD7 0x47 /*--*/ +#define CMD8 0x48 /* Sends SD Memory Card interface condition, + which includes host supply voltage information + and asks the card whether card supports voltage. + Reserved bits shall be set to '0'.*/ +#define CMD9 0x49 /*CSD : Ask the selected card to send its card + specific data*/ +#define CMD10 0x4a /*CID : Ask the selected card to send its card + identification*/ +#define CMD11 0x4b /*--*/ +#define CMD12 0x4c /*--*/ +#define CMD13 0x4d /*Ask the selected card to send its status register*/ +#define CMD14 0x4e /*--*/ +#define CMD15 0x4f /*--*/ +#define CMD16 0x50 /*Select a block length (in bytes) for all following + block commands (Read:between 1-512 and Write:only 512)*/ +#define CMD17 0x51 /*Reads a block of the size selected by the SET_BLOCKLEN + command, the start address and block length must be set + so that the data transferred will not cross a physical + block boundry*/ +#define CMD18 0x52 /*--*/ +#define CMD19 0x53 /*reserved*/ +#define CMD20 0x54 /*--*/ +#define CMD21 0x55 /*reserved*/ +#define CMD22 0x56 /*reserved*/ +#define CMD23 0x57 /*reserved*/ +#define CMD24 0x58 /*Writes a block of the size selected by CMD16, the start + address must be alligned on a sector boundry, the block + length is always 512 bytes*/ +#define CMD25 0x59 /*--*/ +#define CMD26 0x5a /*--*/ +#define CMD27 0x5b /*Programming of the programmable bits of the CSD*/ +#define CMD28 0x5c /*If the card has write protection features, this command + sets the write protection bit of the addressed group. + The porperties of the write protection are coded in + the card specific data (WP_GRP_SIZE)*/ +#define CMD29 0x5d /*If the card has write protection features, this command + clears the write protection bit of the addressed group*/ +#define CMD30 0x5e /*If the card has write protection features, this command + asks the card to send the status of the write protection + bits. 32 write protection bits (representing 32 write + protect groups starting at the specific address) followed + by 16 CRD bits are transferred in a payload format via + the data line*/ +#define CMD31 0x5f /*reserved*/ +#define CMD32 0x60 /*sets the address of the first sector of the erase group*/ +#define CMD33 0x61 /*Sets the address of the last sector in a cont. range + within the selected erase group, or the address of a + single sector to be selected for erase*/ +#define CMD34 0x62 /*Removes on previously selected sector from the erase + selection*/ +#define CMD35 0x63 /*Sets the address of the first erase group within a + range to be selected for erase*/ +#define CMD36 0x64 /*Sets the address of the last erase group within a + continuos range to be selected for erase*/ +#define CMD37 0x65 /*Removes one previously selected erase group from the + erase selection*/ +#define CMD38 0x66 /*Erases all previously selected sectors*/ +#define CMD39 0x67 /*--*/ +#define CMD40 0x68 /*--*/ +#define CMD41 0x69 /*reserved*/ +#define CMD42 0x6a /*reserved*/ +#define CMD43 0x6b /*reserved*/ +#define CMD44 0x6c /*reserved*/ +#define CMD45 0x6d /*reserved*/ +#define CMD46 0x6e /*reserved*/ +#define CMD47 0x6f /*reserved*/ +#define CMD48 0x70 /*reserved*/ +#define CMD49 0x71 /*reserved*/ +#define CMD50 0x72 /*reserved*/ +#define CMD51 0x73 /*reserved*/ +#define CMD52 0x74 /*reserved*/ +#define CMD53 0x75 /*reserved*/ +#define CMD54 0x76 /*reserved*/ +#define CMD55 0x77 /*Indicates to the card that the next command is an + application specific command rather than a standard + command*/ +#define CMD56 0x78 /*reserved*/ +#define CMD57 0x79 /*reserved*/ +#define CMD58 0x7a /*reserved*/ +#define CMD59 0x7b /*Turns the CRC option ON or OFF. A '1' in the CRC option + bit will turn the option ON, a '0' will turn it OFF*/ +#define CMD60 0x7c /*--*/ +#define CMD61 0x7d /*--*/ +#define CMD62 0x7e /*--*/ +#define CMD63 0x7f /*--*/ + +#endif /* __MMC_CMDS_H__ */ diff --git a/pic/scsi.c b/pic/scsi.c new file mode 100644 index 0000000..4ca1a90 --- /dev/null +++ b/pic/scsi.c @@ -0,0 +1,392 @@ +#include +#include +#include +#include + +#include "hardware.h" +#include "mmc.h" +#include "fat16.h" +#include "scsi.h" + +/* SCSI Backbus interface */ +#define BB_REG_LINES 0 +#define BB_LINES_BSY 7 +#define BB_LINES_SEL 6 +#define BB_LINES_ACK 5 +#define BB_LINES_ATN 4 +#define BB_LINES_RST 3 + +#define BB_REG_ASSERT 1 +#define BB_ASSERT_BSY 7 +#define BB_ASSERT_SEL 6 +#define BB_ASSERT_CD 5 +#define BB_ASSERT_IO 4 +#define BB_ASSERT_MSG 3 +#define BB_ASSERT_REQ 2 +#define BB_ASSERT_RST 1 +#define BB_ASSERT_AUTO 0 /* Automatic mode */ + +#define BB_REG_ODATA 2 +#define BB_REG_IDATA 3 +#define BB_REG_ACNT_HI 4 +#define BB_REG_ACNT_LO 5 + +#define MASK(bit) (1u << (bit)) + +static unsigned char scsi_cmd[12]; +static unsigned char scsi_assert; +static unsigned long scsi_size; + +#define scsi_dbg(fmt...) + +void scsi_open(void) +{ + long size; + + size = fat_open("MACHD IMG"); + if (size < 0) { + printf("File MACHD.IMG not found !\n"); + return; + } + scsi_size = size; + fat_file_seek(0); +} + +static unsigned char scsi_get_lines(void) +{ + unsigned char lines; + + spi_enable_fpga(); + do_spi(BB_REG_SCSI_BASE | BB_REG_LINES); + do_spi(0xff); + lines = do_spi(0xff); + spi_disable_fpga(); + + return lines; +} + +static void scsi_set_assert(void) +{ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_SCSI_BASE | BB_REG_ASSERT); + + + do_spi(scsi_assert); + spi_disable_fpga(); +} + +static unsigned char scsi_get_data(void) +{ + unsigned char lines; + + spi_enable_fpga(); + do_spi(BB_REG_SCSI_BASE | BB_REG_ODATA); + do_spi(0xff); + lines = do_spi(0xff); + spi_disable_fpga(); + + return lines; +} + +static void scsi_set_data(unsigned char val) +{ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_SCSI_BASE | BB_REG_IDATA); + do_spi(val); + spi_disable_fpga(); +} + +static void scsi_set_autocnt(unsigned short cnt) +{ + spi_enable_fpga(); + do_spi(BB_WRITE | BB_AUTOINC | BB_REG_SCSI_BASE | BB_REG_ACNT_HI); + do_spi(cnt >> 8); + do_spi(cnt & 0xff); + spi_disable_fpga(); +} + +static unsigned char scsi_do_data_in(unsigned short len) +{ + unsigned short i = 0; + + scsi_assert |= MASK(BB_ASSERT_AUTO) | MASK(BB_ASSERT_IO); + scsi_set_assert(); + scsi_set_autocnt(len); + + spi_enable_fpga(); + do_spi(BB_WRITE | BB_REG_SCSI_BASE | BB_REG_IDATA); + while(len--) { + do_spi(secbuf[i++]); + /* Handhake goes down approx. 160ns after the above + * and we then need to wait for it to go back up, + * the PIC executes an instruction in about 200ns so + * we shouldn't need any delay here before we test it + */ + while(!SCSI_HSHAKE); + } + scsi_assert &= ~MASK(BB_ASSERT_AUTO); + scsi_set_assert(); + return 0; +} + +static unsigned char scsi_do_data_out(unsigned short len) +{ + unsigned short i = 0; + + scsi_assert |= MASK(BB_ASSERT_AUTO); + scsi_assert &= ~MASK(BB_ASSERT_IO); + scsi_set_assert(); + scsi_set_autocnt(len); + + spi_enable_fpga(); + do_spi(BB_REG_SCSI_BASE | BB_REG_ODATA); + /* See handshake timing comment in scsi_do_data_in() */ + while(!SCSI_HSHAKE); + do_spi(0xff); + while(!SCSI_HSHAKE); + do_spi(0xff); + while(!SCSI_HSHAKE); + do_spi(0xff); + while(len--) { + while(!SCSI_HSHAKE); + secbuf[i++] = do_spi(0xff); + } + scsi_assert &= ~MASK(BB_ASSERT_AUTO); + scsi_set_assert(); + return 0; +} + +static unsigned char scsi_do_read(unsigned long lba, + unsigned short cnt) +{ + unsigned char rc = true; + + if (scsi_size == 0) + return 1; + diskled_on(); + fat_file_seek(lba); + while(cnt--) { + if (rc) + rc = fat_file_read(); + if (!rc) { + diskled_off(); + return 1; + } + scsi_do_data_in(512); + if (cnt) + rc = fat_file_next_sector(); + } + diskled_off(); + return 0; +} + +static unsigned char scsi_do_write(unsigned long lba, + unsigned short cnt) +{ + unsigned char rc; + + if (scsi_size == 0) + return 1; + diskled_on(); + fat_file_seek(lba); + while(cnt--) { + scsi_do_data_out(512); + rc = fat_file_write(); + if (rc && cnt) + rc = fat_file_next_sector(); + if (!rc) { + diskled_off(); + return 1; + } + } + diskled_off(); + return 0; +} + +static unsigned char scsi_do_cmd(void) +{ + unsigned long lba; + unsigned short cnt; + + switch(scsi_cmd[0]) { + case 0x00: + printf("scsi: TEST_UNIT_READY !\n"); + return 0; + case 0x03: + printf("scsi: REQUEST_SENSE !\n"); + fat_inval_cache(); + memset(secbuf, 0, 13); + secbuf[0] = 0xf0; + return scsi_do_data_in(13); + case 0x04: + printf("scsi: FORMAT_UNIT !\n"); + return 1; + case 0x08: + lba = scsi_cmd[1] & 0x1f; + lba = (lba << 8) | scsi_cmd[2]; + lba = (lba << 8) | scsi_cmd[3]; + cnt = scsi_cmd[4]; + if (!cnt) + cnt = 256; + printf("scsi: READ6 (lba=%lx cnt=%x) !\n", lba, cnt); + return scsi_do_read(lba, cnt); + case 0x0a: + lba = scsi_cmd[1] & 0x1f; + lba = (lba << 8) | scsi_cmd[2]; + lba = (lba << 8) | scsi_cmd[3]; + cnt = scsi_cmd[4]; + if (!cnt) + cnt = 256; + printf("scsi: WRITE6 (lba=%lx cnt=%x) !\n", lba, cnt); + return scsi_do_write(lba, cnt); + case 0x12: + printf("scsi: INQUIRY !\n"); + fat_inval_cache(); + memset(secbuf, 0, 36); + memcpy(secbuf + 8, " SEAGATE", 8); + memcpy(secbuf + 16, " ST225N", 16); + secbuf[4] = 32; + cnt = scsi_cmd[4]; + if (cnt > 36) + cnt = 36; + return scsi_do_data_in(cnt); + case 0x15: + printf("scsi: MODE_SELECT !\n"); + return 1; + case 0x1a: + printf("scsi: MODE_SENSE !\n"); + return 1; + case 0x1b: + printf("scsi: START_STOP !\n"); + return 1; + case 0x25: + printf("scsi: READ_CAPACITY !\n"); + return 1; + case 0x28: + printf("scsi: READ10 !\n"); + lba = scsi_cmd[2]; + lba = (lba << 8) | scsi_cmd[3]; + lba = (lba << 8) | scsi_cmd[4]; + lba = (lba << 8) | scsi_cmd[5]; + cnt = scsi_cmd[7]; + cnt = (cnt << 8) | scsi_cmd[8]; + return scsi_do_read(lba, cnt); + case 0x2a: + printf("scsi: WRITE10 !\n"); + lba = scsi_cmd[2]; + lba = (lba << 8) | scsi_cmd[3]; + lba = (lba << 8) | scsi_cmd[4]; + lba = (lba << 8) | scsi_cmd[5]; + cnt = scsi_cmd[7]; + cnt = (cnt << 8) | scsi_cmd[8]; + return scsi_do_write(lba, cnt); + case 0x2f: + printf("scsi: VERIFY10 !\n"); + return 10; + case 0x3c: + printf("scsi: READ_BUFFER !\n"); + return 6; + } + return 1; +} + +static void scsi_req_ack(void) +{ + unsigned char l; + + scsi_assert |= MASK(BB_ASSERT_REQ); + scsi_set_assert(); + do + l = scsi_get_lines(); + while (!(l & MASK(BB_LINES_ACK))); +} + +static void scsi_nreq_nack(void) +{ + unsigned char l; + + scsi_assert &= ~MASK(BB_ASSERT_REQ); + scsi_set_assert(); + do + l = scsi_get_lines(); + while (l & MASK(BB_LINES_ACK)); +} + +void do_scsi(void) +{ + unsigned char lines, val, i, len, stat; + + lines = scsi_get_lines(); + if ((lines & MASK(BB_LINES_SEL)) /*&& !(lines & MASK(BB_LINES_BSY)) */) { + val = scsi_get_data(); + if (!(val & MASK(6))) { + //printf("scsi: Wrong ID 0x%x\n", val); + return; + } + scsi_assert = MASK(BB_ASSERT_BSY); + scsi_set_assert(); + } else if (lines & MASK(BB_LINES_RST)) { + printf("scsi: RESET !\n"); + return; + } else { + printf("scsi: Glitch (reset ?) lines=%x!\n", lines); + return; + } + while(lines & MASK(BB_LINES_SEL)) + lines = scsi_get_lines(); + /* XXX Check for ATN for MESSAGE_OUT phase, Mac ROM doesn't do it */ + scsi_dbg("scsi: Selected, command phase...\n"); + i = 0; + stat = 0; + len = 1; + scsi_assert |= MASK(BB_ASSERT_CD); + scsi_set_assert(); + while(len) { + scsi_req_ack(); + val = scsi_get_data(); + scsi_cmd[i++] = scsi_get_data(); + scsi_dbg("scsi: CMD = 0x%x\n", val); + if (i == 1) { + if (val == 0x00 || /* TEST_UNIT_READY */ + val == 0x03 || /* REQUEST_SENSE */ + val == 0x04 || /* FORMAT_UNIT */ + val == 0x08 || /* READ6 */ + val == 0x0a || /* WRITE6 */ + val == 0x12 || /* INQUIRY */ + val == 0x15 || /* MODE_SELECT */ + val == 0x1a || /* MODE_SENSE */ + val == 0x1b || /* START_STOP */ + val == 0x3c || /* READ_BUFFER */ + 0) + len = 6; + else if (val == 0x25 || /* READ_CAPACITY */ + val == 0x28 || /* READ10 */ + val == 0x2a || /* WRITE10 */ + val == 0x2f || /* VERIFY10 */ + 0) + len = 10; + else { + stat = 1; + goto fail; + } + } + len--; + fail: + scsi_nreq_nack(); + } + scsi_assert &= ~MASK(BB_ASSERT_CD); + stat = scsi_do_cmd(); + scsi_dbg("Status phase (stat=%x)\n", stat); + scsi_assert |= MASK(BB_ASSERT_IO) | MASK(BB_ASSERT_CD); + scsi_set_data(stat); + scsi_req_ack(); + scsi_nreq_nack(); + scsi_dbg("Message IN phase (stat=%x)\n", stat); + scsi_assert |= MASK(BB_ASSERT_MSG); + scsi_set_data(0); + scsi_req_ack(); + scsi_nreq_nack(); + scsi_assert = 0; + scsi_set_assert(); +} + diff --git a/pic/scsi.h b/pic/scsi.h new file mode 100644 index 0000000..95c41a7 --- /dev/null +++ b/pic/scsi.h @@ -0,0 +1,7 @@ +#ifndef __SCSI_H__ +#define __SCSI_H__ + +extern void scsi_open(void); +extern void do_scsi(void); + +#endif /* __SCSI_H__ */ -- 2.39.2