OpenCores
URL https://opencores.org/ocsvn/cpu8080/cpu8080/trunk

Subversion Repositories cpu8080

[/] [cpu8080/] [trunk/] [project/] [vga.vhd.save] - Rev 20

Go to most recent revision | Compare with Previous | Blame | View Log

-- *****************************************************************************
-- *                                                                           *
-- *                            VGA TIMING DRIVER                              *
-- *                                                                           *
-- * Creates timing for the VGA port on an XESS board. This is the XESS VGA    *
-- * generator with the pixel buffer fifo ripped out, and the signals it fed   *
-- * are brought out to ports. The buffer is not bad, it is just not required  *
-- * for applications like a dot character generator, that can respond to      *
-- * pixel row timing without delay.                                           *
-- *                                                                           *
-- * Simulation plugs exist in this code. Look for "????? SIMULATION PLUG"     *
-- *                                                                           *
-- *****************************************************************************

library IEEE, unisim;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use unisim.vcomponents.all;
use work.common.all;

package vga_pckg is
  component vga
    generic (
      FREQ            :     natural := 50_000;  -- master clock frequency (in KHz)
      CLK_DIV         :     natural := 2;  -- FREQ / CLK_DIV = pixel clock
      PIXEL_WIDTH     :     natural := 2;  -- pixel width: 1, 2, 4, 8, or 16 bits
      PIXELS_PER_LINE :     natural := 640;  -- pixels per video scan line
      LINES_PER_FRAME :     natural := 480;  -- scan lines per video frame
      NUM_RGB_BITS    :     natural := 3;  -- width of R, G and B color output buses (2 or 3 are only valid values)
      FIT_TO_SCREEN   :     boolean := true  -- fit width x length to monitor screen
      );
    port (
      rst             : in  std_logic;  -- reset
      clk             : in  std_logic;  -- master clock
      pixel_data_in   : in  std_logic_vector(15 downto 0);  -- input databus to pixel buffer
      rd              : out std_logic;  -- read next pixel
      eof             : out std_logic;  -- end of vga frame
      r, g, b         : out std_logic_vector(NUM_RGB_BITS-1 downto 0);  -- R,G,B color output buses
      hsync_n         : out std_logic;  -- horizontal sync pulse
      vsync_n         : out std_logic;  -- vertical sync pulse
      blank           : out std_logic   -- blanking signal
      );
  end component vga;
end package vga_pckg;

library IEEE, unisim;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use unisim.vcomponents.all;
use work.common.all;

entity vga is
  generic (
    FREQ            :     natural := 50_000;  -- master clock frequency (in KHz)
    CLK_DIV         :     natural := 2;  -- FREQ / CLK_DIV = pixel clock
    PIXEL_WIDTH     :     natural := 2;  -- pixel width: 1, 2, 4, 8, or 16 bits
    PIXELS_PER_LINE :     natural := 640;  -- pixels per video scan line
    LINES_PER_FRAME :     natural := 480;  -- scan lines per video frame
    NUM_RGB_BITS    :     natural := 3;  -- width of R, G and B color output buses
    FIT_TO_SCREEN   :     boolean := true  -- fit width x length to monitor screen
    );
  port (
    rst             : in  std_logic;  -- reset
    clk             : in  std_logic;  -- master clock
    pixel_data_in   : in  std_logic_vector(15 downto 0);  -- input databus to pixel buffer
    rd              : out std_logic;  -- read next pixel
    eof             : out std_logic;  -- end of vga frame
    r, g, b         : out std_logic_vector(NUM_RGB_BITS-1 downto 0);  -- R,G,B color output buses
    hsync_n         : out std_logic;  -- horizontal sync pulse
    vsync_n         : out std_logic;  -- vertical sync pulse
    blank           : out std_logic   -- blanking signal
    );
end entity vga;

