aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/architecture.md208
-rw-r--r--docs/ppu.drawio2
-rw-r--r--docs/research.md209
3 files changed, 214 insertions, 205 deletions
diff --git a/docs/architecture.md b/docs/architecture.md
new file mode 100644
index 0000000..eb20cce
--- /dev/null
+++ b/docs/architecture.md
@@ -0,0 +1,208 @@
+# General system architecture
+
+<!-- TODO: top-level system architecture + context diagram -->
+
+## PPU
+
+Here's a list of features our PPU has:
+
+- 320x240 @ 60Hz VGA output (upscaled to 640x480)
+- single tilemap with room for 1024 tiles of 16x16 pixels
+- 8 colors per palette, with 4096 possible colors (12-bit color depth)
+- 640x480 background canvas with scrolling
+- NO background scrolling splits
+- 128 total sprites on screen (NO scanline sprite limit)
+- sprites are always drawn on top of the background layer
+- PPU control using DMA (dual-port asynchronous RAM)
+- tiles can be flipped using FAM or BAM
+- no frame buffer
+- vertical and horizontal sync and blank output
+
+Notable differences:
+
+- NES nametable equivalent is called BAM (background attribute register)
+- NES OAM equivalent is called FAM (foreground attribute register)
+- 320x240 @ 60Hz output
+
+ Since we're using VGA, we can't use custom resolutions without an
+ upscaler/downscaler. This resolution was chosen because it's exactly half of
+ the lowest standard VGA resolution 640x480.
+- No scanline sprite limit
+
+ Unless not imposing any sprite limit makes the hardware implementation
+ impossible, or much more difficult, this is a restriction that will likely
+ lead to frustrating debugging sessions, so will not be replicated in our
+ custom PPU.
+- Sprites are 16x16
+
+ Most NES games already tile multiple 8x8 tiles together into "metatiles" to
+ create the illusion of larger sprites. This was likely done to save on memory
+ costs as RAM was expensive in the '80s, but since we're running on an FPGA
+ cost is irrelevant.
+- Single 1024 sprite tilemap shared between foreground and background sprites
+
+ The NES OAM registers contain a bit to select which tilemap to use (of two),
+ which effectively expands each tile's index address by one byte. Instead of
+ creating the illusion of two separate memory areas for tiles, having one
+ large tilemap seems like a more sensible solution to indexed tiles.
+- 8 total palettes, with 8 colors each
+
+ More colors is better. Increasing the total palette count is a very memory
+ intensive operation, while increaing the palette color count is likely slower
+ when looking up color values for each pixel on real hardware.
+- Sprites can be positioned paritally off-screen on all screen edges using only
+ the offset bits in the FAM register
+
+ The NES has a separate PPUMASK register to control special color effects, and
+ to shift sprites off the left and top screen edges, as the sprite offsets
+ count from 0. Our PPU's FAM sprite offset bits count from -15, so the sprite
+ can shift past the top and left screen edges, as well as the standard bottom
+ and right edges.
+- No status line register, only V-sync and H-sync outputs are supplied back to
+ CPU
+
+ The NES status line register contains some handy lines, such as a buggy
+ status line for reaching the max sprite count per scanline, and a status line
+ for detecting collisions between background and foreground sprites. Our PPU
+ doesn't have a scanline limit, and all hitbox detection is done in software.
+ Software hacks involving swapping tiles during a screen draw cycle can still
+ be achieved by counting the V-sync and H-sync pulses using interrupts.
+- No background scrolling splits
+
+ This feature allows only part of the background canvas to be scrolled, while
+ another portion stays still. This was used to draw HUD elements on the
+ background layer for displaying things like health bars or score counters.
+ Since we are working with a higher foreground sprite limit, we'll use regular
+ foreground sprites to display HUD elements.
+- Sprites are always drawn on top of the background layer
+
+ Our game doesn't need this capability for any visual effects. Leaving this
+ feature out will lead to a simpler hardware design
+
+### Hardware design schematics
+
+#### Top (level 1)
+
+![PPU top-level design](../assets/ppu-level-1.svg)
+
+Important notes:
+
+- The STM32 can reset the PPU. This line will also be connected to a physical
+ button on the FPGA.
+- The STM32 uses direct memory access to control the PPU.
+- The PPU's native resolution is 320x240. It works in this resolution as if it
+ is a valid VGA signal. The STM32 is also only aware of this resolution. This
+ resolution is referred to as "tiny" resolution. Because VGA-compatible LCD's
+ likely don't support this resolution due to low clock speed, a built-in
+ pixel-perfect 2X upscaler is chained after the PPU's "tiny" output. This
+ means that the display sees the resolution as 640x480, but the PPU and STM32
+ only work in 320x240.
+- The STM32 receives the TVSYNC and THSYNC lines from the PPU. These are the
+ VSYNC and HSYNC lines from the tiny VGA signal generator. These lines can be
+ used to trigger interrupts for counting frames, and to make sure no
+ read/write conflicts occur for protected memory regions in the PPU.
+- NVSYNC, NHSYNC and the RGB signals refer to the output of the native VGA
+ signal generator.
+
+#### Level 2
+
+![PPU level 2 design (data flows from top to bottom)](../assets/ppu-level-2.svg)
+
+Important notes:
+
+- The pixel fetch logic is pipelined in 5 stages:
+ 1. - (Foreground sprite info) calculate if foreground sprite exists at
+ current pixel using FAM register
+ - (Background sprite info) get background sprite info from BAM register
+ 2. - (Sprite render) calculate pixel to read from TMM based on sprite info
+ 3. - (Compositor) get pixel with 'highest' priority (pick first foreground
+ sprite with non-transparent color at current pixel in order, fallback to
+ background)
+ - (Palette lookup) lookup palette color using palette register
+ - (VGA signal generator) output real color to VGA signal generator
+- The pipeline stages with two clock cycles contain an address set and memory
+ read step.
+- The pipeline takes 5 clock ticks in total. About 18 are available during each
+ pixel. For optimal display compatibility, the output color signal should be
+ stable before 50% of the pixel clock pulse width (9 clock ticks).
+- Since the "sprite info" and "sprite render" steps are fundamentally different
+ for the foreground and background layer, these components will be combined
+ into one for each layer respectively. They are separated in the above diagram
+ for pipeline stage illustration.
+- The BAX, FAM, and PAL registers are implemented in the component that
+ directly accesses them, but are exposed to the PPU RAM bus for writing.
+- Each foreground sprite render component holds its own sprite data copy from
+ the RAM in it's own cache memory. The cache updates are fetched during the
+ VBLANK time between each frame.
+
+#### Level 3
+
+This diagram has several flaws, but a significant amount of time has already
+been spent on these, so they are highlighted here instead of being fixed.
+
+![PPU level 3 design](../assets/ppu-level-3.svg)
+
+Flaws:
+
+- Pipeline stages 1-4 aren't properly connected in this diagram, see level 2
+ notes for proper functionality
+- The global RESET input resets all PPU RAM, but isn't connected to all RAM
+ ports
+- All DATA inputs on the same line as an ADDR output are connections to a
+ memory component. Not all of these are connected in the diagram, though they
+ should be.
+- All ADDR and ADDR drivers are also tri-state. EN inputs need to be added to
+ support switching the output on/off.
+
+Important notes:
+
+- The background sprite and foreground sprite component internally share some
+ components for coordinate transformations
+- The foreground sprite component is only shown once here, but is cloned for
+ each foreground sprite the PPU allows.
+- The CIDX lines between the sprite and compositor components is shared by all
+ sprite components, and is such tri-state. A single sprite component outputs a
+ CIDX signal based on the \*EN signal from the compositor.
+- All DATA and ADDR lines are shared between all RAM ports. WEN inputs are
+ controlled by the address decoder.
+
+### Registers
+
+|Address|Size (bytes)|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`|BAX |[background auxiliary memory][BAX]|
+
+[TMM]: #tilemap-memory
+#### Tilemap memory
+
+- TODO: list format
+
+[BAM]: #background-attribute-memory
+#### Background attribute memory
+
+- TODO: list format
+
+[FAM]: #foreground-attribute-memory
+#### Foreground attribute memory
+
+- TODO: list format
+
+[PAL]: #palettes
+#### Palettes
+
+- TODO: list format
+
+[BAX]: #background-auxiliary-memory
+#### Background auxiliary memory
+
+- background scrolling
+
+[nesppuspecs]: https://www.copetti.org/writings/consoles/nes/
+[nesppudocs]: https://www.nesdev.org/wiki/PPU_programmer_reference
+[nesppupinout]: https://www.nesdev.org/wiki/PPU_pinout
+[custompputimings]: https://docs.google.com/spreadsheets/d/1MU6K4c4PtMR_JXIpc3I0ZJdLZNnoFO7G2P3olCz6LSc
+
diff --git a/docs/ppu.drawio b/docs/ppu.drawio
index a9e20e8..18970be 100644
--- a/docs/ppu.drawio
+++ b/docs/ppu.drawio
@@ -1 +1 @@
-<mxfile host="Electron" modified="2023-02-10T13:07:29.750Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/20.8.16 Chrome/106.0.5249.199 Electron/21.4.0 Safari/537.36" etag="bE51zr50qo9CGr6ui7xq" version="20.8.16" type="device" pages="3"><diagram name="level-1" id="DBE48RpXtx8JnoEt3ekA">7Vtbd6o4FP41rjXz0C6uXh7x0nbOaR2X2ovnZRZKRHoi8UCsen79BEjkEgoyCtY19qGLbDaB7O/b3w4J1uTOcnvv6KvFEzIArEmCsa3J3ZokNYUm+e8ZdoFBaamBwXQsIzCJoWFk/QbUKFDr2jKAG3PECEFsreLGGbJtMMMxm+44aBN3myMYv+tKNwFnGM10yFtfLQMv6LBUIbQ/AMtcsDuLAj2z1JkzNbgL3UCbiEnu1eSOgxAOjpbbDoBe7FhcguvuPjm7fzAH2PiQC9TdXxi/4fFaHG8mEPXrPeHhhvbyocM1HXBbd3euTGx3g3utJtUh6bw9dciR6R0NBs90OHjHYuSgtW0A7zYC8dgsLAxGK33mnd0QUhDbAi8haYnk0CVnLNt8BHPvqW9UIbQNaRxjRtor1KcADpBrYQvZxDYjowYOOfEBHGwRvB4TDlOEMVoSBx1aZuoVGj2BkfeIfDhZbIg72EZMNLz3AC0BdnbEhZ2tU6gp12Xa3ITEERkbFlHSMEedktXcdx3iSQ4opAXglTh4u5a7grr30GiNV2vMI/zHy7325xXjTzCWpVyMm2VB/AHg6mn9t7j73h3eLUF/OjC+3YhNDitgEAmjTeTgBTKRrcNeaG3H0Qx9HpEXJx/Dd4DxjuqxvsYojjCwmVZLWXF10dqZgQyCylTSdccEOGOUrcDPG1kmSg6AOrY+4uKdFnP/Us1x/FRgDitk2diN9DzwDJEEV+PgK2pccQu5k4Pg/iH2+4EcRIfvj9rLcjKZvH3vtayt9nuK6q8soEewwf0J8Gzx36mxr3N+ZwRZrHl1mBhsZANmu7O8cfk+J2VPalCkr8EeOU4HUcihT7b/0fzJCmmkYozGT7J0J7TEYYcvFp3rdODzUtG6bTXyi8WtIFc5JWD1IoIwLxm2wXJ2BnXXtWZx0OII5wsMgFO/MxbqCAzQB94TjZV3/RyCLb11m0VJYZrBJuD1oAAlmpF65HvTtlxJfRLlAyVmG7t/hAhqyqyB2YrpEi8kYmLO0kiQKwgDvSpDkaTk5KeV6CiIE9dRYaVUEsqXV2iz/eNKGV7NHgfN5y7AtWRmFVPTVFooXK69jCb9DpdwRFFwQhexg36CDoLICSvnnBTNhCmRR0kxW1qG4adgmgwn6n5MYBMaLHBSnTnvO0YkFV4kP9HIsqbUKofawxW1HNTqZ0dNbPF1rOoXIbC18Bsri+R4EjnubqONXZHJb+p463xpyloDOPPkN/ne3MhW9Gz3o6e+qQGVlPMQiDz1njNewyfNrcqaIW/81m7fqoB4DfHhCQrvz78eNen1R/3Xc+PHPwfPiZQvQTypVeidPemeXFRNuh/jfeL1gHRSs9Xn/4kqNi9LFROv+M0cVcx0L4lA6lUVD1FF5UBVVL8G8YQ4k9Qc4gnJpahMd/EY73oFpKZTxcgMf9gb9cYc07/8DD+2NnbaOX5yN0tJWaAobTKftij1JXc10l9FhMuqQsnslguqgVxByor8VvXLUHu60brd4QXmrfedQEWJm77nXFrmHr8Ddc7MlS47c3PebnL8S8pcfk/Jz9yuNtaumZuZudXWXJFD45IyN2UCfkmZKxXMXKmKzOVXwnmKXPcKi+4Vigcy8Cx7hbJwK0T/6nHaCWrq6aI7iWLjqNuUtc+Yk4Q5/pXsM2Z94JKssK+9/rXAZhZYoaQCC/uwa74rgrs2Fs328tt8ODRTPrbl0DFJoFaHD3T/3bM+ZT0ImQHYf5yY9W5Q3reKAheB+xII6gRDKY2h6WTMhDyfofG1soq2THndaF/xiOFR6buzyH8vUsbaxkXicWoAUhfxiyn0wXKcIuYHK3QjhYBSWQSsc+Pvl/XB0nlImIl69STM2sKMglDW90cXCULJlTL16fhCeXZhSFsbOoUwZH3zERn/+PKFIQWmXJpKZ6xO/OxgfPnCcEIQTisMpBn+ODN4UQ9/4Sr3/gU=</diagram><diagram id="MdQQJuunD4ehtDpxKxax" name="level-2">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</diagram><diagram id="lj51MlK96fuJFRP7cw84" name="level-3">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</diagram></mxfile> \ No newline at end of file
+<mxfile host="Electron" modified="2023-02-14T13:06:31.768Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/20.8.16 Chrome/106.0.5249.199 Electron/21.4.0 Safari/537.36" etag="X-qfzIqKq46fJAfw7kxz" version="20.8.16" type="device" pages="3"><diagram name="level-1" id="DBE48RpXtx8JnoEt3ekA">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</diagram><diagram id="MdQQJuunD4ehtDpxKxax" name="level-2">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</diagram><diagram id="lj51MlK96fuJFRP7cw84" name="level-3">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</diagram></mxfile> \ No newline at end of file
diff --git a/docs/research.md b/docs/research.md
index 73618d7..a3e86ff 100644
--- a/docs/research.md
+++ b/docs/research.md
@@ -33,6 +33,11 @@ this chip's features and limitations:
- tiles can be flipped using OAM
- no frame buffer
+Though this chip is documented very well from a programmer's perspective, we
+found very little documentation about any reverse-engineering of the chip's
+actual hardware. While our PPU provides mostly the same features as the NES's
+PPU, our design is entirely custom.
+
### Usage
The NES PPU has a lot of capabilities, so here's a quick run-down of how the
@@ -93,210 +98,6 @@ int main() {
setup();
while(1) loop();
}
-```
-
-## Custom PPU
-
-Here's a list of features our PPU should have:
-<!-- TODO: expand list with PPU spreadsheet -->
-
-- 320x240 @ 60Hz VGA output
-- single tilemap with room for 1024 tiles of 16x16 pixels
-- 8 colors per palette, with 4096 possible colors (12-bit color depth)
-- 512x448 background canvas with scrolling
-- NO background scrolling splits
-- 128 total sprites on screen (NO scanline sprite limit)
-- sprites are always drawn on top of the background layer
-- PPU control using DMA (dual-port asynchronous RAM)
-- tiles can be flipped using FAM or BAM
-- no frame buffer
-- vertical and horizontal sync output
-
-Notable differences:
-
-- NES nametable equivalent is called BAM (background attribute register)
-- NES OAM equivalent is called FAM (foreground attribute register)
-- 320x240 @ 60Hz output
-
- Since we're using VGA, we can't use custom resolutions without an
- upscaler/downscaler. This resolution was chosen because it's exactly half of
- the lowest standard VGA resolution 640x480.
-- No scanline sprite limit
-
- Unless not imposing any sprite limit makes the hardware implementation
- impossible, or much more difficult, this is a restriction that will likely
- lead to frustrating debugging sessions, so will not be replicated in our
- custom PPU.
-- Sprites are 16x16
-
- Most NES games already tile multiple 8x8 tiles together into "metatiles" to
- create the illusion of larger sprites. This was likely done to save on memory
- costs as RAM was expensive in the '80s, but since we're running on an FPGA
- cost is irrelevant.
-- Single 1024 sprite tilemap shared between foreground and background sprites
-
- The NES OAM registers contain a bit to select which tilemap to use (of two),
- which effectively expands each tile's index address by one byte. Instead of
- creating the illusion of two separate memory areas for tiles, having one
- large tilemap seems like a more sensible solution to indexed tiles.
-- 8 total palettes, with 8 colors each
-
- More colors is better. Increasing the total palette count is a very memory
- intensive operation, while increaing the palette color count is likely slower
- when looking up color values for each pixel on real hardware.
-- Sprites can be positioned paritally off-screen on all screen edges using only
- the offset bits in the FAM register
-
- The NES has a separate PPUMASK register to control special color effects, and
- to shift sprites off the left and top screen edges, as the sprite offsets
- count from 0. Our PPU's FAM sprite offset bits count from -15, so the sprite
- can shift past the top and left screen edges, as well as the standard bottom
- and right edges.
-- No status line register, only V-sync and H-sync outputs are supplied back to
- CPU
-
- The NES status line register contains some handy lines, such as a buggy
- status line for reaching the max sprite count per scanline, and a status line
- for detecting collisions between background and foreground sprites. Our PPU
- doesn't have a scanline limit, and all hitbox detection is done in software.
- Software hacks involving swapping tiles during a screen draw cycle can still
- be achieved by counting the V-sync and H-sync pulses using interrupts.
-- No background scrolling splits
-
- This feature allows only part of the background canvas to be scrolled, while
- another portion stays still. This was used to draw HUD elements on the
- background layer for displaying things like health bars or score counters.
- Since we are working with a higher foreground sprite limit, we'll use regular
- foreground sprites to display HUD elements.
-- Sprites are always drawn on top of the background layer
-
- Our game doesn't need this capability for any visual effects. Leaving this
- feature out will lead to a simpler hardware design
-
-### Hardware design schematics
-
-#### Top (level 1)
-
-![PPU top-level design](../assets/ppu-level-1.svg)
-
-Important notes:
-
-- The STM32 can reset the PPU. This line will also be connected to a physical
- button on the FPGA.
-- The STM32 uses direct memory access to control the PPU.
-- The PPU's native resolution is 320x240. It works in this resolution as if it
- is a valid VGA signal. The STM32 is also only aware of this resolution. This
- resolution is referred to as "tiny" resolution. Because VGA-compatible LCD's
- likely don't support this resolution due to low clock speed, a built-in
- pixel-perfect 2X upscaler is chained after the PPU's "tiny" output. This
- means that the display sees the resolution as 640x480, but the PPU and STM32
- only work in 320x240.
-- The STM32 receives the TVSYNC and THSYNC lines from the PPU. These are the
- VSYNC and HSYNC lines from the tiny VGA signal generator. These lines can be
- used to trigger interrupts for counting frames, and to make sure no
- read/write conflicts occur for protected memory regions in the PPU.
-- NVSYNC, NHSYNC and the RGB signals refer to the output of the native VGA
- signal generator.
-
-#### Level 2
-
-![PPU level 2 design (data flows from top to bottom)](../assets/ppu-level-2.svg)
-
-Important notes:
-
-- The pixel fetch logic is pipelined in 5 stages:
- 1. - (Foreground sprite info) calculate if foreground sprite exists at
- current pixel using FAM register
- - (Background sprite info) get background sprite info from BAM register
- 2. - (Sprite render) calculate pixel to read from TMM based on sprite info
- 3. - (Compositor) get pixel with 'highest' priority (pick first foreground
- sprite with non-transparent color at current pixel in order, fallback to
- background)
- - (Palette lookup) lookup palette color using palette register
- - (VGA signal generator) output real color to VGA signal generator
-- The pipeline stages with two clock cycles contain an address set and memory
- read step.
-- The pipeline takes 5 clock ticks in total. About 18 are available during each
- pixel. For optimal display compatibility, the output color signal should be
- stable before 50% of the pixel clock pulse width (9 clock ticks).
-- Since the "sprite info" and "sprite render" steps are fundamentally different
- for the foreground and background layer, these components will be combined
- into one for each layer respectively. They are separated in the above diagram
- for pipeline stage illustration.
-- The BAX, FAM, and PAL registers are implemented in the component that
- directly accesses them, but are exposed to the PPU RAM bus for writing.
-- Each foreground sprite render component holds its own sprite data copy from
- the RAM in it's own cache memory. The cache updates are fetched during the
- VBLANK time between each frame.
-
-#### Level 3
-
-This diagram has several flaws, but a significant amount of time has already
-been spent on these, so they are highlighted here instead of being fixed.
-
-![PPU level 3 design](../assets/ppu-level-3.svg)
-
-Flaws:
-
-- Pipeline stages 1-4 aren't properly connected in this diagram, see level 2
- notes for proper functionality
-- The global RESET input resets all PPU RAM, but isn't connected to all RAM
- ports
-- All DATA inputs on the same line as an ADDR output are connections to a
- memory component. Not all of these are connected in the diagram, though they
- should be.
-
-Important notes:
-
-- The background sprite and foreground sprite component internally share some
- components for coordinate transformations
-- The foreground sprite component is only shown once here, but is cloned for
- each foreground sprite the PPU allows.
-- The CIDX lines between the sprite and compositor components is shared by all
- sprite components, and is such tri-state. A single sprite component outputs a
- CIDX signal based on the \*EN signal from the compositor.
-- All DATA and ADDR lines are shared between all RAM ports. WEN inputs are
- controlled by the address decoder.
-
-### Registers
-
-|Address|Size (bytes)|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`|BAX |[background auxiliary memory][BAX]|
-
-[TMM]: #tilemap-memory
-#### Tilemap memory
-
-- TODO: list format
-
-[BAM]: #background-attribute-memory
-#### Background attribute memory
-
-- TODO: list format
-
-[FAM]: #foreground-attribute-memory
-#### Foreground attribute memory
-
-- TODO: list format
-
-[PAL]: #palettes
-#### Palettes
-
-- TODO: list format
-
-[BAX]: #background-auxiliary-memory
-#### Background auxiliary memory
-
-- background scrolling
-
-[nesppuspecs]: https://www.copetti.org/writings/consoles/nes/
-[nesppudocs]: https://www.nesdev.org/wiki/PPU_programmer_reference
-[nesppupinout]: https://www.nesdev.org/wiki/PPU_pinout
-[custompputimings]: https://docs.google.com/spreadsheets/d/1MU6K4c4PtMR_JXIpc3I0ZJdLZNnoFO7G2P3olCz6LSc
# Generating audio signals