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
|
library ieee;
library work;
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
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);
VSYNC, HSYNC : out std_logic; -- VGA sync out
VBLANK : out std_logic); -- vblank for synchronization
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_BG : out std_logic; -- sprite info fetch + sprite pixel fetch
SPRITE_FG : out std_logic; -- sprite pixel fetch
DONE : out std_logic; -- last pipeline stage done
READY : out std_logic); -- rgb buffer propagation ready
end component;
component ppu_addr_dec port( -- address decoder
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_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
BAM_ADDR : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
FAM_ADDR : out std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0);
PAL_ADDR : out std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0);
AUX_ADDR : out std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0));
end component;
component ppu_bam port( -- BAM block memory
clka : in std_logic;
wea : in std_logic_vector(0 to 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);
clkb : in std_logic;
rstb : in std_logic;
addrb : in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
doutb : out std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
rsta_busy : out std_logic;
rstb_busy : out std_logic);
end component;
component ppu_tmm port( -- TMM block memory
clka : in std_logic;
wea : in std_logic_vector(0 to 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);
clkb : in std_logic;
rstb : in std_logic;
addrb : in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
doutb : out std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
rsta_busy : out std_logic;
rstb_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; -- pipeline clock
RESET : in std_logic; -- reset clock counter
PL_RESET : in std_logic; -- reset pipeline 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
-- 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 -- foreground sprite
generic (
IDX : natural := 0);
port(
-- inputs
CLK : in std_logic; -- system clock
RESET : in std_logic; -- reset internal memory and clock counters
PL_CLK : in std_logic; -- pipeline clock
PL_RESET : in std_logic; -- reset pipeline 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;
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(PPU_COLOR_OUTPUT_DEPTH-1 downto 0)); -- VGA color out
end component;
component ppu_dispctl port(
SYSCLK : in std_logic; -- system clock
RESET : in std_logic;
X : out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- tiny screen pixel x
Y : out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- tiny screen pixel y
RI,GI,BI : in std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- color in
PREADY : in std_logic; -- current pixel ready (pixel color is stable)
RO,GO,BO : out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color out
NVSYNC, NHSYNC : out std_logic; -- VGA sync out
THBLANK, TVBLANK : out std_logic); -- tiny sync signals
end component;
-- signals
signal SYSCLK, SYSRST : std_logic; -- system clock and reset
signal PL_SPRITE_FG, PL_SPRITE_BG, PL_DONE, PL_READY : std_logic; -- pipeline stages
signal TMM_WEN, BAM_WEN, FAM_WEN, PAL_WEN, AUX_WEN : std_logic;
signal TMM_W_ADDR, TMM_R_ADDR : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); -- read/write TMM addr (dual port)
signal BAM_W_ADDR, BAM_R_ADDR : std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); -- read/write BAM addr (dual port)
signal TMM_R_DATA : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); -- internal read TMM data
signal BAM_R_DATA : std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0); -- internal read BAM data
signal FAM_W_ADDR : std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); -- write only FAM addr
signal PAL_W_ADDR : std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); -- write only PAL addr
signal AUX_W_ADDR : std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); -- write only AUX addr
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); -- palette lookup output RGB
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 NVSYNC, NHSYNC, THBLANK, TVBLANK : std_logic;
signal PCEG_RESET : std_logic;
begin
SYSCLK <= CLK100;
SYSRST <= RESET;
VSYNC <= NVSYNC;
HSYNC <= NHSYNC;
PCEG_RESET <= SYSRST or THBLANK;
VBLANK <= TVBLANK;
pipeline_clock_edge_generator : component ppu_pceg port map(
CLK => SYSCLK,
RESET => SYSRST,
SPRITE_FG => PL_SPRITE_FG,
SPRITE_BG => PL_SPRITE_BG,
DONE => PL_DONE,
READY => PL_READY);
address_decoder : component ppu_addr_dec port map(
WEN => WEN,
ADDR => ADDR,
TMM_ADDR => TMM_W_ADDR,
BAM_ADDR => BAM_W_ADDR,
FAM_ADDR => FAM_W_ADDR,
PAL_ADDR => PAL_W_ADDR,
AUX_ADDR => AUX_W_ADDR,
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,
wea => (others => BAM_WEN),
addra => BAM_W_ADDR,
dina => DATA(PPU_BAM_DATA_WIDTH-1 downto 0),
clkb => SYSCLK,
rstb => SYSRST,
addrb => BAM_R_ADDR,
doutb => BAM_R_DATA,
rsta_busy => open,
rstb_busy => open);
tilemap_memory : component ppu_tmm port map(
clka => SYSCLK,
wea => (others => TMM_WEN),
addra => TMM_W_ADDR,
dina => DATA(PPU_TMM_DATA_WIDTH-1 downto 0),
clkb => SYSCLK,
rstb => SYSRST,
addrb => TMM_R_ADDR,
doutb => TMM_R_DATA,
rsta_busy => open,
rstb_busy => open);
aux : component ppu_aux port map(
CLK => SYSCLK,
RESET => SYSRST,
AUX_WEN => AUX_WEN,
AUX_ADDR => AUX_W_ADDR,
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 => PL_SPRITE_BG,
RESET => SYSRST,
PL_RESET => PL_READY,
OE => BG_EN,
X => X,
Y => Y,
BG_SHIFT_X => BG_SHIFT_X,
BG_SHIFT_Y => BG_SHIFT_Y,
BAM_ADDR => BAM_R_ADDR,
BAM_DATA => BAM_R_DATA,
TMM_ADDR => TMM_R_ADDR,
TMM_DATA => TMM_R_DATA,
CIDX => CIDX);
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,
PL_CLK => PL_SPRITE_FG,
PL_RESET => PL_READY,
OE => FG_EN(FG_IDX),
X => X,
Y => Y,
FETCH => FG_FETCH,
VBLANK => TVBLANK,
FAM_WEN => FAM_WEN,
FAM_ADDR => FAM_W_ADDR,
FAM_DATA => DATA(PPU_FAM_DATA_WIDTH-1 downto 0),
TMM_ADDR => TMM_R_ADDR,
TMM_DATA => TMM_R_DATA,
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_W_ADDR,
PAL_DATA => DATA(PPU_PAL_DATA_WIDTH-1 downto 0),
R => UR,
G => UG,
B => UB);
display_controller : component ppu_dispctl port map(
SYSCLK => SYSCLK,
RESET => SYSRST,
PREADY => PL_READY,
X => X,
Y => Y,
RI => UR,
GI => UG,
BI => UB,
RO => R,
GO => G,
BO => B,
NVSYNC => NVSYNC,
NHSYNC => NHSYNC,
TVBLANK => TVBLANK,
THBLANK => THBLANK);
end Behavioral;
|