1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
|
library ieee;
use ieee.std_logic_1164.all;
--use ieee.numeric_std.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);
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_PIPELINE_STAGE_COUNT: natural := 5; -- amount of pipeline clock edges to generate
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
R: in std_logic; -- async reset
S: out std_logic_vector(PPU_PIPELINE_STAGE_COUNT-1 downto 0)); -- pipeline stages
end component;
component ppu_addr_dec port( -- pipeline clock edge generator
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(15 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);
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);
end component;
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;
component ppu_sprite_bg port( -- background sprite
-- inputs
CLK: in std_logic; -- system clock
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;
component ppu_sprite_fg port( -- foreground sprite
-- inputs
CLK: in std_logic; -- system clock
RESET: in std_logic; -- reset internal memory
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)
-- 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;
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));
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;
-- 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(3 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;
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
PREADY: out std_logic; -- current pixel ready (pixel color is stable)
VSYNC, VBLANK,
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;
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;
-- signals
signal SYSCLK, SYSRST: std_logic; -- system clock and reset
signal PL_S: std_logic_vector(PPU_PIPELINE_STAGE_COUNT-1 downto 0); -- 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 TR,TG,TB: std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- tiny RGB out (to be buffered)
signal PREADY: std_logic; -- current pixel color stable
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
SYSCLK <= CLK100;
SYSRST <= RESET;
pipeline_clock_edge_generator: component ppu_pceg port map(
CLK => SYSCLK,
R => SYSRST,
S => PL_S);
address_decoder: component ppu_addr_dec port map(
EN => EN,
WEN => 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,
TMM_WEN => TMM_WEN,
BAM_WEN => BAM_WEN,
FAM_WEN => FAM_WEN,
PAL_WEN => PAL_WEN,
AUX_WEN => AUX_WEN);
background_attribute_memory: component ppu_bam port map(
clka => SYSCLK,
rsta => SYSRST,
wea => (others => BAM_WEN),
addra => BAM_AO,
dina => DATA(PPU_BAM_DATA_WIDTH-1 downto 0),
douta => BAM_DO,
rsta_busy => open);
tilemap_memory: component ppu_tmm port map(
clka => SYSCLK,
rsta => SYSRST,
wea => (others => TMM_WEN),
addra => TMM_AO,
dina => DATA(PPU_TMM_DATA_WIDTH-1 downto 0),
douta => TMM_DO,
rsta_busy => open);
aux: component ppu_aux port map(
CLK => SYSCLK,
RESET => SYSRST,
AUX_WEN => AUX_WEN,
AUX_ADDR => AUX_AO,
AUX_DATA => DATA(PPU_AUX_DATA_WIDTH-1 downto 0),
BG_SHIFT_X => BG_SHIFT_X,
BG_SHIFT_Y => BG_SHIFT_Y,
FG_FETCH => FG_FETCH);
background_sprite: component ppu_sprite_bg port map(
CLK => SYSCLK,
OE => BG_EN,
X => X,
Y => Y,
BG_SHIFT_X => BG_SHIFT_X,
BG_SHIFT_Y => BG_SHIFT_Y,
BAM_ADDR => BAM_AI,
BAM_DATA => BAM_DO,
TMM_ADDR => TMM_AI,
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 => SYSCLK,
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));
end generate;
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
CLK => SYSCLK,
CIDX => CIDX,
RESET => SYSRST,
PAL_WEN => PAL_WEN,
PAL_ADDR => PAL_AO,
PAL_DATA => DATA(PPU_PAL_DATA_WIDTH-1 downto 0),
R => TR,
G => TG,
B => TB);
tiny_vga_signal_generator: component ppu_vga_tiny port map( -- tiny vga signal generator
CLK => SYSCLK,
RESET => SYSRST,
X => X,
Y => Y,
PREADY => PREADY,
VSYNC => TVSYNC,
VBLANK => TVBLANK,
HSYNC => THSYNC,
HBLANK => THBLANK);
native_vga_signal_generator: component ppu_vga_native port map( -- native vga signal generator (upscaler)
CLK => SYSCLK,
RESET => SYSRST,
X => X,
Y => Y,
PREADY => PREADY,
RI => TR,
GI => TG,
BI => TB,
RO => R,
GO => G,
BO => B,
VSYNC => NVSYNC,
HSYNC => NHSYNC);
end Behavioral;
|