Browse Source

Project restructured to support multiple boards. DE2 and DE2-70 support added.

Mikolas Hämäläinen 9 years ago
parent
commit
c0368908b2

+ 23 - 0
board/de0_nano/Makefile

@@ -0,0 +1,23 @@
+DESIGN_NAME = de0_nano
+QUARTUS_OPTIONS =
+
+
+all: asm
+
+project: $(TCL_FILE)
+	quartus_sh $(QUARTUS_OPTIONS) -t $(DESIGN_NAME).tcl
+
+map: project
+	quartus_map $(QUARTUS_OPTIONS) $(DESIGN_NAME)
+
+fit: map
+	quartus_fit $(QUARTUS_OPTIONS) $(DESIGN_NAME)
+
+asm: fit
+	quartus_asm $(QUARTUS_OPTIONS) $(DESIGN_NAME)
+
+sta: asm
+	quartus_sta $(QUARTUS_OPTIONS) $(DESIGN_NAME)
+
+load: asm
+	quartus_pgm --mode=jtag -o p\;$(DESIGN_NAME).sof

+ 204 - 0
board/de0_nano/de0_nano.tcl

@@ -0,0 +1,204 @@
+# Copyright (C) 1991-2013 Altera Corporation
+# Your use of Altera Corporation's design tools, logic functions 
+# and other software and tools, and its AMPP partner logic 
+# functions, and any output files from any of the foregoing 
+# (including device programming or simulation files), and any 
+# associated documentation or information are expressly subject 
+# to the terms and conditions of the Altera Program License 
+# Subscription Agreement, Altera MegaCore Function License 
+# Agreement, or other applicable license agreement, including, 
+# without limitation, that your use is for the sole purpose of 
+# programming logic devices manufactured by Altera and sold by 
+# Altera or its authorized distributors.  Please refer to the 
+# applicable agreement for further details.
+
+# Quartus II: Generate Tcl File for Project
+# File: de0_nano.tcl
+# Generated on: Sat Sep  6 02:27:17 2014
+
+# Load Quartus II Tcl Project package
+package require ::quartus::project
+
+set need_to_close_project 0
+set make_assignments 1
+
+# Check that the right project is open
+if {[is_project_open]} {
+	if {[string compare $quartus(project) "de0_nano"]} {
+		puts "Project de0_nano is not open"
+		set make_assignments 0
+	}
+} else {
+	# Only open if not already open
+	if {[project_exists de0_nano]} {
+		project_open -revision de0_nano de0_nano
+	} else {
+		project_new -revision de0_nano de0_nano
+	}
+	set need_to_close_project 1
+}
+
+# Make assignments
+if {$make_assignments} {
+	set_global_assignment -name FAMILY "Cyclone IV E"
+	set_global_assignment -name DEVICE EP4CE22F17C6
+	set_global_assignment -name TOP_LEVEL_ENTITY top_level
+	set_global_assignment -name ORIGINAL_QUARTUS_VERSION 13.1
+	set_global_assignment -name PROJECT_CREATION_TIME_DATE "19:30:55  AUGUST 28, 2014"
+	set_global_assignment -name LAST_QUARTUS_VERSION 13.1
+	set_global_assignment -name EDA_SIMULATION_TOOL "ModelSim-Altera (VHDL)"
+	set_global_assignment -name EDA_OUTPUT_DATA_FORMAT VHDL -section_id eda_simulation
+	set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
+	set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
+	set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
+	set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0
+	set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85
+	set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW"
+	set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)"
+	set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "2.5 V"
+	set_global_assignment -name VHDL_FILE ../../vhdl/board/de0_nano/SDRAM_Controller.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/DRAM.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/gpio.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/T80.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/board/de0_nano/top_level_de0_nano.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/Z80cpu.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/T80_Pack.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/T80_ALU.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/T80_MCode.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/T80_Reg.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/T80se.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/MMU.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/MonZ80.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/SSRAM.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/uart_interface.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/fifo.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/uart.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/timer.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/board/de0_nano/pll_de0_nano.vhd
+	set_global_assignment -name VHDL_FILE ../../vhdl/clkscale.vhd
+        set_global_assignment -name VHDL_FILE ../../vhdl/RAM4K36.vhd
+        set_global_assignment -name VHDL_FILE ../../vhdl/RAM4K9.vhd
+	set_location_assignment PIN_J15 -to rst_n_pad_i
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to rst_n_pad_i
+	set_location_assignment PIN_R8 -to sys_clk_pad_i
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to sys_clk_pad_i
+	set_location_assignment PIN_F13 -to serial_rx
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to serial_rx
+	set_location_assignment PIN_T15 -to serial_tx
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to serial_tx
+	set_location_assignment PIN_A15 -to leds[0]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to leds[0]
+	set_location_assignment PIN_A13 -to leds[1]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to leds[1]
+	set_location_assignment PIN_B13 -to leds[2]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to leds[2]
+	set_location_assignment PIN_A11 -to leds[3]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to leds[3]
+	set_location_assignment PIN_D1 -to leds[4]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to leds[4]
+	set_location_assignment PIN_P2 -to SDRAM_ADDR[0]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_ADDR[0]
+	set_location_assignment PIN_N5 -to SDRAM_ADDR[1]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_ADDR[1]
+	set_location_assignment PIN_N6 -to SDRAM_ADDR[2]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_ADDR[2]
+	set_location_assignment PIN_M8 -to SDRAM_ADDR[3]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_ADDR[3]
+	set_location_assignment PIN_P8 -to SDRAM_ADDR[4]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_ADDR[4]
+	set_location_assignment PIN_T7 -to SDRAM_ADDR[5]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_ADDR[5]
+	set_location_assignment PIN_N8 -to SDRAM_ADDR[6]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_ADDR[6]
+	set_location_assignment PIN_T6 -to SDRAM_ADDR[7]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_ADDR[7]
+	set_location_assignment PIN_R1 -to SDRAM_ADDR[8]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_ADDR[8]
+	set_location_assignment PIN_P1 -to SDRAM_ADDR[9]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_ADDR[9]
+	set_location_assignment PIN_N2 -to SDRAM_ADDR[10]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_ADDR[10]
+	set_location_assignment PIN_N1 -to SDRAM_ADDR[11]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_ADDR[11]
+	set_location_assignment PIN_L4 -to SDRAM_ADDR[12]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_ADDR[12]
+	set_location_assignment PIN_G2 -to SDRAM_DQ[0]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[0]
+	set_location_assignment PIN_G1 -to SDRAM_DQ[1]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[1]
+	set_location_assignment PIN_L8 -to SDRAM_DQ[2]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[2]
+	set_location_assignment PIN_K5 -to SDRAM_DQ[3]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[3]
+	set_location_assignment PIN_K2 -to SDRAM_DQ[4]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[4]
+	set_location_assignment PIN_J2 -to SDRAM_DQ[5]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[5]
+	set_location_assignment PIN_J1 -to SDRAM_DQ[6]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[6]
+	set_location_assignment PIN_R7 -to SDRAM_DQ[7]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[7]
+	set_location_assignment PIN_T4 -to SDRAM_DQ[8]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[8]
+	set_location_assignment PIN_T2 -to SDRAM_DQ[9]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[9]
+	set_location_assignment PIN_T3 -to SDRAM_DQ[10]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[10]
+	set_location_assignment PIN_R3 -to SDRAM_DQ[11]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[11]
+	set_location_assignment PIN_R5 -to SDRAM_DQ[12]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[12]
+	set_location_assignment PIN_P3 -to SDRAM_DQ[13]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[13]
+	set_location_assignment PIN_N3 -to SDRAM_DQ[14]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[14]
+	set_location_assignment PIN_K1 -to SDRAM_DQ[15]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQ[15]
+	set_location_assignment PIN_R6 -to SDRAM_DQM[0]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQM[0]
+	set_location_assignment PIN_T5 -to SDRAM_DQM[1]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_DQM[1]
+	set_location_assignment PIN_M7 -to SDRAM_BA[0]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_BA[0]
+	set_location_assignment PIN_M6 -to SDRAM_BA[1]
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_BA[1]
+	set_location_assignment PIN_L1 -to SDRAM_nCAS
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_nCAS
+	set_location_assignment PIN_L7 -to SDRAM_CKE
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_CKE
+	set_location_assignment PIN_P6 -to SDRAM_CS
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_CS
+	set_location_assignment PIN_L2 -to SDRAM_nRAS
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_nRAS
+	set_location_assignment PIN_C2 -to SDRAM_nWE
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_nWE
+	set_location_assignment PIN_R4 -to SDRAM_CLK
+	set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SDRAM_CLK
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to "DRAM:dram|SDRAM_Controller:sdram_ctrl|iob_command"
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to "DRAM:dram|SDRAM_Controller:sdram_ctrl|iob_address"
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to "DRAM:dram|SDRAM_Controller:sdram_ctrl|iob_dqm"
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to "DRAM:dram|SDRAM_Controller:sdram_ctrl|iob_cke"
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to "DRAM:dram|SDRAM_Controller:sdram_ctrl|iob_bank"
+	set_instance_assignment -name FAST_INPUT_REGISTER ON -to "DRAM:dram|SDRAM_Controller:sdram_ctrl|iob_data"
+	set_instance_assignment -name FAST_INPUT_REGISTER ON -to "DRAM:dram|SDRAM_Controller:sdram_ctrl|captured_data"
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_ADDR
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_BA
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_CKE
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_CLK
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_CS
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_DQM
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_nCAS
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_nRAS
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_nWE
+	set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_DQ
+	set_instance_assignment -name FAST_INPUT_REGISTER ON -to SDRAM_DQ
+	set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top
+
+	# Commit assignments
+	export_assignments
+
+	# Close project
+	if {$need_to_close_project} {
+		project_close
+	}
+}

+ 472 - 0
vhdl/board/de0_nano/SDRAM_Controller.vhd

