From c395be5d4b20858f5eb183d84c4edaf7b5571060 Mon Sep 17 00:00:00 2001 From: lonkaars Date: Sun, 19 Feb 2023 14:15:40 +0100 Subject: add list of abbreviations to docs folder --- docs/abbreviations.md | 20 ++++++++++++ docs/architecture.md | 85 +++++++++++++++++++++++++++++++++++++++------------ docs/gen/doc.m4 | 4 ++- docs/makefile | 8 +++-- 4 files changed, 93 insertions(+), 24 deletions(-) create mode 100644 docs/abbreviations.md diff --git a/docs/abbreviations.md b/docs/abbreviations.md new file mode 100644 index 0000000..76bd046 --- /dev/null +++ b/docs/abbreviations.md @@ -0,0 +1,20 @@ +# List of abbreviations + +- MSB (most significant bit) +- LSB (least significant bit) +- DMA (direct memory access) +- LUT (lookup table) +- RAM (random access memory) +- PPU (picture processing unit) + - NES (Nintendo entertainment system) + - OAM (object attribute memory) + - TMM (tilemap memory) + - BAM (background attribute memory) + - FAM (foreground attribute memory) + - PAL (palette memory) + - AUX (auxiliary memory) + - HUD (heads-up display) +- APU (audio processing unit) + - PWM (pulse width modulation) + - ADSR (attack, decay, sustain, release) +- CPU (central processing unit) diff --git a/docs/architecture.md b/docs/architecture.md index 6b37604..a60e0da 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -224,51 +224,96 @@ Important notes: ## Registers -|Address|Size (bytes)|Alias|Description| +- 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. + +|Address offset|Size (16-bit words)|Alias|Description| |-|-|-|-| -|`0x00000`|`0x00000`|TMM |[tilemap memory][TMM]| -|`0x00000`|`0x00000`|BAM |[background attribute memory][BAM]| -|`0x00000`|`0x00000`|FAM |[foreground attribute memory][FAM]| -|`0x00000`|`0x00000`|PAL |[palettes][PAL]| -|`0x00000`|`0x00000`|AUX |[auxiliary memory][AUX]| +|`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]| + +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 +in the address ranges between the memory regions. This is considered undefined +behavior, so the CPU should not attempt to write in these locations because +there is no address validity checking. [TMM]: #tilemap-memory ### Tilemap memory -- TODO: list format +- 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. [BAM]: #background-attribute-memory ### Background attribute memory -- 15-bit words (padded with 1 bit) -- 11-bit address width (1200 15-bit words with padding 1) +- 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) + +Word format: -- TODO: list format +|Range (VHDL)|Description| +|-|-| +|`15`|Flip horizontally| +|`14`|Flip vertically| +|`13`|(unused)| +|`12 downto 10`|Palette index for tile| +|`9 downto 0`|Tilemap index| [FAM]: #foreground-attribute-memory ### Foreground attribute memory -- 2 * 16-bit words -- 8-bit address width (256 16-bit words for 128 * 32-bit) +- 32-bit words +- Sprites with lower addresses are drawn "before" sprites with higher addresses + +Word format: -- TODO: list format +|Range (VHDL)|Description| +|-|-| +|`31`|Flip horizontally| +|`30`|Flip vertically| +|`29 downto 21`|horizontal position (offset by -16)| +|`20 downto 13`|vertical position (offset by -16)| +|`12 downto 10`|Palette index for tile| +|`9 downto 0`|Tilemap index| [PAL]: #palettes ### Palettes -- 12-bit words -- 6-bit address width (2^6 = 64 colors total) +- 12-bit words (4 MSB discarded in hardware) +- Address formula for palette color is $p_i*8 + p_c$ where $p_i$ is the palette + index and $p_c$ is the color index within a given palette. -- TODO: list format +Word format: + +|Range (VHDL)|Description| +|-|-| +|`15 downto 13`|(discarded)| +|`12 downto 8`|Red value| +|`7 downto 4`|Green value| +|`3 downto 0`|Blue value| [AUX]: #auxiliary-memory ### Auxiliary memory -- background scrolling (8 + 9 bits) -- fetch foreground sprites bit (1 bit) +- no words + +Format: -- 16-bit words -- 1-bit address width +|Range (VHDL)|Description| +|-|-| +|`31 downto 18`|(unused)| +|`17`|Fetch foreground sprites flag| +|`16 downto 8`|Horizontal background scroll (offset from left edge)| +|`7 downto 0`|Vertical background scroll (offset from top edge)| [custompputimings]: https://docs.google.com/spreadsheets/d/1MU6K4c4PtMR_JXIpc3I0ZJdLZNnoFO7G2P3olCz6LSc diff --git a/docs/gen/doc.m4 b/docs/gen/doc.m4 index b98eb64..c9cbcf5 100644 --- a/docs/gen/doc.m4 +++ b/docs/gen/doc.m4 @@ -6,7 +6,8 @@ define(`docname', NAME, `gameplay', `Game design document', NAME, `architecture', `Architecture document', `UNKNOWN???'))dnl - +define(`abbreviations',`undivert(`abbreviations.con')')dnl +dnl @@ -43,6 +44,7 @@ define(`docname', Niels Stunnebrink (2184532) + ifelse(NAME, `architecture', `abbreviations', `')

