T80 CPU Sim - red-bote/VHDL_Demos GitHub Wiki

T80 VHDL CPU IP Core

On Open Cores (requires registration).

Equivalent to Open Cores Version : 0247

From Pac-Man VHDL original sources ... pacman_rel004_sp3e/t80_latest/T80_v301.zip ... T80/t80_original_latest/t80.tar.gz

Version 0350 standalone project repo

Version 351 development for the MIST FPGA board

FreeCores is a fork of almost all cores that was once on OpenCores.org

Pretty good reference for Z80 on homepage of Watis Leelapatra Khon Kaen University.

Minimal T80 Circuit

The VHDL project implements a minimal test-bench for experimentation with the T80 core.

Import T80 files from trunk/rtl/vhdl/:

  • T80se.vhd (synchronous version with clock-enable)
  • T80.vhd
  • T80_MCode.vhd
  • T80_ALU.vhd
  • T80_Reg.vhd (not extended register set)
  • T80_Pack.vhd (T80 package library)

Instantiate T80 with minimal necessary signal connections:

architecture Behavioral of rtl_top is
    signal reset_l : std_logic;
    signal clk_cpu : std_logic;
    signal clk_cnt : std_logic_vector (3 downto 0);
    signal cpu_addr : std_logic_vector (15 downto 0);
    signal cpu_data_in : std_logic_vector (7 downto 0);
    signal cpu_data_out : std_logic_vector (7 downto 0);
begin
    u_clk : entity work.clock_div
    port map (
        C => clk,
        CLR => reset,
        Q => clk_cnt
        );
    clk_cpu <= clk_cnt(3);

    reset_l <= not reset;

    u_cpu : entity work.T80s
        port map(
            RESET_n => reset_l,
            CLK_n   => clk_cpu,
            WAIT_n  => '1', -- cpu_wait_l,
            INT_n   => '1', -- cpu_int_l,
            NMI_n   => '1', -- cpu_nmi_l,
            BUSRQ_n => '1', -- cpu_busrq_l,
            M1_n    => open, -- cpu_m1_l,
            MREQ_n  => open, -- cpu_mreq_l,
            IORQ_n  => open, -- cpu_iorq_l,
            RD_n    => open, -- cpu_rd_l,
            WR_n    => open, -- cpu_wr_l,
            A       => cpu_addr,
            DI      => (others => '1'), -- cpu_data_in,
            DO      => cpu_data_out
        );
end Behavioral;

With only a clock source connected, the Z80 state can be evaluated for some minimal functionality.

images/t80-CPU-Sim/HxiUzl.png

  • At init: PC=0, SP=$FFFF
  • DI tied to $FF
  • @ M1:=0, read opcode $FF (RST $38) from DI and PC+=1
  • RST $38: push PCl and PCh to stack
  • SP decrement, $FFFE to address bus, PCh ($00) to DO
  • SP decrement, $FFFD to address bus, PCl ($01) to DO
  • DI tied to $FF
  • @ next M1, Read $FF from DI and PC+=1 (opcode RST $38 again)
  • $0038 to A, then read opcode $FF to DI
  • PC:=$0038+1=$0039, $0039 pushed to $FFFC and $FFFB

Program ROM

Create z80 test code and run assembler:

org 0
l_loop:
  add a, 0x5A      ;0100  c6 5a
  jr  l_loop       ;0102  18 fc  (jr $-3)

Disassembly in code comments from output of z80dasm -t a.bin

Convert binary to VHDL:

hex2rom -b  a.bin  prog_rom 6l8s > t80_vga/t80_vga.srcs/sources_1/new/prog_rom.vhd 

Reset Behavioral Simulation in Vivado after update to prog_rom.vhd.

The T80 with program ROM (but no work RAM), enables the data bus from the ROM to the CPU using only the RD_n signal from T80.

    u_prog_rom : entity work.prog_rom
	port map (
        Clk => clk_cpu, -- can 100 Mhz system clock be used here?
        A => cpu_addr(5 downto 0),
        D => rom_data_out
    );
    cpu_data_in <= rom_data_out when cpu_rd_l = '0' else (others => '1');

    u_cpu : entity work.T80se
        port map(
            RESET_n => reset_l,
            CLK_n   => clk_cpu, -- system clock?
            CLKEN   => '1',     -- clock enabled for CPU frequency?
            WAIT_n  => '1',
            INT_n   => '1',
            NMI_n   => '1',
            BUSRQ_n => '1',
            M1_n    => open,
            MREQ_n  => open,
            IORQ_n  => open,
            RD_n    => cpu_rd_l,
            WR_n    => open,
            A       => cpu_addr,
            DI      => cpu_data_in,
            DO      => open
        );

images/t80-CPU-Sim/uEJ1gz.png

  • 4 bytes program code: $C6 $5A $18 $FC
  • address bus shows op-code bytes fetched from $0000, $0001, $0002, $0003
  • address bus also shows refresh accesses R==$00, R==$01, R==$02
  • Refresh reads are distinguished from normal memory reads because RD_n is not active

Unlike a real Z80, the T80 has separate In and Out data bus. Although the program has no operation to write data out, the $5A of the ld A, 0x5A appears on DO bus (have not confirmed if this mimics a real Z80 behavior).

Adding Work RAM

A single-port RAM with enable is added with additional VHDL code to provide an extremely simple example of RAM and ROM chip selection:

    ram_cs <= cpu_addr(15);
    mem_rd <= not(cpu_rd_l or cpu_mreq_l);

    process(ram_cs, mem_rd)
    begin
        cpu_data_in <= (others => '1');
        if mem_rd = '1' then
            if ram_cs = '1' then
                cpu_data_in <= ram_data_out(7 downto 0);
            else
                cpu_data_in <= rom_data_out;
            end if;
        end if;
    end process;

    ram_wr_en <= not cpu_wr_l;
    ram_data_in <= x"00" & cpu_data_out;

    u_work_ram : entity work.rams_08
    port map (
        clk => clk, -- system clock
        en => ram_cs,
        we => ram_wr_en,
        a => cpu_addr(5 downto 0),
        di => ram_data_in,
        do => ram_data_out
        );

Z80 code to test writing to RAM:

org 0
  ld hl, l_ram_start  ;0100  21 00 80
l_loop:
  inc a               ;0103  3c
  ld (hl), a          ;0104  77
  inc l               ;0105  2c
  jr  l_loop          ;0106  18 fb  (jr $-3)

org 0x8000
l_ram_start:
  db  0x00

In the simulation trace the opcodes are read from ROM and appear on the DI bus. Address $8000 is accessed to write data to RAM. The RAM is shown with the first few RAM locations written. There is no read from RAM.

images/t80-CPU-Sim/lyrwek.png

The following simple test writes the starting value $5A to RAM[0] and then increments it in a loop.

org 0
l_0:
  ld hl, l_ram_start  ;0100  21 00 80
  ld a, 0x5A          ;0103  3e 5a
  ld (hl), a          ;0105  77
l_loop:
  inc (hl)            ;0106  34
  jr  l_loop          ;0107  18 fd  (jr $-1)
org 0x8000
l_ram_start:
  db  0x00
  • Data $5A is first seen on DI with ld a, $5A
  • Data $5A is seen on DO with ld (hl), a
  • inc (hl) is shorthand for reading data from address pointed to by HL, increment and write back to same address in HL.
  • Data $5A is seen on DI, then incremented value $5B is seen on DO written back to RAM.

images/t80-CPU-Sim/ZucZGc.png

Test trace exhibits that inc (hl) is more or less equivalent to:

  ld a, (hl)
  inc a
  ld (hl), a

Next