commit 8da567e4b611f2b2e6567e84b56dcc3e3dd7aa45 Author: Sergio L. Pascual Date: Sat Sep 6 02:01:39 2014 +0200 Initial commit. diff --git a/vhdl/DRAM.vhd b/vhdl/DRAM.vhd new file mode 100644 index 0000000..df1fdc6 --- /dev/null +++ b/vhdl/DRAM.vhd @@ -0,0 +1,395 @@ +--+-----------------------------------+-------------------------------------+-- +--| ___ ___ | (c) 2013-2014 William R Sowerbutts |-- +--| ___ ___ ___ ___( _ ) / _ \ | will@sowerbutts.com |-- +--| / __|/ _ \ / __|_ / _ \| | | | | |-- +--| \__ \ (_) | (__ / / (_) | |_| | | A Z80 FPGA computer, just for fun |-- +--| |___/\___/ \___/___\___/ \___/ | |-- +--| | http://sowerbutts.com/ |-- +--+-----------------------------------+-------------------------------------+-- +--| DRAM interface: Connect the CPU to the SDRAM on the Papilio Pro board. |-- +--| The SDRAM takes about 10 cycles to respond with data after making a |-- +--| request, so this module includes a direct-mapped cache to store |-- +--| recently read or written data in order to hide this latency. |-- +--+-------------------------------------------------------------------------+-- +-- +-- The Papilio Pro board has an 8MB SDRAM chip on the board. The socz80 MMU +-- provides a 64MB (26-bit) phyiscal address space. The low 32MB of address space +-- is allocated to the DRAM (the top 32MB being used for other memory devices). +-- +-- The low 32MB is divided into two 16MB blocks. Accesses to the first block +-- (starting at 0MB) go through the cache, while accesses to the second +-- block (starting at 16MB) bypass the cache. There is only 8MB SDRAM on the +-- Papilio Pro so it is aliased twice in each block, ie it appears at 0MB, +-- 8MB, 16MB and 24MB. +-- +-- The cache is direct mapped, ie the low bits of the address dictate which cache +-- line to use and which byte within that line. When a cache line is written to +-- the top bits of the address are stored in "cache tag" memory. When a cache line +-- is read the top bits of the address are compared to the stored tag to determine +-- if the cached data relates to the same address. +-- +-- Each cache line consists of a 45 bits: +-- 32 bits of cached data +-- 4 validity bits to indicate if the cached data is valid or not +-- 9 bits of address tag to indicate the top address bits of the +-- +-- bit number (read these two 22222211111111110000000000 +-- lines top to bottom) 54321098765432109876543210 +-- +-- CPU address is 16 bits wide: PPPPOOOOOOOOOOOO (4 bit page, 12 bit offset) +-- physical address is 26 bits wide: FFFFFFFFFFFFFFOOOOOOOOOOOO (14 bit frame, 12 bit offset) +-- DRAM address is 25 bits wide: CIFFFFFFFFFFFOOOOOOOOOOOO (1 bit cache flag, 1 bit ignored, 11 bit frame, 12 bit offset) +-- cached address is 23 bits wide: TTTTTTTTTLLLLLLLLLLLLBB (9 bit cache line tag, 12 bit cache line, 2 bit byte offset) +-- +-- cache lines use 4096 x 36 bit BRAM +-- cache tags use 4096 x 9 bit BRAM + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +entity DRAM is + generic( + sdram_address_width : natural; + sdram_column_bits : natural; + sdram_startup_cycles: natural; + cycles_per_refresh : natural + ); + port( + -- interface to the system + clk : in std_logic; + reset : in std_logic; + cs : in std_logic; + req_read : in std_logic; + req_write : in std_logic; + mem_address : in std_logic_vector(24 downto 0); + data_in : in std_logic_vector(7 downto 0); + data_out : out std_logic_vector(7 downto 0) := (others => '0'); + mem_wait : out std_logic; + coldboot : out std_logic; -- this signals 1 until the SDRAM has been initialised + + -- interface to hardware 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 DRAM; + +architecture behaviour of DRAM is + -- sdram controller interface + signal cmd_address : std_logic_vector(sdram_address_width-2 downto 0) := (others => '0'); + signal cmd_wr : std_logic := '1'; + signal cmd_enable : std_logic; + signal cmd_byte_enable : std_logic_vector(3 downto 0); + signal cmd_data_in : std_logic_vector(31 downto 0); + signal cmd_ready : std_logic; + signal sdram_data_out : std_logic_vector(31 downto 0); + signal sdram_data_out_ready : std_logic; + signal seen_ready : std_logic := '0'; + signal last_address_word : std_logic_vector(20 downto 0); + + -- internal signals + signal current_word : std_logic_vector(31 downto 0); -- value of current cache line + signal current_byte_valid : std_logic_vector(3 downto 0); -- validity bits for current cache line + signal word_changed : std_logic; -- did the address bus value change? + signal cache_hit : std_logic; + signal address_hit : std_logic; + signal byte_valid_hit : std_logic; + signal write_back : std_logic; + + -- state machine + type controller_state is ( st_idle, -- waiting for command + st_read, -- cache miss: issued read command to controller, waiting for data to arrive + st_read_done, -- cache hit/completed miss: data arrived from controller, waiting for CPU to de-assert + st_write); -- write: issued write command, waiting for CPU to de-assert + signal current_state : controller_state; + signal next_state : controller_state; + + -- here's the cache memory signals + signal cache_line_memory_write_enable : std_logic; + signal cache_line_memory_data_in : std_logic_vector(35 downto 0); -- 35 downto 32: validity bits; 31 downto 0: four data bytes + signal cache_line_memory_data_out : std_logic_vector(35 downto 0); + signal cache_tag_memory_write_enable : std_logic; + signal cache_tag_memory_data_in : std_logic_vector(8 downto 0); + signal cache_tag_memory_data_out : std_logic_vector(8 downto 0); + + -- break up the incoming physical address + alias address_byte : std_logic_vector(1 downto 0) is mem_address(1 downto 0); + alias address_line : std_logic_vector(11 downto 0) is mem_address(13 downto 2); + alias address_tag : std_logic_vector(8 downto 0) is mem_address(22 downto 14); + alias address_word : std_logic_vector(20 downto 0) is mem_address(22 downto 2); + -- mem_address(23) is unused in this design + alias cache_inhibit : std_logic is mem_address(24); + +begin + + -- this should be based on the generic, really + cmd_address <= mem_address(22 downto 2); -- address_tag & address_line + cmd_data_in <= data_in & data_in & data_in & data_in; -- write the same data four times + cmd_wr <= req_write; + cache_tag_memory_data_in <= address_tag; + coldboot <= not seen_ready; + + compute_next_state: process(req_read, req_write, current_state, cache_hit, cmd_ready, cs, sdram_data_out_ready, word_changed) + begin + cmd_enable <= '0'; + mem_wait <= '0'; + write_back <= '0'; + case current_state is + when st_idle => + if cs = '1' and cmd_ready = '1' then + if req_read = '1' then + -- we can't process a read immediately if the address input just changed; delay them for one cycle. + if word_changed = '1' then + mem_wait <= '1'; + next_state <= st_idle; + -- come back next cycle! + else + if cache_hit = '1' then + mem_wait <= '0'; + next_state <= st_read_done; + else + cmd_enable <= '1'; + mem_wait <= '1'; + next_state <= st_read; + end if; + end if; + elsif req_write = '1' then + if word_changed = '1' then + mem_wait <= '1'; + next_state <= st_idle; + -- come back next cycle! + else + next_state <= st_write; + cmd_enable <= '1'; + mem_wait <= '0'; -- no need to wait, the SDRAM controller will latch all the inputs + write_back <= '1'; + end if; + else + next_state <= st_idle; + mem_wait <= '0'; -- we know cmd_ready='1' + end if; + else + next_state <= st_idle; + mem_wait <= (not cmd_ready); + end if; + when st_read => + if cs = '1' and req_read = '1' then + if sdram_data_out_ready = '1' then + next_state <= st_read_done; + else + next_state <= st_read; + end if; + else + -- this kind of implies that they gave up on us? + next_state <= st_idle; + end if; + mem_wait <= (not sdram_data_out_ready) and (not cache_hit); + when st_read_done => + if cs = '1' and req_read = '1' then + next_state <= st_read_done; + else + next_state <= st_idle; + end if; + mem_wait <= (not sdram_data_out_ready) and (not cache_hit); + when st_write => + if cs = '1' and req_write = '1' then + next_state <= st_write; + else + next_state <= st_idle; + end if; + mem_wait <= (not cmd_ready); -- no need to wait once the write has been committed + end case; + end process; + + word_changed_check: process(last_address_word, address_word) + begin + if address_word = last_address_word then + word_changed <= '0'; + else + word_changed <= '1'; + end if; + end process; + + cache_address_check: process(cache_tag_memory_data_out, cache_line_memory_data_out, address_tag) + begin + if cache_tag_memory_data_out = address_tag then + address_hit <= '1'; + current_byte_valid <= cache_line_memory_data_out(35 downto 32); + else + address_hit <= '0'; + current_byte_valid <= "0000"; + end if; + end process; + + cache_byte_valid_check: process(address_byte, current_byte_valid) + begin + case address_byte is + when "00" => byte_valid_hit <= current_byte_valid(0); + when "01" => byte_valid_hit <= current_byte_valid(1); + when "10" => byte_valid_hit <= current_byte_valid(2); + when "11" => byte_valid_hit <= current_byte_valid(3); + when others => byte_valid_hit <= '0'; + end case; + end process; + + cache_hit_check: process(byte_valid_hit, address_hit, cache_inhibit) + begin + if address_hit = '1' and byte_valid_hit = '1' and cache_inhibit = '0' then + cache_hit <= '1'; + else + cache_hit <= '0'; + end if; + end process; + + byte_enable_decode: process(address_byte) + begin + case address_byte is + when "00" => cmd_byte_enable <= "0001"; + when "01" => cmd_byte_enable <= "0010"; + when "10" => cmd_byte_enable <= "0100"; + when "11" => cmd_byte_enable <= "1000"; + when others => cmd_byte_enable <= "1000"; + end case; + end process; + + data_out_demux: process(address_byte, sdram_data_out_ready, sdram_data_out, cache_line_memory_data_out, current_word) + begin + -- when the SDRAM is presenting data, feed it direct to the CPU. + -- otherwise feed data from our cache memory. + if sdram_data_out_ready = '1' then + current_word <= sdram_data_out; + else + current_word <= cache_line_memory_data_out(31 downto 0); + end if; + + case address_byte is + when "00" => data_out <= current_word( 7 downto 0); + when "01" => data_out <= current_word(15 downto 8); + when "10" => data_out <= current_word(23 downto 16); + when "11" => data_out <= current_word(31 downto 24); + when others => data_out <= current_word(31 downto 24); + end case; + end process; + + cache_write: process(current_state, data_in, next_state, write_back, cache_line_memory_data_out, sdram_data_out, sdram_data_out_ready, address_byte, current_byte_valid) + begin + if (next_state = st_read) or (write_back = '1') then + cache_tag_memory_write_enable <= '1'; + else + cache_tag_memory_write_enable <= '0'; + end if; + + cache_line_memory_write_enable <= '0'; + cache_line_memory_data_in <= cache_line_memory_data_out; + + if next_state = st_read then + cache_line_memory_data_in <= (others => '0'); -- set word and all valid flags to 1 + cache_line_memory_write_enable <= '1'; + end if; + + -- has our read completed? + if current_state = st_read then + if sdram_data_out_ready = '1' then + cache_line_memory_data_in <= "1111" & sdram_data_out; + cache_line_memory_write_enable <= '1'; + end if; + elsif write_back = '1' then + case address_byte is + when "00" => + cache_line_memory_data_in <= current_byte_valid(3 downto 1) & "1" & + cache_line_memory_data_out(31 downto 8) & data_in; + when "01" => + cache_line_memory_data_in <= + current_byte_valid(3 downto 2) & "1" & current_byte_valid(0) & + cache_line_memory_data_out(31 downto 16) & data_in & cache_line_memory_data_out(7 downto 0); + when "10" => + cache_line_memory_data_in <= + current_byte_valid(3) & "1" & current_byte_valid(1 downto 0) & + cache_line_memory_data_out(31 downto 24) & data_in & cache_line_memory_data_out(15 downto 0); + when "11" => + cache_line_memory_data_in <= + "1" & current_byte_valid(2 downto 0) & + data_in & cache_line_memory_data_out(23 downto 0); + when others => -- shut up, compiler! + end case; + cache_line_memory_write_enable <= '1'; + end if; + end process; + + sdram_registers: process(clk) + begin + if rising_edge(clk) then + -- state register + current_state <= next_state; + -- coldboot detection + seen_ready <= seen_ready or cmd_ready; + -- track memory address + last_address_word <= address_word; + end if; + end process; + + -- underlying SDRAM controller (thanks, Hamsterworks!) + sdram_ctrl: entity work.SDRAM_Controller + 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 => reset, + + cmd_address => cmd_address, + cmd_wr => cmd_wr, + cmd_enable => cmd_enable, + cmd_ready => cmd_ready, + cmd_byte_enable => cmd_byte_enable, + cmd_data_in => cmd_data_in, + + data_out => sdram_data_out, + data_out_ready => sdram_data_out_ready, + + SDRAM_CLK => SDRAM_CLK, + SDRAM_CKE => SDRAM_CKE, + SDRAM_CS => SDRAM_CS, + SDRAM_RAS => SDRAM_nRAS, + SDRAM_CAS => SDRAM_nCAS, + SDRAM_WE => SDRAM_nWE, + SDRAM_DQM => SDRAM_DQM, + SDRAM_BA => SDRAM_BA, + SDRAM_ADDR => SDRAM_ADDR, + SDRAM_DATA => SDRAM_DQ + ); + + -- block RAM used to store cache line data and byte validity (packs nicely into 36 bits) + cacheline_memory_sram: entity work.RAM4K36 + port map ( + clk => clk, + reset => reset, + write => cache_line_memory_write_enable, + address => address_line, + data_in => cache_line_memory_data_in, + data_out => cache_line_memory_data_out + + ); + + -- block RAM used to store cache line tag memory (packs nicely into 9 bits) + cachetag_memory_sram: entity work.RAM4K9 + port map ( + clk => clk, + reset => reset, + write => cache_tag_memory_write_enable, + address => address_line, + data_in => cache_tag_memory_data_in, + data_out => cache_tag_memory_data_out + ); +end; diff --git a/vhdl/MMU.vhd b/vhdl/MMU.vhd new file mode 100644 index 0000000..7727741 --- /dev/null +++ b/vhdl/MMU.vhd @@ -0,0 +1,319 @@ +--+-----------------------------------+-------------------------------------+-- +--| ___ ___ | (c) 2013-2014 William R Sowerbutts |-- +--| ___ ___ ___ ___( _ ) / _ \ | will@sowerbutts.com |-- +--| / __|/ _ \ / __|_ / _ \| | | | | |-- +--| \__ \ (_) | (__ / / (_) | |_| | | A Z80 FPGA computer, just for fun |-- +--| |___/\___/ \___/___\___/ \___/ | |-- +--| | http://sowerbutts.com/ |-- +--+-----------------------------------+-------------------------------------+-- +--| 4K paged Memory Management Unit: Translates 16-bit virtual addresses |-- +--| from the CPU into 26-bit physical addresses to allow more memory to be |-- +--| addressed. Also has a hack to allow unmapped physical memory to be |-- +--| accessed through an IO port which synthesises memory operations. |-- +--+-------------------------------------------------------------------------+-- +-- +-- The MMU takes a 16-bit virtual address from the CPU and divides it into a +-- 4-bit frame number and a 12-bit offset. The frame number is used as an index +-- into an array of sixteen registers which contain the hardware page numbers +-- (the translation table). The physical address is then formed from the +-- hardware page number concatenated with the 12-bit offset. My hardware page +-- numbers are 14-bits long because I wanted a 64MB physical address space but +-- you could use any length you wished. +-- +-- So if the virtual address 0xABCD is accessed, we'd divide that into frame +-- number 0xA (decimal 10), offset 0xBCD. If the 10th MMU translation register +-- contains 0x1234 then the translated physical address would be 0x1234BCD. +-- +-- My MMU is programmed using 8 I/O ports in the range 0xF8 through 0xFF. The +-- chip select line is asserted for any access in that range and the MMU +-- decodes the low three bits to determine which register is being accessed. +-- +-- The port at 0xF8 is effectively a mux which selects the function of ports +-- 0xFB through 0xFF. +-- +-- Writing 0x00 through 0x0F to the function register at 0xF8 allows you to +-- read/write one of the 16 MMU translation registers. With these selected; +-- - 0xFC contains the high byte of the physical address +-- - 0xFD contains the low byte of the physical address +-- - 0xFB contains permission bits (read/write/execute, currently ignored) +-- +-- So updating a mapping generally requires just three I/O writes: One to 0xF8 +-- to select which frame to modify, and one each to 0xFC and 0xFD to write out +-- the new translation. The permission bits are programmable but currently +-- ignored (I had planned to add some level of memory protection to UZI one day) +-- +-- The "17th page" is a bit of a hack bolted on. The lazy programmer in me finds +-- it much more convenient to sometimes access memory without remapping a frame; +-- in particular you don't have to select which frame to remap such as to avoid +-- remapping the memory pointed to by PC, SP or your source/target pointer. +-- +-- Writing 0xFF to port 0xF8 selects the "17th page pointer". This is a 26-bit +-- register but again could be wider/narrower as required. With this selected +-- ports 0xFC, 0xFD, 0xFE, 0xFF are the register contents (with the high byte in +-- 0xFC, low byte in 0xFF). +-- +-- When the CPU reads or writes the I/O port 0xFA the MMU translates the I/O +-- operation into a memory operation. The physical memory address accessed is +-- the address contained in the 17th page pointer register. After the memory +-- operation completes the 17th page pointer register is incremented so that +-- repeated accesses to 0xFA walk forward through memory. +-- +-- +-- MMU registers: +-- +-- base = 0xF8 in standard socz80 system +-- +-- base+0 mux frame select (write 00..0F to select frame, FF to select "17th page" pointer) +-- base+1 (unused) +-- base+2 17th page (read/write will address pointed memory and post-increment the pointer) +-- base+3 page permissions +-- base+4 page address (high byte) / ptr address (high byte) +-- base+5 page address (low byte) / ptr address +-- base+6 ptr address +-- base+7 ptr address (low byte) +-- +-- Basic operation of the MMU (older documentation, concise but still correct) +-- For memory requests: +-- The top 4 bits of CPU logical address are replaced with 14 bits taken +-- from the MMU translation table entry indexed by those top four bits. +-- For IO requests: +-- Reads or writes to I/O port at (base+2) are converted into memory +-- requests to the address pointed to by "the 17th page", a pointer which +-- can be accessed by writing 0xFF to the frame select register (base+0) +-- and then programming a 26-bit address into I/O ports FC FD FE FF. Each +-- I/O request to port F/A results in the pointer being post-incremented +-- by 1, which means that INIR our OUTIR instructions can read/write blocks +-- of memory outside of the CPU logical address space through this port. +-- +-- Note that the MMU has to insert a forced CPU wait state to give the +-- addressed memory device time to read the new address off the bus. +-- + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +entity MMU is + port( + clk : in std_logic; + reset : in std_logic; + address_in : in std_logic_vector(15 downto 0); + address_out : out std_logic_vector(25 downto 0); + cpu_data_in : in std_logic_vector(7 downto 0); + cpu_data_out : out std_logic_vector(7 downto 0); + cpu_wait : out std_logic; + req_mem_in : in std_logic; + req_mem_out : out std_logic; + req_io_in : in std_logic; + req_io_out : out std_logic; + io_cs : in std_logic; + req_read : in std_logic; + req_write : in std_logic; + access_violated : out std_logic + ); +end MMU; + +architecture behaviour of MMU is + -- each MMU entry looks like this + type mmu_entry_type is + record + frame: std_logic_vector(13 downto 0); + can_read: std_logic; + can_write: std_logic; + end record; + + -- the whole MMU state looks like this + type mmu_entry_array is array(natural range <>) of mmu_entry_type; + + -- and here's our instance + signal mmu_entry : mmu_entry_array(0 to 15); + + -- 17th page pointer (frame FF) + signal mmu_frame_ff_pointer : std_logic_vector(25 downto 0); + + -- IO interface + signal cpu_entry_select : std_logic_vector(7 downto 0); + + -- break up the incoming virtual address + alias frame_number : std_logic_vector( 3 downto 0) is address_in(15 downto 12); + alias page_offset : std_logic_vector(11 downto 0) is address_in(11 downto 0); + + signal map_io_to_ff_ptr : std_logic; + signal was_map_io_to_ff_ptr : std_logic := '0'; +begin + + map_io_to_mem_proc: process(address_in, req_mem_in, req_io_in) + begin + if req_mem_in = '0' and req_io_in = '1' and address_in(7 downto 0) = "11111010" then + map_io_to_ff_ptr <= '1'; + else + map_io_to_ff_ptr <= '0'; + end if; + end process; + + with map_io_to_ff_ptr select + address_out <= + mmu_entry(to_integer(unsigned(frame_number))).frame & page_offset when '0', + mmu_frame_ff_pointer when '1'; + + with map_io_to_ff_ptr select + req_mem_out <= + req_mem_in when '0', + '1' when '1'; + + with map_io_to_ff_ptr select + req_io_out <= + req_io_in when '0', + '0' when '1'; + + with map_io_to_ff_ptr select + access_violated <= + req_mem_in and ((req_read and not mmu_entry(to_integer(unsigned(frame_number))).can_read) or + (req_write and not mmu_entry(to_integer(unsigned(frame_number))).can_write)) when '0', + '0' when '1'; + + -- force CPU to wait one cycle when we map IO to memory access; this + -- is in order to give our synchronous memories a cycle to read the + -- address, look up the data in synchronous memory, and provide a result. + cpu_wait <= map_io_to_ff_ptr and (not was_map_io_to_ff_ptr); + + data_out: process(address_in, cpu_entry_select, mmu_entry, mmu_frame_ff_pointer) + begin + if cpu_entry_select = "11111111" then + -- pointer (FF) + case address_in(2 downto 0) is + when "000" => + cpu_data_out <= cpu_entry_select; + when "011" => + cpu_data_out <= "00000011"; + when "100" => + cpu_data_out <= "000000" & mmu_frame_ff_pointer(25 downto 24); + when "101" => + cpu_data_out <= mmu_frame_ff_pointer(23 downto 16); + when "110" => + cpu_data_out <= mmu_frame_ff_pointer(15 downto 8); + when "111" => + cpu_data_out <= mmu_frame_ff_pointer(7 downto 0); + when others => + cpu_data_out <= "00000000"; + end case; + else + -- 16 frames (00 .. 0F) + case address_in(2 downto 0) is + when "000" => + cpu_data_out <= cpu_entry_select; + when "011" => + cpu_data_out <= "000000" & mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).can_write & mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).can_read; + when "100" => + cpu_data_out <= "00" & mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).frame(13 downto 8); + when "101" => + cpu_data_out <= mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).frame(7 downto 0); + when others => + cpu_data_out <= "00000000"; + end case; + end if; + end process; + + mmu_registers: process(clk) + begin + if rising_edge(clk) then + if reset = '1' then + mmu_entry( 0).frame <= "10000000000000"; -- first page of SRAM (monitor ROM) + mmu_entry( 1).frame <= "00000000000001"; -- DRAM page 1 + mmu_entry( 2).frame <= "00000000000010"; -- DRAM page 2 + mmu_entry( 3).frame <= "00000000000011"; -- DRAM page 3 + mmu_entry( 4).frame <= "00000000000100"; -- DRAM page 4 + mmu_entry( 5).frame <= "00000000000101"; -- DRAM page 5 + mmu_entry( 6).frame <= "00000000000110"; -- DRAM page 6 + mmu_entry( 7).frame <= "00000000000111"; -- DRAM page 7 + mmu_entry( 8).frame <= "00000000001000"; -- DRAM page 8 + mmu_entry( 9).frame <= "00000000001001"; -- DRAM page 9 + mmu_entry(10).frame <= "00000000001010"; -- DRAM page 10 + mmu_entry(11).frame <= "00000000001011"; -- DRAM page 11 + mmu_entry(12).frame <= "00000000001100"; -- DRAM page 12 + mmu_entry(13).frame <= "00000000001101"; -- DRAM page 13 + mmu_entry(14).frame <= "00000000001110"; -- DRAM page 14 + mmu_entry(15).frame <= "10000000000001"; -- second page of SRAM + mmu_entry( 0).can_read <= '1'; + mmu_entry( 1).can_read <= '1'; + mmu_entry( 2).can_read <= '1'; + mmu_entry( 3).can_read <= '1'; + mmu_entry( 4).can_read <= '1'; + mmu_entry( 5).can_read <= '1'; + mmu_entry( 6).can_read <= '1'; + mmu_entry( 7).can_read <= '1'; + mmu_entry( 8).can_read <= '1'; + mmu_entry( 9).can_read <= '1'; + mmu_entry(10).can_read <= '1'; + mmu_entry(11).can_read <= '1'; + mmu_entry(12).can_read <= '1'; + mmu_entry(13).can_read <= '1'; + mmu_entry(14).can_read <= '1'; + mmu_entry(15).can_read <= '1'; + mmu_entry( 0).can_write <= '0'; + mmu_entry( 1).can_write <= '1'; + mmu_entry( 2).can_write <= '1'; + mmu_entry( 3).can_write <= '1'; + mmu_entry( 4).can_write <= '1'; + mmu_entry( 5).can_write <= '1'; + mmu_entry( 6).can_write <= '1'; + mmu_entry( 7).can_write <= '1'; + mmu_entry( 8).can_write <= '1'; + mmu_entry( 9).can_write <= '1'; + mmu_entry(10).can_write <= '1'; + mmu_entry(11).can_write <= '1'; + mmu_entry(12).can_write <= '1'; + mmu_entry(13).can_write <= '1'; + mmu_entry(14).can_write <= '1'; + mmu_entry(15).can_write <= '1'; + mmu_frame_ff_pointer <= "10000000000000000000000000"; -- map first byte of ROM to pointer on reset + was_map_io_to_ff_ptr <= '0'; + else + was_map_io_to_ff_ptr <= map_io_to_ff_ptr; + + if io_cs = '1' and req_write = '1' then + case address_in(2 downto 0) is + when "000" => + cpu_entry_select <= cpu_data_in; + when "011" => + if cpu_entry_select(7 downto 4) = "0000" then + mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).can_read <= cpu_data_in(0); + mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).can_write <= cpu_data_in(1); + end if; + when "100" => + if cpu_entry_select(7 downto 4) = "0000" then + mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).frame(13 downto 0) <= + cpu_data_in(5 downto 0) & mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).frame(7 downto 0); + elsif cpu_entry_select = "11111111" then + mmu_frame_ff_pointer <= + cpu_data_in(1 downto 0) & mmu_frame_ff_pointer(23 downto 0); + end if; + when "101" => + if cpu_entry_select(7 downto 4) = "0000" then + mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).frame(13 downto 0) <= + mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).frame(13 downto 8) & cpu_data_in(7 downto 0); + elsif cpu_entry_select = "11111111" then + mmu_frame_ff_pointer <= + mmu_frame_ff_pointer(25 downto 24) & cpu_data_in(7 downto 0) & mmu_frame_ff_pointer(15 downto 0); + end if; + when "110" => + if cpu_entry_select = "11111111" then + mmu_frame_ff_pointer <= + mmu_frame_ff_pointer(25 downto 16) & cpu_data_in(7 downto 0) & mmu_frame_ff_pointer(7 downto 0); + end if; + when "111" => + if cpu_entry_select = "11111111" then + mmu_frame_ff_pointer <= + mmu_frame_ff_pointer(25 downto 8) & cpu_data_in(7 downto 0); + end if; + when others => + -- nothing + end case; + elsif map_io_to_ff_ptr = '0' and was_map_io_to_ff_ptr = '1' then + -- post-increment our pointer (this is what makes "the 17th page" efficient!) + mmu_frame_ff_pointer <= std_logic_vector(unsigned(mmu_frame_ff_pointer) + 1); + end if; + end if; + end if; + end process; +end; diff --git a/vhdl/MonZ80_template.vhd b/vhdl/MonZ80_template.vhd new file mode 100644 index 0000000..c34c850 --- /dev/null +++ b/vhdl/MonZ80_template.vhd @@ -0,0 +1,56 @@ +--+-----------------------------------+-------------------------------------+-- +--| ___ ___ | (c) 2013-2014 William R Sowerbutts |-- +--| ___ ___ ___ ___( _ ) / _ \ | will@sowerbutts.com |-- +--| / __|/ _ \ / __|_ / _ \| | | | | |-- +--| \__ \ (_) | (__ / / (_) | |_| | | A Z80 FPGA computer, just for fun |-- +--| |___/\___/ \___/___\___/ \___/ | |-- +--| | http://sowerbutts.com/ |-- +--+-----------------------------------+-------------------------------------+-- +--| An inferrable 4KB ROM to contain the monitor program |-- +--+-------------------------------------------------------------------------+-- +-- +-- MonZ80_template.vhd contains the template VHDL for the ROM but no actual +-- data. The "ROMHERE" string is replaced by byte data by the "make_vhdl_rom" +-- tool in software/tools which is invoked to generate "MonZ80.vhd" after +-- the monitor program has been assembled. + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity MonZ80 is + port( + clk : in std_logic; + a : in std_logic_vector(11 downto 0); + d : out std_logic_vector(7 downto 0) + ); +end MonZ80; + +architecture arch of MonZ80 is + constant byte_rom_WIDTH: integer := 8; + type byte_rom_type is array (0 to 4095) of std_logic_vector(byte_rom_WIDTH-1 downto 0); + signal address_latch : std_logic_vector(11 downto 0) := (others => '0'); + + -- actually memory cells + signal byte_rom : byte_rom_type := ( + -- ROM contents follows + + +%ROMHERE% + + + ); + +begin + + ram_process: process(clk, byte_rom) + begin + if rising_edge(clk) then + -- latch the address, in order to infer a synchronous memory + address_latch <= a; + end if; + end process; + + d <= byte_rom(to_integer(unsigned(address_latch))); + +end arch; diff --git a/vhdl/SDRAM_Controller.vhd b/vhdl/SDRAM_Controller.vhd new file mode 100644 index 0000000..f94dd8a --- /dev/null +++ b/vhdl/SDRAM_Controller.vhd @@ -0,0 +1,506 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- 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 UNISIM.VComponents.all; +use IEEE.NUMERIC_STD.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' + + ----------------------------------------------------------- + -- Forward the SDRAM clock to the SDRAM chip - 180 degress + -- out of phase with the control signals (ensuring setup and holdup + ----------------------------------------------------------- + sdram_clk_forward : ODDR2 + generic map(DDR_ALIGNMENT => "NONE", INIT => '0', SRTYPE => "SYNC") + port map (Q => sdram_clk, C0 => clk, C1 => not clk, CE => '1', R => '0', S => '0', D0 => '0', D1 => '1'); + + ----------------------------------------------- + --!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + --!! 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: IOBUF + generic map (DRIVE => 12, IOSTANDARD => "LVTTL", SLEW => "FAST") + port map ( O => sdram_din(i), IO => sdram_data(i), I => iob_data(i), T => 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_6; + 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_2; + 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; + -- can we do a back-to-back write? + if forcing_refresh = '0' and got_transaction = '1' and can_back_to_back = '1' then + if save_wr = '1' then + -- back-to-back write? + state <= s_write_1; + ready_for_new <= '1'; + got_transaction <= '0'; + end if; + -- Although it looks right in simulation you can't go write-to-read + -- here due to bus contention, as iob_dq_hiz takes a few ns. + end if; + + when s_write_3 => -- must wait tRDL, hence the extra idle state + -- back to back transaction? + if forcing_refresh = '0' and got_transaction = '1' and can_back_to_back = '1' then + if save_wr = '1' then + -- back-to-back write? + state <= s_write_1; + ready_for_new <= '1'; + got_transaction <= '0'; + else + -- write-to-read switch? + state <= s_read_1; + iob_dq_hiz <= '1'; + ready_for_new <= '1'; -- we will be ready for a new transaction next cycle! + got_transaction <= '0'; + end if; + else + iob_dq_hiz <= '1'; + state <= s_precharge; + end if; + + ------------------------------------------------------------------- + -- 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; diff --git a/vhdl/SSRAM.vhd b/vhdl/SSRAM.vhd new file mode 100644 index 0000000..461b16a --- /dev/null +++ b/vhdl/SSRAM.vhd @@ -0,0 +1,88 @@ +-- +-- Inferrable Synchronous SRAM for XST synthesis +-- +-- Version : 0220 +-- +-- Copyright (c) 2002 Daniel Wallner (jesus@opencores.org) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- The latest version of this file can be found at: +-- http://www.opencores.org/cvsweb.shtml/t51/ +-- +-- Limitations : +-- +-- File history : +-- 0208 : Initial release +-- 0218 : Fixed data out at write +-- 0220 : Added support for XST + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +entity SSRAM is + generic( + AddrWidth : integer := 11; + DataWidth : integer := 8 + ); + port( + clk : in std_logic; + ce : in std_logic; + we : in std_logic; + A : in std_logic_vector(AddrWidth - 1 downto 0); + DIn : in std_logic_vector(DataWidth - 1 downto 0); + DOut : out std_logic_vector(DataWidth - 1 downto 0) + ); +end SSRAM; + +architecture behaviour of SSRAM is + + type Memory_Image is array (natural range <>) of std_logic_vector(DataWidth - 1 downto 0); + signal RAM : Memory_Image(0 to 2 ** AddrWidth - 1); + signal A_r : std_logic_vector(AddrWidth - 1 downto 0); + +begin + + process (Clk) + begin + if rising_edge(clk) then + if (ce = '1' and we = '1') then + RAM(to_integer(unsigned(A))) <= DIn; + end if; + A_r <= A; + end if; + end process; + + DOut <= RAM(to_integer(unsigned(A_r))); +end; diff --git a/vhdl/T80.vhd b/vhdl/T80.vhd new file mode 100644 index 0000000..0912e3d --- /dev/null +++ b/vhdl/T80.vhd @@ -0,0 +1,1094 @@ +-- **** +-- T80(b) core. In an effort to merge and maintain bug fixes .... +-- +-- +-- Ver 303 add undocumented DDCB and FDCB opcodes by TobiFlex 20.04.2010 +-- Ver 302 fixed IO cycle timing, tested thanks to Alessandro. +-- Ver 301 parity flag is just parity for 8080, also overflow for Z80, by Sean Riddle +-- Ver 300 started tidyup. Rmoved some auto_wait bits from 0247 which caused problems +-- +-- MikeJ March 2005 +-- Latest version from www.fpgaarcade.com (original www.opencores.org) +-- +-- **** +-- +-- Z80 compatible microprocessor core +-- +-- Version : 0247 +-- +-- Copyright (c) 2001-2002 Daniel Wallner (jesus@opencores.org) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- The latest version of this file can be found at: +-- http://www.opencores.org/cvsweb.shtml/t80/ +-- +-- Limitations : +-- +-- File history : +-- +-- 0208 : First complete release +-- +-- 0210 : Fixed wait and halt +-- +-- 0211 : Fixed Refresh addition and IM 1 +-- +-- 0214 : Fixed mostly flags, only the block instructions now fail the zex regression test +-- +-- 0232 : Removed refresh address output for Mode > 1 and added DJNZ M1_n fix by Mike Johnson +-- +-- 0235 : Added clock enable and IM 2 fix by Mike Johnson +-- +-- 0237 : Changed 8080 I/O address output, added IntE output +-- +-- 0238 : Fixed (IX/IY+d) timing and 16 bit ADC and SBC zero flag +-- +-- 0240 : Added interrupt ack fix by Mike Johnson, changed (IX/IY+d) timing and changed flags in GB mode +-- +-- 0242 : Added I/O wait, fixed refresh address, moved some registers to RAM +-- +-- 0247 : Fixed bus req/ack cycle +-- + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use work.T80_Pack.all; + +entity T80 is + generic( + Mode : integer := 0; -- 0 => Z80, 1 => Fast Z80, 2 => 8080, 3 => GB + IOWait : integer := 0; -- 1 => Single cycle I/O, 1 => Std I/O cycle + Flag_C : integer := 0; + Flag_N : integer := 1; + Flag_P : integer := 2; + Flag_X : integer := 3; + Flag_H : integer := 4; + Flag_Y : integer := 5; + Flag_Z : integer := 6; + Flag_S : integer := 7 + ); + port( + RESET_n : in std_logic; + CLK_n : in std_logic; + CEN : in std_logic; + WAIT_n : in std_logic; + INT_n : in std_logic; + NMI_n : in std_logic; + BUSRQ_n : in std_logic; + M1_n : out std_logic; + IORQ : out std_logic; + NoRead : out std_logic; + Write : out std_logic; + RFSH_n : out std_logic; + HALT_n : out std_logic; + BUSAK_n : out std_logic; + A : out std_logic_vector(15 downto 0); + DInst : in std_logic_vector(7 downto 0); + DI : in std_logic_vector(7 downto 0); + DO : out std_logic_vector(7 downto 0); + MC : out std_logic_vector(2 downto 0); + TS : out std_logic_vector(2 downto 0); + IntCycle_n : out std_logic; + IntE : out std_logic; + Stop : out std_logic + ); +end T80; + +architecture rtl of T80 is + + constant aNone : std_logic_vector(2 downto 0) := "111"; + constant aBC : std_logic_vector(2 downto 0) := "000"; + constant aDE : std_logic_vector(2 downto 0) := "001"; + constant aXY : std_logic_vector(2 downto 0) := "010"; + constant aIOA : std_logic_vector(2 downto 0) := "100"; + constant aSP : std_logic_vector(2 downto 0) := "101"; + constant aZI : std_logic_vector(2 downto 0) := "110"; + + -- Registers + signal ACC, F : std_logic_vector(7 downto 0); + signal Ap, Fp : std_logic_vector(7 downto 0); + signal I : std_logic_vector(7 downto 0); + signal R : unsigned(7 downto 0); + signal SP, PC : unsigned(15 downto 0); + + signal RegDIH : std_logic_vector(7 downto 0); + signal RegDIL : std_logic_vector(7 downto 0); + signal RegBusA : std_logic_vector(15 downto 0); + signal RegBusB : std_logic_vector(15 downto 0); + signal RegBusC : std_logic_vector(15 downto 0); + signal RegAddrA_r : std_logic_vector(2 downto 0); + signal RegAddrA : std_logic_vector(2 downto 0); + signal RegAddrB_r : std_logic_vector(2 downto 0); + signal RegAddrB : std_logic_vector(2 downto 0); + signal RegAddrC : std_logic_vector(2 downto 0); + signal RegWEH : std_logic; + signal RegWEL : std_logic; + signal Alternate : std_logic; + + -- Help Registers + signal TmpAddr : std_logic_vector(15 downto 0); -- Temporary address register + signal IR : std_logic_vector(7 downto 0); -- Instruction register + signal ISet : std_logic_vector(1 downto 0); -- Instruction set selector + signal RegBusA_r : std_logic_vector(15 downto 0); + + signal ID16 : signed(15 downto 0); + signal Save_Mux : std_logic_vector(7 downto 0); + + signal TState : unsigned(2 downto 0); + signal MCycle : std_logic_vector(2 downto 0); + signal IntE_FF1 : std_logic; + signal IntE_FF2 : std_logic; + signal Halt_FF : std_logic; + signal BusReq_s : std_logic; + signal BusAck : std_logic; + signal ClkEn : std_logic; + signal NMI_s : std_logic; + signal INT_s : std_logic; + signal IStatus : std_logic_vector(1 downto 0); + + signal DI_Reg : std_logic_vector(7 downto 0); + signal T_Res : std_logic; + signal XY_State : std_logic_vector(1 downto 0); + signal Pre_XY_F_M : std_logic_vector(2 downto 0); + signal NextIs_XY_Fetch : std_logic; + signal XY_Ind : std_logic; + signal No_BTR : std_logic; + signal BTR_r : std_logic; + signal Auto_Wait : std_logic; + signal Auto_Wait_t1 : std_logic; + signal Auto_Wait_t2 : std_logic; + signal IncDecZ : std_logic; + + -- ALU signals + signal BusB : std_logic_vector(7 downto 0); + signal BusA : std_logic_vector(7 downto 0); + signal ALU_Q : std_logic_vector(7 downto 0); + signal F_Out : std_logic_vector(7 downto 0); + + -- Registered micro code outputs + signal Read_To_Reg_r : std_logic_vector(4 downto 0); + signal Arith16_r : std_logic; + signal Z16_r : std_logic; + signal ALU_Op_r : std_logic_vector(3 downto 0); + signal Save_ALU_r : std_logic; + signal PreserveC_r : std_logic; + signal MCycles : std_logic_vector(2 downto 0); + + -- Micro code outputs + signal MCycles_d : std_logic_vector(2 downto 0); + signal TStates : std_logic_vector(2 downto 0); + signal IntCycle : std_logic; + signal NMICycle : std_logic; + signal Inc_PC : std_logic; + signal Inc_WZ : std_logic; + signal IncDec_16 : std_logic_vector(3 downto 0); + signal Prefix : std_logic_vector(1 downto 0); + signal Read_To_Acc : std_logic; + signal Read_To_Reg : std_logic; + signal Set_BusB_To : std_logic_vector(3 downto 0); + signal Set_BusA_To : std_logic_vector(3 downto 0); + signal ALU_Op : std_logic_vector(3 downto 0); + signal Save_ALU : std_logic; + signal PreserveC : std_logic; + signal Arith16 : std_logic; + signal Set_Addr_To : std_logic_vector(2 downto 0); + signal Jump : std_logic; + signal JumpE : std_logic; + signal JumpXY : std_logic; + signal Call : std_logic; + signal RstP : std_logic; + signal LDZ : std_logic; + signal LDW : std_logic; + signal LDSPHL : std_logic; + signal IORQ_i : std_logic; + signal Special_LD : std_logic_vector(2 downto 0); + signal ExchangeDH : std_logic; + signal ExchangeRp : std_logic; + signal ExchangeAF : std_logic; + signal ExchangeRS : std_logic; + signal I_DJNZ : std_logic; + signal I_CPL : std_logic; + signal I_CCF : std_logic; + signal I_SCF : std_logic; + signal I_RETN : std_logic; + signal I_BT : std_logic; + signal I_BC : std_logic; + signal I_BTR : std_logic; + signal I_RLD : std_logic; + signal I_RRD : std_logic; + signal I_INRC : std_logic; + signal SetDI : std_logic; + signal SetEI : std_logic; + signal IMode : std_logic_vector(1 downto 0); + signal Halt : std_logic; + signal XYbit_undoc : std_logic; + + +begin + + mcode : T80_MCode + generic map( + Mode => Mode, + Flag_C => Flag_C, + Flag_N => Flag_N, + Flag_P => Flag_P, + Flag_X => Flag_X, + Flag_H => Flag_H, + Flag_Y => Flag_Y, + Flag_Z => Flag_Z, + Flag_S => Flag_S) + port map( + IR => IR, + ISet => ISet, + MCycle => MCycle, + F => F, + NMICycle => NMICycle, + IntCycle => IntCycle, + XY_State => XY_State, + MCycles => MCycles_d, + TStates => TStates, + Prefix => Prefix, + Inc_PC => Inc_PC, + Inc_WZ => Inc_WZ, + IncDec_16 => IncDec_16, + Read_To_Acc => Read_To_Acc, + Read_To_Reg => Read_To_Reg, + Set_BusB_To => Set_BusB_To, + Set_BusA_To => Set_BusA_To, + ALU_Op => ALU_Op, + Save_ALU => Save_ALU, + PreserveC => PreserveC, + Arith16 => Arith16, + Set_Addr_To => Set_Addr_To, + IORQ => IORQ_i, + Jump => Jump, + JumpE => JumpE, + JumpXY => JumpXY, + Call => Call, + RstP => RstP, + LDZ => LDZ, + LDW => LDW, + LDSPHL => LDSPHL, + Special_LD => Special_LD, + ExchangeDH => ExchangeDH, + ExchangeRp => ExchangeRp, + ExchangeAF => ExchangeAF, + ExchangeRS => ExchangeRS, + I_DJNZ => I_DJNZ, + I_CPL => I_CPL, + I_CCF => I_CCF, + I_SCF => I_SCF, + I_RETN => I_RETN, + I_BT => I_BT, + I_BC => I_BC, + I_BTR => I_BTR, + I_RLD => I_RLD, + I_RRD => I_RRD, + I_INRC => I_INRC, + SetDI => SetDI, + SetEI => SetEI, + IMode => IMode, + Halt => Halt, + NoRead => NoRead, + Write => Write, + XYbit_undoc => XYbit_undoc); + + alu : T80_ALU + generic map( + Mode => Mode, + Flag_C => Flag_C, + Flag_N => Flag_N, + Flag_P => Flag_P, + Flag_X => Flag_X, + Flag_H => Flag_H, + Flag_Y => Flag_Y, + Flag_Z => Flag_Z, + Flag_S => Flag_S) + port map( + Arith16 => Arith16_r, + Z16 => Z16_r, + ALU_Op => ALU_Op_r, + IR => IR(5 downto 0), + ISet => ISet, + BusA => BusA, + BusB => BusB, + F_In => F, + Q => ALU_Q, + F_Out => F_Out); + + ClkEn <= CEN and not BusAck; + + T_Res <= '1' when TState = unsigned(TStates) else '0'; + + NextIs_XY_Fetch <= '1' when XY_State /= "00" and XY_Ind = '0' and + ((Set_Addr_To = aXY) or + (MCycle = "001" and IR = "11001011") or + (MCycle = "001" and IR = "00110110")) else '0'; + + Save_Mux <= BusB when ExchangeRp = '1' else + DI_Reg when Save_ALU_r = '0' else + ALU_Q; + + process (RESET_n, CLK_n) + begin + if RESET_n = '0' then + PC <= (others => '0'); -- Program Counter + A <= (others => '0'); + TmpAddr <= (others => '0'); + IR <= "00000000"; + ISet <= "00"; + XY_State <= "00"; + IStatus <= "00"; + MCycles <= "000"; + DO <= "00000000"; + + ACC <= (others => '1'); + F <= (others => '1'); + Ap <= (others => '1'); + Fp <= (others => '1'); + I <= (others => '0'); + R <= (others => '0'); + SP <= (others => '1'); + Alternate <= '0'; + + Read_To_Reg_r <= "00000"; + F <= (others => '1'); + Arith16_r <= '0'; + BTR_r <= '0'; + Z16_r <= '0'; + ALU_Op_r <= "0000"; + Save_ALU_r <= '0'; + PreserveC_r <= '0'; + XY_Ind <= '0'; + + elsif CLK_n'event and CLK_n = '1' then + + if ClkEn = '1' then + + ALU_Op_r <= "0000"; + Save_ALU_r <= '0'; + Read_To_Reg_r <= "00000"; + + MCycles <= MCycles_d; + + if IMode /= "11" then + IStatus <= IMode; + end if; + + Arith16_r <= Arith16; + PreserveC_r <= PreserveC; + if ISet = "10" and ALU_OP(2) = '0' and ALU_OP(0) = '1' and MCycle = "011" then + Z16_r <= '1'; + else + Z16_r <= '0'; + end if; + + if MCycle = "001" and TState(2) = '0' then + -- MCycle = 1 and TState = 1, 2, or 3 + + if TState = 2 and Wait_n = '1' then + if Mode < 2 then + A(7 downto 0) <= std_logic_vector(R); + A(15 downto 8) <= I; + R(6 downto 0) <= R(6 downto 0) + 1; + end if; + + if Jump = '0' and Call = '0' and NMICycle = '0' and IntCycle = '0' and not (Halt_FF = '1' or Halt = '1') then + PC <= PC + 1; + end if; + + if IntCycle = '1' and IStatus = "01" then + IR <= "11111111"; + elsif Halt_FF = '1' or (IntCycle = '1' and IStatus = "10") or NMICycle = '1' then + IR <= "00000000"; + else + IR <= DInst; + end if; + + ISet <= "00"; + if Prefix /= "00" then + if Prefix = "11" then + if IR(5) = '1' then + XY_State <= "10"; + else + XY_State <= "01"; + end if; + else + if Prefix = "10" then + XY_State <= "00"; + XY_Ind <= '0'; + end if; + ISet <= Prefix; + end if; + else + XY_State <= "00"; + XY_Ind <= '0'; + end if; + end if; + + else + -- either (MCycle > 1) OR (MCycle = 1 AND TState > 3) + + if MCycle = "110" then + XY_Ind <= '1'; + if Prefix = "01" then + ISet <= "01"; + end if; + end if; + + if T_Res = '1' then + BTR_r <= (I_BT or I_BC or I_BTR) and not No_BTR; + if Jump = '1' then + A(15 downto 8) <= DI_Reg; + A(7 downto 0) <= TmpAddr(7 downto 0); + PC(15 downto 8) <= unsigned(DI_Reg); + PC(7 downto 0) <= unsigned(TmpAddr(7 downto 0)); + elsif JumpXY = '1' then + A <= RegBusC; + PC <= unsigned(RegBusC); + elsif Call = '1' or RstP = '1' then + A <= TmpAddr; + PC <= unsigned(TmpAddr); + elsif MCycle = MCycles and NMICycle = '1' then + A <= "0000000001100110"; + PC <= "0000000001100110"; + elsif MCycle = "011" and IntCycle = '1' and IStatus = "10" then + A(15 downto 8) <= I; + A(7 downto 0) <= TmpAddr(7 downto 0); + PC(15 downto 8) <= unsigned(I); + PC(7 downto 0) <= unsigned(TmpAddr(7 downto 0)); + else + case Set_Addr_To is + when aXY => + if XY_State = "00" then + A <= RegBusC; + else + if NextIs_XY_Fetch = '1' then + A <= std_logic_vector(PC); + else + A <= TmpAddr; + end if; + end if; + when aIOA => + if Mode = 3 then + -- Memory map I/O on GBZ80 + A(15 downto 8) <= (others => '1'); + elsif Mode = 2 then + -- Duplicate I/O address on 8080 + A(15 downto 8) <= DI_Reg; + else + A(15 downto 8) <= ACC; + end if; + A(7 downto 0) <= DI_Reg; + when aSP => + A <= std_logic_vector(SP); + when aBC => + if Mode = 3 and IORQ_i = '1' then + -- Memory map I/O on GBZ80 + A(15 downto 8) <= (others => '1'); + A(7 downto 0) <= RegBusC(7 downto 0); + else + A <= RegBusC; + end if; + when aDE => + A <= RegBusC; + when aZI => + if Inc_WZ = '1' then + A <= std_logic_vector(unsigned(TmpAddr) + 1); + else + A(15 downto 8) <= DI_Reg; + A(7 downto 0) <= TmpAddr(7 downto 0); + end if; + when others => + A <= std_logic_vector(PC); + end case; + end if; + + Save_ALU_r <= Save_ALU; + ALU_Op_r <= ALU_Op; + + if I_CPL = '1' then + -- CPL + ACC <= not ACC; + F(Flag_Y) <= not ACC(5); + F(Flag_H) <= '1'; + F(Flag_X) <= not ACC(3); + F(Flag_N) <= '1'; + end if; + if I_CCF = '1' then + -- CCF + F(Flag_C) <= not F(Flag_C); + F(Flag_Y) <= ACC(5); + F(Flag_H) <= F(Flag_C); + F(Flag_X) <= ACC(3); + F(Flag_N) <= '0'; + end if; + if I_SCF = '1' then + -- SCF + F(Flag_C) <= '1'; + F(Flag_Y) <= ACC(5); + F(Flag_H) <= '0'; + F(Flag_X) <= ACC(3); + F(Flag_N) <= '0'; + end if; + end if; + + if TState = 2 and Wait_n = '1' then + if ISet = "01" and MCycle = "111" then + IR <= DInst; + end if; + if JumpE = '1' then + PC <= unsigned(signed(PC) + signed(DI_Reg)); + elsif Inc_PC = '1' then + PC <= PC + 1; + end if; + if BTR_r = '1' then + PC <= PC - 2; + end if; + if RstP = '1' then + TmpAddr <= (others =>'0'); + TmpAddr(5 downto 3) <= IR(5 downto 3); + end if; + end if; + if TState = 3 and MCycle = "110" then + TmpAddr <= std_logic_vector(signed(RegBusC) + signed(DI_Reg)); + end if; + + if (TState = 2 and Wait_n = '1') or (TState = 4 and MCycle = "001") then + if IncDec_16(2 downto 0) = "111" then + if IncDec_16(3) = '1' then + SP <= SP - 1; + else + SP <= SP + 1; + end if; + end if; + end if; + + if LDSPHL = '1' then + SP <= unsigned(RegBusC); + end if; + if ExchangeAF = '1' then + Ap <= ACC; + ACC <= Ap; + Fp <= F; + F <= Fp; + end if; + if ExchangeRS = '1' then + Alternate <= not Alternate; + end if; + end if; + + if TState = 3 then + if LDZ = '1' then + TmpAddr(7 downto 0) <= DI_Reg; + end if; + if LDW = '1' then + TmpAddr(15 downto 8) <= DI_Reg; + end if; + + if Special_LD(2) = '1' then + case Special_LD(1 downto 0) is + when "00" => + ACC <= I; + F(Flag_P) <= IntE_FF2; + when "01" => + ACC <= std_logic_vector(R); + F(Flag_P) <= IntE_FF2; + when "10" => + I <= ACC; + when others => + R <= unsigned(ACC); + end case; + end if; + end if; + + if (I_DJNZ = '0' and Save_ALU_r = '1') or ALU_Op_r = "1001" then + if Mode = 3 then + F(6) <= F_Out(6); + F(5) <= F_Out(5); + F(7) <= F_Out(7); + if PreserveC_r = '0' then + F(4) <= F_Out(4); + end if; + else + F(7 downto 1) <= F_Out(7 downto 1); + if PreserveC_r = '0' then + F(Flag_C) <= F_Out(0); + end if; + end if; + end if; + if T_Res = '1' and I_INRC = '1' then + F(Flag_H) <= '0'; + F(Flag_N) <= '0'; + if DI_Reg(7 downto 0) = "00000000" then + F(Flag_Z) <= '1'; + else + F(Flag_Z) <= '0'; + end if; + F(Flag_S) <= DI_Reg(7); + F(Flag_P) <= not (DI_Reg(0) xor DI_Reg(1) xor DI_Reg(2) xor DI_Reg(3) xor + DI_Reg(4) xor DI_Reg(5) xor DI_Reg(6) xor DI_Reg(7)); + end if; + + if TState = 1 then + DO <= BusB; + if I_RLD = '1' then + DO(3 downto 0) <= BusA(3 downto 0); + DO(7 downto 4) <= BusB(3 downto 0); + end if; + if I_RRD = '1' then + DO(3 downto 0) <= BusB(7 downto 4); + DO(7 downto 4) <= BusA(3 downto 0); + end if; + end if; + + if T_Res = '1' then + Read_To_Reg_r(3 downto 0) <= Set_BusA_To; + Read_To_Reg_r(4) <= Read_To_Reg; + if Read_To_Acc = '1' then + Read_To_Reg_r(3 downto 0) <= "0111"; + Read_To_Reg_r(4) <= '1'; + end if; + end if; + + if TState = 1 and I_BT = '1' then + F(Flag_X) <= ALU_Q(3); + F(Flag_Y) <= ALU_Q(1); + F(Flag_H) <= '0'; + F(Flag_N) <= '0'; + end if; + if I_BC = '1' or I_BT = '1' then + F(Flag_P) <= IncDecZ; + end if; + + if (TState = 1 and Save_ALU_r = '0') or + (Save_ALU_r = '1' and ALU_OP_r /= "0111") then + case Read_To_Reg_r is + when "10111" => + ACC <= Save_Mux; + when "10110" => + DO <= Save_Mux; + when "11000" => + SP(7 downto 0) <= unsigned(Save_Mux); + when "11001" => + SP(15 downto 8) <= unsigned(Save_Mux); + when "11011" => + F <= Save_Mux; + when others => + end case; + if XYbit_undoc='1' then + DO <= ALU_Q; + end if; + end if; + + end if; + + end if; + + end process; + +--------------------------------------------------------------------------- +-- +-- BC('), DE('), HL('), IX and IY +-- +--------------------------------------------------------------------------- + process (CLK_n) + begin + if CLK_n'event and CLK_n = '1' then + if ClkEn = '1' then + -- Bus A / Write + RegAddrA_r <= Alternate & Set_BusA_To(2 downto 1); + if XY_Ind = '0' and XY_State /= "00" and Set_BusA_To(2 downto 1) = "10" then + RegAddrA_r <= XY_State(1) & "11"; + end if; + + -- Bus B + RegAddrB_r <= Alternate & Set_BusB_To(2 downto 1); + if XY_Ind = '0' and XY_State /= "00" and Set_BusB_To(2 downto 1) = "10" then + RegAddrB_r <= XY_State(1) & "11"; + end if; + + -- Address from register + RegAddrC <= Alternate & Set_Addr_To(1 downto 0); + -- Jump (HL), LD SP,HL + if (JumpXY = '1' or LDSPHL = '1') then + RegAddrC <= Alternate & "10"; + end if; + if ((JumpXY = '1' or LDSPHL = '1') and XY_State /= "00") or (MCycle = "110") then + RegAddrC <= XY_State(1) & "11"; + end if; + + if I_DJNZ = '1' and Save_ALU_r = '1' and Mode < 2 then + IncDecZ <= F_Out(Flag_Z); + end if; + if (TState = 2 or (TState = 3 and MCycle = "001")) and IncDec_16(2 downto 0) = "100" then + if ID16 = 0 then + IncDecZ <= '0'; + else + IncDecZ <= '1'; + end if; + end if; + + RegBusA_r <= RegBusA; + end if; + end if; + end process; + + RegAddrA <= + -- 16 bit increment/decrement + Alternate & IncDec_16(1 downto 0) when (TState = 2 or + (TState = 3 and MCycle = "001" and IncDec_16(2) = '1')) and XY_State = "00" else + XY_State(1) & "11" when (TState = 2 or + (TState = 3 and MCycle = "001" and IncDec_16(2) = '1')) and IncDec_16(1 downto 0) = "10" else + -- EX HL,DL + Alternate & "10" when ExchangeDH = '1' and TState = 3 else + Alternate & "01" when ExchangeDH = '1' and TState = 4 else + -- Bus A / Write + RegAddrA_r; + + RegAddrB <= + -- EX HL,DL + Alternate & "01" when ExchangeDH = '1' and TState = 3 else + -- Bus B + RegAddrB_r; + + ID16 <= signed(RegBusA) - 1 when IncDec_16(3) = '1' else + signed(RegBusA) + 1; + + process (Save_ALU_r, Auto_Wait_t1, ALU_OP_r, Read_To_Reg_r, + ExchangeDH, IncDec_16, MCycle, TState, Wait_n) + begin + RegWEH <= '0'; + RegWEL <= '0'; + if (TState = 1 and Save_ALU_r = '0') or + (Save_ALU_r = '1' and ALU_OP_r /= "0111") then + case Read_To_Reg_r is + when "10000" | "10001" | "10010" | "10011" | "10100" | "10101" => + RegWEH <= not Read_To_Reg_r(0); + RegWEL <= Read_To_Reg_r(0); + when others => + end case; + end if; + + if ExchangeDH = '1' and (TState = 3 or TState = 4) then + RegWEH <= '1'; + RegWEL <= '1'; + end if; + + if IncDec_16(2) = '1' and ((TState = 2 and Wait_n = '1' and MCycle /= "001") or (TState = 3 and MCycle = "001")) then + case IncDec_16(1 downto 0) is + when "00" | "01" | "10" => + RegWEH <= '1'; + RegWEL <= '1'; + when others => + end case; + end if; + end process; + + process (Save_Mux, RegBusB, RegBusA_r, ID16, + ExchangeDH, IncDec_16, MCycle, TState, Wait_n) + begin + RegDIH <= Save_Mux; + RegDIL <= Save_Mux; + + if ExchangeDH = '1' and TState = 3 then + RegDIH <= RegBusB(15 downto 8); + RegDIL <= RegBusB(7 downto 0); + end if; + if ExchangeDH = '1' and TState = 4 then + RegDIH <= RegBusA_r(15 downto 8); + RegDIL <= RegBusA_r(7 downto 0); + end if; + + if IncDec_16(2) = '1' and ((TState = 2 and MCycle /= "001") or (TState = 3 and MCycle = "001")) then + RegDIH <= std_logic_vector(ID16(15 downto 8)); + RegDIL <= std_logic_vector(ID16(7 downto 0)); + end if; + end process; + + Regs : T80_Reg + port map( + Clk => CLK_n, + CEN => ClkEn, + WEH => RegWEH, + WEL => RegWEL, + AddrA => RegAddrA, + AddrB => RegAddrB, + AddrC => RegAddrC, + DIH => RegDIH, + DIL => RegDIL, + DOAH => RegBusA(15 downto 8), + DOAL => RegBusA(7 downto 0), + DOBH => RegBusB(15 downto 8), + DOBL => RegBusB(7 downto 0), + DOCH => RegBusC(15 downto 8), + DOCL => RegBusC(7 downto 0)); + +--------------------------------------------------------------------------- +-- +-- Buses +-- +--------------------------------------------------------------------------- + process (CLK_n) + begin + if CLK_n'event and CLK_n = '1' then + if ClkEn = '1' then + case Set_BusB_To is + when "0111" => + BusB <= ACC; + when "0000" | "0001" | "0010" | "0011" | "0100" | "0101" => + if Set_BusB_To(0) = '1' then + BusB <= RegBusB(7 downto 0); + else + BusB <= RegBusB(15 downto 8); + end if; + when "0110" => + BusB <= DI_Reg; + when "1000" => + BusB <= std_logic_vector(SP(7 downto 0)); + when "1001" => + BusB <= std_logic_vector(SP(15 downto 8)); + when "1010" => + BusB <= "00000001"; + when "1011" => + BusB <= F; + when "1100" => + BusB <= std_logic_vector(PC(7 downto 0)); + when "1101" => + BusB <= std_logic_vector(PC(15 downto 8)); + when "1110" => + BusB <= "00000000"; + when others => + BusB <= "--------"; + end case; + + case Set_BusA_To is + when "0111" => + BusA <= ACC; + when "0000" | "0001" | "0010" | "0011" | "0100" | "0101" => + if Set_BusA_To(0) = '1' then + BusA <= RegBusA(7 downto 0); + else + BusA <= RegBusA(15 downto 8); + end if; + when "0110" => + BusA <= DI_Reg; + when "1000" => + BusA <= std_logic_vector(SP(7 downto 0)); + when "1001" => + BusA <= std_logic_vector(SP(15 downto 8)); + when "1010" => + BusA <= "00000000"; + when others => + BusB <= "--------"; + end case; + if XYbit_undoc='1' then + BusA <= DI_Reg; + BusB <= DI_Reg; + end if; + end if; + end if; + end process; + +--------------------------------------------------------------------------- +-- +-- Generate external control signals +-- +--------------------------------------------------------------------------- + process (RESET_n,CLK_n) + begin + if RESET_n = '0' then + RFSH_n <= '1'; + elsif CLK_n'event and CLK_n = '1' then + if CEN = '1' then + if MCycle = "001" and ((TState = 2 and Wait_n = '1') or TState = 3) then + RFSH_n <= '0'; + else + RFSH_n <= '1'; + end if; + end if; + end if; + end process; + + MC <= std_logic_vector(MCycle); + TS <= std_logic_vector(TState); + DI_Reg <= DI; + HALT_n <= not Halt_FF; + BUSAK_n <= not BusAck; + IntCycle_n <= not IntCycle; + IntE <= IntE_FF1; + IORQ <= IORQ_i; + Stop <= I_DJNZ; + +------------------------------------------------------------------------- +-- +-- Syncronise inputs +-- +------------------------------------------------------------------------- + process (RESET_n, CLK_n) + variable OldNMI_n : std_logic; + begin + if RESET_n = '0' then + BusReq_s <= '0'; + INT_s <= '0'; + NMI_s <= '0'; + OldNMI_n := '0'; + elsif CLK_n'event and CLK_n = '1' then + if CEN = '1' then + BusReq_s <= not BUSRQ_n; + INT_s <= not INT_n; + if NMICycle = '1' then + NMI_s <= '0'; + elsif NMI_n = '0' and OldNMI_n = '1' then + NMI_s <= '1'; + end if; + OldNMI_n := NMI_n; + end if; + end if; + end process; + +------------------------------------------------------------------------- +-- +-- Main state machine +-- +------------------------------------------------------------------------- + process (RESET_n, CLK_n) + begin + if RESET_n = '0' then + MCycle <= "001"; + TState <= "000"; + Pre_XY_F_M <= "000"; + Halt_FF <= '0'; + BusAck <= '0'; + NMICycle <= '0'; + IntCycle <= '0'; + IntE_FF1 <= '0'; + IntE_FF2 <= '0'; + No_BTR <= '0'; + Auto_Wait_t1 <= '0'; + Auto_Wait_t2 <= '0'; + M1_n <= '1'; + elsif CLK_n'event and CLK_n = '1' then + if CEN = '1' then + Auto_Wait_t1 <= Auto_Wait; + Auto_Wait_t2 <= Auto_Wait_t1; + No_BTR <= (I_BT and (not IR(4) or not F(Flag_P))) or + (I_BC and (not IR(4) or F(Flag_Z) or not F(Flag_P))) or + (I_BTR and (not IR(4) or F(Flag_Z))); + if TState = 2 then + if SetEI = '1' then + IntE_FF1 <= '1'; + IntE_FF2 <= '1'; + end if; + if I_RETN = '1' then + IntE_FF1 <= IntE_FF2; + end if; + end if; + if TState = 3 then + if SetDI = '1' then + IntE_FF1 <= '0'; + IntE_FF2 <= '0'; + end if; + end if; + if IntCycle = '1' or NMICycle = '1' then + Halt_FF <= '0'; + end if; + if MCycle = "001" and TState = 2 and Wait_n = '1' then + M1_n <= '1'; + end if; + if BusReq_s = '1' and BusAck = '1' then + else + BusAck <= '0'; + if TState = 2 and Wait_n = '0' then + elsif T_Res = '1' then + if Halt = '1' then + Halt_FF <= '1'; + end if; + if BusReq_s = '1' then + BusAck <= '1'; + else + TState <= "001"; + if NextIs_XY_Fetch = '1' then + MCycle <= "110"; + Pre_XY_F_M <= MCycle; + if IR = "00110110" and Mode = 0 then + Pre_XY_F_M <= "010"; + end if; + elsif (MCycle = "111") or + (MCycle = "110" and Mode = 1 and ISet /= "01") then + MCycle <= std_logic_vector(unsigned(Pre_XY_F_M) + 1); + elsif (MCycle = MCycles) or + No_BTR = '1' or + (MCycle = "010" and I_DJNZ = '1' and IncDecZ = '1') then + M1_n <= '0'; + MCycle <= "001"; + IntCycle <= '0'; + NMICycle <= '0'; + if NMI_s = '1' and Prefix = "00" then + NMICycle <= '1'; + IntE_FF1 <= '0'; + elsif (IntE_FF1 = '1' and INT_s = '1') and Prefix = "00" and SetEI = '0' then + IntCycle <= '1'; + IntE_FF1 <= '0'; + IntE_FF2 <= '0'; + end if; + else + MCycle <= std_logic_vector(unsigned(MCycle) + 1); + end if; + end if; + else + if Auto_Wait = '1' nand Auto_Wait_t2 = '0' then + + TState <= TState + 1; + end if; + end if; + end if; + if TState = 0 then + M1_n <= '0'; + end if; + end if; + end if; + end process; + + process (IntCycle, NMICycle, MCycle) + begin + Auto_Wait <= '0'; + if IntCycle = '1' or NMICycle = '1' then + if MCycle = "001" then + Auto_Wait <= '1'; + end if; + end if; + end process; + +end; diff --git a/vhdl/T80_ALU.vhd b/vhdl/T80_ALU.vhd new file mode 100644 index 0000000..95c98da --- /dev/null +++ b/vhdl/T80_ALU.vhd @@ -0,0 +1,371 @@ +-- **** +-- T80(b) core. In an effort to merge and maintain bug fixes .... +-- +-- +-- Ver 301 parity flag is just parity for 8080, also overflow for Z80, by Sean Riddle +-- Ver 300 started tidyup +-- MikeJ March 2005 +-- Latest version from www.fpgaarcade.com (original www.opencores.org) +-- +-- **** +-- +-- Z80 compatible microprocessor core +-- +-- Version : 0247 +-- +-- Copyright (c) 2001-2002 Daniel Wallner (jesus@opencores.org) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- The latest version of this file can be found at: +-- http://www.opencores.org/cvsweb.shtml/t80/ +-- +-- Limitations : +-- +-- File history : +-- +-- 0214 : Fixed mostly flags, only the block instructions now fail the zex regression test +-- +-- 0238 : Fixed zero flag for 16 bit SBC and ADC +-- +-- 0240 : Added GB operations +-- +-- 0242 : Cleanup +-- +-- 0247 : Cleanup +-- + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +entity T80_ALU is + generic( + Mode : integer := 0; + Flag_C : integer := 0; + Flag_N : integer := 1; + Flag_P : integer := 2; + Flag_X : integer := 3; + Flag_H : integer := 4; + Flag_Y : integer := 5; + Flag_Z : integer := 6; + Flag_S : integer := 7 + ); + port( + Arith16 : in std_logic; + Z16 : in std_logic; + ALU_Op : in std_logic_vector(3 downto 0); + IR : in std_logic_vector(5 downto 0); + ISet : in std_logic_vector(1 downto 0); + BusA : in std_logic_vector(7 downto 0); + BusB : in std_logic_vector(7 downto 0); + F_In : in std_logic_vector(7 downto 0); + Q : out std_logic_vector(7 downto 0); + F_Out : out std_logic_vector(7 downto 0) + ); +end T80_ALU; + +architecture rtl of T80_ALU is + + procedure AddSub(A : std_logic_vector; + B : std_logic_vector; + Sub : std_logic; + Carry_In : std_logic; + signal Res : out std_logic_vector; + signal Carry : out std_logic) is + + variable B_i : unsigned(A'length - 1 downto 0); + variable Res_i : unsigned(A'length + 1 downto 0); + begin + if Sub = '1' then + B_i := not unsigned(B); + else + B_i := unsigned(B); + end if; + + Res_i := unsigned("0" & A & Carry_In) + unsigned("0" & B_i & "1"); + Carry <= Res_i(A'length + 1); + Res <= std_logic_vector(Res_i(A'length downto 1)); + end; + + -- AddSub variables (temporary signals) + signal UseCarry : std_logic; + signal Carry7_v : std_logic; + signal Overflow_v : std_logic; + signal HalfCarry_v : std_logic; + signal Carry_v : std_logic; + signal Q_v : std_logic_vector(7 downto 0); + + signal BitMask : std_logic_vector(7 downto 0); + +begin + + with IR(5 downto 3) select BitMask <= "00000001" when "000", + "00000010" when "001", + "00000100" when "010", + "00001000" when "011", + "00010000" when "100", + "00100000" when "101", + "01000000" when "110", + "10000000" when others; + + UseCarry <= not ALU_Op(2) and ALU_Op(0); + AddSub(BusA(3 downto 0), BusB(3 downto 0), ALU_Op(1), ALU_Op(1) xor (UseCarry and F_In(Flag_C)), Q_v(3 downto 0), HalfCarry_v); + AddSub(BusA(6 downto 4), BusB(6 downto 4), ALU_Op(1), HalfCarry_v, Q_v(6 downto 4), Carry7_v); + AddSub(BusA(7 downto 7), BusB(7 downto 7), ALU_Op(1), Carry7_v, Q_v(7 downto 7), Carry_v); + + -- bug fix - parity flag is just parity for 8080, also overflow for Z80 + process (Carry_v, Carry7_v, Q_v) + begin + if(Mode=2) then + OverFlow_v <= not (Q_v(0) xor Q_v(1) xor Q_v(2) xor Q_v(3) xor + Q_v(4) xor Q_v(5) xor Q_v(6) xor Q_v(7)); else + OverFlow_v <= Carry_v xor Carry7_v; + end if; + end process; + + process (Arith16, ALU_OP, F_In, BusA, BusB, IR, Q_v, Carry_v, HalfCarry_v, OverFlow_v, BitMask, ISet, Z16) + variable Q_t : std_logic_vector(7 downto 0); + variable DAA_Q : unsigned(8 downto 0); + begin + Q_t := "--------"; + F_Out <= F_In; + DAA_Q := "---------"; + case ALU_Op is + when "0000" | "0001" | "0010" | "0011" | "0100" | "0101" | "0110" | "0111" => + F_Out(Flag_N) <= '0'; + F_Out(Flag_C) <= '0'; + case ALU_OP(2 downto 0) is + when "000" | "001" => -- ADD, ADC + Q_t := Q_v; + F_Out(Flag_C) <= Carry_v; + F_Out(Flag_H) <= HalfCarry_v; + F_Out(Flag_P) <= OverFlow_v; + when "010" | "011" | "111" => -- SUB, SBC, CP + Q_t := Q_v; + F_Out(Flag_N) <= '1'; + F_Out(Flag_C) <= not Carry_v; + F_Out(Flag_H) <= not HalfCarry_v; + F_Out(Flag_P) <= OverFlow_v; + when "100" => -- AND + Q_t(7 downto 0) := BusA and BusB; + F_Out(Flag_H) <= '1'; + when "101" => -- XOR + Q_t(7 downto 0) := BusA xor BusB; + F_Out(Flag_H) <= '0'; + when others => -- OR "110" + Q_t(7 downto 0) := BusA or BusB; + F_Out(Flag_H) <= '0'; + end case; + if ALU_Op(2 downto 0) = "111" then -- CP + F_Out(Flag_X) <= BusB(3); + F_Out(Flag_Y) <= BusB(5); + else + F_Out(Flag_X) <= Q_t(3); + F_Out(Flag_Y) <= Q_t(5); + end if; + if Q_t(7 downto 0) = "00000000" then + F_Out(Flag_Z) <= '1'; + if Z16 = '1' then + F_Out(Flag_Z) <= F_In(Flag_Z); -- 16 bit ADC,SBC + end if; + else + F_Out(Flag_Z) <= '0'; + end if; + F_Out(Flag_S) <= Q_t(7); + case ALU_Op(2 downto 0) is + when "000" | "001" | "010" | "011" | "111" => -- ADD, ADC, SUB, SBC, CP + when others => + F_Out(Flag_P) <= not (Q_t(0) xor Q_t(1) xor Q_t(2) xor Q_t(3) xor + Q_t(4) xor Q_t(5) xor Q_t(6) xor Q_t(7)); + end case; + if Arith16 = '1' then + F_Out(Flag_S) <= F_In(Flag_S); + F_Out(Flag_Z) <= F_In(Flag_Z); + F_Out(Flag_P) <= F_In(Flag_P); + end if; + when "1100" => + -- DAA + F_Out(Flag_H) <= F_In(Flag_H); + F_Out(Flag_C) <= F_In(Flag_C); + DAA_Q(7 downto 0) := unsigned(BusA); + DAA_Q(8) := '0'; + if F_In(Flag_N) = '0' then + -- After addition + -- Alow > 9 or H = 1 + if DAA_Q(3 downto 0) > 9 or F_In(Flag_H) = '1' then + if (DAA_Q(3 downto 0) > 9) then + F_Out(Flag_H) <= '1'; + else + F_Out(Flag_H) <= '0'; + end if; + DAA_Q := DAA_Q + 6; + end if; + -- new Ahigh > 9 or C = 1 + if DAA_Q(8 downto 4) > 9 or F_In(Flag_C) = '1' then + DAA_Q := DAA_Q + 96; -- 0x60 + end if; + else + -- After subtraction + if DAA_Q(3 downto 0) > 9 or F_In(Flag_H) = '1' then + if DAA_Q(3 downto 0) > 5 then + F_Out(Flag_H) <= '0'; + end if; + DAA_Q(7 downto 0) := DAA_Q(7 downto 0) - 6; + end if; + if unsigned(BusA) > 153 or F_In(Flag_C) = '1' then + DAA_Q := DAA_Q - 352; -- 0x160 + end if; + end if; + F_Out(Flag_X) <= DAA_Q(3); + F_Out(Flag_Y) <= DAA_Q(5); + F_Out(Flag_C) <= F_In(Flag_C) or DAA_Q(8); + Q_t := std_logic_vector(DAA_Q(7 downto 0)); + if DAA_Q(7 downto 0) = "00000000" then + F_Out(Flag_Z) <= '1'; + else + F_Out(Flag_Z) <= '0'; + end if; + F_Out(Flag_S) <= DAA_Q(7); + F_Out(Flag_P) <= not (DAA_Q(0) xor DAA_Q(1) xor DAA_Q(2) xor DAA_Q(3) xor + DAA_Q(4) xor DAA_Q(5) xor DAA_Q(6) xor DAA_Q(7)); + when "1101" | "1110" => + -- RLD, RRD + Q_t(7 downto 4) := BusA(7 downto 4); + if ALU_Op(0) = '1' then + Q_t(3 downto 0) := BusB(7 downto 4); + else + Q_t(3 downto 0) := BusB(3 downto 0); + end if; + F_Out(Flag_H) <= '0'; + F_Out(Flag_N) <= '0'; + F_Out(Flag_X) <= Q_t(3); + F_Out(Flag_Y) <= Q_t(5); + if Q_t(7 downto 0) = "00000000" then + F_Out(Flag_Z) <= '1'; + else + F_Out(Flag_Z) <= '0'; + end if; + F_Out(Flag_S) <= Q_t(7); + F_Out(Flag_P) <= not (Q_t(0) xor Q_t(1) xor Q_t(2) xor Q_t(3) xor + Q_t(4) xor Q_t(5) xor Q_t(6) xor Q_t(7)); + when "1001" => + -- BIT + Q_t(7 downto 0) := BusB and BitMask; + F_Out(Flag_S) <= Q_t(7); + if Q_t(7 downto 0) = "00000000" then + F_Out(Flag_Z) <= '1'; + F_Out(Flag_P) <= '1'; + else + F_Out(Flag_Z) <= '0'; + F_Out(Flag_P) <= '0'; + end if; + F_Out(Flag_H) <= '1'; + F_Out(Flag_N) <= '0'; + F_Out(Flag_X) <= '0'; + F_Out(Flag_Y) <= '0'; + if IR(2 downto 0) /= "110" then + F_Out(Flag_X) <= BusB(3); + F_Out(Flag_Y) <= BusB(5); + end if; + when "1010" => + -- SET + Q_t(7 downto 0) := BusB or BitMask; + when "1011" => + -- RES + Q_t(7 downto 0) := BusB and not BitMask; + when "1000" => + -- ROT + case IR(5 downto 3) is + when "000" => -- RLC + Q_t(7 downto 1) := BusA(6 downto 0); + Q_t(0) := BusA(7); + F_Out(Flag_C) <= BusA(7); + when "010" => -- RL + Q_t(7 downto 1) := BusA(6 downto 0); + Q_t(0) := F_In(Flag_C); + F_Out(Flag_C) <= BusA(7); + when "001" => -- RRC + Q_t(6 downto 0) := BusA(7 downto 1); + Q_t(7) := BusA(0); + F_Out(Flag_C) <= BusA(0); + when "011" => -- RR + Q_t(6 downto 0) := BusA(7 downto 1); + Q_t(7) := F_In(Flag_C); + F_Out(Flag_C) <= BusA(0); + when "100" => -- SLA + Q_t(7 downto 1) := BusA(6 downto 0); + Q_t(0) := '0'; + F_Out(Flag_C) <= BusA(7); + when "110" => -- SLL (Undocumented) / SWAP + if Mode = 3 then + Q_t(7 downto 4) := BusA(3 downto 0); + Q_t(3 downto 0) := BusA(7 downto 4); + F_Out(Flag_C) <= '0'; + else + Q_t(7 downto 1) := BusA(6 downto 0); + Q_t(0) := '1'; + F_Out(Flag_C) <= BusA(7); + end if; + when "101" => -- SRA + Q_t(6 downto 0) := BusA(7 downto 1); + Q_t(7) := BusA(7); + F_Out(Flag_C) <= BusA(0); + when others => -- SRL + Q_t(6 downto 0) := BusA(7 downto 1); + Q_t(7) := '0'; + F_Out(Flag_C) <= BusA(0); + end case; + F_Out(Flag_H) <= '0'; + F_Out(Flag_N) <= '0'; + F_Out(Flag_X) <= Q_t(3); + F_Out(Flag_Y) <= Q_t(5); + F_Out(Flag_S) <= Q_t(7); + if Q_t(7 downto 0) = "00000000" then + F_Out(Flag_Z) <= '1'; + else + F_Out(Flag_Z) <= '0'; + end if; + F_Out(Flag_P) <= not (Q_t(0) xor Q_t(1) xor Q_t(2) xor Q_t(3) xor + Q_t(4) xor Q_t(5) xor Q_t(6) xor Q_t(7)); + if ISet = "00" then + F_Out(Flag_P) <= F_In(Flag_P); + F_Out(Flag_S) <= F_In(Flag_S); + F_Out(Flag_Z) <= F_In(Flag_Z); + end if; + when others => + null; + end case; + Q <= Q_t; + end process; +end; diff --git a/vhdl/T80_MCode.vhd b/vhdl/T80_MCode.vhd new file mode 100644 index 0000000..4c15b9b --- /dev/null +++ b/vhdl/T80_MCode.vhd @@ -0,0 +1,2028 @@ +-- **** +-- T80(b) core. In an effort to merge and maintain bug fixes .... +-- +-- +-- Ver 303 add undocumented DDCB and FDCB opcodes by TobiFlex 20.04.2010 +-- Ver 302 fixed IO cycle timing, tested thanks to Alessandro. +-- Ver 300 started tidyup +-- MikeJ March 2005 +-- Latest version from www.fpgaarcade.com (original www.opencores.org) +-- +-- **** +-- +-- Z80 compatible microprocessor core +-- +-- Version : 0242 +-- +-- Copyright (c) 2001-2002 Daniel Wallner (jesus@opencores.org) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- The latest version of this file can be found at: +-- http://www.opencores.org/cvsweb.shtml/t80/ +-- +-- Limitations : +-- +-- File history : +-- +-- 0208 : First complete release +-- +-- 0211 : Fixed IM 1 +-- +-- 0214 : Fixed mostly flags, only the block instructions now fail the zex regression test +-- +-- 0235 : Added IM 2 fix by Mike Johnson +-- +-- 0238 : Added NoRead signal +-- +-- 0238b: Fixed instruction timing for POP and DJNZ +-- +-- 0240 : Added (IX/IY+d) states, removed op-codes from mode 2 and added all remaining mode 3 op-codes + +-- 0240mj1 fix for HL inc/dec for INI, IND, INIR, INDR, OUTI, OUTD, OTIR, OTDR +-- +-- 0242 : Fixed I/O instruction timing, cleanup +-- + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use work.T80_Pack.all; + +entity T80_MCode is + generic( + Mode : integer := 0; + Flag_C : integer := 0; + Flag_N : integer := 1; + Flag_P : integer := 2; + Flag_X : integer := 3; + Flag_H : integer := 4; + Flag_Y : integer := 5; + Flag_Z : integer := 6; + Flag_S : integer := 7 + ); + port( + IR : in std_logic_vector(7 downto 0); + ISet : in std_logic_vector(1 downto 0); + MCycle : in std_logic_vector(2 downto 0); + F : in std_logic_vector(7 downto 0); + NMICycle : in std_logic; + IntCycle : in std_logic; + XY_State : in std_logic_vector(1 downto 0); + MCycles : out std_logic_vector(2 downto 0); + TStates : out std_logic_vector(2 downto 0); + Prefix : out std_logic_vector(1 downto 0); -- None,CB,ED,DD/FD + Inc_PC : out std_logic; + Inc_WZ : out std_logic; + IncDec_16 : out std_logic_vector(3 downto 0); -- BC,DE,HL,SP 0 is inc + Read_To_Reg : out std_logic; + Read_To_Acc : out std_logic; + Set_BusA_To : out std_logic_vector(3 downto 0); -- B,C,D,E,H,L,DI/DB,A,SP(L),SP(M),0,F + Set_BusB_To : out std_logic_vector(3 downto 0); -- B,C,D,E,H,L,DI,A,SP(L),SP(M),1,F,PC(L),PC(M),0 + ALU_Op : out std_logic_vector(3 downto 0); + -- ADD, ADC, SUB, SBC, AND, XOR, OR, CP, ROT, BIT, SET, RES, DAA, RLD, RRD, None + Save_ALU : out std_logic; + PreserveC : out std_logic; + Arith16 : out std_logic; + Set_Addr_To : out std_logic_vector(2 downto 0); -- aNone,aXY,aIOA,aSP,aBC,aDE,aZI + IORQ : out std_logic; + Jump : out std_logic; + JumpE : out std_logic; + JumpXY : out std_logic; + Call : out std_logic; + RstP : out std_logic; + LDZ : out std_logic; + LDW : out std_logic; + LDSPHL : out std_logic; + Special_LD : out std_logic_vector(2 downto 0); -- A,I;A,R;I,A;R,A;None + ExchangeDH : out std_logic; + ExchangeRp : out std_logic; + ExchangeAF : out std_logic; + ExchangeRS : out std_logic; + I_DJNZ : out std_logic; + I_CPL : out std_logic; + I_CCF : out std_logic; + I_SCF : out std_logic; + I_RETN : out std_logic; + I_BT : out std_logic; + I_BC : out std_logic; + I_BTR : out std_logic; + I_RLD : out std_logic; + I_RRD : out std_logic; + I_INRC : out std_logic; + SetDI : out std_logic; + SetEI : out std_logic; + IMode : out std_logic_vector(1 downto 0); + Halt : out std_logic; + NoRead : out std_logic; + Write : out std_logic; + XYbit_undoc : out std_logic + ); +end T80_MCode; + +architecture rtl of T80_MCode is + + constant aNone : std_logic_vector(2 downto 0) := "111"; + constant aBC : std_logic_vector(2 downto 0) := "000"; + constant aDE : std_logic_vector(2 downto 0) := "001"; + constant aXY : std_logic_vector(2 downto 0) := "010"; + constant aIOA : std_logic_vector(2 downto 0) := "100"; + constant aSP : std_logic_vector(2 downto 0) := "101"; + constant aZI : std_logic_vector(2 downto 0) := "110"; + + function is_cc_true( + F : std_logic_vector(7 downto 0); + cc : bit_vector(2 downto 0) + ) return boolean is + begin + if Mode = 3 then + case cc is + when "000" => return F(7) = '0'; -- NZ + when "001" => return F(7) = '1'; -- Z + when "010" => return F(4) = '0'; -- NC + when "011" => return F(4) = '1'; -- C + when "100" => return false; + when "101" => return false; + when "110" => return false; + when "111" => return false; + end case; + else + case cc is + when "000" => return F(6) = '0'; -- NZ + when "001" => return F(6) = '1'; -- Z + when "010" => return F(0) = '0'; -- NC + when "011" => return F(0) = '1'; -- C + when "100" => return F(2) = '0'; -- PO + when "101" => return F(2) = '1'; -- PE + when "110" => return F(7) = '0'; -- P + when "111" => return F(7) = '1'; -- M + end case; + end if; + end; + +begin + + process (IR, ISet, MCycle, F, NMICycle, IntCycle) + variable DDD : std_logic_vector(2 downto 0); + variable SSS : std_logic_vector(2 downto 0); + variable DPair : std_logic_vector(1 downto 0); + variable IRB : bit_vector(7 downto 0); + begin + DDD := IR(5 downto 3); + SSS := IR(2 downto 0); + DPair := IR(5 downto 4); + IRB := to_bitvector(IR); + + MCycles <= "001"; + if MCycle = "001" then + TStates <= "100"; + else + TStates <= "011"; + end if; + Prefix <= "00"; + Inc_PC <= '0'; + Inc_WZ <= '0'; + IncDec_16 <= "0000"; + Read_To_Acc <= '0'; + Read_To_Reg <= '0'; + Set_BusB_To <= "0000"; + Set_BusA_To <= "0000"; + ALU_Op <= "0" & IR(5 downto 3); + Save_ALU <= '0'; + PreserveC <= '0'; + Arith16 <= '0'; + IORQ <= '0'; + Set_Addr_To <= aNone; + Jump <= '0'; + JumpE <= '0'; + JumpXY <= '0'; + Call <= '0'; + RstP <= '0'; + LDZ <= '0'; + LDW <= '0'; + LDSPHL <= '0'; + Special_LD <= "000"; + ExchangeDH <= '0'; + ExchangeRp <= '0'; + ExchangeAF <= '0'; + ExchangeRS <= '0'; + I_DJNZ <= '0'; + I_CPL <= '0'; + I_CCF <= '0'; + I_SCF <= '0'; + I_RETN <= '0'; + I_BT <= '0'; + I_BC <= '0'; + I_BTR <= '0'; + I_RLD <= '0'; + I_RRD <= '0'; + I_INRC <= '0'; + SetDI <= '0'; + SetEI <= '0'; + IMode <= "11"; + Halt <= '0'; + NoRead <= '0'; + Write <= '0'; + XYbit_undoc <= '0'; + + case ISet is + when "00" => + +------------------------------------------------------------------------------ +-- +-- Unprefixed instructions +-- +------------------------------------------------------------------------------ + + case IRB is +-- 8 BIT LOAD GROUP + when "01000000"|"01000001"|"01000010"|"01000011"|"01000100"|"01000101"|"01000111" + |"01001000"|"01001001"|"01001010"|"01001011"|"01001100"|"01001101"|"01001111" + |"01010000"|"01010001"|"01010010"|"01010011"|"01010100"|"01010101"|"01010111" + |"01011000"|"01011001"|"01011010"|"01011011"|"01011100"|"01011101"|"01011111" + |"01100000"|"01100001"|"01100010"|"01100011"|"01100100"|"01100101"|"01100111" + |"01101000"|"01101001"|"01101010"|"01101011"|"01101100"|"01101101"|"01101111" + |"01111000"|"01111001"|"01111010"|"01111011"|"01111100"|"01111101"|"01111111" => + -- LD r,r' + Set_BusB_To(2 downto 0) <= SSS; + ExchangeRp <= '1'; + Set_BusA_To(2 downto 0) <= DDD; + Read_To_Reg <= '1'; + when "00000110"|"00001110"|"00010110"|"00011110"|"00100110"|"00101110"|"00111110" => + -- LD r,n + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + Set_BusA_To(2 downto 0) <= DDD; + Read_To_Reg <= '1'; + when others => null; + end case; + when "01000110"|"01001110"|"01010110"|"01011110"|"01100110"|"01101110"|"01111110" => + -- LD r,(HL) + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aXY; + when 2 => + Set_BusA_To(2 downto 0) <= DDD; + Read_To_Reg <= '1'; + when others => null; + end case; + when "01110000"|"01110001"|"01110010"|"01110011"|"01110100"|"01110101"|"01110111" => + -- LD (HL),r + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aXY; + Set_BusB_To(2 downto 0) <= SSS; + Set_BusB_To(3) <= '0'; + when 2 => + Write <= '1'; + when others => null; + end case; + when "00110110" => + -- LD (HL),n + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + Set_Addr_To <= aXY; + Set_BusB_To(2 downto 0) <= SSS; + Set_BusB_To(3) <= '0'; + when 3 => + Write <= '1'; + when others => null; + end case; + when "00001010" => + -- LD A,(BC) + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aBC; + when 2 => + Read_To_Acc <= '1'; + when others => null; + end case; + when "00011010" => + -- LD A,(DE) + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aDE; + when 2 => + Read_To_Acc <= '1'; + when others => null; + end case; + when "00111010" => + if Mode = 3 then + -- LDD A,(HL) + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aXY; + when 2 => + Read_To_Acc <= '1'; + IncDec_16 <= "1110"; + when others => null; + end case; + else + -- LD A,(nn) + MCycles <= "100"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + Set_Addr_To <= aZI; + Inc_PC <= '1'; + when 4 => + Read_To_Acc <= '1'; + when others => null; + end case; + end if; + when "00000010" => + -- LD (BC),A + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aBC; + Set_BusB_To <= "0111"; + when 2 => + Write <= '1'; + when others => null; + end case; + when "00010010" => + -- LD (DE),A + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aDE; + Set_BusB_To <= "0111"; + when 2 => + Write <= '1'; + when others => null; + end case; + when "00110010" => + if Mode = 3 then + -- LDD (HL),A + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aXY; + Set_BusB_To <= "0111"; + when 2 => + Write <= '1'; + IncDec_16 <= "1110"; + when others => null; + end case; + else + -- LD (nn),A + MCycles <= "100"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + Set_Addr_To <= aZI; + Inc_PC <= '1'; + Set_BusB_To <= "0111"; + when 4 => + Write <= '1'; + when others => null; + end case; + end if; + +-- 16 BIT LOAD GROUP + when "00000001"|"00010001"|"00100001"|"00110001" => + -- LD dd,nn + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + Read_To_Reg <= '1'; + if DPAIR = "11" then + Set_BusA_To(3 downto 0) <= "1000"; + else + Set_BusA_To(2 downto 1) <= DPAIR; + Set_BusA_To(0) <= '1'; + end if; + when 3 => + Inc_PC <= '1'; + Read_To_Reg <= '1'; + if DPAIR = "11" then + Set_BusA_To(3 downto 0) <= "1001"; + else + Set_BusA_To(2 downto 1) <= DPAIR; + Set_BusA_To(0) <= '0'; + end if; + when others => null; + end case; + when "00101010" => + if Mode = 3 then + -- LDI A,(HL) + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aXY; + when 2 => + Read_To_Acc <= '1'; + IncDec_16 <= "0110"; + when others => null; + end case; + else + -- LD HL,(nn) + MCycles <= "101"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + Set_Addr_To <= aZI; + Inc_PC <= '1'; + LDW <= '1'; + when 4 => + Set_BusA_To(2 downto 0) <= "101"; -- L + Read_To_Reg <= '1'; + Inc_WZ <= '1'; + Set_Addr_To <= aZI; + when 5 => + Set_BusA_To(2 downto 0) <= "100"; -- H + Read_To_Reg <= '1'; + when others => null; + end case; + end if; + when "00100010" => + if Mode = 3 then + -- LDI (HL),A + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aXY; + Set_BusB_To <= "0111"; + when 2 => + Write <= '1'; + IncDec_16 <= "0110"; + when others => null; + end case; + else + -- LD (nn),HL + MCycles <= "101"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + Set_Addr_To <= aZI; + Inc_PC <= '1'; + LDW <= '1'; + Set_BusB_To <= "0101"; -- L + when 4 => + Inc_WZ <= '1'; + Set_Addr_To <= aZI; + Write <= '1'; + Set_BusB_To <= "0100"; -- H + when 5 => + Write <= '1'; + when others => null; + end case; + end if; + when "11111001" => + -- LD SP,HL + TStates <= "110"; + LDSPHL <= '1'; + when "11000101"|"11010101"|"11100101"|"11110101" => + -- PUSH qq + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 => + TStates <= "101"; + IncDec_16 <= "1111"; + Set_Addr_TO <= aSP; + if DPAIR = "11" then + Set_BusB_To <= "0111"; + else + Set_BusB_To(2 downto 1) <= DPAIR; + Set_BusB_To(0) <= '0'; + Set_BusB_To(3) <= '0'; + end if; + when 2 => + IncDec_16 <= "1111"; + Set_Addr_To <= aSP; + if DPAIR = "11" then + Set_BusB_To <= "1011"; + else + Set_BusB_To(2 downto 1) <= DPAIR; + Set_BusB_To(0) <= '1'; + Set_BusB_To(3) <= '0'; + end if; + Write <= '1'; + when 3 => + Write <= '1'; + when others => null; + end case; + when "11000001"|"11010001"|"11100001"|"11110001" => + -- POP qq + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aSP; + when 2 => + IncDec_16 <= "0111"; + Set_Addr_To <= aSP; + Read_To_Reg <= '1'; + if DPAIR = "11" then + Set_BusA_To(3 downto 0) <= "1011"; + else + Set_BusA_To(2 downto 1) <= DPAIR; + Set_BusA_To(0) <= '1'; + end if; + when 3 => + IncDec_16 <= "0111"; + Read_To_Reg <= '1'; + if DPAIR = "11" then + Set_BusA_To(3 downto 0) <= "0111"; + else + Set_BusA_To(2 downto 1) <= DPAIR; + Set_BusA_To(0) <= '0'; + end if; + when others => null; + end case; + +-- EXCHANGE, BLOCK TRANSFER AND SEARCH GROUP + when "11101011" => + if Mode /= 3 then + -- EX DE,HL + ExchangeDH <= '1'; + end if; + when "00001000" => + if Mode = 3 then + -- LD (nn),SP + MCycles <= "101"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + Set_Addr_To <= aZI; + Inc_PC <= '1'; + LDW <= '1'; + Set_BusB_To <= "1000"; + when 4 => + Inc_WZ <= '1'; + Set_Addr_To <= aZI; + Write <= '1'; + Set_BusB_To <= "1001"; + when 5 => + Write <= '1'; + when others => null; + end case; + elsif Mode < 2 then + -- EX AF,AF' + ExchangeAF <= '1'; + end if; + when "11011001" => + if Mode = 3 then + -- RETI + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_TO <= aSP; + when 2 => + IncDec_16 <= "0111"; + Set_Addr_To <= aSP; + LDZ <= '1'; + when 3 => + Jump <= '1'; + IncDec_16 <= "0111"; + I_RETN <= '1'; + SetEI <= '1'; + when others => null; + end case; + elsif Mode < 2 then + -- EXX + ExchangeRS <= '1'; + end if; + when "11100011" => + if Mode /= 3 then + -- EX (SP),HL + MCycles <= "101"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aSP; + when 2 => + Read_To_Reg <= '1'; + Set_BusA_To <= "0101"; + Set_BusB_To <= "0101"; + Set_Addr_To <= aSP; + when 3 => + IncDec_16 <= "0111"; + Set_Addr_To <= aSP; + TStates <= "100"; + Write <= '1'; + when 4 => + Read_To_Reg <= '1'; + Set_BusA_To <= "0100"; + Set_BusB_To <= "0100"; + Set_Addr_To <= aSP; + when 5 => + IncDec_16 <= "1111"; + TStates <= "101"; + Write <= '1'; + when others => null; + end case; + end if; + +-- 8 BIT ARITHMETIC AND LOGICAL GROUP + when "10000000"|"10000001"|"10000010"|"10000011"|"10000100"|"10000101"|"10000111" + |"10001000"|"10001001"|"10001010"|"10001011"|"10001100"|"10001101"|"10001111" + |"10010000"|"10010001"|"10010010"|"10010011"|"10010100"|"10010101"|"10010111" + |"10011000"|"10011001"|"10011010"|"10011011"|"10011100"|"10011101"|"10011111" + |"10100000"|"10100001"|"10100010"|"10100011"|"10100100"|"10100101"|"10100111" + |"10101000"|"10101001"|"10101010"|"10101011"|"10101100"|"10101101"|"10101111" + |"10110000"|"10110001"|"10110010"|"10110011"|"10110100"|"10110101"|"10110111" + |"10111000"|"10111001"|"10111010"|"10111011"|"10111100"|"10111101"|"10111111" => + -- ADD A,r + -- ADC A,r + -- SUB A,r + -- SBC A,r + -- AND A,r + -- OR A,r + -- XOR A,r + -- CP A,r + Set_BusB_To(2 downto 0) <= SSS; + Set_BusA_To(2 downto 0) <= "111"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + when "10000110"|"10001110"|"10010110"|"10011110"|"10100110"|"10101110"|"10110110"|"10111110" => + -- ADD A,(HL) + -- ADC A,(HL) + -- SUB A,(HL) + -- SBC A,(HL) + -- AND A,(HL) + -- OR A,(HL) + -- XOR A,(HL) + -- CP A,(HL) + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aXY; + when 2 => + Read_To_Reg <= '1'; + Save_ALU <= '1'; + Set_BusB_To(2 downto 0) <= SSS; + Set_BusA_To(2 downto 0) <= "111"; + when others => null; + end case; + when "11000110"|"11001110"|"11010110"|"11011110"|"11100110"|"11101110"|"11110110"|"11111110" => + -- ADD A,n + -- ADC A,n + -- SUB A,n + -- SBC A,n + -- AND A,n + -- OR A,n + -- XOR A,n + -- CP A,n + MCycles <= "010"; + if MCycle = "010" then + Inc_PC <= '1'; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + Set_BusB_To(2 downto 0) <= SSS; + Set_BusA_To(2 downto 0) <= "111"; + end if; + when "00000100"|"00001100"|"00010100"|"00011100"|"00100100"|"00101100"|"00111100" => + -- INC r + Set_BusB_To <= "1010"; + Set_BusA_To(2 downto 0) <= DDD; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + PreserveC <= '1'; + ALU_Op <= "0000"; + when "00110100" => + -- INC (HL) + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aXY; + when 2 => + TStates <= "100"; + Set_Addr_To <= aXY; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + PreserveC <= '1'; + ALU_Op <= "0000"; + Set_BusB_To <= "1010"; + Set_BusA_To(2 downto 0) <= DDD; + when 3 => + Write <= '1'; + when others => null; + end case; + when "00000101"|"00001101"|"00010101"|"00011101"|"00100101"|"00101101"|"00111101" => + -- DEC r + Set_BusB_To <= "1010"; + Set_BusA_To(2 downto 0) <= DDD; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + PreserveC <= '1'; + ALU_Op <= "0010"; + when "00110101" => + -- DEC (HL) + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aXY; + when 2 => + TStates <= "100"; + Set_Addr_To <= aXY; + ALU_Op <= "0010"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + PreserveC <= '1'; + Set_BusB_To <= "1010"; + Set_BusA_To(2 downto 0) <= DDD; + when 3 => + Write <= '1'; + when others => null; + end case; + +-- GENERAL PURPOSE ARITHMETIC AND CPU CONTROL GROUPS + when "00100111" => + -- DAA + Set_BusA_To(2 downto 0) <= "111"; + Read_To_Reg <= '1'; + ALU_Op <= "1100"; + Save_ALU <= '1'; + when "00101111" => + -- CPL + I_CPL <= '1'; + when "00111111" => + -- CCF + I_CCF <= '1'; + when "00110111" => + -- SCF + I_SCF <= '1'; + when "00000000" => + if NMICycle = '1' then + -- NMI + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 => + TStates <= "101"; + IncDec_16 <= "1111"; + Set_Addr_To <= aSP; + Set_BusB_To <= "1101"; + when 2 => + TStates <= "100"; + Write <= '1'; + IncDec_16 <= "1111"; + Set_Addr_To <= aSP; + Set_BusB_To <= "1100"; + when 3 => + TStates <= "100"; + Write <= '1'; + when others => null; + end case; + elsif IntCycle = '1' then + -- INT (IM 2) + MCycles <= "101"; + case to_integer(unsigned(MCycle)) is + when 1 => + LDZ <= '1'; + TStates <= "101"; + IncDec_16 <= "1111"; + Set_Addr_To <= aSP; + Set_BusB_To <= "1101"; + when 2 => + TStates <= "100"; + Write <= '1'; + IncDec_16 <= "1111"; + Set_Addr_To <= aSP; + Set_BusB_To <= "1100"; + when 3 => + TStates <= "100"; + Write <= '1'; + when 4 => + Inc_PC <= '1'; + LDZ <= '1'; + when 5 => + Jump <= '1'; + when others => null; + end case; + else + -- NOP + end if; + when "01110110" => + -- HALT + Halt <= '1'; + when "11110011" => + -- DI + SetDI <= '1'; + when "11111011" => + -- EI + SetEI <= '1'; + +-- 16 BIT ARITHMETIC GROUP + when "00001001"|"00011001"|"00101001"|"00111001" => + -- ADD HL,ss + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + NoRead <= '1'; + ALU_Op <= "0000"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + Set_BusA_To(2 downto 0) <= "101"; + case to_integer(unsigned(IR(5 downto 4))) is + when 0|1|2 => + Set_BusB_To(2 downto 1) <= IR(5 downto 4); + Set_BusB_To(0) <= '1'; + when others => + Set_BusB_To <= "1000"; + end case; + TStates <= "100"; + Arith16 <= '1'; + when 3 => + NoRead <= '1'; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + ALU_Op <= "0001"; + Set_BusA_To(2 downto 0) <= "100"; + case to_integer(unsigned(IR(5 downto 4))) is + when 0|1|2 => + Set_BusB_To(2 downto 1) <= IR(5 downto 4); + when others => + Set_BusB_To <= "1001"; + end case; + Arith16 <= '1'; + when others => + end case; + when "00000011"|"00010011"|"00100011"|"00110011" => + -- INC ss + TStates <= "110"; + IncDec_16(3 downto 2) <= "01"; + IncDec_16(1 downto 0) <= DPair; + when "00001011"|"00011011"|"00101011"|"00111011" => + -- DEC ss + TStates <= "110"; + IncDec_16(3 downto 2) <= "11"; + IncDec_16(1 downto 0) <= DPair; + +-- ROTATE AND SHIFT GROUP + when "00000111" + -- RLCA + |"00010111" + -- RLA + |"00001111" + -- RRCA + |"00011111" => + -- RRA + Set_BusA_To(2 downto 0) <= "111"; + ALU_Op <= "1000"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + +-- JUMP GROUP + when "11000011" => + -- JP nn + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + Inc_PC <= '1'; + Jump <= '1'; + when others => null; + end case; + when "11000010"|"11001010"|"11010010"|"11011010"|"11100010"|"11101010"|"11110010"|"11111010" => + if IR(5) = '1' and Mode = 3 then + case IRB(4 downto 3) is + when "00" => + -- LD ($FF00+C),A + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aBC; + Set_BusB_To <= "0111"; + when 2 => + Write <= '1'; + IORQ <= '1'; + when others => + end case; + when "01" => + -- LD (nn),A + MCycles <= "100"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + Set_Addr_To <= aZI; + Inc_PC <= '1'; + Set_BusB_To <= "0111"; + when 4 => + Write <= '1'; + when others => null; + end case; + when "10" => + -- LD A,($FF00+C) + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aBC; + when 2 => + Read_To_Acc <= '1'; + IORQ <= '1'; + when others => + end case; + when "11" => + -- LD A,(nn) + MCycles <= "100"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + Set_Addr_To <= aZI; + Inc_PC <= '1'; + when 4 => + Read_To_Acc <= '1'; + when others => null; + end case; + end case; + else + -- JP cc,nn + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + Inc_PC <= '1'; + if is_cc_true(F, to_bitvector(IR(5 downto 3))) then + Jump <= '1'; + end if; + when others => null; + end case; + end if; + when "00011000" => + if Mode /= 2 then + -- JR e + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + when 3 => + NoRead <= '1'; + JumpE <= '1'; + TStates <= "101"; + when others => null; + end case; + end if; + when "00111000" => + if Mode /= 2 then + -- JR C,e + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + if F(Flag_C) = '0' then + MCycles <= "010"; + end if; + when 3 => + NoRead <= '1'; + JumpE <= '1'; + TStates <= "101"; + when others => null; + end case; + end if; + when "00110000" => + if Mode /= 2 then + -- JR NC,e + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + if F(Flag_C) = '1' then + MCycles <= "010"; + end if; + when 3 => + NoRead <= '1'; + JumpE <= '1'; + TStates <= "101"; + when others => null; + end case; + end if; + when "00101000" => + if Mode /= 2 then + -- JR Z,e + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + if F(Flag_Z) = '0' then + MCycles <= "010"; + end if; + when 3 => + NoRead <= '1'; + JumpE <= '1'; + TStates <= "101"; + when others => null; + end case; + end if; + when "00100000" => + if Mode /= 2 then + -- JR NZ,e + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + if F(Flag_Z) = '1' then + MCycles <= "010"; + end if; + when 3 => + NoRead <= '1'; + JumpE <= '1'; + TStates <= "101"; + when others => null; + end case; + end if; + when "11101001" => + -- JP (HL) + JumpXY <= '1'; + when "00010000" => + if Mode = 3 then + I_DJNZ <= '1'; + elsif Mode < 2 then + -- DJNZ,e + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 => + TStates <= "101"; + I_DJNZ <= '1'; + Set_BusB_To <= "1010"; + Set_BusA_To(2 downto 0) <= "000"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + ALU_Op <= "0010"; + when 2 => + I_DJNZ <= '1'; + Inc_PC <= '1'; + when 3 => + NoRead <= '1'; + JumpE <= '1'; + TStates <= "101"; + when others => null; + end case; + end if; + +-- CALL AND RETURN GROUP + when "11001101" => + -- CALL nn + MCycles <= "101"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + IncDec_16 <= "1111"; + Inc_PC <= '1'; + TStates <= "100"; + Set_Addr_To <= aSP; + LDW <= '1'; + Set_BusB_To <= "1101"; + when 4 => + Write <= '1'; + IncDec_16 <= "1111"; + Set_Addr_To <= aSP; + Set_BusB_To <= "1100"; + when 5 => + Write <= '1'; + Call <= '1'; + when others => null; + end case; + when "11000100"|"11001100"|"11010100"|"11011100"|"11100100"|"11101100"|"11110100"|"11111100" => + if IR(5) = '0' or Mode /= 3 then + -- CALL cc,nn + MCycles <= "101"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + Inc_PC <= '1'; + LDW <= '1'; + if is_cc_true(F, to_bitvector(IR(5 downto 3))) then + IncDec_16 <= "1111"; + Set_Addr_TO <= aSP; + TStates <= "100"; + Set_BusB_To <= "1101"; + else + MCycles <= "011"; + end if; + when 4 => + Write <= '1'; + IncDec_16 <= "1111"; + Set_Addr_To <= aSP; + Set_BusB_To <= "1100"; + when 5 => + Write <= '1'; + Call <= '1'; + when others => null; + end case; + end if; + when "11001001" => + -- RET + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_TO <= aSP; + when 2 => + IncDec_16 <= "0111"; + Set_Addr_To <= aSP; + LDZ <= '1'; + when 3 => + Jump <= '1'; + IncDec_16 <= "0111"; + when others => null; + end case; + when "11000000"|"11001000"|"11010000"|"11011000"|"11100000"|"11101000"|"11110000"|"11111000" => + if IR(5) = '1' and Mode = 3 then + case IRB(4 downto 3) is + when "00" => + -- LD ($FF00+nn),A + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + Set_Addr_To <= aIOA; + Set_BusB_To <= "0111"; + when 3 => + Write <= '1'; + when others => null; + end case; + when "01" => + -- ADD SP,n + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + ALU_Op <= "0000"; + Inc_PC <= '1'; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + Set_BusA_To <= "1000"; + Set_BusB_To <= "0110"; + when 3 => + NoRead <= '1'; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + ALU_Op <= "0001"; + Set_BusA_To <= "1001"; + Set_BusB_To <= "1110"; -- Incorrect unsigned !!!!!!!!!!!!!!!!!!!!! + when others => + end case; + when "10" => + -- LD A,($FF00+nn) + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + Set_Addr_To <= aIOA; + when 3 => + Read_To_Acc <= '1'; + when others => null; + end case; + when "11" => + -- LD HL,SP+n -- Not correct !!!!!!!!!!!!!!!!!!! + MCycles <= "101"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + Set_Addr_To <= aZI; + Inc_PC <= '1'; + LDW <= '1'; + when 4 => + Set_BusA_To(2 downto 0) <= "101"; -- L + Read_To_Reg <= '1'; + Inc_WZ <= '1'; + Set_Addr_To <= aZI; + when 5 => + Set_BusA_To(2 downto 0) <= "100"; -- H + Read_To_Reg <= '1'; + when others => null; + end case; + end case; + else + -- RET cc + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 => + if is_cc_true(F, to_bitvector(IR(5 downto 3))) then + Set_Addr_TO <= aSP; + else + MCycles <= "001"; + end if; + TStates <= "101"; + when 2 => + IncDec_16 <= "0111"; + Set_Addr_To <= aSP; + LDZ <= '1'; + when 3 => + Jump <= '1'; + IncDec_16 <= "0111"; + when others => null; + end case; + end if; + when "11000111"|"11001111"|"11010111"|"11011111"|"11100111"|"11101111"|"11110111"|"11111111" => + -- RST p + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 => + TStates <= "101"; + IncDec_16 <= "1111"; + Set_Addr_To <= aSP; + Set_BusB_To <= "1101"; + when 2 => + Write <= '1'; + IncDec_16 <= "1111"; + Set_Addr_To <= aSP; + Set_BusB_To <= "1100"; + when 3 => + Write <= '1'; + RstP <= '1'; + when others => null; + end case; + +-- INPUT AND OUTPUT GROUP + when "11011011" => + if Mode /= 3 then + -- IN A,(n) + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + Set_Addr_To <= aIOA; + when 3 => + Read_To_Acc <= '1'; + IORQ <= '1'; + TStates <= "100"; -- MIKEJ should be 4 for IO cycle + when others => null; + end case; + end if; + when "11010011" => + if Mode /= 3 then + -- OUT (n),A + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + Set_Addr_To <= aIOA; + Set_BusB_To <= "0111"; + when 3 => + Write <= '1'; + IORQ <= '1'; + TStates <= "100"; -- MIKEJ should be 4 for IO cycle + when others => null; + end case; + end if; + +------------------------------------------------------------------------------ +------------------------------------------------------------------------------ +-- MULTIBYTE INSTRUCTIONS +------------------------------------------------------------------------------ +------------------------------------------------------------------------------ + + when "11001011" => + if Mode /= 2 then + Prefix <= "01"; + end if; + + when "11101101" => + if Mode < 2 then + Prefix <= "10"; + end if; + + when "11011101"|"11111101" => + if Mode < 2 then + Prefix <= "11"; + end if; + + end case; + + when "01" => + +------------------------------------------------------------------------------ +-- +-- CB prefixed instructions +-- +------------------------------------------------------------------------------ + + Set_BusA_To(2 downto 0) <= IR(2 downto 0); + Set_BusB_To(2 downto 0) <= IR(2 downto 0); + + case IRB is + when "00000000"|"00000001"|"00000010"|"00000011"|"00000100"|"00000101"|"00000111" + |"00010000"|"00010001"|"00010010"|"00010011"|"00010100"|"00010101"|"00010111" + |"00001000"|"00001001"|"00001010"|"00001011"|"00001100"|"00001101"|"00001111" + |"00011000"|"00011001"|"00011010"|"00011011"|"00011100"|"00011101"|"00011111" + |"00100000"|"00100001"|"00100010"|"00100011"|"00100100"|"00100101"|"00100111" + |"00101000"|"00101001"|"00101010"|"00101011"|"00101100"|"00101101"|"00101111" + |"00110000"|"00110001"|"00110010"|"00110011"|"00110100"|"00110101"|"00110111" + |"00111000"|"00111001"|"00111010"|"00111011"|"00111100"|"00111101"|"00111111" => + -- RLC r + -- RL r + -- RRC r + -- RR r + -- SLA r + -- SRA r + -- SRL r + -- SLL r (Undocumented) / SWAP r + if XY_State="00" then + if MCycle = "001" then + ALU_Op <= "1000"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + end if; + else + -- R/S (IX+d),Reg, undocumented + MCycles <= "011"; + XYbit_undoc <= '1'; + case to_integer(unsigned(MCycle)) is + when 1 | 7=> + Set_Addr_To <= aXY; + when 2 => + ALU_Op <= "1000"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + Set_Addr_To <= aXY; + TStates <= "100"; + when 3 => + Write <= '1'; + when others => null; + end case; + end if; + + + when "00000110"|"00010110"|"00001110"|"00011110"|"00101110"|"00111110"|"00100110"|"00110110" => + -- RLC (HL) + -- RL (HL) + -- RRC (HL) + -- RR (HL) + -- SRA (HL) + -- SRL (HL) + -- SLA (HL) + -- SLL (HL) (Undocumented) / SWAP (HL) + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 | 7 => + Set_Addr_To <= aXY; + when 2 => + ALU_Op <= "1000"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + Set_Addr_To <= aXY; + TStates <= "100"; + when 3 => + Write <= '1'; + when others => + end case; + when "01000000"|"01000001"|"01000010"|"01000011"|"01000100"|"01000101"|"01000111" + |"01001000"|"01001001"|"01001010"|"01001011"|"01001100"|"01001101"|"01001111" + |"01010000"|"01010001"|"01010010"|"01010011"|"01010100"|"01010101"|"01010111" + |"01011000"|"01011001"|"01011010"|"01011011"|"01011100"|"01011101"|"01011111" + |"01100000"|"01100001"|"01100010"|"01100011"|"01100100"|"01100101"|"01100111" + |"01101000"|"01101001"|"01101010"|"01101011"|"01101100"|"01101101"|"01101111" + |"01110000"|"01110001"|"01110010"|"01110011"|"01110100"|"01110101"|"01110111" + |"01111000"|"01111001"|"01111010"|"01111011"|"01111100"|"01111101"|"01111111" => + -- BIT b,r + if XY_State="00" then + if MCycle = "001" then + Set_BusB_To(2 downto 0) <= IR(2 downto 0); + ALU_Op <= "1001"; + end if; + else + -- BIT b,(IX+d), undocumented + MCycles <= "010"; + XYbit_undoc <= '1'; + case to_integer(unsigned(MCycle)) is + when 1 | 7=> + Set_Addr_To <= aXY; + when 2 => + ALU_Op <= "1001"; + TStates <= "100"; + when others => null; + end case; + end if; + when "01000110"|"01001110"|"01010110"|"01011110"|"01100110"|"01101110"|"01110110"|"01111110" => + -- BIT b,(HL) + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 | 7=> + Set_Addr_To <= aXY; + when 2 => + ALU_Op <= "1001"; + TStates <= "100"; + when others => null; + end case; + when "11000000"|"11000001"|"11000010"|"11000011"|"11000100"|"11000101"|"11000111" + |"11001000"|"11001001"|"11001010"|"11001011"|"11001100"|"11001101"|"11001111" + |"11010000"|"11010001"|"11010010"|"11010011"|"11010100"|"11010101"|"11010111" + |"11011000"|"11011001"|"11011010"|"11011011"|"11011100"|"11011101"|"11011111" + |"11100000"|"11100001"|"11100010"|"11100011"|"11100100"|"11100101"|"11100111" + |"11101000"|"11101001"|"11101010"|"11101011"|"11101100"|"11101101"|"11101111" + |"11110000"|"11110001"|"11110010"|"11110011"|"11110100"|"11110101"|"11110111" + |"11111000"|"11111001"|"11111010"|"11111011"|"11111100"|"11111101"|"11111111" => + -- SET b,r + if XY_State="00" then + if MCycle = "001" then + ALU_Op <= "1010"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + end if; + else + -- SET b,(IX+d),Reg, undocumented + MCycles <= "011"; + XYbit_undoc <= '1'; + case to_integer(unsigned(MCycle)) is + when 1 | 7=> + Set_Addr_To <= aXY; + when 2 => + ALU_Op <= "1010"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + Set_Addr_To <= aXY; + TStates <= "100"; + when 3 => + Write <= '1'; + when others => null; + end case; + end if; + when "11000110"|"11001110"|"11010110"|"11011110"|"11100110"|"11101110"|"11110110"|"11111110" => + -- SET b,(HL) + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 | 7=> + Set_Addr_To <= aXY; + when 2 => + ALU_Op <= "1010"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + Set_Addr_To <= aXY; + TStates <= "100"; + when 3 => + Write <= '1'; + when others => null; + end case; + when "10000000"|"10000001"|"10000010"|"10000011"|"10000100"|"10000101"|"10000111" + |"10001000"|"10001001"|"10001010"|"10001011"|"10001100"|"10001101"|"10001111" + |"10010000"|"10010001"|"10010010"|"10010011"|"10010100"|"10010101"|"10010111" + |"10011000"|"10011001"|"10011010"|"10011011"|"10011100"|"10011101"|"10011111" + |"10100000"|"10100001"|"10100010"|"10100011"|"10100100"|"10100101"|"10100111" + |"10101000"|"10101001"|"10101010"|"10101011"|"10101100"|"10101101"|"10101111" + |"10110000"|"10110001"|"10110010"|"10110011"|"10110100"|"10110101"|"10110111" + |"10111000"|"10111001"|"10111010"|"10111011"|"10111100"|"10111101"|"10111111" => + -- RES b,r + if XY_State="00" then + if MCycle = "001" then + ALU_Op <= "1011"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + end if; + else + -- RES b,(IX+d),Reg, undocumented + MCycles <= "011"; + XYbit_undoc <= '1'; + case to_integer(unsigned(MCycle)) is + when 1 | 7=> + Set_Addr_To <= aXY; + when 2 => + ALU_Op <= "1011"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + Set_Addr_To <= aXY; + TStates <= "100"; + when 3 => + Write <= '1'; + when others => null; + end case; + end if; + + when "10000110"|"10001110"|"10010110"|"10011110"|"10100110"|"10101110"|"10110110"|"10111110" => + -- RES b,(HL) + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 | 7 => + Set_Addr_To <= aXY; + when 2 => + ALU_Op <= "1011"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + Set_Addr_To <= aXY; + TStates <= "100"; + when 3 => + Write <= '1'; + when others => null; + end case; + end case; + + when others => + +------------------------------------------------------------------------------ +-- +-- ED prefixed instructions +-- +------------------------------------------------------------------------------ + + case IRB is + when "00000000"|"00000001"|"00000010"|"00000011"|"00000100"|"00000101"|"00000110"|"00000111" + |"00001000"|"00001001"|"00001010"|"00001011"|"00001100"|"00001101"|"00001110"|"00001111" + |"00010000"|"00010001"|"00010010"|"00010011"|"00010100"|"00010101"|"00010110"|"00010111" + |"00011000"|"00011001"|"00011010"|"00011011"|"00011100"|"00011101"|"00011110"|"00011111" + |"00100000"|"00100001"|"00100010"|"00100011"|"00100100"|"00100101"|"00100110"|"00100111" + |"00101000"|"00101001"|"00101010"|"00101011"|"00101100"|"00101101"|"00101110"|"00101111" + |"00110000"|"00110001"|"00110010"|"00110011"|"00110100"|"00110101"|"00110110"|"00110111" + |"00111000"|"00111001"|"00111010"|"00111011"|"00111100"|"00111101"|"00111110"|"00111111" + + + |"10000000"|"10000001"|"10000010"|"10000011"|"10000100"|"10000101"|"10000110"|"10000111" + |"10001000"|"10001001"|"10001010"|"10001011"|"10001100"|"10001101"|"10001110"|"10001111" + |"10010000"|"10010001"|"10010010"|"10010011"|"10010100"|"10010101"|"10010110"|"10010111" + |"10011000"|"10011001"|"10011010"|"10011011"|"10011100"|"10011101"|"10011110"|"10011111" + | "10100100"|"10100101"|"10100110"|"10100111" + | "10101100"|"10101101"|"10101110"|"10101111" + | "10110100"|"10110101"|"10110110"|"10110111" + | "10111100"|"10111101"|"10111110"|"10111111" + |"11000000"|"11000001"|"11000010"|"11000011"|"11000100"|"11000101"|"11000110"|"11000111" + |"11001000"|"11001001"|"11001010"|"11001011"|"11001100"|"11001101"|"11001110"|"11001111" + |"11010000"|"11010001"|"11010010"|"11010011"|"11010100"|"11010101"|"11010110"|"11010111" + |"11011000"|"11011001"|"11011010"|"11011011"|"11011100"|"11011101"|"11011110"|"11011111" + |"11100000"|"11100001"|"11100010"|"11100011"|"11100100"|"11100101"|"11100110"|"11100111" + |"11101000"|"11101001"|"11101010"|"11101011"|"11101100"|"11101101"|"11101110"|"11101111" + |"11110000"|"11110001"|"11110010"|"11110011"|"11110100"|"11110101"|"11110110"|"11110111" + |"11111000"|"11111001"|"11111010"|"11111011"|"11111100"|"11111101"|"11111110"|"11111111" => + null; -- NOP, undocumented + when "01111110"|"01111111" => + -- NOP, undocumented + null; +-- 8 BIT LOAD GROUP + when "01010111" => + -- LD A,I + Special_LD <= "100"; + TStates <= "101"; + when "01011111" => + -- LD A,R + Special_LD <= "101"; + TStates <= "101"; + when "01000111" => + -- LD I,A + Special_LD <= "110"; + TStates <= "101"; + when "01001111" => + -- LD R,A + Special_LD <= "111"; + TStates <= "101"; +-- 16 BIT LOAD GROUP + when "01001011"|"01011011"|"01101011"|"01111011" => + -- LD dd,(nn) + MCycles <= "101"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + Set_Addr_To <= aZI; + Inc_PC <= '1'; + LDW <= '1'; + when 4 => + Read_To_Reg <= '1'; + if IR(5 downto 4) = "11" then + Set_BusA_To <= "1000"; + else + Set_BusA_To(2 downto 1) <= IR(5 downto 4); + Set_BusA_To(0) <= '1'; + end if; + Inc_WZ <= '1'; + Set_Addr_To <= aZI; + when 5 => + Read_To_Reg <= '1'; + if IR(5 downto 4) = "11" then + Set_BusA_To <= "1001"; + else + Set_BusA_To(2 downto 1) <= IR(5 downto 4); + Set_BusA_To(0) <= '0'; + end if; + when others => null; + end case; + when "01000011"|"01010011"|"01100011"|"01110011" => + -- LD (nn),dd + MCycles <= "101"; + case to_integer(unsigned(MCycle)) is + when 2 => + Inc_PC <= '1'; + LDZ <= '1'; + when 3 => + Set_Addr_To <= aZI; + Inc_PC <= '1'; + LDW <= '1'; + if IR(5 downto 4) = "11" then + Set_BusB_To <= "1000"; + else + Set_BusB_To(2 downto 1) <= IR(5 downto 4); + Set_BusB_To(0) <= '1'; + Set_BusB_To(3) <= '0'; + end if; + when 4 => + Inc_WZ <= '1'; + Set_Addr_To <= aZI; + Write <= '1'; + if IR(5 downto 4) = "11" then + Set_BusB_To <= "1001"; + else + Set_BusB_To(2 downto 1) <= IR(5 downto 4); + Set_BusB_To(0) <= '0'; + Set_BusB_To(3) <= '0'; + end if; + when 5 => + Write <= '1'; + when others => null; + end case; + when "10100000" | "10101000" | "10110000" | "10111000" => + -- LDI, LDD, LDIR, LDDR + MCycles <= "100"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aXY; + IncDec_16 <= "1100"; -- BC + when 2 => + Set_BusB_To <= "0110"; + Set_BusA_To(2 downto 0) <= "111"; + ALU_Op <= "0000"; + Set_Addr_To <= aDE; + if IR(3) = '0' then + IncDec_16 <= "0110"; -- IX + else + IncDec_16 <= "1110"; + end if; + when 3 => + I_BT <= '1'; + TStates <= "101"; + Write <= '1'; + if IR(3) = '0' then + IncDec_16 <= "0101"; -- DE + else + IncDec_16 <= "1101"; + end if; + when 4 => + NoRead <= '1'; + TStates <= "101"; + when others => null; + end case; + when "10100001" | "10101001" | "10110001" | "10111001" => + -- CPI, CPD, CPIR, CPDR + MCycles <= "100"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aXY; + IncDec_16 <= "1100"; -- BC + when 2 => + Set_BusB_To <= "0110"; + Set_BusA_To(2 downto 0) <= "111"; + ALU_Op <= "0111"; + Save_ALU <= '1'; + PreserveC <= '1'; + if IR(3) = '0' then + IncDec_16 <= "0110"; + else + IncDec_16 <= "1110"; + end if; + when 3 => + NoRead <= '1'; + I_BC <= '1'; + TStates <= "101"; + when 4 => + NoRead <= '1'; + TStates <= "101"; + when others => null; + end case; + when "01000100"|"01001100"|"01010100"|"01011100"|"01100100"|"01101100"|"01110100"|"01111100" => + -- NEG + Alu_OP <= "0010"; + Set_BusB_To <= "0111"; + Set_BusA_To <= "1010"; + Read_To_Acc <= '1'; + Save_ALU <= '1'; + when "01000110"|"01001110"|"01100110"|"01101110" => + -- IM 0 + IMode <= "00"; + when "01010110"|"01110110" => + -- IM 1 + IMode <= "01"; + when "01011110"|"01110111" => + -- IM 2 + IMode <= "10"; +-- 16 bit arithmetic + when "01001010"|"01011010"|"01101010"|"01111010" => + -- ADC HL,ss + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + NoRead <= '1'; + ALU_Op <= "0001"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + Set_BusA_To(2 downto 0) <= "101"; + case to_integer(unsigned(IR(5 downto 4))) is + when 0|1|2 => + Set_BusB_To(2 downto 1) <= IR(5 downto 4); + Set_BusB_To(0) <= '1'; + when others => + Set_BusB_To <= "1000"; + end case; + TStates <= "100"; + when 3 => + NoRead <= '1'; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + ALU_Op <= "0001"; + Set_BusA_To(2 downto 0) <= "100"; + case to_integer(unsigned(IR(5 downto 4))) is + when 0|1|2 => + Set_BusB_To(2 downto 1) <= IR(5 downto 4); + Set_BusB_To(0) <= '0'; + when others => + Set_BusB_To <= "1001"; + end case; + when others => + end case; + when "01000010"|"01010010"|"01100010"|"01110010" => + -- SBC HL,ss + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 2 => + NoRead <= '1'; + ALU_Op <= "0011"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + Set_BusA_To(2 downto 0) <= "101"; + case to_integer(unsigned(IR(5 downto 4))) is + when 0|1|2 => + Set_BusB_To(2 downto 1) <= IR(5 downto 4); + Set_BusB_To(0) <= '1'; + when others => + Set_BusB_To <= "1000"; + end case; + TStates <= "100"; + when 3 => + NoRead <= '1'; + ALU_Op <= "0011"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + Set_BusA_To(2 downto 0) <= "100"; + case to_integer(unsigned(IR(5 downto 4))) is + when 0|1|2 => + Set_BusB_To(2 downto 1) <= IR(5 downto 4); + when others => + Set_BusB_To <= "1001"; + end case; + when others => + end case; + when "01101111" => + -- RLD + MCycles <= "100"; + case to_integer(unsigned(MCycle)) is + when 2 => + NoRead <= '1'; + Set_Addr_To <= aXY; + when 3 => + Read_To_Reg <= '1'; + Set_BusB_To(2 downto 0) <= "110"; + Set_BusA_To(2 downto 0) <= "111"; + ALU_Op <= "1101"; + TStates <= "100"; + Set_Addr_To <= aXY; + Save_ALU <= '1'; + when 4 => + I_RLD <= '1'; + Write <= '1'; + when others => + end case; + when "01100111" => + -- RRD + MCycles <= "100"; + case to_integer(unsigned(MCycle)) is + when 2 => + Set_Addr_To <= aXY; + when 3 => + Read_To_Reg <= '1'; + Set_BusB_To(2 downto 0) <= "110"; + Set_BusA_To(2 downto 0) <= "111"; + ALU_Op <= "1110"; + TStates <= "100"; + Set_Addr_To <= aXY; + Save_ALU <= '1'; + when 4 => + I_RRD <= '1'; + Write <= '1'; + when others => + end case; + when "01000101"|"01001101"|"01010101"|"01011101"|"01100101"|"01101101"|"01110101"|"01111101" => + -- RETI, RETN + MCycles <= "011"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_TO <= aSP; + when 2 => + IncDec_16 <= "0111"; + Set_Addr_To <= aSP; + LDZ <= '1'; + when 3 => + Jump <= '1'; + IncDec_16 <= "0111"; + I_RETN <= '1'; + when others => null; + end case; + when "01000000"|"01001000"|"01010000"|"01011000"|"01100000"|"01101000"|"01110000"|"01111000" => + -- IN r,(C) + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aBC; + when 2 => + TStates <= "100"; -- MIKEJ should be 4 for IO cycle + IORQ <= '1'; + if IR(5 downto 3) /= "110" then + Read_To_Reg <= '1'; + Set_BusA_To(2 downto 0) <= IR(5 downto 3); + end if; + I_INRC <= '1'; + when others => + end case; + when "01000001"|"01001001"|"01010001"|"01011001"|"01100001"|"01101001"|"01110001"|"01111001" => + -- OUT (C),r + -- OUT (C),0 + MCycles <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aBC; + Set_BusB_To(2 downto 0) <= IR(5 downto 3); + if IR(5 downto 3) = "110" then + Set_BusB_To(3) <= '1'; + end if; + when 2 => + TStates <= "100"; -- MIKEJ should be 4 for IO cycle + Write <= '1'; + IORQ <= '1'; + when others => + end case; + when "10100010" | "10101010" | "10110010" | "10111010" => + -- INI, IND, INIR, INDR + -- note B is decremented AFTER being put on the bus + MCycles <= "100"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= aBC; + Set_BusB_To <= "1010"; + Set_BusA_To <= "0000"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + ALU_Op <= "0010"; + when 2 => + TStates <= "100"; -- MIKEJ should be 4 for IO cycle + IORQ <= '1'; + Set_BusB_To <= "0110"; + Set_Addr_To <= aXY; + when 3 => + if IR(3) = '0' then + --IncDec_16 <= "0010"; + IncDec_16 <= "0110"; + else + --IncDec_16 <= "1010"; + IncDec_16 <= "1110"; + end if; + TStates <= "100"; + Write <= '1'; + I_BTR <= '1'; + when 4 => + NoRead <= '1'; + TStates <= "101"; + when others => null; + end case; + when "10100011" | "10101011" | "10110011" | "10111011" => + -- OUTI, OUTD, OTIR, OTDR + -- note B is decremented BEFORE being put on the bus. + -- mikej fix for hl inc + MCycles <= "100"; + case to_integer(unsigned(MCycle)) is + when 1 => + TStates <= "101"; + Set_Addr_To <= aXY; + Set_BusB_To <= "1010"; + Set_BusA_To <= "0000"; + Read_To_Reg <= '1'; + Save_ALU <= '1'; + ALU_Op <= "0010"; + when 2 => + Set_BusB_To <= "0110"; + Set_Addr_To <= aBC; + when 3 => + if IR(3) = '0' then + IncDec_16 <= "0110"; -- mikej + else + IncDec_16 <= "1110"; -- mikej + end if; + TStates <= "100"; -- MIKEJ should be 4 for IO cycle + IORQ <= '1'; + Write <= '1'; + I_BTR <= '1'; + when 4 => + NoRead <= '1'; + TStates <= "101"; + when others => null; + end case; + end case; + + end case; + + if Mode = 1 then + if MCycle = "001" then +-- TStates <= "100"; + else + TStates <= "011"; + end if; + end if; + + if Mode = 3 then + if MCycle = "001" then +-- TStates <= "100"; + else + TStates <= "100"; + end if; + end if; + + if Mode < 2 then + if MCycle = "110" then + Inc_PC <= '1'; + if Mode = 1 then + Set_Addr_To <= aXY; + TStates <= "100"; + Set_BusB_To(2 downto 0) <= SSS; + Set_BusB_To(3) <= '0'; + end if; + if IRB = "00110110" or IRB = "11001011" then + Set_Addr_To <= aNone; + end if; + end if; + if MCycle = "111" then + if Mode = 0 then + TStates <= "101"; + end if; + if ISet /= "01" then + Set_Addr_To <= aXY; + end if; + Set_BusB_To(2 downto 0) <= SSS; + Set_BusB_To(3) <= '0'; + if IRB = "00110110" or ISet = "01" then + -- LD (HL),n + Inc_PC <= '1'; + else + NoRead <= '1'; + end if; + end if; + end if; + + end process; + +end; diff --git a/vhdl/T80_Pack.vhd b/vhdl/T80_Pack.vhd new file mode 100644 index 0000000..6904b66 --- /dev/null +++ b/vhdl/T80_Pack.vhd @@ -0,0 +1,220 @@ +-- **** +-- T80(b) core. In an effort to merge and maintain bug fixes .... +-- +-- +-- Ver 303 add undocumented DDCB and FDCB opcodes by TobiFlex 20.04.2010 +-- Ver 300 started tidyup +-- MikeJ March 2005 +-- Latest version from www.fpgaarcade.com (original www.opencores.org) +-- +-- **** +-- +-- Z80 compatible microprocessor core +-- +-- Version : 0242 +-- +-- Copyright (c) 2001-2002 Daniel Wallner (jesus@opencores.org) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- The latest version of this file can be found at: +-- http://www.opencores.org/cvsweb.shtml/t80/ +-- +-- Limitations : +-- +-- File history : +-- + +library IEEE; +use IEEE.std_logic_1164.all; + +package T80_Pack is + + component T80 + generic( + Mode : integer := 0; -- 0 => Z80, 1 => Fast Z80, 2 => 8080, 3 => GB + IOWait : integer := 0; -- 1 => Single cycle I/O, 1 => Std I/O cycle + Flag_C : integer := 0; + Flag_N : integer := 1; + Flag_P : integer := 2; + Flag_X : integer := 3; + Flag_H : integer := 4; + Flag_Y : integer := 5; + Flag_Z : integer := 6; + Flag_S : integer := 7 + ); + port( + RESET_n : in std_logic; + CLK_n : in std_logic; + CEN : in std_logic; + WAIT_n : in std_logic; + INT_n : in std_logic; + NMI_n : in std_logic; + BUSRQ_n : in std_logic; + M1_n : out std_logic; + IORQ : out std_logic; + NoRead : out std_logic; + Write : out std_logic; + RFSH_n : out std_logic; + HALT_n : out std_logic; + BUSAK_n : out std_logic; + A : out std_logic_vector(15 downto 0); + DInst : in std_logic_vector(7 downto 0); + DI : in std_logic_vector(7 downto 0); + DO : out std_logic_vector(7 downto 0); + MC : out std_logic_vector(2 downto 0); + TS : out std_logic_vector(2 downto 0); + IntCycle_n : out std_logic; + IntE : out std_logic; + Stop : out std_logic + ); + end component; + + component T80_Reg + port( + Clk : in std_logic; + CEN : in std_logic; + WEH : in std_logic; + WEL : in std_logic; + AddrA : in std_logic_vector(2 downto 0); + AddrB : in std_logic_vector(2 downto 0); + AddrC : in std_logic_vector(2 downto 0); + DIH : in std_logic_vector(7 downto 0); + DIL : in std_logic_vector(7 downto 0); + DOAH : out std_logic_vector(7 downto 0); + DOAL : out std_logic_vector(7 downto 0); + DOBH : out std_logic_vector(7 downto 0); + DOBL : out std_logic_vector(7 downto 0); + DOCH : out std_logic_vector(7 downto 0); + DOCL : out std_logic_vector(7 downto 0) + ); + end component; + + component T80_MCode + generic( + Mode : integer := 0; + Flag_C : integer := 0; + Flag_N : integer := 1; + Flag_P : integer := 2; + Flag_X : integer := 3; + Flag_H : integer := 4; + Flag_Y : integer := 5; + Flag_Z : integer := 6; + Flag_S : integer := 7 + ); + port( + IR : in std_logic_vector(7 downto 0); + ISet : in std_logic_vector(1 downto 0); + MCycle : in std_logic_vector(2 downto 0); + F : in std_logic_vector(7 downto 0); + NMICycle : in std_logic; + IntCycle : in std_logic; + XY_State : in std_logic_vector(1 downto 0); + MCycles : out std_logic_vector(2 downto 0); + TStates : out std_logic_vector(2 downto 0); + Prefix : out std_logic_vector(1 downto 0); -- None,BC,ED,DD/FD + Inc_PC : out std_logic; + Inc_WZ : out std_logic; + IncDec_16 : out std_logic_vector(3 downto 0); -- BC,DE,HL,SP 0 is inc + Read_To_Reg : out std_logic; + Read_To_Acc : out std_logic; + Set_BusA_To : out std_logic_vector(3 downto 0); -- B,C,D,E,H,L,DI/DB,A,SP(L),SP(M),0,F + Set_BusB_To : out std_logic_vector(3 downto 0); -- B,C,D,E,H,L,DI,A,SP(L),SP(M),1,F,PC(L),PC(M),0 + ALU_Op : out std_logic_vector(3 downto 0); + -- ADD, ADC, SUB, SBC, AND, XOR, OR, CP, ROT, BIT, SET, RES, DAA, RLD, RRD, None + Save_ALU : out std_logic; + PreserveC : out std_logic; + Arith16 : out std_logic; + Set_Addr_To : out std_logic_vector(2 downto 0); -- aNone,aXY,aIOA,aSP,aBC,aDE,aZI + IORQ : out std_logic; + Jump : out std_logic; + JumpE : out std_logic; + JumpXY : out std_logic; + Call : out std_logic; + RstP : out std_logic; + LDZ : out std_logic; + LDW : out std_logic; + LDSPHL : out std_logic; + Special_LD : out std_logic_vector(2 downto 0); -- A,I;A,R;I,A;R,A;None + ExchangeDH : out std_logic; + ExchangeRp : out std_logic; + ExchangeAF : out std_logic; + ExchangeRS : out std_logic; + I_DJNZ : out std_logic; + I_CPL : out std_logic; + I_CCF : out std_logic; + I_SCF : out std_logic; + I_RETN : out std_logic; + I_BT : out std_logic; + I_BC : out std_logic; + I_BTR : out std_logic; + I_RLD : out std_logic; + I_RRD : out std_logic; + I_INRC : out std_logic; + SetDI : out std_logic; + SetEI : out std_logic; + IMode : out std_logic_vector(1 downto 0); + Halt : out std_logic; + NoRead : out std_logic; + Write : out std_logic; + XYbit_undoc : out std_logic + ); + end component; + + component T80_ALU + generic( + Mode : integer := 0; + Flag_C : integer := 0; + Flag_N : integer := 1; + Flag_P : integer := 2; + Flag_X : integer := 3; + Flag_H : integer := 4; + Flag_Y : integer := 5; + Flag_Z : integer := 6; + Flag_S : integer := 7 + ); + port( + Arith16 : in std_logic; + Z16 : in std_logic; + ALU_Op : in std_logic_vector(3 downto 0); + IR : in std_logic_vector(5 downto 0); + ISet : in std_logic_vector(1 downto 0); + BusA : in std_logic_vector(7 downto 0); + BusB : in std_logic_vector(7 downto 0); + F_In : in std_logic_vector(7 downto 0); + Q : out std_logic_vector(7 downto 0); + F_Out : out std_logic_vector(7 downto 0) + ); + end component; + +end; diff --git a/vhdl/T80_Reg.vhd b/vhdl/T80_Reg.vhd new file mode 100644 index 0000000..1c0f263 --- /dev/null +++ b/vhdl/T80_Reg.vhd @@ -0,0 +1,114 @@ +-- **** +-- T80(b) core. In an effort to merge and maintain bug fixes .... +-- +-- +-- Ver 300 started tidyup +-- MikeJ March 2005 +-- Latest version from www.fpgaarcade.com (original www.opencores.org) +-- +-- **** +-- +-- T80 Registers, technology independent +-- +-- Version : 0244 +-- +-- Copyright (c) 2002 Daniel Wallner (jesus@opencores.org) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- The latest version of this file can be found at: +-- http://www.opencores.org/cvsweb.shtml/t51/ +-- +-- Limitations : +-- +-- File history : +-- +-- 0242 : Initial release +-- +-- 0244 : Changed to single register file +-- + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +entity T80_Reg is + port( + Clk : in std_logic; + CEN : in std_logic; + WEH : in std_logic; + WEL : in std_logic; + AddrA : in std_logic_vector(2 downto 0); + AddrB : in std_logic_vector(2 downto 0); + AddrC : in std_logic_vector(2 downto 0); + DIH : in std_logic_vector(7 downto 0); + DIL : in std_logic_vector(7 downto 0); + DOAH : out std_logic_vector(7 downto 0); + DOAL : out std_logic_vector(7 downto 0); + DOBH : out std_logic_vector(7 downto 0); + DOBL : out std_logic_vector(7 downto 0); + DOCH : out std_logic_vector(7 downto 0); + DOCL : out std_logic_vector(7 downto 0) + ); +end T80_Reg; + +architecture rtl of T80_Reg is + + type Register_Image is array (natural range <>) of std_logic_vector(7 downto 0); + signal RegsH : Register_Image(0 to 7); + signal RegsL : Register_Image(0 to 7); + +begin + + process (Clk) + begin + if Clk'event and Clk = '1' then + if CEN = '1' then + if WEH = '1' then + RegsH(to_integer(unsigned(AddrA))) <= DIH; + end if; + if WEL = '1' then + RegsL(to_integer(unsigned(AddrA))) <= DIL; + end if; + end if; + end if; + end process; + + DOAH <= RegsH(to_integer(unsigned(AddrA))); + DOAL <= RegsL(to_integer(unsigned(AddrA))); + DOBH <= RegsH(to_integer(unsigned(AddrB))); + DOBL <= RegsL(to_integer(unsigned(AddrB))); + DOCH <= RegsH(to_integer(unsigned(AddrC))); + DOCL <= RegsL(to_integer(unsigned(AddrC))); + +end; diff --git a/vhdl/T80se.vhd b/vhdl/T80se.vhd new file mode 100644 index 0000000..1b0cb9b --- /dev/null +++ b/vhdl/T80se.vhd @@ -0,0 +1,192 @@ +-- **** +-- T80(b) core. In an effort to merge and maintain bug fixes .... +-- +-- +-- Ver 300 started tidyup +-- MikeJ March 2005 +-- Latest version from www.fpgaarcade.com (original www.opencores.org) +-- +-- **** +-- +-- Z80 compatible microprocessor core, synchronous top level with clock enable +-- Different timing than the original z80 +-- Inputs needs to be synchronous and outputs may glitch +-- +-- Version : 0240 +-- +-- Copyright (c) 2001-2002 Daniel Wallner (jesus@opencores.org) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- The latest version of this file can be found at: +-- http://www.opencores.org/cvsweb.shtml/t80/ +-- +-- Limitations : +-- +-- File history : +-- +-- 0235 : First release +-- +-- 0236 : Added T2Write generic +-- +-- 0237 : Fixed T2Write with wait state +-- +-- 0238 : Updated for T80 interface change +-- +-- 0240 : Updated for T80 interface change +-- +-- 0242 : Updated for T80 interface change +-- +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use work.T80_Pack.all; + +entity T80se is + generic( + Mode : integer := 0; -- 0 => Z80, 1 => Fast Z80, 2 => 8080, 3 => GB + T2Write : integer := 0; -- 0 => WR_n active in T3, /=0 => WR_n active in T2 + IOWait : integer := 1 -- 0 => Single cycle I/O, 1 => Std I/O cycle + ); + port( + RESET_n : in std_logic; + CLK_n : in std_logic; + CLKEN : in std_logic; + WAIT_n : in std_logic; + INT_n : in std_logic; + NMI_n : in std_logic; + BUSRQ_n : in std_logic; + M1_n : out std_logic; + MREQ_n : out std_logic; + IORQ_n : out std_logic; + RD_n : out std_logic; + WR_n : out std_logic; + RFSH_n : out std_logic; + HALT_n : out std_logic; + BUSAK_n : out std_logic; + A : out std_logic_vector(15 downto 0); + DI : in std_logic_vector(7 downto 0); + DO : out std_logic_vector(7 downto 0) + ); +end T80se; + +architecture rtl of T80se is + + signal IntCycle_n : std_logic; + signal NoRead : std_logic; + signal Write : std_logic; + signal IORQ : std_logic; + signal DI_Reg : std_logic_vector(7 downto 0); + signal MCycle : std_logic_vector(2 downto 0); + signal TState : std_logic_vector(2 downto 0); + +begin + + u0 : T80 + generic map( + Mode => Mode, + IOWait => IOWait) + port map( + CEN => CLKEN, + M1_n => M1_n, + IORQ => IORQ, + NoRead => NoRead, + Write => Write, + RFSH_n => RFSH_n, + HALT_n => HALT_n, + WAIT_n => Wait_n, + INT_n => INT_n, + NMI_n => NMI_n, + RESET_n => RESET_n, + BUSRQ_n => BUSRQ_n, + BUSAK_n => BUSAK_n, + CLK_n => CLK_n, + A => A, + DInst => DI, + DI => DI_Reg, + DO => DO, + MC => MCycle, + TS => TState, + IntCycle_n => IntCycle_n); + + process (RESET_n, CLK_n) + begin + if RESET_n = '0' then + RD_n <= '1'; + WR_n <= '1'; + IORQ_n <= '1'; + MREQ_n <= '1'; + DI_Reg <= "00000000"; + elsif CLK_n'event and CLK_n = '1' then + if CLKEN = '1' then + RD_n <= '1'; + WR_n <= '1'; + IORQ_n <= '1'; + MREQ_n <= '1'; + if MCycle = "001" then + if TState = "001" or (TState = "010" and Wait_n = '0') then + RD_n <= not IntCycle_n; + MREQ_n <= not IntCycle_n; + IORQ_n <= IntCycle_n; + end if; + if TState = "011" then + MREQ_n <= '0'; + end if; + else + if (TState = "001" or (TState = "010" and Wait_n = '0')) and NoRead = '0' and Write = '0' then + RD_n <= '0'; + IORQ_n <= not IORQ; + MREQ_n <= IORQ; + end if; + if T2Write = 0 then + if TState = "010" and Write = '1' then + WR_n <= '0'; + IORQ_n <= not IORQ; + MREQ_n <= IORQ; + end if; + else + if (TState = "001" or (TState = "010" and Wait_n = '0')) and Write = '1' then + WR_n <= '0'; + IORQ_n <= not IORQ; + MREQ_n <= IORQ; + end if; + end if; + end if; + if TState = "010" and Wait_n = '1' then + DI_Reg <= DI; + end if; + end if; + end if; + end process; + +end; diff --git a/vhdl/Z80cpu.vhd b/vhdl/Z80cpu.vhd new file mode 100644 index 0000000..263cafe --- /dev/null +++ b/vhdl/Z80cpu.vhd @@ -0,0 +1,96 @@ +--+-----------------------------------+-------------------------------------+-- +--| ___ ___ | (c) 2013-2014 William R Sowerbutts |-- +--| ___ ___ ___ ___( _ ) / _ \ | will@sowerbutts.com |-- +--| / __|/ _ \ / __|_ / _ \| | | | | |-- +--| \__ \ (_) | (__ / / (_) | |_| | | A Z80 FPGA computer, just for fun |-- +--| |___/\___/ \___/___\___/ \___/ | |-- +--| | http://sowerbutts.com/ |-- +--+-----------------------------------+-------------------------------------+-- +--| Wrap the T80 CPU core and produce more easily comprehended signals |-- +--+-------------------------------------------------------------------------+-- + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use work.T80_Pack.all; + +entity Z80cpu is + port ( + -- reset + reset : in std_logic; + + -- clocking + clk : in std_logic; + clk_enable : in std_logic; + + -- indicates when we're in the M1 cycle (start of an instruction) + m1_cycle : out std_logic; + + -- memory and I/O interface + req_mem : out std_logic; -- memory request? + req_io : out std_logic; -- i/o request? + req_read : out std_logic; -- read? + req_write : out std_logic; -- write? + mem_wait : in std_logic; -- memory or i/o can force the CPU to wait + address : out std_logic_vector(15 downto 0); + data_in : in std_logic_vector(7 downto 0); + data_out : out std_logic_vector(7 downto 0); + + -- interrupts + interrupt : in std_logic; + nmi : in std_logic + ); +end Z80cpu; + +architecture behavioural of Z80cpu is + signal RESET_n : std_logic; + signal WAIT_n : std_logic; + signal INT_n : std_logic; + signal NMI_n : std_logic; + signal M1_n : std_logic; + signal MREQ_n : std_logic; + signal IORQ_n : std_logic; + signal RFSH_n : std_logic; + signal RD_n : std_logic; + signal WR_n : std_logic; +begin + + RESET_n <= not reset; + WAIT_n <= not mem_wait; + INT_n <= not interrupt; + NMI_n <= not nmi; + m1_cycle <= not M1_n; + + req_mem <= (not MREQ_n) and (RFSH_n); + req_io <= (not IORQ_n) and (M1_n); -- IORQ is active during M1 when handling interrupts (it's well documented, but I found out the hard way...) + req_read <= (not RD_n) and (RFSH_n); + req_write <= (not WR_n); + + cpu : entity work.T80se + generic map ( + Mode => 1, -- 0 => Z80, 1 => Fast Z80, 2 => 8080, 3 => GB + T2Write => 1, -- 0 => WR_n active in T3, /=0 => WR_n active in T2 + IOWait => 0 -- 0 => single cycle I/O, 1 => standard I/O cycle + ) + port map ( + RESET_n => RESET_n, + CLK_n => clk, + CLKEN => clk_enable, + WAIT_n => WAIT_n, + INT_n => INT_n, + NMI_n => NMI_n, + BUSRQ_n => '1', + BUSAK_n => open, + M1_n => M1_n, + MREQ_n => MREQ_n, + IORQ_n => IORQ_n, + RD_n => RD_n, + WR_n => WR_n, + RFSH_n => RFSH_n, + HALT_n => open, + A => address, + DI => data_in, + DO => data_out + ); + +end; diff --git a/vhdl/clkscale.vhd b/vhdl/clkscale.vhd new file mode 100644 index 0000000..ea5345e --- /dev/null +++ b/vhdl/clkscale.vhd @@ -0,0 +1,74 @@ +--+-----------------------------------+-------------------------------------+-- +--| ___ ___ | (c) 2013-2014 William R Sowerbutts |-- +--| ___ ___ ___ ___( _ ) / _ \ | will@sowerbutts.com |-- +--| / __|/ _ \ / __|_ / _ \| | | | | |-- +--| \__ \ (_) | (__ / / (_) | |_| | | A Z80 FPGA computer, just for fun |-- +--| |___/\___/ \___/___\___/ \___/ | |-- +--| | http://sowerbutts.com/ |-- +--+-----------------------------------+-------------------------------------+-- +--| An attempt to modulate the CPU clock so it can be slowed down without |-- +--| also modulating the clock to the peripherals (which would break the |-- +--| UART and DRAM at least). This works but not all the peripherals are |-- +--| currently compatible (the UART, at least, doesn't handle this well). |-- +--| Strongly uggest you avoid using this before you fix the peripherals. |-- +--+-------------------------------------------------------------------------+-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity clkscale is + port ( clk : in std_logic; + reset : in std_logic; + cpu_address : in std_logic_vector(2 downto 0); + data_in : in std_logic_vector(7 downto 0); + data_out : out std_logic_vector(7 downto 0); + enable : in std_logic; + read_notwrite : in std_logic; + clk_enable : out std_logic + ); +end clkscale; + +-- a counter which counts up until it reaches a target value. +-- when the counter is at the target value the clock is enabled +-- for one cycle and the counter is reset. the clock is disabled +-- the rest of the time. this means the clock is enabled in the +-- proportion 1/(1+r) where r is the register value. + +architecture Behavioral of clkscale is + + signal counter_target : unsigned(7 downto 0) := (others => '0'); + signal counter_value : unsigned(7 downto 0) := (others => '0'); + signal output : std_logic; + +begin + + data_out <= std_logic_vector(counter_target); + clk_enable <= output; + + clkscale_proc: process(clk) + begin + if rising_edge(clk) then + if reset = '1' then + counter_target <= to_unsigned(0, 8); + counter_value <= to_unsigned(0, 8); + output <= '1'; + else + -- reset on target, enable clock for one cycle + if counter_value = counter_target then + counter_value <= to_unsigned(0, 8); + output <= '1'; + else + counter_value <= counter_value + 1; + output <= '0'; + end if; + + -- register write + if enable = '1' and read_notwrite = '0' then + counter_target <= unsigned(data_in); + end if; + end if; + end if; + end process; +end Behavioral; + diff --git a/vhdl/fifo.vhd b/vhdl/fifo.vhd new file mode 100644 index 0000000..80e2e57 --- /dev/null +++ b/vhdl/fifo.vhd @@ -0,0 +1,93 @@ +--+-----------------------------------+-------------------------------------+-- +--| ___ ___ | (c) 2013-2014 William R Sowerbutts |-- +--| ___ ___ ___ ___( _ ) / _ \ | will@sowerbutts.com |-- +--| / __|/ _ \ / __|_ / _ \| | | | | |-- +--| \__ \ (_) | (__ / / (_) | |_| | | A Z80 FPGA computer, just for fun |-- +--| |___/\___/ \___/___\___/ \___/ | |-- +--| | http://sowerbutts.com/ |-- +--+-----------------------------------+-------------------------------------+-- +--| FIFO implementation with high water mark. Could be improved; currently |-- +--| it is impossible to use the last byte in the FIFO (because it cannot |-- +--| distinguish completely-full from completely-empty) |-- +--+-------------------------------------------------------------------------+-- + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +entity fifo is + generic( + depth_log2 : integer := 10; -- 5 gives 32 bytes, implements without a BRAM. + hwm_space : integer := 5; -- minimum bytes free in buffer before we assert flow control signals + width : integer := 8 + ); + port( + clk : in std_logic; + reset : in std_logic; + write_en : in std_logic; + write_ready : out std_logic; -- is there space to write? + read_en : in std_logic; + read_ready : out std_logic; -- is there data waiting to read? + data_in : in std_logic_vector(width-1 downto 0); + data_out : out std_logic_vector(width-1 downto 0); + high_water_mark : out std_logic + ); +end fifo; + +architecture behaviour of fifo is + type fifo_entry is array (natural range <>) of std_logic_vector(width-1 downto 0); + signal fifo_contents : fifo_entry(0 to (2 ** depth_log2) - 1); -- this is the FIFO buffer memory + + signal read_ptr : unsigned(depth_log2-1 downto 0) := (others => '0'); + signal write_ptr : unsigned(depth_log2-1 downto 0) := (others => '0'); + signal full : std_logic; + signal empty : std_logic; +begin + + is_empty: process(read_ptr, write_ptr) + begin + if read_ptr = write_ptr then + empty <= '1'; + else + empty <= '0'; + end if; + if read_ptr = (write_ptr+1) then + full <= '1'; + else + full <= '0'; + end if; + if (write_ptr - read_ptr) >= ((2 ** depth_log2) - 1 - hwm_space) then + high_water_mark <= '1'; + else + high_water_mark <= '0'; + end if; + end process; + + fifo_update: process(clk) + begin + if rising_edge(clk) then + if reset = '1' then + -- reset + read_ptr <= to_unsigned(0, depth_log2); + write_ptr <= to_unsigned(0, depth_log2); + else + -- normal operation + if write_en = '1' and full = '0' then + fifo_contents(to_integer(write_ptr)) <= data_in; + write_ptr <= write_ptr + 1; + end if; + + if read_en = '1' and empty = '0' then + read_ptr <= read_ptr + 1; + end if; + + data_out <= fifo_contents(to_integer(read_ptr)); + + end if; + end if; + end process; + + write_ready <= not full; + read_ready <= not empty; + +end; diff --git a/vhdl/gpio.vhd b/vhdl/gpio.vhd new file mode 100644 index 0000000..0434bac --- /dev/null +++ b/vhdl/gpio.vhd @@ -0,0 +1,64 @@ +--+-----------------------------------+-------------------------------------+-- +--| ___ ___ | (c) 2013-2014 William R Sowerbutts |-- +--| ___ ___ ___ ___( _ ) / _ \ | will@sowerbutts.com |-- +--| / __|/ _ \ / __|_ / _ \| | | | | |-- +--| \__ \ (_) | (__ / / (_) | |_| | | A Z80 FPGA computer, just for fun |-- +--| |___/\___/ \___/___\___/ \___/ | |-- +--| | http://sowerbutts.com/ |-- +--+-----------------------------------+-------------------------------------+-- +--| Simple GPIO interface providing 8 bits each of input and output |-- +--+-------------------------------------------------------------------------+-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity gpio is + port ( clk : in std_logic; + reset : in std_logic; + cpu_address : in std_logic_vector(2 downto 0); + data_in : in std_logic_vector(7 downto 0); + data_out : out std_logic_vector(7 downto 0); + enable : in std_logic; + read_notwrite : in std_logic; + input_pins : in std_logic_vector(7 downto 0); + output_pins : out std_logic_vector(7 downto 0) + ); +end gpio; + +architecture Behavioral of gpio is + + signal captured_inputs : std_logic_vector(7 downto 0); + signal register_outputs : std_logic_vector(7 downto 0) := (others => '1'); + +begin + + with cpu_address select + data_out <= + captured_inputs when "000", + register_outputs when "001", + register_outputs when others; + + output_pins <= register_outputs; + + gpio_proc: process(clk) + begin + if rising_edge(clk) then + if reset = '1' then + captured_inputs <= (others => '0'); + register_outputs <= (others => '1'); + else + captured_inputs <= input_pins; + + if enable = '1' and read_notwrite = '0' then + case cpu_address is + when "000" => -- no change + when "001" => register_outputs <= data_in; + when others => -- no change + end case; + end if; + end if; + end if; + end process; +end Behavioral; + diff --git a/vhdl/timer.vhd b/vhdl/timer.vhd new file mode 100644 index 0000000..fe3990c --- /dev/null +++ b/vhdl/timer.vhd @@ -0,0 +1,183 @@ +--+-----------------------------------+-------------------------------------+-- +--| ___ ___ | (c) 2013-2014 William R Sowerbutts |-- +--| ___ ___ ___ ___( _ ) / _ \ | will@sowerbutts.com |-- +--| / __|/ _ \ / __|_ / _ \| | | | | |-- +--| \__ \ (_) | (__ / / (_) | |_| | | A Z80 FPGA computer, just for fun |-- +--| |___/\___/ \___/___\___/ \___/ | |-- +--| | http://sowerbutts.com/ |-- +--+-----------------------------------+-------------------------------------+-- +--| A simple timer peripheral for timing intervals and generating periodic |-- +--| interrupts. |-- +--+-------------------------------------------------------------------------+-- +-- +-- There are two timers; a 1MHz 32-bit counter which always counts up (unless reset to 0) +-- and whose value can be transferred atomically to a 32-bit latch, and a 1MHz 24-bit down +-- counter which triggers an interrupt and is reset to a programmable value upon reaching +-- zero. Writes to the register at base+1 perform timer operations according to the value +-- written. The 1MHz is derived by prescaling the system clock. +-- +-- register layout: +-- +-- address read value write operation +-- --------- ------------------------------------ ------------------------------------- +-- base+0 status register set status register +-- base+1 (unused) perform operation according to value written +-- base+2 (unused) (no operation) +-- base+3 (unused) (no operation) +-- base+4 muxed register value (low byte) update muxed register value +-- base+5 muxed register value update muxed register value +-- base+6 muxed register value update muxed register value +-- base+7 muxed register value (high byte) update muxed register value +-- +-- +-- operation values (for writes to base+1): +-- 00 -- acknowledge interrupt +-- 01 -- reset upcounter value to zero +-- 02 -- update latched value from upcounter value +-- 03 -- reset downcounter +-- 10 -- set register mux select to upcounter current +-- 11 -- set register mux select to upcounter latched +-- 12 -- set register mux select to downcounter current +-- 13 -- set register mux select to downcountre reset +-- +-- +-- control/status register layout: +-- bits 0, 1, -- register mux select (controls which register is visible in registers at base+4 through base+7): +-- 0 0 upcounter current value +-- 0 1 upcounter latched value +-- 1 0 downcounter current value +-- 1 1 downcounter reset value +-- (bits 2, 3, 4, 5 are currently unused) +-- bit 6 -- countdown timer interrupt enable (0=disable, 1=enable) +-- bit 7 -- interrupt flag (0=no interrupt, 1=one or interrupts occurred but not yet acknowledged) +-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity timer is + generic ( + clk_frequency : natural := (128 * 1000000) + ); + port ( clk : in std_logic; + reset : in std_logic; + cpu_address : in std_logic_vector(2 downto 0); + data_in : in std_logic_vector(7 downto 0); + data_out : out std_logic_vector(7 downto 0); + enable : in std_logic; + req_read : in std_logic; + req_write : in std_logic; + interrupt : out std_logic + ); +end timer; + +architecture Behavioral of timer is + + signal upcounter_value : unsigned(31 downto 0) := (others => '0'); + signal upcounter_latch : unsigned(31 downto 0) := (others => '0'); + signal downcounter_value : unsigned(31 downto 0) := (others => '0'); + signal downcounter_start : unsigned(31 downto 0) := (others => '0'); + + -- if using frequencies > 128MHz this counter will need to be wider than 7 bits + signal counter_prescale : unsigned(6 downto 0) := (others => '0'); + constant prescale_wrap : unsigned(6 downto 0) := to_unsigned((clk_frequency / 1000000) - 1, 7); -- aim for a 1MHz counter + + signal interrupt_enable : std_logic := '0'; + signal interrupt_signal : std_logic := '0'; + signal regmux_select : std_logic_vector(1 downto 0) := "00"; + + signal regmux_output : std_logic_vector(31 downto 0); + signal regmux_updated : std_logic_vector(31 downto 0); + signal status_register_value: std_logic_vector(7 downto 0); + +begin + + interrupt <= (interrupt_signal and interrupt_enable); + + with cpu_address select + data_out <= + status_register_value when "000", + regmux_output(7 downto 0) when "100", + regmux_output(15 downto 8) when "101", + regmux_output(23 downto 16) when "110", + regmux_output(31 downto 24) when "111", + status_register_value when others; + + status_register_value <= interrupt_signal & interrupt_enable & "0000" & regmux_select; + + with regmux_select select + regmux_output <= + std_logic_vector(upcounter_value ) when "00", + std_logic_vector(upcounter_latch ) when "01", + std_logic_vector(downcounter_value) when "10", + std_logic_vector(downcounter_start) when "11", + std_logic_vector(downcounter_start) when others; + + with cpu_address(1 downto 0) select + regmux_updated <= + regmux_output(31 downto 8) & data_in when "00", + regmux_output(31 downto 16) & data_in & regmux_output(7 downto 0) when "01", + regmux_output(31 downto 24) & data_in & regmux_output(15 downto 0) when "10", + data_in & regmux_output(23 downto 0) when "11", + data_in & regmux_output(23 downto 0) when others; + + counter_proc: process(clk) + begin + if rising_edge(clk) then + if reset = '1' then + upcounter_value <= (others => '0'); + upcounter_latch <= (others => '0'); + downcounter_value <= (others => '0'); + downcounter_start <= (others => '0'); + counter_prescale <= (others => '0'); + interrupt_enable <= '0'; + interrupt_signal <= '0'; + regmux_select <= "00"; + else + -- prescaled counter + if counter_prescale = prescale_wrap then + counter_prescale <= (others => '0'); -- reset prescale counter + upcounter_value <= upcounter_value + 1; + if downcounter_value = 0 then + downcounter_value <= downcounter_start; + interrupt_signal <= '1'; + else + downcounter_value <= downcounter_value - 1; + end if; + else + counter_prescale <= counter_prescale + 1; + end if; + + if enable = '1' and req_write = '1' then + if cpu_address = "000" then + interrupt_signal <= data_in(7); + interrupt_enable <= data_in(6); + regmux_select <= data_in(1 downto 0); + elsif cpu_address = "001" then + case data_in is + when "00000000" => interrupt_signal <= '0'; + when "00000001" => upcounter_value <= (others => '0'); + when "00000010" => upcounter_latch <= upcounter_value; + when "00000011" => downcounter_value <= downcounter_start; + when "00010000" => regmux_select <= "00"; + when "00010001" => regmux_select <= "01"; + when "00010010" => regmux_select <= "10"; + when "00010011" => regmux_select <= "11"; + when others => + end case; + elsif cpu_address(2) = '1' then + case regmux_select is + when "00" => upcounter_value <= unsigned(regmux_updated); + when "01" => upcounter_latch <= unsigned(regmux_updated); + when "10" => downcounter_value <= unsigned(regmux_updated); + when "11" => downcounter_start <= unsigned(regmux_updated); + when others => + end case; + end if; + end if; + end if; + end if; + end process; +end Behavioral; + diff --git a/vhdl/top_level.vhd b/vhdl/top_level.vhd new file mode 100644 index 0000000..4de5951 --- /dev/null +++ b/vhdl/top_level.vhd @@ -0,0 +1,563 @@ +--+-----------------------------------+-------------------------------------+-- +--| ___ ___ | (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; +library UNISIM; +use UNISIM.VComponents.all; + +entity top_level is + Port ( sysclk_32m : 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 := 128; -- this is the frequency which the PLL outputs, in MHz. + + -- SDRAM configuration + constant sdram_address_width : natural := 22; + constant sdram_column_bits : natural := 8; + constant cycles_per_refresh : natural := (64000*clk_freq_mhz)/4096-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' or reset_button_sync = '1' or reset_request_uart = '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 => SDRAM_CLK, + 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 + ); + + -- UART connected to MAX3232 on optional IO board + uart1: entity work.uart_interface + generic map ( flow_control => 1, clk_frequency => (clk_freq_mhz * 1000000) ) + port map( + clk => clk, + reset => system_reset, + serial_in => uart1_rx, + serial_out => uart1_tx, + serial_rts => uart1_rts, + serial_cts => uart1_cts, + cpu_address => virtual_address(2 downto 0), + cpu_data_in => cpu_data_out, + cpu_data_out => uart1_data_out, + enable => uart1_cs, + interrupt => uart1_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 + ); + + -- SPI master device connected to Papilio Pro 8MB flash ROM + spimaster0: entity work.spimaster + port map( + clk => clk, + reset => system_reset, + cpu_address => virtual_address(2 downto 0), + cpu_wait => spimaster0_wait, + data_in => cpu_data_out, + data_out => spimaster0_data_out, + enable => spimaster0_cs, + req_read => req_read, + req_write => req_write, + slave_cs => flash_spi_cs, + slave_clk => flash_spi_clk, + slave_mosi => flash_spi_mosi, + slave_miso => flash_spi_miso + ); + + -- SPI master device connected to SD card socket on the IO board + spimaster1: entity work.spimaster + port map( + clk => clk, + reset => system_reset, + cpu_address => virtual_address(2 downto 0), + cpu_wait => spimaster1_wait, + data_in => cpu_data_out, + data_out => spimaster1_data_out, + enable => spimaster1_cs, + req_read => req_read, + req_write => req_write, + slave_cs => sdcard_spi_cs, + slave_clk => sdcard_spi_clk, + slave_mosi => sdcard_spi_mosi, + slave_miso => sdcard_spi_miso + ); + + -- 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 scales 32MHz Papilio Pro oscillator frequency to 128MHz + -- clock for our logic. + clock_pll: PLL_BASE + generic map ( + BANDWIDTH => "OPTIMIZED", -- "HIGH", "LOW" or "OPTIMIZED" + CLKFBOUT_MULT => 16, -- Multiply value for all CLKOUT clock outputs (1-64) + CLKFBOUT_PHASE => 0.0, -- Phase offset in degrees of the clock feedback output (0.0-360.0). + CLKIN_PERIOD => 31.25, -- Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz). + -- CLKOUT0_DIVIDE - CLKOUT5_DIVIDE: Divide amount for CLKOUT# clock output (1-128) + CLKOUT0_DIVIDE => 4, -- 32MHz * 16 / 4 = 128MHz. Adjust clk_freq_mhz constant (above) if you change this. + CLKOUT1_DIVIDE => 1, + CLKOUT2_DIVIDE => 1, + CLKOUT3_DIVIDE => 1, + CLKOUT4_DIVIDE => 1, + CLKOUT5_DIVIDE => 1, + -- CLKOUT0_DUTY_CYCLE - CLKOUT5_DUTY_CYCLE: Duty cycle for CLKOUT# clock output (0.01-0.99). + CLKOUT0_DUTY_CYCLE => 0.5, CLKOUT1_DUTY_CYCLE => 0.5, + CLKOUT2_DUTY_CYCLE => 0.5, CLKOUT3_DUTY_CYCLE => 0.5, + CLKOUT4_DUTY_CYCLE => 0.5, CLKOUT5_DUTY_CYCLE => 0.5, + -- CLKOUT0_PHASE - CLKOUT5_PHASE: Output phase relationship for CLKOUT# clock output (-360.0-360.0). + CLKOUT0_PHASE => 0.0, CLKOUT1_PHASE => 0.0, -- Capture clock + CLKOUT2_PHASE => 0.0, CLKOUT3_PHASE => 0.0, + CLKOUT4_PHASE => 0.0, CLKOUT5_PHASE => 0.0, + + CLK_FEEDBACK => "CLKFBOUT", -- Clock source to drive CLKFBIN ("CLKFBOUT" or "CLKOUT0") + COMPENSATION => "SYSTEM_SYNCHRONOUS", -- "SYSTEM_SYNCHRONOUS", "SOURCE_SYNCHRONOUS", "EXTERNAL" + DIVCLK_DIVIDE => 1, -- Division value for all output clocks (1-52) + REF_JITTER => 0.1, -- Reference Clock Jitter in UI (0.000-0.999). + RESET_ON_LOSS_OF_LOCK => FALSE -- Must be set to FALSE + ) + port map( + CLKIN => sysclk_32m, -- 1-bit input: Clock input + CLKFBOUT => clk_feedback, -- 1-bit output: PLL_BASE feedback output + CLKFBIN => clk_feedback, -- 1-bit input: Feedback clock input + -- CLKOUT0 - CLKOUT5: 1-bit (each) output: Clock outputs + CLKOUT0 => clk_unbuffered, -- 64MHz clock output + CLKOUT1 => open, + CLKOUT2 => open, + CLKOUT3 => open, + CLKOUT4 => open, + CLKOUT5 => open, + LOCKED => open, -- 1-bit output: PLL_BASE lock status output + RST => '0' -- 1-bit input: Reset input + ); + + -- Buffering of clocks + BUFG_clk: BUFG + port map( + O => clk, + I => clk_unbuffered + ); + +end Behavioral; diff --git a/vhdl/uart.vhd b/vhdl/uart.vhd new file mode 100644 index 0000000..e6a28f7 --- /dev/null +++ b/vhdl/uart.vhd @@ -0,0 +1,193 @@ +--+-----------------------------------+-------------------------------------+-- +--| ___ ___ | (c) 2013-2014 William R Sowerbutts |-- +--| ___ ___ ___ ___( _ ) / _ \ | will@sowerbutts.com |-- +--| / __|/ _ \ / __|_ / _ \| | | | | |-- +--| \__ \ (_) | (__ / / (_) | |_| | | A Z80 FPGA computer, just for fun |-- +--| |___/\___/ \___/___\___/ \___/ | |-- +--| | http://sowerbutts.com/ |-- +--+-----------------------------------+-------------------------------------+-- +--| UART implementation |-- +--+-------------------------------------------------------------------------+-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity uart is + generic ( + clk_frequency : natural := (128 * 1000000) + ); + port ( clk : in std_logic; + serial_out : out std_logic; + serial_in : in std_logic; + data_in : in std_logic_vector(7 downto 0); + data_in_load : in std_logic; + data_out : out std_logic_vector(7 downto 0); + data_out_ready : out std_logic; + bad_bit : out std_logic; + transmitter_busy : out std_logic; + can_transmit : in std_logic + ); +end uart; + +architecture Behavioral of uart is + + -- tested at 1,000,000bps with 48MHz clock. Works (apparently). + constant rx_sample_interval : unsigned(13 downto 0) := to_unsigned(clk_frequency / (115200 * 16) - 1, 14); -- clock speed / (baud x 16) - 1 ; eg 32MHz / (9600 * 16) - 1 = 207 + constant bit_duration : unsigned(13 downto 0) := to_unsigned(clk_frequency / (115200 * 1) - 1, 14); -- clock speed / baud - 1 ; eg 32MHz / 9600 - 1 = 3332 + + signal tx_counter : unsigned(13 downto 0) := to_unsigned(0, 14); + signal tx_shift_reg : std_logic_vector(8 downto 0) := "111111111"; + signal tx_bits_left : unsigned(3 downto 0) := to_unsigned(0, 4); + signal tx_busy : std_logic; + + signal rx_counter : unsigned(13 downto 0) := to_unsigned(0, 14); + signal rx_shift_reg : std_logic_vector(8 downto 0) := "000000000"; + signal rx_bits_got : unsigned(3 downto 0) := to_unsigned(0, 4); + signal rx_state : unsigned(7 downto 0) := (others => '0'); -- 10 bits x 16 samples each = at least 160 states. + signal rx_out_ready : std_logic := '0'; + signal data_out_buf : std_logic_vector(7 downto 0) := "00000000"; + + signal rx_clkin1 : std_logic := '0'; + signal rx_clkin2 : std_logic := '0'; + signal rx_sample1 : std_logic := '0'; + signal rx_sample2 : std_logic := '0'; + signal rx_sample3 : std_logic := '0'; + signal rx_sample_majority : std_logic; + + signal rx_badbit : std_logic := '0'; + +begin + + -- -- receiver -- -- + -- + -- Incoming data is oversampled 16 times. We check three samples in the + -- middle of each bit and take a simple majority. There is provision for + -- rejecting noise where a start bit should have been. We compensate for + -- small amounts of clock drift by potentially cutting a stop bit short. + -- + -- This is not dissimilar to how the AVR USART receiver works. + -- + + data_out_ready <= rx_out_ready; + bad_bit <= rx_badbit; + data_out <= data_out_buf; + + rx_sample_majority <= (rx_sample1 and rx_sample2) or (rx_sample1 and rx_sample3) or (rx_sample2 and rx_sample3); -- simple majority wins + + receiver: process(clk) + begin + if rising_edge(clk) then + -- Bring serial_in into our clock domain + rx_clkin1 <= serial_in; + rx_clkin2 <= rx_clkin1; -- rx_clkin2 should now be safe to use. + + -- We latch the incoming serial data at full clock speed (NOT divided down) + rx_sample1 <= rx_clkin2; + rx_out_ready <= '0'; + + -- bad bit + rx_badbit <= rx_badbit; + + -- clock divider + rx_counter <= rx_counter + 1; + if rx_counter = rx_sample_interval then + rx_counter <= (others => '0'); + + if rx_state = "00000000" then + -- line is in the idle state, we're waiting for a start bit! + -- the anticipation is killing me. + if rx_sample1 = '0' then + rx_state <= "00000001"; -- and we're off! + rx_counter <= (others => '0'); + end if; + elsif rx_state = "10011010" then + -- wait for the line to be idle. we don't leave this state until the serial line + -- goes high (it should be already, because we should be in mid stop bit). + if rx_sample1 = '1' then + rx_state <= "00000000"; + end if; + else + -- we're in the normal bit reception pattern + rx_state <= rx_state + 1; + + -- rx_sample1 contains the incoming serial data, latched + rx_sample3 <= rx_sample2; + rx_sample2 <= rx_sample1; + + -- when we have the three middle samples, update the shift register + if rx_state(3 downto 0) = "1001" then + rx_shift_reg <= rx_sample_majority & rx_shift_reg(rx_shift_reg'length-1 downto 1); + -- false start bit noise rejection: if we read the start bit as a logical 1, start over again. + if (rx_state(7 downto 4) = "0000") and (rx_sample_majority = '1') then + rx_badbit <= '1'; + rx_state <= "00000000"; + end if; + -- check stop bit framing and alert CPU if valid byte received + if (rx_state(7 downto 4) = "1001") then + if (rx_sample_majority = '1') then + data_out_buf <= rx_shift_reg(8 downto 1); + rx_out_ready <= '1'; + rx_badbit <= '0'; + else + rx_badbit <= '1'; + end if; + -- if line is high we can skip waiting for the line to go idle which buys us a little more tolerance for clock drift. + if rx_sample1 = '1' then + rx_state <= "00000000"; + end if; + end if; + end if; + end if; + end if; + end if; + end process; + + + -- -- transmitter -- -- + -- + -- just clock out the bits, damn it. + -- + + serial_out <= tx_shift_reg(0); -- we always output the bottom bit of the shift register. + transmitter_busy <= tx_busy; -- or data_in_load; + + transmitter: process(clk) + begin + + if rising_edge(clk) then + tx_busy <= '1'; + + if tx_bits_left = 0 then + -- idle + if data_in_load = '1' then + tx_shift_reg <= data_in & '0'; -- data bits, start bit + tx_bits_left <= to_unsigned(10, 4); -- total ten bits to transmit including stop bit + tx_counter <= (others => '0'); -- reset counter + else + if can_transmit = '0' then + tx_busy <= '1'; + else + tx_busy <= '0'; + end if; + end if; + else + -- busy + if (tx_counter = 0) and (tx_bits_left = 10) and (can_transmit = '0') then + -- do nothing, we're waiting for our peer to indicate that we can transmit + tx_counter <= (others => '0'); + else + tx_counter <= tx_counter + 1; + if tx_counter = bit_duration then + -- shift out the next bit + tx_shift_reg <= '1' & tx_shift_reg(8 downto 1); -- stop bit and line idle state are both 1 so shift that in the top + tx_counter <= (others => '0'); -- reset counter + tx_bits_left <= tx_bits_left - 1; + end if; + end if; + end if; + end if; + end process; + +end Behavioral; + diff --git a/vhdl/uart_interface.vhd b/vhdl/uart_interface.vhd new file mode 100644 index 0000000..c658d44 --- /dev/null +++ b/vhdl/uart_interface.vhd @@ -0,0 +1,216 @@ +--+-----------------------------------+-------------------------------------+-- +--| ___ ___ | (c) 2013-2014 William R Sowerbutts |-- +--| ___ ___ ___ ___( _ ) / _ \ | will@sowerbutts.com |-- +--| / __|/ _ \ / __|_ / _ \| | | | | |-- +--| \__ \ (_) | (__ / / (_) | |_| | | A Z80 FPGA computer, just for fun |-- +--| |___/\___/ \___/___\___/ \___/ | |-- +--| | http://sowerbutts.com/ |-- +--+-----------------------------------+-------------------------------------+-- +--| Combine the UART with a receive FIFO and provide an interface to the |-- +--| microprocessor. |-- +--+-------------------------------------------------------------------------+-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity uart_interface is + Generic ( watch_for_reset : integer := 0; + clk_frequency : natural := (128*1000000); + flow_control : integer := 0) ; + Port ( clk : in std_logic; + reset : in std_logic; + -- rs232 + serial_in : in std_logic; + serial_out : out std_logic; + -- flow control (optional) + serial_cts : in std_logic; + serial_rts : out std_logic; + -- memory interface + cpu_address : in std_logic_vector (2 downto 0); + cpu_data_in : in std_logic_vector (7 downto 0); + cpu_data_out : out std_logic_vector (7 downto 0); + reset_out : out std_logic; + enable : in std_logic; + interrupt : out std_logic; + req_read : in std_logic; + req_write : in std_logic); +end uart_interface; + +architecture Behavioral of uart_interface is + signal uart_data_in : std_logic_vector(7 downto 0); + signal uart_data_out : std_logic_vector(7 downto 0); + signal fifo_data_out : std_logic_vector(7 downto 0); + signal fifo_data_ready : std_logic; + signal uart_rx_ready : std_logic; + signal uart_tx_busy : std_logic; + signal uart_badbit : std_logic; + signal uart_data_load : std_logic; + signal fifo_data_ack : std_logic; + signal uart_status_register : std_logic_vector(7 downto 0); + signal rx_interrupt_enable : std_logic := '0'; + signal tx_interrupt_enable : std_logic := '0'; + signal rx_interrupt_signal : std_logic := '0'; + signal tx_interrupt_signal : std_logic := '0'; + signal uart_tx_was_busy : std_logic := '0'; + signal fifo_data_was_ready : std_logic := '0'; + signal cts_clk1 : std_logic; + signal cts_clk2 : std_logic; + signal fifo_nearly_full : std_logic; + signal can_transmit : std_logic; + + type reset_seq_state is ( + st_idle, + st_seen1, + st_seen2, + st_seen3, + st_seen4, + st_seen5 + ); + signal reset_seq : reset_seq_state := st_idle; + signal reset_saw_byte0 : std_logic; + signal reset_saw_byte1 : std_logic; +begin + + -- this whole module could really do with a bit of a rethink. + with cpu_address select + cpu_data_out <= + uart_status_register when "000", + fifo_data_out when others; + + uart_data_in <= cpu_data_in; + + uart_status_register <= fifo_data_ready & uart_tx_busy & '0' & uart_badbit & rx_interrupt_enable & tx_interrupt_enable & rx_interrupt_signal & tx_interrupt_signal; + + interrupt <= (rx_interrupt_signal and rx_interrupt_enable) or (tx_interrupt_signal and tx_interrupt_enable); + + -- this decodes cpu_address="001" in a rather longwinded way. + uart_data_load <= cpu_address(0) and (not cpu_address(1)) and (not cpu_address(2)) and enable and req_write; + fifo_data_ack <= cpu_address(0) and (not cpu_address(1)) and (not cpu_address(2)) and enable and req_read; + + -- optional hardware flow control + process(fifo_nearly_full, cts_clk2) + begin + if flow_control = 1 then + serial_rts <= fifo_nearly_full; + can_transmit <= (not cts_clk2); + else + serial_rts <= '0'; + can_transmit <= '1'; + end if; + end process; + + -- optional reset on data sequence + process(uart_data_out) + begin + if watch_for_reset = 1 then + if uart_data_out = "00100001" then + reset_saw_byte0 <= '1'; + reset_saw_byte1 <= '0'; + elsif uart_data_out = "01111110" then + reset_saw_byte0 <= '0'; + reset_saw_byte1 <= '1'; + else + reset_saw_byte0 <= '0'; + reset_saw_byte1 <= '0'; + end if; + end if; + end process; + + + process(clk) + begin + if rising_edge(clk) then + if reset = '1' then + fifo_data_was_ready <= '0'; + rx_interrupt_enable <= '0'; + rx_interrupt_signal <= '0'; + tx_interrupt_enable <= '0'; + tx_interrupt_signal <= '0'; + uart_tx_was_busy <= '0'; + reset_out <= '0'; + reset_seq <= st_idle; + cts_clk1 <= '0'; + cts_clk2 <= '0'; + else + tx_interrupt_signal <= tx_interrupt_signal; + rx_interrupt_signal <= rx_interrupt_signal; + + -- bring CTS into our clock domain + if flow_control = 1 then + cts_clk1 <= serial_cts; + cts_clk2 <= cts_clk1; + end if; + + -- handle writes to the status register + if enable = '1' and req_write = '1' and cpu_address = "000" then + rx_interrupt_enable <= cpu_data_in(3); + tx_interrupt_enable <= cpu_data_in(2); + rx_interrupt_signal <= cpu_data_in(1); + tx_interrupt_signal <= cpu_data_in(0); + end if; + + uart_tx_was_busy <= uart_tx_busy; + fifo_data_was_ready <= fifo_data_ready; + + if uart_tx_was_busy = '1' and uart_tx_busy = '0' then + tx_interrupt_signal <= '1'; + end if; + + if fifo_data_ready = '1' and fifo_data_was_ready = '0' then + rx_interrupt_signal <= '1'; + end if; + + if watch_for_reset = 1 then + if uart_rx_ready = '1' then + reset_seq <= st_idle; -- end up here unless we match the conditions below + reset_out <= '0'; + case reset_seq is + when st_idle => if reset_saw_byte0 = '1' then reset_seq <= st_seen1; end if; + when st_seen1 => if reset_saw_byte1 = '1' then reset_seq <= st_seen2; end if; + when st_seen2 => if reset_saw_byte0 = '1' then reset_seq <= st_seen3; end if; + when st_seen3 => if reset_saw_byte1 = '1' then reset_seq <= st_seen4; end if; + when st_seen4 => if reset_saw_byte0 = '1' then reset_seq <= st_seen5; end if; + when st_seen5 => if reset_saw_byte1 = '1' then reset_out <= '1'; end if; + when others => + end case; + else + reset_seq <= reset_seq; + reset_out <= '0'; + end if; + end if; + end if; + end if; + end process; + + fifo_instance: entity work.fifo + PORT MAP( + clk => clk, + reset => reset, + data_in => uart_data_out, + data_out => fifo_data_out, + read_ready => fifo_data_ready, + read_en => fifo_data_ack, + write_ready => open, + write_en => uart_rx_ready, + high_water_mark => fifo_nearly_full + ); + + uart_instance: entity work.uart + GENERIC MAP( + clk_frequency => clk_frequency + ) + PORT MAP( + clk => clk, + serial_out => serial_out, + serial_in => serial_in, + data_in => uart_data_in, + data_in_load => uart_data_load, + data_out => uart_data_out, + data_out_ready => uart_rx_ready, + bad_bit => uart_badbit, + transmitter_busy => uart_tx_busy, + can_transmit => can_transmit + ); + +end Behavioral;