Papilio Plus/Pacman

From Hamsterworks Wiki!

Jump to: navigation, search

This Papilio Plus project is my first real attempt at getting a 'useful' project running on the board.

Please note that a Windows tool is used to convert ROM images into VHDL source, so Linux users will have an extra bit of work to do.

Contents

Project setup

Download and extract the package from http://www.fpgaarcade.com/pac_main.htm.

If you have legal access to the original ROMs then put them in the 'roms' directory. If you do not have them there is a set of demonstration program included in the archive.

Convert the ROMs

In the base directory run "build_roms.bat". Make sure that there are no errors.

Creating a project file

Create a new ISE project, using these settings:

Pacman-settings.png

And then add in all the VHD files in then 'source' 'roms' and directory.

New UCF file

The existing UCF file is for a Spartan 3E board, and needs to be completely replaced with the following (if using the Papilio Spartan 6 board with the Arcade megawing):

NET I_CLK_REF       TNM_NET = clk_ref_grp;
TIMESPEC TS01 = PERIOD : clk_ref_grp : 31.25 : PRIORITY 1;   # 32.00 MHz

NET O_VIDEO_B(0)     LOC="P99"  | IOSTANDARD=LVTTL;  # B0
NET O_VIDEO_B(1)     LOC="P97"  | IOSTANDARD=LVTTL;  # B1
NET O_VIDEO_B(2)     LOC="P92"  | IOSTANDARD=LVTTL;  # B2
NET O_VIDEO_B(3)     LOC="P87"  | IOSTANDARD=LVTTL;  # B3
 
NET O_VIDEO_G(0)    LOC="P84"  | IOSTANDARD=LVTTL;  # B4
NET O_VIDEO_G(1)    LOC="P82"  | IOSTANDARD=LVTTL;  # B5
NET O_VIDEO_G(2)    LOC="P80"  | IOSTANDARD=LVTTL;  # B6
NET O_VIDEO_G(3)    LOC="P78"  | IOSTANDARD=LVTTL;  # B7

NET O_VIDEO_R(0)      LOC="P118" | IOSTANDARD=LVTTL;  # C4
NET O_VIDEO_R(1)      LOC="P119" | IOSTANDARD=LVTTL;  # C5
NET O_VIDEO_R(2)      LOC="P120" | IOSTANDARD=LVTTL;  # C6
NET O_VIDEO_R(3)      LOC="P121" | IOSTANDARD=LVTTL;  # C7

NET O_VSYNC       LOC="P116" | IOSTANDARD=LVTTL;  # C2
NET O_HSYNC       LOC="P117" | IOSTANDARD=LVTTL;  # C3

#NET I_SW(3)    LOC = "N17" | IOSTANDARD = LVTTL | PULLUP;
#NET I_SW(2)    LOC = "H18" | IOSTANDARD = LVTTL | PULLUP;
#NET I_SW(1)    LOC = "L14" | IOSTANDARD = LVTTL | PULLUP;
#NET I_SW(0)    LOC = "L13" | IOSTANDARD = LVTTL | PULLUP;

NET I_Button(0) LOC="P95"  | IOSTANDARD=LVTTL; # B9   Up
NET I_Button(1) LOC="P74"  | IOSTANDARD=LVTTL; # B8   Left
NET I_Button(2) LOC="P59"  | IOSTANDARD=LVTTL; # B11  Right
NET I_Button(3) LOC="P62"  | IOSTANDARD=LVTTL; # B10  Down

NET O_LED(0)    LOC="P75"  | IOSTANDARD=LVTTL; # A7  LED1
NET O_LED(1)    LOC="P67"  | IOSTANDARD=LVTTL; # A6  LED2
NET O_LED(2)    LOC="P66"  | IOSTANDARD=LVTTL; # A5  LED3
NET O_LED(3)    LOC="P61"  | IOSTANDARD=LVTTL; # A4  LED3

NET I_RESET     LOC="P85"  | IOSTANDARD=LVTTL;                             # A11

NET I_CLK_REF   LOC = "P94" | IOSTANDARD = LVCMOS25 | PERIOD = 31.25ns;