architecture vga_arch of vga is
  constant NORM : natural := 1000;      -- normalization factor for us * KHz

  -- video timing parameters for FIT_TO_SCREEN mode
  constant HSYNC_START_F  : natural := (NORM * PIXELS_PER_LINE * CLK_DIV)/FREQ + 1;  -- start of horiz. sync pulse with a scanline (us)
  constant HSYNC_PERIOD_F : natural := HSYNC_START_F + 6;  -- horizontal scanline period (us)
  constant HSYNC_WIDTH_F  : natural := 4;  -- width of horiz. sync pulse (us)
  constant HSYNC_FREQ_F   : natural := NORM / HSYNC_PERIOD_F;  -- scanline frequency (KHz) 
  constant VSYNC_START_F  : natural := HSYNC_PERIOD_F * LINES_PER_FRAME + 340;  -- start of vert. sync pulse within a frame (us)
  constant VSYNC_PERIOD_F : natural := VSYNC_START_F + 1084;  -- video frame period (us)
  constant VSYNC_WIDTH_F  : natural := 64;  -- width of vert. sync pulse (us)

  -- video timing for 31 KHz horizontal, 60 Hz vertical screen refresh
  constant HSYNC_START  : natural := 26;  -- start of horiz. sync pulse with a scanline (us)
  constant HSYNC_PERIOD : natural := 32;  -- horizontal scanline period (us)
  constant HSYNC_WIDTH  : natural := 4;  -- width of horiz. sync pulse (us)
  constant HSYNC_FREQ   : natural := NORM / HSYNC_PERIOD;  -- scanline frequency (KHz) 
  constant VSYNC_START  : natural := 15_700;  -- start of vert. sync pulse within a frame (us)
  constant VSYNC_PERIOD : natural := 16_784;  -- video frame period (us)
  constant VSYNC_WIDTH  : natural := 64;  -- width of vert. sync pulse (us)

  signal   clk_div_cnt                :     unsigned(7 downto 0);
  signal   cke                        :     std_logic;
  signal   line_cnt, pixel_cnt        :     unsigned(15 downto 0);  -- current video line and pixel within line
  signal   eof_i, eof_x, eof_r        :     std_logic;
  signal   v_gate, cke_v_gate         :     std_logic;
  signal   h_blank, v_blank, visible  :     std_logic;
  constant PIX_PROC_DELAY             :     natural := 3;  -- time delay to read a pixel from the FIFO and colormap it
  signal   hsync_x, hsync_r           :     std_logic_vector(PIX_PROC_DELAY downto 1);
  signal   blank_x, blank_r           :     std_logic_vector(PIX_PROC_DELAY downto 1);
  signal   rd_x, rd_r                 :     std_logic;
  signal   pixel                      :     std_logic_vector(PIXEL_WIDTH-1 downto 0);
  signal   pixel_data_x, pixel_data_r :     std_logic_vector(15 downto 0);
  signal   rgb_x, rgb_r               :     std_logic_vector(3*NUM_RGB_BITS-1 downto 0);

  component sync
    generic (
      FREQ                            :     natural := 50_000;  -- master clock frequency (in KHz)
      PERIOD                          :     natural := 32;  -- period of sync pulse (in us)
      START                           :     natural := 26;  -- time sync pulse starts within the period (in us)
      WIDTH                           :     natural := 4;  -- width of sync pulse (in us)
      VISIBLE                         :     natural := 1024  -- number of visible pixels/line or lines/frame
      );
    port (
      rst                             : in  std_logic;  -- reset
      clk                             : in  std_logic;  -- master clock
      cke                             : in  std_logic;  -- clock-enable
      sync_n                          : out std_logic;  -- sync pulse
      gate                            : out std_logic;  -- single-clock pulse at start of sync pulse
      blank                           : out std_logic;  -- blanking signal
      cnt                             : out unsigned(15 downto 0)  -- output the timing counter value
      );
  end component sync;

