123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- --+-----------------------------------+-------------------------------------+--
- --| ___ ___ | (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;
|