aboutsummaryrefslogtreecommitdiff
path: root/basys3/basys3.srcs/ppu_dispctl.vhd
blob: 725a6f294dce196d8faf4b4b133ca4ece928f7c8 (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
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use work.ppu_consts.all;

entity ppu_dispctl is port(
	CLK : 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 ppu_dispctl;

architecture Behavioral of ppu_dispctl is
	component ppu_dispctl_slbuf port( -- scanline buffer
		clka : in std_logic;
		wea : in std_logic_vector(0 to 0);
		addra : in std_logic_vector(PPU_DISPCTL_SLBUF_ADDR_WIDTH-1 downto 0);
		dina : in std_logic_vector(PPU_RGB_COLOR_OUTPUT_DEPTH-1 downto 0);
		clkb : in std_logic;
		rstb : in std_logic;
		addrb : in std_logic_vector(PPU_DISPCTL_SLBUF_ADDR_WIDTH-1 downto 0);
		doutb : out std_logic_vector(PPU_RGB_COLOR_OUTPUT_DEPTH-1 downto 0);
		rsta_busy : out std_logic;
		rstb_busy : out std_logic);
	end component;
	signal CLK25 : unsigned(1 downto 0) := (others => '0'); -- clock divider (100_000_000/4)
	signal PCOUNT, NHCOUNT, NVCOUNT, THCOUNT, TVCOUNT: unsigned(PPU_VGA_SIGNAL_PIXEL_WIDTH-1 downto 0) := (others => '0');
	signal ADDR_I, ADDR_O : std_logic_vector(PPU_DISPCTL_SLBUF_ADDR_WIDTH-1 downto 0);
	signal DATA_I, DATA_O : std_logic_vector(PPU_RGB_COLOR_OUTPUT_DEPTH-1 downto 0);
	signal T_POS_X : unsigned(PPU_SCREEN_T_POS_X_WIDTH-1 downto 0) := (others => '0'); -- real tiny x position
	signal T_POS_Y : unsigned(PPU_SCREEN_T_POS_Y_WIDTH-1 downto 0) := (others => '0'); -- real tiny y position
	signal U_POS_X : unsigned(PPU_SCREEN_T_POS_X_WIDTH-1 downto 0) := (others => '0'); -- upscaled tiny x position
	signal U_POS_Y : unsigned(PPU_SCREEN_T_POS_Y_WIDTH-1 downto 0) := (others => '0'); -- upscaled tiny y position
	signal N_POS_X : unsigned(PPU_SCREEN_N_POS_X_WIDTH-1 downto 0) := (others => '0'); -- native x position
	signal N_POS_Y : unsigned(PPU_SCREEN_N_POS_Y_WIDTH-1 downto 0) := (others => '0'); -- native y position
	signal NACTIVE, NHACTIVE, NVACTIVE : std_logic := '0';
	signal TACTIVE, THACTIVE, TVACTIVE : std_logic := '0';
begin
	NHCOUNT <= PCOUNT mod PPU_VGA_H_TOTAL;
	NVCOUNT <= (PCOUNT / PPU_VGA_H_TOTAL) mod PPU_VGA_V_TOTAL;

	THCOUNT <= (PCOUNT / 4) mod (PPU_VGA_H_TOTAL / 2);
	TVCOUNT <= (PCOUNT / 4) / (PPU_VGA_H_TOTAL / 2) mod (PPU_VGA_V_TOTAL / 2);

	NHACTIVE <= '1' when
		(NHCOUNT >= (PPU_VGA_H_PORCH_BACK)) and
		(NHCOUNT <  (PPU_VGA_H_PORCH_BACK + PPU_VGA_H_ACTIVE)) else '0';
	NVACTIVE <= '1' when
		(NVCOUNT >= (PPU_VGA_V_PORCH_BACK)) and
		(NVCOUNT <  (PPU_VGA_V_PORCH_BACK + PPU_VGA_V_ACTIVE)) else '0';
	NACTIVE <= NHACTIVE and NVACTIVE;
	
	NVSYNC <= '1' when
		(NVCOUNT >= (PPU_VGA_V_PORCH_BACK + PPU_VGA_V_ACTIVE)) and
		(NVCOUNT <  (PPU_VGA_V_PORCH_BACK + PPU_VGA_V_ACTIVE + PPU_VGA_V_SYNC)) else '0';
	NHSYNC <= '1' when NVACTIVE = '1' and
		(NHCOUNT >= (PPU_VGA_H_PORCH_BACK + PPU_VGA_H_ACTIVE)) and
		(NHCOUNT <  (PPU_VGA_H_PORCH_BACK + PPU_VGA_H_ACTIVE + PPU_VGA_H_SYNC)) else '0';
	
	N_POS_X <= resize(NHCOUNT - PPU_VGA_H_PORCH_BACK, N_POS_X'length) when NHACTIVE = '1' else (others => '0');
	N_POS_Y <= resize(NVCOUNT - PPU_VGA_V_PORCH_BACK, N_POS_Y'length) when NVACTIVE = '1' else (others => '0');

	T_POS_X <= resize(THCOUNT - (PPU_VGA_H_PORCH_BACK / 4), T_POS_X'length) when N_POS_Y(0) = '0' else
	           resize(THCOUNT - ((PPU_VGA_H_PORCH_BACK + PPU_VGA_H_BLANK) / 4), T_POS_X'length); -- divide tiny x equally over two native scanlines
	T_POS_Y <= resize(TVCOUNT - (PPU_VGA_V_PORCH_BACK / 2), T_POS_Y'length);

	DATA_I <= RI & GI & BI;
	ADDR_I <= std_logic_vector(resize(T_POS_X, ADDR_I'length)) when T_POS_Y(0) = '0' else std_logic_vector(resize(T_POS_X, ADDR_I'length) + PPU_SCREEN_WIDTH);

	X <= std_logic_vector(T_POS_X) when NACTIVE = '1' else (others => '0');
	Y <= std_logic_vector(T_POS_Y) when NACTIVE = '1' else (others => '0');

	U_POS_X <= resize(N_POS_X / 2, U_POS_X'length);
	U_POS_Y <= resize(N_POS_Y / 2, U_POS_Y'length);

	ADDR_O <= std_logic_vector(resize(U_POS_X, ADDR_I'length)) when U_POS_Y(0) = '0' else std_logic_vector(resize(U_POS_X, ADDR_I'length) + PPU_SCREEN_WIDTH);
	RO <= DATA_O(11 downto 8);
	GO <= DATA_O(7 downto 4);
	BO <= DATA_O(3 downto 0);

	scanline_buffer : component ppu_dispctl_slbuf port map(
		clka => CLK,
		wea => (others => PREADY),
		addra => ADDR_I,
		dina => DATA_I,
		clkb => CLK,
		rstb => RESET,
		addrb => ADDR_O,
		doutb => DATA_O,
		rsta_busy => open,
		rstb_busy => open);

	process(CLK, RESET)
	begin
		if RESET = '1' then
			CLK25 <= (others => '0');
		elsif rising_edge(CLK) then
			CLK25 <= CLK25 + 1;
		end if;
	end process;

	process(CLK25(1), RESET)
	begin
		if RESET = '1' then
			PCOUNT <= (others => '0');
		elsif rising_edge(CLK25(1)) then
			if PCOUNT + 1 >= PPU_VGA_SIGNAL_PIXEL_IDX_MAX then
				PCOUNT <= (others => '0');
			else
				PCOUNT <= PCOUNT + 1;
			end if;
		end if;
	end process;
end Behavioral;