aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlonkaars <loek@pipeframe.xyz>2023-02-20 15:31:12 +0100
committerlonkaars <loek@pipeframe.xyz>2023-02-20 15:31:12 +0100
commit29108f38c5f885412b0a75ae46b85720946cfdb5 (patch)
tree6b78d25a98898db4a92d659d84ba1840ee6de0ee
parent77870488404563df2c18e4ec4719e767fff98919 (diff)
update TMM data width + update memory map (WIP ppu_sprite_bg)
-rw-r--r--basys3/basys3.srcs/ppu.vhd4
-rw-r--r--basys3/basys3.srcs/ppu_addr_dec.vhd10
-rw-r--r--basys3/basys3.srcs/ppu_consts.vhd36
-rw-r--r--basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci38
-rw-r--r--basys3/basys3.xpr34
-rw-r--r--docs/architecture.md53
6 files changed, 104 insertions, 71 deletions
diff --git a/basys3/basys3.srcs/ppu.vhd b/basys3/basys3.srcs/ppu.vhd
index acf546f..c798400 100644
--- a/basys3/basys3.srcs/ppu.vhd
+++ b/basys3/basys3.srcs/ppu.vhd
@@ -79,7 +79,7 @@ architecture Behavioral of ppu is
end component;
component ppu_sprite_bg port( -- background sprite
-- inputs
- CLK : in std_logic; -- system clock
+ 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
@@ -100,7 +100,7 @@ architecture Behavioral of ppu is
end component;
component ppu_sprite_fg port( -- foreground sprite
-- inputs
- CLK : in std_logic; -- system clock
+ CLK : in std_logic; -- pipeline 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
diff --git a/basys3/basys3.srcs/ppu_addr_dec.vhd b/basys3/basys3.srcs/ppu_addr_dec.vhd
index df83964..e0c374f 100644
--- a/basys3/basys3.srcs/ppu_addr_dec.vhd
+++ b/basys3/basys3.srcs/ppu_addr_dec.vhd
@@ -43,9 +43,9 @@ begin
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';
+ 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_consts.vhd b/basys3/basys3.srcs/ppu_consts.vhd
index ab2ccf1..f4d428e 100644
--- a/basys3/basys3.srcs/ppu_consts.vhd
+++ b/basys3/basys3.srcs/ppu_consts.vhd
@@ -1,22 +1,32 @@
+-- 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 := 1;
- constant PPU_AUX_DATA_WIDTH : natural := 16;
+ 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; -- amount of bits for horizontal screen offset
constant PPU_POS_V_WIDTH : natural := 8; -- amount of 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_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; -- amount of tiles (horizontally) on background canvas
+ constant PPU_BG_CANVAS_TILES_V : natural := 30; -- amount of tiles (vertically) on background canvas
+ constant PPU_TILE_INDEX_WIDTH : natural := 10; -- amount of bits needed to index a tile from TMM memory
+ constant PPU_PIXELS_PER_TILE_WORD : natural := 5; -- amount of pixels defined in one word in TMM memory
+ constant PPU_TILE_SIZE : natural := 48;
end package ppu_consts;
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 24d2b58..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" } ],
@@ -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 90e7823..8dcc698 100644
--- a/basys3/basys3.xpr
+++ b/basys3/basys3.xpr
@@ -61,20 +61,20 @@
<Option Name="IPStaticSourceDir" Val="$PIPUSERFILESDIR/ipstatic"/>
<Option Name="EnableBDX" Val="FALSE"/>
<Option Name="DSABoardId" Val="basys3"/>
- <Option Name="WTXSimLaunchSim" Val="21"/>
+ <Option Name="WTXSimLaunchSim" Val="27"/>
<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"/>
@@ -148,9 +148,15 @@
<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>
<Config>
<Option Name="DesignMode" Val="RTL"/>
- <Option Name="TopModule" Val="ppu"/>
+ <Option Name="TopModule" Val="ppu_sprite_bg"/>
<Option Name="dataflowViewerSettings" Val="min_width=16"/>
</Config>
</FileSet>
@@ -163,7 +169,7 @@
<FileSet Name="sim_1" Type="SimulationSrcs" RelSrcDir="$PSRCDIR/sim_1" RelGenDir="$PGENDIR/sim_1">
<Filter Type="Srcs"/>
<File Path="$PSRCDIR/ppu_addr_dec_tb.vhd">
- <FileInfo SFType="VHDL2008">
+ <FileInfo>
<Attr Name="UsedIn" Val="synthesis"/>
<Attr Name="UsedIn" Val="simulation"/>
</FileInfo>
@@ -201,7 +207,7 @@
</File>
<Config>
<Option Name="DesignMode" Val="RTL"/>
- <Option Name="TopModule" Val="ppu_aux_tb"/>
+ <Option Name="TopModule" Val="ppu_addr_dec_tb"/>
<Option Name="TopLib" Val="xil_defaultlib"/>
<Option Name="TransportPathDelay" Val="0"/>
<Option Name="TransportIntDelay" Val="0"/>
@@ -270,9 +276,7 @@
<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">
<Strategy Version="1" Minor="2">
- <StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2022">
- <Desc>Vivado Synthesis Defaults</Desc>
- </StratHandle>
+ <StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2022"/>
<Step Id="synth_design"/>
</Strategy>
<ReportStrategy Name="Vivado Synthesis Default Reports" Flow="Vivado Synthesis 2022"/>
@@ -301,9 +305,7 @@
</Run>
<Run Id="impl_1" Type="Ft2:EntireDesign" Part="xc7a35tcpg236-1" ConstrsSet="constrs_1" Description="Default settings for Implementation." AutoIncrementalCheckpoint="false" WriteIncrSynthDcp="false" State="current" SynthRun="synth_1" IncludeInArchive="true" IsChild="false" GenFullBitstream="true" AutoIncrementalDir="$PSRCDIR/utils_1/imports/impl_1" AutoRQSDir="$PSRCDIR/utils_1/imports/impl_1">
<Strategy Version="1" Minor="2">
- <StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2022">
- <Desc>Default settings for Implementation.</Desc>
- </StratHandle>
+ <StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2022"/>
<Step Id="init_design"/>
<Step Id="opt_design"/>
<Step Id="power_opt_design"/>
diff --git a/docs/architecture.md b/docs/architecture.md
index a60e0da..56bbb62 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -227,16 +227,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 +248,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|