Initial commit
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>
Sun, 19 Jun 2011 12:00:10 +0000 (22:00 +1000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Sun, 19 Jun 2011 12:00:10 +0000 (22:00 +1000)
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
45 files changed:
.gitignore [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
fpga/Makefile [new file with mode: 0644]
fpga/addr_decode.v [new file with mode: 0644]
fpga/cpu_intf.v [new file with mode: 0644]
fpga/ctrl.v [new file with mode: 0644]
fpga/fpga_clocks.v [new file with mode: 0644]
fpga/fpga_clocks_dll.v [new file with mode: 0644]
fpga/iwm.v [new file with mode: 0644]
fpga/mem_intf.v [new file with mode: 0644]
fpga/minimigmac.ucf [new file with mode: 0644]
fpga/minimigmac.v [new file with mode: 0644]
fpga/ncr5380.v [new file with mode: 0644]
fpga/ps2.v [new file with mode: 0644]
fpga/ps2_kbd.v [new file with mode: 0644]
fpga/ps2_mouse.v [new file with mode: 0644]
fpga/rtc.v [new file with mode: 0644]
fpga/scc.v [new file with mode: 0644]
fpga/scope.v [new file with mode: 0644]
fpga/sim_bench.v [new file with mode: 0644]
fpga/sim_clocks.v [new file with mode: 0644]
fpga/sim_kbd.v [new file with mode: 0644]
fpga/sim_mouse.v [new file with mode: 0644]
fpga/sim_pic.v [new file with mode: 0644]
fpga/sim_sram.v [new file with mode: 0644]
fpga/spi_backbus.v [new file with mode: 0644]
fpga/spi_slave.v [new file with mode: 0644]
fpga/via6522.v [new file with mode: 0644]
fpga/video.v [new file with mode: 0644]
fpga/xilinx/minimigmac.xise [new file with mode: 0644]
pic/18f252_mod.lkr [new file with mode: 0644]
pic/Makefile [new file with mode: 0644]
pic/fat16.c [new file with mode: 0644]
pic/fat16.h [new file with mode: 0644]
pic/fat16_defs.h [new file with mode: 0644]
pic/hardware.c [new file with mode: 0644]
pic/hardware.h [new file with mode: 0644]
pic/main.c [new file with mode: 0644]
pic/minimigmac.piklab [new file with mode: 0644]
pic/mmc.c [new file with mode: 0644]
pic/mmc.h [new file with mode: 0644]
pic/mmc_cmds.h [new file with mode: 0644]
pic/scsi.c [new file with mode: 0644]
pic/scsi.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a65a45b
--- /dev/null
@@ -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 (file)
index 0000000..5e8b33c
--- /dev/null
+++ b/README
@@ -0,0 +1,100 @@
+Minimigmac v0.1
+---------------
+
+Copyright 2011 Ben Herrenschmidt <benh@kernel.crashing.org>
+
+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 (file)
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 (file)
index 0000000..691566b
--- /dev/null
@@ -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 (file)
index 0000000..74e721f
--- /dev/null
@@ -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 (file)
index 0000000..bc76d49
--- /dev/null
@@ -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 (file)
index 0000000..2c1110e
--- /dev/null
@@ -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 (file)
index 0000000..74e62f7
--- /dev/null
@@ -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 (file)
index 0000000..29194f5
--- /dev/null
@@ -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 (file)
index 0000000..52ee8aa
--- /dev/null
@@ -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 (file)
index 0000000..5f0d106
--- /dev/null
@@ -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 (file)
index 0000000..dc87492
--- /dev/null
@@ -0,0 +1,199 @@
+INST "ram_data<0>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<1>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<2>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<3>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<4>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<5>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<6>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<7>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<8>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<9>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<10>"    TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<11>"    TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<12>"    TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<13>"    TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<14>"    TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<15>"    TNM = "RAMDAT"; #SRAM data\r
+INST "ram_address<1>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<2>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<3>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<4>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<5>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<6>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<7>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<8>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<9>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<10>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<11>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<12>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<13>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<14>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<15>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<16>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<17>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<18>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<19>" TNM = "RAMADD"; #SRAM address\r
+INST "_ram_bhe"        TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_ble"        TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_we"         TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_oe"         TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_ce[0]"      TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_ce[1]"      TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_ce[2]"      TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_ce[3]"      TNM = "RAMCTL"; #SRAM address and control\r
+INST "_cpu_as"         TNM = "CPU_AS";\r
+INST "_cpu_uds"        TNM = "CPU_DS";\r
+INST "_cpu_lds"        TNM = "CPU_DS";\r
+INST "_cpu_dtack"      TNM = "CPU_DTACK";\r
+#TIMESPEC TS11 = FROM "FFS"    TO "RAMCTL"    33 ns;\r
+#TIMESPEC TS12 = FROM "RAMDAT" TO "FFS"       33 ns;\r
+#TIMESPEC TS13 = FROM "FFS"    TO "RAMDAT"    33 ns;\r
+#TIMESPEC TS14 = FROM "FFS"    TO "RAMADD"    33 ns;\r
+TIMESPEC TS14 = FROM "CPU_DS" TO "RAMCTL" 20 ns;\r
+TIMESPEC TS14 = FROM "CPU_AS" TO "RAMCTL" 20 ns;\r
+TIMESPEC TS15 = FROM "CPU_AS" TO "CPU_DTACK" 20 ns;\r
+#TIMESPEC TS16 = FROM "RAMDAT" TO "CPU_DTACK" 20 ns;\r
+#TIMESPEC TS17 = FROM "CPU_DS" TO "CPU_DTACK" 20 ns;\r
+\r
+#PACE: Start of Constraints generated by PACE\r
+#PACE: Start of PACE I/O Pin Assignments\r
+NET "mclk"             LOC = "P80"  |IOSTANDARD = LVCMOS33 |PERIOD = 225;\r
+NET "_cpu_reset"       LOC = "P95"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |PULLUP; \r
+NET "cpu_clk"          LOC = "P101" |IOSTANDARD = LVCMOS33 |SLEW = FAST |DRIVE = 24; \r
+NET "_cpu_dtack"       LOC = "P102" |IOSTANDARD = LVCMOS33 |SLEW = FAST |DRIVE = 12; \r
+NET "cpu_r_w"          LOC = "P106" |IOSTANDARD = LVCMOS33 ; \r
+NET "_cpu_as"          LOC = "P109" |IOSTANDARD = LVCMOS33 ; \r
+NET "_cpu_uds"         LOC = "P108" |IOSTANDARD = LVCMOS33 ; \r
+NET "_cpu_lds"         LOC = "P107" |IOSTANDARD = LVCMOS33 ; \r
+NET "_cpu_ipl[0]"      LOC = "P96"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_cpu_ipl[1]"      LOC = "P97"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_cpu_ipl[2]"      LOC = "P100" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "cpu_address[10]"  LOC = "P149" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[11]"  LOC = "P148" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[12]"  LOC = "P147" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[13]"  LOC = "P146" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[14]"  LOC = "P144" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[15]"  LOC = "P143" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[16]"  LOC = "P141" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[17]"  LOC = "P140" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[18]"  LOC = "P139" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[19]"  LOC = "P138" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[1]"   LOC = "P166" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[20]"  LOC = "P137" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[21]"  LOC = "P135" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[22]"  LOC = "P133" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[23]"  LOC = "P132" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[2]"   LOC = "P165" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[3]"   LOC = "P162" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[4]"   LOC = "P161" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[5]"   LOC = "P156" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[6]"   LOC = "P155" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[7]"   LOC = "P154" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[8]"   LOC = "P152" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[9]"   LOC = "P150" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_data[0]"      LOC = "P111" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[10]"     LOC = "P124" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[11]"     LOC = "P125" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[12]"     LOC = "P126" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[13]"     LOC = "P128" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[14]"     LOC = "P130" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[15]"     LOC = "P131" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[1]"      LOC = "P113" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[2]"      LOC = "P114" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[3]"      LOC = "P115" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[4]"      LOC = "P116" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[5]"      LOC = "P117" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[6]"      LOC = "P119" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[7]"      LOC = "P120" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[8]"      LOC = "P122" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[9]"      LOC = "P123" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "_ram_ce[0]"       LOC = "P28"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_ce[1]"       LOC = "P20"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_ce[2]"       LOC = "P15"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_ce[3]"       LOC = "P18"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_oe"          LOC = "P39"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_we"          LOC = "P72"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_bhe"         LOC = "P40"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_ble"         LOC = "P42"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[10]"  LOC = "P58"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[11]"  LOC = "P61"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[12]"  LOC = "P62"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[13]"  LOC = "P63"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[14]"  LOC = "P64"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[15]"  LOC = "P79"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[16]"  LOC = "P78"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[17]"  LOC = "P77"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[18]"  LOC = "P76"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[19]"  LOC = "P74"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[1]"   LOC = "P27"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[2]"   LOC = "P26"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[3]"   LOC = "P24"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[4]"   LOC = "P22"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[5]"   LOC = "P21"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[6]"   LOC = "P35"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[7]"   LOC = "P36"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[8]"   LOC = "P37"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[9]"   LOC = "P57"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_data[0]"      LOC = "P29"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[10]"     LOC = "P50"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[11]"     LOC = "P48"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[12]"     LOC = "P46"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[13]"     LOC = "P45"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[14]"     LOC = "P44"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[15]"     LOC = "P43"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[1]"      LOC = "P31"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[2]"      LOC = "P33"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[3]"      LOC = "P34"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[4]"      LOC = "P65"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[5]"      LOC = "P67"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[6]"      LOC = "P68"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[7]"      LOC = "P71"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[8]"      LOC = "P52"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[9]"      LOC = "P51"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "red[0]"           LOC = "P4"   |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "red[1]"           LOC = "P5"   |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "red[2]"           LOC = "P7"   |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "red[3]"           LOC = "P9"   |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "green[0]"         LOC = "P204" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "green[1]"         LOC = "P205" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "green[2]"         LOC = "P2"   |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "green[3]"         LOC = "P3"   |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "blue[0]"          LOC = "P198" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "blue[1]"          LOC = "P199" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "blue[2]"          LOC = "P200" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "blue[3]"          LOC = "P203" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "_hsync"           LOC = "P196" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "_vsync"           LOC = "P197" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "_joy1[0]"         LOC = "P172" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy1[1]"         LOC = "P171" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy1[2]"         LOC = "P169" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy1[3]"         LOC = "P167" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy1[4]"         LOC = "P168" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy1[5]"         LOC = "P175" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy2[0]"         LOC = "P182" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy2[1]"         LOC = "P181" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy2[2]"         LOC = "P180" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy2[3]"         LOC = "P176" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy2[4]"         LOC = "P178" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy2[5]"         LOC = "P183" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "kbdclk"           LOC = "P11"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; \r
+NET "kbddat"           LOC = "P10"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; \r
+NET "msclk"            LOC = "P13"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; \r
+NET "msdat"            LOC = "P12"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; \r
+NET "left"             LOC = "P191" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "right"            LOC = "P190" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "sdi"              LOC = "P85"  |IOSTANDARD = LVCMOS33 ; \r
+NET "sdo"              LOC = "P19"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "sck"              LOC = "P90"  |IOSTANDARD = LVCMOS33 |CLOCK_DEDICATED_ROUTE = FALSE;\r
+NET "_spi_cs"          LOC = "P93"  |IOSTANDARD = LVCMOS33 ; \r
+NET "scsi_hshake"       LOC = "P86"  |IOSTANDARD = LVCMOS33 ;\r
+NET "nmi"             LOC = "P87"  |IOSTANDARD = LVCMOS33 ;\r
+NET "rxd"              LOC = "P185" |IOSTANDARD = LVCMOS33 ; \r
+NET "txd"              LOC = "P184" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "rts"              LOC = "P187" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "cts"              LOC = "P189" |IOSTANDARD = LVCMOS33 ; \r
+NET "pwrled"           LOC = "P94"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; \r
+NET "_15khz"           LOC = "P194" |IOSTANDARD = LVCMOS33 |PULLUP ; \r
+NET "gpio"             LOC = "P16"  |IOSTANDARD = LVCMOS33 |PULLUP ;\r
+NET "init_b"           LOC = "P83"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4;\r
+NET "_cpu_as" CLOCK_DEDICATED_ROUTE = FALSE;\r
diff --git a/fpga/minimigmac.v b/fpga/minimigmac.v
new file mode 100644 (file)
index 0000000..84caef3
--- /dev/null
@@ -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 (\r
+               .Q(cpu_clk), // Data output (connect directly to
+                                            // top-level port)\r
+               .C0(sysclk16), // 0 degree clock input\r
+               .C1(~sysclk16), // 180 degree clock input\r
+               .CE(1'b1), // Clock enable input\r
+               .CLR(1'b0), // Asynchronous reset input\r
+               .D0(1'b1), // Posedge data input\r
+               .D1(1'b0), // Negedge data input\r
+               .PRE(1'b0) // Asynchronous preset input\r
+       );
+       
+       /* 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 (file)
index 0000000..7ad7b46
--- /dev/null
@@ -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 (file)
index 0000000..31f7fb4
--- /dev/null
@@ -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 (file)
index 0000000..3049745
--- /dev/null
@@ -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 <OSD>
+                         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 <OSD>
+                         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 <OSD>
+                         9'h17b:               keymac[8:0] <= 9'h07b;
+                         9'h17c:               keymac[8:0] <= 9'h07b;  //PRTSCR <OSD>
+                         9'h17d:               keymac[8:0] <= 9'h07b;  //PGUP <OSD>
+                         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 (file)
index 0000000..0a04f0a
--- /dev/null
@@ -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 (file)
index 0000000..1928562
--- /dev/null
@@ -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 (file)
index 0000000..6984af9
--- /dev/null
@@ -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 (file)
index 0000000..c219f90
--- /dev/null
@@ -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 (file)
index 0000000..e632ba5
--- /dev/null
@@ -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 (file)
index 0000000..3fa0d32
--- /dev/null
@@ -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 (file)
index 0000000..d36a77c
--- /dev/null
@@ -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 (file)
index 0000000..c2c8e03
--- /dev/null
@@ -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);
+