aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlonkaars <loek@pipeframe.xyz>2023-03-08 17:31:53 +0100
committerlonkaars <loek@pipeframe.xyz>2023-03-08 17:31:53 +0100
commitd35688314dbd531437edda6c78674c24ef2e1207 (patch)
tree52babacec96c038e9df676e4f8988eefe52533ef
parent77090b9a6a9052848ccf233a8f8bee59418da0d8 (diff)
WIP dispctl
-rw-r--r--basys3/basys3.srcs/ppu_consts.vhd19
-rw-r--r--basys3/basys3.srcs/ppu_dispctl.vhd76
-rw-r--r--basys3/basys3.srcs/ppu_dispctl_tb.vhd63
-rw-r--r--basys3/basys3.xpr27
4 files changed, 169 insertions, 16 deletions
diff --git a/basys3/basys3.srcs/ppu_consts.vhd b/basys3/basys3.srcs/ppu_consts.vhd
index 48fab5a..c140b98 100644
--- a/basys3/basys3.srcs/ppu_consts.vhd
+++ b/basys3/basys3.srcs/ppu_consts.vhd
@@ -50,6 +50,25 @@ package ppu_consts is
constant PPU_TMM_CACHE_FETCH_A_WIDTH : natural := ceil_log2(PPU_TMM_CACHE_FETCH_A_COUNT);
constant PPU_ACCURATE_FG_SPRITE_COUNT : natural := 16;
constant PPU_PL_TOTAL_STAGES : natural := 14;
+ -- VGA signal timings (https://tomverbeure.github.io/video_timings_calculator)
+ constant PPU_VGA_H_ACTIVE : natural := PPU_NATIVE_SCREEN_WIDTH;
+ constant PPU_VGA_H_PORCH_FRONT : natural := 16;
+ constant PPU_VGA_H_SYNC : natural := 64;
+ constant PPU_VGA_H_PORCH_BACK : natural := 80;
+ constant PPU_VGA_H_BLANK : natural := PPU_VGA_H_PORCH_FRONT + PPU_VGA_H_SYNC + PPU_VGA_H_PORCH_BACK;
+ constant PPU_VGA_H_TOTAL : natural := PPU_VGA_H_BLANK + PPU_VGA_H_ACTIVE;
+ constant PPU_VGA_V_ACTIVE : natural := PPU_NATIVE_SCREEN_HEIGHT;
+ constant PPU_VGA_V_PORCH_FRONT : natural := 3;
+ constant PPU_VGA_V_SYNC : natural := 4;
+ constant PPU_VGA_V_PORCH_BACK : natural := 13;
+ constant PPU_VGA_V_BLANK : natural := PPU_VGA_V_PORCH_FRONT + PPU_VGA_V_SYNC + PPU_VGA_V_PORCH_BACK;
+ constant PPU_VGA_V_TOTAL : natural := PPU_VGA_V_BLANK + PPU_VGA_V_ACTIVE;
+ constant PPU_VGA_SIGNAL_PIXEL_IDX_MAX : natural := PPU_VGA_V_TOTAL * PPU_VGA_H_TOTAL; -- horizontal and vertical pixel clock index
+ constant PPU_VGA_SIGNAL_PIXEL_WIDTH : natural := ceil_log2(PPU_VGA_SIGNAL_PIXEL_IDX_MAX); -- bit width to count total horizontal and vertical pixel clock index
+ constant PPU_SCREEN_T_POS_X_WIDTH : natural := ceil_log2(PPU_SCREEN_WIDTH);
+ constant PPU_SCREEN_T_POS_Y_WIDTH : natural := ceil_log2(PPU_SCREEN_HEIGHT);
+ constant PPU_SCREEN_N_POS_X_WIDTH : natural := ceil_log2(PPU_NATIVE_SCREEN_WIDTH);
+ constant PPU_SCREEN_N_POS_Y_WIDTH : natural := ceil_log2(PPU_NATIVE_SCREEN_HEIGHT);
end package ppu_consts;
package body ppu_consts is
-- https://stackoverflow.com/questions/21783280/number-of-bits-to-represent-an-integer-in-vhdl
diff --git a/basys3/basys3.srcs/ppu_dispctl.vhd b/basys3/basys3.srcs/ppu_dispctl.vhd
index e1086b2..8f07f93 100644
--- a/basys3/basys3.srcs/ppu_dispctl.vhd
+++ b/basys3/basys3.srcs/ppu_dispctl.vhd
@@ -1,5 +1,6 @@
library ieee;
use ieee.std_logic_1164.all;
+use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use work.ppu_consts.all;
@@ -30,19 +31,82 @@ architecture Behavioral of ppu_dispctl is
rsta_busy : out std_logic;
rstb_busy : out std_logic);
end component;
- signal RGB_COLOR : std_logic_vector(PPU_RGB_COLOR_OUTPUT_DEPTH-1 downto 0);
+ signal CLK25 : unsigned(1 downto 0) := (others => '0'); -- clock divider (100_000_000/4)
+ signal PCOUNT, HCOUNT, VCOUNT : 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');
+ signal T_POS_Y : unsigned(PPU_SCREEN_T_POS_Y_WIDTH-1 downto 0) := (others => '0');
+ signal N_POS_X : unsigned(PPU_SCREEN_N_POS_X_WIDTH-1 downto 0) := (others => '0');
+ signal N_POS_Y : unsigned(PPU_SCREEN_N_POS_Y_WIDTH-1 downto 0) := (others => '0');
+ signal ACTIVE, HACTIVE, VACTIVE : std_logic := '0';
begin
- RGB_COLOR <= RI & GI & BI;
+ 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);
+ Y <= std_logic_vector(T_POS_Y);
+
+ RO <= DATA_O(11 downto 8);
+ GO <= DATA_O(7 downto 4);
+ BO <= DATA_O(3 downto 0);
+
+ HCOUNT <= PCOUNT mod PPU_VGA_H_TOTAL;
+ VCOUNT <= PCOUNT / PPU_VGA_H_TOTAL mod PPU_VGA_V_TOTAL;
+
+ HACTIVE <= '1' when
+ (HCOUNT > (PPU_VGA_H_PORCH_BACK)) and
+ (HCOUNT <= (PPU_VGA_H_PORCH_BACK + PPU_VGA_H_ACTIVE)) else '0';
+ VACTIVE <= '1' when
+ (VCOUNT > (PPU_VGA_V_PORCH_BACK)) and
+ (VCOUNT <= (PPU_VGA_V_PORCH_BACK + PPU_VGA_V_ACTIVE)) else '0';
+ ACTIVE <= HACTIVE and VACTIVE;
+
+ NVSYNC <= '1' when
+ (VCOUNT > (PPU_VGA_V_PORCH_BACK + PPU_VGA_V_ACTIVE)) and
+ (VCOUNT <= (PPU_VGA_V_PORCH_BACK + PPU_VGA_V_ACTIVE + PPU_VGA_V_SYNC)) else '0';
+ NHSYNC <= '1' when VACTIVE = '1' and
+ (HCOUNT > (PPU_VGA_H_PORCH_BACK + PPU_VGA_H_ACTIVE)) and
+ (HCOUNT <= (PPU_VGA_H_PORCH_BACK + PPU_VGA_H_ACTIVE + PPU_VGA_H_SYNC)) else '0';
+
+ N_POS_X <= resize(HCOUNT - PPU_VGA_H_PORCH_BACK, N_POS_X'length);
+ N_POS_Y <= resize(VCOUNT - PPU_VGA_V_PORCH_BACK, N_POS_Y'length);
+
+ T_POS_X <= resize(N_POS_X / 2, T_POS_X'length);
+ T_POS_Y <= resize(N_POS_Y / 2, T_POS_Y'length);
scanline_buffer : component ppu_dispctl_slbuf port map(
clka => CLK,
wea => (others => PREADY),
- addra => (others => '0'),
- dina => RGB_COLOR,
+ addra => ADDR_I,
+ dina => DATA_I,
clkb => CLK,
rstb => RESET,
- addrb => (others => '0'),
- doutb => open,
+ 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)
+ variable V_PCOUNT : unsigned(PPU_VGA_SIGNAL_PIXEL_WIDTH-1 downto 0) := (others => '0');
+ begin
+ PCOUNT <= V_PCOUNT;
+ if RESET = '1' then
+ V_PCOUNT := (others => '0');
+ elsif rising_edge(CLK25(1)) then
+ V_PCOUNT := V_PCOUNT + 1;
+ if V_PCOUNT = PPU_VGA_SIGNAL_PIXEL_IDX_MAX then
+ V_PCOUNT := (others => '0');
+ end if;
+ end if;
+ end process;
end Behavioral;
diff --git a/basys3/basys3.srcs/ppu_dispctl_tb.vhd b/basys3/basys3.srcs/ppu_dispctl_tb.vhd
new file mode 100644
index 0000000..fa11655
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_dispctl_tb.vhd
@@ -0,0 +1,63 @@
+library ieee;
+library unisim;
+
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use unisim.vcomponents.all;
+use work.ppu_consts.all;
+
+entity ppu_dispctl_tb is
+end ppu_dispctl_tb;
+
+architecture behavioral of ppu_dispctl_tb is
+ component ppu_dispctl 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 component;
+ signal CLK : std_logic := '0';
+ signal RESET : std_logic := '0';
+ signal X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0) := (others => '0');
+ signal Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0) := (others => '0');
+ signal RI,GI,BI : std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0) := (others => '0');
+ signal PREADY : std_logic := '0';
+ signal RO,GO,BO : std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0) := (others => '0');
+ signal NVSYNC, NHSYNC : std_logic := '0';
+ signal THBLANK, TVBLANK : std_logic := '0';
+begin
+ uut : component ppu_dispctl port map(
+ CLK => CLK,
+ RESET => RESET,
+ PREADY => PREADY,
+ X => X,
+ Y => Y,
+ RI => RI,
+ GI => GI,
+ BI => BI,
+ RO => RO,
+ GO => GO,
+ BO => BO,
+ NVSYNC => NVSYNC,
+ NHSYNC => NHSYNC,
+ TVBLANK => TVBLANK,
+ THBLANK => THBLANK);
+
+ process
+ begin
+ for i in 0 to 3200000 loop
+ wait for 5 ps;
+ CLK <= '1';
+ wait for 5 ps;
+ CLK <= '0';
+ end loop;
+ wait; -- stop for simulator
+ end process;
+end;
diff --git a/basys3/basys3.xpr b/basys3/basys3.xpr
index 4134b4d..97a30aa 100644
--- a/basys3/basys3.xpr
+++ b/basys3/basys3.xpr
@@ -61,7 +61,7 @@
<Option Name="IPStaticSourceDir" Val="$PIPUSERFILESDIR/ipstatic"/>
<Option Name="EnableBDX" Val="FALSE"/>
<Option Name="DSABoardId" Val="basys3"/>
- <Option Name="WTXSimLaunchSim" Val="126"/>
+ <Option Name="WTXSimLaunchSim" Val="153"/>
<Option Name="WTModelSimLaunchSim" Val="0"/>
<Option Name="WTQuestaLaunchSim" Val="0"/>
<Option Name="WTIesLaunchSim" Val="0"/>
@@ -260,9 +260,15 @@
<Attr Name="UsedIn" Val="simulation"/>
</FileInfo>
</File>
+ <File Path="$PSRCDIR/ppu_dispctl_tb.vhd">
+ <FileInfo>
+ <Attr Name="UsedIn" Val="synthesis"/>
+ <Attr Name="UsedIn" Val="simulation"/>
+ </FileInfo>
+ </File>
<Config>
<Option Name="DesignMode" Val="RTL"/>
- <Option Name="TopModule" Val="ppu_pceg_tb"/>
+ <Option Name="TopModule" Val="ppu_dispctl_tb"/>
<Option Name="TopLib" Val="xil_defaultlib"/>
<Option Name="TransportPathDelay" Val="0"/>
<Option Name="TransportIntDelay" Val="0"/>
@@ -272,6 +278,7 @@
<Option Name="PamSignalDriverFile" Val="xil_bypass_driver"/>
<Option Name="PamPseudoTop" Val="pseudo_tb"/>
<Option Name="SrcSet" Val="sources_1"/>
+ <Option Name="xsim.simulate.runtime" Val="100 us"/>
</Config>
</FileSet>
<FileSet Name="utils_1" Type="Utils" RelSrcDir="$PSRCDIR/utils_1" RelGenDir="$PGENDIR/utils_1">
@@ -354,7 +361,9 @@
<Runs Version="1" Minor="19">
<Run Id="synth_1" Type="Ft3:Synth" SrcSet="sources_1" Part="xc7a35tcpg236-1" ConstrsSet="constrs_1" Description="Vivado Synthesis Defaults" AutoIncrementalCheckpoint="true" IncrementalCheckpoint="$PSRCDIR/utils_1/imports/synth_1/ppu.dcp" WriteIncrSynthDcp="false" State="current" Dir="$PRUNDIR/synth_1" IncludeInArchive="true" IsChild="false" AutoIncrementalDir="$PSRCDIR/utils_1/imports/synth_1" AutoRQSDir="$PSRCDIR/utils_1/imports/synth_1">
<Strategy Version="1" Minor="2">
- <StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2022"/>
+ <StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2022">
+ <Desc>Vivado Synthesis Defaults</Desc>
+ </StratHandle>
<Step Id="synth_design"/>
</Strategy>
<GeneratedRun Dir="$PRUNDIR" File="gen_run.xml"/>
@@ -384,9 +393,7 @@
</Run>
<Run Id="ppu_dispctl_slbuf_synth_1" Type="Ft3:Synth" SrcSet="ppu_dispctl_slbuf" Part="xc7a35tcpg236-1" ConstrsSet="ppu_dispctl_slbuf" Description="Vivado Synthesis Defaults" AutoIncrementalCheckpoint="false" WriteIncrSynthDcp="false" Dir="$PRUNDIR/ppu_dispctl_slbuf_synth_1" IncludeInArchive="true" IsChild="false" AutoIncrementalDir="$PSRCDIR/utils_1/imports/ppu_dispctl_slbuf_synth_1" AutoRQSDir="$PSRCDIR/utils_1/imports/ppu_dispctl_slbuf_synth_1">
<Strategy Version="1" Minor="2">
- <StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2022">
- <Desc>Vivado Synthesis Defaults</Desc>
- </StratHandle>
+ <StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2022"/>
<Step Id="synth_design"/>
</Strategy>
<GeneratedRun Dir="$PRUNDIR" File="gen_run.xml"/>
@@ -396,7 +403,9 @@
</Run>
<Run Id="impl_1" Type="Ft2:EntireDesign" Part="xc7a35tcpg236-1" ConstrsSet="constrs_1" Description="Default settings for Implementation." AutoIncrementalCheckpoint="false" WriteIncrSynthDcp="false" State="current" Dir="$PRUNDIR/impl_1" SynthRun="synth_1" IncludeInArchive="true" IsChild="false" GenFullBitstream="true" AutoIncrementalDir="$PSRCDIR/utils_1/imports/impl_1" AutoRQSDir="$PSRCDIR/utils_1/imports/impl_1">
<Strategy Version="1" Minor="2">
- <StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2022"/>
+ <StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2022">
+ <Desc>Default settings for Implementation.</Desc>
+ </StratHandle>
<Step Id="init_design"/>
<Step Id="opt_design"/>
<Step Id="power_opt_design"/>
@@ -448,9 +457,7 @@
</Run>
<Run Id="ppu_dispctl_slbuf_impl_1" Type="Ft2:EntireDesign" Part="xc7a35tcpg236-1" ConstrsSet="ppu_dispctl_slbuf" Description="Default settings for Implementation." AutoIncrementalCheckpoint="false" WriteIncrSynthDcp="false" SynthRun="ppu_dispctl_slbuf_synth_1" IncludeInArchive="false" IsChild="false" GenFullBitstream="true" AutoIncrementalDir="$PSRCDIR/utils_1/imports/ppu_dispctl_slbuf_impl_1" AutoRQSDir="$PSRCDIR/utils_1/imports/ppu_dispctl_slbuf_impl_1">
<Strategy Version="1" Minor="2">
- <StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2022">
- <Desc>Default settings for Implementation.</Desc>
- </StratHandle>
+ <StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2022"/>
<Step Id="init_design"/>
<Step Id="opt_design"/>
<Step Id="power_opt_design"/>