aboutsummaryrefslogtreecommitdiff
path: root/basys3/basys3.srcs/ppu.vhd
blob: 445ae149f73d006935cedfcdf2e283c75ad3d8c7 (plain)
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
330
331
332
333
334
335
336
337
338
339
340
library ieee;
library work;

use ieee.std_logic_1164.all;
use work.ppu_consts.all;
use work.ppu_pceg_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, HBLANK : out std_logic; -- vblank and hblank for synchronization
	RESOUT : out std_logic); -- reset out
end ppu;

architecture Behavioral of ppu is
	component ppu_pceg port(
		CLK : in std_logic; -- system clock
		RESET : in std_logic; -- async reset
		SPRITE_BG : out ppu_sprite_bg_pl_state := PL_BG_IDLE; -- sprite info fetch + sprite pixel fetch
		SPRITE_FG : out ppu_sprite_fg_pl_state := PL_FG_IDLE; -- 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;
		RESOUT : out std_logic);
	end component;
	component ppu_sprite_bg port( -- background sprite
		-- inputs
		CLK : in std_logic; -- system clock
		RESET : in std_logic; -- reset clock counter
		PL_STAGE : in ppu_sprite_bg_pl_state; -- pipeline stage
		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_STAGE : in ppu_sprite_fg_pl_state; -- pipeline stage
			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
		OE : in std_logic; -- global output enable (screen active)
		FG_HIT : in std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0); -- foreground hit array
		BG_EN : out std_logic; -- background enable output
		FG_EN : out std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0)); -- foreground enable output
	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
		ACTIVE : out std_logic); -- screen currently active (currently same for tiny/native, TODO: offset tiny for first scanline)
	end component;

	-- signals
	signal SYSCLK, SYSRST : std_logic; -- system clock and reset
	signal PL_DONE, PL_READY : std_logic; -- pipeline stages
	signal PL_SPRITE_BG : ppu_sprite_bg_pl_state;
	signal PL_SPRITE_FG : ppu_sprite_fg_pl_state;
	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 NVSYNC, NHSYNC, THBLANK, TVBLANK : std_logic;
	signal ACTIVE : std_logic;
	signal PCEG_RESET : std_logic;
	signal FG_FETCH, FG_FETCH_S, FG_FETCH_R : std_logic := '0';
begin
	SYSCLK <= CLK100;
	SYSRST <= RESET;

	VSYNC <= NVSYNC;
	HSYNC <= NHSYNC;

	PCEG_RESET <= SYSRST or (not ACTIVE);
	VBLANK <= TVBLANK;
	HBLANK <= THBLANK;

	pipeline_clock_edge_generator : component ppu_pceg port map(
		CLK => SYSCLK,
		RESET => PCEG_RESET,
		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_S,
		RESOUT => RESOUT);

	-- set/reset TODO: reset on scanline 1 (buffer VBLANK to check edge)
	process(SYSCLK)
	begin
		if SYSRST = '1' then
			FG_FETCH <= '0';
		elsif rising_edge(SYSCLK) then
			if FG_FETCH_S = '1' then
				FG_FETCH <= '1';
			elsif FG_FETCH_R = '1' then
				FG_FETCH <= '0';
			end if;
		end if;
	end process;

	background_sprite : component ppu_sprite_bg port map(
		CLK => SYSCLK,
		RESET => SYSRST,
		PL_STAGE => PL_SPRITE_BG,
		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_STAGE => PL_SPRITE_FG,
				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
		OE => ACTIVE,
		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,
		ACTIVE => ACTIVE);
end Behavioral;