--- /dev/null
+*.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
+
--- /dev/null
+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.
+
--- /dev/null
+- 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)
--- /dev/null
+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 *~
--- /dev/null
+`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
--- /dev/null
+`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
--- /dev/null
+/* 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
+
+
+
+
+
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+`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
--- /dev/null
+/*
+ * 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
--- /dev/null
+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
--- /dev/null
+`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
--- /dev/null
+`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
--- /dev/null
+`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
--- /dev/null
+`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
--- /dev/null
+`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
--- /dev/null
+/* 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
--- /dev/null
+`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
--- /dev/null
+/*
+ * 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
--- /dev/null
+`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
--- /dev/null
+`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
--- /dev/null
+`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
+
--- /dev/null
+`timescale 1ns / 100ps
+
+module sim_mouse(inout clk, inout dat);
+`define CLK_HALF 5000 /* faster sim, should be more like 50000 */
+
+ reg aclk;
+ reg adat;
+ reg [7:0] cmd;
+
+ task do_send;
+ input [7:0] d;
+ output ok;
+ reg [10:0] sr;
+ reg p;
+ integer i;
+ begin
+ $display("sim_mouse: send %x", d);
+
+ p = ~(d[0] ^ d[1] ^ d[2] ^ d[3] ^
+ d[4] ^ d[5] ^ d[6] ^ d[7]);
+ sr = { 1'b1, p, d, 1'b0 };
+ aclk = 0;
+ adat = 0;
+ ok = 1;
+ i = 11;
+ while(i > 0 && ok) begin
+ if (clk == 0) begin
+ $display("Mouse: Clock low, aborting send");
+ ok = 0;
+ end else begin
+ adat = ~sr[0];
+ #`CLK_HALF aclk = 1;
+ #`CLK_HALF aclk = 0;
+ #1;
+ sr = { 1'b0, sr[10:1] };
+ i = i - 1;
+ end
+ end
+ aclk = 0;
+ adat = 0;
+ #`CLK_HALF;
+ #`CLK_HALF;
+ end
+ endtask
+
+ task do_receive;
+ output [7:0] d;
+ output ok;
+ reg [10:0] sr;
+ reg p;
+ integer i;
+ begin
+ aclk = 0;
+ adat = 0;
+ sr = 0;
+ wait(dat == 0);
+ wait(clk != 0);
+ ok = 1;
+ i = 10;
+ while(i > 0 && ok) begin
+ if (clk == 0) begin
+ $display("Mouse: Clock low, aborting receive");
+ ok = 0;
+ end else begin
+ #`CLK_HALF aclk = 1;
+ #`CLK_HALF aclk = 0;
+ #1;
+ sr = { dat != 0, sr[10:1] };
+ i = i - 1;
+ end
+ end // while (i > 0 && ok)
+ adat = 1;
+ #`CLK_HALF aclk = 1;
+ #`CLK_HALF aclk = 0;
+ adat = 0;
+// $display("rx shift final: %b", sr);
+ aclk = 0;
+ adat = 0;
+ d = sr[8:1];
+ #`CLK_HALF;
+ #`CLK_HALF;
+ $display("sim_mouse: got %x ok=%b", d, ok);
+ end
+ endtask
+
+ pullup(clk);
+ pullup(dat);
+
+ assign clk = aclk ? 1'b0 : 1'bz;
+ assign dat = adat ? 1'b0 : 1'bz;
+
+ reg ok;
+
+ always@(clk, dat) begin
+ while (clk == 0) begin
+ #`CLK_HALF;
+ if (clk == 0) begin
+ #`CLK_HALF;
+ if (clk == 0) begin
+ do_receive(cmd, ok);
+ if (ok) begin
+ if (cmd == 8'hf4) begin
+ do_send(8'hfa, ok);
+ end else if (cmd == 8'hff) begin
+ if (ok)
+ do_send(8'haa, ok);
+ if (ok)
+ do_send(8'h00, ok);
+ end
+ end
+ end
+ end
+ end
+ end
+
+ initial begin
+ aclk = 0;
+ adat = 0;
+ #`CLK_HALF;
+ #`CLK_HALF;
+ #`CLK_HALF;
+ #`CLK_HALF;
+ do_send(8'haa, ok);
+ do_send(8'h00, ok);
+ #6000000;
+ do_send(8'h00, ok);
+ do_send(8'h02, ok);
+ do_send(8'h00, ok);
+ #1000000;
+ do_send(8'h00, ok);
+ do_send(8'h02, ok);
+ do_send(8'h00, ok);
+ #1000000;
+ do_send(8'h00, ok);
+ do_send(8'h00, ok);
+ do_send(8'h02, ok);
+ end
+endmodule
+
--- /dev/null
+`timescale 1ns / 100ps
+
+`define ROM_MAX 'h20000
+
+`define BB_WRITE 8'h80
+`define BB_AUTOINC 8'h40
+
+`define BB_REG_CTRL_BASE 6'h00
+`define BB_REG_CPU_BASE 6'h10
+`define BB_REG_MEM_BASE 6'h20
+`define BB_REG_IWM_BASE 6'h28
+`define BB_REG_SCSI_BASE 6'h30
+`define BB_REG_SCOPE_BASE 6'h38
+
+/* SCSI Backbus interface */
+`define BB_REG_LINES 0
+`define BB_LINES_BSY 7
+`define BB_LINES_SEL 6
+`define BB_LINES_ACK 5
+`define BB_LINES_ATN 4
+`define BB_LINES_RST 3
+
+`define BB_REG_ASSERT 1
+`define BB_ASSERT_BSY 7
+`define BB_ASSERT_SEL 6
+`define BB_ASSERT_CD 5
+`define BB_ASSERT_IO 4
+`define BB_ASSERT_MSG 3
+`define BB_ASSERT_REQ 2
+`define BB_ASSERT_RST 1
+`define BB_ASSERT_AUTO 0 /* Automatic mode */
+
+`define BB_REG_ODATA 2
+`define BB_REG_IDATA 3
+`define BB_REG_ACNT_HI 4
+`define BB_REG_ACNT_LO 5
+
+module sim_pic
+(
+ input sdi,
+ output reg sdo,
+ output reg sck,
+ output reg _spi_cs,
+ input scsi_hshake,
+ output reg nmi);
+ integer rom_file;
+ integer rom_size;
+ reg [7:0] rom[0:`ROM_MAX];
+ reg [7:0] scsi_asserts;
+ reg [7:0] scsi_cdb[0:11];
+ reg [7:0] scsi_buf[0:2047];
+ integer dbg_foo;
+
+ /* SPI half clock period */
+ parameter SPI_HCLOCK = 40;
+ /* Setup time of data vs rising clock edge */
+ parameter SPI_HEAD = 4;
+
+ task do_spi;
+ input [7:0] out_byte; /* Out to FPGA */
+ output reg [7:0] in_byte; /* In from FPGA */
+ reg [7:0] in_sr;
+ reg [7:0] out_sr;
+ begin
+ in_sr = 8'hff;
+ out_sr = out_byte;
+ repeat(8) begin
+ sck = 0;
+ #(SPI_HCLOCK-SPI_HEAD);
+ sdo = out_sr[7];
+ out_sr = { out_sr[6:0], 1'b0 };
+ in_sr = { in_sr[6:0], sdi };
+ #SPI_HEAD sck = 1;
+ #SPI_HCLOCK;
+ end
+ sck = 0;
+ in_byte = in_sr;
+ end
+ endtask // do_spi
+
+ task run_to;
+ input [23:0] addr;
+ reg [7:0] stat;
+ begin
+ /* Set breakpoint */
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_AUTOINC | `BB_REG_CPU_BASE | 8, val);
+ do_spi(addr[23:16], val);
+ do_spi(addr[15:8], val);
+ do_spi(addr[7:0], val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+
+ /* Run CPU interface */
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val);
+ do_spi(8'h42, val); /* RUN + BKEN */
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+
+ stat = 0;
+ while(!stat[3]) begin
+ /* Get CPU status */
+ _spi_cs = 0;
+ do_spi(`BB_REG_CPU_BASE | 7, val);
+ do_spi(8'hff, val);
+ do_spi(8'hff, stat);
+ _spi_cs = 1;
+// $display("stat: %h", stat);
+// #(SPI_HCLOCK*40);
+ end
+ end
+ endtask // run_to
+
+ task scsi_set_lines;
+ input [7:0] lines;
+ begin
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_REG_SCSI_BASE | `BB_REG_ASSERT, val);
+ do_spi(lines, val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+ end
+ endtask
+
+ task scsi_set;
+ input integer line;
+ begin
+ scsi_asserts[line] = 1'b1;
+ scsi_set_lines(scsi_asserts);
+ end
+ endtask
+
+ task scsi_clear;
+ input integer line;
+ begin
+ scsi_asserts[line] = 1'b0;
+ scsi_set_lines(scsi_asserts);
+ end
+ endtask
+
+ task scsi_set_autocnt;
+ input integer cnt;
+ begin
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_AUTOINC |
+ `BB_REG_SCSI_BASE | `BB_REG_ACNT_HI, val);
+ do_spi(cnt[15:8], val);
+ do_spi(cnt[7:0], val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+ end
+ endtask
+
+ task scsi_set_data;
+ input [7:0] data;
+ begin
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_REG_SCSI_BASE | `BB_REG_IDATA, val);
+ do_spi(data, val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+ end
+ endtask
+
+ task scsi_get_data;
+ output [7:0] val;
+ reg [7:0] dummy;
+ begin
+ _spi_cs = 0;
+ do_spi(`BB_REG_SCSI_BASE | `BB_REG_ODATA, dummy);
+ do_spi(8'hff, dummy);
+ do_spi(8'hff, val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+ end
+ endtask
+
+ task scsi_get_lines;
+ output [7:0] val;
+ reg [7:0] dummy;
+ begin
+ _spi_cs = 0;
+ do_spi(`BB_REG_SCSI_BASE | `BB_REG_LINES, dummy);
+ do_spi(8'hff, dummy);
+ do_spi(8'hff, val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+ end
+ endtask
+
+ task scsi_do_cmd;
+ output integer len;
+ output [7:0] stat;
+ output direction;
+ integer i;
+
+ begin
+ stat = 0;
+ len = 0;
+ direction = 0;
+ case(scsi_cdb[0])
+ 'h12: begin /* INQUIRY */
+ direction = 1;
+ for (i=0; i < 256; i = i + 1)
+ scsi_buf[i] = 0;
+ scsi_buf[4] = 32;
+ scsi_buf[8] = 'h46;
+ scsi_buf[9] = 'h4f;
+ scsi_buf[10] = 'h4f;
+ scsi_buf[16] = 'h42;
+ scsi_buf[17] = 'h41;
+ scsi_buf[18] = 'h52;
+ len = scsi_cdb[4];
+ $display("CMD: INQUIRY 0x%h bytes", len);
+ end
+ 'h08: begin /* READ6 */
+ direction = 1;
+ len = 512 * scsi_cdb[4];
+ if (len == 0)
+ len = 512 * 256;
+ $display("CMD: READ6 0x%h bytes", len);
+ end
+ 'h0a: begin /* WRITE6 */
+ direction = 0;
+ len = 512 * scsi_cdb[4];
+ if (len == 0)
+ len = 512 * 256;
+ $display("CMD: WRITE6 0x%h bytes", len);
+ end
+ 'h28: begin /* READ10 */
+ direction = 1;
+ len = 512 * (scsi_cdb[7] * 256 + scsi_cdb[8]);
+ $display("CMD: READ10 0x%h bytes", len);
+ end
+ 'h2a: begin /* WRITE10 */
+ direction = 0;
+ len = 512 * (scsi_cdb[7] * 256 + scsi_cdb[8]);
+ $display("CMD: WRITE10 0x%h bytes", len);
+ end
+ default: begin
+ stat = 1; /* CHECK CONDITION */
+ end
+ endcase
+ end
+ endtask // scsi_init_cmd
+
+ task do_scsi;
+ reg [7:0] lines;
+ reg [7:0] data;
+ integer len, llen;
+ reg [7:0] stat;
+ reg direction;
+
+ begin
+ scsi_get_lines(lines);
+ $display("Waiting selection... Lines: %h", lines);
+ while (!lines[`BB_LINES_SEL] || lines[`BB_LINES_BSY])
+ scsi_get_lines(lines);
+ scsi_get_data(data);
+ $display("Select ID: %h lines %h", data, lines);
+ stat = 0;
+ if (!data[6])
+ $display("Wrong ID !");
+ else begin
+ $display("Setting BSY");
+ scsi_set(`BB_ASSERT_BSY);
+ $display("Waiting !SEL...");
+ while(lines[`BB_LINES_SEL])
+ scsi_get_lines(lines);
+ $display("Setting CMD, starting cmd transfer");
+ scsi_set(`BB_ASSERT_CD);
+ len = 1;
+ i = 0;
+ stat = 0;
+ while(len) begin
+ scsi_set(`BB_ASSERT_REQ);
+ while(!lines[`BB_LINES_ACK])
+ scsi_get_lines(lines);
+ scsi_get_data(data);
+ $display("CMD %h = %h", i, data);
+ scsi_cdb[i] = data;
+ len = len - 1;
+ i = i + 1;
+ if (i == 1) begin
+ /* get command size */
+ case (data)
+ 'h12: len = 6 - 1;
+ 'h08: len = 6 - 1;
+ 'h0a: len = 6 - 1;
+ 'h28: len = 10 - 1;
+ 'h2a: len = 10 - 1;
+ default: begin
+ len = 0;
+ stat = 1;
+ end
+ endcase
+ $display("CMD size adjust %h", len);
+ end
+ scsi_clear(`BB_ASSERT_REQ);
+ while(lines[`BB_LINES_ACK])
+ scsi_get_lines(lines);
+ end
+
+ scsi_do_cmd(len, stat, direction);
+ scsi_clear(`BB_ASSERT_CD);
+ if (direction)
+ scsi_set(`BB_ASSERT_IO);
+ i = 0;
+ while (len) begin
+ scsi_set(`BB_ASSERT_AUTO);
+ /* Break up transfer */
+ if (len > 256)
+ llen = 256;
+ else
+ llen = len;
+ len = len - llen;
+ $display("Chunk of %h, remaining %h...", llen, len);
+ scsi_set_autocnt(llen);
+ _spi_cs = 0;
+ if (direction) begin
+ do_spi(`BB_WRITE | `BB_REG_SCSI_BASE | `BB_REG_IDATA, val);
+ while(llen != 0) begin
+ $display("Writing byte %h", i);
+ do_spi(scsi_buf[i], val);
+ wait (!scsi_hshake);
+ wait (scsi_hshake);
+ llen = llen - 1;
+ i = i + 1;
+
+ end
+ end else begin
+ do_spi(`BB_REG_SCSI_BASE | `BB_REG_ODATA, val);
+ #160;
+ wait (scsi_hshake);
+ do_spi(8'hff, val);
+ wait (!scsi_hshake);
+ $display("dummy1: %h", val);
+ wait (scsi_hshake);
+ do_spi(8'hff, val);
+ wait (!scsi_hshake);
+ $display("dummy2: %h", val);
+ wait (scsi_hshake);
+ do_spi(8'hff, val);
+ wait (!scsi_hshake);
+ $display("dummy3: %h", val);
+ while(llen != 0) begin
+ wait (scsi_hshake) ;
+ do_spi(8'hff, data);
+ if (llen > 3)
+ wait (!scsi_hshake);
+ scsi_buf[i] = data;
+ $display("DATA %h: %h", i, data);
+ llen = llen - 1;
+ i = i + 1;
+ end
+ end
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+ scsi_clear(`BB_ASSERT_AUTO);
+ end
+ $display("sending status %h", stat);
+ scsi_set(`BB_ASSERT_CD);
+ scsi_set(`BB_ASSERT_IO);
+ scsi_set_data(stat);
+ scsi_set(`BB_ASSERT_REQ);
+ while(!lines[`BB_LINES_ACK])
+ scsi_get_lines(lines);
+ scsi_clear(`BB_ASSERT_REQ);
+ while(lines[`BB_LINES_ACK])
+ scsi_get_lines(lines);
+ scsi_set(`BB_ASSERT_MSG);
+ $display("Sending message");
+ scsi_set_data(0);
+ scsi_set(`BB_ASSERT_REQ);
+ while(!lines[`BB_LINES_ACK])
+ scsi_get_lines(lines);
+ scsi_clear(`BB_ASSERT_REQ);
+ while(lines[`BB_LINES_ACK])
+ scsi_get_lines(lines);
+ $display("Clearing phase & BSY");
+ scsi_clear(`BB_ASSERT_CD);
+ scsi_clear(`BB_ASSERT_IO);
+ scsi_clear(`BB_ASSERT_MSG);
+ scsi_clear(`BB_ASSERT_BSY);
+ end // else: !if(!data[6])
+ end
+ endtask // do_scsi
+
+ task run_scsi;
+ begin
+ while(1) begin
+ wait (scsi_hshake)
+ do_scsi();
+ end
+ end
+ endtask // run_scsi
+
+ task dump_latches;
+ reg [7:0] sval[0:7];
+ begin
+ /* Read latches */
+ _spi_cs = 0;
+ do_spi(`BB_AUTOINC | `BB_REG_CPU_BASE, val);
+ do_spi(8'hff, val);
+ do_spi(8'hff, sval[0]);
+ do_spi(8'hff, sval[1]);
+ do_spi(8'hff, sval[2]);
+ do_spi(8'hff, sval[3]);
+ do_spi(8'hff, sval[4]);
+ do_spi(8'hff, sval[5]);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+ $display("RW:%b U:%b L:%b ADDR: %h DATA: %h",
+ sval[5][0], sval[5][1], sval[5][2],
+ { sval[0], sval[1], sval[2] },
+ { sval[3], sval[4] });
+ end
+ endtask // dump_latches
+
+ task step;
+ input integer count;
+ begin
+ repeat(count) begin
+ /* Step CPU interface */
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val);
+ do_spi(8'h82, val); /* RUN + STEP */
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+
+ /* Should wait for completion... not useful
+ * in practice as SPI is too slow
+ */
+ end // repeat (count)
+ end
+ endtask // step
+
+
+ reg [7:0] val;
+// reg [7:0] scopedump[0:(2048 * 8)-1];
+ integer i;
+
+ initial begin
+ nmi = 0;
+ _spi_cs = 1;
+ sdo = 1'bz;
+ sck = 0;
+ scsi_asserts = 0;
+
+ #100 _spi_cs = 0;
+ do_spi(`BB_AUTOINC | `BB_REG_CTRL_BASE, val);
+ do_spi(8'hff, val);
+ do_spi(8'hff, val);
+ $display("FPGA ID = 0x%h", val);
+ do_spi(8'hff, val);
+ $display("FPGA Version = 0x%h", val);
+ do_spi(8'hff, val);
+ $display("FPGA Control = 0x%h", val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+
+ /* Un-reset CPU */
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_REG_CTRL_BASE | 2, val);
+ do_spi(8'h01, val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+
+`ifdef foo
+ $display("Running to puts...");
+ run_to('h40326c);
+ dump_latches();
+ repeat(1000) begin
+ step(1);
+ dump_latches();
+ end
+`endif
+
+ $display("Run....");
+ /* Run CPU interface */
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val);
+ do_spi(8'h02, val); /* RUN */
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+ run_scsi();
+`ifdef foo
+ $display("Running to iwm_test1...");
+ run_to('h4010f0);
+ dump_latches();
+ repeat(100) begin
+ step(1);
+ dump_latches();
+ end
+`endif
+
+`ifdef foo
+ $display("Running to P_mBootBeep...");
+ run_to('h40028a);
+ dump_latches();
+ step(1);
+ dump_latches();
+ $display("Running to IWM init...");
+ run_to('h4000fc);
+ dump_latches();
+ step(1);
+ dump_latches();
+ $display("Running to P_ChecksumRomAndTestMemory...");
+ run_to('h400d76);
+ dump_latches();
+ step(1);
+ dump_latches();
+ $display("Running to P_BootPart2 (fake hit)...");
+ run_to('h400352);
+ dump_latches();
+ step(1);
+ dump_latches();
+ $display("Running to P_BootPart2...");
+ run_to('h400352);
+ dump_latches();
+ step(1);
+ dump_latches();
+
+ repeat(1000) begin
+ step(1);
+ dump_latches();
+ end
+`endif
+
+`ifdef __disabled__
+ /* Write memory from 0x201000 using mem SPI (ROM) */
+ $display("Loading ROM...");
+ rom_file = $fopenr("rom.bin");
+ rom_size = $fread(rom, rom_file, 0);
+ $fclose(rom_file);
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_AUTOINC | `BB_REG_MEM_BSAE, val);
+ do_spi(8'h20, val); /* ROM base */
+ do_spi(8'h00, val);
+ do_spi(8'h00, val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_REG_MEM_BASE | 3, val);/
+ i = 0;
+ repeat(rom_size) begin
+ do_spi(rom[i], val);
+ i = i+1;
+ if ((i % 1024) == 0)
+ $display("%h", i);
+ end
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+
+ $display("RAM dump...");
+ $display("%h %h %h %h %h %h %h %h",
+ sval[0], sval[1], sval[2], sval[3],
+ sval[4], sval[5], sval[6], sval[7]);
+
+ /* Start the scope */
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_REG_SCOPE_BASE, val);
+ do_spi(8'h02, val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+
+ /* Un-reset CPU */
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_REG_CTRL_BASE | 2, val);
+ do_spi(8'h01, val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+
+ /* Set breakpoint */
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_AUTOINC | `BB_REG_CPU_BASE | 8, val);
+ do_spi(8'h00, val);
+ do_spi(8'h10, val);
+ do_spi(8'h22, val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+
+ /* Run CPU interface */
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val);
+ do_spi(8'h42, val); /* RUN + BKEN */
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+
+ repeat(10) begin
+ /* Get CPU status */
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 7, val);
+ do_spi(8'hff, val);
+ do_spi(8'hff, val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+ $display("CPU Stat: %h", val);
+ end
+`endif
+
+`ifdef foo
+ $display("Steps:");
+ repeat(100) begin
+ /* Step CPU interface */
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val);
+ do_spi(8'h82, val); /* RUN + STEP */
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+
+ /* We shall wait for cycle to complete but we know
+ * how slow our SPI is ...
+ */
+ /* Read latches */
+ _spi_cs = 0;
+ do_spi(`BB_AUTOINC | `BB_REG_CPU_BASE, val);
+ do_spi(8'hff, val);
+ do_spi(8'hff, sval[0]);
+ do_spi(8'hff, sval[1]);
+ do_spi(8'hff, sval[2]);
+ do_spi(8'hff, sval[3]);
+ do_spi(8'hff, sval[4]);
+ do_spi(8'hff, sval[5]);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+ $display("RW:%b U:%b L:%b ADDR: %h DATA: %h",
+ sval[5][0], sval[5][1], sval[5][2],
+ { sval[0], sval[1], sval[2] },
+ { sval[3], sval[4] });
+ end
+`endif
+
+`ifdef foo
+
+ $display("Reading 0x1020 via SPI initiaed read cycle:");
+
+ /* Read value at 0x1020 */
+ _spi_cs = 0;
+ do_spi(8'hc8, val); /* b001_000 */
+ do_spi(8'h00, val);
+ do_spi(8'h10, val);
+ do_spi(8'h20, val);
+ do_spi(8'haa, val);
+ do_spi(8'h55, val);
+ do_spi(8'h01, val); /* _uds = 0, _lds = 0, r_w = 1 */
+ do_spi(8'h04, val); /* ctrl: cycle */
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+
+ /* Here too, shall wait but heh ! */
+
+ /* Read latches */
+ _spi_cs = 0;
+ do_spi(8'h48, val); /* b001_000 */
+ do_spi(8'hff, val);
+ do_spi(8'hff, sval[0]);
+ do_spi(8'hff, sval[1]);
+ do_spi(8'hff, sval[2]);
+ do_spi(8'hff, sval[3]);
+ do_spi(8'hff, sval[4]);
+ do_spi(8'hff, sval[5]);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+ $display("RW:%b U:%b L:%b ADDR: %h DATA: %h",
+ sval[5][0], sval[5][1], sval[5][2],
+ { sval[0], sval[1], sval[2] },
+ { sval[3], sval[4] });
+
+ /* Read memory from 0x1000 using mem SPI */
+ _spi_cs = 0;
+ do_spi(8'he0, val); /* b100_000 */
+ do_spi(8'h00, val);
+ do_spi(8'h10, val);
+ do_spi(8'h00, val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+ _spi_cs = 0;
+ do_spi(8'h23, val); /* b100_000 */
+ do_spi(8'hff, val);
+ do_spi(8'hff, val); /* add. dummy cycle for reads */
+ do_spi(8'hff, sval[0]);
+ do_spi(8'hff, sval[1]);
+ do_spi(8'hff, sval[2]);
+ do_spi(8'hff, sval[3]);
+ do_spi(8'hff, sval[4]);
+ do_spi(8'hff, sval[5]);
+ do_spi(8'hff, sval[6]);
+ do_spi(8'hff, sval[7]);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+
+ $display("RAM dump...");
+ $display("%h %h %h %h %h %h %h %h",
+ sval[0], sval[1], sval[2], sval[3],
+ sval[4], sval[5], sval[6], sval[7]);
+
+`endif
+
+`ifdef foo
+ $display("Run....");
+
+ /* Run CPU interface */
+ _spi_cs = 0;
+ do_spi(8'h8e, val); /* b001_110 */
+ do_spi(8'h02, val); /* RUN */
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+`endif
+ /* Wait a bit and stop the scope */
+ #4000000;
+
+`ifdef foo
+ _spi_cs = 0;
+ do_spi(`BB_WRITE | `BB_REG_SCOPE_BASE, val);
+ do_spi(8'h00, val);
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+`endif
+
+`ifdef foo
+ /* Dump scope output */
+ _spi_cs = 0;
+ do_spi(8'h3c, val);
+ do_spi(8'hff, val);
+ i = 0;
+ repeat(2048) begin
+ do_spi(8'hff, sval[0]);
+ do_spi(8'hff, sval[1]);
+ do_spi(8'hff, sval[2]);
+ do_spi(8'hff, sval[3]);
+ do_spi(8'hff, sval[4]);
+ do_spi(8'hff, sval[5]);
+ do_spi(8'hff, sval[6]);
+ do_spi(8'hff, sval[7]);
+ $display("%h %h %h %h %h %h %h %h",
+ sval[0], sval[1], sval[2], sval[3],
+ sval[4], sval[5], sval[6], sval[7]);
+ end
+ _spi_cs = 1;
+ #(SPI_HCLOCK*4);
+`endif
+ end
+
+endmodule
--- /dev/null
+/* Behavioural of our SRAM chips */
+module sim_sram512x16(input [18:0] addr,
+ input _cs,
+ input _oe,
+ input _we,
+ input _ub,
+ input _lb,
+ inout [15:0] data);
+
+
+ reg sel;
+ reg [15:0] mem[0:'h7ffff];
+ reg [15:0] dlatch;
+ wire [15:0] dout;
+
+ /* Simplified model, roughly based on a IS64LV6416L model adapted
+ * to look like our AS6C8016
+ */
+ parameter Taa = 55;
+ parameter Toh = 10;
+ parameter Tchz = 20;
+ parameter Tas = 0;
+ parameter Twhz = 20;
+
+ wire r_en = _we & (~_cs) & (~_oe);
+ wire w_en = (~_we) & (~_cs) & ((~_ub) | (~_lb));
+ assign #(r_en ? Taa : Tchz) data = r_en ? dout : 16'bz;
+ assign dout[ 7:0] = _lb ? 8'bz : mem[addr][ 7:0];
+ assign dout[15:8] = _ub ? 8'bz : mem[addr][15:8];
+
+ always@(addr or w_en) begin
+ #Tas
+ if (w_en)
+ #Twhz begin
+ /* XXX Something's wrong with iverilog. It appears that
+ * the if (w_en) above isn't enough, if I don't also test
+ * w_en in the assignment belwow, it -will- write spuriously
+ * to the RAMs even when w_en has never kicked for a while
+ * and I really have no idea why. --BenH
+ */
+ mem[addr][ 7:0] = _lb | ~w_en ?mem[addr][ 7:0] : data[ 7:0];
+ mem[addr][15:8] = _ub | ~w_en ?mem[addr][15:8] : data[15:8];
+ end
+ end
+
+`ifdef foo
+ initial begin
+
+ $display("initializing RAM...");
+ mem[0] = 0;
+ mem[1] = 'h2000 - 16;
+ mem[2] = 0;
+ mem[3] = 'h1000;
+ // 1000: 303c 000a movew #10,%d0
+ mem['h1000/2] = 'h303c;
+ mem['h1002/2] = 'h000a;
+ // 1004: 41fa 001a lea %pc@(1020 <_buf>),%a0
+ mem['h1004/2] = 'h41fa;
+ mem['h1006/2] = 'h001a;
+ // 1008: 3080 movew %d0,%a0@
+ mem['h1008/2] = 'h3080;
+ // 100a: 0640 0001 addiw #1,%d0
+ mem['h100a/2] = 'h0640;
+ mem['h100c/2] = 'h0001;
+ // 100e: 6000 fff4 braw 1004
+ mem['h100e/2] = 'h6000;
+ mem['h1010/2] = 'hfff4;
+ $display("done");
+ end // initial begin
+`endif
+
+endmodule
--- /dev/null
+`timescale 1ns / 100ps
+
+/* SPI to backbus interface for minimigmac
+ *
+ * This layers on top of the spi_slave module. It provides a protocol
+ * which allows to read and write from of 8-bit wide, 6 addr bit bus
+ * which is connected to various modules in the system.
+ *
+ * The basic SPI protocol starts with one byte received by the FPGA,
+ * which is has the format <RW:1> <AI:1> <ADDR:6>
+ *
+ * RW : Read (0) / Write (1)
+ * AI : Auto-increment
+ * ADDR : Register address
+ *
+ * For writes, all subsequent bytes are directly relevant. For reads,
+ * the next byte after the read command shall be ignored, then subsequent
+ * bytes are relevant.
+ *
+ * The backbus side protocol is:
+ *
+ * - Writes: at clock with strobe high and wr high, data and address
+ * are stable and shall be latched.
+ * - Reads : at clock with strobe high and wr low, address stable,
+ * we latch data immediately.
+ */
+
+module spi_backbus
+(
+ /* Clock & Reset */
+ input sysclk,
+ input reset,
+
+ /* SPI signals (through to the SPI slave core) */
+ input sdi,
+ output sdo,
+ input sck,
+ input _scs,
+
+ /* Backbus interface */
+ output reg [5:0] bb_addr,
+ output [7:0] bb_wdata,
+ input [7:0] bb_rdata,
+ output reg bb_strobe,
+ output reg bb_wr
+ );
+
+ /* SPI slave wires */
+ wire spi_rx;
+ wire spi_cmd;
+
+ /* Protocol internals */
+ reg ainc;
+ reg delayed_ainc;
+ reg write;
+
+ /* Data read latch */
+ reg [7:0] data;
+
+ /* Instanciate SPI slave interface */
+ spi_slave spi0(.sysclk(sysclk),
+ .reset(reset),
+ ._scs(_scs),
+ .sdi(sdi),
+ .sdo(sdo),
+ .sck(sck),
+ .wdata(data),
+ .rdata(bb_wdata),
+ .rx(spi_rx),
+ .cmd(spi_cmd));
+
+ /* Data latch */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset) begin
+ data <= 0;
+ end else begin
+ /* Latch read data at read strobe */
+ if (bb_strobe && !bb_wr)
+ data <= bb_rdata;
+ end
+ end
+
+ /* One state machine to rule them all coz I'm lazy */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset) begin
+ bb_addr <= 0;
+ end else begin
+ /* Command byte. Establish addr, wr and set strobe on read */
+ if (spi_rx && spi_cmd) begin
+ bb_addr <= bb_wdata[5:0];
+ delayed_ainc <= bb_wdata[6];
+ ainc <= bb_wdata[7] ? 0 : bb_wdata[6];
+ bb_wr <= bb_wdata[7];
+ bb_strobe <= ~bb_wdata[7];
+ end else if (spi_rx) begin
+ /* Subsequent bytes */
+ ainc <= delayed_ainc;
+ if (ainc) begin
+ bb_addr <= bb_addr + 1;
+ end
+ /* New strobe */
+ bb_strobe <= 1;
+ end else begin
+ bb_strobe <= 0;
+ end
+ end
+ end
+endmodule
+
+
\ No newline at end of file
--- /dev/null
+`timescale 1ns / 100ps
+
+/* SPI slave module
+ *
+ * Expected configuration on the master is SPI mode 0
+ * (CPOL 0 CPHA 0)
+ * - SCK idle state is 0
+ * - We latch input on positive edges of SCK
+ * - We setup output on negative edge of SCK
+ *
+ * IE. For a PIC, this means setting:
+ * CKP=0 CKE=1 SMP=0
+ */
+module spi_slave
+(
+ input sysclk,
+ input reset, /* Async system reset */
+ input _scs,
+ input sdi,
+ output sdo,
+ input sck,
+ input [7:0] wdata, /* Output data */
+ output reg [7:0] rdata, /* Input data */
+ output rx, /* One-sysclk signal of a new byte */
+ output reg cmd /* Indicate first byte of rx, variable len */
+);
+
+ reg [6:0] in_sr;
+ reg [7:0] out_sr;
+ reg [2:0] cnt;
+ reg first;
+ reg new_byte;
+ reg rx1;
+ reg rx2;
+ reg rx3;
+ reg tx;
+
+ /* Bit counter */
+ always@(posedge sck or posedge _scs) begin
+ if (_scs) begin
+ cnt <= 0;
+ end else begin
+ cnt <= cnt + 1;
+ end
+ end
+
+ /* Shift input, latch on positive edge of sck. We only shift in 7
+ * bits as we'll take sdi directly into the final latch.
+ */
+ always@(posedge sck) begin
+ if (!_scs) begin
+ in_sr[6:0] <= { in_sr[5:0], sdi };
+ end
+ end
+
+ /* We latch the input byte so it remains stable for a while byte for
+ * consumption by sysclk domain.
+ */
+ always@(posedge sck) begin
+ if (!_scs && cnt == 3'b111) begin
+ rdata[7:0] <= { in_sr[6:0], sdi };
+ end
+ end
+
+ /* Shift output. We latch "data" on bit 7 falling edge (so before we
+ * set new_byte and thus before rx catches up sysclk domain).
+ *
+ * That means we are ahead by one bit, so we add a one bit delay latch
+ * on sdo
+ *
+ */
+ always@(negedge sck or posedge _scs) begin
+ if (_scs) begin
+ out_sr <= 8'hff;
+ end else begin
+ if (cnt == 3'b111) begin
+ out_sr <= wdata;
+ end else begin
+ out_sr[7:0] <= { out_sr[6:0], 1'b0 };
+ end
+ end
+ end
+
+ always@(negedge sck or posedge _scs) begin
+ if (_scs) begin
+ tx <= 1;
+ end else begin
+ tx <= out_sr[7];
+ end
+ end
+
+ /* We keep output hi-z if chip select not set. We do have a transcient
+ * undefined state between _scs assertion and the first clock, which
+ * I'm happy to ignore for now
+ */
+ assign sdo = _scs ? 1'bz : tx;
+
+ /* Now we need to generate rx. It's tricky because sck can/will stop
+ * immediately after the last bit.
+ *
+ * What we do is we generate a signal "new_byte" at the same time as
+ * latching the input byte, which we clear half way through the next
+ * byte. We then double flip-flop synchronize that into sysclk domain
+ * where we do an edge detection. If we lose sck, this signal will
+ * remains set until we start a new transmission, but since the delay
+ * between two transmissions is unpredictable, we must make sure we
+ * don't clear it until half way through the new byte (ie, we keep it
+ * set even when SCS is gone). For the same reason we don't
+ * clear our input latch either when SCS is gone.
+ *
+ * Now, the nasty thing is we need to reset that dude asynchronously
+ * from sysclk domain, which could probably use some sychronisation
+ * too. For now, we assume the sysclk reset happens long enough before
+ * sck toggles. This will do for us, but in a more complex system,
+ * some logic might be needed to ensure sck is effectively masked
+ * out until a few clocks after reset completes to avoid metastability
+ *
+ * Note: FPGAs are nice, we could probably just rely on new_byte being
+ * 0 at powerup time :-)
+ */
+ always@(posedge sck or posedge reset) begin
+ if (reset) begin
+ new_byte <= 0;
+ end else begin
+ if (cnt == 3'b111) begin
+ new_byte <= 1;
+ end else if (cnt == 3'b011) begin
+ new_byte <= 0;
+ end
+ end
+ end
+
+ always@(posedge sysclk or posedge reset) begin
+ if (reset) begin
+ rx1 <= 0;
+ rx2 <= 0;
+ rx3 <= 0;
+ end else begin
+ rx1 <= new_byte;
+ rx2 <= rx1;
+ rx3 <= rx2;
+ end
+ end
+
+ assign rx = rx2 & !rx3;
+
+ /* "first" is set in sck domain during clocking of first byte,
+ * and flushed into "cmd" at bit 7, thus cmd moves along with
+ * the data itself and will be sampled with the data by the user.
+ */
+ always@(posedge sck or posedge _scs) begin
+ if (_scs) begin
+ first <= 1;
+ end else if (cnt == 3'b111) begin
+ first <= 0;
+ cmd <= first;
+ end
+ end
+endmodule
--- /dev/null
+`timescale 1ns / 100ps
+
+/*
+ * VIA6522 module for minimigmac.
+ *
+ * The processor interface part of the VIA modified to use my
+ * home made bus interface. The rest is hugely simplified to
+ * only operate in the modes used by a Macintosh. The shift
+ * register is not implemented as such, instead, we have a
+ * direct 8 bit bus to the keyboard interface.
+ *
+ * My understanding of the original Mac design is that the VIA
+ * two phase clock is fed from the original m68k's E line, thus
+ * ticks at 1/10 of the 7.82 Mhz CPU frequency.
+ *
+ * In this variant, the register interface is clocked at the full
+ * internal system clock, and we generate artificially a 782 kHz
+ * enable signal from it to feed the timers. We don't aim for
+ * great precision here, so for now we simply divide 16.666 Mhz
+ * by 21 which gives us an approximate 793kHz.
+ *
+ * In addition, to simplify the logic, the direction of the
+ * port A and B bits is hard wired and the reset values adapted
+ * to enable the ROM overlay at startup.
+ *
+ */
+
+/* Register numbers */
+`define VIA_REG_ORB 4'h0
+`define VIA_REG_ORA 4'h1
+`define VIA_REG_DDRB 4'h2
+`define VIA_REG_DDRA 4'h3
+`define VIA_REG_T1CL 4'h4
+`define VIA_REG_T1CH 4'h5
+`define VIA_REG_T1LL 4'h6
+`define VIA_REG_T1LH 4'h7
+`define VIA_REG_T2CL 4'h8
+`define VIA_REG_T2CH 4'h9
+`define VIA_REG_SR 4'ha
+`define VIA_REG_ACR 4'hb
+`define VIA_REG_PCR 4'hc
+`define VIA_REG_IFR 4'hd
+`define VIA_REG_IER 4'he
+`define VIA_REG_ORA2 4'hf
+
+/* Interrupt bits */
+`define CA2_IRQ 0
+`define CA1_IRQ 1
+`define SR_IRQ 2
+`define CB2_IRQ 3
+`define CB1_IRQ 4
+`define T2_IRQ 5
+`define T1_IRQ 6
+
+module via6522(input sysclk,
+ input reset,
+
+ /* Bus interface. 4-bit address, to be wired
+ * appropriately upstream (to A9..A12)
+ */
+ input cs,
+ input we,
+ input [3:0] rs,
+ input [7:0] wdata,
+ output [7:0] rdata,
+ output _irq,
+
+ /* Port A and B signals. Fixed direction. Port B bit
+ * 0 (RTC data) is duplicated for in/out.
+ */
+ input [7:7] pa_in7,
+ output [6:0] pa_out,
+ output [2:0] pb_out2_0,
+ output pb_out7,
+ input [6:3] pb_in6_3,
+ input pb_in0,
+
+ /* CA1 and CA2 are inputs always */
+ input ca1,
+ input ca2,
+
+ /* Pseudo shift-register keyboard interface */
+ input [7:0] sr_in,
+ input sr_in_strobe,
+ output [7:0] sr_out,
+ output reg sr_out_strobe);
+
+
+ /* Registers */
+ reg [7:0] pa;
+ reg [7:0] pb;
+ reg [7:0] sr;
+ reg [7:0] acr;
+ reg [6:0] ier;
+ reg [6:0] ifr;
+ reg [7:0] pcr;
+ reg [15:0] t1;
+ reg [15:0] t1l;
+ reg [15:0] t2;
+ reg [7:0] t2l;
+ reg ddr_b0;
+ wire pb0_v;
+ wire ifr7;
+ reg ca1_old;
+ reg ca2_old;
+ reg pb6_old;
+ reg t1_armed;
+ reg t2_armed;
+ reg t1_fired;
+ reg t2_fired;
+ reg [4:0] ckdiv;
+ wire ckhalf;
+ reg t1pb7;
+ wire tstrobe;
+ wire t2strobe;
+ wire t1irq;
+ wire t2irq;
+
+ /* Register selects */
+ wire rs_orb;
+ wire rs_ora;
+ wire rs_ddrb;
+ wire rs_ddra;
+ wire rs_t1cl;
+ wire rs_t1ch;
+ wire rs_t1ll;
+ wire rs_t1lh;
+ wire rs_t2cl;
+ wire rs_t2ch;
+ wire rs_sr;
+ wire rs_acr;
+ wire rs_pcr;
+ wire rs_ifr;
+ wire rs_ier;
+ wire rs_ora2;
+
+ assign rs_orb = cs && rs == `VIA_REG_ORB;
+ assign rs_ora = cs && rs == `VIA_REG_ORA;
+ assign rs_ddrb = cs && rs == `VIA_REG_DDRB;
+ assign rs_ddra = cs && rs == `VIA_REG_DDRA;
+ assign rs_t1cl = cs && rs == `VIA_REG_T1CL;
+ assign rs_t1ch = cs && rs == `VIA_REG_T1CH;
+ assign rs_t1ll = cs && rs == `VIA_REG_T1LL;
+ assign rs_t1lh = cs && rs == `VIA_REG_T1LH;
+ assign rs_t2cl = cs && rs == `VIA_REG_T2CL;
+ assign rs_t2ch = cs && rs == `VIA_REG_T2CH;
+ assign rs_sr = cs && rs == `VIA_REG_SR;
+ assign rs_acr = cs && rs == `VIA_REG_ACR;
+ assign rs_pcr = cs && rs == `VIA_REG_PCR;
+ assign rs_ifr = cs && rs == `VIA_REG_IFR;
+ assign rs_ier = cs && rs == `VIA_REG_IER;
+ assign rs_ora2 = cs && rs == `VIA_REG_ORA2;
+
+ /* Outputs & interrupt generation */
+ assign pa_out = pa[6:0] ;
+ assign pb_out2_0 = pb[2:0];
+ assign pb_out7 = acr[7] ? t1pb7 : pb[7];
+ assign sr_out = sr;
+ assign ifr7 = (ifr & ier) != 7'b0000000;
+ assign _irq = ~ifr7;
+
+ /* PB[0] value */
+ assign pb0_v = ddr_b0 ? pb[0] : pb_in0;
+
+ /* Register reads */
+ assign rdata = rs_ora ? { pa_in7, pa[6:0] } :
+ rs_ora2 ? { pa_in7, pa[6:0] } :
+ rs_orb ? { pb[7], pb_in6_3, pb[2:1], pb0_v } :
+ rs_ddrb ? { 1'b1, 4'b0000, 2'b11, ddr_b0 } :
+ rs_ddra ? { 1'b0, 7'b1111111 } :
+ rs_t1cl ? t1[7:0] :
+ rs_t1ch ? t1[15:8] :
+ rs_t1ll ? t1l[7:0] :
+ rs_t1lh ? t1l[15:8] :
+ rs_t2cl ? t2[7:0] :
+ rs_t2ch ? t2[15:8] :
+ rs_sr ? sr :
+ rs_acr ? acr :
+ rs_pcr ? pcr :
+ rs_ifr ? { ifr7, ifr } :
+ rs_ier ? { 1'b1, ier } :
+ 8'b11111111;
+
+ /* Write to ORA */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ pa <= 8'b11111111;
+ else if (we && (rs_ora || rs_ora2))
+ pa <= wdata;
+ end
+
+ /* Write to ORB */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ pb <= 8'b11111111;
+ else if (we && rs_orb)
+ pb <= wdata;
+ end
+
+ /* Write to DDRA ignored */
+ /* Write to DDRB */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ ddr_b0 <= 1'b0;
+ else if (we && rs_orb)
+ ddr_b0 <= wdata[0];
+ end
+
+ /* Write to ACR */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ acr <= 8'b0;
+ else if (we && rs_acr)
+ acr <= wdata;
+ end
+
+ /* Write to PCR */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ pcr <= 8'b0;
+ else if (we && rs_pcr)
+ pcr <= wdata;
+ end
+
+ /* Timers
+ *
+ * Note: When writing to TnCH, we load the timer immediately
+ * and start counting on the next tstrobe (ie. P2 clock), which
+ * means that depending on when the CPU hits, the first P2 period
+ * might be reduced to one sysclk ... this could be "fixed" by causing
+ * all VIA accesses to wait for a P2 clock to complete but I'm happy
+ * to ignore the "problem" for now.
+ *
+ * Also, to keep things simple, PB7 goes up right after writing to
+ * T1CH, tho then follows proper periods (hopefully)
+ */
+
+ /* Clock division. We divide by 21 to get approx 793kHz */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ ckdiv <= 20;
+ else begin
+ if (ckdiv == 0)
+ ckdiv <= 20;
+ else
+ ckdiv <= ckdiv - 1;
+ end
+ end
+ assign tstrobe = (ckdiv == 0);
+ assign ckhalf = (ckdiv == 10);
+
+ /* Timer 1 latches */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ t1l <= 0;
+ else begin
+ if (we && (rs_t1cl || rs_t1ll))
+ t1l[7:0] <= wdata;
+ if (we && (rs_t1ch || rs_t1lh))
+ t1l[15:8] <= wdata;
+ end
+ end
+
+ /* Timer 1 counter */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ t1 <= 0;
+ else begin
+ /* Handle transfer from latch on write */
+ if (we && rs_t1ch) begin
+ t1[7:0] <= t1l[7:0];
+ t1[15:8] <= wdata;
+ end else if (tstrobe) begin
+ /* In "free run", reload from latch
+ *
+ * XXX There's an inconsistency in the doco
+ * as to whether we shall just roll over and
+ * count in one-shot mode or whether we shall
+ * -also- reload from the latch on T1 (it's
+ * clear for T2). For now I just let it roll
+ * over, we'll see if any SW gets upset
+ */
+ if (acr[6] && t1_fired)
+ t1 <= t1l;
+ else
+ t1 <= t1 - 1;
+ end
+ end
+ end
+
+ /* "armed" latch. This is set on a write and cleared when
+ * running out in "one shot" mode
+ */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ t1_armed <= 0;
+ else begin
+ /* Always armed when in continuous mode, else
+ * arm when writing to T1CH
+ */
+ if ((we && rs_t1ch) || acr[6])
+ t1_armed <= 1;
+ else if (t1_fired && !acr[6])
+ t1_armed <= 0;
+ end
+ end
+
+ /* "fired" latch. This is set on tstrobe with counter == 0
+ * and is used to do whatever has to be done (irq, pb7, ...)
+ * on the next half phase 2 clock. It's set for the duration
+ * of the next P2 period.
+ */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ t1_fired <= 0;
+ else begin
+ if (tstrobe)
+ t1_fired <= t1_armed && t1 == 0;
+ end
+ end
+
+ /* Generate PB7 output */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ t1pb7 <= 1;
+ else begin
+ /* We are 0 when timer is armed and not fired yet */
+ if (we && rs_t1ch)
+ t1pb7 <= 0;
+ /* We invert when fired */
+ if (ckhalf && t1_fired)
+ t1pb7 <= ~t1pb7;
+ end
+ end
+
+ /* T1 interrupt */
+ assign t1irq = ckhalf & t1_fired;
+
+ /* Timer 2
+ *
+ * Either one shot or count PB6 pulses (ie. hblank)
+ */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ pb6_old <= 1'b0;
+ else
+ pb6_old <= pb_in6_3[6];
+ end
+ assign t2strobe = acr[5] ? (pb6_old & ~pb_in6_3[6]) : tstrobe;
+
+ /* Timer 2 latche (low only) */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ t2l <= 0;
+ else begin
+ if (we && rs_t2cl)
+ t2l <= wdata;
+ end
+ end
+
+ /* Timer 2 counter */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ t2 <= 0;
+ else begin
+ /* Handle transfer from latch on write */
+ if (we && rs_t2ch) begin
+ t2[7:0] <= t2l;
+ t2[15:8] <= wdata;
+ end else if (t2strobe)
+ t2 <= t2 - 1;
+ end
+ end
+
+ /* T2 "armed" latch (see T1) */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ t2_armed <= 0;
+ else begin
+ if (we && rs_t2ch)
+ t2_armed <= 1;
+ else if (t2_fired)
+ t2_armed <= 0;
+ end
+ end
+
+ /* "fired" latch. This is set on tstrobe with counter == 0
+ * or in pulse counting right when hitting the 0 -> -1
+ * transitionand. It's set for the duration of the next P2 period
+ * in the former case, and for one sysclk in the later case.
+ */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ t2_fired <= 0;
+ else begin
+ if (t2strobe)
+ t2_fired <= t2_armed && t2 == 0;
+ if (acr[5] && t2_fired)
+ t2_fired <= 0;
+ end
+ end
+
+ /* T2 Interrupt */
+ assign t2irq = acr[5] ? t2_fired : (t2_fired & ckhalf);
+
+ /* Shift register
+ *
+ * CB1 is the kbd clock, CB2 the shift register,
+ * we don't implement them that way, instead we
+ * have a direct 8-bit bus to the PS2 conversion
+ * module. The Mac only uses these ACR modes:
+ * 111 used when sending to the keyboard
+ * 110 used to assert the data line to 0 to start comm
+ * 011 used when receiving from the keyboard
+ * 000 used when transitioning
+ *
+ * We ignore the 110 mode as we don't need the
+ * keyboard to clock us. Thus we only care about
+ * mode 111 and 011. In the former, any write to
+ * sr results in an sr_out_strobe pulse to signal
+ * the keyboard. In mode 011, a pulse on sr_in_strobe
+ * results in updating the SR and eventually signaling
+ * an interrupt
+ */
+
+ /* Write to SR (including external input) */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ sr <= 8'b0;
+ else begin
+ if (we && rs_sr)
+ sr <= wdata;
+ if (acr[4:2] == 3'b011 && sr_in_strobe)
+ sr <= sr_in;
+ end
+ end
+
+ /* Generate sr_out_strobe */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ sr_out_strobe <= 1'b0;
+ else begin
+ if (we && rs_sr && acr[4:2] == 3'b111)
+ sr_out_strobe <= 1;
+ else
+ sr_out_strobe <= 0;
+ end
+ end
+
+ /* CA1 / CA2 edge detection */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset) begin
+ ca1_old <= 0;
+ ca2_old <= 0;
+ end else begin
+ ca1_old <= ca1;
+ ca2_old <= ca2;
+ end
+ end
+
+
+ /* Interrupts */
+
+
+ /* Write to IER */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ ier <= 1'b0;
+ else if (we && rs_ier) begin
+ if (wdata[7])
+ ier <= ier | wdata[6:0];
+ else
+ ier <= ier & ~wdata[6:0];
+ end
+ end
+
+ /* Handle IFR */
+ always@(posedge sysclk or posedge reset) begin
+ if (reset)
+ ifr <= 8'b0;
+ else begin
+ /* First, write-1-to-clear */
+ if (we && rs_ifr)
+ ifr <= ifr & ~wdata[6:0];
+
+ /* Now clear individual sources based on
+ * register accesses
+ */
+ if (rs_ora && (pcr[3:1] == 3'b000 ||
+ pcr[3:1] == 3'b010))
+ ifr[`CA2_IRQ] <= 0;
+ if (rs_ora)
+ ifr[`CA1_IRQ] <= 0;
+ if (rs_sr)
+ ifr[`SR_IRQ] <= 0;
+ if (rs_orb && (pcr[7:5] == 3'b000 ||
+ pcr[7:5] == 3'b010))
+ ifr[`CB2_IRQ] <= 0;
+ if (rs_orb)
+ ifr[`CB1_IRQ] <= 0;
+ if (we && rs_t2ch)
+ ifr[`T2_IRQ] <= 0;
+ if ((!we) && rs_t2cl)
+ ifr[`T2_IRQ] <= 0;
+ if (we && (rs_t1ch || rs_t1lh))
+ ifr[`T1_IRQ] <= 0;
+ if ((!we) && rs_t1cl)
+ ifr[`T1_IRQ] <= 0;
+
+ /* Finally set sources based on relevant
+ * events
+ */
+
+ /* -- SR interrupt --
+ *
+ /* Note that when shifting out, we strobe and
+ * thus interrupt immediately when ACR is set
+ * to 111 (shift under external clock). That is
+ * sending commands to the keyboard.
+ *
+ * When ACR is set to 110 (sending a 0 pulse to
+ * the keyboard to wake it up), we just swallow
+ * everything here and don't generate an interrupt,
+ * the Mac ROM seems to be happy enough that way.
+ */
+
+ /* Shift out under control of external clock */
+ if (we && rs_sr && acr[4:2] == 3'b111)
+ ifr[`SR_IRQ] <= 1;
+
+ /* Shift in under control of external clock */
+ if (acr[4:2] == 3'b011 && sr_in_strobe)
+ ifr[`SR_IRQ] <= 1;
+
+ /* CA2 */
+ if (pcr[3] == 0 &&
+ ((!pcr[2] && ca2_old && !ca2) ||
+ ( pcr[2] && !ca2_old && ca2)))
+ ifr[`CA2_IRQ] <= 1;
+
+ /* CA1 */
+ if ((!pcr[0] && ca1_old && !ca1) ||
+ ( pcr[0] && !ca1_old && ca1))
+ ifr[`CA1_IRQ] <= 1;
+
+ /* No CB1/CB2 on the mac ? Well they are kbd data
+ * and clock, I don't think they are ever used, but
+ * we may want to check...
+ */
+
+ /* Timer IRQs */
+ if (t1irq)
+ ifr[`T1_IRQ] <= 1;
+ if (t2irq)
+ ifr[`T2_IRQ] <= 1;
+ end
+ end
+endmodule
+
--- /dev/null
+module video(input pixclk,
+ input reset,
+ input [15:0] pixels,
+ output reg req_pix,
+ input ack_pix,
+ output _hsync,
+ output _vsync,
+ output [3:0] red,
+ output [3:0] green,
+ output [3:0] blue,
+ output hblank);
+
+ /* Max coordinate size in bits - 1 */
+ parameter csize = 9;
+
+ /* Fixed CRTC params for now. Based on a 640x480 VGA signal with
+ * added borders to center the Mac's 512x342.
+ *
+ * The added border is 64 pixels left and right and 69 pixels top
+ * and bottom.
+ *
+ * The constants are used as count-down and thus include a -1 to
+ * account for the 0 state.
+ */
+ parameter hdisp_frontp = 8 - 1;
+ parameter hdisp_hsync = 96 - 1;
+ parameter hdisp_backp = 40 - 1;
+ parameter hdisp_lbord = 8 + 64 - 1;
+ parameter hdisp_display = 512 - 1;
+ parameter hdisp_rbord = 8 + 64 - 1;
+
+ parameter vdisp_frontp = 2 - 1;
+ parameter vdisp_vsync = 2 - 1;
+ parameter vdisp_backp = 25 - 1;
+ parameter vdisp_tbord = 8 + 69 - 1;
+ parameter vdisp_display = 342 - 1;
+ parameter vdisp_bbord = 8 + 69 - 1;
+
+ /* Corresponding stages */
+ localparam hstage_frontp = 0;
+ localparam hstage_hsync = 1;
+ localparam hstage_backp = 2;
+ localparam hstage_lbord = 3;
+ localparam hstage_display = 4;
+ localparam hstage_rbord = 5;
+
+ localparam vstage_frontp = 0;
+ localparam vstage_vsync = 1;
+ localparam vstage_backp = 2;
+ localparam vstage_tbord = 3;
+ localparam vstage_display = 4;
+ localparam vstage_bbord = 5;
+
+ /* Running h/v counters */
+ reg [csize:0] hcnt;
+ reg [csize:0] vcnt;
+ reg [csize:0] hnextcnt;
+ reg [csize:0] vnextcnt;
+
+ /* State machines */
+ reg [2:0] hstage;
+ reg [2:0] vstage;
+
+ /* Output latches */
+ reg _hsync_r;
+ reg _vsync_r;
+ reg pix_r;
+
+ /* Pixel shift reg */
+ reg [15:0] pix_shift;
+ wire vid_on;
+
+ /* Request/ack protocol helpers */
+ wire pix_req_reset;
+
+ /* Obtain the new count for the next stage */
+ always@(hstage) begin
+ case(hstage)
+ hstage_frontp: hnextcnt = hdisp_hsync;
+ hstage_hsync: hnextcnt = hdisp_backp;
+ hstage_backp: hnextcnt = hdisp_lbord;
+ hstage_lbord: hnextcnt = hdisp_display;
+ hstage_display: hnextcnt = hdisp_rbord;
+ hstage_rbord: hnextcnt = hdisp_frontp;
+ default: hnextcnt = hdisp_frontp;
+ endcase
+ end
+ always@(vstage) begin
+ case(vstage)
+ vstage_frontp: vnextcnt = vdisp_vsync;
+ vstage_vsync: vnextcnt = vdisp_backp;
+ vstage_backp: vnextcnt = vdisp_tbord;
+ vstage_tbord: vnextcnt = vdisp_display;
+ vstage_display: vnextcnt = vdisp_bbord;
+ vstage_bbord: vnextcnt = vdisp_frontp;
+ default: vnextcnt = vdisp_frontp;
+ endcase
+ end
+
+ /* H counter */
+ always@(posedge pixclk or posedge reset) begin
+ if (reset) begin
+ hcnt <= 0;
+ hstage <= hstage_frontp;
+ end else begin
+ if (hcnt == 0) begin
+ hcnt <= hnextcnt;
+ if (hstage == hstage_rbord)
+ hstage <= hstage_frontp;
+ else
+ hstage <= hstage + 1;
+ end else
+ hcnt <= hcnt - 1;
+ end
+ end
+
+ /* V counter */
+ always@(posedge pixclk or posedge reset) begin
+ if (reset) begin
+ vcnt <= 0;
+ vstage <= vstage_frontp;
+ end else begin
+ if (hstage == hstage_rbord && hcnt == 0) begin
+ if (vcnt == 0) begin
+ vcnt <= vnextcnt;
+ if (vstage == vstage_bbord)
+ vstage <= vstage_frontp;
+ else
+ vstage <= vstage + 1;
+ end else begin
+ vcnt <= vcnt - 1;
+ end
+ end
+ end
+ end
+
+ /* Visible signal */
+ assign vid_on = hstage == hstage_display &&
+ vstage == vstage_display;
+
+ /* Pixel request, async clear */
+ assign pix_req_reset = reset | ack_pix;
+
+ /* Generate pixel request. NOTE: This relies on the display
+ * width being a multiple of 16
+ */
+ always@(posedge pixclk or posedge pix_req_reset) begin
+ if (pix_req_reset) begin
+ req_pix <= 0;
+ end else begin
+ if (vstage == vstage_display) begin
+ if (hstage == hstage_backp && hcnt == 0) begin
+ req_pix <= 1;
+ end else if (hstage == hstage_display &&
+ hcnt[3:0] == 4'b1111 &&
+ hcnt[csize:4]) begin
+ req_pix <= 1;
+ end
+ end
+
+ end
+ end
+
+ /* Shift register */
+ always@(posedge pixclk or posedge reset) begin
+ if (reset) begin
+ pix_shift <= 0;
+ end else begin
+ if (hcnt[3:0] == 0)
+ pix_shift <= pixels;
+ else
+ pix_shift[15:1] <= pix_shift[14:0];
+ end
+ end
+
+ /* Output latches */
+ always@(posedge pixclk or posedge reset) begin
+ if (reset) begin
+ _hsync_r <= 1;
+ _vsync_r <= 1;
+ pix_r <= 0;
+ end else begin
+ _hsync_r <= ~(hstage == hstage_hsync);
+ _vsync_r <= ~(vstage == vstage_vsync);
+ pix_r <= vid_on & ~pix_shift[15];
+ end
+ end
+
+ /* Output wires */
+ assign _hsync = _hsync_r;
+ assign _vsync = _vsync_r;
+ assign red = { pix_r, pix_r, pix_r, pix_r };
+ assign green = { pix_r, pix_r, pix_r, pix_r };
+ assign blue = { pix_r, pix_r, pix_r, pix_r };
+ assign hblank = hstage != hstage_display;
+endmodule
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
+<project xmlns="http://www.xilinx.com/XMLSchema" xmlns:xil_pn="http://www.xilinx.com/XMLSchema">
+
+ <header>
+ <!-- ISE source project file created by Project Navigator. -->
+ <!-- -->
+ <!-- This file contains project source information including a list of -->
+ <!-- project source files, project and process properties. This file, -->
+ <!-- along with the project source files, is sufficient to open and -->
+ <!-- implement in ISE Project Navigator. -->
+ <!-- -->
+ <!-- Copyright (c) 1995-2011 Xilinx, Inc. All rights reserved. -->
+ </header>
+
+ <version xil_pn:ise_version="13.1" xil_pn:schema_version="2"/>
+
+ <files>
+ <file xil_pn:name="../minimigmac.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="1"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="18"/>
+ </file>
+ <file xil_pn:name="../fpga_clocks.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="2"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="15"/>
+ </file>
+ <file xil_pn:name="../scope.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="3"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="7"/>
+ </file>
+ <file xil_pn:name="../spi_backbus.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="4"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="6"/>
+ </file>
+ <file xil_pn:name="../spi_slave.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="5"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="1"/>
+ </file>
+ <file xil_pn:name="../ctrl.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="6"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="16"/>
+ </file>
+ <file xil_pn:name="../cpu_intf.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="7"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="17"/>
+ </file>
+ <file xil_pn:name="../iwm.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="8"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="14"/>
+ </file>
+ <file xil_pn:name="../mem_intf.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="9"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="13"/>
+ </file>
+ <file xil_pn:name="../scc.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="10"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="8"/>
+ </file>
+ <file xil_pn:name="../ncr5380.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="11"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="12"/>
+ </file>
+ <file xil_pn:name="../via6522.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="12"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="5"/>
+ </file>
+ <file xil_pn:name="../addr_decode.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="13"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="3"/>
+ </file>
+ <file xil_pn:name="../video.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="14"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="4"/>
+ </file>
+ <file xil_pn:name="../rtc.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="15"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="9"/>
+ </file>
+ <file xil_pn:name="../ps2_mouse.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="16"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="10"/>
+ </file>
+ <file xil_pn:name="../ps2.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="17"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="2"/>
+ </file>
+ <file xil_pn:name="../ps2_kbd.v" xil_pn:type="FILE_VERILOG">
+ <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="18"/>
+ <association xil_pn:name="Implementation" xil_pn:seqID="11"/>
+ </file>
+ <file xil_pn:name="../minimigmac.ucf" xil_pn:type="FILE_UCF">
+ <association xil_pn:name="Implementation" xil_pn:seqID="0"/>
+ </file>
+ </files>
+
+ <properties>
+ <property xil_pn:name="Add I/O Buffers" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Allow Logic Optimization Across Hierarchy" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Allow SelectMAP Pins to Persist" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Allow Unexpanded Blocks" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Allow Unmatched LOC Constraints" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Allow Unmatched Timing Group Constraints" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Asynchronous To Synchronous" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Auto Implementation Compile Order" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Auto Implementation Top" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Automatic BRAM Packing" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Automatically Insert glbl Module in the Netlist" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Automatically Run Generate Target PROM/ACE File" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="BRAM Utilization Ratio" xil_pn:value="100" xil_pn:valueState="default"/>
+ <property xil_pn:name="Bring Out Global Set/Reset Net as a Port" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Bring Out Global Tristate Net as a Port" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Bus Delimiter" xil_pn:value="<>" xil_pn:valueState="default"/>
+ <property xil_pn:name="CLB Pack Factor Percentage" xil_pn:value="100" xil_pn:valueState="default"/>
+ <property xil_pn:name="Case" xil_pn:value="Maintain" xil_pn:valueState="default"/>
+ <property xil_pn:name="Case Implementation Style" xil_pn:value="None" xil_pn:valueState="default"/>
+ <property xil_pn:name="Change Device Speed To" xil_pn:value="-4" xil_pn:valueState="default"/>
+ <property xil_pn:name="Change Device Speed To Post Trace" xil_pn:value="-4" xil_pn:valueState="default"/>
+ <property xil_pn:name="Combinatorial Logic Optimization" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Compile EDK Simulation Library" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Compile SIMPRIM (Timing) Simulation Library" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Compile UNISIM (Functional) Simulation Library" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Compile XilinxCoreLib (CORE Generator) Simulation Library" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Compile for HDL Debugging" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Configuration Clk (Configuration Pins)" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+ <property xil_pn:name="Configuration Pin Done" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+ <property xil_pn:name="Configuration Pin HSWAPEN" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+ <property xil_pn:name="Configuration Pin M0" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+ <property xil_pn:name="Configuration Pin M1" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+ <property xil_pn:name="Configuration Pin M2" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+ <property xil_pn:name="Configuration Pin Program" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+ <property xil_pn:name="Configuration Rate" xil_pn:value="Default (6)" xil_pn:valueState="default"/>
+ <property xil_pn:name="Correlate Output to Input Design" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Create ASCII Configuration File" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Create Binary Configuration File" xil_pn:value="true" xil_pn:valueState="non-default"/>
+ <property xil_pn:name="Create Bit File" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Create I/O Pads from Ports" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Create IEEE 1532 Configuration File" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Create Logic Allocation File" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Create Mask File" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Create ReadBack Data Files" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Cross Clock Analysis" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="DCI Update Mode" xil_pn:value="As Required" xil_pn:valueState="default"/>
+ <property xil_pn:name="Decoder Extraction" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Delay Values To Be Read from SDF" xil_pn:value="Setup Time" xil_pn:valueState="default"/>
+ <property xil_pn:name="Device" xil_pn:value="xc3s400" xil_pn:valueState="non-default"/>
+ <property xil_pn:name="Device Family" xil_pn:value="Spartan3" xil_pn:valueState="non-default"/>
+ <property xil_pn:name="Device Speed Grade/Select ABS Minimum" xil_pn:value="-4" xil_pn:valueState="default"/>
+ <property xil_pn:name="Do Not Escape Signal and Instance Names in Netlist" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Done (Output Events)" xil_pn:value="6" xil_pn:valueState="non-default"/>
+ <property xil_pn:name="Drive Done Pin High" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Enable BitStream Compression" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Enable Cyclic Redundancy Checking (CRC)" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Enable Debugging of Serial Mode BitStream" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Enable Hardware Co-Simulation" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Enable Internal Done Pipe" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Enable Message Filtering" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Enable Outputs (Output Events)" xil_pn:value="Default (5)" xil_pn:valueState="default"/>
+ <property xil_pn:name="Equivalent Register Removal XST" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Exclude Compilation of Deprecated EDK Cores" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Exclude Compilation of EDK Sub-Libraries" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Extra Effort" xil_pn:value="None" xil_pn:valueState="default"/>
+ <property xil_pn:name="Extra Effort (Highest PAR level only)" xil_pn:value="None" xil_pn:valueState="default"/>
+ <property xil_pn:name="FPGA Start-Up Clock" xil_pn:value="CCLK" xil_pn:valueState="default"/>
+ <property xil_pn:name="FSM Encoding Algorithm" xil_pn:value="Auto" xil_pn:valueState="default"/>
+ <property xil_pn:name="FSM Style" xil_pn:value="LUT" xil_pn:valueState="default"/>
+ <property xil_pn:name="Filter Files From Compile Order" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Flatten Output Netlist" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Functional Model Target Language ArchWiz" xil_pn:value="Verilog" xil_pn:valueState="default"/>
+ <property xil_pn:name="Functional Model Target Language Coregen" xil_pn:value="Verilog" xil_pn:valueState="default"/>
+ <property xil_pn:name="Functional Model Target Language Schematic" xil_pn:value="Verilog" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Architecture Only (No Entity Declaration)" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Asynchronous Delay Report" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Clock Region Report" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Constraints Interaction Report" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Constraints Interaction Report Post Trace" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Datasheet Section" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Datasheet Section Post Trace" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Detailed MAP Report" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Multiple Hierarchical Netlist Files" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Post-Place & Route Power Report" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Post-Place & Route Simulation Model" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate RTL Schematic" xil_pn:value="Yes" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate SAIF File for Power Optimization/Estimation Par" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Testbench File" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Timegroups Section" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generate Timegroups Section Post Trace" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Generics, Parameters" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Global Optimization Goal" xil_pn:value="AllClockNets" xil_pn:valueState="default"/>
+ <property xil_pn:name="Global Set/Reset Port Name" xil_pn:value="GSR_PORT" xil_pn:valueState="default"/>
+ <property xil_pn:name="Global Tristate Port Name" xil_pn:value="GTS_PORT" xil_pn:valueState="default"/>
+ <property xil_pn:name="Hierarchy Separator" xil_pn:value="/" xil_pn:valueState="default"/>
+ <property xil_pn:name="ISim UUT Instance Name" xil_pn:value="UUT" xil_pn:valueState="default"/>
+ <property xil_pn:name="Ignore User Timing Constraints Map" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Ignore User Timing Constraints Par" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Implementation Top" xil_pn:value="Module|minimigmac" xil_pn:valueState="non-default"/>
+ <property xil_pn:name="Implementation Top File" xil_pn:value="../minimigmac.v" xil_pn:valueState="non-default"/>
+ <property xil_pn:name="Implementation Top Instance Path" xil_pn:value="/minimigmac" xil_pn:valueState="non-default"/>
+ <property xil_pn:name="Include 'uselib Directive in Verilog File" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Include SIMPRIM Models in Verilog File" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Include UNISIM Models in Verilog File" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Include sdf_annotate task in Verilog File" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Incremental Compilation" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Insert Buffers to Prevent Pulse Swallowing" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Instantiation Template Target Language Xps" xil_pn:value="Verilog" xil_pn:valueState="default"/>
+ <property xil_pn:name="JTAG Pin TCK" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+ <property xil_pn:name="JTAG Pin TDI" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+ <property xil_pn:name="JTAG Pin TDO" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+ <property xil_pn:name="JTAG Pin TMS" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+ <property xil_pn:name="Keep Hierarchy" xil_pn:value="No" xil_pn:valueState="default"/>
+ <property xil_pn:name="Language" xil_pn:value="VHDL" xil_pn:valueState="default"/>
+ <property xil_pn:name="Last Applied Goal" xil_pn:value="Balanced" xil_pn:valueState="default"/>
+ <property xil_pn:name="Last Applied Strategy" xil_pn:value="Xilinx Default (unlocked)" xil_pn:valueState="default"/>
+ <property xil_pn:name="Last Unlock Status" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Launch SDK after Export" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Library for Verilog Sources" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Load glbl" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Logical Shifter Extraction" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Manual Implementation Compile Order" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Map Effort Level" xil_pn:value="High" xil_pn:valueState="default"/>
+ <property xil_pn:name="Map Slice Logic into Unused Block RAMs" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Max Fanout" xil_pn:value="500" xil_pn:valueState="default"/>
+ <property xil_pn:name="Maximum Number of Lines in Report" xil_pn:value="1000" xil_pn:valueState="default"/>
+ <property xil_pn:name="Maximum Signal Name Length" xil_pn:value="20" xil_pn:valueState="default"/>
+ <property xil_pn:name="Move First Flip-Flop Stage" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Move Last Flip-Flop Stage" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Multiplier Style" xil_pn:value="Auto" xil_pn:valueState="default"/>
+ <property xil_pn:name="Mux Extraction" xil_pn:value="Yes" xil_pn:valueState="default"/>
+ <property xil_pn:name="Mux Style" xil_pn:value="Auto" xil_pn:valueState="default"/>
+ <property xil_pn:name="Netlist Hierarchy" xil_pn:value="As Optimized" xil_pn:valueState="default"/>
+ <property xil_pn:name="Netlist Translation Type" xil_pn:value="Timestamp" xil_pn:valueState="default"/>
+ <property xil_pn:name="Number of Clock Buffers" xil_pn:value="8" xil_pn:valueState="default"/>
+ <property xil_pn:name="Number of Paths in Error/Verbose Report" xil_pn:value="3" xil_pn:valueState="default"/>
+ <property xil_pn:name="Number of Paths in Error/Verbose Report Post Trace" xil_pn:value="3" xil_pn:valueState="default"/>
+ <property xil_pn:name="Optimization Effort" xil_pn:value="Normal" xil_pn:valueState="default"/>
+ <property xil_pn:name="Optimization Goal" xil_pn:value="Speed" xil_pn:valueState="default"/>
+ <property xil_pn:name="Optimization Strategy (Cover Mode)" xil_pn:value="Area" xil_pn:valueState="default"/>
+ <property xil_pn:name="Optimize Instantiated Primitives" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other Bitgen Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other Compiler Options" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other Compiler Options Map" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other Compiler Options Par" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other Compiler Options Translate" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other Compxlib Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other Map Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other NETGEN Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other Ngdbuild Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other Place & Route Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other Simulator Commands Behavioral" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other Simulator Commands Post-Map" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other Simulator Commands Post-Route" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other Simulator Commands Post-Translate" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other XPWR Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Other XST Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Output Extended Identifiers" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Output File Name" xil_pn:value="minimigmac" xil_pn:valueState="default"/>
+ <property xil_pn:name="Overwrite Compiled Libraries" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Pack I/O Registers into IOBs" xil_pn:value="Auto" xil_pn:valueState="default"/>
+ <property xil_pn:name="Pack I/O Registers/Latches into IOBs" xil_pn:value="Off" xil_pn:valueState="default"/>
+ <property xil_pn:name="Package" xil_pn:value="pq208" xil_pn:valueState="default"/>
+ <property xil_pn:name="Perform Advanced Analysis" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Perform Advanced Analysis Post Trace" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Perform Timing-Driven Packing and Placement" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Place & Route Effort Level (Overall)" xil_pn:value="High" xil_pn:valueState="default"/>
+ <property xil_pn:name="Place And Route Mode" xil_pn:value="Normal Place and Route" xil_pn:valueState="default"/>
+ <property xil_pn:name="Placer Effort Level (Overrides Overall Level)" xil_pn:value="None" xil_pn:valueState="default"/>
+ <property xil_pn:name="Port to be used" xil_pn:value="Auto - default" xil_pn:valueState="default"/>
+ <property xil_pn:name="Post Map Simulation Model Name" xil_pn:value="minimigmac_map.v" xil_pn:valueState="default"/>
+ <property xil_pn:name="Post Place & Route Simulation Model Name" xil_pn:value="minimigmac_timesim.v" xil_pn:valueState="default"/>
+ <property xil_pn:name="Post Synthesis Simulation Model Name" xil_pn:value="minimigmac_synthesis.v" xil_pn:valueState="default"/>
+ <property xil_pn:name="Post Translate Simulation Model Name" xil_pn:value="minimigmac_translate.v" xil_pn:valueState="default"/>
+ <property xil_pn:name="Power Reduction Map" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Power Reduction Par" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Preferred Language" xil_pn:value="Verilog" xil_pn:valueState="default"/>
+ <property xil_pn:name="Priority Encoder Extraction" xil_pn:value="Yes" xil_pn:valueState="default"/>
+ <property xil_pn:name="Produce Verbose Report" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Project Description" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Property Specification in Project File" xil_pn:value="Store all values" xil_pn:valueState="default"/>
+ <property xil_pn:name="RAM Extraction" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="RAM Style" xil_pn:value="Auto" xil_pn:valueState="default"/>
+ <property xil_pn:name="ROM Extraction" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="ROM Style" xil_pn:value="Auto" xil_pn:valueState="default"/>
+ <property xil_pn:name="Read Cores" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Regenerate Core" xil_pn:value="Under Current Project Setting" xil_pn:valueState="default"/>
+ <property xil_pn:name="Register Balancing" xil_pn:value="No" xil_pn:valueState="default"/>
+ <property xil_pn:name="Register Duplication" xil_pn:value="Off" xil_pn:valueState="default"/>
+ <property xil_pn:name="Register Duplication Xst" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Release Write Enable (Output Events)" xil_pn:value="Default (6)" xil_pn:valueState="default"/>
+ <property xil_pn:name="Rename Design Instance in Testbench File to" xil_pn:value="UUT" xil_pn:valueState="default"/>
+ <property xil_pn:name="Rename Top Level Architecture To" xil_pn:value="Structure" xil_pn:valueState="default"/>
+ <property xil_pn:name="Rename Top Level Entity to" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Rename Top Level Module To" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Report Fastest Path(s) in Each Constraint" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Report Fastest Path(s) in Each Constraint Post Trace" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Report Paths by Endpoint" xil_pn:value="3" xil_pn:valueState="default"/>
+ <property xil_pn:name="Report Paths by Endpoint Post Trace" xil_pn:value="3" xil_pn:valueState="default"/>
+ <property xil_pn:name="Report Type" xil_pn:value="Verbose Report" xil_pn:valueState="default"/>
+ <property xil_pn:name="Report Type Post Trace" xil_pn:value="Verbose Report" xil_pn:valueState="default"/>
+ <property xil_pn:name="Report Unconstrained Paths" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Report Unconstrained Paths Post Trace" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Reset DCM if SHUTDOWN & AGHIGH performed" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Reset On Configuration Pulse Width" xil_pn:value="100" xil_pn:valueState="default"/>
+ <property xil_pn:name="Resource Sharing" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Retain Hierarchy" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Router Effort Level (Overrides Overall Level)" xil_pn:value="None" xil_pn:valueState="default"/>
+ <property xil_pn:name="Run Design Rules Checker (DRC)" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Run for Specified Time" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Run for Specified Time Map" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Run for Specified Time Par" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Run for Specified Time Translate" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Safe Implementation" xil_pn:value="No" xil_pn:valueState="default"/>
+ <property xil_pn:name="Security" xil_pn:value="Enable Readback and Reconfiguration" xil_pn:valueState="default"/>
+ <property xil_pn:name="Selected Simulation Root Source Node Behavioral" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Selected Simulation Root Source Node Post-Map" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Selected Simulation Root Source Node Post-Route" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Selected Simulation Root Source Node Post-Translate" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Selected Simulation Source Node" xil_pn:value="UUT" xil_pn:valueState="default"/>
+ <property xil_pn:name="Shift Register Extraction" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Show All Models" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Simulation Model Target" xil_pn:value="Verilog" xil_pn:valueState="default"/>
+ <property xil_pn:name="Simulation Run Time ISim" xil_pn:value="1000 ns" xil_pn:valueState="default"/>
+ <property xil_pn:name="Simulation Run Time Map" xil_pn:value="1000 ns" xil_pn:valueState="default"/>
+ <property xil_pn:name="Simulation Run Time Par" xil_pn:value="1000 ns" xil_pn:valueState="default"/>
+ <property xil_pn:name="Simulation Run Time Translate" xil_pn:value="1000 ns" xil_pn:valueState="default"/>
+ <property xil_pn:name="Simulator" xil_pn:value="ISim (VHDL/Verilog)" xil_pn:valueState="default"/>
+ <property xil_pn:name="Slice Packing" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Slice Utilization Ratio" xil_pn:value="100" xil_pn:valueState="default"/>
+ <property xil_pn:name="Specify 'define Macro Name and Value" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Specify Top Level Instance Names Behavioral" xil_pn:value="Default" xil_pn:valueState="default"/>
+ <property xil_pn:name="Specify Top Level Instance Names Post-Map" xil_pn:value="Default" xil_pn:valueState="default"/>
+ <property xil_pn:name="Specify Top Level Instance Names Post-Route" xil_pn:value="Default" xil_pn:valueState="default"/>
+ <property xil_pn:name="Specify Top Level Instance Names Post-Translate" xil_pn:value="Default" xil_pn:valueState="default"/>
+ <property xil_pn:name="Speed Grade" xil_pn:value="-4" xil_pn:valueState="non-default"/>
+ <property xil_pn:name="Starting Placer Cost Table (1-100) Map" xil_pn:value="1" xil_pn:valueState="default"/>
+ <property xil_pn:name="Starting Placer Cost Table (1-100) Par" xil_pn:value="1" xil_pn:valueState="default"/>
+ <property xil_pn:name="Synthesis Tool" xil_pn:value="XST (VHDL/Verilog)" xil_pn:valueState="default"/>
+ <property xil_pn:name="Target Simulator" xil_pn:value="Please Specify" xil_pn:valueState="default"/>
+ <property xil_pn:name="Timing Mode Map" xil_pn:value="Non Timing Driven" xil_pn:valueState="default"/>
+ <property xil_pn:name="Timing Mode Par" xil_pn:value="Performance Evaluation" xil_pn:valueState="default"/>
+ <property xil_pn:name="Top-Level Module Name in Output Netlist" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Top-Level Source Type" xil_pn:value="HDL" xil_pn:valueState="default"/>
+ <property xil_pn:name="Trim Unconnected Signals" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Tristate On Configuration Pulse Width" xil_pn:value="0" xil_pn:valueState="default"/>
+ <property xil_pn:name="Unused IOB Pins" xil_pn:value="Pull Down" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use 64-bit PlanAhead on 64-bit Systems" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Clock Enable" xil_pn:value="Yes" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Custom Project File Behavioral" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Custom Project File Post-Map" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Custom Project File Post-Route" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Custom Project File Post-Translate" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Custom Simulation Command File Behavioral" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Custom Simulation Command File Map" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Custom Simulation Command File Par" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Custom Simulation Command File Translate" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Custom Waveform Configuration File Behav" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Custom Waveform Configuration File Map" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Custom Waveform Configuration File Par" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Custom Waveform Configuration File Translate" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use LOC Constraints" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use RLOC Constraints" xil_pn:value="Yes" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Smart Guide" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Synchronous Reset" xil_pn:value="Yes" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Synchronous Set" xil_pn:value="Yes" xil_pn:valueState="default"/>
+ <property xil_pn:name="Use Synthesis Constraints File" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="User Browsed Strategy Files" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="UserID Code (8 Digit Hexadecimal)" xil_pn:value="0xFFFFFFFF" xil_pn:valueState="default"/>
+ <property xil_pn:name="VHDL Source Analysis Standard" xil_pn:value="VHDL-93" xil_pn:valueState="default"/>
+ <property xil_pn:name="Value Range Check" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="Verilog 2001 Xst" xil_pn:value="true" xil_pn:valueState="default"/>
+ <property xil_pn:name="Verilog Macros" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="Wait for DCI Match (Output Events) virtex2" xil_pn:value="Auto" xil_pn:valueState="default"/>
+ <property xil_pn:name="Wait for DLL Lock (Output Events)" xil_pn:value="Default (NoWait)" xil_pn:valueState="default"/>
+ <property xil_pn:name="Working Directory" xil_pn:value="." xil_pn:valueState="non-default"/>
+ <property xil_pn:name="Write Timing Constraints" xil_pn:value="false" xil_pn:valueState="default"/>
+ <property xil_pn:name="XOR Collapsing" xil_pn:value="true" xil_pn:valueState="default"/>
+ <!-- -->
+ <!-- The following properties are for internal use only. These should not be modified.-->
+ <!-- -->
+ <property xil_pn:name="PROP_BehavioralSimTop" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="PROP_DesignName" xil_pn:value="minimigmac" xil_pn:valueState="non-default"/>
+ <property xil_pn:name="PROP_DevFamilyPMName" xil_pn:value="spartan3" xil_pn:valueState="default"/>
+ <property xil_pn:name="PROP_FPGAConfiguration" xil_pn:value="FPGAConfiguration" xil_pn:valueState="default"/>
+ <property xil_pn:name="PROP_PostMapSimTop" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="PROP_PostParSimTop" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="PROP_PostSynthSimTop" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="PROP_PostXlateSimTop" xil_pn:value="" xil_pn:valueState="default"/>
+ <property xil_pn:name="PROP_PreSynthesis" xil_pn:value="PreSynthesis" xil_pn:valueState="default"/>
+ <property xil_pn:name="PROP_intProjectCreationTimestamp" xil_pn:value="2010-10-22T13:02:34" xil_pn:valueState="non-default"/>
+ <property xil_pn:name="PROP_intWbtProjectID" xil_pn:value="144A4261866D6D927EEA5C3F80F1E6AA" xil_pn:valueState="non-default"/>
+ <property xil_pn:name="PROP_intWorkingDirLocWRTProjDir" xil_pn:value="Same" xil_pn:valueState="non-default"/>
+ <property xil_pn:name="PROP_intWorkingDirUsed" xil_pn:value="No" xil_pn:valueState="non-default"/>
+ </properties>
+
+ <bindings/>
+
+ <libraries/>
+
+ <autoManagedFiles>
+ <!-- The following files are identified by `include statements in verilog -->
+ <!-- source files and are automatically managed by Project Navigator. -->
+ <!-- -->
+ <!-- Do not hand-edit this section, as it will be overwritten when the -->
+ <!-- project is analyzed based on files automatically identified as -->
+ <!-- include files. -->
+ </autoManagedFiles>
+
+</project>
--- /dev/null
+// File: 18f252.lkr
+// Sample linker script for the PIC18F252 processor
+
+// Not intended for use with MPLAB C18. For C18 projects,
+// use the linker scripts provided with that product.
+
+// Modified for larger data bank to fit secbuf
+
+LIBPATH .
+
+CODEPAGE NAME=page START=0x0 END=0x7FFF
+CODEPAGE NAME=idlocs START=0x200000 END=0x200007 PROTECTED
+CODEPAGE NAME=config START=0x300000 END=0x30000D PROTECTED
+CODEPAGE NAME=devid START=0x3FFFFE END=0x3FFFFF PROTECTED
+CODEPAGE NAME=eedata START=0xF00000 END=0xF000FF PROTECTED
+
+ACCESSBANK NAME=accessram START=0x0 END=0x7F
+DATABANK NAME=gpr0 START=0x80 END=0xFF
+DATABANK NAME=gpr1 START=0x100 END=0x1FF
+DATABANK NAME=gpr2 START=0x200 END=0x2FF
+DATABANK NAME=gpr3 START=0x300 END=0x3FF
+DATABANK NAME=gpr45 START=0x400 END=0x5FF
+ACCESSBANK NAME=accesssfr START=0xF80 END=0xFFF PROTECTED
--- /dev/null
+OBJS = main.o hardware.o mmc.o fat16.o scsi.o
+
+LIBS = /usr/share/sdcc/lib/pic16/libc18f.lib
+LIBS += /usr/share/sdcc/lib/pic16/libio18f252.lib
+
+SDCC_BASE_FLAGS = -mpic16 -p18f252 -V --debug
+SDCC_CFLAGS = $(SDCC_BASE_FLAGS) --obanksel=2 --denable-peeps --optimize-cmp --optimize-df
+SDCC_LDFLAGS = $(SDCC_BASE_FLAGS) -Wl-c -Wl-m -Wl-s18f252_mod.lkr
+
+
+%.o : %.c
+ sdcc $(SDCC_CFLAGS) -c $^
+
+minimigmac.hex: $(OBJS) 18f252_mod.lkr
+ sdcc $(SDCC_LDFLAGS) -o$@ $(OBJS) $(LIBS)
+
+all: minimigmac.hex
+
+clean:
+ rm -rf *.o
+ rm -rf *.asm
+ rm -rf *.lst
+ rm -rf *.adb
+
+distclean: clean
+ rm -rf *~
+
+
--- /dev/null
+/*
+Copyright 2005, 2006, 2007 Dennis van Weeren
+
+This file is part of Minimig
+
+Minimig is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+Minimig is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+This is a simple FAT16 handler. It works on a sector basis to allow fastest acces on disk
+images.
+
+Simplified from Minimig version
+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "mmc.h"
+#include "fat16.h"
+#include "fat16_defs.h"
+
+/* Enable / Disable Debug info */
+#undef DEBUG_FAT
+
+/* Structure containing selected partition data */
+struct partition_info {
+ unsigned char fatType; // 0x10=FAT16, 0x20=FAT32
+ unsigned long partStart; // start LBA of partition
+ unsigned long fatStart; // start LBA of first FAT table
+ unsigned long dataStart; // start LBA of data field
+ unsigned long rootDirCluster; // root directory cluster
+ unsigned long rootDirStart; // start LBA of root directory table
+ unsigned short rootDirEntries; // start LBA of root directory table
+ unsigned char fatNo; // number of FAT tables
+ unsigned char clusterSize; // size of a cluster in blocks
+ unsigned long clusterMask; // binary mask of cluster number
+};
+
+
+/* Open file info */
+struct file_info {
+ unsigned char name[12]; // Short file name
+ unsigned char attributes; // File attributes
+ unsigned short entry; // File-entry index in directory table
+ unsigned long sector; // Sector index in file
+ unsigned long len; // File size
+ unsigned long cluster; // Current cluster
+ unsigned long firstCluster; // First file cluster
+};
+
+
+#define MAX_LFN_SIZE 128 // Maximum supported long file name, default is 256
+
+/* Directory Seek Modes */
+#define DIRECTORY_BROWSE_START 0 // start search from beginning of directory
+#define DIRECTORY_BROWSE_NEXT 2 // find next file in directory
+
+/* Sector buffer. Shared globally to save space on PIC */
+static unsigned long cached_cur_lba;
+
+/* Selected Partition */
+static struct partition_info fat_partition;
+
+static unsigned char longFilename[MAX_LFN_SIZE]; // Default long filename entry
+
+static struct file_info fat_cur_file; // global file handle
+static struct file_info fat_cur_dir; // global directory file handle
+
+// Long filename entry char pos
+static const unsigned char charLFNPos[] = { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 };
+
+
+#ifdef DEBUG_FAT
+#define pr_debug(args...) printf_tiny(args)
+#else
+#define pr_debug(args...)
+#endif
+
+/* Caching functions for MMC */
+static unsigned char cached_read(unsigned long lba)
+{
+ if (cached_cur_lba == lba)
+ return true;
+
+ if (!mmc_read(lba, secbuf))
+ return false;
+
+ cached_cur_lba = lba;
+ return true;
+}
+
+static unsigned char cached_write(unsigned long lba)
+{
+ cached_cur_lba = lba;
+ return mmc_write(lba, secbuf);
+}
+
+/* Calculate LBA from current file position cluster and sector */
+static unsigned long fat_get_lba(struct file_info near *file)
+{
+ unsigned long lba;
+
+ /* Calc sector to read, when first cluster is 0 it is root directory of FAT16 */
+ if (file->firstCluster == 0) {
+ lba = fat_partition.rootDirStart;
+ lba += file->sector;
+ } else {
+ lba = fat_partition.dataStart; // start of data in partition
+ lba += (file->cluster-2) * fat_partition.clusterSize; // cluster offset
+ lba += file->sector & ~fat_partition.clusterMask; // sector offset in cluster
+ }
+ return lba;
+}
+
+/* Find next file cluster from the FAT */
+static unsigned char fat_get_next_cluster(struct file_info near *file)
+{
+ unsigned short index;
+ unsigned long fatSectorLba;
+
+ /* calculate sector that contains FAT-link */
+ fatSectorLba = fat_partition.fatStart;
+
+ if (fat_partition.fatType & 0x10) {
+ /* FAT16 256 cluster infos in sector on disk */
+ fatSectorLba += (file->cluster) >> 8;
+
+ /* calculate offset */
+ index = (file->cluster) & 0xFF;
+ index <<= 1;
+ } else {
+ /* FAT32 128 cluster infos in sector on disk */
+ fatSectorLba += (file->cluster) >> 7;
+
+ /* FAT32 128 cluster infos in sector */
+ index = (file->cluster) & 0x7F;
+ index <<= 2;
+ }
+
+ /* Read sector of FAT */
+ if (!cached_read(fatSectorLba))
+ return false;
+
+ if (fat_partition.fatType & 0x10) {
+ /* get FAT-link */
+ file->cluster = *((unsigned short*)&secbuf[index]);
+
+ /* Exit if end of chain */
+ if (file->cluster == 0xffff)
+ return false;
+ } else {
+ /* For FAT32 Read long instead short */
+ file->cluster = *((unsigned long*)&secbuf[index]);
+ file->cluster &= 0x0FFFFFFF;
+
+ /* Exit if end of chain */
+ if (file->cluster == 0x0fffffff)
+ return false;
+ }
+ return true;
+}
+
+/* Point to next sector in file */
+static unsigned char fat_do_next_sector(struct file_info near *file)
+{
+ /* increment sector index */
+ file->sector++;
+
+ /* if we are now in another cluster, look up cluster */
+ if ((file->sector & ~fat_partition.clusterMask) == 0)
+ if (!fat_get_next_cluster(file))
+ return false;
+
+ pr_debug(" n s=0x%lx c=%lx\r\n", file->sector, file->cluster);
+
+ return true;
+}
+
+/* Seek within a file (or directory) entry */
+static unsigned char fat_file_do_seek(struct file_info near *file, unsigned long sector)
+{
+ long currentClusterNo;
+ long clusterNo;
+
+ /* Calculate Current ClusterNo to avoid seek if new secor is on
+ * same cluster
+ */
+ currentClusterNo = (long)file->sector;
+ currentClusterNo /= (long)fat_partition.clusterSize;
+
+ /* Sector in file to read */
+ file->sector = sector;
+
+ /* Calculate new ClusterNO in file */
+ clusterNo = (long)file->sector;
+ clusterNo /= (long)fat_partition.clusterSize;
+
+ /* Calc ClusterNo Difference */
+ currentClusterNo -= clusterNo;
+
+ /* Check if we are on same cluster */
+ if (currentClusterNo == 0) {
+ pr_debug(" S s=0x%lx c=%lx\r\n", file->sector, file->cluster);
+ return true;
+ }
+
+ /* Reset current cluster in file to first */
+ file->cluster = file->firstCluster;
+
+ /* If first cluster in file exit */
+ if (clusterNo == 0)
+ return true;
+
+ /* Loop through cluster links */
+ while(--clusterNo >= 0) {
+ if (!fat_get_next_cluster(file))
+ return false;
+ // pr_debug(" -seek: clusterNo: 0x%lx", clusterNo);
+ // pr_debug(" , cluster: 0x%lx\r\n", file->cluster);
+ }
+
+ pr_debug(" s s=0x%lx c=%lx\r\n", file->sector, file->cluster);
+ return true;
+}
+
+static unsigned char fat_process_dir_entry(struct file_info near *file, union FAT_directoryEntry * dirEntry)
+{
+ short i;
+ short char_offset;
+
+ /* Check if we are at directory end */
+ if (dirEntry->entry.shortName.name[0] == FAT_ENTRY_FREE) {
+ pr_debug("Entry free %d\r\n", file->entry);
+ return 0;
+ }
+
+ /* Check if file deleted */
+ if (FAT_ENTRY_DELETED != dirEntry->entry.shortName.name[0]) {
+ /* Check if longfilename */
+ if(FAT_ATTRIB_LFN_TEXT == dirEntry->entry.attributes) {
+ /* Calc pos to copy part of LFN */
+ char_offset = ((dirEntry->LFN.sequenceNo & 0x3f)-1) * 13;
+
+ /* Copy part of LFN */
+ for(i = 0; i < 13 && (char_offset + i) < (MAX_LFN_SIZE - 1); i++)
+ longFilename[char_offset + i] = dirEntry->bytes[charLFNPos[i]];
+ } else if (dirEntry->entry.attributes &
+ (FAT_ATTRIB_HIDDEN | FAT_ATTRIB_SYSTEM | FAT_ATTRIB_VOLUME)) {
+ pr_debug("FAT: Skiping entry no:%d attr:0x%02X\n",
+ file->entry, dirEntry->entry.attributes);
+ /* Clear longfile name just in case */
+ memset(longFilename,0,MAX_LFN_SIZE);
+ } else {
+ /* Copy name */
+ memcpy(&file->name, &dirEntry->entry.shortName, 11);
+ file->name[11] = 0x00; /* In theory not needed since never trashed */
+ file->attributes = dirEntry->entry.attributes;
+ file->len = dirEntry->entry.length; /* get length of file */
+ file->firstCluster = (unsigned long)dirEntry->entry.firstClusterLow; // get first cluster of file
+ /* FAT32 high bytes of cluster */
+ if (fat_partition.fatType & 0x20)
+ file->firstCluster |= ((unsigned long)dirEntry->entry.firstClusterHigh) << 16;
+ file->cluster = file->firstCluster; /* Copy current cluster to first cluster */
+ file->sector = 0; /* Reset current sector index */
+
+ /* Check if there is no LFN than copy short name as LFN */
+ /* Skip entries that have LFN and entries starting with '.' (Parent and Current dir) */
+ if(!longFilename[0] && (file->name[0] != '.')) {
+ char_offset = 0;
+ for(i=0; i < 11; i++) {
+ if(file->name[i] != ' ')
+ longFilename[char_offset++] = file->name[i];
+ if(7 == i)
+ longFilename[char_offset++] = '.';
+ }
+ /* Delete Last dot */
+ if(longFilename[char_offset-1]=='.')
+ char_offset--;
+ longFilename[char_offset] = 0x00;
+ }
+ return 1;
+ }
+ }
+ return 2;
+}
+
+/* Iterate directory entries */
+static unsigned char fat_get_dir_entry(struct file_info near *file, struct file_info near *dir,
+ unsigned char mode)
+{
+ short entryOffset;
+ char rc;
+
+ /* Clear long file name */
+ memset(longFilename,0,MAX_LFN_SIZE);
+
+ /* Check for iteration mode */
+ switch(mode) {
+ case DIRECTORY_BROWSE_NEXT:
+ file->entry++;
+ break;
+ case DIRECTORY_BROWSE_START:
+ default:
+ file->entry = 0;
+ break;
+ }
+
+ /* Seek directory to selected file entry */
+ if(!fat_file_do_seek(dir, file->entry >> 4))
+ return false;
+
+ /* Infinite loop for search */
+ while(1) {
+ /* Read sector in buffer */
+ if (!cached_read(fat_get_lba(dir)))
+ return false;
+
+ /* Calculate file entry offset in directory sector, 16 entries in sector 32 byte big */
+ entryOffset = (file->entry & 0xF) << 5;
+
+ /* Process directory entry */
+ rc = fat_process_dir_entry(file, (union FAT_directoryEntry *)(secbuf + entryOffset));
+ if (!rc)
+ return false;
+ if (rc == 1)
+ return true;
+
+ /* Go to next Fat Entry */
+ file->entry++;
+
+ /* Check should we seek directory to next sector */
+ if (!(file->entry & 0xF))
+ if (!fat_do_next_sector(dir))
+ return false;
+ }
+}
+
+
+/* Move to root directory */
+static void fat_cd_root_dir(struct file_info near *dir)
+{
+ /* Clear Entry */
+ memset(dir, 0, sizeof(struct file_info));
+ dir->name[0] = '/';
+ dir->attributes = FAT_ATTRIB_DIR;
+ dir->firstCluster = fat_partition.rootDirCluster;
+ dir->cluster = dir->firstCluster;
+}
+
+/* Find file by name and open it, return len or -1 */
+long fat_open(const unsigned char *name)
+{
+ struct file_info near *file = &fat_cur_file;
+ unsigned char mode = DIRECTORY_BROWSE_START;
+ unsigned char i;
+
+ pr_debug("FAT: Open file: %s\n",name);
+
+ while (fat_get_dir_entry(file, &fat_cur_dir, mode)) {
+ pr_debug(" found: %s...",file->name);
+ for(i=0; i<11; i++)
+ if (file->name[i]!=name[i])
+ break;
+ if (i==11) {
+ pr_debug(" GOOD !\n");
+ return file->len;
+ }
+ pr_debug("\n");
+ mode = DIRECTORY_BROWSE_NEXT;
+ }
+
+ pr_debug("FAT: File not found\n");
+
+ return -1;
+}
+
+unsigned char fat_file_read(void)
+{
+ return cached_read(fat_get_lba(&fat_cur_file));
+}
+
+unsigned char fat_file_write(void)
+{
+ return cached_write(fat_get_lba(&fat_cur_file));
+}
+
+unsigned char fat_file_seek(unsigned long sector)
+{
+ return fat_file_do_seek(&fat_cur_file, sector);
+}
+
+unsigned char fat_file_next_sector(void)
+{
+ return fat_do_next_sector(&fat_cur_file);
+}
+
+void fat_inval_cache(void)
+{
+ cached_cur_lba = 0xffffffff;
+}
+
+/* fat_init()
+ *
+ * Checks if a card is present. if a card is present it will check for
+ * a valid FAT16 or FAT32 primary partition
+ */
+unsigned char fat_init(void)
+{
+ unsigned long fatsize; // size of fat
+ unsigned long dirsize; // size of directory region in sectors
+
+ struct MBR_Disk *mbr = (struct MBR_Disk *)secbuf;
+ struct FAT_Boot_Sector *boot = (struct FAT_Boot_Sector *)secbuf;
+
+ /* Invalidate cache */
+ cached_cur_lba = 0xffffffff;
+
+ /* Read partition sector */
+ if (!cached_read(0)) {
+ pr_debug("FAT: Error Unable to read partition sector\n");
+ return false;
+ }
+
+ /* Check for signature */
+ if (mbr->signature != 0xAA55) {
+ pr_debug("FAT: Error invalid FAT signature\n");
+ return false;
+ }
+
+ /* Check first partition type */
+ switch (mbr->partitions[0].Type) {
+ case PARTITION_TYPE_FAT16_32MB:
+ case PARTITION_TYPE_FAT16:
+ /* First partition filesystem type: FAT16 */
+ fat_partition.fatType = 0x10;
+ /* Get start of first partition */
+ fat_partition.partStart = mbr->partitions[0].LBAFirst;
+ break;
+
+ case PARTITION_TYPE_FAT32:
+ case PARTITION_TYPE_FAT32_LBA:
+ /* First partition filesystem type: FAT32 */
+ fat_partition.fatType = 0x20;
+ /* get start of first partition */
+ fat_partition.partStart = mbr->partitions[0].LBAFirst;
+ break;
+
+ default:
+ pr_debug("FAT: Error no supported partition found\n");
+ return false;
+ }
+
+ /* Read boot sector */
+ if (!cached_read(fat_partition.partStart)) {
+ pr_debug("FAT: Error unable to read boot sector\n");
+ return false;
+ }
+
+ /* Check for near-jump or short-jump opcode */
+ if (0xE9 != boot->jumpInstruction[0] && 0xEB != boot->jumpInstruction[0]) {
+ pr_debug("FAT: Error invalid 0x%x boot sector jump\n",
+ boot->jumpInstruction[0]);
+ return false;
+ }
+
+ /* Check if blocksize is really 512 bytes */
+ if (0x200 != boot->bytesPerSector) {
+ pr_debug("FAT: Error block size not 512 bytes it is 0x%x\n",
+ boot->bytesPerSector);
+ return false;
+ }
+
+ /* Check medium descriptor byte, must be 0xf8 for hard drive */
+ if (0xF8 != boot->mediaDescriptor) {
+ pr_debug("Error invalid media descriptor\n");
+ return false;
+ }
+
+ /* Get cluster size */
+ fat_partition.clusterSize = boot->sectorsPerCluster;
+ /* calculate cluster mask */
+ fat_partition.clusterMask = ~(fat_partition.clusterSize-1);
+
+
+ /* Calculate drive's parameters from bootsector, first up is size of directory */
+ /* Get Max ROOT Dir entries FAT16 only !!*/
+ fat_partition.rootDirEntries = boot->maxRootEntries;
+
+ /* Calculate start of FAT, size of FAT and number of FAT's */
+ fat_partition.fatStart = fat_partition.partStart + boot->reservedSectorCount;
+ fat_partition.fatNo = boot->noOfFATs;
+
+ /* When FAT16 */
+ if (0x10 == fat_partition.fatType) {
+ fatsize = boot->sectorsPerFAT;
+ // Fat 16 Root dir cluster
+ fat_partition.rootDirCluster = 0;
+ // calculate start of FAT16 ROOT directory
+ fat_partition.rootDirStart = fat_partition.fatStart + (fat_partition.fatNo * fatsize);
+ // Calculate dire size in sectors
+ dirsize = ((fat_partition.rootDirEntries<<5)+511)>>9;
+ // calculate start of data
+ fat_partition.dataStart = fat_partition.rootDirStart + dirsize;
+ } else {
+ fatsize = boot->extParams.fat32Ext.sectorsPerFAT;
+ // calculate data start
+ fat_partition.dataStart = fat_partition.fatStart + (fat_partition.fatNo * fatsize);
+ // Fat 32 Root dir cluster
+ fat_partition.rootDirCluster = boot->extParams.fat32Ext.rootDirCluster;
+ // Get FAT32 root dir start
+ fat_partition.rootDirStart = fat_partition.dataStart +
+ (fat_partition.rootDirCluster-2) * fat_partition.clusterSize;
+ }
+
+ /* Open Root Directory */
+ fat_cd_root_dir(&fat_cur_dir);
+
+ pr_debug("FAT%d partition found.\n", fat_partition.fatType);
+ pr_debug("Fat Size: %ld\n",fatsize);
+ pr_debug("No of fats: %d\n",fat_partition.fatNo);
+ pr_debug("Fat start: %ld\n", fat_partition.fatStart);
+ pr_debug("Direntries: %d\n",fat_partition.rootDirEntries);
+ pr_debug("Dir start: %ld\n", fat_partition.rootDirStart);
+ pr_debug("Data start: %ld\n", fat_partition.dataStart);
+ pr_debug("Cluster size: %d\n",fat_partition.clusterSize);
+ pr_debug("Cluster mask: %lx\n",fat_partition.clusterMask);
+
+ return true;
+}
+
--- /dev/null
+#ifndef __FAT16_H__\r
+#define __FAT16_H__\r
+\r
+/* Global sector buffer, data for read/write actions is stored here\r
+ *\r
+ * Any operation (init, open, seek and read) will clobber it. If you\r
+ * modify this buffer content, you _MUST_ either follow with a\r
+ * fat_file_write or a fat_inval_cache _BEFORE_ any other open, read\r
+ * or seek operation\r
+ */\r
+extern unsigned char secbuf[512];\r
+\r
+/* Initialize, find first partition */\r
+unsigned char fat_init(void);\r
+\r
+/* Open a file by name, one open file at a time, replace the previous one */\r
+long fat_open(const unsigned char *name);\r
+\r
+/* Seek into file WARNING: Can clobber secbuf ! */\r
+unsigned char fat_file_seek(unsigned long sector);\r
+unsigned char fat_file_next_sector(void);\r
+\r
+/* Read and write current file sector */\r
+unsigned char fat_file_read(void);\r
+unsigned char fat_file_write(void);\r
+\r
+/* Invalidate sector cache */\r
+void fat_inval_cache(void);\r
+\r
+#endif /* __FAT16_H__ */\r
--- /dev/null
+#ifndef __FAT_DEFS_H__
+#define __FAT_DEFS_H__
+
+/* Partition Type Defninition */
+#define PARTITION_TYPE_FREE 0x00 /* The partition table entry is not used. */
+#define PARTITION_TYPE_FAT12 0x01 /* The partition contains a FAT12 filesystem. */
+#define PARTITION_TYPE_FAT16_32MB 0x04 /* The partition contains a FAT16 filesystem with
+ 32MB maximum. */
+#define PARTITION_TYPE_EXTENDED 0x05 /* The partition is an extended partition with its own
+ partition table. */
+#define PARTITION_TYPE_FAT16 0x06 /* The partition contains a FAT16 filesystem. */
+#define PARTITION_TYPE_FAT32 0x0b /* The partition contains a FAT32 filesystem. */
+#define PARTITION_TYPE_FAT32_LBA 0x0c /* The partition contains a FAT32 filesystem with LBA. */
+#define PARTITION_TYPE_FAT16_LBA 0x0e /* The partition contains a FAT16 filesystem with LBA. */
+#define PARTITION_TYPE_EXTENDED_LBA 0x0f /* The partition is an extended partition with LBA. */
+#define PARTITION_TYPE_UNKNOWN 0xff /* The partition has an unknown type. */
+
+
+/* File Attributes */
+#define FAT_ATTRIB_READONLY (1 << 0) // The file is read-only.
+#define FAT_ATTRIB_HIDDEN (1 << 1) // The file is hidden.
+#define FAT_ATTRIB_SYSTEM (1 << 2) // The file is a system file.
+#define FAT_ATTRIB_VOLUME (1 << 3) // The file is empty and has the volume label as its name.
+#define FAT_ATTRIB_DIR (1 << 4) // The file is a directory.
+#define FAT_ATTRIB_ARCHIVE (1 << 5) // The file has to be archived.
+#define FAT_ATTRIB_LFN_TEXT (FAT_ATTRIB_VOLUME | FAT_ATTRIB_SYSTEM | FAT_ATTRIB_HIDDEN |\
+ FAT_ATTRIB_READONLY) // 0x0F Long filename entry
+
+#define FAT_LFN_LAST_MASK 0x40 // Last long entry flag
+
+/* First Characters in Fat name */
+#define FAT_ENTRY_FREE 0x00 /* Fat entry is free */
+#define FAT_ENTRY_KANJI 0x05 /* Fat entry is kanji */
+#define FAT_ENTRY_PARENT_DIRECTORY 0x2E /* Fat entry is directory or parent direcotry */
+#define FAT_ENTRY_DELETED 0xE5 /* Fat entry is deleted */
+
+
+/* Define partition structure */
+struct PRIMARY_Partition
+{
+ unsigned char Status; // Partition status
+ unsigned char CHSFirstBlock[3];
+ unsigned char Type; // Partition type
+ unsigned char CHSLastBlock[3];
+ unsigned long LBAFirst; // First LBA block
+ unsigned long LBABlocks; // Number of LBA blocks in partition
+};
+
+
+/* Define Master Boot Record */
+struct MBR_Disk
+{
+ unsigned char bootCode[440]; // Code Area
+ unsigned long diskSignature; // Optional Disk signature
+ unsigned short reserved; // Usually Nulls; 0x0000
+ struct PRIMARY_Partition partitions[4]; // Table of primary partitions
+ // (Four 16-byte entries, IBM Partition Table scheme)
+ unsigned short signature; // MBR signature; 0xAA55
+};
+
+
+/* FAT 16 Extened bios parametars block */
+struct FAT16_ExtBiosParams
+{
+ unsigned char physicalDriveNo; // Physical drive number
+ unsigned char reserved; // Reserved ("current head") In Windows NT bit 0 is
+ // a dirty flag to request chkdsk at boot time.
+ // bit 1 requests surface scan too.[28]
+ unsigned char extBootSignature; // Extended boot signature.Value is 0x29[27] or 0x28.
+ unsigned long ID; // ID (serial number)
+ unsigned char volumeLabel[11]; // Volume Label, padded with blanks (0x20).
+ unsigned char FATFileSystemType[8]; // FAT file system type, padded with blanks (0x20),
+ // e.g.: "FAT12 ", "FAT16 ". This is not meant
+ // to be used to determine drive type, however,
+ // some utilities use it in this way.
+ unsigned char OsBootCode[448]; // Operating system boot code
+};
+
+
+/* FAT32 Extended parametars block */
+struct FAT32_ExtBiosParams
+{
+ unsigned long sectorsPerFAT; // Sectors per file allocation table
+ unsigned short flags; // FAT Flags
+ unsigned short version; // Version
+ unsigned long rootDirCluster; // Cluster number of root directory start
+ unsigned short fsInformationSector; // Sector number of FS Information Sector
+ unsigned short bootSectorCopy; // Sector number of a copy of this boot sector
+ unsigned char reserved1[12]; // Reserved
+ unsigned char physicalDriveNo; // Physical drive number
+ unsigned char reserved2; // Reserved
+ unsigned char extBootSignature; // Extended boot signature.
+ unsigned long ID; // ID (serial number)
+ unsigned char volumeLabel[11]; // Volume Label
+ unsigned char FATFileSystemType[8]; // FAT file system type: "FAT32 "
+ unsigned char OsBootCode[420]; // Operating system boot code
+};
+
+
+/* Fat Boot Sector */
+struct FAT_Boot_Sector
+{
+ unsigned char jumpInstruction[3]; // Jump instruction. This instruction will be executed and will skip past the rest of the (non-executable) header if the partition is booted from. See Volume Boot Record. If the jump is two-byte near jmp it is followed by a NOP instruction.
+ unsigned char oemName[8]; // OEM Name (padded with spaces). This value determines in which system disk was formatted. MS-DOS checks this field to determine which other parts of the boot record can be relied on.[25][26] Common values are IBM 3.3 (with two spaces between the "IBM" and the "3.3"), MSDOS5.0 and MSWIN4.1 and mkdosfs.
+ unsigned short bytesPerSector; // Bytes per sector. A common value is 512, especially for file systems on IDE (or compatible) disks. The BIOS Parameter Block starts here.
+ unsigned char sectorsPerCluster; // Sectors per cluster. Allowed values are powers of two from 1 to 128. However, the value must not be such that the number of bytes per cluster becomes greater than 32 KB.
+ unsigned short reservedSectorCount; // Reserved sector count. The number of sectors before the first FAT in the file system image. Should be 1 for FAT12/FAT16. Usually 32 for FAT32.
+ unsigned char noOfFATs; // Number of file allocation tables. Almost always 2.
+ unsigned short maxRootEntries; // Maximum number of root directory entries. Only used on FAT12 and FAT16, where the root directory is handled specially. Should be 0 for FAT32. This value should always be such that the root directory ends on a sector boundary (i.e. such that its size becomes a multiple of the sector size). 224 is typical for floppy disks.
+ unsigned short totalSectorsFAT16; // Total sectors (if zero, use 4 byte value at offset totalSectorsFAT32)
+ unsigned char mediaDescriptor; // Media descriptor 0xF8 Fixed disk (i.e. Hard disk).
+ unsigned short sectorsPerFAT; // Sectors per File Allocation Table for FAT12/FAT16
+ unsigned short sectorsPerTrack; // Sectors per track
+ unsigned short noHeads; // Number of heads
+ unsigned long hiddenSectors; // Hidden sectors
+ unsigned long totalSectorsFAT32; // Total sectors (if greater than 65535; otherwise, see offset totalSectorsFAT16)
+ union
+ {
+ struct FAT16_ExtBiosParams fat16Ext;
+ struct FAT32_ExtBiosParams fat32Ext;
+ } extParams;
+ unsigned short signature; // Boot sector signature (0x55 0xAA)
+};
+
+
+// FAT Directory Entry
+struct FAT_dirEntryDefault
+{
+ struct
+ {
+ unsigned char name[8]; // File name
+ unsigned char ext[3]; // File name extension
+ } shortName; // Short file name
+ unsigned char attributes; // File attributes
+ unsigned char reserved; // Reserved
+ unsigned char createTimeFineRes; // Create time fine resolution 10ms
+ unsigned short createTime; // Create time
+ unsigned short createDate; // Create date
+ unsigned short lastAccessDate; // Last access date
+ unsigned short firstClusterHigh; // First cluster high two bytes (FAT32 only)
+ unsigned short modifiedTime; // Last modified time
+ unsigned short modifiedDate; // Last modified date
+ unsigned short firstClusterLow; // First cluster low two bytes
+ unsigned long length; // File length
+};
+
+
+// FAT LFN Directory entry structure
+struct FAT_dirEntryLFN
+{
+ unsigned char sequenceNo; // Sequence number for long file name entry
+ unsigned short fiveUTF16[5]; // Five UTF16 characters
+ unsigned char attributes; // Attributes always 0x0F
+ unsigned char type; // LFN entry type always 0x00
+ unsigned char checksum; // Dos file name checksum
+ unsigned short sixUTF16[6]; // Six UTF16 characters
+ unsigned short firstCluster; // First cluster always 0x0000
+ unsigned short twoUTF16[2]; // Two UTF16 characters
+};
+
+
+// FAT file entry structure all possible structures combined
+union FAT_directoryEntry
+{
+ struct FAT_dirEntryDefault entry; // Default File Entry
+ struct FAT_dirEntryLFN LFN; // FAT LFN file entry structure
+ unsigned char bytes[32]; // Simple Byte Buffer
+};
+
+
+#endif /* __FAT_DEFS_H__ */
--- /dev/null
+/*\r
+Copyright 2005, 2006, 2007 Dennis van Weeren\r
+\r
+This file is part of Minimig\r
+\r
+Minimig is free software; you can redistribute it and/or modify\r
+it under the terms of the GNU General Public License as published by\r
+the Free Software Foundation; either version 3 of the License, or\r
+(at your option) any later version.\r
+\r
+Minimig is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+GNU General Public License for more details.\r
+\r
+You should have received a copy of the GNU General Public License\r
+along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+\r
+Hardware control routines\r
+\r
+27-11-2005 -started coding\r
+29-01-2006 -done a lot of work\r
+31-01-2006 -added key repeat\r
+06-02-2006 -took out all button handling stuff\r
+\r
+-- Goran Ljubojevic --\r
+2009-11-13 - OsdCommand added\r
+2009-11-21 - small cleanup\r
+2009-12-20 - systimer reset on every hardware init to support propper timings on reset\r
+2009-12-30 - Support for new FPGA firmware added in header file\r
+ - GetFPGAStatus function added\r
+2010-01-29 - ResetFPGA() macro added to header file.\r
+2010-08-21 - YQ100818 FPGA core support\r
+2010-08-26 - Added firmwareConfiguration.h\r
+2010-09-09 - Added _SPI macro to help save space on multiple SPI calls\r
+2010-10-15 - Moved over to minimigmac, coding style changes, etc...\r
+*/\r
+\r
+#include <stdio.h>\r
+\r
+#include "hardware.h"\r
+\r
+static volatile unsigned short systimer;\r
+\r
+/*initialize hardware*/\r
+void hardware_init(void)\r
+{\r
+ /* Disable analog inputs */\r
+ ADCON1 = 0b00000110;\r
+\r
+ /* Initalize output register */\r
+ PORTA = 0b00100011;\r
+ PORTB = 0b00000000;\r
+ PORTC = 0b00010001;\r
+\r
+ /* Enable PORTB weak pullup */\r
+ INTCON2bits.RBPU = 0;\r
+\r
+ /* Initialize SPI to master mode 0, sample on posedge, 5Mhz */\r
+ SSPSTAT = 0x40;\r
+ SSPCON1 = 0x20;\r
+\r
+ /* Initialize input/ouput configuration */\r
+ TRISA = 0b11001100;\r
+ TRISB = 0b00101011;\r
+ TRISC = 0b10010000;\r
+\r
+ /* Initialize serial port */\r
+ SPBRG = 10; /*115200 BAUD @ 20MHz*/\r
+ TXSTA = 0x24;\r
+ RCSTA = 0x90;\r
+\r
+ /* Init timer0, internal clk, prescaler 1:256 */\r
+ T0CON = 0xc7;\r
+ TMR0H = 0;\r
+ TMR0L = 0;\r
+\r
+ /* Enable interrupt for timer 0 */\r
+ INTCONbits.TMR0IE = 1;\r
+ INTCONbits.GIE = 1;\r
+}\r
+\r
+/* interrupt service routine */\r
+static void isr(void) interrupt 1\r
+{\r
+ unsigned short t;\r
+ static unsigned char button_state;\r
+\r
+ /* Clear timer 0 interrupt flag */\r
+ INTCONbits.TMR0IF = 0;\r
+\r
+ /* Set timer to timeout every 10ms\r
+ * @20Mhz --> instruction = 200ns\r
+ * 200ns * 256 * 195 = 10ms\r
+ *\r
+ * Not sure we can just cast, so for now be gross\r
+ * since SDCC doesn't give us a 16-bit TMR0\r
+ */\r
+ t = TMR0L;\r
+ t |= ((unsigned short)TMR0H) << 8;\r
+ t -= 195;\r
+ TMR0H = t >> 8;\r
+ TMR0L = t & 0xff;\r
+\r
+ /* Increment system timer */\r
+ systimer++;\r
+\r
+ /* Debounce button */\r
+ if (check_button()) {\r
+ if (button_state < 10) {\r
+ button_state++;\r
+ if (button_state == 10) {\r
+ putchar('[');\r
+ putchar('N');\r
+ putchar('M');\r
+ putchar('I');\r
+ putchar(']');\r
+ NMI = 1;\r
+ }\r
+ }\r
+ } else {\r
+ button_state = 0;\r
+ NMI = 0;\r
+ }\r
+}\r
+\r
+/* Get system timer + offset (handy for lots of things)*/\r
+unsigned short get_timer(unsigned short offset)\r
+{\r
+ unsigned short r;\r
+\r
+ /* Get system time SAFELY */\r
+ INTCONbits.GIE = 0;\r
+ r = systimer;\r
+ INTCONbits.GIE = 1;\r
+\r
+ /*add offset*/\r
+ r += offset;\r
+\r
+ return r;\r
+}\r
+\r
+/* Check if timer is past given time in <t>\r
+ * it may be maximum 30000 ticks in the future\r
+ */\r
+char check_timer(unsigned short t)\r
+{\r
+ /* calculate difference */\r
+ INTCONbits.GIE = 0;\r
+ t -= systimer;\r
+ INTCONbits.GIE = 1;\r
+\r
+ /* check if <t> has passed */\r
+ return t > 30000;\r
+}\r
+\r
+void wait_timer(unsigned short time)\r
+{\r
+ time = get_timer(time);\r
+ while (!check_timer(time))\r
+ ;\r
+}\r
+\r
+/* SPI-bus. Should inline ? */\r
+unsigned char do_spi(unsigned char d)\r
+{\r
+ SSPBUF = d;\r
+ while (!SSPSTATbits.BF); /*Wait untill controller is ready*/\r
+ return(SSPBUF); /*Return with received value*/ \r
+}\r
+\r
+/* FPAG configuration serial interface */\r
+void shift_fpga(unsigned char byte)\r
+{\r
+ /*bit 0*/\r
+ DIN = 0;\r
+ CCLK = 0;\r
+ if (byte & 0x80)\r
+ DIN = 1;\r
+ CCLK = 1;\r
+\r
+ /*bit 1*/\r
+ DIN = 0;\r
+ CCLK = 0;\r
+ if (byte & 0x40)\r
+ DIN = 1;\r
+ CCLK = 1;\r
+\r
+ /*bit 2*/\r
+ DIN = 0;\r
+ CCLK = 0;\r
+ if (byte & 0x20)\r
+ DIN = 1;\r
+ CCLK = 1;\r
+\r
+ /*bit 3*/\r
+ DIN = 0;\r
+ CCLK = 0;\r
+ if (byte & 0x10)\r
+ DIN = 1;\r
+ CCLK = 1;\r
+\r
+ /*bit 4*/\r
+ DIN = 0;\r
+ CCLK = 0;\r
+ if (byte & 0x08)\r
+ DIN = 1;\r
+ CCLK = 1;\r
+\r
+ /*bit 5*/\r
+ DIN = 0;\r
+ CCLK = 0;\r
+ if (byte & 0x04)\r
+ DIN = 1;\r
+ CCLK = 1;\r
+\r
+ /*bit 6*/\r
+ DIN = 0;\r
+ CCLK = 0;\r
+ if (byte & 0x02)\r
+ DIN = 1;\r
+ CCLK = 1;\r
+\r
+ /*bit 7*/\r
+ DIN = 0;\r
+ CCLK = 0;\r
+ if (byte & 0x01)\r
+ DIN = 1;\r
+ CCLK = 1;\r
+}\r
+\r
+/* Put out a chacter to the serial port */\r
+void putchar(char ch) __wparam\r
+{\r
+ while(TXSTAbits.TRMT == 0);\r
+ TXREG = ch;\r
+ if (ch == 10) {\r
+ while(TXSTAbits.TRMT == 0);\r
+ TXREG = 13;\r
+ }\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
--- /dev/null
+#ifndef __HARDWARE_H__\r
+#define __HARDWARE_H__\r
+\r
+#include <pic18f252.h>\r
+#include <stdbool.h>\r
+\r
+/* IO defines*/\r
+#define PROG_B TRISAbits.TRISA2 /* FPGA config */\r
+#define PROG_B_VAL PORTAbits.RA2 /* FPGA config */\r
+#define DONE PORTBbits.RB3 /* FPGA config */\r
+#define INIT_B PORTAbits.RA3 /* FPGA config */\r
+#define CCLK LATAbits.LATA5 /* FPGA config */\r
+#define DIN LATBbits.LATB7 /* FPGA config */\r
+#define _SW0 PORTBbits.RB0 /* Switch input */\r
+#define _SW1 PORTBbits.RB1 /* Switch input */\r
+#define _M_CD TRISAbits.TRISA0 /* mmc card clock disable */\r
+#define _M_CS PORTCbits.RC0 /* mmc card spi select */\r
+#define _F_CS0 PORTAbits.RA1 /* FGPA spi0 select */\r
+#define SCSI_HSHAKE PORTBbits.RB5 /* SCSI data request */\r
+#define NMI PORTBbits.RB6 /* Programmer switch */\r
+\r
+/* Some IO accessors. static inline somewhat broken with sdcc so\r
+ * I use macros... sad\r
+ */\r
+#define spi_enable_fpga() do { \\r
+ _F_CS0 = 0; \\r
+} while(0)\r
+\r
+#define spi_disable_fpga() do { \\r
+ _F_CS0 = 1; \\r
+} while(0)\r
+\r
+#define spi_enable_osd() do { \\r
+ _F_CS1 = 0; \\r
+} while(0)\r
+\r
+#define spi_disable_osd() do { \\r
+ _F_CS1 = 1; \\r
+} while(0)\r
+\r
+#define spi_enable_sdcard() do { \\r
+ _M_CD = 1; \\r
+ _M_CS = 0; \\r
+} while(0)\r
+\r
+#define spi_disable_sdcard() do { \\r
+ _M_CS = 1; \\r
+ do_spi(0xff); \\r
+ _M_CD = 0; \\r
+} while(0)\r
+\r
+#define check_button(void) (!_SW0)\r
+\r
+#define reset_fpga() do { \\r
+ PROG_B_VAL = 0; \\r
+ PROG_B = 0; \\r
+ PROG_B = 1; \\r
+} while(0)\r
+\r
+#define diskled_on() do { \\r
+ PORTBbits.RB4 = 1; \\r
+} while(0)\r
+\r
+#define diskled_off() do { \\r
+ PORTBbits.RB4 = 0; \\r
+} while(0)\r
+\r
+/* Functions */\r
+extern void hardware_init(void);\r
+\r
+extern unsigned short get_timer(unsigned short offset);\r
+extern char check_timer(unsigned short t);\r
+extern void wait_timer(unsigned short time);\r
+\r
+extern unsigned char do_spi(unsigned char data);\r
+extern void shift_fpga(unsigned char data);\r
+\r
+/* Backbus interface defines */\r
+#define BB_WRITE 0x80\r
+#define BB_AUTOINC 0x40\r
+\r
+#define BB_REG_CTRL_BASE 0x00\r
+#define BB_REG_CPU_BASE 0x10\r
+#define BB_REG_MEM_BASE 0x20\r
+#define BB_REG_IWM_BASE 0x28\r
+#define BB_REG_SCSI_BASE 0x30\r
+#define BB_REG_SCOPE_BASE 0x38\r
+\r
+\r
+#endif /* __HARDWARE_H__ */\r
+\r
--- /dev/null
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "hardware.h"
+#include "mmc.h"
+#include "fat16.h"
+#include "scsi.h"
+
+#define printf printf_tiny
+
+/* Sector buffer. Shared globally to save space on PIC
+ *
+ * XXX In this file to work around a missing bank select problem
+ */
+unsigned char secbuf[512];
+
+static char hex[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c',
+ 'd','e','f'};
+
+/* Temp buffer used for tests */
+static unsigned char c[10];
+
+static unsigned char load_file(char *name, unsigned long addr)
+{
+ unsigned short t;
+ unsigned short n;
+ unsigned char *ptr = secbuf;
+ long size;
+
+ size = fat_open(name);
+ if (size < 0) {
+ printf("File %s not found !\n", name);
+ return false;
+ }
+ fat_file_seek(0);
+
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_AUTOINC | BB_REG_MEM_BASE);
+ do_spi((addr >> 16) & 0xff);
+ do_spi((addr >> 8) & 0xff);
+ do_spi((addr ) & 0xff);
+ spi_disable_fpga();
+
+ t = 0;
+ n = (size + 7) >> 3;
+ do {
+ /* read sector if 512 (64*8) bytes done */
+ if ((t & 0x3f) == 0) {
+ if ((t>>9)&1)
+ diskled_on();
+ else
+ diskled_off();
+ putchar('*');
+ if (!fat_file_read()) {
+ printf("Reading failed !\n");
+ return false;
+ }
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_MEM_BASE | 3);
+ ptr = secbuf;
+ }
+
+ /* send data in packets of 8 bytes */
+ do_spi(*(ptr++));
+ do_spi(*(ptr++));
+ do_spi(*(ptr++));
+ do_spi(*(ptr++));
+ do_spi(*(ptr++));
+ do_spi(*(ptr++));
+ do_spi(*(ptr++));
+ do_spi(*(ptr++));
+ t++;
+
+ /* read next sector if 512 (64*8) bytes done */
+ if ((t & 0x3f) == 0) {
+ spi_disable_fpga();
+ fat_file_next_sector();
+ }
+ if ((t & 0x7ff) == 0)
+ putchar(10);
+ } while (t < n);
+ spi_disable_fpga();
+
+ putchar(10);
+
+ return true;
+}
+
+static unsigned char configure_fpga(void)
+{
+ unsigned short t;
+ unsigned short n;
+ unsigned char *ptr = secbuf;
+ long size;
+
+ /* Reset FGPA configuration sequence */
+ reset_fpga();
+
+ // now wait for INIT to go high
+ t = 50000;
+ while (!INIT_B) {
+ if (--t==0) {
+ printf("FPGA init is NOT high!\n");
+ return false;
+ }
+ }
+ if (DONE) {
+ printf("FPGA done is high before configuration!\n");
+ return false;
+ }
+
+ /* Open bitstream file */
+ size = fat_open("MIMIGMACBIN");
+ if (size < 0) {
+ printf("No FPGA configuration file found!\n");
+ return false;
+ }
+ fat_file_seek(0);
+
+ /* Send all bytes to FPGA in loop */
+ t = 0;
+ n = size >> 3;
+ do {
+ /* read sector if 512 (64*8) bytes done */
+ if ((t & 0x3f) == 0) {
+ if ((t>>9)&1)
+ diskled_on();
+ else
+ diskled_off();
+ putchar('*');
+ if (!fat_file_read()) {
+ printf("Reading failed !\n");
+ return false;
+ }
+ ptr = secbuf;
+ }
+
+ /* send data in packets of 8 bytes */
+ shift_fpga(*(ptr++));
+ shift_fpga(*(ptr++));
+ shift_fpga(*(ptr++));
+ shift_fpga(*(ptr++));
+ shift_fpga(*(ptr++));
+ shift_fpga(*(ptr++));
+ shift_fpga(*(ptr++));
+ shift_fpga(*(ptr++));
+ t++;
+
+ /* read next sector if 512 (64*8) bytes done */
+ if ((t & 0x3f) == 0)
+ fat_file_next_sector();
+ if ((t & 0x7ff) == 0)
+ putchar(10);
+ } while (t < n);
+
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+
+ diskled_off();
+
+ /* check if DONE is high */
+ if (DONE) {
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ shift_fpga(0xff);
+ putchar(10);
+ return true;
+ }
+
+ printf("FPGA done is NOT high!\n");
+ return false;
+}
+
+static void print_latches(void)
+{
+ printf(" RW:%d U:%d L:%d ADDR: %x %x %x DATA: %x %x\n",
+ !!(c[5] & 0x01),
+ !!(c[5] & 0x02),
+ !!(c[5] & 0x04),
+ c[0], c[1], c[2], c[3], c[4]);
+}
+
+#if 1
+static void dump_scope(void)
+{
+ unsigned short i;
+
+ /* Dump scope output */
+ spi_enable_fpga();
+ do_spi(BB_AUTOINC | BB_REG_SCOPE_BASE | 2);
+ do_spi(0xff);
+ c[0] = do_spi(0xff);
+ c[1] = do_spi(0xff);
+ printf("sample count: %x %x\n", c[0], c[1]);
+ spi_disable_fpga();
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_SCOPE_BASE | 4);
+ do_spi(0xff);
+ spi_disable_fpga();
+
+ spi_enable_fpga();
+ do_spi(BB_REG_SCOPE_BASE | 4);
+ do_spi(0xff);
+ // for (i = 0; i < 0x200; i++) {
+ for (i = 0; i < (32768/8); i++) {
+ c[0] = do_spi(0xff);
+ c[1] = do_spi(0xff);
+ c[2] = do_spi(0xff);
+ c[3] = do_spi(0xff);
+ c[4] = do_spi(0xff);
+ c[5] = do_spi(0xff);
+ c[6] = do_spi(0xff);
+ c[7] = do_spi(0xff);
+ printf("%x: %x %x %x %x %x %x %x %x\n", i * 8,
+ c[0], c[1], c[2], c[3],
+ c[4], c[5], c[6], c[7]);
+ }
+ spi_disable_fpga();
+}
+#endif
+
+static unsigned char check_fpga(void)
+{
+ spi_enable_fpga();
+ do_spi(BB_AUTOINC | BB_REG_CTRL_BASE);
+ do_spi(0xff); /* pad */
+ c[0] = do_spi(0xff); /* Read data 0 : ID */
+ c[1] = do_spi(0xff); /* Read data 1 : Version */
+ spi_disable_fpga();
+
+ printf("FPGA ID %x Version %d\n", c[0], c[1]);
+ if (c[0] != 0x42 && c[1] != 1) {
+ printf("Unsupported !\n");
+ return false;
+ }
+
+ /* Test CPU inteface latches */
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_AUTOINC | BB_REG_CPU_BASE);
+ do_spi(0xde);
+ do_spi(0xad);
+ do_spi(0xbf);
+ do_spi(0xf0);
+ do_spi(0x0d);
+ do_spi(0xff);
+ spi_disable_fpga();
+
+ spi_enable_fpga();
+ do_spi(BB_AUTOINC | BB_REG_CPU_BASE);
+ do_spi(0xff);
+ c[0] = do_spi(0xff);
+ c[1] = do_spi(0xff);
+ c[2] = do_spi(0xff);
+ c[3] = do_spi(0xff);
+ c[4] = do_spi(0xff);
+ c[5] = do_spi(0xff);
+ spi_disable_fpga();
+
+ if (c[0] != 0xde || c[1] != 0xad || c[2] != 0xbe ||
+ c[3] != 0xf0 || c[4] != 0x0d) {
+ printf("Latches test FAILED want:\n");
+ printf(" RW:1 U:1 L:1 ADDR: de ad be DATA: f0 0d\n");
+ printf("Got:\n");
+ print_latches();
+ return false;
+ }
+ printf("CPU interface latch test passed !\n");
+
+ return true;
+}
+
+/* Bits inverted from what goes into the SPI request */
+#define SIM_CYCLE_READ 0x00
+#define SIM_CYCLE_WRITE 0x01
+#define SIM_CYCLE_UPPER 0x02
+#define SIM_CYCLE_LOWER 0x04
+#define SIM_CYCLE_WORD (SIM_CYCLE_UPPER | SIM_CYCLE_LOWER)
+
+static unsigned short sim_bus_cycle(unsigned long addr,
+ unsigned short dat,
+ unsigned char flags)
+{
+ unsigned char stat;
+
+ /* Read value at 0x1020 */
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_AUTOINC | BB_REG_CPU_BASE);
+ do_spi((addr >> 16) & 0xff);
+ do_spi((addr >> 8) & 0xff);
+ do_spi((addr ) & 0xff);
+ do_spi((dat >> 8 ) & 0xff);
+ do_spi((dat ) & 0xff);
+ do_spi(~flags);
+ do_spi(0x04); /* ctrl: cycle */
+ spi_disable_fpga();
+
+ /* Wait for stop state */
+ spi_enable_fpga();
+ do_spi(BB_REG_CPU_BASE + 0x7);
+ do_spi(0xff);
+ do
+ stat = do_spi(0xff);
+ while(!(stat & 0x08));
+ spi_disable_fpga();
+
+ /* Read latches */
+ spi_enable_fpga();
+ do_spi(BB_AUTOINC | BB_REG_CPU_BASE | 3);
+ do_spi(0xff);
+ dat = do_spi(0xff);
+ dat <<= 8;
+ dat |= do_spi(0xff);
+ spi_disable_fpga();
+
+ return dat ;
+}
+
+static void dump_latches(void)
+{
+ /* Read latches */
+ spi_enable_fpga();
+ do_spi(BB_AUTOINC | BB_REG_CPU_BASE);
+ do_spi(0xff);
+ c[0] = do_spi(0xff);
+ c[1] = do_spi(0xff);
+ c[2] = do_spi(0xff);
+ c[3] = do_spi(0xff);
+ c[4] = do_spi(0xff);
+ c[5] = do_spi(0xff);
+ spi_disable_fpga();
+ printf(" RW:%d U:%d L:%d ADDR: %x %x %x DATA: %x %x %s\n",
+ !!(c[5] & 0x01),
+ !!(c[5] & 0x02),
+ !!(c[5] & 0x04),
+ c[0], c[1], c[2], c[3], c[4],
+ SCSI_HSHAKE ? " [SCSI]" : "");
+}
+
+static void run_to(unsigned long addr)
+{
+ unsigned char stat;
+
+ /* Set breakpoint */
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_AUTOINC | BB_REG_CPU_BASE | 8);
+ do_spi((addr >> 16) & 0xff);
+ do_spi((addr >> 8) & 0xff);
+ do_spi((addr ) & 0xff);
+ spi_disable_fpga();
+
+ /* Start CPU with BP */
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+ do_spi(0x42); /* RUN + BKEN */
+ spi_disable_fpga();
+
+ /* Wait for stop state */
+ spi_enable_fpga();
+ do_spi(BB_REG_CPU_BASE + 0x7);
+ do_spi(0xff);
+ do {
+ if (SCSI_HSHAKE) {
+ spi_disable_fpga();
+ do_scsi();
+ spi_enable_fpga();
+ do_spi(BB_REG_CPU_BASE + 0x7);
+ do_spi(0xff);
+ }
+ stat = do_spi(0xff);
+ } while(!(stat & 0x08));
+ spi_disable_fpga();
+}
+
+static void step(unsigned short n)
+{
+ unsigned short i;
+
+ for (i = 0; i < n; i++) {
+ /* Step CPU */
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+ do_spi(0x82); /* RUN + STEP */
+ spi_disable_fpga();
+
+#if 0 /* Need in theory but CPU never fast enough */
+ /* Wait for stop state */
+ spi_enable_fpga();
+ do_spi(BB_REG_CPU_BASE + 0x7);
+ do_spi(0xff);
+ do
+ stat = do_spi(0xff);
+ while(!(stat & 0x08));
+ spi_disable_fpga();
+#endif
+ }
+}
+
+void main(void)
+{
+ unsigned char rc;
+ unsigned short i;
+ unsigned short v, v2;
+
+ hardware_init();
+
+ printf("Initializing MMC...\n");
+
+ rc = mmc_init();
+ if (!rc)
+ return;
+
+ printf("Initializing FAT...\n");
+
+ rc = fat_init();
+ if (!rc)
+ return;
+
+ printf("Configuring FPGA...\n");
+
+ if (!configure_fpga())
+ return;
+
+ // printf("Checking FPGA...\n");
+ if (!check_fpga())
+ return;
+
+ printf("Loading ROM file...\n");
+ if (!load_file("MIMIGMACROM", 0x200000))
+ return;
+#if 0
+ printf("Loading Screenshot...\n");
+ if (!load_file("SCRNSHOTRAW", 0x1fa700))
+ return;
+#endif
+
+ printf("Opening SCSI disk image...\n");
+ scsi_open();
+
+#if 0
+ /* Start scope */
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_SCOPE_BASE);
+ do_spi(0x02);
+ spi_disable_fpga();
+
+ /* Read ROM first words */
+ v = sim_bus_cycle(0, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ v2 = sim_bus_cycle(2, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ printf("Mem 0: %x %x\n", v, v2);
+ v = sim_bus_cycle(4, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ v2 = sim_bus_cycle(6, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ printf("Mem 4: %x %x\n", v, v2);
+
+ /* Read ROM las words */
+ v = sim_bus_cycle(0x41fff8, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ v2 = sim_bus_cycle(0x41fffa, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ printf("ROM end 0: %x %x\n", v, v2);
+ v = sim_bus_cycle(0x41fffc, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ v2 = sim_bus_cycle(0x41fffe, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ printf("ROM end 4: %x %x\n", v, v2);
+#endif
+
+ /* Un-reset CPU */
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_CTRL_BASE | 2);
+ do_spi(0x01);
+ spi_disable_fpga();
+
+#if 0
+ {
+ unsigned char b,oldb,t;
+
+ spi_enable_fpga();
+ do_spi(BB_REG_CTRL_BASE | 6);
+ do_spi(0xff);
+ oldb = 0;
+ t = 0, i = 0;
+ while (i < 512) {
+ oldb = b;
+ b = do_spi(0xff);
+ if (b == oldb && t < 0xff)
+ t++;
+ else {
+ secbuf[i++] = b;
+ secbuf[i++] = t;
+ t = 0;
+ }
+ }
+ spi_disable_fpga();
+ for (i = 0; i < 512; i+=2)
+ printf("%x %x\n", secbuf[i], secbuf[i+1]);
+ }
+#endif
+
+#if 0
+
+
+ // printf("Running to main()\n");
+ // run_to(0x4010c8);
+ printf("Stepping...()\n");
+ dump_latches();
+ step(1);
+ dump_latches();
+ step(1);
+
+ for (i = 0; i < 1000; i++) {
+ step(1);
+ dump_latches();
+ }
+
+ for (;;)
+ ;
+#endif
+#if 0
+ for (;;) {
+ printf("Running to exception \n");
+ run_to(0x401336);
+ dump_latches();
+ step(1);
+ }
+#endif
+
+#if 0
+ printf("Running to P_mBootBeep...\n");
+ run_to(0x40028a);
+ dump_latches();
+ step(1);
+ dump_latches();
+ printf("Running to IWM init...\n");
+ run_to(0x4000fc);
+ dump_latches();
+ step(1);
+ dump_latches();
+ printf("Running to P_ChecksumRomAndTestMemory...\n");
+ run_to(0x400d76);
+ dump_latches();
+ step(1);
+ dump_latches();
+ printf("Running to P_BootPart2 (fake hit)...\n");
+ run_to(0x400352);
+ dump_latches();
+ step(1);
+ dump_latches();
+ printf("Running to P_BootPart2...\n");
+ run_to(0x400352);
+ dump_latches();
+ step(1);
+ dump_latches();
+#endif
+
+#if 0
+ /* Read video first words */
+ v = sim_bus_cycle(0x1fa700, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ v2 = sim_bus_cycle(0x1fa702, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ printf("Vid 0: %x %x\n", v, v2);
+ v = sim_bus_cycle(0x1fa704, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ v2 = sim_bus_cycle(0x1fa706, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ printf("Vid 4: %x %x\n", v, v2);
+ printf("Running to setting MemTop...\n");
+ run_to(0x40037e);
+ dump_latches();
+ step(1);
+ dump_latches();
+ step(1);
+ dump_latches();
+ step(1);
+ dump_latches();
+ step(1);
+ dump_latches();
+ v = sim_bus_cycle(0x108, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ printf("Detected memory size: 0x%x0000 bytes\n", v);
+ printf("Running to L53 (CPUflag test)...\n");
+ run_to(0x400584);
+ dump_latches();
+ for (i = 0; i < 50; i++) {
+ step(1);
+ dump_latches();
+ }
+ printf("Running to Bsr P5...\n");
+ run_to(0x4005fa);
+ dump_latches();
+ printf("Running to call to _InitResources...\n");
+ run_to(0x400600);
+ dump_latches();
+ step(1);
+ dump_latches();
+ printf("Running to P_mInitQueue...\n");
+ run_to(0x401a38);
+ dump_latches();
+ step(1);
+ dump_latches();
+ printf("Running to P_InitSCC...\n");
+ run_to(0x40082c);
+ dump_latches();
+ step(1);
+ dump_latches();
+ printf("Running to P_InitKeyboard...\n");
+ run_to(0x402568);
+ dump_latches();
+ step(1);
+ dump_latches();
+ printf("Running to P_mInitIOMgr...\n");
+ run_to(0x40087c);
+ dump_latches();
+ step(1);
+ dump_latches();
+#endif
+#if 0
+
+ printf("Running to P252...\n");
+ run_to(0x407d40);
+ dump_latches();
+
+ /* HWCfgFlags at 0xb22 bit 7 is SCSI */
+ v = sim_bus_cycle(0xb22, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ printf("HWCfgFlags=%x\n", v >> 8);
+ v = sim_bus_cycle(0xb2e, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ printf("SCSIDrvrs =%x\n", v >> 8);
+
+ printf("Running to SCSI driver jump...\n");
+ run_to(0x407dc4);
+ dump_latches();
+ printf("Running to SCSI driver csum...\n");
+ run_to(0x1d36);
+ dump_latches();
+#endif
+
+#if 0
+ printf("Running to after plot disk...\n");
+ run_to(0x4006e4);
+ dump_latches();
+ v = sim_bus_cycle(0x30a, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ v2 = sim_bus_cycle(0x30c, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ printf("DrvQHdr.Head=%x %x\n", v, v2);
+#endif
+
+#if 0
+ printf("Running to P_mInitIOMgr..x1...\n");
+ run_to(0x40089c);
+ dump_latches();
+ step(1);
+ dump_latches();
+ printf("Running to P_mInitIOMgr..x2...\n");
+ run_to(0x4008a6);
+ dump_latches();
+ step(1);
+ dump_latches();
+ printf("Running to P_mInitIOMgr..x3...\n");
+ run_to(0x4008ac);
+ dump_latches();
+#endif
+
+#if 0
+ for (i = 0; i < 1000; i++) {
+ step(1);
+ dump_latches();
+ }
+#endif
+ /* Run CPU */
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+ do_spi(0x02);
+ spi_disable_fpga();
+
+ i = 0;
+ for (;;) {
+ /* This is VERY timing sensitive, the ROM selection
+ * timeout is very short and the fact that we run at
+ * twice the speed doesn't help.
+ * We could improve that by pre-setting the SPI to point
+ * to the SCSI lines register and just read from them,
+ * or we could have some of the selection logic moved to
+ * the FPGA
+ *
+ * Better HACK: The FPGA could delay CPU read cycles
+ * from the bus status register when SEL
+ * is set by a significant amount in the
+ * FPGA thus allowing a lot more time for
+ * the selection process
+ */
+ if (SCSI_HSHAKE)
+ do_scsi();
+ }
+
+#if 0
+ printf("Running to E_Sony_Open...\n");
+ run_to(0x417d9e);
+ dump_latches();
+ step(1);
+ dump_latches();
+ printf("Running to E_Sony_Open:17DFA (_VInstall)...\n");
+ run_to(0x417dfa);
+ dump_latches();
+ step(1);
+ dump_latches();
+ printf("Running to E_Sony_Open:17E12 (bsr P721)...\n");
+ run_to(0x417e12);
+ dump_latches();
+ for (i = 0; i < 1000; i++) {
+ step(1);
+ dump_latches();
+ }
+ printf("Running to E_Sony_Open:17E16 (bsr P720)...\n");
+ run_to(0x417e12);
+ dump_latches();
+ for (i = 0; i < 100; i++) {
+ step(1);
+ dump_latches();
+ }
+ printf("Running to E_Sony_Open:17E24 (bsr P715)...\n");
+ run_to(0x417e12);
+ dump_latches();
+ step(1);
+ dump_latches();
+ step(1);
+ dump_latches();
+ for (i = 0; i < 1000; i++) {
+ step(1);
+ dump_latches();
+ }
+ printf("Running to L58...\n");
+ run_to(0x400692);
+ dump_latches();
+ step(1);
+ dump_latches();
+ for (i = 0; i < 1000; i++) {
+ step(1);
+ dump_latches();
+ }
+#endif
+
+
+#if 0
+ for (i = 0; i < 100; i++) {
+ wait_timer(100);
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+ do_spi(0x01); /* RUN + STEP */
+ spi_disable_fpga();
+
+ /* We shall wait for cycle to complete but we know
+ * how slow our SPI is ...
+ */
+ dump_latches();
+
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+ do_spi(0x02); /* RUN + STEP */
+ spi_disable_fpga();
+ }
+
+ /* Stop CPU */
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+ do_spi(0x01); /* RUN + STEP */
+ spi_disable_fpga();
+#endif
+
+#if 0
+ step(10000);
+ step(10000);
+ step(10000);
+ step(10000);
+ step(10000);
+ step(10000);
+ step(10000);
+ step(10000);
+ step(10000);
+ step(10000);
+ step(10000);
+#endif
+
+#if 0
+ /* Stop scope */
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_SCOPE_BASE);
+ do_spi(0x00);
+ spi_disable_fpga();
+
+ printf("Reading 0x1020 via SPI initiated read cycle: ");
+ v = sim_bus_cycle(0x1020, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+ printf("%x\n", v);
+
+ /* Reading memory via RAM fifo */
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_AUTOINC | BB_REG_MEM_BASE);
+ do_spi(0x20); /* ROM base */
+ do_spi(0x10);
+ do_spi(0x00);
+ spi_disable_fpga();
+
+ spi_enable_fpga();
+ do_spi(BB_REG_MEM_BASE);
+ do_spi(0xff);
+ do_spi(0xff); /* one more dummy cycle with RAM fifo */
+ for (i = 0; i < 0x40; i++) {
+ printf(" %x", do_spi(0xff));
+ if ((i & 0xf) == 0xf)
+ printf("\n");
+ }
+ printf("\n");
+#endif
+
+#if 0
+ /* Start CPU */
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+ do_spi(0x02); /* RUN */
+ spi_disable_fpga();
+#endif
+
+#if 0
+ for (;;)
+ ;
+#endif
+}
--- /dev/null
+<!DOCTYPE piklab>
+<piklab>
+ <general>
+ <device>18F252</device>
+ <tool>sdcc</tool>
+ <programmer>custom</programmer>
+ <files>
+ <item>main.c</item>
+ <item>hardware.c</item>
+ <item>/usr/share/sdcc/lib/pic16/libc18f.lib</item>
+ <item>/usr/share/sdcc/lib/pic16/libio18f252.lib</item>
+ <item>mmc.c</item>
+ <item>fat16.c</item>
+ <item>18f252_mod.lkr</item>
+ <item>scsi.c</item>
+ </files>
+ <custom_linker_script>/home/benh/hackplace/minimigmac/pic/18f252_mod.lkr</custom_linker_script>
+ <description></description>
+ <version>0.1</version>
+ <output_type>executable</output_type>
+ </general>
+ <editors/>
+ <compiler>
+ <has_custom_arguments>true</has_custom_arguments>
+ <custom_arguments>
+ <item>-m%FAMILY</item>
+ <item>-%DEVICE</item>
+ <item>-V</item>
+ <item>--debug</item>
+ <item>-I$(SRCPATH)</item>
+ <item>-c</item>
+ <item>%I</item>
+ <item>--obanksel=2</item>
+ <item>--denable-peeps</item>
+ <item>--optimize-cmp</item>
+ <item>--optimize-df</item>
+ </custom_arguments>
+ <includes>
+ <item>$(SRCPATH)</item>
+ </includes>
+ <custom_options></custom_options>
+ </compiler>
+ <linker>
+ <has_custom_arguments>false</has_custom_arguments>
+ <custom_arguments>
+ <item>-m%FAMILY</item>
+ <item>-%DEVICE</item>
+ <item>-V</item>
+ <item>--debug</item>
+ <item>-Wl-c</item>
+ <item>-Wl-m</item>
+ <item>$LKR(-Wl-s%LKR)</item>
+ <item>-I$(SRCPATH)</item>
+ <item>-o%O</item>
+ <item>%OBJS</item>
+ <item>%LIBS</item>
+ </custom_arguments>
+ <includes>
+ <item>$(SRCPATH)</item>
+ </includes>
+ <custom_options></custom_options>
+ <format>inhx32</format>
+ </linker>
+ <librarian>
+ <has_custom_arguments>false</has_custom_arguments>
+ <custom_arguments>
+ <item>-c</item>
+ <item>%O</item>
+ <item>%OBJS</item>
+ <item>%LIBS</item>
+ </custom_arguments>
+ <custom_options></custom_options>
+ </librarian>
+</piklab>
--- /dev/null
+/*
+ * MMC/SD-Card driver
+ *
+ * Essentially copied from Minimig
+ *
+ * Current implementation is polled & synchronous
+ */
+
+#include <stdio.h>
+
+#include "hardware.h"
+#include "mmc.h"
+#include "mmc_cmds.h"
+
+/* Enable / Disable Debug info */
+#undef DEBUG_SDMMC
+
+/* MMC, or SD Card Specifification V1, V2, SDHC */
+#define MMC_CARD_NONE 0x00
+#define MMC_CARD_MMC 0x01
+#define MMC_CARD_SD_RAW_SPEC_1 0x02
+#define MMC_CARD_SD_RAW_SPEC_2 0x04
+#define MMC_CARD_SD_RAW_SPEC_SDHC 0x08
+
+static unsigned char mmc_card;
+static unsigned char mmc_crc_7;
+static unsigned int mmc_timeout;
+static unsigned char mmc_resp[5];
+
+#ifdef DEBUG_SDMMC
+#define pr_debug(args...) printf_tiny(args)
+#else
+#define pr_debug(args...)
+#endif
+
+/* Calculate CRC7 checksum */
+static void mmc_add_crc7(unsigned char c)
+{
+ unsigned char i;
+
+ for (i = 0; i < 8; i++) {
+ mmc_crc_7 <<= 1;
+ if (c & 0x80)
+ mmc_crc_7 ^= 0x09;
+ if (mmc_crc_7 & 0x80)
+ mmc_crc_7 ^= 0x09;
+ c <<= 1;
+ }
+}
+
+/* Send a byte and calculate CRC */
+static void mmc_spi_crc(unsigned char cmd)
+{
+ do_spi(cmd);
+ mmc_add_crc7(cmd);
+}
+
+/* Send a command to the SDcard */
+static void mmc_cmd_r0(char cmd, unsigned short addr_h, unsigned short addr_l)
+{
+ mmc_crc_7 = 0;
+
+ /* Flush SPI-bus */
+ do_spi(0xff);
+
+ /* Send command */
+ mmc_spi_crc(cmd);
+
+ /* Send address */
+ mmc_spi_crc(addr_h >> 8);
+ mmc_spi_crc(addr_h & 0xff);
+ mmc_spi_crc(addr_l >> 8);
+ mmc_spi_crc(addr_l & 0xff);
+
+ /* Cook and send CRC */
+ mmc_crc_7 <<= 1;
+ mmc_crc_7++;
+ do_spi(mmc_crc_7);
+}
+
+/* Send a command to the SDcard, a one byte response is expected */
+static void mmc_cmd_r1(char cmd, unsigned short addr_h, unsigned short addr_l)
+{
+ unsigned char i = 100;
+
+ mmc_cmd_r0(cmd, addr_h, addr_l);
+ do
+ mmc_resp[0] = do_spi(0xff);
+ while (mmc_resp[0] == 0xff && --i);
+}
+
+/* Send a command to the SDcard, a two byte response is expected */
+static void mmc_cmd_r2(char cmd, unsigned short addr_h, unsigned short addr_l)
+{
+ unsigned char i = 100;
+
+ mmc_cmd_r0(cmd, addr_h, addr_l);
+ do
+ mmc_resp[0] = do_spi(0xff);
+ while (mmc_resp[0] == 0xff && --i);
+ mmc_resp[1] = do_spi(0xff);
+}
+
+/* Send a command to the SDcard, a five byte response is expected */
+static void mmc_cmd_r3(char cmd, unsigned short addr_h, unsigned short addr_l)
+{
+ unsigned char i = 100;
+
+ mmc_cmd_r0(cmd, addr_h, addr_l);
+ do
+ mmc_resp[0] = do_spi(0xff);
+ while (mmc_resp[0] == 0xff && --i);
+ mmc_resp[1] = do_spi(0xff);
+ mmc_resp[2] = do_spi(0xff);
+ mmc_resp[3] = do_spi(0xff);
+ mmc_resp[4] = do_spi(0xff);
+}
+
+/* Enable the MMC/SD card correctly */
+unsigned char mmc_init(void)
+{
+ unsigned short lp;
+
+ /* Set SPI clock .. Might need to be smarter at changing the
+ * SPI clock based on what device is enabled...
+ */
+ SSPCON1 = 0x22; //spiclk = Fosc/64 (init clock 100-400 kHz)
+
+ _M_CD = 0;
+ _M_CS = 1;
+ wait_timer(10);
+
+ _M_CD = 1; /* Enable clock*/
+ _M_CS = 1; /* SDcard Disabled */
+
+ /* Set SDcard in SPI-Mode, Reset*/
+ /* (10 * 8bits = 80 clockpulses) */
+ for (lp = 0; lp < 10; lp++)
+ do_spi(0xff);
+
+ /* Delay for a lot of milliseconds (least 16 bus clock cycles) */
+ for (lp=0; lp < 56000; lp++)
+ ; /* PRAY sdcc doesn't optimize that out !
+ Should use timer instead ! */
+ wait_timer(10);
+
+ /* SDcard Enabled */
+ _M_CS = 0;
+
+ /* No card detected by default */
+ mmc_card = MMC_CARD_NONE;
+
+ /* CMD0: Reset all cards to IDLE state */
+ mmc_cmd_r1(CMD0, 0, 0);
+ if (mmc_resp[0] != 0x01) {
+ pr_debug("No card detected ! (resp=%x)\n", mmc_resp[0]);
+ goto fail;
+ }
+
+ /* Default to MMC */
+ mmc_card = MMC_CARD_MMC;
+
+ /* Check for SD Card V2 */
+ /* (Voltage 2.7V - 3.6V, 0xaa test pattern) */
+ mmc_cmd_r3(CMD8, 0x0000, 0x01aa);
+ if (!(mmc_resp[0] & 0x04)) {
+ if (mmc_resp[3] == 0x01 && mmc_resp[4] == 0xaa) {
+ pr_debug("SD card V2 detected, possible SDHC\n");
+ mmc_card |= MMC_CARD_SD_RAW_SPEC_2;
+ } else {
+ pr_debug("Error detecting SD V2 card\n");
+ goto fail;
+ }
+ } else {
+ /* Check for SD card V1 */
+ mmc_cmd_r1(CMD55, 0, 0);
+ mmc_cmd_r1(CMD41, 0, 0);
+ if (!(mmc_resp[0] & 0x04)) {
+ pr_debug("SD card V1 detected\n");
+ mmc_card |= MMC_CARD_SD_RAW_SPEC_1;
+ } else
+ pr_debug("MMC-card detected\n");
+ }
+
+ /* Wait for card to get ready */
+ mmc_timeout = 0;
+ while (1) {
+ if (mmc_card & (MMC_CARD_SD_RAW_SPEC_1 | MMC_CARD_SD_RAW_SPEC_2)) {
+ /* When SD Card detected */
+ lp = 0x0;
+ if (mmc_card & MMC_CARD_SD_RAW_SPEC_2)
+ lp = 0x4000;
+ /* Activate SD card init process */
+ mmc_cmd_r1(CMD55, 0, 0);
+ mmc_cmd_r1(CMD41, lp, 0);
+ } else {
+ /* Activate the MMC cards init process */
+ mmc_cmd_r1(CMD1, 0, 0);
+ }
+
+ /* Check if card is idle */
+ if (mmc_resp[0] == 0x00)
+ break;
+ mmc_timeout++;
+ if (mmc_timeout == 1000) {
+ pr_debug("SD/MMC ACMD41/CMD1 response timeout...\n");
+ goto fail;
+ }
+ }
+
+ /* Check for SDHC card */
+ if (mmc_card & MMC_CARD_SD_RAW_SPEC_2) {
+ /* Get operating conditions */
+ mmc_cmd_r3(CMD58, 0, 0);
+ if (mmc_resp[0]) {
+ pr_debug("SD/MMC: Failed to get operating conditions\n");
+ goto fail;
+ }
+
+ if (mmc_resp[1] & 0x40) {
+ pr_debug("SDHC Card Detected\n");
+ mmc_card |= MMC_CARD_SD_RAW_SPEC_SDHC;
+ }
+ }
+
+ /* Set block size to 512 bytes */
+ mmc_timeout = 0;
+ while(1) {
+ mmc_cmd_r1(CMD16, 0x0000, 0x0200);
+ if (!mmc_resp[0])
+ break;
+ mmc_timeout++;
+ if(mmc_timeout == 100) {
+ pr_debug("Set block size to 512 timeout\n");
+ goto fail;
+ }
+ }
+
+ spi_disable_sdcard();
+
+ /* XXX Stuff below from minimig... it's strange... it sets SSPCON1 to a
+ * different speed from what is done in hardware_init.c for everybody
+ */
+ // TODO: Change speed for card access
+ SSPCON1 = 0x20; //spiclk = Fosc/4 (5 MHz)
+ return true;
+ fail:
+ mmc_card = MMC_CARD_NONE;
+ spi_disable_sdcard();
+ return false;
+}
+
+/* Read single block (with block-size set by CMD16 to 512 by default) */
+unsigned char mmc_read(unsigned long lba, unsigned char *out_data)
+{
+ unsigned short upper_lba, lower_lba;
+ unsigned char i;
+ unsigned char *p;
+
+ /* !SDHC uses byte address instead of LBA */
+ if (!(mmc_card & MMC_CARD_SD_RAW_SPEC_SDHC))
+ lba <<= 9;
+
+ pr_debug("SD/MMC: Reading LBA 0x%lx\n", lba);
+
+ upper_lba = (unsigned short)(lba>>16);
+ lower_lba = (unsigned short)lba;
+
+ spi_enable_sdcard();
+
+ mmc_cmd_r1(CMD17, upper_lba, lower_lba);
+
+ /* Exit if invalid response*/
+ if (mmc_resp[0]) {
+ pr_debug("SD/MMC CMD17: invalid response %x\n", mmc_resp[0]);
+ goto fail;
+ }
+
+ /* Wait for start of data transfer with timeout */
+ mmc_timeout = 0;
+ while(do_spi(0xff) != 0xfe) {
+ if (mmc_timeout++ >= 50000) {
+ pr_debug("SD/MMC CMD17: no data token\n");
+ goto fail;
+ }
+ }
+
+ /* Read data and exit OK */
+ p = out_data;
+ for (i = 0; i < 128; i++) {
+ SSPBUF = 0xff;
+ while (!SSPSTATbits.BF)
+ ;
+ *(p++) = SSPBUF;
+ SSPBUF = 0xff;
+ while (!SSPSTATbits.BF)
+ ;
+ *(p++) = SSPBUF;
+ SSPBUF = 0xff;
+ while (!SSPSTATbits.BF)
+ ;
+ *(p++) = SSPBUF;
+ SSPBUF = 0xff;
+ while (!SSPSTATbits.BF)
+ ;
+ *(p++) = SSPBUF;
+ }
+
+ /* XXX Todo: check CRC */
+ do_spi(0xff); //Read CRC lo byte
+ do_spi(0xff); //Read CRC hi byte
+
+ spi_disable_sdcard();
+ return true;
+ fail:
+ spi_disable_sdcard();
+ return false;
+}
+
+
+
+/* Write: 512 Byte-Mode, this will not work (read MMC and SD-card specs) with any other
+ * sector/block size
+ */
+unsigned char mmc_write(unsigned long lba, unsigned char *in_data)
+{
+ unsigned short upper_lba, lower_lba;
+ unsigned char i;
+ unsigned char *p;
+
+ /* !SDHC uses byte address instead of LBA */
+ if (!(mmc_card & MMC_CARD_SD_RAW_SPEC_SDHC))
+ lba <<= 9;
+
+ pr_debug("SD/MMC: Writing LBA 0x%lx\n", lba);
+
+ upper_lba = (unsigned short)(lba>>16);
+ lower_lba = (unsigned short)lba;
+
+ spi_enable_sdcard();
+
+ mmc_cmd_r1(CMD24, upper_lba, lower_lba);
+
+ /* Exit if invalid response*/
+ if (mmc_resp[0]) {
+ pr_debug("SD/MMC CMD24: invalid response %x\n", mmc_resp[0]);
+ goto fail;
+ }
+
+ do_spi(0xff); //One byte gap
+ do_spi(0xfe); //Send Data token
+
+ /* Send bytes for sector */
+ p = in_data;
+ for (i = 0; i < 128; i++) {
+ SSPBUF = *(p++);
+ while (!SSPSTATbits.BF)
+ ;
+ SSPBUF = *(p++);
+ while (!SSPSTATbits.BF)
+ ;
+ SSPBUF = *(p++);
+ while (!SSPSTATbits.BF)
+ ;
+ SSPBUF = *(p++);
+ while (!SSPSTATbits.BF)
+ ;
+ }
+
+ /* XXX Todo: Send proper CRC ? */
+ do_spi(0xff); //Send CRC lo byte
+ do_spi(0xff); //Send CRC hi byte
+
+ /* Read packet response */
+ i = do_spi(0xff);
+
+ /* Status codes
+ * 010 = Data accepted
+ * 101 = Data rejected due to CRC error
+ * 110 = Data rejected due to write error
+ */
+ i &= 0b00011111;
+ if (i != 0b00000101) {
+ pr_debug("SD/MMC CMD24: write error %x\n", i);
+ goto fail;
+ }
+
+ mmc_timeout = 0;
+ /* Wait until the card has finished writing the data */
+ while (do_spi(0xff) == 0x00) {
+ if (mmc_timeout++ >= 50000) {
+ pr_debug("SD/MMC CMD24: busy wait timeout\n");
+ goto fail;
+ }
+ }
+ spi_disable_sdcard();
+ return true;
+ fail:
+ spi_disable_sdcard();
+ return false;
+}
+
--- /dev/null
+#ifndef __MMC_H__
+#define __MMC_H__
+
+unsigned char mmc_init(void);
+unsigned char mmc_read(unsigned long lba, unsigned char *out_data);
+unsigned char mmc_write(unsigned long lba, unsigned char *in_data);
+
+#endif
--- /dev/null
+#ifndef __MMC_CMDS_H__
+#define __MMC_CMDS_H__
+
+/* MMC command set */
+#define CMD0 0x40 /*Resets the multimedia card*/
+#define CMD1 0x41 /*Activates the card's initialization process*/
+#define CMD2 0x42 /*--*/
+#define CMD3 0x43 /*--*/
+#define CMD4 0x44 /*--*/
+#define CMD5 0x45 /*reseved*/
+#define CMD6 0x46 /*reserved*/
+#define CMD7 0x47 /*--*/
+#define CMD8 0x48 /* Sends SD Memory Card interface condition,
+ which includes host supply voltage information
+ and asks the card whether card supports voltage.
+ Reserved bits shall be set to '0'.*/
+#define CMD9 0x49 /*CSD : Ask the selected card to send its card
+ specific data*/
+#define CMD10 0x4a /*CID : Ask the selected card to send its card
+ identification*/
+#define CMD11 0x4b /*--*/
+#define CMD12 0x4c /*--*/
+#define CMD13 0x4d /*Ask the selected card to send its status register*/
+#define CMD14 0x4e /*--*/
+#define CMD15 0x4f /*--*/
+#define CMD16 0x50 /*Select a block length (in bytes) for all following
+ block commands (Read:between 1-512 and Write:only 512)*/
+#define CMD17 0x51 /*Reads a block of the size selected by the SET_BLOCKLEN
+ command, the start address and block length must be set
+ so that the data transferred will not cross a physical
+ block boundry*/
+#define CMD18 0x52 /*--*/
+#define CMD19 0x53 /*reserved*/
+#define CMD20 0x54 /*--*/
+#define CMD21 0x55 /*reserved*/
+#define CMD22 0x56 /*reserved*/
+#define CMD23 0x57 /*reserved*/
+#define CMD24 0x58 /*Writes a block of the size selected by CMD16, the start
+ address must be alligned on a sector boundry, the block
+ length is always 512 bytes*/
+#define CMD25 0x59 /*--*/
+#define CMD26 0x5a /*--*/
+#define CMD27 0x5b /*Programming of the programmable bits of the CSD*/
+#define CMD28 0x5c /*If the card has write protection features, this command
+ sets the write protection bit of the addressed group.
+ The porperties of the write protection are coded in
+ the card specific data (WP_GRP_SIZE)*/
+#define CMD29 0x5d /*If the card has write protection features, this command
+ clears the write protection bit of the addressed group*/
+#define CMD30 0x5e /*If the card has write protection features, this command
+ asks the card to send the status of the write protection
+ bits. 32 write protection bits (representing 32 write
+ protect groups starting at the specific address) followed
+ by 16 CRD bits are transferred in a payload format via
+ the data line*/
+#define CMD31 0x5f /*reserved*/
+#define CMD32 0x60 /*sets the address of the first sector of the erase group*/
+#define CMD33 0x61 /*Sets the address of the last sector in a cont. range
+ within the selected erase group, or the address of a
+ single sector to be selected for erase*/
+#define CMD34 0x62 /*Removes on previously selected sector from the erase
+ selection*/
+#define CMD35 0x63 /*Sets the address of the first erase group within a
+ range to be selected for erase*/
+#define CMD36 0x64 /*Sets the address of the last erase group within a
+ continuos range to be selected for erase*/
+#define CMD37 0x65 /*Removes one previously selected erase group from the
+ erase selection*/
+#define CMD38 0x66 /*Erases all previously selected sectors*/
+#define CMD39 0x67 /*--*/
+#define CMD40 0x68 /*--*/
+#define CMD41 0x69 /*reserved*/
+#define CMD42 0x6a /*reserved*/
+#define CMD43 0x6b /*reserved*/
+#define CMD44 0x6c /*reserved*/
+#define CMD45 0x6d /*reserved*/
+#define CMD46 0x6e /*reserved*/
+#define CMD47 0x6f /*reserved*/
+#define CMD48 0x70 /*reserved*/
+#define CMD49 0x71 /*reserved*/
+#define CMD50 0x72 /*reserved*/
+#define CMD51 0x73 /*reserved*/
+#define CMD52 0x74 /*reserved*/
+#define CMD53 0x75 /*reserved*/
+#define CMD54 0x76 /*reserved*/
+#define CMD55 0x77 /*Indicates to the card that the next command is an
+ application specific command rather than a standard
+ command*/
+#define CMD56 0x78 /*reserved*/
+#define CMD57 0x79 /*reserved*/
+#define CMD58 0x7a /*reserved*/
+#define CMD59 0x7b /*Turns the CRC option ON or OFF. A '1' in the CRC option
+ bit will turn the option ON, a '0' will turn it OFF*/
+#define CMD60 0x7c /*--*/
+#define CMD61 0x7d /*--*/
+#define CMD62 0x7e /*--*/
+#define CMD63 0x7f /*--*/
+
+#endif /* __MMC_CMDS_H__ */
--- /dev/null
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "hardware.h"
+#include "mmc.h"
+#include "fat16.h"
+#include "scsi.h"
+
+/* SCSI Backbus interface */
+#define BB_REG_LINES 0
+#define BB_LINES_BSY 7
+#define BB_LINES_SEL 6
+#define BB_LINES_ACK 5
+#define BB_LINES_ATN 4
+#define BB_LINES_RST 3
+
+#define BB_REG_ASSERT 1
+#define BB_ASSERT_BSY 7
+#define BB_ASSERT_SEL 6
+#define BB_ASSERT_CD 5
+#define BB_ASSERT_IO 4
+#define BB_ASSERT_MSG 3
+#define BB_ASSERT_REQ 2
+#define BB_ASSERT_RST 1
+#define BB_ASSERT_AUTO 0 /* Automatic mode */
+
+#define BB_REG_ODATA 2
+#define BB_REG_IDATA 3
+#define BB_REG_ACNT_HI 4
+#define BB_REG_ACNT_LO 5
+
+#define MASK(bit) (1u << (bit))
+
+static unsigned char scsi_cmd[12];
+static unsigned char scsi_assert;
+static unsigned long scsi_size;
+
+#define scsi_dbg(fmt...)
+
+void scsi_open(void)
+{
+ long size;
+
+ size = fat_open("MACHD IMG");
+ if (size < 0) {
+ printf("File MACHD.IMG not found !\n");
+ return;
+ }
+ scsi_size = size;
+ fat_file_seek(0);
+}
+
+static unsigned char scsi_get_lines(void)
+{
+ unsigned char lines;
+
+ spi_enable_fpga();
+ do_spi(BB_REG_SCSI_BASE | BB_REG_LINES);
+ do_spi(0xff);
+ lines = do_spi(0xff);
+ spi_disable_fpga();
+
+ return lines;
+}
+
+static void scsi_set_assert(void)
+{
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_SCSI_BASE | BB_REG_ASSERT);
+
+
+ do_spi(scsi_assert);
+ spi_disable_fpga();
+}
+
+static unsigned char scsi_get_data(void)
+{
+ unsigned char lines;
+
+ spi_enable_fpga();
+ do_spi(BB_REG_SCSI_BASE | BB_REG_ODATA);
+ do_spi(0xff);
+ lines = do_spi(0xff);
+ spi_disable_fpga();
+
+ return lines;
+}
+
+static void scsi_set_data(unsigned char val)
+{
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_SCSI_BASE | BB_REG_IDATA);
+ do_spi(val);
+ spi_disable_fpga();
+}
+
+static void scsi_set_autocnt(unsigned short cnt)
+{
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_AUTOINC | BB_REG_SCSI_BASE | BB_REG_ACNT_HI);
+ do_spi(cnt >> 8);
+ do_spi(cnt & 0xff);
+ spi_disable_fpga();
+}
+
+static unsigned char scsi_do_data_in(unsigned short len)
+{
+ unsigned short i = 0;
+
+ scsi_assert |= MASK(BB_ASSERT_AUTO) | MASK(BB_ASSERT_IO);
+ scsi_set_assert();
+ scsi_set_autocnt(len);
+
+ spi_enable_fpga();
+ do_spi(BB_WRITE | BB_REG_SCSI_BASE | BB_REG_IDATA);
+ while(len--) {
+ do_spi(secbuf[i++]);
+ /* Handhake goes down approx. 160ns after the above
+ * and we then need to wait for it to go back up,
+ * the PIC executes an instruction in about 200ns so
+ * we shouldn't need any delay here before we test it
+ */
+ while(!SCSI_HSHAKE);
+ }
+ scsi_assert &= ~MASK(BB_ASSERT_AUTO);
+ scsi_set_assert();
+ return 0;
+}
+
+static unsigned char scsi_do_data_out(unsigned short len)
+{
+ unsigned short i = 0;
+
+ scsi_assert |= MASK(BB_ASSERT_AUTO);
+ scsi_assert &= ~MASK(BB_ASSERT_IO);
+ scsi_set_assert();
+ scsi_set_autocnt(len);
+
+ spi_enable_fpga();
+ do_spi(BB_REG_SCSI_BASE | BB_REG_ODATA);
+ /* See handshake timing comment in scsi_do_data_in() */
+ while(!SCSI_HSHAKE);
+ do_spi(0xff);
+ while(!SCSI_HSHAKE);
+ do_spi(0xff);
+ while(!SCSI_HSHAKE);
+ do_spi(0xff);
+ while(len--) {
+ while(!SCSI_HSHAKE);
+ secbuf[i++] = do_spi(0xff);
+ }
+ scsi_assert &= ~MASK(BB_ASSERT_AUTO);
+ scsi_set_assert();
+ return 0;
+}
+
+static unsigned char scsi_do_read(unsigned long lba,
+ unsigned short cnt)
+{
+ unsigned char rc = true;
+
+ if (scsi_size == 0)
+ return 1;
+ diskled_on();
+ fat_file_seek(lba);
+ while(cnt--) {
+ if (rc)
+ rc = fat_file_read();
+ if (!rc) {
+ diskled_off();
+ return 1;
+ }
+ scsi_do_data_in(512);
+ if (cnt)
+ rc = fat_file_next_sector();
+ }
+ diskled_off();
+ return 0;
+}
+
+static unsigned char scsi_do_write(unsigned long lba,
+ unsigned short cnt)
+{
+ unsigned char rc;
+
+ if (scsi_size == 0)
+ return 1;
+ diskled_on();
+ fat_file_seek(lba);
+ while(cnt--) {
+ scsi_do_data_out(512);
+ rc = fat_file_write();
+ if (rc && cnt)
+ rc = fat_file_next_sector();
+ if (!rc) {
+ diskled_off();
+ return 1;
+ }
+ }
+ diskled_off();
+ return 0;
+}
+
+static unsigned char scsi_do_cmd(void)
+{
+ unsigned long lba;
+ unsigned short cnt;
+
+ switch(scsi_cmd[0]) {
+ case 0x00:
+ printf("scsi: TEST_UNIT_READY !\n");
+ return 0;
+ case 0x03:
+ printf("scsi: REQUEST_SENSE !\n");
+ fat_inval_cache();
+ memset(secbuf, 0, 13);
+ secbuf[0] = 0xf0;
+ return scsi_do_data_in(13);
+ case 0x04:
+ printf("scsi: FORMAT_UNIT !\n");
+ return 1;
+ case 0x08:
+ lba = scsi_cmd[1] & 0x1f;
+ lba = (lba << 8) | scsi_cmd[2];
+ lba = (lba << 8) | scsi_cmd[3];
+ cnt = scsi_cmd[4];
+ if (!cnt)
+ cnt = 256;
+ printf("scsi: READ6 (lba=%lx cnt=%x) !\n", lba, cnt);
+ return scsi_do_read(lba, cnt);
+ case 0x0a:
+ lba = scsi_cmd[1] & 0x1f;
+ lba = (lba << 8) | scsi_cmd[2];
+ lba = (lba << 8) | scsi_cmd[3];
+ cnt = scsi_cmd[4];
+ if (!cnt)
+ cnt = 256;
+ printf("scsi: WRITE6 (lba=%lx cnt=%x) !\n", lba, cnt);
+ return scsi_do_write(lba, cnt);
+ case 0x12:
+ printf("scsi: INQUIRY !\n");
+ fat_inval_cache();
+ memset(secbuf, 0, 36);
+ memcpy(secbuf + 8, " SEAGATE", 8);
+ memcpy(secbuf + 16, " ST225N", 16);
+ secbuf[4] = 32;
+ cnt = scsi_cmd[4];
+ if (cnt > 36)
+ cnt = 36;
+ return scsi_do_data_in(cnt);
+ case 0x15:
+ printf("scsi: MODE_SELECT !\n");
+ return 1;
+ case 0x1a:
+ printf("scsi: MODE_SENSE !\n");
+ return 1;
+ case 0x1b:
+ printf("scsi: START_STOP !\n");
+ return 1;
+ case 0x25:
+ printf("scsi: READ_CAPACITY !\n");
+ return 1;
+ case 0x28:
+ printf("scsi: READ10 !\n");
+ lba = scsi_cmd[2];
+ lba = (lba << 8) | scsi_cmd[3];
+ lba = (lba << 8) | scsi_cmd[4];
+ lba = (lba << 8) | scsi_cmd[5];
+ cnt = scsi_cmd[7];
+ cnt = (cnt << 8) | scsi_cmd[8];
+ return scsi_do_read(lba, cnt);
+ case 0x2a:
+ printf("scsi: WRITE10 !\n");
+ lba = scsi_cmd[2];
+ lba = (lba << 8) | scsi_cmd[3];
+ lba = (lba << 8) | scsi_cmd[4];
+ lba = (lba << 8) | scsi_cmd[5];
+ cnt = scsi_cmd[7];
+ cnt = (cnt << 8) | scsi_cmd[8];
+ return scsi_do_write(lba, cnt);
+ case 0x2f:
+ printf("scsi: VERIFY10 !\n");
+ return 10;
+ case 0x3c:
+ printf("scsi: READ_BUFFER !\n");
+ return 6;
+ }
+ return 1;
+}
+
+static void scsi_req_ack(void)
+{
+ unsigned char l;
+
+ scsi_assert |= MASK(BB_ASSERT_REQ);
+ scsi_set_assert();
+ do
+ l = scsi_get_lines();
+ while (!(l & MASK(BB_LINES_ACK)));
+}
+
+static void scsi_nreq_nack(void)
+{
+ unsigned char l;
+
+ scsi_assert &= ~MASK(BB_ASSERT_REQ);
+ scsi_set_assert();
+ do
+ l = scsi_get_lines();
+ while (l & MASK(BB_LINES_ACK));
+}
+
+void do_scsi(void)
+{
+ unsigned char lines, val, i, len, stat;
+
+ lines = scsi_get_lines();
+ if ((lines & MASK(BB_LINES_SEL)) /*&& !(lines & MASK(BB_LINES_BSY)) */) {
+ val = scsi_get_data();
+ if (!(val & MASK(6))) {
+ //printf("scsi: Wrong ID 0x%x\n", val);
+ return;
+ }
+ scsi_assert = MASK(BB_ASSERT_BSY);
+ scsi_set_assert();
+ } else if (lines & MASK(BB_LINES_RST)) {
+ printf("scsi: RESET !\n");
+ return;
+ } else {
+ printf("scsi: Glitch (reset ?) lines=%x!\n", lines);
+ return;
+ }
+ while(lines & MASK(BB_LINES_SEL))
+ lines = scsi_get_lines();
+ /* XXX Check for ATN for MESSAGE_OUT phase, Mac ROM doesn't do it */
+ scsi_dbg("scsi: Selected, command phase...\n");
+ i = 0;
+ stat = 0;
+ len = 1;
+ scsi_assert |= MASK(BB_ASSERT_CD);
+ scsi_set_assert();
+ while(len) {
+ scsi_req_ack();
+ val = scsi_get_data();
+ scsi_cmd[i++] = scsi_get_data();
+ scsi_dbg("scsi: CMD = 0x%x\n", val);
+ if (i == 1) {
+ if (val == 0x00 || /* TEST_UNIT_READY */
+ val == 0x03 || /* REQUEST_SENSE */
+ val == 0x04 || /* FORMAT_UNIT */
+ val == 0x08 || /* READ6 */
+ val == 0x0a || /* WRITE6 */
+ val == 0x12 || /* INQUIRY */
+ val == 0x15 || /* MODE_SELECT */
+ val == 0x1a || /* MODE_SENSE */
+ val == 0x1b || /* START_STOP */
+ val == 0x3c || /* READ_BUFFER */
+ 0)
+ len = 6;
+ else if (val == 0x25 || /* READ_CAPACITY */
+ val == 0x28 || /* READ10 */
+ val == 0x2a || /* WRITE10 */
+ val == 0x2f || /* VERIFY10 */
+ 0)
+ len = 10;
+ else {
+ stat = 1;
+ goto fail;
+ }
+ }
+ len--;
+ fail:
+ scsi_nreq_nack();
+ }
+ scsi_assert &= ~MASK(BB_ASSERT_CD);
+ stat = scsi_do_cmd();
+ scsi_dbg("Status phase (stat=%x)\n", stat);
+ scsi_assert |= MASK(BB_ASSERT_IO) | MASK(BB_ASSERT_CD);
+ scsi_set_data(stat);
+ scsi_req_ack();
+ scsi_nreq_nack();
+ scsi_dbg("Message IN phase (stat=%x)\n", stat);
+ scsi_assert |= MASK(BB_ASSERT_MSG);
+ scsi_set_data(0);
+ scsi_req_ack();
+ scsi_nreq_nack();
+ scsi_assert = 0;
+ scsi_set_assert();
+}
+
--- /dev/null
+#ifndef __SCSI_H__
+#define __SCSI_H__
+
+extern void scsi_open(void);
+extern void do_scsi(void);
+
+#endif /* __SCSI_H__ */