aboutsummaryrefslogtreecommitdiff
path: root/basys3/basys3.srcs/ppu_dispctl.vhd
diff options
context:
space:
mode:
Diffstat (limited to 'basys3/basys3.srcs/ppu_dispctl.vhd')
-rw-r--r--basys3/basys3.srcs/ppu_dispctl.vhd166
1 files changed, 166 insertions, 0 deletions
diff --git a/basys3/basys3.srcs/ppu_dispctl.vhd b/basys3/basys3.srcs/ppu_dispctl.vhd
new file mode 100644
index 0000000..117b780
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_dispctl.vhd
@@ -0,0 +1,166 @@
+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(
+ 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 ppu_dispctl;
+
+architecture Behavioral of ppu_dispctl is
+ component ppu_dispctl_pixclk is port (
+ clk_out1 : out std_logic;
+ clk_out2 : out std_logic;
+ reset : in std_logic;
+ clk_in1 : in std_logic);
+ end component;
+ 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 NPIXCLK, TPIXCLK : std_logic;
+ signal NHCOUNT, NVCOUNT : 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
+ -- scanline buffer data in
+ 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);
+
+ T_POS_Y <= U_POS_Y;
+ -- tiny VCOUNT and HCOUNT
+ process(TPIXCLK, RESET)
+ variable TMP_T_POS_X : unsigned(PPU_SCREEN_T_POS_X_WIDTH-1 downto 0) := (others => '0');
+ variable TMP_THBLANK, TMP_TVBLANK : std_logic := '0';
+ begin
+ if RESET = '1' then
+ TMP_THBLANK := '0'; -- TODO
+ TMP_TVBLANK := '0'; -- TODO
+ elsif rising_edge(TPIXCLK) then
+ T_POS_X <= TMP_T_POS_X;
+
+ THBLANK <= TMP_THBLANK;
+ TVBLANK <= TMP_TVBLANK;
+
+ if NACTIVE = '1' then
+ TMP_T_POS_X := TMP_T_POS_X + 1;
+ if TMP_T_POS_X >= PPU_SCREEN_WIDTH then
+ TMP_T_POS_X := (others => '0');
+ end if;
+ end if;
+ end if;
+ end process;
+
+ 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(N_POS_X'length-1 downto 1), U_POS_X'length);
+ U_POS_Y <= resize(N_POS_Y(N_POS_Y'length-1 downto 1), U_POS_Y'length);
+
+ -- scanline buffer data out
+ ADDR_O <= std_logic_vector(resize(U_POS_X, ADDR_I'length)) when U_POS_Y(0) = '1' 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');
+
+ -- native (+upscaled) VCOUNT and HCOUNT
+ process(NPIXCLK, RESET)
+ variable TMP_NHCOUNT, TMP_NVCOUNT : unsigned(PPU_VGA_SIGNAL_PIXEL_WIDTH-1 downto 0) := (others => '0');
+ variable TMP_NHACTIVE, TMP_NVACTIVE : std_logic := '0';
+ variable TMP_NHSYNC, TMP_NVSYNC : std_logic := '0';
+ begin
+ if RESET = '1' then
+ TMP_NHCOUNT := (others => '0');
+ TMP_NVCOUNT := (others => '0');
+ TMP_NHACTIVE := '0';
+ TMP_NVACTIVE := '0';
+ TMP_NVSYNC := '0';
+ TMP_NHSYNC := '0';
+ elsif rising_edge(NPIXCLK) then
+ -- sync write (needs to be here to happen on rising edge)
+ NVCOUNT <= TMP_NVCOUNT;
+ NHCOUNT <= TMP_NHCOUNT;
+ NHACTIVE <= TMP_NHACTIVE;
+ NVACTIVE <= TMP_NVACTIVE;
+ NACTIVE <= TMP_NHACTIVE and TMP_NVACTIVE;
+ NVSYNC <= TMP_NVSYNC;
+ NHSYNC <= TMP_NHSYNC;
+ N_POS_X <= resize(TMP_NHCOUNT - PPU_VGA_H_PORCH_BACK, N_POS_X'length) when TMP_NHACTIVE = '1' else (others => '0');
+ N_POS_Y <= resize(TMP_NVCOUNT - PPU_VGA_V_PORCH_BACK, N_POS_Y'length) when TMP_NVACTIVE = '1' else (others => '0');
+
+ -- horizontal count (pixel)
+ TMP_NHCOUNT := TMP_NHCOUNT + 1;
+ if TMP_NHCOUNT >= PPU_VGA_H_TOTAL then
+ TMP_NHCOUNT := (others => '0');
+
+ -- vertical count (scanline)
+ TMP_NVCOUNT := TMP_NVCOUNT + 1;
+ if TMP_NVCOUNT >= PPU_VGA_V_TOTAL then
+ TMP_NVCOUNT := (others => '0');
+ end if;
+
+ -- vertical display area (active)
+ if TMP_NVCOUNT = PPU_VGA_V_PORCH_BACK then TMP_NVACTIVE := '1'; end if;
+ if TMP_NVCOUNT = PPU_VGA_V_PORCH_BACK + PPU_VGA_V_ACTIVE then TMP_NVACTIVE := '0'; end if;
+
+ -- vertical sync period
+ if TMP_NVCOUNT = PPU_VGA_V_PORCH_BACK + PPU_VGA_V_ACTIVE then TMP_NVSYNC := '1'; end if;
+ if TMP_NVCOUNT = PPU_VGA_V_PORCH_BACK + PPU_VGA_V_ACTIVE + PPU_VGA_V_SYNC then TMP_NVSYNC := '0'; end if;
+ end if;
+
+ -- horizontal display area (active)
+ if TMP_NHCOUNT = PPU_VGA_H_PORCH_BACK then TMP_NHACTIVE := '1'; end if;
+ if TMP_NHCOUNT = PPU_VGA_H_PORCH_BACK + PPU_VGA_H_ACTIVE then TMP_NHACTIVE := '0'; end if;
+
+ -- horizontal sync period
+ if TMP_NHCOUNT = PPU_VGA_H_PORCH_BACK + PPU_VGA_H_ACTIVE then TMP_NHSYNC := '1'; end if;
+ if TMP_NHCOUNT = PPU_VGA_H_PORCH_BACK + PPU_VGA_H_ACTIVE + PPU_VGA_H_SYNC then TMP_NHSYNC := '0'; end if;
+ end if;
+ end process;
+
+ scanline_buffer : component ppu_dispctl_slbuf port map(
+ clka => SYSCLK,
+ wea => (others => PREADY),
+ addra => ADDR_I,
+ dina => DATA_I,
+ clkb => SYSCLK,
+ rstb => RESET,
+ addrb => ADDR_O,
+ doutb => DATA_O,
+ rsta_busy => open,
+ rstb_busy => open);
+
+ pixel_clock: component ppu_dispctl_pixclk port map(
+ clk_in1 => SYSCLK,
+ reset => RESET,
+ clk_out1 => NPIXCLK,
+ clk_out2 => TPIXCLK);
+end Behavioral;