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) when NACTIVE = '1' else (others => '0');
GO <= DATA_O(7 downto 4) when NACTIVE = '1' else (others => '0');
BO <= DATA_O(3 downto 0) when NACTIVE = '1' else (others => '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;
|