NET O_AUDIO_L    LOC="P98"  | IOSTANDARD=LVTTL;                           # A14 - Audio 
NET O_AUDIO_R    LOC="P100" | IOSTANDARD=LVTTL;                           # A15 - Audio

Correcting the clocking

The clocking for the original project used a DCM primitive which I had trouble to recreate. It was also based around a 50MHz reference clock, not the 32MHz on the Papilio board.

Instead of bashing my head against a brick wall I used the clocking wizard as follows:

Pacman-cw-1.png Pacman-cw-2.png Pacman-cw-3.png Pacman-cw-4.png

Replace the u_clocking.vhd with the following

--
-- A simulation model of Pacman hardware
-- Copyright (c) MikeJ - January 2006
--
-- 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 CODE 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.
--
-- You are responsible for any legal issues arising from your use of this code.
--
-- The latest version of this file can be found at: www.fpgaarcade.com
--
-- Email pacman@fpgaarcade.com
--
-- Revision list
--
-- version 005 spartan6 - hamster@snap.net.nz
-- version 004 spartan3e release
-- version 001 Jan 2006 release - initial release of this module

library ieee;
  use ieee.std_logic_1164.all;
  use ieee.std_logic_unsigned.all;
  use ieee.numeric_std.all;

library UNISIM;
use UNISIM.Vcomponents.all;

entity PACMAN_CLOCKS is
  port (
    I_CLK_REF         : in    std_logic;
    I_RESET_L         : in    std_logic;
    --
    O_CLK_REF         : out   std_logic;
    --
    O_ENA_12          : out   std_logic;
    O_ENA_6           : out   std_logic;
    O_CLK             : out   std_logic;
    O_RESET           : out   std_logic
    );
end;

architecture RTL of PACMAN_CLOCKS is

  signal reset_dcm_h            : std_logic;
  signal clk_ref_ibuf           : std_logic;
  signal clk                    : std_logic;
  signal delay_count            : std_logic_vector(7 downto 0) := (others => '0');
  signal div_cnt                : std_logic_vector(1 downto 0);

  attribute DLL_FREQUENCY_MODE    : string;
  attribute DUTY_CYCLE_CORRECTION : string;
  attribute CLKOUT_PHASE_SHIFT    : string;
  attribute PHASE_SHIFT           : integer;
  attribute CLKFX_MULTIPLY        : integer;
  attribute CLKFX_DIVIDE          : integer;
  attribute CLKDV_DIVIDE          : real;
  attribute STARTUP_WAIT          : string;
  attribute CLKIN_PERIOD          : real;

  -- The original uses a 6.144 MHz clock
  --
  -- We are then clock enabling the whole design at /4 and /2

  function str2bool (str : string) return boolean is
  begin
    if (str = "TRUE") or (str = "true") then
      return TRUE;
    else
      return FALSE;
    end if;
  end str2bool;

  component MyDCM
  port
   (CLK_IN1           : in     std_logic;
    CLK_24M          : out    std_logic;
    RESET             : in     std_logic
   );
  end component;

begin

  reset_dcm_h <= not I_RESET_L;

MyDCM_inst : MyDCM
  port map
   (
	 CLK_IN1 => I_CLK_REF,
    CLK_24M => clk,
    RESET  => reset_dcm_h
	);


  O_CLK_REF <= clk;
  O_CLK <= clk;

  p_delay : process(I_RESET_L, clk)
  begin
    if (I_RESET_L = '0') then
      delay_count <= x"00"; -- longer delay for cpu
      O_RESET <= '1';
    elsif rising_edge(clk) then
      if (delay_count(7 downto 0) = (x"FF")) then
        delay_count <= (x"FF");
        O_RESET <= '0';
      else
        delay_count <= delay_count + "1";
        O_RESET <= '1';
      end if;
    end if;
  end process;

  p_clk_div : process(I_RESET_L, clk)
  begin
    if (I_RESET_L = '0') then
      div_cnt <= (others => '0');
    elsif rising_edge(clk) then
      div_cnt <= div_cnt + "1";
    end if;
  end process;

  p_assign_ena : process(div_cnt)
  begin
    O_ENA_12 <= div_cnt(0);
    O_ENA_6  <= div_cnt(0) and not div_cnt(1);
  end process;