begin

  -- clock divider for reducing the pixel clock rate
  process(clk, rst)
  begin
    if rst = YES then
      clk_div_cnt   <= (others => '0');
      cke           <= YES;
    elsif rising_edge(clk) then
      if clk_div_cnt = CLK_DIV-1 then
        clk_div_cnt <= (others => '0');
        cke         <= YES;
      else
        clk_div_cnt <= clk_div_cnt + 1;
        cke         <= NO;
      end if;
    end if;
  end process;

  -- form read pixel signal
  rd <= rd_x and cke;

  -- the clock enable for the vertical sync module is also combined with a gate signal
  -- that is generated at the end of every scanline by the horizontal sync module
  cke_v_gate <= cke and v_gate;

  -- generate the horizontal and vertical sync pulses for FIT_TO_SCREEN mode
  gen_syncs_fit : if FIT_TO_SCREEN = true generate
    hsync       : sync
      generic map (
        FREQ    => FREQ / CLK_DIV,      -- master pixel-clock frequency
        PERIOD  => HSYNC_PERIOD_F,      -- scanline period (32 us)
        START   => HSYNC_START_F,       -- start of horizontal sync pulse in scan line
        WIDTH   => HSYNC_WIDTH_F,       -- width of horizontal sync pulse
        VISIBLE => PIXELS_PER_LINE
        )
      port map (
        rst     => rst,
        clk     => clk,                 -- master clock
        cke     => cke,                 -- a new pixel is output whenever the clock is enabled
        sync_n  => hsync_x(1),          -- send pulse through delay line
        gate    => v_gate,              -- send gate signal to increment vertical sync pulse generator once per scan line
        blank   => h_blank,             -- blanking signal within a scan line
        cnt     => pixel_cnt            -- current pixel within the scan line
        );

    vsync : sync
      generic map (
        FREQ    => HSYNC_FREQ_F,        -- scanline frequency
        PERIOD  => VSYNC_PERIOD_F,      -- image frame period
        START   => VSYNC_START_F,       -- start of vertical sync pulse in frame
        WIDTH   => VSYNC_WIDTH_F,       -- width of vertical sync pulse
        VISIBLE => LINES_PER_FRAME
        )
      port map (
        rst     => rst,
        clk     => clk,                 -- master clock
        cke     => cke_v_gate,          -- enable clock once per horizontal scan line
        sync_n  => vsync_n,             -- send pulse through delay line
        gate    => eof_x,               -- indicate the end of a complete frame
        blank   => v_blank,             -- blanking signal within a frame
        cnt     => line_cnt             -- current scan line within a frame
        );
  end generate;

  -- generate the horizontal and vertical sync pulses for 31 KHz horizontal, 60 Hz vertical screen refresh
  gen_syncs_nofit : if FIT_TO_SCREEN = false generate
    hsync         : sync
      generic map (
        FREQ    => FREQ / CLK_DIV,      -- master pixel-clock frequency
        PERIOD  => HSYNC_PERIOD,        -- scanline period (32 us)
        START   => HSYNC_START,         -- start of horizontal sync pulse in scan line
        WIDTH   => HSYNC_WIDTH,         -- width of horizontal sync pulse
        VISIBLE => PIXELS_PER_LINE
        )
      port map (
        rst     => rst,
        clk     => clk,                 -- master clock
        cke     => cke,                 -- clock always enabled so there is a new pixel output on every clock pulse
        sync_n  => hsync_x(1),          -- send pulse through delay line
        gate    => v_gate,              -- send gate signal to increment vertical sync pulse generator once per scan line
        blank   => h_blank,             -- blanking signal within a scan line
        cnt     => pixel_cnt            -- current pixel within the scan line
        );

    vsync : sync
      generic map (
        FREQ    => HSYNC_FREQ,          -- scanline frequency (KHz)
        PERIOD  => VSYNC_PERIOD,        -- image frame period
        START   => VSYNC_START,         -- start of vertical sync pulse in frame
        WIDTH   => VSYNC_WIDTH,         -- width of vertical sync pulse
        VISIBLE => LINES_PER_FRAME
        )
      port map (
        rst     => rst,
        clk     => clk,                 -- master clock
        cke     => cke_v_gate,          -- enable clock once per horizontal scan line
        sync_n  => vsync_n,             -- send pulse through delay line
        gate    => eof_x,               -- indicate the end of a complete frame
        blank   => v_blank,             -- blanking signal within a frame
        cnt     => line_cnt             -- current scan line within a frame
        );
  end generate;

  eof_i    <= eof_x and not eof_r;      -- shorten end-of-frame signal to a single clock cycle
  eof      <= eof_i;

  visible    <= h_blank nor v_blank;    -- pixels are visible when horiz. & vertical blank are inactive 
  blank_x(1) <= not visible;            -- send blanking signal through delay line

  -- pass the horiz. and vert. syncs and blanking signal through delay lines to compensate for the
  -- processing delays incurred by the pixel data
  hsync_x(hsync_x'high downto 2) <= hsync_r(hsync_r'high-1 downto 1);
  hsync_n                        <= hsync_r(hsync_r'high);
  blank_x(blank_x'high downto 2) <= blank_r(blank_r'high-1 downto 1);
  blank                          <= blank_r(blank_r'high);

  -- get the current pixel from the word of pixel data or read more pixel data from the buffer
  get_pixel : process(visible, pixel_data_in, pixel_data_r, rd_r, pixel_cnt)
  begin
    rd_x <= NO;                         -- by default, don't read next word of pixel data from the buffer

    -- shift pixel data depending on its width so the next pixel is in the LSBs of the pixel data shift register
    case PIXEL_WIDTH is
      when 1      =>                    -- 1-bit pixels, 16 per pixel data word
        if (visible = YES) and (pixel_cnt(3 downto 0) = 0) then
          rd_x       <= YES;            -- read new pixel data from buffer every 16 clocks during visible portion of scan line
        end if;
        pixel_data_x <= "0" & pixel_data_r(15 downto 1);  -- left-shift pixel data to move next pixel to LSB
      when 2      =>                    -- 2-bit pixels, 8 per pixel data word
        if (visible = YES) and (pixel_cnt(2 downto 0) = 0) then
          rd_x       <= YES;            -- read new pixel data from buffer every 8 clocks during visible portion of scan line
        end if;
        pixel_data_x <= "00" & pixel_data_r(15 downto 2);  -- left-shift pixel data to move next pixel to LSB 
      when 4      =>                    -- 4-bit pixels, 4 per pixel data word
        if (visible = YES) and (pixel_cnt(1 downto 0) = 0) then
          rd_x       <= YES;            -- read new pixel data from buffer every 4 clocks during visible portion of scan line
        end if;
        pixel_data_x <= "0000" & pixel_data_r(15 downto 4);  -- left-shift pixel data to move next pixel to LSB 
      when 8      =>                    -- 8-bit pixels, 2 per pixel data word
        if (visible = YES) and (pixel_cnt(0 downto 0) = 0) then
          rd_x       <= YES;            -- read new pixel data from buffer every 2 clocks during visible portion of scan line
        end if;
        pixel_data_x <= "00000000" & pixel_data_r(15 downto 8);  -- left-shift pixel data to move next pixel to LSB 
      when others =>                    -- any other width, then 1 per pixel data word
        if (visible = YES) then
          rd_x       <= YES;            -- read new pixel data from buffer every clock during visible portion of scan line
        end if;
        pixel_data_x <= pixel_data_r;
    end case;

    -- store the pixel data from the buffer instead of shifting the pixel data
    -- if a read operation was initiated in the previous cycle.
    if rd_r = YES then
      pixel_data_x <= pixel_data_in;
    end if;

    -- the current pixel is in the lower bits of the pixel data shift register
    pixel <= pixel_data_r(pixel'range);
  end process get_pixel;

  -- map the current pixel to RGB values
  map_pixel : process(pixel, rgb_r, blank_r)
  begin
    if NUM_RGB_BITS=2 then
    case PIXEL_WIDTH is
      when 1          =>                -- 1-bit pixels map to black or white
                          rgb_x <= (others => pixel(0));
      when 2          =>                -- 2-bit pixels map to black, 2/3 gray, 1/3 gray, and white
                          rgb_x <= pixel(1 downto 0) & pixel(1 downto 0) & pixel(1 downto 0);
      when 4          =>                -- 4-bit pixels map to 8 colors (ignore MSB)
                          rgb_x <= pixel(2) & pixel(2) & pixel(1) & pixel(1) & pixel(0) & pixel(0);
      when 8          =>                -- 8-bit pixels map directly to RGB values
        rgb_x <= pixel(7 downto 6) & pixel(4 downto 1);
      when others     =>                -- 16-bit pixels maps directly to RGB values
        rgb_x <= pixel(8) & pixel(7) & pixel(5) & pixel(4) & pixel(2) & pixel(1);
    end case;
    else -- NUM_RGB_BITS=3
    case PIXEL_WIDTH is
      when 1          =>                -- 1-bit pixels map to black or white
                          rgb_x <= (others => pixel(0));
      when 2          =>                -- 2-bit pixels map to black, 5/7 gray, 3/7 gray, and  1/7 gray
                          rgb_x <= pixel(1 downto 0) & '0' & pixel(1 downto 0) & '0' & pixel(1 downto 0) & '0';
      when 4          =>                -- 4-bit pixels map to 8 colors (ignore MSB)
                          rgb_x <= pixel(2) & pixel(2) & pixel(2) & pixel(1) & pixel(1) & pixel(1) & pixel(0) & pixel(0) & pixel(0);
      when 8          =>                -- 8-bit pixels map to RGB with reduced resolution in green component
        rgb_x <= pixel(7 downto 5) & pixel(4 downto 3) & '0' & pixel(2 downto 0);
      when others     =>                -- 16-bit pixels map directly to RGB values
        rgb_x <= pixel(8 downto 0);
    end case;
    end if;

    -- just blank the pixel if not in the visible region of the screen
    if blank_r(blank_r'high-1) = YES then
      rgb_x <= (others => '0');
    end if;

    -- break the pixel into its red, green and blue components
    r <= rgb_r(3*NUM_RGB_BITS-1 downto 2*NUM_RGB_BITS);
    g <= rgb_r(2*NUM_RGB_BITS-1 downto NUM_RGB_BITS);
    b <= rgb_r(NUM_RGB_BITS-1 downto 0);
  end process map_pixel;

-- update registers
  update : process(rst, clk)
  begin
    if rst = YES then
      eof_r          <= '0';
      rd_r           <= NO;
      hsync_r        <= (others => '1');
      blank_r        <= (others => '0');
      pixel_data_r   <= (others => '0');
      rgb_r          <= (others => '0');
    elsif rising_edge(clk) then
      eof_r          <= eof_x;          -- end-of-frame signal goes at full clock rate to external system
      if cke = YES then
        rd_r         <= rd_x;
        hsync_r      <= hsync_x;
        blank_r      <= blank_x;
        pixel_data_r <= pixel_data_x;
        rgb_r        <= rgb_x;
      end if;
    end if;
  end process update;

end architecture vga_arch;

library IEEE, unisim;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use unisim.vcomponents.all;
use work.common.all;

-- Generate a sync pulse within a waveform PERIOD.
-- Also output the value of the counter used for timing so that
-- it can be used in generating an address for a video RAM.

entity sync is
  generic (
    FREQ    :     natural := 100_000;    -- master clock frequency (in KHz)
    PERIOD  :     natural := 32;        -- period of sync pulse (in us)
    START   :     natural := 26;        -- time sync pulse starts within the period (in us)
    WIDTH   :     natural := 4;         -- width of sync pulse (in us)
    VISIBLE :     natural := 1024       -- number of visible pixels/line or lines/frame
    );
  port (
    rst     : in  std_logic;            -- reset
    clk     : in  std_logic;            -- master clock
    cke     : in  std_logic;            -- clock-enable
    sync_n  : out std_logic;            -- sync pulse
    gate    : out std_logic;            -- single-clock pulse at start of sync pulse
    blank   : out std_logic;            -- blanking signal
    cnt     : out unsigned(15 downto 0)  -- output the timing counter value
    );
end entity sync;

architecture sync_arch of sync is
  constant NORM             : natural := 1000;  -- normalization factor for us * KHz
  constant CYC_PERIOD       : natural := (PERIOD * FREQ)/NORM;  -- sync wave PERIOD in clock cycles
  constant CYC_START        : natural := (START * FREQ)/NORM;  -- sync pulse START in cycles
  constant CYC_WIDTH        : natural := (WIDTH * FREQ)/NORM;  -- sync pulse WIDTH in cycles
  constant CYC_END          : natural := CYC_START + CYC_WIDTH;  -- sync pulse end in cycles
  signal   cnt_r, cnt_x     : unsigned(cnt'range);  -- counter for timing sync pulse waveform
  signal   sync_r, sync_x   : std_logic;  -- sync register
  signal   gate_r, gate_x   : std_logic;  -- gate register
  signal   blank_r, blank_x : std_logic;  -- blank register
begin

-- increment counter and wrap around to zero at end of period
  cnt_x <= (others => '0') when cnt_r = CYC_PERIOD-1 else cnt_r+1;

-- generate sync pulse within waveform period
  sync_x <= LO when cnt_r = CYC_START-1 else
            HI when cnt_r = CYC_END-1   else
            sync_r;
  sync_n <= sync_r;

-- generate gate signal at start of sync pulse
  gate_x <= YES when cnt_r = CYC_START-1 else NO;
  gate   <= gate_r;

-- generate blank signal after initial visible period
  blank_x <= YES when cnt_r = VISIBLE-1    else
             NO  when cnt_r = CYC_PERIOD-1 else
             blank_r;
  blank   <= blank_r;

-- output counter value
  cnt <= cnt_r;

-- update counter and registers
  update : process(rst, clk)
  begin
    if rst = YES then
-- ????? SIMULATION PLUG
-- swap this next to place in non-blank cycle
-- this allows data to appear in simulation, and only produces a momentary
-- glitch in real hardware
--      cnt_r     <= (others => '0');
      cnt_r     <= "0000000011111111";
      sync_r    <= HI;
      gate_r    <= NO;
-- ????? SIMULATION PLUG
-- swap this next to place in non-blank cycle
-- this allows data to appear in simulation, and only produces a momentary
-- glitch in real hardware
--      blank_r   <= YES;
      blank_r   <= NO;
    elsif rising_edge(clk) then
      if cke = YES then
        cnt_r   <= cnt_x;
        sync_r  <= sync_x;
        gate_r  <= gate_x;
        blank_r <= blank_x;
      end if;
    end if;
  end process update;

end architecture sync_arch;

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.