@@ -0,0 +1,472 @@
+----------------------------------------------------------------------------------
+-- Engineer: Mike Field <hamster@snap.net.nz>
+-- 
+-- Create Date:    14:09:12 09/15/2013 
+-- Module Name:    SDRAM_Controller - Behavioral 
+-- Description:    Simple SDRAM controller for a Micron 48LC16M16A2-7E
+--                 or Micron 48LC4M16A2-7E @ 100MHz      
+-- Revision: 
+-- Revision 0.1 - Initial version
+-- Revision 0.2 - Removed second clock signal that isn't needed.
+-- Revision 0.3 - Added back-to-back reads and writes.
+-- Revision 0.4 - Allow refeshes to be delayed till next PRECHARGE is issued,
+--                Unless they get really, really delayed. If a delay occurs multiple
+--                refreshes might get pushed out, but it will have avioded about 
+--                50% of the refresh overhead
+-- Revision 0.5 - Add more paramaters to the design, allowing it to work for both the 
+--                Papilio Pro and Logi-Pi
+--
+-- Worst case performance (single accesses to different rows or banks) is: 
+-- Writes 16 cycles = 6,250,000 writes/sec = 25.0MB/s (excluding refresh overhead)
+-- Reads  17 cycles = 5,882,352 reads/sec  = 23.5MB/s (excluding refresh overhead)
+--
+-- For 1:1 mixed reads and writes into the same row it is around 88MB/s 
+-- For reads or wries to the same it is can be as high as 184MB/s 
+----------------------------------------------------------------------------------
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+library UNISIM;
+use IEEE.NUMERIC_STD.ALL;
+library altera_mf;
+use altera_mf.altera_mf_components.all;
+
+
+entity SDRAM_Controller is
+    generic (
+      sdram_address_width : natural;
+      sdram_column_bits   : natural;
+      sdram_startup_cycles: natural;
+      cycles_per_refresh  : natural
+    );
+    Port ( clk           : in  STD_LOGIC;
+           reset         : in  STD_LOGIC;
+           
+           -- Interface to issue reads or write data
+           cmd_ready         : out STD_LOGIC;                     -- '1' when a new command will be acted on
+           cmd_enable        : in  STD_LOGIC;                     -- Set to '1' to issue new command (only acted on when cmd_read = '1')
+           cmd_wr            : in  STD_LOGIC;                     -- Is this a write?
+           cmd_address       : in  STD_LOGIC_VECTOR(sdram_address_width-2 downto 0); -- address to read/write
+           cmd_byte_enable   : in  STD_LOGIC_VECTOR(3 downto 0);  -- byte masks for the write command
+           cmd_data_in       : in  STD_LOGIC_VECTOR(31 downto 0); -- data for the write command
+           
+           data_out          : out STD_LOGIC_VECTOR(31 downto 0); -- word read from SDRAM
+           data_out_ready    : out STD_LOGIC;                     -- is new data ready?
+           
+           -- SDRAM signals
+           SDRAM_CLK     : out   STD_LOGIC;
+           SDRAM_CKE     : out   STD_LOGIC;
+           SDRAM_CS      : out   STD_LOGIC;
+           SDRAM_RAS     : out   STD_LOGIC;
+           SDRAM_CAS     : out   STD_LOGIC;
+           SDRAM_WE      : out   STD_LOGIC;
+           SDRAM_DQM     : out   STD_LOGIC_VECTOR( 1 downto 0);
+           SDRAM_ADDR    : out   STD_LOGIC_VECTOR(12 downto 0);
+           SDRAM_BA      : out   STD_LOGIC_VECTOR( 1 downto 0);
+           SDRAM_DATA    : inout STD_LOGIC_VECTOR(15 downto 0));
+end SDRAM_Controller;
+
+architecture Behavioral of SDRAM_Controller is
+   -- From page 37 of MT48LC16M16A2 datasheet
+   -- Name (Function)       CS# RAS# CAS# WE# DQM  Addr    Data
+   -- COMMAND INHIBIT (NOP)  H   X    X    X   X     X       X
+   -- NO OPERATION (NOP)     L   H    H    H   X     X       X
+   -- ACTIVE                 L   L    H    H   X  Bank/row   X
+   -- READ                   L   H    L    H  L/H Bank/col   X
+   -- WRITE                  L   H    L    L  L/H Bank/col Valid
+   -- BURST TERMINATE        L   H    H    L   X     X     Active
+   -- PRECHARGE              L   L    H    L   X   Code      X
+   -- AUTO REFRESH           L   L    L    H   X     X       X 
+   -- LOAD MODE REGISTER     L   L    L    L   X  Op-code    X 
+   -- Write enable           X   X    X    X   L     X     Active
+   -- Write inhibit          X   X    X    X   H     X     High-Z
+
+   -- Here are the commands mapped to constants   
+   constant CMD_UNSELECTED    : std_logic_vector(3 downto 0) := "1000";
+   constant CMD_NOP           : std_logic_vector(3 downto 0) := "0111";
+   constant CMD_ACTIVE        : std_logic_vector(3 downto 0) := "0011";
+   constant CMD_READ          : std_logic_vector(3 downto 0) := "0101";
+   constant CMD_WRITE         : std_logic_vector(3 downto 0) := "0100";
+   constant CMD_TERMINATE     : std_logic_vector(3 downto 0) := "0110";
+   constant CMD_PRECHARGE     : std_logic_vector(3 downto 0) := "0010";
+   constant CMD_REFRESH       : std_logic_vector(3 downto 0) := "0001";
+   constant CMD_LOAD_MODE_REG : std_logic_vector(3 downto 0) := "0000";
+
+   constant MODE_REG          : std_logic_vector(12 downto 0) := 
+    -- Reserved, wr bust, OpMode, CAS Latency (2), Burst Type, Burst Length (2)
+         "000" &   "0"  &  "00"  &    "010"      &     "0"    &   "001";
+
+   signal iob_command     : std_logic_vector( 3 downto 0) := CMD_NOP;
+   signal iob_address     : std_logic_vector(12 downto 0) := (others => '0');
+   signal iob_data        : std_logic_vector(15 downto 0) := (others => '0');
+   signal iob_dqm         : std_logic_vector( 1 downto 0) := (others => '0');
+   signal iob_cke         : std_logic := '0';
+   signal iob_bank        : std_logic_vector( 1 downto 0) := (others => '0');
+   
+   attribute IOB: string;
+   attribute IOB of iob_command: signal is "true";
+   attribute IOB of iob_address: signal is "true";
+   attribute IOB of iob_dqm    : signal is "true";
+   attribute IOB of iob_cke    : signal is "true";
+   attribute IOB of iob_bank   : signal is "true";
+   attribute IOB of iob_data   : signal is "true";
+   
+   signal iob_data_next      : std_logic_vector(15 downto 0) := (others => '0');
+   signal captured_data      : std_logic_vector(15 downto 0) := (others => '0');
+   signal captured_data_last : std_logic_vector(15 downto 0) := (others => '0');
+   signal sdram_din          : std_logic_vector(15 downto 0);
+   attribute IOB of captured_data : signal is "true";
+   
+   type fsm_state is (s_startup,
+                      s_idle_in_6, s_idle_in_5, s_idle_in_4,   s_idle_in_3, s_idle_in_2, s_idle_in_1,
+                      s_idle,
+                      s_open_in_2, s_open_in_1,
+                      s_write_1, s_write_2, s_write_3,
+                      s_read_1,  s_read_2,  s_read_3,  s_read_4,  
+                      s_precharge
+                      );
+
+   signal state              : fsm_state := s_startup;
+   attribute FSM_ENCODING : string;
+   attribute FSM_ENCODING of state : signal is "ONE-HOT";
+   
+   -- dual purpose counter, it counts up during the startup phase, then is used to trigger refreshes.
+   constant startup_refresh_max   : unsigned(13 downto 0) := (others => '1');  
+   signal   startup_refresh_count : unsigned(13 downto 0) := startup_refresh_max-to_unsigned(sdram_startup_cycles,14);
+
+   -- logic to decide when to refresh
+   signal pending_refresh : std_logic := '0';
+   signal forcing_refresh : std_logic := '0';
+
+   -- The incoming address is split into these three values
+   signal addr_row         : std_logic_vector(12 downto 0) := (others => '0');
+   signal addr_col         : std_logic_vector(12 downto 0) := (others => '0');
+   signal addr_bank        : std_logic_vector( 1 downto 0) := (others => '0');
+   
+   signal dqm_sr           : std_logic_vector( 3 downto 0) := (others => '1'); -- an extra two bits in case CAS=3
+   
+   -- signals to hold the requested transaction before it is completed
+   signal save_wr          : std_logic := '0';
+   signal save_row         : std_logic_vector(12 downto 0);
+   signal save_bank        : std_logic_vector( 1 downto 0);
+   signal save_col         : std_logic_vector(12 downto 0);
+   signal save_data_in     : std_logic_vector(31 downto 0);
+   signal save_byte_enable : std_logic_vector( 3 downto 0);
+   
+   -- control when new transactions are accepted
+   signal ready_for_new    : std_logic := '0';
+   signal got_transaction  : std_logic := '0';
+   
+   signal can_back_to_back : std_logic := '0';
+
+   -- signal to control the Hi-Z state of the DQ bus
+   signal iob_dq_hiz       : std_logic := '1';
+
+   -- signals for when to read the data off of the bus
+   signal data_ready_delay : std_logic_vector( 4 downto 0);   
+   
+   -- bit indexes used when splitting the address into row/colum/bank.
+   constant start_of_col  : natural := 0;
+   constant end_of_col    : natural := sdram_column_bits-2;
+   constant start_of_bank : natural := sdram_column_bits-1;
+   constant end_of_bank   : natural := sdram_column_bits;
+   constant start_of_row  : natural := sdram_column_bits+1;
+   constant end_of_row    : natural := sdram_address_width-2;
+   constant prefresh_cmd  : natural := 10;
+begin
+   -- Indicate the need to refresh when the counter is 2048,
+   -- Force a refresh when the counter is 4096 - (if a refresh is forced, 
+   -- multiple refresshes will be forced until the counter is below 2048
+   pending_refresh <= startup_refresh_count(11);
+   forcing_refresh <= startup_refresh_count(12);
+
+   -- tell the outside world when we can accept a new transaction;
+   cmd_ready <= ready_for_new;
+   ----------------------------------------------------------------------------
+   -- Seperate the address into row / bank / address
+   ----------------------------------------------------------------------------
+   addr_row(end_of_row-start_of_row downto 0) <= cmd_address(end_of_row  downto start_of_row);       -- 12:0 <=  22:10
+   addr_bank                                  <= cmd_address(end_of_bank downto start_of_bank);      -- 1:0  <=  9:8
+   addr_col(sdram_column_bits-1 downto 0)     <= cmd_address(end_of_col  downto start_of_col) & '0'; -- 8:0  <=  7:0 & '0'
+   --addr_row(12 downto 0) <= cmd_address(22 downto 10);       -- 12:0 <=  22:10
+   --addr_bank             <= cmd_address( 9 downto  8);      -- 1:0  <=  9:8
+   --addr_col(8 downto 0)  <= cmd_address( 7 downto  0) & '0'; -- 8:0  <=  7:0 & '0'
+
+   -----------------------------------------------
+   --!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+   --!! Ensure that all outputs are registered. !!
+   --!! Check the pinout report to be sure      !!
+   --!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+   -----------------------------------------------
+   sdram_cke  <= iob_cke;
+   sdram_CS   <= iob_command(3);
+   sdram_RAS  <= iob_command(2);
+   sdram_CAS  <= iob_command(1);
+   sdram_WE   <= iob_command(0);
+   sdram_dqm  <= iob_dqm;
+   sdram_ba   <= iob_bank;
+   sdram_addr <= iob_address;
+   
+   ---------------------------------------------------------------
+   -- Explicitly set up the tristate I/O buffers on the DQ signals
+   ---------------------------------------------------------------
+iob_dq_g: for i in 0 to 15 generate
+   begin
+iob_dq_iob: altiobuf_bidir
+   generic map (number_of_channels => 1)
+   port map ( dataout(0)  => sdram_din(i), dataio(0) => sdram_data(i), datain(0) => iob_data(i), oe(0)  => iob_dq_hiz);
+end generate;
+                                     
+capture_proc: process(clk) 
+   begin
+     if rising_edge(clk) then
+         captured_data      <= sdram_din;
+      end if;
+   end process;
+
+main_proc: process(clk) 
+   begin
+      if rising_edge(clk) then
+         captured_data_last <= captured_data;
+      
+         ------------------------------------------------
+         -- Default state is to do nothing
+         ------------------------------------------------
+         iob_command     <= CMD_NOP;
+         iob_address     <= (others => '0');
+         iob_bank        <= (others => '0');
+
+         ------------------------------------------------
+         -- countdown for initialisation & refresh
+         ------------------------------------------------
+         startup_refresh_count <= startup_refresh_count+1;
+                  
+         -------------------------------------------------------------------
+         -- It we are ready for a new tranasction and one is being presented
+         -- then accept it. Also remember what we are reading or writing,
+         -- and if it can be back-to-backed with the last transaction
+         -------------------------------------------------------------------
+         if ready_for_new = '1' and cmd_enable = '1' then
+            if save_bank = addr_bank and save_row = addr_row then
+               can_back_to_back <= '1';
+            else
+               can_back_to_back <= '0';
+            end if;
+            save_row         <= addr_row;
+            save_bank        <= addr_bank;
+            save_col         <= addr_col;
+            save_wr          <= cmd_wr; 
+            save_data_in     <= cmd_data_in;
+            save_byte_enable <= cmd_byte_enable;
+            got_transaction  <= '1';
+            ready_for_new    <= '0';
+         end if;
+
+         ------------------------------------------------
+         -- Handle the data coming back from the 
+         -- SDRAM for the Read transaction
+         ------------------------------------------------
+         data_out_ready <= '0';
+         if data_ready_delay(0) = '1' then
+            data_out       <= captured_data & captured_data_last;
+            data_out_ready <= '1';
+         end if;
+         
+         ----------------------------------------------------------------------------
+         -- update shift registers used to choose when to present data to/from memory
+         ----------------------------------------------------------------------------
+         data_ready_delay <= '0' & data_ready_delay(data_ready_delay'high downto 1);
+         iob_dqm          <= dqm_sr(1 downto 0);
+         dqm_sr           <= "11" & dqm_sr(dqm_sr'high downto 2);
+         
+         case state is 
+            when s_startup =>
+               ------------------------------------------------------------------------
+               -- This is the initial startup state, where we wait for at least 100us
+               -- before starting the start sequence
+               -- 
+               -- The initialisation is sequence is 
+               --  * de-assert SDRAM_CKE
+               --  * 100us wait, 
+               --  * assert SDRAM_CKE
+               --  * wait at least one cycle, 
+               --  * PRECHARGE
+               --  * wait 2 cycles
+               --  * REFRESH, 
+               --  * tREF wait
+               --  * REFRESH, 
+               --  * tREF wait 
+               --  * LOAD_MODE_REG 
+               --  * 2 cycles wait
+               ------------------------------------------------------------------------
+               iob_CKE <= '1';
+               
+               -- All the commands during the startup are NOPS, except these
+               if startup_refresh_count = startup_refresh_max-31 then      
+                  -- ensure all rows are closed
+                  iob_command     <= CMD_PRECHARGE;
+                  iob_address(prefresh_cmd) <= '1';  -- all banks
+                  iob_bank        <= (others => '0');
+               elsif startup_refresh_count = startup_refresh_max-23 then   
+                  -- these refreshes need to be at least tREF (66ns) apart
+                  iob_command     <= CMD_REFRESH;
+               elsif startup_refresh_count = startup_refresh_max-15 then
+                  iob_command     <= CMD_REFRESH;
+               elsif startup_refresh_count = startup_refresh_max-7 then    
+                  -- Now load the mode register
+                  iob_command     <= CMD_LOAD_MODE_REG;
+                  iob_address     <= MODE_REG;
+               end if;
+
+               ------------------------------------------------------
+               -- if startup is coomplete then go into idle mode,
+               -- get prepared to accept a new command, and schedule
+               -- the first refresh cycle
+               ------------------------------------------------------
+               if startup_refresh_count = 0 then
+                  state           <= s_idle;
+                  ready_for_new   <= '1';
+                  got_transaction <= '0';
+                  startup_refresh_count <= to_unsigned(2048 - cycles_per_refresh+1,14);
+               end if;
+               
+            when s_idle_in_6 => state <= s_idle_in_5;
+            when s_idle_in_5 => state <= s_idle_in_4;
+            when s_idle_in_4 => state <= s_idle_in_3;
+            when s_idle_in_3 => state <= s_idle_in_2;
+            when s_idle_in_2 => state <= s_idle_in_1;
+            when s_idle_in_1 => state <= s_idle;
+
+            when s_idle =>
+               -- Priority is to issue a refresh if one is outstanding
+               if pending_refresh = '1' or forcing_refresh = '1' then
+                 ------------------------------------------------------------------------
+                  -- Start the refresh cycle. 
+                  -- This tasks tRFC (66ns), so 6 idle cycles are needed @ 100MHz
+                  ------------------------------------------------------------------------
+                  state       <= s_idle_in_3;
+                  iob_command <= CMD_REFRESH;
+                  startup_refresh_count <= startup_refresh_count - cycles_per_refresh+1;
+               elsif got_transaction = '1' then
+                  --------------------------------
+                  -- Start the read or write cycle. 
+                  -- First task is to open the row
+                  --------------------------------
+                  state       <= s_open_in_1;
+                  iob_command <= CMD_ACTIVE;
+                  iob_address <= save_row;
+                  iob_bank    <= save_bank;
+               end if;               
+               
+            --------------------------------------------
+            -- Opening the row ready for reads or writes
+            --------------------------------------------
+            when s_open_in_2 => state <= s_open_in_1;
+
+            when s_open_in_1 =>
+               -- still waiting for row to open
+               if save_wr = '1' then
+                  state       <= s_write_1;
+                  iob_dq_hiz  <= '0';
+                  iob_data    <= save_data_in(15 downto 0); -- get the DQ bus out of HiZ early
+               else
+                  iob_dq_hiz  <= '1';
+                  state       <= s_read_1;
+               end if;
+               -- we will be ready for a new transaction next cycle!
+               ready_for_new   <= '1'; 
+               got_transaction <= '0';                  
+
+            ----------------------------------
+            -- Processing the read transaction
+            ----------------------------------
+            when s_read_1 =>
+               state           <= s_read_2;
+               iob_command     <= CMD_READ;
+               iob_address     <= save_col; 
+               iob_bank        <= save_bank;
+               iob_address(prefresh_cmd) <= '0'; -- A10 actually matters - it selects auto precharge
+               
+               -- Schedule reading the data values off the bus
+               data_ready_delay(data_ready_delay'high)   <= '1';
+               
+               -- Set the data masks to read all bytes
+               iob_dqm            <= (others => '0');
+               dqm_sr(1 downto 0) <= (others => '0');
+               
+            when s_read_2 =>
+               state <= s_read_3;
+               if forcing_refresh = '0' and got_transaction = '1' and can_back_to_back = '1' then
+                  if save_wr = '0' then
+                     state         <= s_read_1;
+                     ready_for_new <= '1'; -- we will be ready for a new transaction next cycle!
+                  end if;
+               end if;
+               
+            when s_read_3 => 
+               state <= s_read_4;
+               if forcing_refresh = '0' and got_transaction = '1' and can_back_to_back = '1' then
+                  if save_wr = '0' then
+                     state         <= s_read_1;
+                     ready_for_new <= '1'; -- we will be ready for a new transaction next cycle!
+                  end if;
+               end if;
+
+            when s_read_4 => 
+               state <= s_precharge;
+               -- can we do back-to-back read?
+               if forcing_refresh = '0' and got_transaction = '1' and can_back_to_back = '1' then
+                  if save_wr = '0' then
+                     state         <= s_read_1;
+                     ready_for_new <= '1'; -- we will be ready for a new transaction next cycle!
+                  else
+                     state <= s_open_in_2; -- we have to wait for the read data to come back before we swutch the bus into HiZ
+                  end if;
+               end if;
+
+            ------------------------------------------------------------------
+            -- Processing the write transaction
+            -------------------------------------------------------------------
+            when s_write_1 =>
+               state              <= s_write_2;
+               iob_command        <= CMD_WRITE;
+               iob_address        <= save_col; 
+               iob_address(prefresh_cmd)    <= '0'; -- A10 actually matters - it selects auto precharge
+               iob_bank           <= save_bank;
+               iob_dqm            <= NOT save_byte_enable(1 downto 0);    
+               dqm_sr(1 downto 0) <= NOT save_byte_enable(3 downto 2);    
+               iob_data           <= save_data_in(15 downto 0);
+               iob_data_next      <= save_data_in(31 downto 16);
+               
+            when s_write_2 =>
+               state           <= s_write_3;
+               iob_data        <= iob_data_next;
+         
+            when s_write_3 =>  -- must wait tRDL, hence the extra idle state
+               iob_dq_hiz         <= '1';
+               state              <= s_precharge;
+
+            -------------------------------------------------------------------
+            -- Closing the row off (this closes all banks)
+            -------------------------------------------------------------------
+            when s_precharge =>
+               state           <= s_idle_in_3;
+               iob_command     <= CMD_PRECHARGE;
+               iob_address(prefresh_cmd) <= '1'; -- A10 actually matters - it selects all banks or just one
+
+            -------------------------------------------------------------------
+            -- We should never get here, but if we do then reset the memory
+            -------------------------------------------------------------------
+            when others => 
+               state                 <= s_startup;
+               ready_for_new         <= '0';
+               startup_refresh_count <= startup_refresh_max-to_unsigned(sdram_startup_cycles,14);
+         end case;
+
+         if reset = '1' then  -- Sync reset
+            state                 <= s_startup;
+            ready_for_new         <= '0';
+            startup_refresh_count <= startup_refresh_max-to_unsigned(sdram_startup_cycles,14);
+         end if;
+      end if;      
+   end process;
+end Behavioral;

+ 429 - 0
vhdl/board/de0_nano/pll_de0_nano.vhd

@@ -0,0 +1,429 @@
+-- megafunction wizard: %ALTPLL%
+-- GENERATION: STANDARD
+-- VERSION: WM1.0
+-- MODULE: altpll 
+
+-- ============================================================
+-- File Name: pll.vhd
+-- Megafunction Name(s):
+-- 			altpll
+--
+-- Simulation Library Files(s):
+-- 			altera_mf
+-- ============================================================
+-- ************************************************************
+-- THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE!
+--
+-- 13.1.0 Build 162 10/23/2013 SJ Web Edition
+-- ************************************************************
+
+
+--Copyright (C) 1991-2013 Altera Corporation
+--Your use of Altera Corporation's design tools, logic functions 
+--and other software and tools, and its AMPP partner logic 
+--functions, and any output files from any of the foregoing 
+--(including device programming or simulation files), and any 
+--associated documentation or information are expressly subject 
+--to the terms and conditions of the Altera Program License 
+--Subscription Agreement, Altera MegaCore Function License 
+--Agreement, or other applicable license agreement, including, 
+--without limitation, that your use is for the sole purpose of 
+--programming logic devices manufactured by Altera and sold by 
+--Altera or its authorized distributors.  Please refer to the 
+--applicable agreement for further details.
+
+
+LIBRARY ieee;
+USE ieee.std_logic_1164.all;
+
+LIBRARY altera_mf;
+USE altera_mf.all;
+
+ENTITY pll IS
+	PORT
+	(
+		areset		: IN STD_LOGIC  := '0';
+		inclk0		: IN STD_LOGIC  := '0';
+		c0		: OUT STD_LOGIC ;
+		c1		: OUT STD_LOGIC ;
+		c2		: OUT STD_LOGIC ;
+		locked		: OUT STD_LOGIC 
+	);
+END pll;
+
+
+ARCHITECTURE SYN OF pll IS
+
+	SIGNAL sub_wire0	: STD_LOGIC_VECTOR (4 DOWNTO 0);
+	SIGNAL sub_wire1	: STD_LOGIC ;
+	SIGNAL sub_wire2	: STD_LOGIC ;
+	SIGNAL sub_wire3	: STD_LOGIC ;
+	SIGNAL sub_wire4	: STD_LOGIC ;
+	SIGNAL sub_wire5	: STD_LOGIC ;
+	SIGNAL sub_wire6	: STD_LOGIC_VECTOR (1 DOWNTO 0);
+	SIGNAL sub_wire7_bv	: BIT_VECTOR (0 DOWNTO 0);
+	SIGNAL sub_wire7	: STD_LOGIC_VECTOR (0 DOWNTO 0);
+
+
+
+	COMPONENT altpll
+	GENERIC (
+		bandwidth_type		: STRING;
+		clk0_divide_by		: NATURAL;
+		clk0_duty_cycle		: NATURAL;
+		clk0_multiply_by		: NATURAL;
+		clk0_phase_shift		: STRING;
+		clk1_divide_by		: NATURAL;
+		clk1_duty_cycle		: NATURAL;
+		clk1_multiply_by		: NATURAL;
+		clk1_phase_shift		: STRING;
+		clk2_divide_by		: NATURAL;
+		clk2_duty_cycle		: NATURAL;
+		clk2_multiply_by		: NATURAL;
+		clk2_phase_shift		: STRING;
+		compensate_clock		: STRING;
+		inclk0_input_frequency		: NATURAL;
+		intended_device_family		: STRING;
+		lpm_hint		: STRING;
+		lpm_type		: STRING;
+		operation_mode		: STRING;
+		pll_type		: STRING;
+		port_activeclock		: STRING;
+		port_areset		: STRING;
+		port_clkbad0		: STRING;
+		port_clkbad1		: STRING;
+		port_clkloss		: STRING;
+		port_clkswitch		: STRING;
+		port_configupdate		: STRING;
+		port_fbin		: STRING;
+		port_inclk0		: STRING;
+		port_inclk1		: STRING;
+		port_locked		: STRING;
+		port_pfdena		: STRING;
+		port_phasecounterselect		: STRING;
+		port_phasedone		: STRING;
+		port_phasestep		: STRING;
+		port_phaseupdown		: STRING;
+		port_pllena		: STRING;
+		port_scanaclr		: STRING;
+		port_scanclk		: STRING;
+		port_scanclkena		: STRING;
+		port_scandata		: STRING;
+		port_scandataout		: STRING;
+		port_scandone		: STRING;
+		port_scanread		: STRING;
+		port_scanwrite		: STRING;
+		port_clk0		: STRING;
+		port_clk1		: STRING;
+		port_clk2		: STRING;
+		port_clk3		: STRING;
+		port_clk4		: STRING;
+		port_clk5		: STRING;
+		port_clkena0		: STRING;
+		port_clkena1		: STRING;
+		port_clkena2		: STRING;
+		port_clkena3		: STRING;
+		port_clkena4		: STRING;
+		port_clkena5		: STRING;
+		port_extclk0		: STRING;
+		port_extclk1		: STRING;
+		port_extclk2		: STRING;
+		port_extclk3		: STRING;
+		self_reset_on_loss_lock		: STRING;
+		width_clock		: NATURAL
+	);
+	PORT (
+			areset	: IN STD_LOGIC ;
+			clk	: OUT STD_LOGIC_VECTOR (4 DOWNTO 0);
+			inclk	: IN STD_LOGIC_VECTOR (1 DOWNTO 0);
+			locked	: OUT STD_LOGIC 
+	);
+	END COMPONENT;
+
+BEGIN
+	sub_wire7_bv(0 DOWNTO 0) <= "0";
+	sub_wire7    <= To_stdlogicvector(sub_wire7_bv);
+	sub_wire4    <= sub_wire0(2);
+	sub_wire3    <= sub_wire0(0);
+	sub_wire1    <= sub_wire0(1);
+	c1    <= sub_wire1;
+	locked    <= sub_wire2;
+	c0    <= sub_wire3;
+	c2    <= sub_wire4;
+	sub_wire5    <= inclk0;
+	sub_wire6    <= sub_wire7(0 DOWNTO 0) & sub_wire5;
+
+	altpll_component : altpll
+	GENERIC MAP (
+		bandwidth_type => "AUTO",
+		clk0_divide_by => 1,
+		clk0_duty_cycle => 50,
+		clk0_multiply_by => 1,
+		clk0_phase_shift => "-3000",
+		clk1_divide_by => 1,
+		clk1_duty_cycle => 50,
+		clk1_multiply_by => 1,
+		clk1_phase_shift => "0",
+		clk2_divide_by => 1,
+		clk2_duty_cycle => 50,
+		clk2_multiply_by => 2,
+		clk2_phase_shift => "0",
+		compensate_clock => "CLK1",
+		inclk0_input_frequency => 20000,
+		intended_device_family => "Cyclone IV E",
+		lpm_hint => "CBX_MODULE_PREFIX=pll",
+		lpm_type => "altpll",
+		operation_mode => "NORMAL",
+		pll_type => "AUTO",
+		port_activeclock => "PORT_UNUSED",
+		port_areset => "PORT_USED",
+		port_clkbad0 => "PORT_UNUSED",
+		port_clkbad1 => "PORT_UNUSED",
+		port_clkloss => "PORT_UNUSED",
+		port_clkswitch => "PORT_UNUSED",
+		port_configupdate => "PORT_UNUSED",
+		port_fbin => "PORT_UNUSED",
+		port_inclk0 => "PORT_USED",
+		port_inclk1 => "PORT_UNUSED",
+		port_locked => "PORT_USED",
+		port_pfdena => "PORT_UNUSED",
+		port_phasecounterselect => "PORT_UNUSED",
+		port_phasedone => "PORT_UNUSED",
+		port_phasestep => "PORT_UNUSED",
+		port_phaseupdown => "PORT_UNUSED",
+		port_pllena => "PORT_UNUSED",
+		port_scanaclr => "PORT_UNUSED",
+		port_scanclk => "PORT_UNUSED",
+		port_scanclkena => "PORT_UNUSED",
+		port_scandata => "PORT_UNUSED",
+		port_scandataout => "PORT_UNUSED",
+		port_scandone => "PORT_UNUSED",
+		port_scanread => "PORT_UNUSED",
+		port_scanwrite => "PORT_UNUSED",
+		port_clk0 => "PORT_USED",
+		port_clk1 => "PORT_USED",
+		port_clk2 => "PORT_USED",
+		port_clk3 => "PORT_UNUSED",
+		port_clk4 => "PORT_UNUSED",
+		port_clk5 => "PORT_UNUSED",
+		port_clkena0 => "PORT_UNUSED",
+		port_clkena1 => "PORT_UNUSED",
+		port_clkena2 => "PORT_UNUSED",
+		port_clkena3 => "PORT_UNUSED",
+		port_clkena4 => "PORT_UNUSED",
+		port_clkena5 => "PORT_UNUSED",
+		port_extclk0 => "PORT_UNUSED",
+		port_extclk1 => "PORT_UNUSED",
+		port_extclk2 => "PORT_UNUSED",
+		port_extclk3 => "PORT_UNUSED",
+		self_reset_on_loss_lock => "OFF",
+		width_clock => 5
+	)
+	PORT MAP (
+		areset => areset,
+		inclk => sub_wire6,
+		clk => sub_wire0,
+		locked => sub_wire2
+	);
+
+
+
+END SYN;
+
+-- ============================================================
+-- CNX file retrieval info
+-- ============================================================
+-- Retrieval info: PRIVATE: ACTIVECLK_CHECK STRING "0"
+-- Retrieval info: PRIVATE: BANDWIDTH STRING "1.000"
+-- Retrieval info: PRIVATE: BANDWIDTH_FEATURE_ENABLED STRING "1"
+-- Retrieval info: PRIVATE: BANDWIDTH_FREQ_UNIT STRING "MHz"
+-- Retrieval info: PRIVATE: BANDWIDTH_PRESET STRING "Low"
+-- Retrieval info: PRIVATE: BANDWIDTH_USE_AUTO STRING "1"
+-- Retrieval info: PRIVATE: BANDWIDTH_USE_PRESET STRING "0"
+-- Retrieval info: PRIVATE: CLKBAD_SWITCHOVER_CHECK STRING "0"
+-- Retrieval info: PRIVATE: CLKLOSS_CHECK STRING "0"
+-- Retrieval info: PRIVATE: CLKSWITCH_CHECK STRING "0"
+-- Retrieval info: PRIVATE: CNX_NO_COMPENSATE_RADIO STRING "0"
+-- Retrieval info: PRIVATE: CREATE_CLKBAD_CHECK STRING "0"
+-- Retrieval info: PRIVATE: CREATE_INCLK1_CHECK STRING "0"
+-- Retrieval info: PRIVATE: CUR_DEDICATED_CLK STRING "c1"
+-- Retrieval info: PRIVATE: CUR_FBIN_CLK STRING "c0"
+-- Retrieval info: PRIVATE: DEVICE_SPEED_GRADE STRING "Any"
+-- Retrieval info: PRIVATE: DIV_FACTOR0 NUMERIC "1"
+-- Retrieval info: PRIVATE: DIV_FACTOR1 NUMERIC "1"
+-- Retrieval info: PRIVATE: DIV_FACTOR2 NUMERIC "1"
+-- Retrieval info: PRIVATE: DUTY_CYCLE0 STRING "50.00000000"
+-- Retrieval info: PRIVATE: DUTY_CYCLE1 STRING "50.00000000"
+-- Retrieval info: PRIVATE: DUTY_CYCLE2 STRING "50.00000000"
+-- Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE0 STRING "100.000000"
+-- Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE1 STRING "100.000000"
+-- Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE2 STRING "100.000000"
+-- Retrieval info: PRIVATE: EXPLICIT_SWITCHOVER_COUNTER STRING "0"
+-- Retrieval info: PRIVATE: EXT_FEEDBACK_RADIO STRING "0"
+-- Retrieval info: PRIVATE: GLOCKED_COUNTER_EDIT_CHANGED STRING "1"
+-- Retrieval info: PRIVATE: GLOCKED_FEATURE_ENABLED STRING "0"
+-- Retrieval info: PRIVATE: GLOCKED_MODE_CHECK STRING "0"
+-- Retrieval info: PRIVATE: GLOCK_COUNTER_EDIT NUMERIC "1048575"
+-- Retrieval info: PRIVATE: HAS_MANUAL_SWITCHOVER STRING "1"
+-- Retrieval info: PRIVATE: INCLK0_FREQ_EDIT STRING "50.000"
+-- Retrieval info: PRIVATE: INCLK0_FREQ_UNIT_COMBO STRING "MHz"
+-- Retrieval info: PRIVATE: INCLK1_FREQ_EDIT STRING "100.000"
+-- Retrieval info: PRIVATE: INCLK1_FREQ_EDIT_CHANGED STRING "1"
+-- Retrieval info: PRIVATE: INCLK1_FREQ_UNIT_CHANGED STRING "1"
+-- Retrieval info: PRIVATE: INCLK1_FREQ_UNIT_COMBO STRING "MHz"
+-- Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E"
+-- Retrieval info: PRIVATE: INT_FEEDBACK__MODE_RADIO STRING "1"
+-- Retrieval info: PRIVATE: LOCKED_OUTPUT_CHECK STRING "1"
+-- Retrieval info: PRIVATE: LONG_SCAN_RADIO STRING "1"
+-- Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE STRING "Not Available"
+-- Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE_DIRTY NUMERIC "0"
+-- Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT0 STRING "deg"
+-- Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT1 STRING "deg"
+-- Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT2 STRING "ps"
+-- Retrieval info: PRIVATE: MIG_DEVICE_SPEED_GRADE STRING "Any"
+-- Retrieval info: PRIVATE: MIRROR_CLK0 STRING "0"
+-- Retrieval info: PRIVATE: MIRROR_CLK1 STRING "0"
+-- Retrieval info: PRIVATE: MIRROR_CLK2 STRING "0"
+-- Retrieval info: PRIVATE: MULT_FACTOR0 NUMERIC "2"
+-- Retrieval info: PRIVATE: MULT_FACTOR1 NUMERIC "2"
+-- Retrieval info: PRIVATE: MULT_FACTOR2 NUMERIC "2"
+-- Retrieval info: PRIVATE: NORMAL_MODE_RADIO STRING "1"
+-- Retrieval info: PRIVATE: OUTPUT_FREQ0 STRING "90.00000000"
+-- Retrieval info: PRIVATE: OUTPUT_FREQ1 STRING "100.00000000"
+-- Retrieval info: PRIVATE: OUTPUT_FREQ2 STRING "100.00000000"
+-- Retrieval info: PRIVATE: OUTPUT_FREQ_MODE0 STRING "0"
+-- Retrieval info: PRIVATE: OUTPUT_FREQ_MODE1 STRING "0"
+-- Retrieval info: PRIVATE: OUTPUT_FREQ_MODE2 STRING "0"
+-- Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT0 STRING "MHz"
+-- Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT1 STRING "MHz"
+-- Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT2 STRING "MHz"
+-- Retrieval info: PRIVATE: PHASE_RECONFIG_FEATURE_ENABLED STRING "1"
+-- Retrieval info: PRIVATE: PHASE_RECONFIG_INPUTS_CHECK STRING "0"
+-- Retrieval info: PRIVATE: PHASE_SHIFT0 STRING "-54.00000000"
+-- Retrieval info: PRIVATE: PHASE_SHIFT1 STRING "0.00000000"
+-- Retrieval info: PRIVATE: PHASE_SHIFT2 STRING "0.00000000"
+-- Retrieval info: PRIVATE: PHASE_SHIFT_STEP_ENABLED_CHECK STRING "0"
+-- Retrieval info: PRIVATE: PHASE_SHIFT_UNIT0 STRING "deg"
+-- Retrieval info: PRIVATE: PHASE_SHIFT_UNIT1 STRING "deg"
+-- Retrieval info: PRIVATE: PHASE_SHIFT_UNIT2 STRING "ps"
+-- Retrieval info: PRIVATE: PLL_ADVANCED_PARAM_CHECK STRING "0"
+-- Retrieval info: PRIVATE: PLL_ARESET_CHECK STRING "1"
+-- Retrieval info: PRIVATE: PLL_AUTOPLL_CHECK NUMERIC "1"
+-- Retrieval info: PRIVATE: PLL_ENHPLL_CHECK NUMERIC "0"
+-- Retrieval info: PRIVATE: PLL_FASTPLL_CHECK NUMERIC "0"
+-- Retrieval info: PRIVATE: PLL_FBMIMIC_CHECK STRING "0"
+-- Retrieval info: PRIVATE: PLL_LVDS_PLL_CHECK NUMERIC "0"
+-- Retrieval info: PRIVATE: PLL_PFDENA_CHECK STRING "0"
+-- Retrieval info: PRIVATE: PLL_TARGET_HARCOPY_CHECK NUMERIC "0"
+-- Retrieval info: PRIVATE: PRIMARY_CLK_COMBO STRING "inclk0"
+-- Retrieval info: PRIVATE: RECONFIG_FILE STRING "pll.mif"
+-- Retrieval info: PRIVATE: SACN_INPUTS_CHECK STRING "0"
+-- Retrieval info: PRIVATE: SCAN_FEATURE_ENABLED STRING "1"
+-- Retrieval info: PRIVATE: SELF_RESET_LOCK_LOSS STRING "0"
+-- Retrieval info: PRIVATE: SHORT_SCAN_RADIO STRING "0"
+-- Retrieval info: PRIVATE: SPREAD_FEATURE_ENABLED STRING "0"
+-- Retrieval info: PRIVATE: SPREAD_FREQ STRING "50.000"
+-- Retrieval info: PRIVATE: SPREAD_FREQ_UNIT STRING "KHz"
+-- Retrieval info: PRIVATE: SPREAD_PERCENT STRING "0.500"
+-- Retrieval info: PRIVATE: SPREAD_USE STRING "0"
+-- Retrieval info: PRIVATE: SRC_SYNCH_COMP_RADIO STRING "0"
+-- Retrieval info: PRIVATE: STICKY_CLK0 STRING "1"
+-- Retrieval info: PRIVATE: STICKY_CLK1 STRING "1"
+-- Retrieval info: PRIVATE: STICKY_CLK2 STRING "1"
+-- Retrieval info: PRIVATE: SWITCHOVER_COUNT_EDIT NUMERIC "1"
+-- Retrieval info: PRIVATE: SWITCHOVER_FEATURE_ENABLED STRING "1"
+-- Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0"
+-- Retrieval info: PRIVATE: USE_CLK0 STRING "1"
+-- Retrieval info: PRIVATE: USE_CLK1 STRING "1"
+-- Retrieval info: PRIVATE: USE_CLK2 STRING "1"
+-- Retrieval info: PRIVATE: USE_CLKENA0 STRING "0"
+-- Retrieval info: PRIVATE: USE_CLKENA1 STRING "0"
+-- Retrieval info: PRIVATE: USE_CLKENA2 STRING "0"
+-- Retrieval info: PRIVATE: USE_MIL_SPEED_GRADE NUMERIC "0"
+-- Retrieval info: PRIVATE: ZERO_DELAY_RADIO STRING "0"
+-- Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all
+-- Retrieval info: CONSTANT: BANDWIDTH_TYPE STRING "AUTO"
+-- Retrieval info: CONSTANT: CLK0_DIVIDE_BY NUMERIC "1"
+-- Retrieval info: CONSTANT: CLK0_DUTY_CYCLE NUMERIC "50"
+-- Retrieval info: CONSTANT: CLK0_MULTIPLY_BY NUMERIC "2"
+-- Retrieval info: CONSTANT: CLK0_PHASE_SHIFT STRING "-1500"
+-- Retrieval info: CONSTANT: CLK1_DIVIDE_BY NUMERIC "1"
+-- Retrieval info: CONSTANT: CLK1_DUTY_CYCLE NUMERIC "50"
+-- Retrieval info: CONSTANT: CLK1_MULTIPLY_BY NUMERIC "2"
+-- Retrieval info: CONSTANT: CLK1_PHASE_SHIFT STRING "0"
+-- Retrieval info: CONSTANT: CLK2_DIVIDE_BY NUMERIC "1"
+-- Retrieval info: CONSTANT: CLK2_DUTY_CYCLE NUMERIC "50"
+-- Retrieval info: CONSTANT: CLK2_MULTIPLY_BY NUMERIC "2"
+-- Retrieval info: CONSTANT: CLK2_PHASE_SHIFT STRING "0"
+-- Retrieval info: CONSTANT: COMPENSATE_CLOCK STRING "CLK1"
+-- Retrieval info: CONSTANT: INCLK0_INPUT_FREQUENCY NUMERIC "20000"
+-- Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E"
+-- Retrieval info: CONSTANT: LPM_TYPE STRING "altpll"
+-- Retrieval info: CONSTANT: OPERATION_MODE STRING "NORMAL"
+-- Retrieval info: CONSTANT: PLL_TYPE STRING "AUTO"
+-- Retrieval info: CONSTANT: PORT_ACTIVECLOCK STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_ARESET STRING "PORT_USED"
+-- Retrieval info: CONSTANT: PORT_CLKBAD0 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_CLKBAD1 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_CLKLOSS STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_CLKSWITCH STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_CONFIGUPDATE STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_FBIN STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_INCLK0 STRING "PORT_USED"
+-- Retrieval info: CONSTANT: PORT_INCLK1 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_LOCKED STRING "PORT_USED"
+-- Retrieval info: CONSTANT: PORT_PFDENA STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_PHASECOUNTERSELECT STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_PHASEDONE STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_PHASESTEP STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_PHASEUPDOWN STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_PLLENA STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_SCANACLR STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_SCANCLK STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_SCANCLKENA STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_SCANDATA STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_SCANDATAOUT STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_SCANDONE STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_SCANREAD STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_SCANWRITE STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_clk0 STRING "PORT_USED"
+-- Retrieval info: CONSTANT: PORT_clk1 STRING "PORT_USED"
+-- Retrieval info: CONSTANT: PORT_clk2 STRING "PORT_USED"
+-- Retrieval info: CONSTANT: PORT_clk3 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_clk4 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_clk5 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_clkena0 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_clkena1 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_clkena2 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_clkena3 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_clkena4 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_clkena5 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_extclk0 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_extclk1 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_extclk2 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: PORT_extclk3 STRING "PORT_UNUSED"
+-- Retrieval info: CONSTANT: SELF_RESET_ON_LOSS_LOCK STRING "OFF"
+-- Retrieval info: CONSTANT: WIDTH_CLOCK NUMERIC "5"
+-- Retrieval info: USED_PORT: @clk 0 0 5 0 OUTPUT_CLK_EXT VCC "@clk[4..0]"
+-- Retrieval info: USED_PORT: @inclk 0 0 2 0 INPUT_CLK_EXT VCC "@inclk[1..0]"
+-- Retrieval info: USED_PORT: areset 0 0 0 0 INPUT GND "areset"
+-- Retrieval info: USED_PORT: c0 0 0 0 0 OUTPUT_CLK_EXT VCC "c0"
+-- Retrieval info: USED_PORT: c1 0 0 0 0 OUTPUT_CLK_EXT VCC "c1"
+-- Retrieval info: USED_PORT: c2 0 0 0 0 OUTPUT_CLK_EXT VCC "c2"
+-- Retrieval info: USED_PORT: inclk0 0 0 0 0 INPUT_CLK_EXT GND "inclk0"
+-- Retrieval info: USED_PORT: locked 0 0 0 0 OUTPUT GND "locked"
+-- Retrieval info: CONNECT: @areset 0 0 0 0 areset 0 0 0 0
+-- Retrieval info: CONNECT: @inclk 0 0 1 1 GND 0 0 0 0
+-- Retrieval info: CONNECT: @inclk 0 0 1 0 inclk0 0 0 0 0
+-- Retrieval info: CONNECT: c0 0 0 0 0 @clk 0 0 1 0
+-- Retrieval info: CONNECT: c1 0 0 0 0 @clk 0 0 1 1
+-- Retrieval info: CONNECT: c2 0 0 0 0 @clk 0 0 1 2
+-- Retrieval info: CONNECT: locked 0 0 0 0 @locked 0 0 0 0
+-- Retrieval info: GEN_FILE: TYPE_NORMAL pll.vhd TRUE
+-- Retrieval info: GEN_FILE: TYPE_NORMAL pll.ppf TRUE
+-- Retrieval info: GEN_FILE: TYPE_NORMAL pll.inc FALSE
+-- Retrieval info: GEN_FILE: TYPE_NORMAL pll.cmp TRUE
+-- Retrieval info: GEN_FILE: TYPE_NORMAL pll.bsf FALSE
+-- Retrieval info: GEN_FILE: TYPE_NORMAL pll_inst.vhd FALSE
+-- Retrieval info: LIB_FILE: altera_mf
+-- Retrieval info: CBX_MODULE_PREFIX: ON

+ 465 - 0
vhdl/board/de0_nano/top_level_de0_nano.vhd

@@ -0,0 +1,465 @@
+--+-----------------------------------+-------------------------------------+--
+--|                      ___   ___    | (c) 2013-2014 William R Sowerbutts  |--
+--|   ___  ___   ___ ___( _ ) / _ \   | will@sowerbutts.com                 |--
+--|  / __|/ _ \ / __|_  / _ \| | | |  |                                     |--
+--|  \__ \ (_) | (__ / / (_) | |_| |  | A Z80 FPGA computer, just for fun   |--
+--|  |___/\___/ \___/___\___/ \___/   |                                     |--
+--|                                   |              http://sowerbutts.com/ |--
+--+-----------------------------------+-------------------------------------+--
+--| Top level module: connects modules to each other and the outside world  |--
+--+-------------------------------------------------------------------------+--
+--
+-- See README.txt for more details
+--
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+
+entity top_level is
+    Port ( sysclk_32m          : in    std_logic;
+           sys_clk_pad_i       : in    std_logic;
+           rst_n_pad_i         : in    std_logic;
+           leds                : out   std_logic_vector(4 downto 0);
+           reset_button        : in    std_logic;
+           console_select      : in    std_logic;
+
+           -- UART0 (to FTDI USB chip, no flow control)
+           serial_rx           : in    std_logic;
+           serial_tx           : out   std_logic;
+
+           -- UART0 (to MAX3232 level shifter chip, hardware flow control)
+           uart1_rx            : in    std_logic;
+           uart1_cts           : in    std_logic;
+           uart1_tx            : out   std_logic;
+           uart1_rts           : out   std_logic;
+
+           -- SPI flash chip
+           flash_spi_cs        : out   std_logic;
+           flash_spi_clk       : out   std_logic;
+           flash_spi_mosi      : out   std_logic;
+           flash_spi_miso      : in    std_logic;
+
+           -- SD card socket
+           sdcard_spi_cs       : out   std_logic;
+           sdcard_spi_clk      : out   std_logic;
+           sdcard_spi_mosi     : out   std_logic;
+           sdcard_spi_miso     : in    std_logic;
+
+           -- SDRAM chip
+           SDRAM_CLK           : out   std_logic;
+           SDRAM_CKE           : out   std_logic;
+           SDRAM_CS            : out   std_logic;
+           SDRAM_nRAS          : out   std_logic;
+           SDRAM_nCAS          : out   std_logic;
+           SDRAM_nWE           : out   std_logic;
+           SDRAM_DQM           : out   std_logic_vector( 1 downto 0);
+           SDRAM_ADDR          : out   std_logic_vector (12 downto 0);
+           SDRAM_BA            : out   std_logic_vector( 1 downto 0);
+           SDRAM_DQ            : inout std_logic_vector (15 downto 0)
+       );
+end top_level;
+
+architecture Behavioral of top_level is
+    constant clk_freq_mhz        : natural := 50; -- this is the frequency which the PLL outputs, in MHz.
+
+    -- SDRAM configuration
+    constant sdram_address_width : natural := 24;
+    constant sdram_column_bits   : natural := 9;
+    constant cycles_per_refresh  : natural := (64000*clk_freq_mhz)/8192-1;
+
+    -- For simulation, we don't need a long init stage. but for real DRAM we need approx 101us.
+    -- The constant below has a different value when interpreted by the synthesis and simulator 
+    -- tools in order to achieve the desired timing in each.
+    constant sdram_startup_cycles: natural := 101 * clk_freq_mhz
+    -- pragma translate_off
+    - 10000 -- reduce the value the simulator uses
+    -- pragma translate_on
+    ;
+
+    -- signals for clocking
+    signal clk_feedback         : std_logic;  -- PLL clock feedback
+    signal clk_unbuffered       : std_logic;  -- unbuffered system clock
+    signal clk                  : std_logic;  -- buffered system clock (all logic should be clocked by this)
+
+    -- console latch
+    signal console_select_clk1  : std_logic;
+    signal console_select_sync  : std_logic;
+    signal swap_uart01          : std_logic := '0';
+
+    -- system reset signals
+    signal power_on_reset       : std_logic_vector(1 downto 0) := (others => '1');
+    signal system_reset         : std_logic;
+    signal reset_button_clk1    : std_logic;
+    signal reset_button_sync    : std_logic; -- reset button signal, synchronised to our clock
+    signal reset_request_uart   : std_logic; -- reset request signal from FTDI UART (when you send "!~!~!~" to the UART, this line is asserted)
+
+    -- CPU control
+    signal coldboot             : std_logic;
+    signal cpu_clk_enable       : std_logic;
+    signal cpu_m1_cycle         : std_logic;
+    signal cpu_req_mem          : std_logic;
+    signal cpu_req_io           : std_logic;
+    signal req_mem              : std_logic;
+    signal req_io               : std_logic;
+    signal req_read             : std_logic;
+    signal req_write            : std_logic;
+    signal virtual_address      : std_logic_vector(15 downto 0);
+    signal physical_address     : std_logic_vector(25 downto 0);
+    signal mem_wait             : std_logic;
+    signal cpu_wait             : std_logic;
+    signal dram_wait            : std_logic;
+    signal mmu_wait             : std_logic;
+    signal spimaster0_wait      : std_logic;
+    signal spimaster1_wait      : std_logic;
+
+    -- chip selects
+    signal mmu_cs               : std_logic;
+    signal rom_cs               : std_logic;
+    signal sram_cs              : std_logic;
+    signal dram_cs              : std_logic;
+    signal uartA_cs             : std_logic;
+    signal uartB_cs             : std_logic;
+    signal uart0_cs             : std_logic;
+    signal uart1_cs             : std_logic;
+    signal timer_cs             : std_logic;
+    signal spimaster0_cs        : std_logic;
+    signal spimaster1_cs        : std_logic;
+    signal clkscale_cs          : std_logic;
+    signal gpio_cs              : std_logic;
+
+    -- data bus
+    signal cpu_data_in          : std_logic_vector(7 downto 0);
+    signal cpu_data_out         : std_logic_vector(7 downto 0);
+    signal rom_data_out         : std_logic_vector(7 downto 0);
+    signal sram_data_out        : std_logic_vector(7 downto 0);
+    signal dram_data_out        : std_logic_vector(7 downto 0);
+    signal uart0_data_out       : std_logic_vector(7 downto 0);
+    signal uart1_data_out       : std_logic_vector(7 downto 0);
+    signal timer_data_out       : std_logic_vector(7 downto 0);
+    signal spimaster0_data_out  : std_logic_vector(7 downto 0);
+    signal spimaster1_data_out  : std_logic_vector(7 downto 0);
+    signal mmu_data_out         : std_logic_vector(7 downto 0);
+    signal clkscale_out         : std_logic_vector(7 downto 0);
+    signal gpio_data_out        : std_logic_vector(7 downto 0);
+
+    -- GPIO
+    signal gpio_input           : std_logic_vector(7 downto 0);
+    signal gpio_output          : std_logic_vector(7 downto 0);
+
+    -- Interrupts
+    signal cpu_interrupt_in     : std_logic;
+    signal timer_interrupt      : std_logic;
+    signal uart0_interrupt      : std_logic;
+    signal uart1_interrupt      : std_logic;
+
+begin
+    -- Hold CPU reset high for 8 clock cycles on startup,
+    -- and when the user presses their reset button.
+    process(clk)
+    begin
+        if rising_edge(clk) then
+            -- Xilinx advises using two flip-flops are used to bring external
+            -- signals which feed control logic into our clock domain.
+            reset_button_clk1 <= reset_button;
+            reset_button_sync <= reset_button_clk1;
+            console_select_clk1 <= console_select;
+            console_select_sync <= console_select_clk1;
+
+            -- reset the system when requested
+            if (power_on_reset(0) = '1') then
+                system_reset <= '1';
+            else
+                system_reset <= '0';
+            end if;
+
+            -- shift 0s into the power_on_reset shift register from the MSB
+            power_on_reset <= '0' & power_on_reset(power_on_reset'length-1 downto 1);
+
+            -- During reset, latch the console select jumper. This is used to
+            -- optionally swap over the UART roles and move the system console to
+            -- the second serial port on the IO board.
+            if system_reset = '1' then
+                swap_uart01 <= console_select_sync;
+            else
+                swap_uart01 <= swap_uart01;
+            end if;
+        end if;
+    end process;
+
+    -- GPIO input signal routing
+    gpio_input <= coldboot & swap_uart01 & "000000";
+
+    -- GPIO output signal routing
+    leds(0) <= gpio_output(0);
+    leds(1) <= gpio_output(1);
+    leds(2) <= gpio_output(2);
+    leds(3) <= gpio_output(3);
+
+    -- User LED (LED1) on Papilio Pro indicates when the CPU is being asked to wait (eg by the SDRAM cache)
+    leds(4) <= cpu_wait;
+
+    -- Interrupt signal for the CPU
+    cpu_interrupt_in <= (timer_interrupt or uart0_interrupt or uart1_interrupt);
+
+    -- Z80 CPU core
+    cpu: entity work.Z80cpu
+    port map (
+                 reset => system_reset,
+                 clk => clk,
+                 clk_enable => cpu_clk_enable,
+                 m1_cycle => cpu_m1_cycle,
+                 interrupt => cpu_interrupt_in,
+                 nmi => '0',
+                 req_mem => cpu_req_mem,
+                 req_io => cpu_req_io,
+                 req_read => req_read,
+                 req_write => req_write,
+                 mem_wait => cpu_wait,
+                 address => virtual_address,
+                 data_in => cpu_data_in,
+                 data_out => cpu_data_out
+             );
+
+    -- Memory management unit
+    mmu: entity work.MMU
+    port map (
+                reset => system_reset,
+                clk => clk,
+                address_in => virtual_address,
+                address_out => physical_address,
+                cpu_data_in => cpu_data_out,
+                cpu_data_out => mmu_data_out,
+                req_mem_in => cpu_req_mem,
+                req_io_in  => cpu_req_io,
+                req_mem_out => req_mem,
+                req_io_out => req_io,
+                req_read => req_read,
+                req_write => req_write,
+                io_cs => mmu_cs,
+                cpu_wait => mmu_wait,
+                access_violated => open -- for now!!
+            );
+
+    -- This process determines which IO or memory device the CPU is addressing
+    -- and asserts the appropriate chip select signals.
+    cs_process: process(req_mem, req_io, physical_address, virtual_address, uartA_cs, uartB_cs, swap_uart01)
+    begin
+        -- memory chip selects: default to unselected
+        rom_cs   <= '0';
+        sram_cs  <= '0';
+        dram_cs  <= '0';
+
+        -- io chip selects: default to unselected
+        uartA_cs      <= '0';
+        uartB_cs      <= '0';
+        mmu_cs        <= '0';
+        timer_cs      <= '0';
+        spimaster0_cs <= '0';
+        spimaster1_cs <= '0';
+        clkscale_cs   <= '0';
+        gpio_cs       <= '0';
+
+        -- memory address decoding
+        -- address space is organised as:
+        -- 0x0 000 000 - 0x0 FFF FFF  16MB DRAM (cached)    (mapped to 8MB DRAM twice)
+        -- 0x1 000 000 - 0x1 FFF FFF  16MB DRAM (uncached)  (mapped to 8MB DRAM twice)
+        -- 0x2 000 000 - 0x2 000 FFF   4KB monitor ROM      (FPGA block RAM)
+        -- 0x2 001 000 - 0x2 001 FFF   4KB SRAM             (FPGA block RAM)
+        -- 0x2 002 000 - 0x3 FFF FFF  unused space for future expansion
+        if physical_address(25) = '0' then
+            -- bottom 32MB: DRAM handles this
+            dram_cs <= req_mem;
+        else
+            -- top 32MB: other memory devices
+            case physical_address(24 downto 12) is
+                when "0000000000000" =>  rom_cs <= req_mem;
+                when "0000000000001" => sram_cs <= req_mem;
+                when others => -- undecoded memory space
+            end case;
+        end if;
+
+        -- IO address decoding
+        case virtual_address(7 downto 3) is
+            when "00000" => uartA_cs            <= req_io;  -- 00 ... 07
+            when "00010" => timer_cs            <= req_io;  -- 10 ... 17
+            when "00011" => spimaster0_cs       <= req_io;  -- 18 ... 1F
+            when "00100" => gpio_cs             <= req_io;  -- 20 ... 27
+            when "00101" => uartB_cs            <= req_io;  -- 28 ... 2F
+            when "00110" => spimaster1_cs       <= req_io;  -- 30 ... 37
+                                                            -- unused ports
+            when "11110" => clkscale_cs         <= req_io;  -- F0 ... F7
+            when "11111" => mmu_cs              <= req_io;  -- F8 ... FF
+            when others =>
+        end case;
+
+        -- send the UART chip select to the appropriate UART depending
+        -- on whether they have been swapped over or not.
+        if swap_uart01 = '0' then
+            uart0_cs <= uartB_cs;
+            uart1_cs <= uartA_cs;
+        else
+            uart0_cs <= uartA_cs;
+            uart1_cs <= uartB_cs;
+        end if;
+    end process;
+
+    -- the selected memory device can request the CPU to wait
+    mem_wait <=
+       dram_wait       when dram_cs='1' else
+       spimaster0_wait when spimaster0_cs='1' else
+       spimaster1_wait when spimaster1_cs='1' else
+       '0';
+
+    -- the MMU can, at any time, request the CPU wait (this is used when 
+    -- translating IO to memory requests, to implement a wait state for 
+    -- the "17th page")
+    cpu_wait <= (mem_wait or mmu_wait);
+
+    -- input mux for CPU data bus
+    cpu_data_in <=
+       rom_data_out        when        rom_cs='1' else
+       dram_data_out       when       dram_cs='1' else
+       sram_data_out       when       sram_cs='1' else
+       uart0_data_out      when      uart0_cs='1' else
+       uart1_data_out      when      uart1_cs='1' else
+       timer_data_out      when      timer_cs='1' else
+       mmu_data_out        when        mmu_cs='1' else
+       spimaster0_data_out when spimaster0_cs='1' else
+       spimaster1_data_out when spimaster1_cs='1' else
+       clkscale_out        when   clkscale_cs='1' else
+       gpio_data_out       when       gpio_cs='1' else
+       rom_data_out; -- default case
+
+   dram: entity work.DRAM
+   generic map(
+               sdram_address_width => sdram_address_width,
+               sdram_column_bits   => sdram_column_bits,
+               sdram_startup_cycles=> sdram_startup_cycles,
+               cycles_per_refresh  => cycles_per_refresh
+              )
+   port map(
+               clk             => clk,
+               reset           => '0', -- important to note that we DO NOT reset the SDRAM controller on reset (it would stop refreshing, which would be bad)
+
+               -- interface to synthetic CPU
+               cs => dram_cs,
+               req_read => req_read,
+               req_write => req_write,
+               mem_address => physical_address(24 downto 0),
+               mem_wait => dram_wait,
+               data_in => cpu_data_out,
+               data_out => dram_data_out,
+               coldboot => coldboot,
+
+               -- interface to hardware SDRAM chip
+               SDRAM_CLK       => open,
+               SDRAM_CKE       => SDRAM_CKE,
+               SDRAM_CS        => SDRAM_CS,
+               SDRAM_nRAS      => SDRAM_nRAS,
+               SDRAM_nCAS      => SDRAM_nCAS,
+               SDRAM_nWE       => SDRAM_nWE,
+               SDRAM_DQM       => SDRAM_DQM,
+               SDRAM_BA        => SDRAM_BA,
+               SDRAM_ADDR      => SDRAM_ADDR,
+               SDRAM_DQ        => SDRAM_DQ
+           );
+
+   -- 4KB system ROM implemented in block RAM
+   rom: entity work.MonZ80
+   port map(
+               clk => clk,
+               A => physical_address(11 downto 0),
+               D => rom_data_out
+           );
+
+   -- 4KB SRAM memory implemented in block RAM
+   sram: entity work.SSRAM
+   generic map(
+                  AddrWidth => 12
+              )
+   port map(
+               clk => clk,
+               ce => sram_cs,
+               we => req_write,
+               A => physical_address(11 downto 0),
+               DIn => cpu_data_out,
+               DOut => sram_data_out
+           );
+
+   -- UART connected to FTDI USB UART
+   uart0: entity work.uart_interface
+   generic map ( watch_for_reset => 1, clk_frequency => (clk_freq_mhz * 1000000) )
+   port map(
+               clk => clk,
+               reset => system_reset,
+               reset_out => reset_request_uart, -- result of watching for reset sequence on the input
+               serial_in => serial_rx,
+               serial_out => serial_tx,
+               serial_rts => open,
+               serial_cts => '0',
+               cpu_address => virtual_address(2 downto 0),
+               cpu_data_in => cpu_data_out,
+               cpu_data_out => uart0_data_out,
+               enable => uart0_cs,
+               interrupt => uart0_interrupt,
+               req_read => req_read,
+               req_write => req_write
+           );
+
+   -- Timer device (internally scales the clock to 1MHz)
+   timer: entity work.timer
+   generic map ( clk_frequency => (clk_freq_mhz * 1000000) )
+   port map(
+               clk => clk,
+               reset => system_reset,
+               cpu_address => virtual_address(2 downto 0),
+               data_in => cpu_data_out,
+               data_out => timer_data_out,
+               enable => timer_cs,
+               req_read => req_read,
+               req_write => req_write,
+               interrupt => timer_interrupt
+           );
+
+   -- GPIO to FPGA pins and/or internal signals
+   gpio: entity work.gpio
+   port map(
+               clk => clk,
+               reset => system_reset,
+               cpu_address => virtual_address(2 downto 0),
+               data_in => cpu_data_out,
+               data_out => gpio_data_out,
+               enable => gpio_cs,
+               read_notwrite => req_read,
+               input_pins => gpio_input,
+               output_pins => gpio_output
+           );
+
+   -- An attempt to allow the CPU clock to be scaled back to run
+   -- at slower speeds without affecting the clock signal sent to
+   -- IO devices. Basically this was an attempt to make CP/M games 
+   -- playable :) Very limited success. Might be simpler to remove
+   -- this entirely.
+   clkscale: entity work.clkscale
+   port map (
+               clk => clk,
+               reset => system_reset,
+               cpu_address => virtual_address(2 downto 0),
+               data_in => cpu_data_out,
+               data_out => clkscale_out,
+               enable => clkscale_cs,
+               read_notwrite => req_read,
+               clk_enable => cpu_clk_enable
+           );
+
+   pll: entity work.pll
+   port map (
+               areset => open,
+               inclk0 => sys_clk_pad_i,
+               c0 => sdram_clk, -- 100 Mhz - 180 deg
+               c1 => clk, -- 100 Mhz
+               locked => open
+           );
+
+end Behavioral;

+ 466 - 0
vhdl/board/de2/top_level_de2.vhd

@@ -0,0 +1,466 @@
+--+-----------------------------------+-------------------------------------+--
+--|                      ___   ___    | (c) 2013-2014 William R Sowerbutts  |--
+--|   ___  ___   ___ ___( _ ) / _ \   | will@sowerbutts.com                 |--
+--|  / __|/ _ \ / __|_  / _ \| | | |  |                                     |--
+--|  \__ \ (_) | (__ / / (_) | |_| |  | A Z80 FPGA computer, just for fun   |--
+--|  |___/\___/ \___/___\___/ \___/   |                                     |--
+--|                                   |              http://sowerbutts.com/ |--
+--+-----------------------------------+-------------------------------------+--
+--| Top level module: connects modules to each other and the outside world  |--
+--+-------------------------------------------------------------------------+--
+--
+-- See README.txt for more details
+--
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+use work.T80_Pack.ALL;
+
+entity top_level is
+    Port ( sysclk_32m          : in    std_logic;
+           sys_clk_pad_i       : in    std_logic;
+           rst_n_pad_i         : in    std_logic;
+           leds                : out   std_logic_vector(4 downto 0);
+           reset_button        : in    std_logic;
+           console_select      : in    std_logic;
+
+           -- UART0 (to FTDI USB chip, no flow control)
+           serial_rx           : in    std_logic;
+           serial_tx           : out   std_logic;
+
+           -- UART0 (to MAX3232 level shifter chip, hardware flow control)
+           uart1_rx            : in    std_logic;
+           uart1_cts           : in    std_logic;
+           uart1_tx            : out   std_logic;
+           uart1_rts           : out   std_logic;
+
+           -- SPI flash chip
+           flash_spi_cs        : out   std_logic;
+           flash_spi_clk       : out   std_logic;
+           flash_spi_mosi      : out   std_logic;
+           flash_spi_miso      : in    std_logic;
+
+           -- SD card socket
+           sdcard_spi_cs       : out   std_logic;
+           sdcard_spi_clk      : out   std_logic;
+           sdcard_spi_mosi     : out   std_logic;
+           sdcard_spi_miso     : in    std_logic;
+
+           -- SDRAM chip
+           SDRAM_CLK           : out   std_logic;
+           SDRAM_CKE           : out   std_logic;
+           SDRAM_CS            : out   std_logic;
+           SDRAM_nRAS          : out   std_logic;
+           SDRAM_nCAS          : out   std_logic;
+           SDRAM_nWE           : out   std_logic;
+           SDRAM_DQM           : out   std_logic_vector( 1 downto 0);
+           SDRAM_ADDR          : out   std_logic_vector (12 downto 0);
+           SDRAM_BA            : out   std_logic_vector( 1 downto 0);
+           SDRAM_DQ            : inout std_logic_vector (15 downto 0)
+       );
+end top_level;
+
+architecture Behavioral of top_level is
+    constant clk_freq_mhz        : natural := 50; -- this is the frequency which the PLL outputs, in MHz.
+
+    -- SDRAM configuration
+    constant sdram_address_width : natural := 24;
+    constant sdram_column_bits   : natural := 9;
+    constant cycles_per_refresh  : natural := (64000*clk_freq_mhz)/8192-1;
+
+    -- For simulation, we don't need a long init stage. but for real DRAM we need approx 101us.
+    -- The constant below has a different value when interpreted by the synthesis and simulator 
+    -- tools in order to achieve the desired timing in each.
+    constant sdram_startup_cycles: natural := 101 * clk_freq_mhz
+    -- pragma translate_off
+    - 10000 -- reduce the value the simulator uses
+    -- pragma translate_on
+    ;
+
+    -- signals for clocking
+    signal clk_feedback         : std_logic;  -- PLL clock feedback
+    signal clk_unbuffered       : std_logic;  -- unbuffered system clock
+    signal clk                  : std_logic;  -- buffered system clock (all logic should be clocked by this)
+
+    -- console latch
+    signal console_select_clk1  : std_logic;
+    signal console_select_sync  : std_logic;
+    signal swap_uart01          : std_logic := '0';
+
+    -- system reset signals
+    signal power_on_reset       : std_logic_vector(1 downto 0) := (others => '1');
+    signal system_reset         : std_logic;
+    signal reset_button_clk1    : std_logic;
+    signal reset_button_sync    : std_logic; -- reset button signal, synchronised to our clock
+    signal reset_request_uart   : std_logic; -- reset request signal from FTDI UART (when you send "!~!~!~" to the UART, this line is asserted)
+
+    -- CPU control
+    signal coldboot             : std_logic;
+    signal cpu_clk_enable       : std_logic;
+    signal cpu_m1_cycle         : std_logic;
+    signal cpu_req_mem          : std_logic;
+    signal cpu_req_io           : std_logic;
+    signal req_mem              : std_logic;
+    signal req_io               : std_logic;
+    signal req_read             : std_logic;
+    signal req_write            : std_logic;
+    signal virtual_address      : std_logic_vector(15 downto 0);
+    signal physical_address     : std_logic_vector(25 downto 0);
+    signal mem_wait             : std_logic;
+    signal cpu_wait             : std_logic;
+    signal dram_wait            : std_logic;
+    signal mmu_wait             : std_logic;
+    signal spimaster0_wait      : std_logic;
+    signal spimaster1_wait      : std_logic;
+
+    -- chip selects
+    signal mmu_cs               : std_logic;
+    signal rom_cs               : std_logic;
+    signal sram_cs              : std_logic;
+    signal dram_cs              : std_logic;
+    signal uartA_cs             : std_logic;
+    signal uartB_cs             : std_logic;
+    signal uart0_cs             : std_logic;
+    signal uart1_cs             : std_logic;
+    signal timer_cs             : std_logic;
+    signal spimaster0_cs        : std_logic;
+    signal spimaster1_cs        : std_logic;
+    signal clkscale_cs          : std_logic;
+    signal gpio_cs              : std_logic;
+
+    -- data bus
+    signal cpu_data_in          : std_logic_vector(7 downto 0);
+    signal cpu_data_out         : std_logic_vector(7 downto 0);
+    signal rom_data_out         : std_logic_vector(7 downto 0);
+    signal sram_data_out        : std_logic_vector(7 downto 0);
+    signal dram_data_out        : std_logic_vector(7 downto 0);
+    signal uart0_data_out       : std_logic_vector(7 downto 0);
+    signal uart1_data_out       : std_logic_vector(7 downto 0);
+    signal timer_data_out       : std_logic_vector(7 downto 0);
+    signal spimaster0_data_out  : std_logic_vector(7 downto 0);
+    signal spimaster1_data_out  : std_logic_vector(7 downto 0);
+    signal mmu_data_out         : std_logic_vector(7 downto 0);
+    signal clkscale_out         : std_logic_vector(7 downto 0);
+    signal gpio_data_out        : std_logic_vector(7 downto 0);
+
+    -- GPIO
+    signal gpio_input           : std_logic_vector(7 downto 0);
+    signal gpio_output          : std_logic_vector(7 downto 0);
+
+    -- Interrupts
+    signal cpu_interrupt_in     : std_logic;
+    signal timer_interrupt      : std_logic;
+    signal uart0_interrupt      : std_logic;
+    signal uart1_interrupt      : std_logic;
+
+begin
+    -- Hold CPU reset high for 8 clock cycles on startup,
+    -- and when the user presses their reset button.
+    process(clk)
+    begin
+        if rising_edge(clk) then
+            -- Xilinx advises using two flip-flops are used to bring external
+            -- signals which feed control logic into our clock domain.
+            reset_button_clk1 <= reset_button;
+            reset_button_sync <= reset_button_clk1;
+            console_select_clk1 <= console_select;
+            console_select_sync <= console_select_clk1;
+
+            -- reset the system when requested
+            if (power_on_reset(0) = '1') then
+                system_reset <= '1';
+            else
+                system_reset <= '0';
+            end if;
+
+            -- shift 0s into the power_on_reset shift register from the MSB
+            power_on_reset <= '0' & power_on_reset(power_on_reset'length-1 downto 1);
+
+            -- During reset, latch the console select jumper. This is used to
+            -- optionally swap over the UART roles and move the system console to
+            -- the second serial port on the IO board.
+            if system_reset = '1' then
+                swap_uart01 <= console_select_sync;
+            else
+                swap_uart01 <= swap_uart01;
+            end if;
+        end if;
+    end process;
+
+    -- GPIO input signal routing
+    gpio_input <= coldboot & swap_uart01 & "000000";
+
+    -- GPIO output signal routing
+    leds(0) <= gpio_output(0);
+    leds(1) <= gpio_output(1);
+    leds(2) <= gpio_output(2);
+    leds(3) <= gpio_output(3);
+
+    -- User LED (LED1) on Papilio Pro indicates when the CPU is being asked to wait (eg by the SDRAM cache)
+    leds(4) <= cpu_wait;
+
+    -- Interrupt signal for the CPU
+    cpu_interrupt_in <= (timer_interrupt or uart0_interrupt or uart1_interrupt);
+
+    -- Z80 CPU core
+    cpu: entity work.Z80cpu
+    port map (
+                 reset => system_reset,
+                 clk => clk,
+                 clk_enable => cpu_clk_enable,
+                 m1_cycle => cpu_m1_cycle,
+                 interrupt => cpu_interrupt_in,
+                 nmi => '0',
+                 req_mem => cpu_req_mem,
+                 req_io => cpu_req_io,
+                 req_read => req_read,
+                 req_write => req_write,
+                 mem_wait => cpu_wait,
+                 address => virtual_address,
+                 data_in => cpu_data_in,
+                 data_out => cpu_data_out
+             );
+
+    -- Memory management unit
+    mmu: entity work.MMU
+    port map (
+                reset => system_reset,
+                clk => clk,
+                address_in => virtual_address,
+                address_out => physical_address,
+                cpu_data_in => cpu_data_out,
+                cpu_data_out => mmu_data_out,
+                req_mem_in => cpu_req_mem,
+                req_io_in  => cpu_req_io,
+                req_mem_out => req_mem,
+                req_io_out => req_io,
+                req_read => req_read,
+                req_write => req_write,
+                io_cs => mmu_cs,
+                cpu_wait => mmu_wait,
+                access_violated => open -- for now!!
+            );
+
+    -- This process determines which IO or memory device the CPU is addressing
+    -- and asserts the appropriate chip select signals.
+    cs_process: process(req_mem, req_io, physical_address, virtual_address, uartA_cs, uartB_cs, swap_uart01)
+    begin
+        -- memory chip selects: default to unselected
+        rom_cs   <= '0';
+        sram_cs  <= '0';
+        dram_cs  <= '0';
+
+        -- io chip selects: default to unselected
+        uartA_cs      <= '0';
+        uartB_cs      <= '0';
+        mmu_cs        <= '0';
+        timer_cs      <= '0';
+        spimaster0_cs <= '0';
+        spimaster1_cs <= '0';
+        clkscale_cs   <= '0';
+        gpio_cs       <= '0';
+
+        -- memory address decoding
+        -- address space is organised as:
+        -- 0x0 000 000 - 0x0 FFF FFF  16MB DRAM (cached)    (mapped to 8MB DRAM twice)
+        -- 0x1 000 000 - 0x1 FFF FFF  16MB DRAM (uncached)  (mapped to 8MB DRAM twice)
+        -- 0x2 000 000 - 0x2 000 FFF   4KB monitor ROM      (FPGA block RAM)
+        -- 0x2 001 000 - 0x2 001 FFF   4KB SRAM             (FPGA block RAM)
+        -- 0x2 002 000 - 0x3 FFF FFF  unused space for future expansion
+        if physical_address(25) = '0' then
+            -- bottom 32MB: DRAM handles this
+            dram_cs <= req_mem;
+        else
+            -- top 32MB: other memory devices
+            case physical_address(24 downto 12) is
+                when "0000000000000" =>  rom_cs <= req_mem;
+                when "0000000000001" => sram_cs <= req_mem;
+                when others => -- undecoded memory space
+            end case;
+        end if;
+
+        -- IO address decoding
+        case virtual_address(7 downto 3) is
+            when "00000" => uartA_cs            <= req_io;  -- 00 ... 07
+            when "00010" => timer_cs            <= req_io;  -- 10 ... 17
+            when "00011" => spimaster0_cs       <= req_io;  -- 18 ... 1F
+            when "00100" => gpio_cs             <= req_io;  -- 20 ... 27
+            when "00101" => uartB_cs            <= req_io;  -- 28 ... 2F
+            when "00110" => spimaster1_cs       <= req_io;  -- 30 ... 37
+                                                            -- unused ports
+            when "11110" => clkscale_cs         <= req_io;  -- F0 ... F7
+            when "11111" => mmu_cs              <= req_io;  -- F8 ... FF
+            when others =>
+        end case;
+
+        -- send the UART chip select to the appropriate UART depending
+        -- on whether they have been swapped over or not.
+        if swap_uart01 = '0' then
+            uart0_cs <= uartB_cs;
+            uart1_cs <= uartA_cs;
+        else
+            uart0_cs <= uartA_cs;
+            uart1_cs <= uartB_cs;
+        end if;
+    end process;
+
+    -- the selected memory device can request the CPU to wait
+    mem_wait <=
+       dram_wait       when dram_cs='1' else
+       spimaster0_wait when spimaster0_cs='1' else
+       spimaster1_wait when spimaster1_cs='1' else
+       '0';
+
+    -- the MMU can, at any time, request the CPU wait (this is used when 
+    -- translating IO to memory requests, to implement a wait state for 
+    -- the "17th page")
+    cpu_wait <= (mem_wait or mmu_wait);
+
+    -- input mux for CPU data bus
+    cpu_data_in <=
+       rom_data_out        when        rom_cs='1' else
+       dram_data_out       when       dram_cs='1' else
+       sram_data_out       when       sram_cs='1' else
+       uart0_data_out      when      uart0_cs='1' else
+       uart1_data_out      when      uart1_cs='1' else
+       timer_data_out      when      timer_cs='1' else
+       mmu_data_out        when        mmu_cs='1' else
+       spimaster0_data_out when spimaster0_cs='1' else
+       spimaster1_data_out when spimaster1_cs='1' else
+       clkscale_out        when   clkscale_cs='1' else
+       gpio_data_out       when       gpio_cs='1' else
+       rom_data_out; -- default case
+
+   dram: entity work.DRAM
+   generic map(
+               sdram_address_width => sdram_address_width,
+               sdram_column_bits   => sdram_column_bits,
+               sdram_startup_cycles=> sdram_startup_cycles,
+               cycles_per_refresh  => cycles_per_refresh
+              )
+   port map(
+               clk             => clk,
+               reset           => '0', -- important to note that we DO NOT reset the SDRAM controller on reset (it would stop refreshing, which would be bad)
+
+               -- interface to synthetic CPU
+               cs => dram_cs,
+               req_read => req_read,
+               req_write => req_write,
+               mem_address => physical_address(24 downto 0),
+               mem_wait => dram_wait,
+               data_in => cpu_data_out,
+               data_out => dram_data_out,
+               coldboot => coldboot,
+
+               -- interface to hardware SDRAM chip
+               SDRAM_CLK       => open,
+               SDRAM_CKE       => SDRAM_CKE,
+               SDRAM_CS        => SDRAM_CS,
+               SDRAM_nRAS      => SDRAM_nRAS,
+               SDRAM_nCAS      => SDRAM_nCAS,
+               SDRAM_nWE       => SDRAM_nWE,
+               SDRAM_DQM       => SDRAM_DQM,
+               SDRAM_BA        => SDRAM_BA,
+               SDRAM_ADDR      => SDRAM_ADDR,
+               SDRAM_DQ        => SDRAM_DQ
+           );
+
+   -- 4KB system ROM implemented in block RAM
+   rom: entity work.MonZ80
+   port map(
+               clk => clk,
+               A => physical_address(11 downto 0),
+               D => rom_data_out
+           );
+
+   -- 4KB SRAM memory implemented in block RAM
+   sram: entity work.SSRAM
+   generic map(
+                  AddrWidth => 12
+              )
+   port map(
+               clk => clk,
+               ce => sram_cs,
+               we => req_write,
+               A => physical_address(11 downto 0),
+               DIn => cpu_data_out,
+               DOut => sram_data_out
+           );
+
+   -- UART connected to FTDI USB UART
+   uart0: entity work.uart_interface
+   generic map ( watch_for_reset => 1, clk_frequency => (clk_freq_mhz * 1000000) )
+   port map(
+               clk => clk,
+               reset => system_reset,
+               reset_out => reset_request_uart, -- result of watching for reset sequence on the input
+               serial_in => serial_rx,
+               serial_out => serial_tx,
+               serial_rts => open,
+               serial_cts => '0',
+               cpu_address => virtual_address(2 downto 0),
+               cpu_data_in => cpu_data_out,
+               cpu_data_out => uart0_data_out,
+               enable => uart0_cs,
+               interrupt => uart0_interrupt,
+               req_read => req_read,
+               req_write => req_write
+           );
+
+   -- Timer device (internally scales the clock to 1MHz)
+   timer: entity work.timer
+   generic map ( clk_frequency => (clk_freq_mhz * 1000000) )
+   port map(
+               clk => clk,
+               reset => system_reset,
+               cpu_address => virtual_address(2 downto 0),
+               data_in => cpu_data_out,
+               data_out => timer_data_out,
+               enable => timer_cs,
+               req_read => req_read,
+               req_write => req_write,
+               interrupt => timer_interrupt
+           );
+
+   -- GPIO to FPGA pins and/or internal signals
+   gpio: entity work.gpio
+   port map(
+               clk => clk,
+               reset => system_reset,
+               cpu_address => virtual_address(2 downto 0),
+               data_in => cpu_data_out,
+               data_out => gpio_data_out,
+               enable => gpio_cs,
+               read_notwrite => req_read,
+               input_pins => gpio_input,
+               output_pins => gpio_output
+           );
+
+   -- An attempt to allow the CPU clock to be scaled back to run
+   -- at slower speeds without affecting the clock signal sent to
+   -- IO devices. Basically this was an attempt to make CP/M games 
+   -- playable :) Very limited success. Might be simpler to remove
+   -- this entirely.
+   clkscale: entity work.clkscale
+   port map (
+               clk => clk,
+               reset => system_reset,
+               cpu_address => virtual_address(2 downto 0),
+               data_in => cpu_data_out,
+               data_out => clkscale_out,
+               enable => clkscale_cs,
+               read_notwrite => req_read,
+               clk_enable => cpu_clk_enable
+           );
+
+   pll: entity work.pll
+   port map (
+               areset => open,
+               inclk0 => sys_clk_pad_i,
+               c0 => sdram_clk, -- 100 Mhz - 180 deg
+               c1 => clk, -- 100 Mhz
+               locked => open
+           );
+
+end Behavioral;