end RTL;

Updating the scan line doubler

The original game uses a 16kHz horizontal refresh rate, which is too slow for most monitors.

A scan rate doubler has been added to the hardware to make it display (it can be turned off and on by one of the switches).

Unfortunately it used a RAM4B_S8_S8 primitive, that is available on the Spartan 6. So it was out with the IP generator to make something similar "MyRAM4B_S8_S8".

Pacman-brw-1.png Pacman-brw-2.png Pacman-brw-3.png Pacman-brw-4.png

The other change needed was that the WEA and WEB ports on the generated part is now a STD_LOGIC_VECTOR rather than just STD_LOGIC, so the definitions needed to be updated. Here is the updated source:

 
--
-- A simulation model of Pacman hardware
-- VHDL conversion by MikeJ - October 2002
--
-- FPGA PACMAN video scan doubler
--
-- based on a design by Tatsuyuki Satoh
--
--
-- 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 CODE 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.
--
-- You are responsible for any legal issues arising from your use of this code.
--
-- The latest version of this file can be found at: www.fpgaarcade.com
--
-- Email pacman@fpgaarcade.com
--
-- Revision list
--
-- version 005 spartan6 release (hamster@snap.net.nz)
-- version 004 spartan3e release
-- version 003 Jan 2006 release, general tidy up
-- version 002 initial release
--
library ieee;
  use ieee.std_logic_1164.all;
  use ieee.std_logic_unsigned.all;
  use ieee.numeric_std.all;
	
library UNISIM;
  use UNISIM.Vcomponents.all;

use work.pkg_pacman.all;

entity PACMAN_DBLSCAN is
  port (
	I_R               : in    std_logic_vector( 2 downto 0);
	I_G               : in    std_logic_vector( 2 downto 0);
	I_B               : in    std_logic_vector( 1 downto 0);
	I_HSYNC           : in    std_logic;
	I_VSYNC           : in    std_logic;
	--
	O_R               : out   std_logic_vector( 2 downto 0);
	O_G               : out   std_logic_vector( 2 downto 0);
	O_B               : out   std_logic_vector( 1 downto 0);
	O_HSYNC           : out   std_logic;
	O_VSYNC           : out   std_logic;
	--
	ENA_6             : in    std_logic;
	ENA_12            : in    std_logic;
	CLK               : in    std_logic
	);
end;

architecture RTL of PACMAN_DBLSCAN is
  signal ram_ena_12  : std_logic;
  signal ram_ena_6   : std_logic;
  --
  -- input timing
  --
  signal hsync_in_t1 : std_logic;
  signal vsync_in_t1 : std_logic;
  signal hpos_i      : std_logic_vector(8 downto 0) := (others => '0');    -- input capture postion
  signal ibank       : std_logic;
  signal we_a        : std_logic_vector(0 downto 0);
  signal we_b        : std_logic_vector(0 downto 0);
  signal rgb_in      : std_logic_vector(7 downto 0);
  --
  -- output timing
  --
  signal hpos_o      : std_logic_vector(8 downto 0) := (others => '0');
  signal ohs         : std_logic;
  signal ohs_t1      : std_logic;
  signal ovs         : std_logic;
  signal ovs_t1      : std_logic;
  signal obank       : std_logic;
  signal obank_t1    : std_logic;
  --
  signal vs_cnt      : std_logic_vector(2 downto 0);
  signal rgb_out_a   : std_logic_vector(7 downto 0);
  signal rgb_out_b   : std_logic_vector(7 downto 0);
  
  COMPONENT MyRAMB4_S8_S8
  PORT (
    clka : IN STD_LOGIC;
    rsta : IN STD_LOGIC;
    ena : IN STD_LOGIC;
    wea : IN STD_LOGIC_VECTOR(0 DOWNTO 0);
    addra : IN STD_LOGIC_VECTOR(8 DOWNTO 0);
    dina : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
    douta : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
    clkb : IN STD_LOGIC;
    rstb : IN STD_LOGIC;
    enb : IN STD_LOGIC;
    web : IN STD_LOGIC_VECTOR(0 DOWNTO 0);
    addrb : IN STD_LOGIC_VECTOR(8 DOWNTO 0);
    dinb : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
    doutb : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
	);
	END COMPONENT;

