From 50f36afcf4160529d9b7d03680989a373a44f83f Mon Sep 17 00:00:00 2001 From: Max P Date: Fri, 25 Apr 2025 15:59:37 +0000 Subject: [PATCH] Adds VGA controller and testbench enhancements Introduces a new VGA module for pixel rendering and sync signal generation. Implements an XY position generator for coordinate management. Updates testbench with color cycling logic and additional color constants. Adjusts timing generator logic for improved sync signal handling and accuracy. Modifies UCF constraints for compatibility with LVTTL standard. Improves modularity and flexibility of the VGA system. --- project.cfg | 9 +- src/VGA.vhd | 173 ++++++++++++++++++++++++++++++++ src/VGATimingGenerator.vhd | 43 ++++---- src/VGATimingGenerator_test.ucf | 20 ++-- src/VGATimingGenerator_test.vhd | 98 +++++++++++++----- src/XY_Generator.vhd | 92 +++++++++++++++++ 6 files changed, 380 insertions(+), 55 deletions(-) create mode 100644 src/VGA.vhd create mode 100644 src/XY_Generator.vhd diff --git a/project.cfg b/project.cfg index dda78c5..1afe342 100644 --- a/project.cfg +++ b/project.cfg @@ -28,9 +28,16 @@ CONSTRAINTS = src/VGATimingGenerator_test.ucf # @example `VSOURCE += src/main.v` (add a single Verilog file per line) # @example `VHDSOURCE += src/main.vhd` (add a single VHDL file per line) -VHDSOURCE += src/VGATimingGenerator_pb.vhd +# VHDSOURCE += src/VGATimingGenerator_pb.vhd VHDSOURCE += src/VGATimingGenerator_test.vhd VHDSOURCE += src/VGATimingGenerator.vhd +VHDSOURCE += src/XY_Generator.vhd +VHDSOURCE += src/VGA.vhd +VHDSOURCE += ../Asynchronous-FIFO-AXI-Handshake/libs/GrayCounter.vhd +VHDSOURCE += ../Asynchronous-FIFO-AXI-Handshake/src/AsyncFIFO.vhd +VHDSOURCE += ../Asynchronous-FIFO-AXI-Handshake/libs/Pipeline-AXI-Handshake/src/PipelineRegister.vhd +VHDSOURCE += ../Asynchronous-FIFO-AXI-Handshake/libs/Pipeline-AXI-Handshake/src/PipelineStage.vhd +VHDSOURCE += ../Asynchronous-FIFO-AXI-Handshake/libs/Pipeline-AXI-Handshake/src/PipelineController.vhd ## Test files settings.. ## # The testbench files to be compiled diff --git a/src/VGA.vhd b/src/VGA.vhd new file mode 100644 index 0000000..8b1f290 --- /dev/null +++ b/src/VGA.vhd @@ -0,0 +1,173 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; + +entity VGA is + port ( + --@ Pixel Clock; (**Rising edge** triggered) + I_VGA_PixelCLK : in std_logic; + --@ Pixel Clock Enable; (**Synchronous**, **Active high**) + I_VGA_PixelCE : in std_logic; + --@ Pixel Reset; (**Synchronous**, **Active high**) + I_VGA_PixelRST : in std_logic; + + --@ Clock; (**Rising edge** triggered) + I_VGA_CLK : in std_logic; + --@ Clock Enable; (**Synchronous**, **Active high**) + I_VGA_CE : in std_logic; + --@ Reset; (**Synchronous**, **Active high**) + I_VGA_RST : in std_logic; + + --@ @virtualbus VGA-Signals @dir out VGA signals + --@ Horizontal Sync signal; **Active low** + O_VGA_HSync : out std_logic; + --@ Vertical Sync signal; **Active low** + O_VGA_VSync : out std_logic; + --@ VGA Red Channel + O_VGA_Red : out std_logic_vector(2 downto 0); + --@ VGA Green Channel + O_VGA_Green : out std_logic_vector(2 downto 0); + --@ VGA Blue Channel + O_VGA_Blue : out std_logic_vector(1 downto 0); + --@ @end + + --@ @virtualbus XY-Request @dir Out Request for the X and Y positions + O_VGA_Req_Y_Valid : out std_logic; + I_VGA_Req_Y_Ready : in std_logic; + O_VGA_Req_Y : out std_logic_vector(9 downto 0); + O_VGA_Req_X_Valid : out std_logic; + I_VGA_Req_X_Ready : in std_logic; + O_VGA_Req_X : out std_logic_vector(9 downto 0); + --@ @end + + --@ @virtualbus Pixel-Data @dir In Calculated pixel data + I_VGA_PixelData_Valid : in std_logic; + O_VGA_PixelData_Ready : out std_logic; + I_VGA_PixelData : in std_logic_vector(7 downto 0); + --@ @end + + --@ @virtualbus Error-Register @dir Out Error register; (Synchronous to I_VGA_CLK) + I_VGA_Error_RST : in std_logic := '0'; + O_VGA_Error : out std_logic_vector(7 downto 0) := (others => '0') + --@ @end + + ); +end entity VGA; + +architecture RTL of VGA is + signal O_TimingGenerator_PixelReady : std_logic; + signal O_TimingGenerator_HSync : std_logic; + signal O_TimingGenerator_VSync : std_logic; + signal O_AsyncPixelDataFIFO_Valid : std_logic; + signal O_AsyncPixelDataFIFO_Data : std_logic_vector(7 downto 0); + signal C_DisablePixelOutput : std_logic := '0'; + + -- Error signals -- + signal R_Error : std_logic_vector(7 downto 0) := (others => '0'); + --@ Is set if the pixel data is requested but not available in the FIFO + signal C_Error_PixelUnderrun : std_logic := '0'; +begin + INST_TimingGenerator : entity work.VGATimingGenerator + port map( + I_CLK => I_VGA_PixelCLK, + I_CE => I_VGA_PixelCE, + I_RST => I_VGA_PixelRST, + O_PixelReady => O_TimingGenerator_PixelReady, + O_HSync => O_TimingGenerator_HSync, + O_VSync => O_TimingGenerator_VSync + ); + + O_VGA_HSync <= O_TimingGenerator_HSync; + O_VGA_VSync <= O_TimingGenerator_VSync; + + C_DisablePixelOutput <= not O_TimingGenerator_HSync and + not O_TimingGenerator_VSync; + + INST_XY_Generator : entity work.XY_Generator + generic map( + G_X => 640, + G_Y => 480, + G_X_Width => 10, + G_Y_Width => 10 + ) + port map( + I_CLK => I_VGA_CLK, + I_CE => I_VGA_CE, + I_RST => I_VGA_RST, + O_Y_Valid => O_VGA_Req_Y_Valid, + I_Y_Ready => I_VGA_Req_Y_Ready, + O_Y => O_VGA_Req_Y, + + O_X_Valid => O_VGA_Req_X_Valid, + I_X_Ready => I_VGA_Req_X_Ready, + O_X => O_VGA_Req_X + ); + + INST_AsyncPixelDataFIFO : entity work.AsyncFIFO + generic map( + G_Width => 8, + G_Depth => 16, + G_RamTypeFifo => "Distributed" + ) + port map( + I_Write_CLK => I_VGA_CLK, + I_Write_CE => I_VGA_CE, + I_Write_Data => I_VGA_PixelData, + I_Write_Valid => I_VGA_PixelData_Valid, + O_Write_Ready => O_VGA_PixelData_Ready, + I_Read_CLK => I_VGA_PixelCLK, + I_Read_CE => I_VGA_PixelCE, + O_Read_Data => O_AsyncPixelDataFIFO_Data, + I_Read_Ready => O_TimingGenerator_PixelReady, + O_Read_Valid => O_AsyncPixelDataFIFO_Valid + ); + + P_O_Pixel_Register : process (I_VGA_PixelCLK) + begin + if rising_edge(I_VGA_PixelCLK) then + if I_VGA_PixelRST = '1' then + O_VGA_Red <= (others => '0'); + O_VGA_Green <= (others => '0'); + O_VGA_Blue <= (others => '0'); + C_Error_PixelUnderrun <= '0'; + elsif I_VGA_PixelCE = '1' then + -- Reset Error flags -- + C_Error_PixelUnderrun <= '0'; + -- -- -- -- -- -- -- -- + + if (O_TimingGenerator_PixelReady and O_AsyncPixelDataFIFO_Valid) = '1' then + O_VGA_Red <= O_AsyncPixelDataFIFO_Data(7 downto 5); + O_VGA_Green <= O_AsyncPixelDataFIFO_Data(4 downto 2); + O_VGA_Blue <= O_AsyncPixelDataFIFO_Data(1 downto 0); + elsif O_TimingGenerator_PixelReady = '1' then + --@ Pixel data is requested but not available in the FIFO + --@ Set error flag + C_Error_PixelUnderrun <= '1'; + elsif C_DisablePixelOutput = '1' then + --@ Disable pixel output + O_VGA_Red <= (others => '0'); + O_VGA_Green <= (others => '0'); + O_VGA_Blue <= (others => '0'); + end if; + end if; + end if; + end process; + + + P_Error_Register : process (I_VGA_CLK) + begin + if rising_edge(I_VGA_CLK) then + if I_VGA_RST = '1' then + R_Error <= (others => '0'); + elsif I_VGA_Error_RST = '1' then + R_Error <= (others => '0'); + else + R_Error(0) <= R_Error(0) or C_Error_PixelUnderrun; + end if; + end if; + end process; + + O_VGA_Error <= R_Error; + +end architecture; diff --git a/src/VGATimingGenerator.vhd b/src/VGATimingGenerator.vhd index 205062d..1be7a26 100644 --- a/src/VGATimingGenerator.vhd +++ b/src/VGATimingGenerator.vhd @@ -1,7 +1,7 @@ ---------------------------------------------------------------------------------- --@ - Name: **VGA Timing Generator** --@ - Version: 0.0.1 ---@ - Author: _Maximilian Passarello ([Blog](mpassarello.de))_ +--@ - Author: _0xMax42 ([Blog](0xMax42.io))_ --@ - License: [MIT](LICENSE) --@ --@ The VGA Timing Generator is a simple module that generates the horizontal and vertical sync signals for a VGA display. @@ -35,14 +35,15 @@ entity VGATimingGenerator is G_VBack : integer := 33; --@ Vertical Total resolution G_VTotal : integer := 525 - ); + ); port ( - --@ Clock signal; **Rising edge** triggered - I_CLK : in std_logic; - --@ Clock Enable signal - I_CE : in std_logic; - --@ Synchronous reset signal - I_RST : in std_logic; + --@ Clock; (**Rising edge** triggered) + I_CLK : in std_logic; + --@ Clock Enable; (**Synchronous**, **Active high**) + I_CE : in std_logic; + --@ Reset; (**Synchronous**, **Active high**) + I_RST : in std_logic; + --@ Ready signal (AXI like) to indicate that the pixel data is ready to be displayed O_PixelReady : out std_logic; --@ @virtualbus VGA-Timing-Signals @dir out VGA timing signals @@ -50,8 +51,8 @@ entity VGATimingGenerator is O_HSync : out std_logic; --@ Vertical Sync signal; **Active low** O_VSync : out std_logic - --@ @end - ); + --@ @end + ); end entity VGATimingGenerator; architecture RTL of VGATimingGenerator is @@ -61,19 +62,19 @@ architecture RTL of VGATimingGenerator is signal R_VerticalCounter : unsigned(integer(ceil(log2(real(G_VTotal)))) - 1 downto 0) := (others => '0'); --@ Counter Enable signal for Vertical Counter - signal C_VerticalCE : std_logic := '0'; + signal C_VerticalCE : std_logic := '0'; --@ Flag to indicate if the horizontal counter is in the visible area - signal C_HorizontalVisible : std_logic := '0'; + signal C_HorizontalVisible : std_logic := '0'; --@ Flag to indicate if the vertical counter is in the visible area - signal C_VerticalVisible : std_logic := '0'; + signal C_VerticalVisible : std_logic := '0'; --@ Horizontal Sync signal shift register - signal R_HSync : std_logic_vector(1 downto 0) := (others => '0'); + signal R_HSync : std_logic_vector(1 downto 0) := (others => '0'); --@ Vertical Sync signal register - signal R_VSync : std_logic := '1'; + signal R_VSync : std_logic := '1'; --@ Pixel Ready signal - signal C_PixelReady : std_logic := '0'; + signal C_PixelReady : std_logic := '0'; begin --@ Horizontal Pixel Counter. --@ Overflows at G_HTotal. @@ -138,7 +139,7 @@ begin if rising_edge(I_CLK) then if I_RST = '1' then R_VerticalCounter <= (others => '0'); - elsif C_VerticalCE = '1' then + elsif (I_CE and C_VerticalCE) = '1' then if R_VerticalCounter = G_VTotal - 1 then R_VerticalCounter <= (others => '0'); else @@ -154,8 +155,8 @@ begin if rising_edge(I_CLK) then if I_RST = '1' then R_VSync <= '1'; - elsif C_VerticalCE = '1' then - if R_VerticalCounter < G_VSync then + elsif (I_CE and C_VerticalCE) = '1' then + if R_VerticalCounter >= G_VTotal - G_VSync then R_VSync <= '0'; else R_VSync <= '1'; @@ -167,8 +168,8 @@ begin --@ Flag generator for vertical visible area. P_VerticalVisible : process (R_VerticalCounter) begin - if R_VerticalCounter >= G_VSync + G_VBack and - R_VerticalCounter <= G_VTotal - G_VFront - 1 then + if R_VerticalCounter >= G_VBack and + R_VerticalCounter <= G_VTotal - G_VFront - G_VSync - 1 then C_VerticalVisible <= '1'; else C_VerticalVisible <= '0'; diff --git a/src/VGATimingGenerator_test.ucf b/src/VGATimingGenerator_test.ucf index 19419ae..d41d69d 100644 --- a/src/VGATimingGenerator_test.ucf +++ b/src/VGATimingGenerator_test.ucf @@ -2,14 +2,14 @@ NET I_CLK LOC = B8; NET I_CLK TNM_NET = CLOCK; TIMESPEC TS_CLOCK = PERIOD CLOCK 50 MHz HIGH 50 %; -NET O_HSync LOC = T4 | IOSTANDARD = LVCMOS25 | SLEW = FAST | DRIVE = 12; -NET O_VSync LOC = U3 | IOSTANDARD = LVCMOS25 | SLEW = FAST | DRIVE = 12; +NET O_HSync LOC = T4 | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 2; +NET O_VSync LOC = U3 | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 2; -NET O_Red<0> LOC = R9 | IOSTANDARD = LVCMOS25 | SLEW = FAST | DRIVE = 12; -NET O_Red<1> LOC = T8 | IOSTANDARD = LVCMOS25 | SLEW = FAST | DRIVE = 12; -NET O_Red<2> LOC = R8 | IOSTANDARD = LVCMOS25 | SLEW = FAST | DRIVE = 12; -NET O_Green<0> LOC = N8 | IOSTANDARD = LVCMOS25 | SLEW = FAST | DRIVE = 12; -NET O_Green<1> LOC = P8 | IOSTANDARD = LVCMOS25 | SLEW = FAST | DRIVE = 12; -NET O_Green<2> LOC = P6 | IOSTANDARD = LVCMOS25 | SLEW = FAST | DRIVE = 12; -NET O_Blue<0> LOC = U5 | IOSTANDARD = LVCMOS25 | SLEW = FAST | DRIVE = 12; -NET O_Blue<1> LOC = U4 | IOSTANDARD = LVCMOS25 | SLEW = FAST | DRIVE = 12; \ No newline at end of file +NET O_Red<0> LOC = R9 | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 2; +NET O_Red<1> LOC = T8 | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 2; +NET O_Red<2> LOC = R8 | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 2; +NET O_Green<0> LOC = N8 | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 2; +NET O_Green<1> LOC = P8 | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 2; +NET O_Green<2> LOC = P6 | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 2; +NET O_Blue<0> LOC = U5 | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 2; +NET O_Blue<1> LOC = U4 | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 2; \ No newline at end of file diff --git a/src/VGATimingGenerator_test.vhd b/src/VGATimingGenerator_test.vhd index 4fbdfae..30cc9e1 100644 --- a/src/VGATimingGenerator_test.vhd +++ b/src/VGATimingGenerator_test.vhd @@ -23,11 +23,17 @@ entity VGATimingGenerator_test is end entity VGATimingGenerator_test; architecture RTL of VGATimingGenerator_test is + + function rgb_to_vector(red : unsigned(2 downto 0); green : unsigned(2 downto 0); blue : unsigned(1 downto 0)) return std_logic_vector is + begin + return std_logic_vector(std_logic_vector(red) & std_logic_vector(green) & std_logic_vector(blue)); + end function; + signal I_VGA_PixelCE : std_logic := '0'; signal I_VGA_PixelRST : std_logic := '1'; signal O_VGA_Req_Y_Valid : std_logic; - signal I_VGA_Req_Y_Ready : std_logic; + signal I_VGA_Req_Y_Ready : std_logic := '0'; signal O_VGA_Req_Y : std_logic_vector(9 downto 0); signal O_VGA_Req_X_Valid : std_logic; @@ -43,24 +49,60 @@ architecture RTL of VGATimingGenerator_test is signal I_VGA_PixelData : std_logic_vector(7 downto 0); ---- - constant COLOR_RED : std_logic_vector(7 downto 0) := "11100000"; -- R=7, G=0, B=0 - constant COLOR_GREEN : std_logic_vector(7 downto 0) := "00011100"; -- R=0, G=7, B=0 - constant COLOR_BLUE : std_logic_vector(7 downto 0) := "00000011"; -- R=0, G=0, B=3 + constant K_COLOR_BLACK : std_logic_vector(7 downto 0) := rgb_to_vector("000", "000", "00"); + constant K_COLOR_WHITE : std_logic_vector(7 downto 0) := rgb_to_vector("111", "111", "11"); - signal R_CurrentColor : std_logic_vector(7 downto 0) := COLOR_BLUE; -- Pre-Startfarbe: Blau - constant COLOR_CHANGE_INTERVAL : integer := 16; + constant K_COLOR_RED : std_logic_vector(7 downto 0) := rgb_to_vector("111", "000", "00"); + constant K_COLOR_GREEN : std_logic_vector(7 downto 0) := rgb_to_vector("000", "111", "00"); + constant K_COLOR_BLUE : std_logic_vector(7 downto 0) := rgb_to_vector("000", "000", "11"); --- Farbwechsel-Logik (einfache zyklische Farben) - function next_color(current : std_logic_vector(7 downto 0)) return std_logic_vector is + constant K_COLOR_CYAN : std_logic_vector(7 downto 0) := rgb_to_vector("000", "111", "11"); -- G + B + constant K_COLOR_MAGENTA : std_logic_vector(7 downto 0) := rgb_to_vector("111", "000", "11"); -- R + B + constant K_COLOR_YELLOW : std_logic_vector(7 downto 0) := rgb_to_vector("111", "111", "00"); -- R + G + + constant K_COLOR_GRAY : std_logic_vector(7 downto 0) := rgb_to_vector("100", "100", "10"); -- mittelgrau + constant K_COLOR_DARK_GRAY : std_logic_vector(7 downto 0) := rgb_to_vector("010", "010", "01"); + constant K_COLOR_LIGHT_GRAY : std_logic_vector(7 downto 0) := rgb_to_vector("110", "110", "10"); + + constant K_COLOR_ORANGE : std_logic_vector(7 downto 0) := rgb_to_vector("111", "011", "00"); + constant K_COLOR_PINK : std_logic_vector(7 downto 0) := rgb_to_vector("111", "100", "10"); + constant K_COLOR_BROWN : std_logic_vector(7 downto 0) := rgb_to_vector("100", "010", "00"); + constant K_COLOR_LIME : std_logic_vector(7 downto 0) := rgb_to_vector("010", "111", "00"); + constant K_COLOR_NAVY : std_logic_vector(7 downto 0) := rgb_to_vector("000", "000", "01"); + + + constant K_DEFAULT_COLOR : std_logic_vector(7 downto 0) := K_COLOR_CYAN; + + signal R_CurrentColor : std_logic_vector(7 downto 0); + signal R_CurrentColorAlt : std_logic_vector(7 downto 0); + signal R_MUX_Color : std_logic := '0'; + constant K_COLOR_CHANGE_INTERVAL : integer := 16; + + -- Farbwechsel-Logik (einfache zyklische Farben) + function next_color(currentColor : std_logic_vector(7 downto 0)) return std_logic_vector is begin - if current = COLOR_RED then -- Rot - return COLOR_GREEN; -- Grün - elsif current = COLOR_GREEN then -- Grün - return COLOR_BLUE; -- Blau - else - return COLOR_RED; -- Rot - end if; + case currentColor is + when K_COLOR_BLACK => return K_COLOR_WHITE; + when K_COLOR_WHITE => return K_COLOR_RED; + when K_COLOR_RED => return K_COLOR_GREEN; + when K_COLOR_GREEN => return K_COLOR_BLUE; + when K_COLOR_BLUE => return K_COLOR_CYAN; + when K_COLOR_CYAN => return K_COLOR_MAGENTA; + when K_COLOR_MAGENTA => return K_COLOR_YELLOW; + when K_COLOR_YELLOW => return K_COLOR_GRAY; + when K_COLOR_GRAY => return K_COLOR_DARK_GRAY; + when K_COLOR_DARK_GRAY => return K_COLOR_LIGHT_GRAY; + when K_COLOR_LIGHT_GRAY => return K_COLOR_ORANGE; + when K_COLOR_ORANGE => return K_COLOR_PINK; + when K_COLOR_PINK => return K_COLOR_BROWN; + when K_COLOR_BROWN => return K_COLOR_LIME; + when K_COLOR_LIME => return K_COLOR_NAVY; + when K_COLOR_NAVY => return K_COLOR_BLACK; -- wieder von vorn + when others => return K_DEFAULT_COLOR; + end case; end function; + + begin process (I_CLK) @@ -72,23 +114,33 @@ begin I_VGA_PixelData_Valid <= O_VGA_Req_X_Valid; I_VGA_Req_X_Ready <= O_VGA_PixelData_Ready; + + I_VGA_PixelData <= R_CurrentColor when R_MUX_Color = '0' else + R_CurrentColorAlt; process(I_CLK) begin if rising_edge(I_CLK) then I_VGA_PixelRST <= '0'; - I_VGA_PixelData <= R_CurrentColor; + if O_VGA_Req_X_Valid = '1' and O_VGA_PixelData_Ready = '1' then + if unsigned(O_VGA_Req_X) = to_unsigned(640-1, O_VGA_Req_X'length) then + R_MUX_Color <= '0'; + elsif unsigned(O_VGA_Req_X) = to_unsigned(320-1, O_VGA_Req_X'length) then + R_MUX_Color <= '1'; + end if; + end if; - -- Default - I_VGA_Req_Y_Ready <= '0'; - - -- Zeilenwechsel erkennen (Req_X zurück auf 0) if O_VGA_Req_Y_Valid = '1' and I_VGA_Req_Y_Ready = '0' then I_VGA_Req_Y_Ready <= '1'; - - if (unsigned(O_VGA_Req_Y) mod COLOR_CHANGE_INTERVAL) = 0 then - R_CurrentColor <= next_color(R_CurrentColor); + if O_VGA_Req_Y = (O_VGA_Req_Y'range => '0') then + R_CurrentColor <= K_DEFAULT_COLOR; + R_CurrentColorAlt <= next_color(K_DEFAULT_COLOR); + elsif (unsigned(O_VGA_Req_Y) mod K_COLOR_CHANGE_INTERVAL) = 0 then + R_CurrentColor <= next_color(R_CurrentColor); + R_CurrentColorAlt <= next_color(R_CurrentColorAlt); end if; + elsif I_VGA_Req_Y_Ready = '1' then + I_VGA_Req_Y_Ready <= '0'; end if; end if; diff --git a/src/XY_Generator.vhd b/src/XY_Generator.vhd new file mode 100644 index 0000000..46b8e2a --- /dev/null +++ b/src/XY_Generator.vhd @@ -0,0 +1,92 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; + +entity XY_Generator is + generic ( + G_X : integer := 640; + G_Y : integer := 480; + --@ Width of the X position (Row) register + G_X_Width : integer := 10; + --@ Width of the Y position (Line) register + G_Y_Width : integer := 10 + ); + port ( + --@ Clock; (**Rising edge** triggered) + I_CLK : in std_logic; + --@ Clock Enable; (**Synchronous**, **Active high**) + I_CE : in std_logic; + --@ Reset; (**Synchronous**, **Active high**) + I_RST : in std_logic; + + --@ @virtualbus Y @dir Out Output of the Y positions, with priority over X + O_Y_Valid : out std_logic; + I_Y_Ready : in std_logic; + O_Y : out std_logic_vector(G_Y_Width - 1 downto 0); + --@ @end + --@ @virtualbus Y @dir Out Output of the X positions + O_X_Valid : out std_logic; + I_X_Ready : in std_logic; + O_X : out std_logic_vector(G_X_Width - 1 downto 0) + --@ @end + + ); + +end entity XY_Generator; + +architecture RTL of XY_Generator is + signal R_X_Counter : unsigned(G_X_Width - 1 downto 0) := (others => '0'); + signal R_Y_Counter : unsigned(G_Y_Width - 1 downto 0) := (others => '0'); + + signal R_Y_Valid : std_logic := '1'; + signal R_X_Valid : std_logic := '1'; + signal C_X_Valid : std_logic := '0'; +begin + + C_X_Valid <= R_X_Valid and not R_Y_Valid; + + process (I_CLK) + begin + if rising_edge(I_CLK) then + if I_RST = '1' then + R_X_Counter <= (others => '0'); + R_Y_Counter <= (others => '0'); + R_Y_Valid <= '1'; + R_X_Valid <= '1'; + elsif I_CE = '1' then + if R_Y_Valid = '1' then + if I_Y_Ready = '1' then + R_Y_Valid <= '0'; + end if; + elsif R_X_Valid = '1' then + if I_X_Ready = '1' then + R_X_Valid <= '0'; + end if; + else + if R_X_Counter = (G_X - 1) then + R_X_Counter <= (others => '0'); + R_X_Valid <= '1'; + + if R_Y_Counter = (G_Y - 1) then + R_Y_Counter <= (others => '0'); + R_Y_Valid <= '1'; + else + R_Y_Counter <= R_Y_Counter + 1; + R_Y_Valid <= '1'; + end if; + else + R_X_Counter <= R_X_Counter + 1; + R_X_Valid <= '1'; + end if; + end if; + end if; + end if; + end process; + + O_X <= std_logic_vector(R_X_Counter); + O_Y <= std_logic_vector(R_Y_Counter); + O_Y_Valid <= R_Y_Valid; + O_X_Valid <= C_X_Valid; + +end architecture;