aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--basys3/basys3.srcs/apu.vhd32
-rw-r--r--basys3/basys3.srcs/apu_lut_reader.vhd (renamed from basys3/basys3.srcs/apu_LUT_reader.vhd)4
-rw-r--r--basys3/basys3.srcs/apu_lut_reader_tb.vhd (renamed from basys3/basys3.srcs/apu_tb_LUT_reader.vhd)10
-rw-r--r--basys3/basys3.srcs/apu_note_to_frequency.vhd6
-rw-r--r--basys3/basys3.srcs/apu_note_to_frequency_tb.vhd14
-rw-r--r--basys3/basys3.srcs/er_ram.vhd53
-rw-r--r--basys3/basys3.srcs/er_ram_mod.vhd34
-rw-r--r--basys3/basys3.srcs/er_ram_mod_tb.vhd81
-rw-r--r--basys3/basys3.srcs/er_ram_tb.vhd116
-rw-r--r--basys3/basys3.srcs/ppu.vhd331
-rw-r--r--basys3/basys3.srcs/ppu_addr_dec.vhd51
-rw-r--r--basys3/basys3.srcs/ppu_addr_dec.vhdl51
-rw-r--r--basys3/basys3.srcs/ppu_addr_dec_tb.vhd86
-rw-r--r--basys3/basys3.srcs/ppu_addr_dec_tb.vhdl86
-rw-r--r--basys3/basys3.srcs/ppu_aux.vhd52
-rw-r--r--basys3/basys3.srcs/ppu_aux_tb.vhd100
-rw-r--r--basys3/basys3.srcs/ppu_comp.vhd36
-rw-r--r--basys3/basys3.srcs/ppu_comp_tb.vhd40
-rw-r--r--basys3/basys3.srcs/ppu_consts.vhd73
-rw-r--r--basys3/basys3.srcs/ppu_pceg.vhd (renamed from basys3/basys3.srcs/ppu_pceg.vhdl)16
-rw-r--r--basys3/basys3.srcs/ppu_pceg_tb.vhd (renamed from basys3/basys3.srcs/ppu_pceg_tb.vhdl)24
-rw-r--r--basys3/basys3.srcs/ppu_plut.vhd69
-rw-r--r--basys3/basys3.srcs/ppu_sprite_bg.vhd138
-rw-r--r--basys3/basys3.srcs/ppu_sprite_bg_tb.vhd107
-rw-r--r--basys3/basys3.srcs/ppu_sprite_fg.vhd192
-rw-r--r--basys3/basys3.srcs/ppu_sprite_fg_tb.vhd140
-rw-r--r--basys3/basys3.srcs/ppu_sprite_transform.vhd24
-rw-r--r--basys3/basys3.srcs/ppu_sprite_transform_tb.vhd66
-rw-r--r--basys3/basys3.srcs/ppu_vga_native.vhd95
-rw-r--r--basys3/basys3.srcs/ppu_vga_native_tb.vhd89
-rw-r--r--basys3/basys3.srcs/ppu_vga_tiny.vhd73
-rw-r--r--basys3/basys3.srcs/sources_1/ip/ppu_bam/ppu_bam.xci2
-rw-r--r--basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci40
-rw-r--r--basys3/basys3.xpr155
-rw-r--r--docs/architecture.md59
-rw-r--r--style.md31
-rw-r--r--test/sdl2/.gitignore2
-rw-r--r--test/sdl2/main.c48
-rw-r--r--test/sdl2/makefile18
-rw-r--r--test/sdl2/readme.md6
40 files changed, 2223 insertions, 427 deletions
diff --git a/basys3/basys3.srcs/apu.vhd b/basys3/basys3.srcs/apu.vhd
index 4a594ab..1fff1e8 100644
--- a/basys3/basys3.srcs/apu.vhd
+++ b/basys3/basys3.srcs/apu.vhd
@@ -3,29 +3,29 @@ use ieee.std_logic_1164.all;
--use ieee.numeric_std.all;
entity apu is port(
- CLK100: in std_logic; -- system clock
- RESET: in std_logic; -- global (async) system reset
- DATA: in std_logic_vector(15 downto 0);
- SOUND: out std_logic);
+ CLK100 : in std_logic; -- system clock
+ RESET : in std_logic; -- global (async) system reset
+ DATA : in std_logic_vector(15 downto 0);
+ SOUND : out std_logic);
- -- EN: in std_logic; -- PPU VRAM enable (enable ADDR and DATA tri-state drivers)
- -- WEN: in std_logic; -- PPU VRAM write enable
- -- ADDR: in std_logic_vector(15 downto 0); -- PPU VRAM ADDR
- -- R,G,B: out std_logic_vector(3 downto 0);
- -- NVSYNC, NHSYNC: out std_logic; -- native VGA out
- -- TVSYNC, TVBLANK, THSYNC, THBLANK: out std_logic); -- tiny VGA out
+ -- EN : in std_logic; -- PPU VRAM enable (enable ADDR and DATA tri-state drivers)
+ -- WEN : in std_logic; -- PPU VRAM write enable
+ -- ADDR : in std_logic_vector(15 downto 0); -- PPU VRAM ADDR
+ -- R,G,B : out std_logic_vector(3 downto 0);
+ -- NVSYNC, NHSYNC : out std_logic; -- native VGA out
+ -- TVSYNC, TVBLANK, THSYNC, THBLANK : out std_logic); -- tiny VGA out
end apu;
architecture Behavioral of apu is
component apu_note_to_frequency port(
- data: in std_logic_vector(7 downto 0);
- freq: out std_logic_vector(7 downto 0)); --frequency
+ data : in std_logic_vector(7 downto 0);
+ freq : out std_logic_vector(7 downto 0)); --frequency
end component;
component apu_LUT_reader port(
- clk: in std_logic;
- rst: in std_logic;
- wave: in std_logic_vector(1 downto 0);
- level: out std_logic_vector(7 downto 0));
+ clk : in std_logic;
+ rst : in std_logic;
+ wave : in std_logic_vector(1 downto 0);
+ level : out std_logic_vector(7 downto 0));
end component;
begin
diff --git a/basys3/basys3.srcs/apu_LUT_reader.vhd b/basys3/basys3.srcs/apu_lut_reader.vhd
index 6039798..2f92eca 100644
--- a/basys3/basys3.srcs/apu_LUT_reader.vhd
+++ b/basys3/basys3.srcs/apu_lut_reader.vhd
@@ -2,7 +2,7 @@ library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-entity apu_LUT_reader is
+entity apu_lut_reader is
port (
clk : in std_logic;
rst : in std_logic;
@@ -12,7 +12,7 @@ entity apu_LUT_reader is
);
end entity;
-architecture Behavioral of apu_LUT_reader is
+architecture behavioral of apu_lut_reader is
constant AMPLITUDE : natural := 0;
constant SAMPLE_SIZE : natural := 256;
diff --git a/basys3/basys3.srcs/apu_tb_LUT_reader.vhd b/basys3/basys3.srcs/apu_lut_reader_tb.vhd
index 5a38d39..1b425bf 100644
--- a/basys3/basys3.srcs/apu_tb_LUT_reader.vhd
+++ b/basys3/basys3.srcs/apu_lut_reader_tb.vhd
@@ -2,14 +2,14 @@ library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-library UNISIM;
-use UNISIM.VComponents.all;
+library unisim;
+use unisim.vcomponents.all;
-entity apu_tb_LUT_reader is
+entity apu_lut_reader_tb is
end entity;
-architecture Behavioral of apu_tb_LUT_reader is
- component apu_LUT_reader is
+architecture Behavioral of apu_lut_reader_tb is
+ component apu_lut_reader is
port (
clk : in std_logic;
rst : in std_logic;
diff --git a/basys3/basys3.srcs/apu_note_to_frequency.vhd b/basys3/basys3.srcs/apu_note_to_frequency.vhd
index 8a7b3d6..810cef9 100644
--- a/basys3/basys3.srcs/apu_note_to_frequency.vhd
+++ b/basys3/basys3.srcs/apu_note_to_frequency.vhd
@@ -10,9 +10,9 @@ entity apu_note_to_frequency is port (
end entity;
architecture Behavioral of apu_note_to_frequency is
- signal buff_small: std_logic_vector(7 downto 0) := (others => '0');
- signal buff: std_logic_vector(11 downto 0) := (others => '0');
- signal shift: integer;
+ signal buff_small : std_logic_vector(7 downto 0) := (others => '0');
+ signal buff : std_logic_vector(15 downto 0) := (others => '0');
+ signal shift : integer;
begin
shift <= to_integer(unsigned( data(2 downto 0) ));
diff --git a/basys3/basys3.srcs/apu_note_to_frequency_tb.vhd b/basys3/basys3.srcs/apu_note_to_frequency_tb.vhd
index 6814c1f..f48a40c 100644
--- a/basys3/basys3.srcs/apu_note_to_frequency_tb.vhd
+++ b/basys3/basys3.srcs/apu_note_to_frequency_tb.vhd
@@ -10,20 +10,20 @@ end entity;
architecture Behavioral of apu_note_to_frequency_tb is
component apu_note_to_frequency is port(
- data: in std_logic_vector(7 downto 0);
- freq: out std_logic_vector(11 downto 0)); -- frequency
+ data : in std_logic_vector(7 downto 0);
+ freq : out std_logic_vector(11 downto 0)); -- frequency
end component;
- signal data: std_logic_vector(7 downto 0) := (others => '0');
- signal freq: std_logic_vector(11 downto 0) := (others => '0');
+ signal data : std_logic_vector(7 downto 0) := (others => '0');
+ signal freq : std_logic_vector(11 downto 0) := (others => '0');
- signal ok: boolean := false;
+ signal ok : boolean := false;
begin
- uut: apu_note_to_frequency port map(
+ uut : apu_note_to_frequency port map(
data => data,
freq => freq);
- tb: process
+ tb : process
begin
for i in 0 to 255 loop
data <= std_logic_vector(to_unsigned(i, 8));
diff --git a/basys3/basys3.srcs/er_ram.vhd b/basys3/basys3.srcs/er_ram.vhd
new file mode 100644
index 0000000..66f905b
--- /dev/null
+++ b/basys3/basys3.srcs/er_ram.vhd
@@ -0,0 +1,53 @@
+library ieee;
+library work;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+entity er_ram is -- exposed register RAM
+ generic(
+ ADDR_W : natural := 2; -- ADDR line width
+ DATA_W : natural := 2; -- DATA line width
+ ADDR_LOW : natural := 16#0000#; -- starting address
+ ADDR_RANGE : natural := 16#0002#); -- amount of valid addresses after ADDR_LOW
+ port(
+ CLK : in std_logic; -- clock
+ RST : in std_logic; -- async memory clear
+ WEN : in std_logic; -- write enable
+ ADDR : in std_logic_vector(ADDR_W-1 downto 0); -- address line
+ DATA : in std_logic_vector(DATA_W-1 downto 0); -- data input
+ REG : out std_logic_vector((ADDR_RANGE*DATA_W)-1 downto 0)); -- exposed register output
+end er_ram;
+
+architecture Behavioral of er_ram is
+ component er_ram_mod
+ generic(
+ W : natural := DATA_W; -- module data width
+ ADDR_W : natural := ADDR_W; -- address width
+ ADDR_M : std_logic_vector(ADDR_W-1 downto 0) := (others => '0')); -- address match
+ port(
+ CLK : in std_logic; -- clock
+ RST : in std_logic; -- async memory clear
+ WEN : in std_logic; -- write enable
+ ADDR : in std_logic_vector(ADDR_W-1 downto 0);
+ DATA : in std_logic_vector(W-1 downto 0); -- data
+ REG : out std_logic_vector(W-1 downto 0)); -- direct register out
+ end component;
+ signal INT_REG : std_logic_vector((ADDR_RANGE*DATA_W)-1 downto 0);
+begin
+ REG <= INT_REG;
+
+ registers : for idx in 0 to ADDR_RANGE - 1 generate
+ reg : component er_ram_mod
+ generic map(
+ W => DATA_W,
+ ADDR_W => ADDR_W,
+ ADDR_M => std_logic_vector(to_unsigned(ADDR_LOW + idx, ADDR_W)))
+ port map(
+ CLK => CLK,
+ RST => RST,
+ WEN => WEN,
+ ADDR => ADDR,
+ DATA => DATA,
+ REG => INT_REG(idx*DATA_W+DATA_W-1 downto idx*DATA_W));
+ end generate;
+end Behavioral;
diff --git a/basys3/basys3.srcs/er_ram_mod.vhd b/basys3/basys3.srcs/er_ram_mod.vhd
new file mode 100644
index 0000000..ae680b1
--- /dev/null
+++ b/basys3/basys3.srcs/er_ram_mod.vhd
@@ -0,0 +1,34 @@
+library ieee;
+library work;
+use ieee.std_logic_1164.all;
+
+entity er_ram_mod is -- exposed register RAM module (single register)
+ generic(
+ W : natural := 1; -- module data width
+ ADDR_W : natural := 1; -- address width
+ ADDR_M : std_logic_vector(ADDR_W-1 downto 0) := (others => '0')); -- address match
+ port(
+ CLK : in std_logic; -- clock
+ RST : in std_logic; -- async memory clear
+ WEN : in std_logic; -- write enable
+ ADDR : in std_logic_vector(ADDR_W-1 downto 0); -- RAM address line
+ DATA : in std_logic_vector(W-1 downto 0); -- RAM input data line
+ REG : out std_logic_vector(W-1 downto 0)); -- direct register output lines
+end er_ram_mod;
+
+architecture Behavioral of er_ram_mod is
+ signal DATA_REG : std_logic_vector(W-1 downto 0);
+begin
+ REG <= DATA_REG;
+
+ process(CLK, RST)
+ begin
+ if RST = '1' then
+ DATA_REG <= (others => '0');
+ elsif rising_edge(CLK) then
+ if WEN = '1' and ADDR = ADDR_M then
+ DATA_REG <= DATA;
+ end if;
+ end if;
+ end process;
+end Behavioral;
diff --git a/basys3/basys3.srcs/er_ram_mod_tb.vhd b/basys3/basys3.srcs/er_ram_mod_tb.vhd
new file mode 100644
index 0000000..aa77a56
--- /dev/null
+++ b/basys3/basys3.srcs/er_ram_mod_tb.vhd
@@ -0,0 +1,81 @@
+library ieee;
+library unisim;
+
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use unisim.vcomponents.all;
+
+entity er_ram_mod_tb is
+end er_ram_mod_tb;
+
+architecture behavioral of er_ram_mod_tb is
+ component er_ram_mod
+ generic(
+ W : natural := 1; -- module data width
+ ADDR_W : natural := 1; -- address width
+ ADDR_M : std_logic_vector(ADDR_W-1 downto 0) := (others => '0')); -- address match
+ port(
+ CLK : in std_logic; -- clock
+ RST : in std_logic; -- async memory clear
+ WEN : in std_logic; -- write enable
+ ADDR : in std_logic_vector(ADDR_W-1 downto 0);
+ DATA : in std_logic_vector(W-1 downto 0); -- data
+ REG : out std_logic_vector(W-1 downto 0)); -- direct register out
+ end component;
+
+ signal CLK, RST, WEN : std_logic := '0';
+ signal ADDR : std_logic_vector(3 downto 0);
+ signal DATA : std_logic_vector(7 downto 0);
+ signal REG : std_logic_vector(7 downto 0);
+begin
+ uut : component er_ram_mod
+ generic map(
+ W => 8,
+ ADDR_W => 4,
+ ADDR_M => x"5")
+ port map(
+ CLK => CLK,
+ RST => RST,
+ WEN => WEN,
+ ADDR => ADDR,
+ DATA => DATA,
+ REG => REG);
+
+ tb : process
+ begin
+ wait for 5 ns;
+
+ -- async reset (safety)
+ RST <= '1';
+ wait for 5 ns;
+ RST <= '0';
+ wait for 5 ns;
+
+ -- set 0xef at address 0x5 (exists)
+ DATA <= x"ef";
+ ADDR <= x"5";
+ WEN <= '1';
+
+ CLK <= '1';
+ wait for 5 ns;
+ CLK <= '0';
+ wait for 5 ns;
+
+ -- set 0x34 at address 0x4 (doesn't exist)
+ ADDR <= x"4";
+ DATA <= x"34";
+
+ CLK <= '1';
+ wait for 5 ns;
+ CLK <= '0';
+ wait for 5 ns;
+
+ -- reset
+ RST <= '1';
+ wait for 5 ns;
+ RST <= '0';
+ wait for 5 ns;
+
+ wait; -- stop for simulator
+ end process;
+end;
diff --git a/basys3/basys3.srcs/er_ram_tb.vhd b/basys3/basys3.srcs/er_ram_tb.vhd
new file mode 100644
index 0000000..d360442
--- /dev/null
+++ b/basys3/basys3.srcs/er_ram_tb.vhd
@@ -0,0 +1,116 @@
+library ieee;
+library unisim;
+
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use unisim.vcomponents.all;
+
+entity er_ram_tb is
+end er_ram_tb;
+
+architecture behavioral of er_ram_tb is
+ component er_ram
+ generic(
+ ADDR_W : natural := 2; -- ADDR line width
+ DATA_W : natural := 2; -- DATA line width
+ ADDR_LOW : natural := 16#0000#; -- starting address
+ ADDR_RANGE : natural := 16#0002#); -- amount of valid addresses after ADDR_LOW
+ port(
+ CLK : in std_logic; -- clock
+ RST : in std_logic; -- async memory clear
+ WEN : in std_logic; -- write enable
+ ADDR : in std_logic_vector(ADDR_W-1 downto 0);
+ DATA : in std_logic_vector(DATA_W-1 downto 0);
+ REG : out std_logic_vector((ADDR_W*DATA_W)-1 downto 0));
+ end component;
+
+ signal CLK, RST, WEN : std_logic := '0';
+ signal ADDR : std_logic_vector(3 downto 0);
+ signal DATA : std_logic_vector(7 downto 0);
+ signal REG : std_logic_vector(31 downto 0);
+begin
+ uut : component er_ram
+ generic map(
+ ADDR_W => 4,
+ DATA_W => 8,
+ ADDR_LOW => 0,
+ ADDR_RANGE => 4)
+ port map(
+ CLK => CLK,
+ RST => RST,
+ WEN => WEN,
+ ADDR => ADDR,
+ DATA => DATA,
+ REG => REG);
+
+ tb : process
+ begin
+ wait for 5 ns;
+
+ -- async reset (safety)
+ RST <= '1';
+ wait for 5 ns;
+ RST <= '0';
+ wait for 5 ns;
+
+ -- set 0xef at address 0x1 (exists)
+ DATA <= x"ef";
+ ADDR <= x"1";
+ WEN <= '1';
+
+ CLK <= '1';
+ wait for 5 ns;
+ CLK <= '0';
+ wait for 5 ns;
+
+ -- set 0x34 at address 0x4 (doesn't exist)
+ ADDR <= x"4";
+ DATA <= x"34";
+
+ CLK <= '1';
+ wait for 5 ns;
+ CLK <= '0';
+ wait for 5 ns;
+
+ -- reset
+ RST <= '1';
+ wait for 5 ns;
+ RST <= '0';
+ wait for 5 ns;
+
+ -- set REG to 0x12345678
+ ADDR <= x"0";
+ DATA <= x"12";
+
+ CLK <= '1';
+ wait for 1 ns;
+ CLK <= '0';
+ wait for 1 ns;
+
+ ADDR <= x"1";
+ DATA <= x"34";
+
+ CLK <= '1';
+ wait for 1 ns;
+ CLK <= '0';
+ wait for 1 ns;
+
+ ADDR <= x"2";
+ DATA <= x"56";
+
+ CLK <= '1';
+ wait for 1 ns;
+ CLK <= '0';
+ wait for 1 ns;
+
+ ADDR <= x"3";
+ DATA <= x"78";
+
+ CLK <= '1';
+ wait for 1 ns;
+ CLK <= '0';
+ wait for 1 ns;
+
+ wait; -- stop for simulator
+ end process;
+end;
diff --git a/basys3/basys3.srcs/ppu.vhd b/basys3/basys3.srcs/ppu.vhd
index 663f3ab..9cf1bc0 100644
--- a/basys3/basys3.srcs/ppu.vhd
+++ b/basys3/basys3.srcs/ppu.vhd
@@ -6,184 +6,191 @@ use ieee.std_logic_1164.all;
use work.ppu_consts.all;
entity ppu is port(
- CLK100: in std_logic; -- system clock
- RESET: in std_logic; -- global (async) system reset
- EN: in std_logic; -- PPU VRAM enable (enable ADDR and DATA tri-state drivers)
- WEN: in std_logic; -- PPU VRAM write enable
- ADDR: in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- PPU VRAM ADDR
- DATA: in std_logic_vector(PPU_RAM_BUS_DATA_WIDTH-1 downto 0);
- R,G,B: out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0);
- NVSYNC, NHSYNC: out std_logic; -- native VGA out
- TVSYNC, TVBLANK, THSYNC, THBLANK: out std_logic); -- tiny VGA out
+ CLK100 : in std_logic; -- system clock
+ RESET : in std_logic; -- global (async) system reset
+ EN : in std_logic; -- PPU VRAM enable (enable ADDR and DATA tri-state drivers)
+ WEN : in std_logic; -- PPU VRAM write enable
+ ADDR : in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- PPU VRAM ADDR
+ DATA : in std_logic_vector(PPU_RAM_BUS_DATA_WIDTH-1 downto 0);
+ R,G,B : out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0);
+ NVSYNC, NHSYNC : out std_logic; -- native VGA out
+ TVSYNC, TVBLANK, THSYNC, THBLANK : out std_logic); -- tiny VGA out
end ppu;
architecture Behavioral of ppu is
+ -- TODO: separate SPRITE_BG and SPRITE_FG lines (foreground_sprite only needs 2 clock cycles)
component ppu_pceg port( -- pipeline clock edge generator
- CLK: in std_logic; -- system clock
- RESET: in std_logic; -- async reset
- SPRITE: out std_logic; -- sprite info fetch + sprite pixel fetch
- COMP_PAL: out std_logic; -- compositor + palette lookup
- DONE: out std_logic); -- last pipeline stage done
+ CLK : in std_logic; -- system clock
+ RESET : in std_logic; -- async reset
+ SPRITE : out std_logic; -- sprite info fetch + sprite pixel fetch
+ COMP_PAL : out std_logic; -- compositor + palette lookup
+ DONE : out std_logic); -- last pipeline stage done
end component;
component ppu_addr_dec port( -- pipeline clock edge generator
- WEN: in std_logic; -- EXT write enable
+ WEN : in std_logic; -- EXT write enable
TMM_WEN,
BAM_WEN,
FAM_WEN,
PAL_WEN,
- AUX_WEN: out std_logic; -- write enable MUX
- EN: in std_logic; -- EXT *ADDR enable (switch *AO to ADDR instead of *AI)
- ADDR: in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- address in
- TMM_AI: in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
- BAM_AI: in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
- FAM_AI: in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
- PAL_AI: in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
- AUX_AI: in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0);
- TMM_AO: out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
- BAM_AO: out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
- FAM_AO: out std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
- PAL_AO: out std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
- AUX_AO: out std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0));
+ AUX_WEN : out std_logic; -- write enable MUX
+ EN : in std_logic; -- EXT *ADDR enable (switch *AO to ADDR instead of *AI)
+ ADDR : in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- address in
+ TMM_AI : in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ BAM_AI : in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ FAM_AI : in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
+ PAL_AI : in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
+ AUX_AI : in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0);
+ TMM_AO : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ BAM_AO : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ FAM_AO : out std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
+ PAL_AO : out std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
+ AUX_AO : out std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0));
end component;
component ppu_bam port( -- BAM block memory
- clka: in std_logic;
- rsta: in std_logic;
- wea: in std_logic_vector(0 downto 0);
- addra: in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
- dina: in std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
- douta: out std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
- rsta_busy: out std_logic);
+ clka : in std_logic;
+ rsta : in std_logic;
+ wea : in std_logic_vector(0 downto 0);
+ addra : in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ dina : in std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
+ douta : out std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
+ rsta_busy : out std_logic);
end component;
component ppu_tmm port( -- TMM block memory
- clka: in std_logic;
- rsta: in std_logic;
- wea: in std_logic_vector(0 downto 0);
- addra: in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
- dina: in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
- douta: out std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
- rsta_busy: out std_logic);
+ clka : in std_logic;
+ rsta : in std_logic;
+ wea : in std_logic_vector(0 downto 0);
+ addra : in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ dina : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
+ douta : out std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
+ rsta_busy : out std_logic);
end component;
component ppu_aux port(
- CLK: in std_logic; -- system clock
- RESET: in std_logic; -- reset memory
+ CLK : in std_logic; -- system clock
+ RESET : in std_logic; -- reset memory
-- internal memory block (AUX)
- AUX_WEN: in std_logic; -- VRAM AUX write enable
- AUX_ADDR: in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); -- VRAM AUX address
- AUX_DATA: in std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0); -- VRAM AUX data
+ AUX_WEN : in std_logic; -- VRAM AUX write enable
+ AUX_ADDR : in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); -- VRAM AUX address
+ AUX_DATA : in std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0); -- VRAM AUX data
-- aux outputs
- BG_SHIFT_X: out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0);
- BG_SHIFT_Y: out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0);
- FG_FETCH: out std_logic);
+ BG_SHIFT_X : out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0);
+ BG_SHIFT_Y : out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0);
+ FG_FETCH : out std_logic);
end component;
component ppu_sprite_bg port( -- background sprite
-- inputs
- CLK: in std_logic; -- system clock
- RESET: in std_logic; -- reset clock counter
- OE: in std_logic; -- output enable (of CIDX)
- X: in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
- Y: in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
+ CLK : in std_logic; -- pipeline clock
+ RESET : in std_logic; -- reset clock counter
+ OE : in std_logic; -- output enable (of CIDX)
+ X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
+ Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
-- aux inputs
- BG_SHIFT_X: in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0);
- BG_SHIFT_Y: in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0);
+ BG_SHIFT_X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0);
+ BG_SHIFT_Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0);
-- used memory blocks
- BAM_ADDR: out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
- BAM_DATA: in std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
- TMM_ADDR: out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
- TMM_DATA: in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
+ BAM_ADDR : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ BAM_DATA : in std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
+ TMM_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ TMM_DATA : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
-- outputs
- CIDX: out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0)); -- output color
+ CIDX : out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0)); -- output color
end component;
- component ppu_sprite_fg port( -- foreground sprite
- -- inputs
- CLK: in std_logic; -- system clock
- RESET: in std_logic; -- reset internal memory and clock counters
- OE: in std_logic; -- output enable (of CIDX)
- X: in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
- Y: in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
- FETCH: in std_logic; -- fetch sprite data from TMM (TODO: generic map, set foreground sprite component index)
+ component ppu_sprite_fg -- foreground sprite
+ generic (
+ IDX : natural := 0);
+ port(
+ -- inputs
+ CLK : in std_logic; -- system clock
+ RESET : in std_logic; -- reset internal memory and clock counters
+ OE : in std_logic; -- output enable (of CIDX)
+ X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
+ Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
+ FETCH : in std_logic; -- fetch sprite data from TMM
+ VBLANK : in std_logic; -- fetch during vblank
- -- internal memory block (FAM)
- FAM_WEN: in std_logic; -- VRAM FAM write enable
- FAM_ADDR: in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); -- VRAM fam address
- FAM_DATA: in std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0); -- VRAM fam data
+ -- internal memory block (FAM)
+ FAM_WEN : in std_logic; -- VRAM FAM write enable
+ FAM_ADDR : in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); -- VRAM fam address
+ FAM_DATA : in std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0); -- VRAM fam data
- -- used memory blocks
- TMM_ADDR: out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
- TMM_DATA: in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
+ -- used memory blocks
+ TMM_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ TMM_DATA : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
- -- outputs
- CIDX: out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- output color
- HIT: out std_logic); -- current pixel is not transparent
+ -- outputs
+ CIDX : out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- output color
+ HIT : out std_logic); -- current pixel is not transparent
end component;
component ppu_comp port( -- compositor
- FG_HIT: in std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0);
- BG_EN: out std_logic;
- FG_EN: out std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0));
+ FG_HIT : in std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0);
+ BG_EN : out std_logic;
+ FG_EN : out std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0));
end component;
component ppu_plut port( -- palette lookup table
- CLK: in std_logic; -- system clock
- CIDX: in std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- color in
- RESET: in std_logic;
+ CLK : in std_logic; -- system clock
+ CIDX : in std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- color in
+ RESET : in std_logic;
-- internal memory block (AUX)
- PAL_WEN: in std_logic; -- VRAM PAL write enable
- PAL_ADDR: in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); -- VRAM PAL address
- PAL_DATA: in std_logic_vector(PPU_PAL_DATA_WIDTH-1 downto 0); -- VRAM PAL data
+ PAL_WEN : in std_logic; -- VRAM PAL write enable
+ PAL_ADDR : in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); -- VRAM PAL address
+ PAL_DATA : in std_logic_vector(PPU_PAL_DATA_WIDTH-1 downto 0); -- VRAM PAL data
- R,G,B: out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0)); -- VGA color out
+ R,G,B : out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0)); -- VGA color out
end component;
component ppu_vga_tiny port( -- tiny vga signal generator
- CLK: in std_logic; -- system clock
- RESET: in std_logic;
+ CLK : in std_logic; -- system clock
+ RESET : in std_logic;
- X: out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
- Y: out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
+ X : out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
+ Y : out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
VSYNC, VBLANK,
- HSYNC, HBLANK: out std_logic); -- VGA sync outputs
+ HSYNC, HBLANK : out std_logic); -- VGA sync outputs
end component;
component ppu_vga_native port( -- native vga signal generator (upscaler)
- CLK: in std_logic; -- system clock
- RESET: in std_logic;
+ CLK : in std_logic; -- system clock
+ RESET : in std_logic;
- X: in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
- Y: in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
- PREADY: in std_logic; -- current pixel ready (pixel color is stable)
- RI,GI,BI: in std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color in
+ X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
+ Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
+ PREADY : in std_logic; -- current pixel ready (pixel color is stable)
+ RI,GI,BI : in std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color in
- RO,GO,BO: out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color out
- VSYNC, HSYNC: out std_logic); -- VGA sync outputs
+ RO,GO,BO : out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color out
+ VSYNC, HSYNC : out std_logic); -- VGA sync outputs
end component;
-- signals
- signal SYSCLK, SYSRST: std_logic; -- system clock and reset
- signal PL_SPRITE, PL_COMP_PAL, PL_DONE: std_logic; -- pipeline stages
- signal TMM_WEN, BAM_WEN, FAM_WEN, PAL_WEN, AUX_WEN: std_logic;
- signal TMM_AI, TMM_AO: std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
- signal BAM_AI, BAM_AO: std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
- signal FAM_AI, FAM_AO: std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
- signal PAL_AI, PAL_AO: std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
- signal AUX_AI, AUX_AO: std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0);
- signal TMM_DO: std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
- signal BAM_DO: std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
- signal FAM_DO: std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0);
- signal PAL_DO: std_logic_vector(PPU_PAL_DATA_WIDTH-1 downto 0);
- signal AUX_DO: std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0);
- signal CIDX: std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0);
- signal BG_EN: std_logic;
- signal FG_EN, FG_HIT: std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0);
- signal X: std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
- signal Y: std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
- signal UR,UG,UB: std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- unstable RGB (to be buffered)
- signal SR,SG,SB: std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- stable RGB (buffered until PL_COMP_PAL)
- signal BG_SHIFT_X: std_logic_vector(PPU_POS_H_WIDTH-1 downto 0);
- signal BG_SHIFT_Y: std_logic_vector(PPU_POS_V_WIDTH-1 downto 0);
- signal FG_FETCH: std_logic;
+ signal SYSCLK, SYSRST : std_logic; -- system clock and reset
+ signal PL_SPRITE, PL_COMP_PAL, PL_DONE : std_logic; -- pipeline stages
+ signal TMM_WEN, BAM_WEN, FAM_WEN, PAL_WEN, AUX_WEN : std_logic;
+ signal TMM_AI, TMM_AO : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ signal BAM_AI, BAM_AO : std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ signal FAM_AI, FAM_AO : std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
+ signal PAL_AI, PAL_AO : std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
+ signal AUX_AI, AUX_AO : std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0);
+ signal TMM_DO : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
+ signal BAM_DO : std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
+ signal FAM_DO : std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0);
+ signal PAL_DO : std_logic_vector(PPU_PAL_DATA_WIDTH-1 downto 0);
+ signal AUX_DO : std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0);
+ signal CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0);
+ signal BG_EN : std_logic;
+ signal FG_EN, FG_HIT : std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0);
+ signal X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
+ signal Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
+ signal UR,UG,UB : std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- unstable RGB (to be buffered)
+ signal SR,SG,SB : std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- stable RGB (buffered until PL_COMP_PAL)
+ signal BG_SHIFT_X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0);
+ signal BG_SHIFT_Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0);
+ signal FG_FETCH : std_logic;
+ signal TINY_VBLANK, TINY_VSYNC, TINY_HBLANK, TINY_HSYNC,
+ NATIVE_VSYNC, NATIVE_HSYNC : std_logic;
begin
SYSCLK <= CLK100;
SYSRST <= RESET;
@@ -197,14 +204,21 @@ begin
FAM_AI <= (others => '0');
PAL_AI <= (others => '0');
- pipeline_clock_edge_generator: component ppu_pceg port map(
+ TVBLANK <= TINY_VBLANK;
+ TVSYNC <= TINY_VSYNC;
+ THBLANK <= TINY_HBLANK;
+ THSYNC <= TINY_HSYNC;
+ NVSYNC <= NATIVE_VSYNC;
+ NHSYNC <= NATIVE_HSYNC;
+
+ pipeline_clock_edge_generator : component ppu_pceg port map(
CLK => SYSCLK,
RESET => SYSRST,
SPRITE => PL_SPRITE,
COMP_PAL => PL_COMP_PAL,
DONE => PL_DONE);
- address_decoder: component ppu_addr_dec port map(
+ address_decoder : component ppu_addr_dec port map(
EN => EN,
WEN => WEN,
ADDR => ADDR,
@@ -224,7 +238,7 @@ begin
PAL_WEN => PAL_WEN,
AUX_WEN => AUX_WEN);
- background_attribute_memory: component ppu_bam port map(
+ background_attribute_memory : component ppu_bam port map(
clka => SYSCLK,
rsta => SYSRST,
wea => (others => BAM_WEN),
@@ -232,7 +246,7 @@ begin
dina => DATA(PPU_BAM_DATA_WIDTH-1 downto 0),
douta => BAM_DO,
rsta_busy => open);
- tilemap_memory: component ppu_tmm port map(
+ tilemap_memory : component ppu_tmm port map(
clka => SYSCLK,
rsta => SYSRST,
wea => (others => TMM_WEN),
@@ -241,7 +255,7 @@ begin
douta => TMM_DO,
rsta_busy => open);
- aux: component ppu_aux port map(
+ aux : component ppu_aux port map(
CLK => SYSCLK,
RESET => SYSRST,
AUX_WEN => AUX_WEN,
@@ -251,7 +265,7 @@ begin
BG_SHIFT_Y => BG_SHIFT_Y,
FG_FETCH => FG_FETCH);
- background_sprite: component ppu_sprite_bg port map(
+ background_sprite : component ppu_sprite_bg port map(
CLK => PL_SPRITE,
RESET => SYSRST,
OE => BG_EN,
@@ -265,29 +279,32 @@ begin
TMM_DATA => TMM_DO,
CIDX => CIDX);
- foreground_sprites: for FG_IDX in 0 to PPU_FG_SPRITE_COUNT-1 generate
- foreground_sprite: component ppu_sprite_fg port map(
- CLK => PL_SPRITE,
- RESET => SYSRST,
- OE => FG_EN(FG_IDX),
- X => X,
- Y => Y,
- FETCH => FG_FETCH,
- FAM_WEN => FAM_WEN,
- FAM_ADDR => FAM_AO,
- FAM_DATA => DATA(PPU_FAM_DATA_WIDTH-1 downto 0),
- TMM_ADDR => TMM_AI,
- TMM_DATA => TMM_DO,
- CIDX => CIDX,
- HIT => FG_HIT(FG_IDX));
+ foreground_sprites : for FG_IDX in 0 to PPU_FG_SPRITE_COUNT-1 generate
+ foreground_sprite : component ppu_sprite_fg
+ generic map( IDX => FG_IDX )
+ port map(
+ CLK => SYSCLK,
+ RESET => SYSRST,
+ OE => FG_EN(FG_IDX),
+ X => X,
+ Y => Y,
+ FETCH => FG_FETCH,
+ VBLANK => TINY_VBLANK,
+ FAM_WEN => FAM_WEN,
+ FAM_ADDR => FAM_AO,
+ FAM_DATA => DATA(PPU_FAM_DATA_WIDTH-1 downto 0),
+ TMM_ADDR => TMM_AI,
+ TMM_DATA => TMM_DO,
+ CIDX => CIDX,
+ HIT => FG_HIT(FG_IDX));
end generate;
- compositor: component ppu_comp port map( -- compositor
+ compositor : component ppu_comp port map( -- compositor
FG_HIT => FG_HIT,
BG_EN => BG_EN,
FG_EN => FG_EN);
- palette_lookup: component ppu_plut port map( -- palette lookup table
+ palette_lookup : component ppu_plut port map( -- palette lookup table
CLK => SYSCLK,
CIDX => CIDX,
RESET => SYSRST,
@@ -312,17 +329,17 @@ begin
end if;
end process;
- tiny_vga_signal_generator: component ppu_vga_tiny port map( -- tiny vga signal generator
+ tiny_vga_signal_generator : component ppu_vga_tiny port map( -- tiny vga signal generator
CLK => SYSCLK,
RESET => SYSRST,
X => X,
Y => Y,
- VSYNC => TVSYNC,
- VBLANK => TVBLANK,
- HSYNC => THSYNC,
- HBLANK => THBLANK);
+ VSYNC => TINY_VSYNC,
+ VBLANK => TINY_VBLANK,
+ HSYNC => TINY_HSYNC,
+ HBLANK => TINY_HBLANK);
- native_vga_signal_generator: component ppu_vga_native port map( -- native vga signal generator (upscaler)
+ native_vga_signal_generator : component ppu_vga_native port map( -- native vga signal generator (upscaler)
CLK => SYSCLK,
RESET => SYSRST,
X => X,
@@ -334,6 +351,6 @@ begin
RO => R,
GO => G,
BO => B,
- VSYNC => NVSYNC,
- HSYNC => NHSYNC);
+ VSYNC => NATIVE_VSYNC,
+ HSYNC => NATIVE_HSYNC);
end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_addr_dec.vhd b/basys3/basys3.srcs/ppu_addr_dec.vhd
new file mode 100644
index 0000000..e0c374f
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_addr_dec.vhd
@@ -0,0 +1,51 @@
+library ieee;
+library work;
+use ieee.std_logic_1164.all;
+--use ieee.numeric_std.all;
+use work.ppu_consts.all;
+
+entity ppu_addr_dec is port(
+ EN : in std_logic; -- EXT *ADDR enable (switch *AO to ADDR instead of *AI)
+ WEN : in std_logic; -- EXT write enable
+ TMM_WEN,
+ BAM_WEN,
+ FAM_WEN,
+ PAL_WEN,
+ AUX_WEN : out std_logic; -- write enable MUX
+ ADDR : in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- address in
+ TMM_AI : in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ BAM_AI : in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ FAM_AI : in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
+ PAL_AI : in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
+ AUX_AI : in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0);
+ TMM_AO : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ BAM_AO : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ FAM_AO : out std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
+ PAL_AO : out std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
+ AUX_AO : out std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0));
+end ppu_addr_dec;
+
+architecture Behavioral of ppu_addr_dec is
+ signal TMM_RANGE, BAM_RANGE, FAM_RANGE, PAL_RANGE, AUX_RANGE : std_logic := '0'; -- ADDR in range of memory area
+begin
+ -- address MUX
+ TMM_AO <= ADDR(PPU_TMM_ADDR_WIDTH-1 downto 0) when EN = '1' else TMM_AI;
+ BAM_AO <= ADDR(PPU_BAM_ADDR_WIDTH-1 downto 0) when EN = '1' else BAM_AI;
+ FAM_AO <= ADDR(PPU_FAM_ADDR_WIDTH-1 downto 0) when EN = '1' else FAM_AI;
+ PAL_AO <= ADDR(PPU_PAL_ADDR_WIDTH-1 downto 0) when EN = '1' else PAL_AI;
+ AUX_AO <= ADDR(PPU_AUX_ADDR_WIDTH-1 downto 0) when EN = '1' else AUX_AI;
+
+ -- WEN MUX
+ TMM_WEN <= TMM_RANGE and WEN;
+ BAM_WEN <= BAM_RANGE and WEN;
+ FAM_WEN <= FAM_RANGE and WEN;
+ PAL_WEN <= PAL_RANGE and WEN;
+ AUX_WEN <= AUX_RANGE and WEN;
+
+ -- address ranges
+ TMM_RANGE <= '1' when not ((ADDR(15) and ADDR(14) and ADDR(13)) or (ADDR(15) and ADDR(14) and ADDR(12))) else '0';
+ BAM_RANGE <= '1' when TMM_RANGE = '0' and (ADDR(11) = '0') else '0';
+ FAM_RANGE <= '1' when TMM_RANGE = '0' and (ADDR(11) = '1' and ADDR(10) = '0') else '0';
+ PAL_RANGE <= '1' when TMM_RANGE = '0' and (ADDR(11) = '1' and ADDR(10) = '1' and ADDR(9) = '0') else '0';
+ AUX_RANGE <= '1' when TMM_RANGE = '0' and (ADDR(11) = '1' and ADDR(10) = '1' and ADDR(9) = '1') else '0';
+end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_addr_dec.vhdl b/basys3/basys3.srcs/ppu_addr_dec.vhdl
deleted file mode 100644
index 28c22fc..0000000
--- a/basys3/basys3.srcs/ppu_addr_dec.vhdl
+++ /dev/null
@@ -1,51 +0,0 @@
-library ieee;
-library work;
-use ieee.std_logic_1164.all;
---use ieee.numeric_std.all;
-use work.ppu_consts.all;
-
-entity ppu_addr_dec is port(
- EN: in std_logic; -- EXT *ADDR enable (switch *AO to ADDR instead of *AI)
- WEN: in std_logic; -- EXT write enable
- TMM_WEN,
- BAM_WEN,
- FAM_WEN,
- PAL_WEN,
- AUX_WEN: out std_logic; -- write enable MUX
- ADDR: in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- address in
- TMM_AI: in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
- BAM_AI: in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
- FAM_AI: in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
- PAL_AI: in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
- AUX_AI: in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0);
- TMM_AO: out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
- BAM_AO: out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
- FAM_AO: out std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
- PAL_AO: out std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
- AUX_AO: out std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0));
-end ppu_addr_dec;
-
-architecture Behavioral of ppu_addr_dec is
- signal TMM_RANGE, BAM_RANGE, FAM_RANGE, PAL_RANGE, AUX_RANGE: std_logic := '0'; -- ADDR in range of memory area
-begin
- -- address MUX
- TMM_AO <= ADDR(PPU_TMM_ADDR_WIDTH-1 downto 0) when EN = '1' else TMM_AI;
- BAM_AO <= ADDR(PPU_BAM_ADDR_WIDTH-1 downto 0) when EN = '1' else BAM_AI;
- FAM_AO <= ADDR(PPU_FAM_ADDR_WIDTH-1 downto 0) when EN = '1' else FAM_AI;
- PAL_AO <= ADDR(PPU_PAL_ADDR_WIDTH-1 downto 0) when EN = '1' else PAL_AI;
- AUX_AO <= ADDR(PPU_AUX_ADDR_WIDTH-1 downto 0) when EN = '1' else AUX_AI;
-
- -- WEN MUX
- TMM_WEN <= TMM_RANGE and WEN;
- BAM_WEN <= BAM_RANGE and WEN;
- FAM_WEN <= FAM_RANGE and WEN;
- PAL_WEN <= PAL_RANGE and WEN;
- AUX_WEN <= AUX_RANGE and WEN;
-
- -- address ranges
- TMM_RANGE <= '1' when not (ADDR(15) = '1' and ADDR(14) = '1') else '0';
- BAM_RANGE <= '1' when (ADDR(15) = '1' and ADDR(14) = '1') and (ADDR(11) = '0') else '0';
- FAM_RANGE <= '1' when (ADDR(15) = '1' and ADDR(14) = '1') and (ADDR(11) = '1' and ADDR(10) = '0') else '0';
- PAL_RANGE <= '1' when (ADDR(15) = '1' and ADDR(14) = '1') and (ADDR(11) = '1' and ADDR(10) = '1' and ADDR(9) = '0') else '0';
- AUX_RANGE <= '1' when (ADDR(15) = '1' and ADDR(14) = '1') and (ADDR(11) = '1' and ADDR(10) = '1' and ADDR(9) = '1') else '0';
-end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_addr_dec_tb.vhd b/basys3/basys3.srcs/ppu_addr_dec_tb.vhd
new file mode 100644
index 0000000..f31ee67
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_addr_dec_tb.vhd
@@ -0,0 +1,86 @@
+library ieee;
+library unisim;
+library work;
+
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use unisim.vcomponents.all;
+use work.ppu_consts.all;
+
+entity ppu_addr_dec_tb is
+end ppu_addr_dec_tb;
+
+architecture behavioral of ppu_addr_dec_tb is
+ component ppu_addr_dec port(
+ EN : in std_logic; -- EXT *ADDR enable (switch *AO to ADDR instead of *AI)
+ WEN : in std_logic; -- EXT write enable
+ TMM_WEN,
+ BAM_WEN,
+ FAM_WEN,
+ PAL_WEN,
+ AUX_WEN : out std_logic; -- write enable MUX
+ ADDR : in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- address in
+ TMM_AI : in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ BAM_AI : in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ FAM_AI : in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
+ PAL_AI : in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
+ AUX_AI : in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0);
+ TMM_AO : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ BAM_AO : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ FAM_AO : out std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
+ PAL_AO : out std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
+ AUX_AO : out std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0));
+ end component;
+ signal EN : std_logic;
+ signal WEN : std_logic;
+ signal TMM_WEN, BAM_WEN, FAM_WEN, PAL_WEN, AUX_WEN : std_logic;
+ signal ADDR : std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0);
+ signal TMM_AI : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ signal BAM_AI : std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ signal FAM_AI : std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
+ signal PAL_AI : std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
+ signal AUX_AI : std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0);
+ signal TMM_AO : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ signal BAM_AO : std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ signal FAM_AO : std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
+ signal PAL_AO : std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
+ signal AUX_AO : std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0);
+begin
+ uut : ppu_addr_dec port map(
+ EN => EN,
+ WEN => WEN,
+ TMM_WEN => TMM_WEN,
+ BAM_WEN => BAM_WEN,
+ FAM_WEN => FAM_WEN,
+ PAL_WEN => PAL_WEN,
+ AUX_WEN => AUX_WEN,
+ ADDR => ADDR,
+ TMM_AI => TMM_AI,
+ BAM_AI => BAM_AI,
+ FAM_AI => FAM_AI,
+ PAL_AI => PAL_AI,
+ AUX_AI => AUX_AI,
+ TMM_AO => TMM_AO,
+ BAM_AO => BAM_AO,
+ FAM_AO => FAM_AO,
+ PAL_AO => PAL_AO,
+ AUX_AO => AUX_AO);
+
+ EN <= '1';
+ WEN <= '1';
+
+ TMM_AI <= (others => '1');
+ BAM_AI <= (others => '0');
+ FAM_AI <= (others => '1');
+ PAL_AI <= (others => '0');
+ AUX_AI <= (others => '0');
+
+ tb : process
+ begin
+ for i in 0 to 65535 loop
+ ADDR <= std_logic_vector(to_unsigned(i,16));
+ wait for 10 ps;
+ end loop;
+ wait; -- stop for simulator
+ end process;
+end;
diff --git a/basys3/basys3.srcs/ppu_addr_dec_tb.vhdl b/basys3/basys3.srcs/ppu_addr_dec_tb.vhdl
deleted file mode 100644
index 5c7119d..0000000
--- a/basys3/basys3.srcs/ppu_addr_dec_tb.vhdl
+++ /dev/null
@@ -1,86 +0,0 @@
-library ieee;
-library unisim;
-library work;
-
-use ieee.std_logic_1164.all;
-use ieee.numeric_std.all;
-use unisim.vcomponents.all;
-use work.ppu_consts.all;
-
-entity ppu_addr_dec_tb is
-end ppu_addr_dec_tb;
-
-architecture behavioral of ppu_addr_dec_tb is
- component ppu_addr_dec port(
- EN: in std_logic; -- EXT *ADDR enable (switch *AO to ADDR instead of *AI)
- WEN: in std_logic; -- EXT write enable
- TMM_WEN,
- BAM_WEN,
- FAM_WEN,
- PAL_WEN,
- AUX_WEN: out std_logic; -- write enable MUX
- ADDR: in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- address in
- TMM_AI: in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
- BAM_AI: in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
- FAM_AI: in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
- PAL_AI: in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
- AUX_AI: in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0);
- TMM_AO: out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
- BAM_AO: out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
- FAM_AO: out std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
- PAL_AO: out std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
- AUX_AO: out std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0));
- end component;
- signal EN: std_logic;
- signal WEN: std_logic;
- signal TMM_WEN, BAM_WEN, FAM_WEN, PAL_WEN, AUX_WEN: std_logic;
- signal ADDR: std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0);
- signal TMM_AI: std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
- signal BAM_AI: std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
- signal FAM_AI: std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
- signal PAL_AI: std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
- signal AUX_AI: std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0);
- signal TMM_AO: std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
- signal BAM_AO: std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
- signal FAM_AO: std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
- signal PAL_AO: std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
- signal AUX_AO: std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0);
-begin
- uut: ppu_addr_dec port map(
- EN => EN,
- WEN => WEN,
- TMM_WEN => TMM_WEN,
- BAM_WEN => BAM_WEN,
- FAM_WEN => FAM_WEN,
- PAL_WEN => PAL_WEN,
- AUX_WEN => AUX_WEN,
- ADDR => ADDR,
- TMM_AI => TMM_AI,
- BAM_AI => BAM_AI,
- FAM_AI => FAM_AI,
- PAL_AI => PAL_AI,
- AUX_AI => AUX_AI,
- TMM_AO => TMM_AO,
- BAM_AO => BAM_AO,
- FAM_AO => FAM_AO,
- PAL_AO => PAL_AO,
- AUX_AO => AUX_AO);
-
- EN <= '1';
- WEN <= '1';
-
- TMM_AI <= (others => '1');
- BAM_AI <= (others => '0');
- FAM_AI <= (others => '1');
- PAL_AI <= (others => '0');
- AUX_AI <= (others => '0');
-
- tb: process
- begin
- for i in 0 to 65535 loop
- ADDR <= std_logic_vector(to_unsigned(i,16));
- wait for 10 ps;
- end loop;
- wait; -- stop for simulator
- end process;
-end;
diff --git a/basys3/basys3.srcs/ppu_aux.vhd b/basys3/basys3.srcs/ppu_aux.vhd
new file mode 100644
index 0000000..9062bc4
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_aux.vhd
@@ -0,0 +1,52 @@
+library ieee;
+library work;
+use ieee.std_logic_1164.all;
+--use ieee.numeric_std.all;
+use work.ppu_consts.all;
+
+entity ppu_aux is port(
+ CLK : in std_logic; -- system clock
+ RESET : in std_logic; -- reset memory
+
+ -- internal memory block (AUX)
+ AUX_WEN : in std_logic; -- VRAM AUX write enable
+ AUX_ADDR : in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); -- VRAM AUX address
+ AUX_DATA : in std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0); -- VRAM AUX data
+
+ -- aux outputs
+ BG_SHIFT_X : out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0);
+ BG_SHIFT_Y : out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0);
+ FG_FETCH : out std_logic);
+end ppu_aux;
+
+architecture Behavioral of ppu_aux is
+ component er_ram
+ generic(
+ ADDR_W : natural := PPU_AUX_ADDR_WIDTH; -- ADDR line width
+ DATA_W : natural := PPU_AUX_DATA_WIDTH; -- DATA line width
+ ADDR_LOW : natural := 16#0000#; -- starting address
+ ADDR_RANGE : natural := 16#0002#); -- amount of valid addresses after ADDR_LOW
+ port(
+ CLK : in std_logic; -- clock
+ RST : in std_logic; -- async memory clear
+ WEN : in std_logic; -- write enable
+ ADDR : in std_logic_vector(ADDR_W-1 downto 0);
+ DATA : in std_logic_vector(DATA_W-1 downto 0);
+ REG : out std_logic_vector((ADDR_RANGE*DATA_W)-1 downto 0)); -- exposed register output
+ end component;
+
+ signal INT_REG : std_logic_vector(2 * PPU_AUX_DATA_WIDTH - 1 downto 0);
+begin
+ -- docs/architecture.md#auxiliary-memory
+ FG_FETCH <= INT_REG(17);
+ BG_SHIFT_X <= INT_REG(16 downto 8);
+ BG_SHIFT_Y <= INT_REG(7 downto 0);
+
+ RAM : component er_ram port map(
+ CLK => CLK,
+ RST => RESET,
+ WEN => AUX_WEN,
+ ADDR => AUX_ADDR,
+ DATA => AUX_DATA,
+ REG => INT_REG);
+end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_aux_tb.vhd b/basys3/basys3.srcs/ppu_aux_tb.vhd
new file mode 100644
index 0000000..ab3db62
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_aux_tb.vhd
@@ -0,0 +1,100 @@
+library ieee;
+library unisim;
+library work;
+
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use unisim.vcomponents.all;
+use work.ppu_consts.all;
+
+entity ppu_aux_tb is
+end ppu_aux_tb;
+
+architecture behavioral of ppu_aux_tb is
+ component ppu_aux port(
+ CLK : in std_logic; -- system clock
+ RESET : in std_logic; -- reset memory
+
+ -- internal memory block (AUX)
+ AUX_WEN : in std_logic; -- VRAM AUX write enable
+ AUX_ADDR : in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); -- VRAM AUX address
+ AUX_DATA : in std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0); -- VRAM AUX data
+
+ -- aux outputs
+ BG_SHIFT_X : out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0);
+ BG_SHIFT_Y : out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0);
+ FG_FETCH : out std_logic);
+ end component;
+
+ signal CLK, RST, WEN : std_logic := '0';
+ signal ADDR : std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0) := (others => '0');
+ signal DATA : std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0) := (others => '0');
+
+ signal BG_SHIFT_X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0);
+ signal BG_SHIFT_Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0);
+ signal FG_FETCH : std_logic;
+begin
+ uut : component ppu_aux
+ port map(
+ CLK => CLK,
+ RESET => RST,
+ AUX_WEN => WEN,
+ AUX_ADDR => ADDR,
+ AUX_DATA => DATA,
+ BG_SHIFT_X => BG_SHIFT_X,
+ BG_SHIFT_Y => BG_SHIFT_Y,
+ FG_FETCH => FG_FETCH);
+
+ tb : process
+ begin
+ -- reset
+ RST <= '1';
+ wait for 5 ns;
+ RST <= '0';
+ wait for 5 ns;
+
+ WEN <= '1';
+ -- set BG_SHIFT_X to 178
+ -- set BG_SHIFT_Y to 39
+ -- set FG_FETCH to 1
+ ADDR(0) <= '0';
+ DATA <= x"b327";
+
+ CLK <= '1';
+ wait for 1 ns;
+ CLK <= '0';
+ wait for 1 ns;
+
+ ADDR(0) <= '1';
+ DATA <= x"0002";
+
+ CLK <= '1';
+ wait for 1 ns;
+ CLK <= '0';
+ wait for 1 ns;
+
+ wait for 5 ns;
+
+ -- set BG_SHIFT_X to 30
+ -- set BG_SHIFT_Y to 69
+ -- set FG_FETCH to 0
+ ADDR(0) <= '0';
+ DATA <= x"1e45";
+
+ CLK <= '1';
+ wait for 1 ns;
+ CLK <= '0';
+ wait for 1 ns;
+
+ ADDR(0) <= '1';
+ DATA <= x"0000";
+
+ CLK <= '1';
+ wait for 1 ns;
+ CLK <= '0';
+ wait for 1 ns;
+
+
+ wait; -- stop for simulator
+ end process;
+end;
diff --git a/basys3/basys3.srcs/ppu_comp.vhd b/basys3/basys3.srcs/ppu_comp.vhd
new file mode 100644
index 0000000..e79738f
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_comp.vhd
@@ -0,0 +1,36 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use work.ppu_consts.all;
+
+entity ppu_comp is port (
+ FG_HIT : in std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0);
+ BG_EN : out std_logic;
+ FG_EN : out std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0));
+end ppu_comp;
+
+architecture Behavioral of ppu_comp is
+ signal FG_HIT_EMPTY : std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0) := (others => '0');
+begin
+ process (FG_HIT)
+ variable HIT : boolean := false;
+ begin
+ -- check if FG_HIT is not empty
+ if FG_HIT /= FG_HIT_EMPTY then
+ BG_EN <= '0';
+ for i in 0 to PPU_FG_SPRITE_COUNT-1 loop
+ -- if FG_HIT is the first one then enable it
+ if(FG_HIT(i) = '1' and HIT = false) then
+ FG_EN(i) <= '1';
+ HIT := true;
+ else
+ -- make rest low
+ FG_EN(i) <= '0';
+ end if;
+ end loop;
+ HIT := false;
+ else
+ BG_EN <= '1';
+ FG_EN <= (others => '0');
+ end if;
+ end process;
+end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_comp_tb.vhd b/basys3/basys3.srcs/ppu_comp_tb.vhd
new file mode 100644
index 0000000..be4c2e3
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_comp_tb.vhd
@@ -0,0 +1,40 @@
+library ieee;
+library unisim;
+use ieee.std_logic_1164.all;
+use work.ppu_consts.all;
+use unisim.vcomponents.all;
+
+entity ppu_comp_tb is
+end ppu_comp_tb;
+
+architecture behavioral of ppu_comp_tb is
+ component ppu_comp port (
+ FG_HIT : in std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0);
+ BG_EN : out std_logic;
+ FG_EN : out std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0));
+ end component;
+ signal FG_HIT : std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0) := (others => '0');
+ signal BG_EN : std_logic := '0';
+ signal FG_EN : std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0) := (others => '0');
+begin
+ uut : ppu_comp port map (
+ FG_HIT => FG_HIT,
+ BG_EN => BG_EN,
+ FG_EN => FG_EN
+ );
+ tb : process
+ begin
+ FG_HIT <= (others => '0');
+ wait for 1 ps;
+ FG_HIT(6) <= '1';
+ FG_HIT(5) <= '1';
+ FG_HIT(100) <= '1';
+ wait for 1 ps;
+
+ FG_HIT(0) <= '1';
+ wait for 1 ps;
+ FG_HIT <= (others => '0');
+ wait for 1 ps;
+ wait;
+ end process;
+end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_consts.vhd b/basys3/basys3.srcs/ppu_consts.vhd
index c063586..75b6168 100644
--- a/basys3/basys3.srcs/ppu_consts.vhd
+++ b/basys3/basys3.srcs/ppu_consts.vhd
@@ -1,22 +1,55 @@
+library ieee;
+use ieee.math_real.all;
+
+-- https://docs.google.com/spreadsheets/d/1MU6K4c4PtMR_JXIpc3I0ZJdLZNnoFO7G2P3olCz6LSc
package ppu_consts is
- constant PPU_RAM_BUS_ADDR_WIDTH: natural := 16; -- RAM bus address width
- constant PPU_RAM_BUS_DATA_WIDTH: natural := 16; -- RAM bus data width
- constant PPU_FG_SPRITE_COUNT: natural := 128; -- amount of foreground sprites
- constant PPU_COLOR_OUTPUT_DEPTH: natural := 4; -- VGA output channel depth
- constant PPU_PALETTE_IDX_WIDTH: natural := 3; -- palette index width (within sprite)
- constant PPU_PALETTE_WIDTH: natural := 3; -- palette index width (palette table)
- constant PPU_PALETTE_CIDX_WIDTH: natural := PPU_PALETTE_IDX_WIDTH + PPU_PALETTE_WIDTH; -- global palette index width
- constant PPU_TMM_ADDR_WIDTH: natural := 16;
- constant PPU_TMM_DATA_WIDTH: natural := 16;
- constant PPU_BAM_ADDR_WIDTH: natural := 11;
- constant PPU_BAM_DATA_WIDTH: natural := 15;
- constant PPU_FAM_ADDR_WIDTH: natural := 8;
- constant PPU_FAM_DATA_WIDTH: natural := 16;
- constant PPU_PAL_ADDR_WIDTH: natural := 6;
- constant PPU_PAL_DATA_WIDTH: natural := 12;
- constant PPU_AUX_ADDR_WIDTH: natural := 2;
- constant PPU_AUX_DATA_WIDTH: natural := 16;
- constant PPU_POS_H_WIDTH: natural := 9; -- amount of bits for horizontal screen offset
- constant PPU_POS_V_WIDTH: natural := 8; -- amount of bits for vertical screen offset
-end package ppu_consts;
+ -- utility functions
+ function ceil_log2(n : natural) return natural;
+ constant PPU_RAM_BUS_ADDR_WIDTH : natural := 16; -- RAM bus address width
+ constant PPU_RAM_BUS_DATA_WIDTH : natural := 16; -- RAM bus data width
+ constant PPU_FG_SPRITE_COUNT : natural := 128; -- foreground sprites
+ constant PPU_COLOR_OUTPUT_DEPTH : natural := 4; -- VGA output channel depth
+ constant PPU_PALETTE_COLOR_WIDTH : natural := 3; -- palette index width (within sprite)
+ constant PPU_PALETTE_INDEX_WIDTH : natural := 3; -- palette index width (palette table)
+ constant PPU_PALETTE_CIDX_WIDTH : natural := (PPU_PALETTE_COLOR_WIDTH + PPU_PALETTE_INDEX_WIDTH); -- global palette index width
+ constant PPU_TMM_ADDR_WIDTH : natural := 16; -- tilemap memory ram bus address width
+ constant PPU_TMM_DATA_WIDTH : natural := 15; -- tilemap memory ram bus data width
+ constant PPU_BAM_ADDR_WIDTH : natural := 11; -- background attribute memory ram bus address width
+ constant PPU_BAM_DATA_WIDTH : natural := 15; -- background attribute memory ram bus data width
+ constant PPU_FAM_ADDR_WIDTH : natural := 8; -- foreground attribute memory ram bus address width
+ constant PPU_FAM_DATA_WIDTH : natural := 16; -- foreground attribute memory ram bus data width
+ constant PPU_PAL_ADDR_WIDTH : natural := 6; -- palette memory ram bus address width
+ constant PPU_PAL_DATA_WIDTH : natural := 12; -- palette memory ram bus data width
+ constant PPU_AUX_ADDR_WIDTH : natural := 1; -- auxiliary memory ram bus address width
+ constant PPU_AUX_DATA_WIDTH : natural := 16; -- auxiliary memory ram bus data width
+ constant PPU_POS_H_WIDTH : natural := 9; -- bits for horizontal screen offset
+ constant PPU_POS_V_WIDTH : natural := 8; -- bits for vertical screen offset
+ constant PPU_SPRITE_WIDTH : natural := 16; -- sprite width (pixels)
+ constant PPU_SPRITE_HEIGHT : natural := 16; -- sprite height (pixels)
+ constant PPU_SPRITE_PIDX_WIDTH : natural := 8; -- bits needed to identify horizontal and vertical pixel within sprite
+ constant PPU_SPRITE_POS_H_WIDTH: natural := 4; -- bits needed to identify horizontal pixel within sprite
+ constant PPU_SPRITE_POS_V_WIDTH: natural := 4; -- bits needed to identify vertical pixel within sprite
+ constant PPU_SCREEN_WIDTH : natural := 320; -- absolute screen width (pixels)
+ constant PPU_SCREEN_HEIGHT : natural := 240; -- absolute screen height (pixels)
+ constant PPU_BG_CANVAS_TILES_H : natural := 40; -- tiles (horizontally) on background canvas
+ constant PPU_BG_CANVAS_TILES_V : natural := 30; -- tiles (vertically) on background canvas
+ constant PPU_BG_CANVAS_TILE_H_WIDTH : natural := 6; -- bits needed to describe horizontal bg tile index (grid coordinates)
+ constant PPU_BG_CANVAS_TILE_V_WIDTH : natural := 5; -- bits needed to describe vertical bg tile index (grid coordinates)
+ constant PPU_TILE_INDEX_WIDTH : natural := 10; -- bits needed to index a tile from TMM memory
+ constant PPU_PIXELS_PER_TILE_WORD : natural := 5; -- pixels defined in one word in TMM memory
+ constant PPU_SPRITE_WORD_COUNT : natural := 52; -- words needed for a single sprite
+ constant PPU_PIXEL_BIT_WIDTH : natural := 3; -- bits needed to identify pixel in TMM word
+ constant PPU_TILE_BIT_WIDTH : natural := (PPU_SPRITE_WIDTH * PPU_SPRITE_HEIGHT * PPU_PALETTE_COLOR_WIDTH); -- bits in single tile
+ constant PPU_TMM_CACHE_FETCH_C_COUNT : natural := PPU_SPRITE_WORD_COUNT + 1;
+ constant PPU_TMM_CACHE_FETCH_A_COUNT : natural := PPU_TMM_CACHE_FETCH_C_COUNT * PPU_FG_SPRITE_COUNT; -- amount of clocks to fetch new TMM cache
+ constant PPU_TMM_CACHE_FETCH_A_WIDTH : natural := ceil_log2(PPU_TMM_CACHE_FETCH_A_COUNT);
+end package ppu_consts;
+package body ppu_consts is
+ -- https://stackoverflow.com/questions/21783280/number-of-bits-to-represent-an-integer-in-vhdl
+ -- Returns number of bits required to represent val in binary vector
+ function ceil_log2(n : natural) return natural is
+ begin
+ return natural(integer(ceil(log2(real(n - 1)))));
+ end function;
+end package body ppu_consts;
diff --git a/basys3/basys3.srcs/ppu_pceg.vhdl b/basys3/basys3.srcs/ppu_pceg.vhd
index 9675e5b..1aaeee4 100644
--- a/basys3/basys3.srcs/ppu_pceg.vhdl
+++ b/basys3/basys3.srcs/ppu_pceg.vhd
@@ -3,18 +3,18 @@ use ieee.std_logic_1164.all;
--use ieee.numeric_std.all;
entity ppu_pceg is port(
- CLK: in std_logic; -- system clock
- RESET: in std_logic; -- async reset
- SPRITE: out std_logic; -- sprite info fetch + sprite pixel fetch
- COMP_PAL: out std_logic; -- compositor + palette lookup
- DONE: out std_logic); -- last pipeline stage done
+ CLK : in std_logic; -- system clock
+ RESET : in std_logic; -- async reset
+ SPRITE : out std_logic; -- sprite info fetch + sprite pixel fetch
+ COMP_PAL : out std_logic; -- compositor + palette lookup
+ DONE : out std_logic); -- last pipeline stage done
end ppu_pceg;
architecture Behavioral of ppu_pceg is
- constant PPU_PL_TOTAL_STAGES: natural := 14;
+ constant PPU_PL_TOTAL_STAGES : natural := 14;
type states is (PL_SPRITE, PL_COMP_PAL, PL_DONE);
- signal state: states := PL_SPRITE;
+ signal state : states := PL_SPRITE;
begin
-- output drivers
SPRITE <= CLK when RESET = '0' and state = PL_SPRITE else '0';
@@ -22,7 +22,7 @@ begin
DONE <= '1' when RESET = '0' and state = PL_DONE else '0';
process(CLK, RESET)
- variable CLK_IDX: natural range 0 to PPU_PL_TOTAL_STAGES+1 := 0;
+ variable CLK_IDX : natural range 0 to PPU_PL_TOTAL_STAGES+1 := 0;
begin
if RESET = '1' then
state <= PL_SPRITE;
diff --git a/basys3/basys3.srcs/ppu_pceg_tb.vhdl b/basys3/basys3.srcs/ppu_pceg_tb.vhd
index 137d4b4..719ec06 100644
--- a/basys3/basys3.srcs/ppu_pceg_tb.vhdl
+++ b/basys3/basys3.srcs/ppu_pceg_tb.vhd
@@ -10,27 +10,27 @@ end ppu_pceg_tb;
architecture behavioral of ppu_pceg_tb is
component ppu_pceg port(
- CLK: in std_logic; -- system clock
- RESET: in std_logic; -- async reset
- SPRITE: out std_logic; -- sprite info fetch + sprite pixel fetch
- COMP_PAL: out std_logic; -- compositor + palette lookup
- DONE: out std_logic); -- last pipeline stage done
+ CLK : in std_logic; -- system clock
+ RESET : in std_logic; -- async reset
+ SPRITE : out std_logic; -- sprite info fetch + sprite pixel fetch
+ COMP_PAL : out std_logic; -- compositor + palette lookup
+ DONE : out std_logic); -- last pipeline stage done
end component;
- signal CLK: std_logic := '0';
- signal RESET: std_logic := '0';
- signal SPRITE: std_logic;
- signal COMP_PAL: std_logic;
- signal DONE: std_logic;
+ signal CLK : std_logic := '0';
+ signal RESET : std_logic := '0';
+ signal SPRITE : std_logic;
+ signal COMP_PAL : std_logic;
+ signal DONE : std_logic;
begin
- uut: ppu_pceg port map(
+ uut : ppu_pceg port map(
CLK => CLK,
RESET => RESET,
SPRITE => SPRITE,
COMP_PAL => COMP_PAL,
DONE => DONE);
- tb: process
+ tb : process
begin
for i in 0 to 32 loop
if i > 20 then
diff --git a/basys3/basys3.srcs/ppu_plut.vhd b/basys3/basys3.srcs/ppu_plut.vhd
new file mode 100644
index 0000000..d2e132e
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_plut.vhd
@@ -0,0 +1,69 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use work.ppu_consts.all;
+use ieee.numeric_std.all;
+use ieee.std_logic_unsigned.all;
+
+entity ppu_plut is port (
+ CLK : in std_logic; -- system clock
+ CIDX : in std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- color in
+ RESET : in std_logic;
+
+ -- internal memory block (AUX)
+ PAL_WEN : in std_logic; -- VRAM PAL write enable
+ PAL_ADDR : in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); -- VRAM PAL address
+ PAL_DATA : in std_logic_vector(PPU_PAL_DATA_WIDTH-1 downto 0); -- VRAM PAL data
+
+ R,G,B : out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0)); -- VGA color out
+end ppu_plut;
+
+architecture Behavioral of ppu_plut is
+ component er_ram
+ generic(
+ ADDR_W : natural := PPU_PAL_ADDR_WIDTH; -- ADDR line width
+ DATA_W : natural := PPU_PAL_DATA_WIDTH; -- DATA line width
+ ADDR_LOW : natural := 16#0000#; -- starting address
+ ADDR_RANGE : natural := 16#0040#); -- amount of valid addresses after ADDR_LOW
+ port(
+ CLK : in std_logic; -- clock
+ RST : in std_logic; -- async memory clear
+ WEN : in std_logic; -- write enable
+ ADDR : in std_logic_vector(ADDR_W-1 downto 0);
+ DATA : in std_logic_vector(DATA_W-1 downto 0);
+ REG : out std_logic_vector((ADDR_RANGE*DATA_W)-1 downto 0)); -- exposed register output
+ end component;
+
+ signal PLUT : std_logic_vector((64 * PPU_PAL_DATA_WIDTH)-1 downto 0) := (others => '0');
+ signal CHECK_ZERO_CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0) := (others => '0'); -- color in
+begin
+ RAM : component er_ram port map(
+ CLK => CLK,
+ RST => RESET,
+ WEN => PAL_WEN,
+ ADDR => PAL_ADDR,
+ DATA => PAL_DATA,
+ REG => PLUT);
+
+ process(CLK, RESET)
+ variable COLOR : std_logic_vector(PPU_PAL_DATA_WIDTH-1 downto 0) := (others => '0'); -- COLORS RGB IN
+ variable CIDX_INT : integer := 0;
+ begin
+ if RESET = '1' then
+ PLUT <= (others => '0');
+ else
+ if rising_edge (CLK) then
+ if (CIDX /= CHECK_ZERO_CIDX) then
+ CIDX_INT := to_integer(unsigned(CIDX));
+ COLOR := PLUT((12 * CIDX_INT) + 11 downto (12*CIDX_INT));
+ R <= COLOR(11 downto 8);
+ G <= COLOR(7 downto 4);
+ B <= COLOR(3 downto 0);
+ else
+ R <= x"0";
+ G <= x"0";
+ B <= x"0";
+ end if;
+ end if;
+ end if;
+ end process;
+end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_sprite_bg.vhd b/basys3/basys3.srcs/ppu_sprite_bg.vhd
new file mode 100644
index 0000000..dba5b8e
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_sprite_bg.vhd
@@ -0,0 +1,138 @@
+library ieee;
+library work;
+
+use ieee.std_logic_1164.all;
+use ieee.std_logic_unsigned.all;
+use ieee.numeric_std.all;
+use work.ppu_consts.all;
+
+-- TODO: add input stable / output stable pipeline stages if this doesn't work with propagation delays
+entity ppu_sprite_bg is port(
+ -- inputs
+ CLK : in std_logic; -- pipeline clock
+ RESET : in std_logic; -- reset clock counter
+ OE : in std_logic; -- output enable (of CIDX)
+ X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
+ Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
+
+ -- aux inputs
+ BG_SHIFT_X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0);
+ BG_SHIFT_Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0);
+
+ -- used memory blocks
+ BAM_ADDR : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ BAM_DATA : in std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
+ TMM_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ TMM_DATA : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
+
+ -- outputs
+ CIDX : out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0)); -- output color
+end ppu_sprite_bg;
+
+architecture Behavioral of ppu_sprite_bg is
+ component ppu_sprite_transform port(
+ XI : in unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- pixel position relative to tile
+ YI : in unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0); -- pixel position relative to tile
+ FLIP_H, FLIP_V : in std_logic; -- flip sprite
+ XO : out unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- new pixel position relative to tile
+ YO : out unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0)); -- new pixel position relative to tile
+ end component;
+
+ -- BAM and TMM in/out temp + registers
+ signal T_BAM_ADDR, R_BAM_ADDR : std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0) := (others => '0');
+ signal T_BAM_DATA, R_BAM_DATA : std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0) := (others => '0');
+ signal T_TMM_ADDR, R_TMM_ADDR : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0) := (others => '0');
+ signal T_TMM_DATA, R_TMM_DATA : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0) := (others => '0');
+
+ -- state machine for synchronizing pipeline stages
+ type states is (PL_BAM_ADDR, PL_BAM_DATA, PL_TMM_ADDR, PL_TMM_DATA);
+ signal state : states := PL_BAM_ADDR;
+
+ -- docs/architecture.md#background-attribute-memory
+ alias BAM_DATA_FLIP_H is R_BAM_DATA(14); -- flip horizontally
+ alias BAM_DATA_FLIP_V is R_BAM_DATA(13); -- flip vertically
+ alias BAM_DATA_TILE_IDX is R_BAM_DATA(9 downto 0); -- tilemap tile index
+ alias BAM_DATA_COL_IDX is R_BAM_DATA(12 downto 10); -- palette for sprite
+
+ -- auxiliary signals (temp variables)
+ signal PIXEL_ABS_X, PIXEL_ABS_Y : integer := 0; -- absolute pixel position (relative to BG canvas instead of viewport)
+ signal TILE_IDX_X, TILE_IDX_Y : integer := 0; -- background canvas tile grid xy
+ signal TILE_PIDX_X, TRANS_TILE_PIDX_X : unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0) := (others => '0'); -- x position of pixel within tile (local tile coords)
+ signal TILE_PIDX_Y, TRANS_TILE_PIDX_Y : unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0) := (others => '0'); -- y position of pixel within tile (local tile coords)
+ signal TRANS_TILE_PIDX : integer := 0; -- index of pixel within tile (reading order)
+ signal TILEMAP_WORD_OFFSET : integer := 0; -- word offset from tile start address in TMM
+ signal PIXEL_BIT_OFFSET : integer := 0; -- pixel index within word of TMM
+ signal TMM_DATA_PAL_IDX : std_logic_vector(PPU_PALETTE_COLOR_WIDTH-1 downto 0); -- color of palette
+ signal T_CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0) := (others => '0'); -- output color buffer/register
+begin
+ -- output drivers
+ CIDX <= T_CIDX when OE = '1' else (others => 'Z');
+ BAM_ADDR <= R_BAM_ADDR;
+ TMM_ADDR <= R_TMM_ADDR;
+ T_BAM_DATA <= BAM_DATA;
+ T_TMM_DATA <= TMM_DATA;
+ -- CIDX combination
+ T_CIDX <= BAM_DATA_COL_IDX & TMM_DATA_PAL_IDX;
+
+ -- BAM address calculations
+ PIXEL_ABS_X <= to_integer(unsigned(X)) + to_integer(unsigned(BG_SHIFT_X)); -- pixel position relative to background canvas
+ PIXEL_ABS_Y <= to_integer(unsigned(Y)) + to_integer(unsigned(BG_SHIFT_Y)); -- pixel position relative to background canvas
+ TILE_IDX_X <= PIXEL_ABS_X / 16; -- tile grid index
+ TILE_IDX_Y <= PIXEL_ABS_Y / 16; -- tile grid index
+ TILE_PIDX_X <= to_unsigned(PIXEL_ABS_X - TILE_IDX_X * 16, TILE_PIDX_X'length); -- (sprite local) pixel coords
+ TILE_PIDX_Y <= to_unsigned(PIXEL_ABS_Y - TILE_IDX_Y * 16, TILE_PIDX_Y'length); -- (sprite local) pixel coords
+ T_BAM_ADDR <= std_logic_vector(to_unsigned((TILE_IDX_Y * integer(PPU_BG_CANVAS_TILES_H)) + TILE_IDX_X, PPU_BAM_ADDR_WIDTH));
+
+ -- BAM data dependant calculations
+ transform: component ppu_sprite_transform port map(
+ XI => TILE_PIDX_X,
+ YI => TILE_PIDX_Y,
+ FLIP_H => BAM_DATA_FLIP_H,
+ FLIP_V => BAM_DATA_FLIP_V,
+ XO => TRANS_TILE_PIDX_X,
+ YO => TRANS_TILE_PIDX_Y);
+
+ -- TMM address calculations
+ TRANS_TILE_PIDX <= integer(PPU_SPRITE_WIDTH) * to_integer(TRANS_TILE_PIDX_Y) + to_integer(TRANS_TILE_PIDX_X); -- pixel index of sprite
+ TILEMAP_WORD_OFFSET <= TRANS_TILE_PIDX / PPU_PIXELS_PER_TILE_WORD; -- word offset from starting word of sprite
+ PIXEL_BIT_OFFSET <= TRANS_TILE_PIDX mod PPU_PIXELS_PER_TILE_WORD; -- pixel bit offset
+ T_TMM_ADDR <= std_logic_vector(to_unsigned(PPU_SPRITE_WORD_COUNT * to_integer(unsigned(BAM_DATA_TILE_IDX)) + TILEMAP_WORD_OFFSET, PPU_TMM_ADDR_WIDTH)); -- TMM address
+
+ -- TMM DATA
+ with PIXEL_BIT_OFFSET select
+ TMM_DATA_PAL_IDX <= R_TMM_DATA(2 downto 0) when 0,
+ R_TMM_DATA(5 downto 3) when 1,
+ R_TMM_DATA(8 downto 6) when 2,
+ R_TMM_DATA(11 downto 9) when 3,
+ R_TMM_DATA(14 downto 12) when 4,
+ (others => '0') when others;
+
+ -- state machine (pipeline stage counter) + sync r/w
+ process(CLK, RESET)
+ begin
+ if RESET = '1' then
+ -- reset state
+ state <= PL_BAM_ADDR;
+ -- reset internal pipeline registers
+ R_BAM_ADDR <= (others => '0');
+ R_BAM_DATA <= (others => '0');
+ R_TMM_ADDR <= (others => '0');
+ R_TMM_DATA <= (others => '0');
+ elsif rising_edge(CLK) then
+ case state is
+ when PL_BAM_ADDR =>
+ state <= PL_BAM_DATA;
+ R_BAM_ADDR <= T_BAM_ADDR;
+ when PL_BAM_DATA =>
+ state <= PL_TMM_ADDR;
+ R_BAM_DATA <= T_BAM_DATA;
+ when PL_TMM_ADDR =>
+ state <= PL_TMM_DATA;
+ R_TMM_ADDR <= T_TMM_ADDR;
+ when PL_TMM_DATA =>
+ state <= PL_BAM_ADDR;
+ R_TMM_DATA <= T_TMM_DATA;
+ end case;
+ end if;
+ end process;
+end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_sprite_bg_tb.vhd b/basys3/basys3.srcs/ppu_sprite_bg_tb.vhd
new file mode 100644
index 0000000..65da15c
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_sprite_bg_tb.vhd
@@ -0,0 +1,107 @@
+library ieee;
+library work;
+library unisim;
+
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use unisim.vcomponents.all;
+use work.ppu_consts.all;
+
+entity ppu_sprite_bg_tb is
+end ppu_sprite_bg_tb;
+
+architecture Behavioral of ppu_sprite_bg_tb is
+ component ppu_sprite_bg port(
+ -- inputs
+ CLK : in std_logic; -- pipeline clock
+ RESET : in std_logic; -- reset clock counter
+ OE : in std_logic; -- output enable (of CIDX)
+ X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
+ Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
+
+ -- aux inputs
+ BG_SHIFT_X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0);
+ BG_SHIFT_Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0);
+
+ -- used memory blocks
+ BAM_ADDR : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ BAM_DATA : in std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
+ TMM_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ TMM_DATA : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
+
+ -- outputs
+ CIDX : out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0)); -- output color
+ end component;
+ signal CLK : std_logic := '0'; -- pipeline clock
+ signal RESET : std_logic := '0'; -- reset clock counter
+ signal OE : std_logic := '0'; -- output enable (of CIDX)
+ signal X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0) := (others => '0'); -- current screen pixel x
+ signal Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0) := (others => '0'); -- current screen pixel y
+ signal BG_SHIFT_X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0) := (others => '0');
+ signal BG_SHIFT_Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0) := (others => '0');
+ signal BAM_ADDR : std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
+ signal BAM_DATA : std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0) := (others => '0');
+ signal TMM_ADDR : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ signal TMM_DATA : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0) := (others => '0');
+ signal CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- output color
+begin
+ uut : ppu_sprite_bg port map(
+ CLK => CLK,
+ RESET => RESET,
+ OE => OE,
+ X => X,
+ Y => Y,
+ BG_SHIFT_X => BG_SHIFT_X,
+ BG_SHIFT_Y => BG_SHIFT_Y,
+ BAM_ADDR => BAM_ADDR,
+ BAM_DATA => BAM_DATA,
+ TMM_ADDR => TMM_ADDR,
+ TMM_DATA => TMM_DATA,
+ CIDX => CIDX);
+
+ -- inputs
+ -- BAM_DATA -> FLIP_H + palette index 2 + tilemap index 10
+ -- TMM_DATA -> all pixels color 7
+ -- X -> 25
+ -- Y -> 60
+ -- BG_SHIFT_X -> 3
+ -- BG_SHIFT_Y -> 3
+ BAM_DATA <= 15x"4814"; -- hex((1 << 14) | (0 << 13) | (2 << 10) | (20 << 0))
+ TMM_DATA <= 15x"7e3f"; -- hex(0x7fff & ~(0b111 << 6))
+ X <= std_logic_vector(to_unsigned(25, PPU_POS_H_WIDTH));
+ Y <= std_logic_vector(to_unsigned(60, PPU_POS_V_WIDTH));
+ BG_SHIFT_X <= std_logic_vector(to_unsigned(3, PPU_POS_H_WIDTH));
+ BG_SHIFT_Y <= std_logic_vector(to_unsigned(3, PPU_POS_V_WIDTH));
+
+ -- expected outputs:
+ -- absolute pixel coordinates -> (28, 63)
+ -- absolute background canvas tile index coordinates -> (1, 3)
+ -- tile index -> 3 * 40 + 1 = 121
+ -- BAM_ADDR => 121 (OK)
+ -- sprite local coordinates -> (12, 15)
+ -- transformed local coordinates -> (12, 0) (apply FLIP_H)
+ -- pixel index -> 0 * 16 + 12 = 12
+ -- pixel word address -> 52 * 20 + 12//5 -> 1042
+ -- TMM_ADDR => 1042 (OK)
+ -- pixel bits -> [pixel n+] = 12 % 5 = 2 -> 8 downto 6
+ -- cidx should be -- (2 << 3) | (0 << 0) = 16
+ -- CIDX => 16 (OK)
+
+ tb : process
+ begin
+ for i in 0 to 32 loop
+ if i > 10 then
+ OE <= '1';
+ end if;
+ if i > 20 then
+ RESET <= '1';
+ end if;
+
+ wait for 5 ns;
+ CLK <= '1';
+ wait for 5 ns;
+ CLK <= '0';
+ end loop;
+ wait; -- stop for simulator
+ end process;
+end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_sprite_fg.vhd b/basys3/basys3.srcs/ppu_sprite_fg.vhd
new file mode 100644
index 0000000..af7cfa3
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_sprite_fg.vhd
@@ -0,0 +1,192 @@
+library ieee;
+library work;
+
+use ieee.std_logic_1164.all;
+use ieee.std_logic_unsigned.all;
+use ieee.numeric_std.all;
+use work.ppu_consts.all;
+
+-- TODO: add input stable / output stable pipeline stages if this doesn't work with propagation delays
+entity ppu_sprite_fg is -- foreground sprite
+ generic (
+ IDX : natural := 0); -- sprite index number
+ port(
+ -- inputs
+ CLK : in std_logic; -- system clock
+ RESET : in std_logic; -- reset internal memory and clock counters
+ OE : in std_logic; -- output enable (of CIDX)
+ X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
+ Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
+ FETCH : in std_logic; -- fetch sprite data from TMM
+ VBLANK : in std_logic; -- fetch during vblank
+
+ -- internal memory block (FAM)
+ FAM_WEN : in std_logic; -- VRAM FAM write enable
+ FAM_ADDR : in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); -- VRAM fam address
+ FAM_DATA : in std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0); -- VRAM fam data
+
+ -- used memory blocks
+ TMM_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ TMM_DATA : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
+
+ -- outputs
+ CIDX : out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- output color
+ HIT : out std_logic); -- current pixel is not transparent
+end ppu_sprite_fg;
+
+architecture Behavioral of ppu_sprite_fg is
+ component ppu_sprite_transform port(
+ XI : in unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- pixel position relative to tile
+ YI : in unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0); -- pixel position relative to tile
+ FLIP_H, FLIP_V : in std_logic; -- flip sprite
+ XO : out unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- new pixel position relative to tile
+ YO : out unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0)); -- new pixel position relative to tile
+ end component;
+ component er_ram -- exposed register RAM
+ generic(
+ ADDR_W : natural := 2; -- ADDR line width
+ DATA_W : natural := 2; -- DATA line width
+ ADDR_LOW : natural := 16#0000#; -- starting address
+ ADDR_RANGE : natural := 16#0002#); -- amount of valid addresses after ADDR_LOW
+ port(
+ CLK : in std_logic; -- clock
+ RST : in std_logic; -- async memory clear
+ WEN : in std_logic; -- write enable
+ ADDR : in std_logic_vector(ADDR_W-1 downto 0); -- address line
+ DATA : in std_logic_vector(DATA_W-1 downto 0); -- data input
+ REG : out std_logic_vector((ADDR_RANGE*DATA_W)-1 downto 0)); -- exposed register output
+ end component;
+
+ -- FAM and TMM in/out lines
+ signal T_TMM_ADDR : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0) := (others => '0');
+ signal T_TMM_DATA : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0) := (others => '0');
+
+ -- auxiliary signals (temp variables)
+ signal T_CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0) := (others => '0'); -- output color buffer/register
+ signal INT_FAM : std_logic_vector(31 downto 0);
+
+ -- FAM aliases (docs/architecture.md#foreground-attribute-memory)
+ alias FAM_REG_FLIP_H is INT_FAM(31); -- Flip horizontally
+ alias FAM_REG_FLIP_V is INT_FAM(30); -- Flip vertically
+ alias FAM_REG_POS_H is INT_FAM(29 downto 21); -- horizontal position (offset by -16)
+ alias FAM_REG_POS_V is INT_FAM(20 downto 13); -- vertical position (offset by -16)
+ alias FAM_REG_COL_IDX is INT_FAM(12 downto 10); -- Palette index for tile
+ alias FAM_REG_TILE_IDX is INT_FAM(9 downto 0); -- Tilemap index
+
+ signal SPRITE_ACTIVE : std_logic := '0'; -- is pixel in bounding box of sprite
+ signal PIXEL_ABS_X, PIXEL_ABS_Y : integer := 0; -- absolute pixel position (relative to FG canvas instead of viewport)
+ signal TILE_PIDX_X, TRANS_TILE_PIDX_X : unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0) := (others => '0'); -- xy position of pixel within tile (local tile coords)
+ signal TILE_PIDX_Y, TRANS_TILE_PIDX_Y : unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0) := (others => '0'); -- xy position of pixel within tile (local tile coords)
+ signal TRANS_TILE_PIXEL_IDX : integer := 0; -- index of pixel within tile (reading order)
+ signal TILEMAP_WORD_OFFSET : integer := 0; -- word offset from tile start address in TMM
+ signal TMM_DATA_PAL_IDX : std_logic_vector(PPU_PALETTE_COLOR_WIDTH-1 downto 0); -- color of palette
+
+ -- TMM cache lines
+ signal TMM_CACHE_WEN, TMM_CACHE_UPDATE_TURN : std_logic := '0';
+ signal TMM_CACHE_DATA : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0) := (others => '0');
+ signal TMM_CACHE_ADDR : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0) := (others => '0');
+ signal TMM_CACHE : std_logic_vector((PPU_SPRITE_WORD_COUNT * PPU_TMM_DATA_WIDTH)-1 downto 0);
+begin
+ -- output drivers
+ CIDX <= T_CIDX when OE = '1' else (others => 'Z');
+ -- CIDX combination
+ T_CIDX <= FAM_REG_COL_IDX & TMM_DATA_PAL_IDX;
+
+ -- FAM memory
+ FAM : component er_ram
+ generic map(
+ ADDR_W => PPU_FAM_ADDR_WIDTH,
+ DATA_W => PPU_FAM_DATA_WIDTH,
+ ADDR_LOW => IDX*2,
+ ADDR_RANGE => 2)
+ port map(
+ CLK => CLK,
+ RST => RESET,
+ WEN => FAM_WEN,
+ ADDR => FAM_ADDR,
+ DATA => FAM_DATA,
+ REG => INT_FAM);
+
+ -- pixel position within bounding box of sprite
+ SPRITE_ACTIVE <= '1' when ((unsigned(X) + 16) >= unsigned(FAM_REG_POS_H)) and
+ ((unsigned(X) + 16) < (unsigned(FAM_REG_POS_H) + to_unsigned(PPU_SPRITE_WIDTH, PPU_POS_H_WIDTH))) and
+ ((unsigned(Y) + 16) >= unsigned(FAM_REG_POS_V)) and
+ ((unsigned(Y) + 16) < (unsigned(FAM_REG_POS_V) + to_unsigned(PPU_SPRITE_HEIGHT, PPU_POS_V_WIDTH))) else '0';
+
+ -- (sprite local) pixel coords
+ TILE_PIDX_X <= resize(unsigned(X) + 16 - resize(unsigned(FAM_REG_POS_H), TILE_PIDX_X'length), TILE_PIDX_X'length);
+ TILE_PIDX_Y <= resize(unsigned(Y) + 16 - resize(unsigned(FAM_REG_POS_V), TILE_PIDX_Y'length), TILE_PIDX_Y'length);
+
+ -- transform local coords
+ transform: component ppu_sprite_transform port map(
+ XI => TILE_PIDX_X,
+ YI => TILE_PIDX_Y,
+ FLIP_H => FAM_REG_FLIP_H,
+ FLIP_V => FAM_REG_FLIP_V,
+ XO => TRANS_TILE_PIDX_X,
+ YO => TRANS_TILE_PIDX_Y);
+
+ -- pixel index
+ TRANS_TILE_PIXEL_IDX <= integer(PPU_SPRITE_WIDTH) * to_integer(TRANS_TILE_PIDX_Y) + to_integer(TRANS_TILE_PIDX_X);
+ -- palette color at pixel
+ TMM_DATA_PAL_IDX <= TMM_CACHE(TRANS_TILE_PIXEL_IDX * integer(PPU_PALETTE_COLOR_WIDTH) + integer(PPU_PALETTE_COLOR_WIDTH)-1 downto TRANS_TILE_PIXEL_IDX * integer(PPU_PALETTE_COLOR_WIDTH));
+ -- if pixel in sprite hitbox and TMM_DATA_PAL_IDX > 0
+ HIT <= SPRITE_ACTIVE and (nor TMM_DATA_PAL_IDX);
+
+ -- FETCH LOGIC BELOW
+ TMM_ADDR <= T_TMM_ADDR when TMM_CACHE_UPDATE_TURN else (others => 'Z');
+ T_TMM_DATA <= TMM_DATA;
+
+ -- TTM cache
+ ttm_cache : component er_ram
+ generic map(
+ ADDR_W => PPU_TMM_ADDR_WIDTH,
+ DATA_W => PPU_TMM_DATA_WIDTH,
+ ADDR_LOW => 0,
+ ADDR_RANGE => PPU_SPRITE_WORD_COUNT)
+ port map(
+ CLK => CLK,
+ RST => RESET,
+ WEN => TMM_CACHE_WEN,
+ ADDR => TMM_CACHE_ADDR,
+ DATA => TMM_CACHE_DATA,
+ REG => TMM_CACHE);
+
+ -- fetch machine, should do the following (offset data read by one clock -> propagation/lookup delay):
+ -- CLK[53 * IDX + 0] (addr = 0)
+ -- CLK[53 * IDX + 1] (addr = 1, read data[0])
+ -- CLK[53 * IDX + 2] (addr = 2, read data[1]), etc
+ -- a full tile is 52 words, but since the offset is 1 clock, a total copy takes 53 clock cycles
+ process(CLK, RESET, FETCH)
+ constant TMM_FETCH_CLK_RANGE_BEGIN : natural := PPU_TMM_CACHE_FETCH_C_COUNT * IDX; -- fetch CLK count for copying this module's sprite from TMM
+ variable TMM_FETCH_CTR : unsigned(PPU_TMM_CACHE_FETCH_A_WIDTH-1 downto 0) := (others => '0'); -- CLK counter while FETCH=1
+ variable TMM_FETCH_CTR_REL : unsigned(PPU_TMM_CACHE_FETCH_A_WIDTH-1 downto 0) := (others => '0'); -- CLK counter relative for sprite[IDX]
+ begin
+ if RESET = '1' or FETCH = '0' then
+ TMM_FETCH_CTR := (others => '0');
+ TMM_FETCH_CTR_REL := (others => '0');
+ TMM_CACHE_WEN <= '0';
+ TMM_CACHE_UPDATE_TURN <= '0';
+ elsif rising_edge(CLK) then
+ TMM_FETCH_CTR := TMM_FETCH_CTR + 1;
+ TMM_FETCH_CTR_REL := TMM_FETCH_CTR - TMM_FETCH_CLK_RANGE_BEGIN;
+
+ if TMM_FETCH_CTR >= TMM_FETCH_CLK_RANGE_BEGIN and
+ TMM_FETCH_CTR < (TMM_FETCH_CLK_RANGE_BEGIN + PPU_TMM_CACHE_FETCH_C_COUNT) then
+ TMM_CACHE_UPDATE_TURN <= '1';
+ if TMM_FETCH_CTR_REL < PPU_TMM_CACHE_FETCH_C_COUNT - 1 then -- calculate address until second to last clock
+ T_TMM_ADDR <= std_logic_vector(resize(TMM_FETCH_CTR - IDX, T_TMM_ADDR'length));
+ TMM_CACHE_ADDR <= std_logic_vector(resize(TMM_FETCH_CTR_REL - 1, TMM_CACHE_ADDR'length));
+ end if;
+
+ if TMM_FETCH_CTR_REL > 0 then -- read offset
+ TMM_CACHE_DATA <= T_TMM_DATA;
+ TMM_CACHE_WEN <= '1';
+ end if;
+ else
+ TMM_CACHE_WEN <= '0';
+ TMM_CACHE_UPDATE_TURN <= '0';
+ end if;
+ end if;
+ end process;
+end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_sprite_fg_tb.vhd b/basys3/basys3.srcs/ppu_sprite_fg_tb.vhd
new file mode 100644
index 0000000..19b9f35
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_sprite_fg_tb.vhd
@@ -0,0 +1,140 @@
+library ieee;
+library work;
+library unisim;
+
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use unisim.vcomponents.all;
+use work.ppu_consts.all;
+
+entity ppu_sprite_fg_tb is
+end ppu_sprite_fg_tb;
+
+architecture Behavioral of ppu_sprite_fg_tb is
+ component ppu_sprite_fg -- foreground sprite
+ generic (
+ IDX : natural := 0); -- sprite index number
+ port(
+ -- inputs
+ CLK : in std_logic; -- system clock
+ RESET : in std_logic; -- reset internal memory and clock counters
+ OE : in std_logic; -- output enable (of CIDX)
+ X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
+ Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
+ FETCH : in std_logic; -- fetch sprite data from TMM
+ VBLANK : in std_logic; -- fetch during vblank
+
+ -- internal memory block (FAM)
+ FAM_WEN : in std_logic; -- VRAM FAM write enable
+ FAM_ADDR : in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); -- VRAM fam address
+ FAM_DATA : in std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0); -- VRAM fam data
+
+ -- used memory blocks
+ TMM_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ TMM_DATA : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
+
+ -- outputs
+ CIDX : out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- output color
+ HIT : out std_logic); -- current pixel is not transparent
+ end component;
+ signal CLK : std_logic := '0';
+ signal RESET : std_logic := '0';
+ signal OE : std_logic := '0';
+ signal X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0) := (others => '0');
+ signal Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0) := (others => '0');
+ signal FETCH : std_logic := '0';
+ signal VBLANK : std_logic := '0';
+ signal FAM_WEN : std_logic := '0';
+ signal FAM_ADDR : std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0) := (others => '0');
+ signal FAM_DATA : std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0) := (others => '0');
+ signal TMM_ADDR : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
+ signal TMM_DATA : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0) := (others => '0');
+ signal CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0) := (others => '0');
+ signal HIT : std_logic;
+
+ signal CLK_I : unsigned(31 downto 0) := (others => '0');
+begin
+ uut: component ppu_sprite_fg
+ generic map( IDX => 2 )
+ port map(
+ CLK => CLK,
+ RESET => RESET,
+ OE => OE,
+ X => X,
+ Y => Y,
+ FETCH => FETCH,
+ VBLANK => VBLANK,
+ FAM_WEN => FAM_WEN,
+ FAM_ADDR => FAM_ADDR,
+ FAM_DATA => FAM_DATA,
+ TMM_ADDR => TMM_ADDR,
+ TMM_DATA => TMM_DATA,
+ CIDX => CIDX,
+ HIT => HIT);
+
+ tb : process
+ begin
+ -- initialize TMM cache
+ RESET <= '1'; wait for 1 ns; RESET <= '0'; wait for 1 ns;
+
+ OE <= '1';
+ VBLANK <= '0';
+ FETCH <= '0';
+
+ -- FAM contents:
+ -- flip horizontally
+ -- xy -> (42-16, 8-16) = (26, -8)
+ -- palette index 2
+ -- tilemap index 460 (0x1cc)
+ -- = hex((1 << 31) | (0 << 30) | (42 << 21) | (8 << 13) | (2 << 10) | (460 << 0))
+ FAM_WEN <= '1';
+ FAM_ADDR <= std_logic_vector(to_unsigned(16#0005#, FAM_ADDR'length));
+ FAM_DATA <= std_logic_vector(to_unsigned(16#8541#, FAM_DATA'length));
+ CLK <= '1'; wait for 1 ns; CLK <= '0'; wait for 1 ns;
+ FAM_ADDR <= std_logic_vector(to_unsigned(16#0004#, FAM_ADDR'length));
+ FAM_DATA <= std_logic_vector(to_unsigned(16#09cc#, FAM_DATA'length));
+ CLK <= '1'; wait for 1 ns; CLK <= '0'; wait for 1 ns;
+ FAM_WEN <= '0';
+
+ for i in 0 to 32 loop
+ if i = 0 then
+ X <= std_logic_vector(to_unsigned(25, X'length));
+ Y <= std_logic_vector(to_unsigned(60, Y'length));
+ end if;
+
+ if i = 3 then
+ X <= std_logic_vector(to_unsigned(29, X'length));
+ Y <= std_logic_vector(to_unsigned(4, Y'length));
+ end if;
+
+ wait for 5 ns; CLK <= '1'; wait for 5 ns; CLK <= '0';
+ end loop;
+
+ wait for 1 ns;
+ RESET <= '1';
+ wait for 10 ns;
+ RESET <= '0';
+ VBLANK <= '1';
+ FETCH <= '1';
+ wait for 1 ns;
+
+ -- FETCH check
+ for i in 0 to 500 loop
+ CLK_I <= to_unsigned(i, 32);
+ -- if i > 10 then
+ -- OE <= '1';
+ -- end if;
+ -- if i > 20 then
+ -- RESET <= '1';
+ -- end if;
+
+ TMM_DATA <= std_logic_vector(to_unsigned(i - 106, TMM_DATA'length));
+
+ CLK <= '1';
+ wait for 1 ns;
+ CLK <= '0';
+ wait for 1 ns;
+ end loop;
+ wait; -- stop for simulator
+ end process;
+end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_sprite_transform.vhd b/basys3/basys3.srcs/ppu_sprite_transform.vhd
new file mode 100644
index 0000000..f94616c
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_sprite_transform.vhd
@@ -0,0 +1,24 @@
+library ieee;
+library work;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use work.ppu_consts.all;
+
+entity ppu_sprite_transform is port( -- flip sprites
+ XI : in unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- pixel position relative to tile
+ YI : in unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0); -- pixel position relative to tile
+ FLIP_H, FLIP_V : in std_logic; -- flip sprite
+ XO : out unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- new pixel position relative to tile
+ YO : out unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0)); -- new pixel position relative to tile
+end ppu_sprite_transform;
+
+architecture Behavioral of ppu_sprite_transform is
+ signal FLIPPED_X : unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0);
+ signal FLIPPED_Y : unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0);
+begin
+ FLIPPED_X <= to_unsigned(PPU_SPRITE_WIDTH-1, PPU_SPRITE_POS_H_WIDTH) - XI;
+ FLIPPED_Y <= to_unsigned(PPU_SPRITE_HEIGHT-1, PPU_SPRITE_POS_V_WIDTH) - YI;
+
+ XO <= FLIPPED_X when FLIP_V = '1' else XI;
+ YO <= FLIPPED_Y when FLIP_H = '1' else YI;
+end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_sprite_transform_tb.vhd b/basys3/basys3.srcs/ppu_sprite_transform_tb.vhd
new file mode 100644
index 0000000..516a653
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_sprite_transform_tb.vhd
@@ -0,0 +1,66 @@
+library ieee;
+library unisim;
+library work;
+
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use unisim.vcomponents.all;
+use work.ppu_consts.all;
+
+entity ppu_sprite_transform_tb is
+end ppu_sprite_transform_tb;
+
+architecture behavioral of ppu_sprite_transform_tb is
+ component ppu_sprite_transform port( -- flip sprites
+ XI : in unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- pixel position relative to tile
+ YI : in unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0); -- pixel position relative to tile
+ FLIP_H, FLIP_V : in std_logic; -- flip sprite
+ XO : out unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- new pixel position relative to tile
+ YO : out unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0)); -- new pixel position relative to tile
+ end component;
+
+ signal XI : unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0);
+ signal YI : unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0);
+ signal FLIP_H, FLIP_V : std_logic := '0';
+ signal XO : unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0);
+ signal YO : unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0);
+begin
+ uut : component ppu_sprite_transform
+ port map(
+ XI => XI,
+ YI => YI,
+ FLIP_H => FLIP_H,
+ FLIP_V => FLIP_V,
+ XO => XO,
+ YO => YO);
+
+ tb : process
+ begin
+ XI <= to_unsigned(4, PPU_SPRITE_POS_H_WIDTH);
+ YI <= to_unsigned(6, PPU_SPRITE_POS_V_WIDTH);
+ wait for 5 ns;
+
+ XI <= to_unsigned(2, PPU_SPRITE_POS_H_WIDTH);
+ YI <= to_unsigned(14, PPU_SPRITE_POS_V_WIDTH);
+ wait for 5 ns;
+
+ FLIP_H <= '1';
+ wait for 5 ns;
+
+ FLIP_V <= '1';
+ wait for 5 ns;
+
+ XI <= to_unsigned(6, PPU_SPRITE_POS_H_WIDTH);
+ YI <= to_unsigned(8, PPU_SPRITE_POS_V_WIDTH);
+ wait for 5 ns;
+
+ FLIP_H <= '0';
+ wait for 5 ns;
+
+ XI <= to_unsigned(2, PPU_SPRITE_POS_H_WIDTH);
+ YI <= to_unsigned(14, PPU_SPRITE_POS_V_WIDTH);
+ wait for 5 ns;
+
+ wait; -- stop for simulator
+ end process;
+end;
diff --git a/basys3/basys3.srcs/ppu_vga_native.vhd b/basys3/basys3.srcs/ppu_vga_native.vhd
new file mode 100644
index 0000000..47288e9
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_vga_native.vhd
@@ -0,0 +1,95 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use work.ppu_consts.all;
+use ieee.numeric_std.all;
+use ieee.std_logic_unsigned.all;
+
+entity ppu_vga_native is port (
+ CLK: in std_logic; -- system clock
+ RESET: in std_logic;
+
+ X: in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
+ Y: in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
+ PREADY: in std_logic; -- current pixel ready (pixel color is stable)
+ RI,GI,BI: in std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color in
+
+ RO,GO,BO: out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color out
+ VSYNC, HSYNC: out std_logic); -- VGA sync outputs
+end ppu_vga_native;
+
+architecture Behavioral of ppu_vga_native is
+ type line_buffer is array(319 downto 0) of std_logic_vector(11 downto 0);
+ signal ram_x0 : line_buffer; -- buffer 0
+ signal ram_x1: line_buffer; -- buffer 1
+ signal hcount: std_logic_vector(9 downto 0):= (others => '0');
+ signal vcount: std_logic_vector(9 downto 0):= (others => '0');
+ signal clk_counter: std_logic_vector(1 downto 0):= (others => '0');
+ signal rgb_out : std_logic_vector(11 downto 0):= (others => '0'); -- output colors
+ signal px : integer; -- conversion for hcount
+ signal py :integer; -- conversion for vcount
+ signal buffer_filled_on_buffer0 : integer;
+ signal buffer_filled_on_buffer1 : integer;
+begin
+ process (clk, x, y)
+ variable v_x : integer; -- integer to hold vector X
+ begin
+ if rising_edge(clk) then
+ clk_counter <= clk_counter + 1;
+ if clk_counter = "11" then
+ v_x := to_integer(unsigned(x) - 72);
+ if v_x >= 0 and v_x < 320 and PREADY = '1' then
+ if y(0) = '0' then
+ ram_x0(v_x) <= RI & GI & BI;
+ if v_x = 319 then
+ buffer_filled_on_buffer0 <= to_integer(unsigned(y) - 14);
+ end if;
+ else
+ ram_x1(v_x) <= RI & GI & BI;
+ if v_x = 319 then
+ buffer_filled_on_buffer1 <= to_integer(unsigned(y) - 14);
+ end if;
+ end if;
+ end if;
+ -- T display(display data)
+ if (hcount >= 144) and (hcount < 784) and (vcount >= 31) and (vcount < 511) then
+ px <= to_integer(unsigned(hcount) - 144);
+ py <= to_integer(unsigned(vcount) - 31);
+ if buffer_filled_on_buffer0 = (py/2) then
+ rgb_out <= ram_x0(px/2);
+ elsif buffer_filled_on_buffer1 = (py/2) then
+ rgb_out <= ram_x1(px/2);
+ else
+ rgb_out <= (others => '0');
+ end if;
+ end if;
+ -- pulse width
+ hsync <= '1';
+ if hcount < 97 then
+ hsync <= '0';
+ end if;
+
+ vsync <= '1';
+ if vcount < 3 then
+ vsync <= '0';
+ end if;
+
+ -- sync pulse time
+ hcount <= hcount + 1;
+
+ if hcount = 800 then
+ vcount <= vcount + 1;
+ hcount <= (others => '0');
+ end if;
+
+ if vcount = 521 then
+ vcount <= (others => '0');
+ end if;
+ end if;
+
+ -- output colors
+ RO <= rgb_out(11 downto 8);
+ GO <= rgb_out(7 downto 4);
+ BO <= rgb_out(3 downto 0);
+ end if;
+ end process;
+end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_vga_native_tb.vhd b/basys3/basys3.srcs/ppu_vga_native_tb.vhd
new file mode 100644
index 0000000..06061a0
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_vga_native_tb.vhd
@@ -0,0 +1,89 @@
+library ieee;
+library unisim;
+use ieee.std_logic_1164.all;
+use work.ppu_consts.all;
+use ieee.numeric_std.all;
+use ieee.std_logic_unsigned.all;
+use unisim.vcomponents.all;
+
+entity ppu_vga_native_tb is
+end ppu_vga_native_tb;
+
+architecture Behavioral of ppu_vga_native_tb is
+ component ppu_vga_native port (
+ CLK : in std_logic; -- system clock
+ RESET : in std_logic;
+
+ X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
+ Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
+ PREADY : in std_logic; -- current pixel ready (pixel color is stable)
+ RI,GI,BI : in std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color in
+
+ RO,GO,BO : out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color out
+ VSYNC, HSYNC : out std_logic); -- VGA sync outputs
+ end component;
+ signal CLK : std_logic := '0';
+ signal RST : std_logic := '0';
+ signal PREADY : std_logic := '0';
+
+ signal X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0) := (others => '0');
+ signal Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0) := (others => '0');
+
+ signal RI,GI,BI : std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0) := (others => '0'); -- VGA color in
+ signal RO,GO,BO : std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0) := (others => '0'); -- VGA color out
+ signal VSYNC, HSYNC : std_logic := '0';
+
+ signal Xas : integer := 72;
+ signal Yas : integer := 14;
+ signal counter : std_logic_vector(1 downto 0) := (others => '0');
+begin
+ uut : ppu_vga_native port map(
+ CLK => CLK,
+ RESET => RST,
+ X => X,
+ Y => Y,
+ PREADY => PREADY,
+ RI => RI,
+ GI => GI,
+ BI => BI,
+ RO => RO,
+ GO => GO,
+ BO => BO,
+ VSYNC => VSYNC,
+ HSYNC => HSYNC
+ );
+
+ tb : process
+ begin
+ CLK <= '1';
+ wait for 1 ps;
+ CLK <= '0';
+ wait for 1 ps;
+ end process;
+
+ process(CLK)
+ begin
+ if rising_edge(CLK) then
+ counter <= counter + 1;
+ end if;
+
+ if(counter = "11") then
+ pready <= '1';
+ ri <= x"d";
+ gi <= x"a";
+ bi <= x"d";
+ x <= std_logic_vector(to_unsigned(Xas, x'length));
+ if (Xas = 391) then
+ Xas <= 72;
+ y <= std_logic_vector(to_unsigned(Yas, y'length));
+ if (Yas = 255) then
+ Yas <= 14;
+ else
+ Yas <= Yas + 1;
+ end if;
+ else
+ Xas <= Xas + 1;
+ end if;
+ end if;
+ end process;
+end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_vga_tiny.vhd b/basys3/basys3.srcs/ppu_vga_tiny.vhd
new file mode 100644
index 0000000..0e496f6
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_vga_tiny.vhd
@@ -0,0 +1,73 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use work.ppu_consts.all;
+use ieee.numeric_std.all;
+use ieee.std_logic_unsigned.all;
+
+entity ppu_vga_tiny is port (
+ CLK : in std_logic; -- system clock
+ RESET : in std_logic;
+
+ X : out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
+ Y : out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
+
+ VSYNC, VBLANK,
+ HSYNC, HBLANK : out std_logic); -- VGA sync outputs
+end ppu_vga_tiny;
+
+architecture Behavioral of ppu_vga_tiny is
+ signal hcount : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0) := (others => '0');
+ signal vcount : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0) := (others => '0');
+ signal clk_counter : std_logic_vector(4 downto 0) := (others => '0');
+begin
+ process (CLK)
+ begin
+ if rising_edge(CLK) then
+ clk_counter <= clk_counter + 1;
+ if(clk_counter > 15) then
+ clk_counter <= (others => '0');
+ -- x,y data out
+ X <= hcount;
+ Y <= vcount;
+
+ --pulse width
+ if hcount < 32 or hcount >= 320-80 then
+ hsync <= '0';
+ else
+ hsync <= '1';
+ end if;
+
+ if vcount < 8 or vcount >= 240-15 then
+ vsync <= '0';
+ else
+ vsync <= '1';
+ end if;
+
+ -- Hblank and Vblank outputs
+ if hcount >= 320-80 then
+ hblank <= '1';
+ else
+ hblank <= '0';
+ end if;
+
+ if vcount >= 240-15 then
+ vblank <= '1';
+ else
+ vblank <= '0';
+ end if;
+
+ -- sync pulse time
+ hcount <= hcount + 1;
+
+ if hcount = 400 then
+ vcount <= vcount + 1;
+ hcount <= (others => '0');
+ end if;
+
+ if vcount = 255 then
+ vcount <= (others => '0');
+ end if;
+ end if;
+ end if;
+ end process;
+end Behavioral;
diff --git a/basys3/basys3.srcs/sources_1/ip/ppu_bam/ppu_bam.xci b/basys3/basys3.srcs/sources_1/ip/ppu_bam/ppu_bam.xci
index f5e1696..e299ea1 100644
--- a/basys3/basys3.srcs/sources_1/ip/ppu_bam/ppu_bam.xci
+++ b/basys3/basys3.srcs/sources_1/ip/ppu_bam/ppu_bam.xci
@@ -163,7 +163,7 @@
"BOARD_CONNECTIONS": [ { "value": "" } ],
"DEVICE": [ { "value": "xc7a35t" } ],
"PACKAGE": [ { "value": "cpg236" } ],
- "PREFHDL": [ { "value": "VERILOG" } ],
+ "PREFHDL": [ { "value": "VHDL" } ],
"SILICON_REVISION": [ { "value": "" } ],
"SIMULATOR_LANGUAGE": [ { "value": "MIXED" } ],
"SPEEDGRADE": [ { "value": "-1" } ],
diff --git a/basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci b/basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci
index 51914b1..9663635 100644
--- a/basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci
+++ b/basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci
@@ -14,7 +14,7 @@
"Use_AXI_ID": [ { "value": "false", "resolve_type": "user", "format": "bool", "enabled": false, "usage": "all" } ],
"AXI_ID_Width": [ { "value": "4", "resolve_type": "user", "format": "long", "enabled": false, "usage": "all" } ],
"Memory_Type": [ { "value": "Single_Port_RAM", "resolve_type": "user", "usage": "all" } ],
- "PRIM_type_to_Implement": [ { "value": "BRAM", "resolve_type": "user", "usage": "all" } ],
+ "PRIM_type_to_Implement": [ { "value": "BRAM", "resolve_type": "user", "enabled": false, "usage": "all" } ],
"Enable_32bit_Address": [ { "value": "false", "resolve_type": "user", "format": "bool", "usage": "all" } ],
"ecctype": [ { "value": "No_ECC", "resolve_type": "user", "enabled": false, "usage": "all" } ],
"ECC": [ { "value": "false", "resolve_type": "user", "format": "bool", "enabled": false, "usage": "all" } ],
@@ -32,13 +32,13 @@
"Algorithm": [ { "value": "Minimum_Area", "resolve_type": "user", "usage": "all" } ],
"Primitive": [ { "value": "8kx2", "resolve_type": "user", "enabled": false, "usage": "all" } ],
"Assume_Synchronous_Clk": [ { "value": "false", "resolve_type": "user", "format": "bool", "enabled": false, "usage": "all" } ],
- "Write_Width_A": [ { "value": "16", "resolve_type": "user", "format": "long", "usage": "all" } ],
- "Write_Depth_A": [ { "value": "49152", "value_src": "user", "resolve_type": "user", "format": "long", "usage": "all" } ],
- "Read_Width_A": [ { "value": "16", "resolve_type": "user", "usage": "all" } ],
+ "Write_Width_A": [ { "value": "15", "value_src": "user", "resolve_type": "user", "format": "long", "usage": "all" } ],
+ "Write_Depth_A": [ { "value": "53248", "value_src": "user", "resolve_type": "user", "format": "long", "usage": "all" } ],
+ "Read_Width_A": [ { "value": "15", "resolve_type": "user", "usage": "all" } ],
"Operating_Mode_A": [ { "value": "WRITE_FIRST", "resolve_type": "user", "usage": "all" } ],
"Enable_A": [ { "value": "Always_Enabled", "value_src": "user", "resolve_type": "user", "usage": "all" } ],
- "Write_Width_B": [ { "value": "16", "resolve_type": "user", "enabled": false, "usage": "all" } ],
- "Read_Width_B": [ { "value": "16", "resolve_type": "user", "enabled": false, "usage": "all" } ],
+ "Write_Width_B": [ { "value": "15", "resolve_type": "user", "enabled": false, "usage": "all" } ],
+ "Read_Width_B": [ { "value": "15", "resolve_type": "user", "enabled": false, "usage": "all" } ],
"Operating_Mode_B": [ { "value": "WRITE_FIRST", "resolve_type": "user", "enabled": false, "usage": "all" } ],
"Enable_B": [ { "value": "Always_Enabled", "resolve_type": "user", "enabled": false, "usage": "all" } ],
"Register_PortA_Output_of_Memory_Primitives": [ { "value": "true", "resolve_type": "user", "format": "bool", "usage": "all" } ],
@@ -110,10 +110,10 @@
"C_USE_BYTE_WEA": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ],
"C_WEA_WIDTH": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ],
"C_WRITE_MODE_A": [ { "value": "WRITE_FIRST", "resolve_type": "generated", "usage": "all" } ],
- "C_WRITE_WIDTH_A": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ],
- "C_READ_WIDTH_A": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ],
- "C_WRITE_DEPTH_A": [ { "value": "49152", "resolve_type": "generated", "format": "long", "usage": "all" } ],
- "C_READ_DEPTH_A": [ { "value": "49152", "resolve_type": "generated", "format": "long", "usage": "all" } ],
+ "C_WRITE_WIDTH_A": [ { "value": "15", "resolve_type": "generated", "format": "long", "usage": "all" } ],
+ "C_READ_WIDTH_A": [ { "value": "15", "resolve_type": "generated", "format": "long", "usage": "all" } ],
+ "C_WRITE_DEPTH_A": [ { "value": "53248", "resolve_type": "generated", "format": "long", "usage": "all" } ],
+ "C_READ_DEPTH_A": [ { "value": "53248", "resolve_type": "generated", "format": "long", "usage": "all" } ],
"C_ADDRA_WIDTH": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ],
"C_HAS_RSTB": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ],
"C_RST_PRIORITY_B": [ { "value": "CE", "resolve_type": "generated", "usage": "all" } ],
@@ -124,10 +124,10 @@
"C_USE_BYTE_WEB": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ],
"C_WEB_WIDTH": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ],
"C_WRITE_MODE_B": [ { "value": "WRITE_FIRST", "resolve_type": "generated", "usage": "all" } ],
- "C_WRITE_WIDTH_B": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ],
- "C_READ_WIDTH_B": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ],
- "C_WRITE_DEPTH_B": [ { "value": "49152", "resolve_type": "generated", "format": "long", "usage": "all" } ],
- "C_READ_DEPTH_B": [ { "value": "49152", "resolve_type": "generated", "format": "long", "usage": "all" } ],
+ "C_WRITE_WIDTH_B": [ { "value": "15", "resolve_type": "generated", "format": "long", "usage": "all" } ],
+ "C_READ_WIDTH_B": [ { "value": "15", "resolve_type": "generated", "format": "long", "usage": "all" } ],
+ "C_WRITE_DEPTH_B": [ { "value": "53248", "resolve_type": "generated", "format": "long", "usage": "all" } ],
+ "C_READ_DEPTH_B": [ { "value": "53248", "resolve_type": "generated", "format": "long", "usage": "all" } ],
"C_ADDRB_WIDTH": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ],
"C_HAS_MEM_OUTPUT_REGS_A": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ],
"C_HAS_MEM_OUTPUT_REGS_B": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ],
@@ -153,9 +153,9 @@
"C_EN_SHUTDOWN_PIN": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ],
"C_EN_SAFETY_CKT": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ],
"C_DISABLE_WARN_BHV_RANGE": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ],
- "C_COUNT_36K_BRAM": [ { "value": "22", "resolve_type": "generated", "usage": "all" } ],
- "C_COUNT_18K_BRAM": [ { "value": "1", "resolve_type": "generated", "usage": "all" } ],
- "C_EST_POWER_SUMMARY": [ { "value": "Estimated Power for IP : 16.534446 mW", "resolve_type": "generated", "usage": "all" } ]
+ "C_COUNT_36K_BRAM": [ { "value": "19", "resolve_type": "generated", "usage": "all" } ],
+ "C_COUNT_18K_BRAM": [ { "value": "8", "resolve_type": "generated", "usage": "all" } ],
+ "C_EST_POWER_SUMMARY": [ { "value": "Estimated Power for IP : 13.861152 mW", "resolve_type": "generated", "usage": "all" } ]
},
"project_parameters": {
"ARCHITECTURE": [ { "value": "artix7" } ],
@@ -163,7 +163,7 @@
"BOARD_CONNECTIONS": [ { "value": "" } ],
"DEVICE": [ { "value": "xc7a35t" } ],
"PACKAGE": [ { "value": "cpg236" } ],
- "PREFHDL": [ { "value": "VERILOG" } ],
+ "PREFHDL": [ { "value": "VHDL" } ],
"SILICON_REVISION": [ { "value": "" } ],
"SIMULATOR_LANGUAGE": [ { "value": "MIXED" } ],
"SPEEDGRADE": [ { "value": "-1" } ],
@@ -189,8 +189,8 @@
"rsta": [ { "direction": "in", "driver_value": "0" } ],
"wea": [ { "direction": "in", "size_left": "0", "size_right": "0", "driver_value": "0" } ],
"addra": [ { "direction": "in", "size_left": "15", "size_right": "0", "driver_value": "0" } ],
- "dina": [ { "direction": "in", "size_left": "15", "size_right": "0", "driver_value": "0" } ],
- "douta": [ { "direction": "out", "size_left": "15", "size_right": "0" } ],
+ "dina": [ { "direction": "in", "size_left": "14", "size_right": "0", "driver_value": "0" } ],
+ "douta": [ { "direction": "out", "size_left": "14", "size_right": "0" } ],
"rsta_busy": [ { "direction": "out" } ]
},
"interfaces": {
diff --git a/basys3/basys3.xpr b/basys3/basys3.xpr
index 466cc1f..4060a21 100644
--- a/basys3/basys3.xpr
+++ b/basys3/basys3.xpr
@@ -42,8 +42,10 @@
<Option Name="SimulatorGccVersionVCS" Val="9.2.0"/>
<Option Name="SimulatorGccVersionRiviera" Val="9.3.0"/>
<Option Name="SimulatorGccVersionActiveHdl" Val="9.3.0"/>
+ <Option Name="TargetLanguage" Val="VHDL"/>
<Option Name="BoardPart" Val="digilentinc.com:basys3:part0:1.2"/>
<Option Name="BoardPartRepoPaths" Val="$PPRDIR/../../../../.Xilinx/Vivado/2022.2/xhub/board_store/xilinx_board_store"/>
+ <Option Name="SourceMgmtMode" Val="DisplayOnly"/>
<Option Name="ActiveSimSet" Val="sim_1"/>
<Option Name="DefaultLib" Val="xil_defaultlib"/>
<Option Name="ProjectType" Val="Default"/>
@@ -59,20 +61,20 @@
<Option Name="IPStaticSourceDir" Val="$PIPUSERFILESDIR/ipstatic"/>
<Option Name="EnableBDX" Val="FALSE"/>
<Option Name="DSABoardId" Val="basys3"/>
- <Option Name="WTXSimLaunchSim" Val="12"/>
+ <Option Name="WTXSimLaunchSim" Val="113"/>
<Option Name="WTModelSimLaunchSim" Val="0"/>
<Option Name="WTQuestaLaunchSim" Val="0"/>
<Option Name="WTIesLaunchSim" Val="0"/>
<Option Name="WTVcsLaunchSim" Val="0"/>
<Option Name="WTRivieraLaunchSim" Val="0"/>
<Option Name="WTActivehdlLaunchSim" Val="0"/>
- <Option Name="WTXSimExportSim" Val="3"/>
- <Option Name="WTModelSimExportSim" Val="3"/>
- <Option Name="WTQuestaExportSim" Val="3"/>
+ <Option Name="WTXSimExportSim" Val="4"/>
+ <Option Name="WTModelSimExportSim" Val="4"/>
+ <Option Name="WTQuestaExportSim" Val="4"/>
<Option Name="WTIesExportSim" Val="0"/>
- <Option Name="WTVcsExportSim" Val="3"/>
- <Option Name="WTRivieraExportSim" Val="3"/>
- <Option Name="WTActivehdlExportSim" Val="3"/>
+ <Option Name="WTVcsExportSim" Val="4"/>
+ <Option Name="WTRivieraExportSim" Val="4"/>
+ <Option Name="WTActivehdlExportSim" Val="4"/>
<Option Name="GenerateIPUpgradeLog" Val="TRUE"/>
<Option Name="XSimRadix" Val="hex"/>
<Option Name="XSimTimeUnit" Val="ns"/>
@@ -96,20 +98,20 @@
<Attr Name="UsedIn" Val="simulation"/>
</FileInfo>
</File>
- <File Path="$PSRCDIR/ppu_addr_dec.vhdl">
- <FileInfo>
+ <File Path="$PSRCDIR/ppu_pceg.vhd">
+ <FileInfo SFType="VHDL2008">
<Attr Name="UsedIn" Val="synthesis"/>
<Attr Name="UsedIn" Val="simulation"/>
</FileInfo>
</File>
- <File Path="$PSRCDIR/ppu_pceg.vhdl">
- <FileInfo>
+ <File Path="$PSRCDIR/ppu.vhd">
+ <FileInfo SFType="VHDL2008">
<Attr Name="UsedIn" Val="synthesis"/>
<Attr Name="UsedIn" Val="simulation"/>
</FileInfo>
</File>
- <File Path="$PSRCDIR/ppu.vhd">
- <FileInfo>
+ <File Path="$PSRCDIR/ppu_addr_dec.vhd">
+ <FileInfo SFType="VHDL2008">
<Attr Name="UsedIn" Val="synthesis"/>
<Attr Name="UsedIn" Val="simulation"/>
</FileInfo>
@@ -117,7 +119,6 @@
<File Path="$PSRCDIR/apu.vhd">
<FileInfo>
<Attr Name="UserDisabled" Val="1"/>
- <Attr Name="AutoDisabled" Val="1"/>
<Attr Name="UsedIn" Val="synthesis"/>
<Attr Name="UsedIn" Val="simulation"/>
</FileInfo>
@@ -125,7 +126,73 @@
<File Path="$PSRCDIR/apu_note_to_frequency.vhd">
<FileInfo>
<Attr Name="UserDisabled" Val="1"/>
- <Attr Name="AutoDisabled" Val="1"/>
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/er_ram.vhd">
+ <FileInfo SFType="VHDL2008">
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/er_ram_mod.vhd">
+ <FileInfo SFType="VHDL2008">
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/ppu_aux.vhd">
+ <FileInfo SFType="VHDL2008">
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/ppu_sprite_bg.vhd">
+ <FileInfo>
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/ppu_sprite_transform.vhd">
+ <FileInfo>
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/ppu_sprite_fg.vhd">
+ <FileInfo SFType="VHDL2008">
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/ppu_vga_tiny.vhd">
+ <FileInfo>
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/ppu_comp.vhd">
+ <FileInfo>
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/ppu_plut.vhd">
+ <FileInfo>
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/ppu_vga_native.vhd">
+ <FileInfo>
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/apu_lut_reader.vhd">
+ <FileInfo>
+ <Attr Name="UserDisabled" Val="1"/>
<Attr Name="UsedIn" Val="synthesis"/>
<Attr Name="UsedIn" Val="simulation"/>
</FileInfo>
@@ -144,15 +211,14 @@
</FileSet>
<FileSet Name="sim_1" Type="SimulationSrcs" RelSrcDir="$PSRCDIR/sim_1" RelGenDir="$PGENDIR/sim_1">
<Filter Type="Srcs"/>
- <File Path="$PSRCDIR/ppu_addr_dec_tb.vhdl">
+ <File Path="$PSRCDIR/ppu_addr_dec_tb.vhd">
<FileInfo>
<Attr Name="UsedIn" Val="synthesis"/>
<Attr Name="UsedIn" Val="simulation"/>
</FileInfo>
</File>
- <File Path="$PSRCDIR/ppu_pceg_tb.vhdl">
- <FileInfo>
- <Attr Name="AutoDisabled" Val="1"/>
+ <File Path="$PSRCDIR/ppu_pceg_tb.vhd">
+ <FileInfo SFType="VHDL2008">
<Attr Name="UsedIn" Val="synthesis"/>
<Attr Name="UsedIn" Val="simulation"/>
</FileInfo>
@@ -160,14 +226,49 @@
<File Path="$PSRCDIR/apu_note_to_frequency_tb.vhd">
<FileInfo>
<Attr Name="UserDisabled" Val="1"/>
- <Attr Name="AutoDisabled" Val="1"/>
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/er_ram_mod_tb.vhd">
+ <FileInfo SFType="VHDL2008">
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/er_ram_tb.vhd">
+ <FileInfo SFType="VHDL2008">
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/ppu_aux_tb.vhd">
+ <FileInfo>
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/ppu_sprite_bg_tb.vhd">
+ <FileInfo SFType="VHDL2008">
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/ppu_sprite_transform_tb.vhd">
+ <FileInfo>
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
+ <File Path="$PSRCDIR/ppu_sprite_fg_tb.vhd">
+ <FileInfo SFType="VHDL2008">
<Attr Name="UsedIn" Val="synthesis"/>
<Attr Name="UsedIn" Val="simulation"/>
</FileInfo>
</File>
<Config>
<Option Name="DesignMode" Val="RTL"/>
- <Option Name="TopModule" Val="ppu_addr_dec_tb"/>
+ <Option Name="TopModule" Val="ppu_sprite_fg_tb"/>
<Option Name="TopLib" Val="xil_defaultlib"/>
<Option Name="TransportPathDelay" Val="0"/>
<Option Name="TransportIntDelay" Val="0"/>
@@ -177,10 +278,19 @@
<Option Name="PamSignalDriverFile" Val="xil_bypass_driver"/>
<Option Name="PamPseudoTop" Val="pseudo_tb"/>
<Option Name="SrcSet" Val="sources_1"/>
+ <Option Name="NLNetlistMode" Val="funcsim"/>
</Config>
</FileSet>
<FileSet Name="utils_1" Type="Utils" RelSrcDir="$PSRCDIR/utils_1" RelGenDir="$PGENDIR/utils_1">
<Filter Type="Utils"/>
+ <File Path="$PSRCDIR/utils_1/imports/synth_1/ppu.dcp">
+ <FileInfo>
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="implementation"/>
+ <Attr Name="UsedInSteps" Val="synth_1"/>
+ <Attr Name="AutoDcp" Val="1"/>
+ </FileInfo>
+ </File>
<Config>
<Option Name="TopAutoSet" Val="TRUE"/>
</Config>
@@ -234,11 +344,12 @@
</Simulator>
</Simulators>
<Runs Version="1" Minor="19">
- <Run Id="synth_1" Type="Ft3:Synth" SrcSet="sources_1" Part="xc7a35tcpg236-1" ConstrsSet="constrs_1" Description="Vivado Synthesis Defaults" AutoIncrementalCheckpoint="true" WriteIncrSynthDcp="false" State="current" IncludeInArchive="true" IsChild="false" AutoIncrementalDir="$PSRCDIR/utils_1/imports/synth_1" AutoRQSDir="$PSRCDIR/utils_1/imports/synth_1">
+ <Run Id="synth_1" Type="Ft3:Synth" SrcSet="sources_1" Part="xc7a35tcpg236-1" ConstrsSet="constrs_1" Description="Vivado Synthesis Defaults" AutoIncrementalCheckpoint="true" IncrementalCheckpoint="$PSRCDIR/utils_1/imports/synth_1/ppu.dcp" WriteIncrSynthDcp="false" State="current" Dir="$PRUNDIR/synth_1" IncludeInArchive="true" IsChild="false" AutoIncrementalDir="$PSRCDIR/utils_1/imports/synth_1" AutoRQSDir="$PSRCDIR/utils_1/imports/synth_1">
<Strategy Version="1" Minor="2">
<StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2022"/>
<Step Id="synth_design"/>
</Strategy>
+ <GeneratedRun Dir="$PRUNDIR" File="gen_run.xml"/>
<ReportStrategy Name="Vivado Synthesis Default Reports" Flow="Vivado Synthesis 2022"/>
<Report Name="ROUTE_DESIGN.REPORT_METHODOLOGY" Enabled="1"/>
<RQSFiles/>
diff --git a/docs/architecture.md b/docs/architecture.md
index a60e0da..0c6fea7 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -134,6 +134,12 @@ Notable differences:
Our game doesn't need this capability for any visual effects. Leaving this
feature out will lead to a simpler hardware design
+- Sprites are positioned relative to the viewport, not the background layer
+
+ This leads to a simpler hardware architecture for the foreground sprite
+ rendering component. Since the CPU is already likely to reposition all
+ foreground sprites on every frame, the position calculation is moved to
+ hardware to software.
## Hardware design schematics
@@ -227,16 +233,17 @@ Important notes:
- The PPU's memory bus has 16-bit addresses and 16-bit words.
- Some memory regions use physical word sizes smaller than 16-bits, so
"unneeded" bits will be discarded by the PPU.
-- All "fields" or words containing multiple bit strings representing different
- pieces of information are ordered from MSB to LSB.
+- Apparent size means the amount of addresses in a given memory region. As
+ mentioned earlier, the exact word sizes of a memory area can vary, though
+ this is not visible to the CPU as all data is presented as 16-bit words.
-|Address offset|Size (16-bit words)|Alias|Description|
+|Address offset|Apparent size|Alias|Description|
|-|-|-|-|
-|`0x0000`|`0xc000`|TMM |[tilemap memory][TMM]|
-|`0xc000`|`0x04b0`|BAM |[background attribute memory][BAM]|
-|`0xc800`|`0x0100`|FAM |[foreground attribute memory][FAM]|
-|`0xcc00`|`0x0040`|PAL |[palettes][PAL]|
-|`0xce00`|`0x0002`|AUX |[auxiliary memory][AUX]|
+|`0x0000`|`0xd000`|TMM |[tilemap memory][TMM]|
+|`0xd000`|`0x04b0`|BAM |[background attribute memory][BAM]|
+|`0xd800`|`0x0100`|FAM |[foreground attribute memory][FAM]|
+|`0xdc00`|`0x0040`|PAL |[palettes][PAL]|
+|`0xde00`|`0x0002`|AUX |[auxiliary memory][AUX]|
This table contains the "official" PPU register offsets and sizes. Due to the
way the address decoder works, some of these memory regions might be duplicated
@@ -247,24 +254,44 @@ there is no address validity checking.
[TMM]: #tilemap-memory
### Tilemap memory
-- Each sprite takes up 768 bits spread across 48 16-bit words
-- Sprites and pixels are stored adjacently in memory without padding
-- Pixel order is from top-left to bottom-right in (English) reading order.
+- Each sprite takes up 768 bits spread across 52 15-bit words (with one
+ discarded padding bit per word)
+- Pixel index order is from top-left to bottom-right in (English) reading
+ order.
+- Bits `14 downto 3` of the byte with the highest address for a given tile are
+ not used
+- To calculate TMM address $a$ for any given pixel $p$ of tile with index $t$,
+ compute $a=52*t+\left\lfloor\frac{p}{5}\right\rfloor$
+
+Word format:
+
+|Range (VHDL)|Description|
+|-|-|
+|`15`|(discarded)|
+|`14 downto 12`|pixel $n+4$|
+|`11 downto 9`|pixel $n+3$|
+|`8 downto 6`|pixel $n+2$|
+|`5 downto 3`|pixel $n+1$|
+|`2 downto 0`|pixel $n+0$|
+
[BAM]: #background-attribute-memory
### Background attribute memory
- 15-bit words (MSB discarded in hardware)
-- Address indicates which background sprite is currently targeted in reading order
- e.g. $\textrm{addr} = c000_{\textrm{hex}} + x + y*w$ where $x$ and $y$ are the background tile, and $w$ is the amount of horizontal tiles fit on the background layer (40)
+- Address indicates which background sprite is currently targeted in reading
+ order
+ e.g. $\textrm{addr} = c000_{\textrm{hex}} + x + y*w$ where $x$ and $y$
+ are the background tile, and $w$ is the amount of horizontal tiles fit on the
+ background layer (40)
Word format:
|Range (VHDL)|Description|
|-|-|
-|`15`|Flip horizontally|
-|`14`|Flip vertically|
-|`13`|(unused)|
+|`15`|(discarded)|
+|`14`|Flip horizontally|
+|`13`|Flip vertically|
|`12 downto 10`|Palette index for tile|
|`9 downto 0`|Tilemap index|
diff --git a/style.md b/style.md
index 7e6b9b6..3fe912b 100644
--- a/style.md
+++ b/style.md
@@ -30,12 +30,14 @@ before formatting as a failsafe.
- custom typedefs are prefixed with `hh_` and suffixed with `_t` (e.g. `hh_bam_tile_t`)
- library hooks that need specific symbol names are exempt from the naming
conventions (e.g. `main` or `HAL_UART_MspInit`)
+- names are always in English
## others
- document **how to use** code using doxygen-style comments in headers
- document **what code is doing** using inline comments (where applicable)
- don't write redundant comments (e.g. `int c = a + b; // add a and b`)
+- comments are always in English
### markdown
@@ -50,4 +52,31 @@ before formatting as a failsafe.
- use lower case keywords
- testbench name is the component name with `_tb` as suffix
- vhdl filename is the same as the component name
-
+- vhdl files should end in the `.vhd` file extension, not `.vhdl`
+- use spaces around the colon used for setting the type of signal definitions
+- do not mix uppercase and lowercase in entity/component/port/signal names
+- remove the default vivado file headers (large comment blocks with file info)
+- add a comment to the entity definition describing what the entity does
+- library import names should be lowercase
+- with the exception of off by one shifts, absolutely NO magic numbers are
+ allowed. use existing constants if possible, or create new ones in a consts
+ file instead of using magic numbers.
+- entities/components without a generic map should put the opening `port (` on
+ the same line as the `entity ... is ` line
+- entities/components with a generic map should be formatted like this:
+ ```
+ entity ... is
+ generic map(
+ NAME : type := default;
+ NAME : type := default)
+ port map(
+ NAME : in type;
+ NAME : out type);
+ end ...;
+ ```
+- port/generic maps with only one signal should be condensed into a single line
+- all library declarations should be grouped together at the top of the file
+- all `use` directives should be grouped together below the library
+ declarations
+- use a space before and after the comment syntax, e.g. `variable x : integer
+ := 0; -- comment here`
diff --git a/test/sdl2/.gitignore b/test/sdl2/.gitignore
new file mode 100644
index 0000000..f0c9b81
--- /dev/null
+++ b/test/sdl2/.gitignore
@@ -0,0 +1,2 @@
+*.o
+main
diff --git a/test/sdl2/main.c b/test/sdl2/main.c
new file mode 100644
index 0000000..beddc3f
--- /dev/null
+++ b/test/sdl2/main.c
@@ -0,0 +1,48 @@
+#include <stdlib.h>
+#include <stdbool.h>
+#include <math.h>
+#include <SDL2/SDL.h>
+
+#define UPSCALE_FACTOR 3
+#define WIN_HEIGHT 240
+#define WIN_WIDTH 320
+#define FPS 60
+
+static void pixel(SDL_Renderer* r, unsigned px, unsigned py, unsigned cr, unsigned cg, unsigned cb) {
+ SDL_SetRenderDrawColor(r, cr, cg, cb, 255);
+ SDL_RenderFillRect(r, &(SDL_Rect) {.x = px * UPSCALE_FACTOR, .y = py * UPSCALE_FACTOR, .w = UPSCALE_FACTOR, .h = UPSCALE_FACTOR});
+}
+
+int main() {
+ SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
+ SDL_Window *window = SDL_CreateWindow("sdl2 test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIN_WIDTH * UPSCALE_FACTOR, WIN_HEIGHT * UPSCALE_FACTOR, SDL_WINDOW_SHOWN);
+ SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
+
+ SDL_Event e;
+ unsigned long frame = 0;
+ while (true) {
+ uint32_t start = SDL_GetTicks();
+ frame++;
+ while (SDL_PollEvent(&e)) if (e.type == SDL_QUIT) exit(0);
+
+ SDL_RenderClear(renderer);
+
+ for (unsigned i = 0; i < WIN_WIDTH; i++) {
+ for (unsigned j = 0; j < WIN_HEIGHT; j++) {
+ pixel(renderer, i, j, (unsigned)(sqrt(i * 20) + frame) % 255, (i * j + frame) % 255, (j * 20) % 255);
+ }
+ }
+
+ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
+ SDL_RenderPresent(renderer);
+
+ uint32_t end = SDL_GetTicks();
+ int wait_for = 1000 / FPS - (end - start);
+ if (wait_for > 0) SDL_Delay(wait_for);
+ }
+
+ SDL_DestroyRenderer(renderer);
+ SDL_DestroyWindow(window);
+ SDL_Quit();
+ return 0;
+}
diff --git a/test/sdl2/makefile b/test/sdl2/makefile
new file mode 100644
index 0000000..4c26dc3
--- /dev/null
+++ b/test/sdl2/makefile
@@ -0,0 +1,18 @@
+CC = gcc
+LD = gcc
+RM = rm -f
+TARGET = main
+LFLAGS += -lSDL2 -lm
+OBJS += main.o
+
+all: $(TARGET)
+
+%.o: %.c
+ $(CC) -c $(CFLAGS) $< -o $@
+
+$(TARGET): $(OBJS)
+ $(LD) $^ $(LFLAGS) -o $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
diff --git a/test/sdl2/readme.md b/test/sdl2/readme.md
new file mode 100644
index 0000000..940e315
--- /dev/null
+++ b/test/sdl2/readme.md
@@ -0,0 +1,6 @@
+# sdl2 test
+
+this is a simple POC that makes a window to draw PPU graphics to using SDL2.
+this code will be moved to the stm32/ppu directory, and the stm32's makefile
+will be edited to include a sim target for building the game for desktop
+debugging.