begin

  p_input_timing : process
	variable rising_h : boolean;
	variable rising_v : boolean;
  begin
	wait until rising_edge (CLK);
	if (ENA_6 = '1') then
	  hsync_in_t1 <= I_HSYNC;
	  vsync_in_t1 <= I_VSYNC;

	  rising_h := (I_HSYNC = '1') and (hsync_in_t1 = '0');
	  rising_v := (I_VSYNC = '1') and (vsync_in_t1 = '0');

	  if rising_v then
		ibank <= '0';
	  elsif rising_h then
		ibank <= not ibank;
	  end if;

	  if rising_h then
		hpos_i <= (others => '0');
	  else
		hpos_i <= hpos_i + "1";
	  end if;
	end if;
  end process;

  we_a(0) <=     ibank;
  we_b(0) <= not ibank;
  rgb_in <= I_B & I_G & I_R;

  u_ram_a : MyRAMB4_S8_S8
	port map (
	  -- output
	  DOutB   => rgb_out_a,
	  DInB   => x"00",
	  ADDRB => hpos_o,
	  WEB   => "0",
	  ENB   => ENA_12,
	  RSTB  => '0',
	  CLKB  => CLK,

	  -- input
	  DOutA   => open,
	  DInA   => rgb_in,
	  ADDRA => hpos_i,
	  WEA   => we_a,
	  ENA   => ENA_6,
	  RSTA  => '0',
	  CLKA  => CLK
	  );

  u_ram_b : MyRAMB4_S8_S8
	port map (
	  -- output
	  DOutB   => rgb_out_b,
	  DInB   => x"00",
	  ADDRB => hpos_o,
	  WEB   => "0",
	  ENB   => ENA_12,
	  RSTB  => '0',
	  CLKB  => CLK,

	  -- input
	  DOutA   => open,
	  DInA   => rgb_in,
	  ADDRA => hpos_i,
	  WEA   => we_b,
	  ENA   => ENA_6,
	  RSTA  => '0',
	  CLKA  => CLK
	  );

  p_output_timing : process
	variable rising_h : boolean;
  begin
	wait until rising_edge (CLK);
	if  (ENA_12 = '1') then
	  rising_h := ((ohs = '1') and (ohs_t1 = '0'));

	  if rising_h or (hpos_o = "101111111") then
		hpos_o <= (others => '0');
	  else
		hpos_o <= hpos_o + "1";
	  end if;

	  if (ovs = '1') and (ovs_t1 = '0') then -- rising_v
		obank <= '0';
		vs_cnt <= "000";
	  elsif rising_h then
		obank <= not obank;
		if (vs_cnt(2) = '0') then
		  vs_cnt <= vs_cnt + "1";
		end if;
	  end if;

	  ohs <= I_HSYNC; -- reg on clk_12
	  ohs_t1 <= ohs;

	  ovs <= I_VSYNC; -- reg on clk_12
	  ovs_t1 <= ovs;
	end if;
  end process;

  p_op : process
  begin
	wait until rising_edge (CLK);
	if (ENA_12 = '1') then
	  O_HSYNC <= '0';
	  if (hpos_o < 32) then -- may need tweaking !
		O_HSYNC <= '1';
	  end if;

	  obank_t1 <= obank;
	  if (obank_t1 = '1') then
		O_B <= rgb_out_b(7 downto 6);
		O_G <= rgb_out_b(5 downto 3);
		O_R <= rgb_out_b(2 downto 0);
	  else
		O_B <= rgb_out_a(7 downto 6);
		O_G <= rgb_out_a(5 downto 3);
		O_R <= rgb_out_a(2 downto 0);
	  end if;

	  O_VSYNC <= not vs_cnt(2);
	end if;
  end process;
end architecture RTL;

The project should now build, download and run without issue!

Personal tools