Table of contents

undivert(NAME`.toc') undivert(NAME`.con') diff --git a/docs/makefile b/docs/makefile index e4fcb15..a729024 100644 --- a/docs/makefile +++ b/docs/makefile @@ -5,7 +5,9 @@ CHROME = chromium --headless --run-all-compositor-stages-before-draw --virtual-t CURL = curl PUP = pup -SRCS = $(wildcard *.md) +SRCS += gameplay.md \ + research.md \ + architecture.md HTML_T = $(SRCS:.md=.html) PDF_T = $(SRCS:.md=.pdf) @@ -22,8 +24,8 @@ gen/paged.polyfill.js: %.toc: %.md $(PANDOC) $< -s --toc --to=html 2> /dev/null | $(PUP) '#TOC' | sed -r 's/<(.?)ul>/<\1ol>/g' > $@ -%.html: %.con %.toc gen/doc.m4 gen/paged.polyfill.js gen/style.css - $(M4) -Igen -DNAME=$(basename $<) gen/doc.m4 > $@ +%.html: %.con %.toc gen/doc.m4 gen/paged.polyfill.js gen/style.css abbreviations.con + $(M4) -Igen -I. -DNAME=$(basename $<) gen/doc.m4 > $@ %.pdf: %.html $(CHROME) --print-to-pdf=$@ $< 2> /dev/null -- cgit v1.2.3 From a5155f3346d962d2e79f82b1e89f16398b79f52a Mon Sep 17 00:00:00 2001 From: lonkaars Date: Sun, 19 Feb 2023 14:16:11 +0100 Subject: separate constants file for vhdl code --- basys3/basys3.srcs/ppu.vhd | 40 +++++++++++++++++---------------------- basys3/basys3.srcs/ppu_consts.vhd | 22 +++++++++++++++++++++ 2 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 basys3/basys3.srcs/ppu_consts.vhd diff --git a/basys3/basys3.srcs/ppu.vhd b/basys3/basys3.srcs/ppu.vhd index efe2125..28134c6 100644 --- a/basys3/basys3.srcs/ppu.vhd +++ b/basys3/basys3.srcs/ppu.vhd @@ -1,38 +1,23 @@ library ieee; +library work; + use ieee.std_logic_1164.all; --use ieee.numeric_std.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(15 downto 0); -- PPU VRAM ADDR - DATA: in std_logic_vector(15 downto 0); - R,G,B: out std_logic_vector(3 downto 0); + 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 - 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 - component ppu_pceg port( -- pipeline clock edge generator CLK: in std_logic; -- system clock RESET: in std_logic; -- async reset @@ -48,7 +33,7 @@ architecture Behavioral of ppu is 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(15 downto 0); -- address in + 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); @@ -150,7 +135,7 @@ architecture Behavioral of ppu is 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(3 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 @@ -203,6 +188,15 @@ begin SYSCLK <= CLK100; SYSRST <= RESET; + -- internal unused lines + -- + -- these lines would be used if components use memory blocks as RAM blocks + -- (like how TMM and BAM work), the registers of these memory regions are + -- directly exposed internally, and are as such not used as RAM blocks + AUX_AI <= (others => '0'); + FAM_AI <= (others => '0'); + PAL_AI <= (others => '0'); + pipeline_clock_edge_generator: component ppu_pceg port map( CLK => SYSCLK, RESET => SYSRST, diff --git a/basys3/basys3.srcs/ppu_consts.vhd b/basys3/basys3.srcs/ppu_consts.vhd new file mode 100644 index 0000000..d3c8403 --- /dev/null +++ b/basys3/basys3.srcs/ppu_consts.vhd @@ -0,0 +1,22 @@ +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; + -- cgit v1.2.3 From 20c2f0c57fa0269a1465fb8f331b591425c21879 Mon Sep 17 00:00:00 2001 From: lonkaars Date: Sun, 19 Feb 2023 14:16:21 +0100 Subject: finish ppu address decoder --- basys3/basys3.srcs/ppu_addr_dec.vhdl | 51 +++++++++++++++++++ basys3/basys3.srcs/ppu_addr_dec_tb.vhdl | 86 +++++++++++++++++++++++++++++++++ basys3/basys3.xpr | 23 ++++++++- 3 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 basys3/basys3.srcs/ppu_addr_dec.vhdl create mode 100644 basys3/basys3.srcs/ppu_addr_dec_tb.vhdl diff --git a/basys3/basys3.srcs/ppu_addr_dec.vhdl b/basys3/basys3.srcs/ppu_addr_dec.vhdl new file mode 100644 index 0000000..28c22fc --- /dev/null +++ b/basys3/basys3.srcs/ppu_addr_dec.vhdl @@ -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) = '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.vhdl b/basys3/basys3.srcs/ppu_addr_dec_tb.vhdl new file mode 100644 index 0000000..5c7119d --- /dev/null +++ b/basys3/basys3.srcs/ppu_addr_dec_tb.vhdl @@ -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.xpr b/basys3/basys3.xpr index 1a6d509..5df2675 100644 --- a/basys3/basys3.xpr +++ b/basys3/basys3.xpr @@ -59,7 +59,7 @@