📄 lancelot_vga.vhd
字号:
line_buffer_dma_empty <= line_buffer_wpbottom1;
line_buffer_wprst1 <= '0';
line_buffer_wprst2 <= line_end;
line_buffer_rprst1 <= '0';
line_buffer_rprst2 <= line_begin;
END IF;
END IF;
END PROCESS;
---------------------
-- colour Table Mux --
---------------------
PROCESS(colour_byte_sel_int, video_data)
BEGIN
CASE colour_byte_sel_int IS
WHEN 0 =>
colour_table_address <= video_data(7 DOWNTO 0);
WHEN 1 =>
colour_table_address <= video_data(15 DOWNTO 8);
WHEN 2 =>
colour_table_address <= video_data(23 DOWNTO 16);
WHEN 3 =>
colour_table_address <= video_data(31 DOWNTO 24);
WHEN others =>
colour_table_address <= (others => '0');
END CASE;
END PROCESS;
-- Pipeline signal colour_byte_sel, because the video data has a latency through the line buffer of 5 video clocks.
PROCESS(VIDEO_CLK, reset_int, colour_byte_sel)
BEGIN
IF reset_int = '1' THEN
colour_byte_sel_int <= 0;
colour_byte_sel3 <= 0;
colour_byte_sel2 <= 0;
colour_byte_sel1 <= 0;
colour_byte_sel0 <= 0;
ELSIF (VIDEO_CLK'event AND VIDEO_CLK = '1') THEN
colour_byte_sel_int <= colour_byte_sel3;
colour_byte_sel3 <= colour_byte_sel2;
colour_byte_sel2 <= colour_byte_sel1;
colour_byte_sel1 <= colour_byte_sel0;
colour_byte_sel0 <= colour_byte_sel;
END IF;
END PROCESS;
-------------------------
-- Line Buffer Select --
-------------------------
PROCESS(VIDEO_CLK, reset_int, line_buffer_toggle)
BEGIN
IF reset_int = '1' THEN
line_buffer_sel <= 0;
ELSIF (VIDEO_CLK'event AND VIDEO_CLK = '1') THEN
IF line_buffer_toggle = '1' THEN
IF line_buffer_sel = 0 THEN
line_buffer_sel <= 1;
ELSE
line_buffer_sel <= 0;
END IF;
END IF;
END IF;
END PROCESS;
-------------------
-- Pixel Counter --
-------------------
PROCESS(VIDEO_CLK, reset_int, pixel_counter_load, pixel_counter_zero)
VARIABLE pixel_counter : INTEGER RANGE 0 TO 1024;
BEGIN
IF reset_int = '1' THEN
pixel_counter := 0;
pixel_counter_zero <= '1';
ELSIF (VIDEO_CLK'event AND VIDEO_CLK = '1') THEN
IF pixel_counter_load = '1' THEN
pixel_counter := (pixel_counter_data - 1);
pixel_counter_zero <= '0' AFTER 5 ns;
ELSE
IF pixel_counter_zero = '0' THEN
pixel_counter := pixel_counter - 1;
IF pixel_counter = 0 THEN
pixel_counter_zero <= '1' AFTER 5 ns;
END IF;
END IF;
END IF;
END IF;
END PROCESS;
-------------------
-- Vsync Counter --
-------------------
PROCESS(VIDEO_CLK, reset_int, vsync_counter_reset, vsync_counter_inc)
BEGIN
IF reset_int = '1' THEN
vsync_counter <= 0;
ELSIF (VIDEO_CLK'event AND VIDEO_CLK = '1') THEN
IF vsync_counter_reset = '1' THEN
vsync_counter <= 0;
ELSE
IF vsync_counter_inc = '1' THEN
vsync_counter <= vsync_counter + 1 AFTER 5 ns;
END IF;
END IF;
END IF;
END PROCESS;
-----------------------
-- DMA State Machine --
-----------------------
-- This state machine has the task of requesting new video data from SDRAM.
-- If the dma line buffer is empty and the display is active, then it will request a new DMA transfer.
PROCESS(VIDEO_CLK, reset_int)
BEGIN
IF reset_int = '1' THEN
dma_current_state <= start_dma;
ELSIF (VIDEO_CLK'event AND VIDEO_CLK = '1') THEN
dma_current_state <= dma_next_state AFTER 5 ns;
END IF;
END PROCESS;
PROCESS(dma_current_state, control_reg, line_buffer_dma_empty, line_buffer_dma_full, vsync_counter, vsync_top1, vsync_top2, vsync_bottom)
BEGIN
video_dma_request <= '0';
video_dma_first <= '0';
CASE dma_current_state IS
-- Wait for start
WHEN start_dma =>
IF start_video = '1' THEN
dma_next_state <= clear_dma;
ELSE
dma_next_state <= start_dma;
END IF;
-- Clear irq
WHEN clear_dma =>
IF (line_buffer_dma_empty = '1' AND
vsync_counter > vsync_top1 AND
vsync_counter < vsync_bottom) THEN
dma_next_state <= set_dma;
ELSE
dma_next_state <= clear_dma;
END IF;
-- Set dma
WHEN set_dma =>
video_dma_request <= '1';
IF (vsync_counter = vsync_top2) THEN
video_dma_first <= '1';
END IF;
IF (line_buffer_dma_full = '1') THEN
dma_next_state <= clear_dma;
ELSE
dma_next_state <= set_dma;
END IF;
WHEN others =>
dma_next_state <= start_dma;
END CASE;
END PROCESS;
-- Pipeline vsync equations to increase design performance
PROCESS(VIDEO_CLK, reset_int)
BEGIN
IF reset_int = '1' THEN
vsync_top1 <= 0;
vsync_top2 <= 0;
vsync_bottom <= 0;
ELSIF (VIDEO_CLK'event AND VIDEO_CLK = '1') THEN
IF vsync_pulse_width > 0 THEN
vsync_top1 <= vsync_pulse_width + vsync_back_porch_width - 2;
vsync_bottom <= vsync_pulse_width + vsync_back_porch_width + vertical_resolution;
vsync_top2 <= vsync_pulse_width + vsync_back_porch_width - 1;
ELSE
vsync_top1 <= 0;
vsync_top2 <= 0;
vsync_bottom <= 0;
END IF;
END IF;
END PROCESS;
-------------------------
-- Hsync State Machine --
-------------------------
-- This state machine is responsable for hsync timing and display video data
-- First it generates the hsync pulse. If new video data is available and the display is active (depends on vsync),
-- the line buffers are swapped and it reads out the line buffer in four cycles until it is empty.
-- Else it will generate a blank line. The state machine repeats this process forever.
PROCESS(VIDEO_CLK, reset_int)
BEGIN
IF reset_int = '1' THEN
hsync_current_state <= hsync_reset;
ELSIF (VIDEO_CLK'event AND VIDEO_CLK = '1') THEN
hsync_current_state <= hsync_next_state AFTER 5 ns;
END IF;
END PROCESS;
PROCESS(hsync_current_state, control_reg, line_buffer_video_empty, pixel_counter_zero,
horizontal_resolution, blank_vs, hsync_pulse_width, line_buffer_dma_full, hsync_back_porch_width, hsync_front_porch_width)
BEGIN
line_buffer_rd <= '0';
vsync_counter_inc <= '0';
line_begin <= '0';
line_end <= '0';
line_buffer_toggle <= '0';
colour_byte_sel <= 0;
blank_hs <= '0';
pixel_counter_load <= '0';
pixel_counter_data <= 0;
hs_int <= '1';
line_missed <= '0';
CASE hsync_current_state IS
-- Wait for video starts
WHEN hsync_reset =>
IF start_video = '1' THEN
hsync_next_state <= hsync_pulse;
ELSE
hsync_next_state <= hsync_reset;
END IF;
-- Generate hsync pulse
WHEN hsync_pulse =>
vsync_counter_inc <= '1';
pixel_counter_data <= hsync_pulse_width;
pixel_counter_load <= '1';
hsync_next_state <= hsync_pulse1;
WHEN hsync_pulse1 =>
hs_int <= '0';
IF pixel_counter_zero = '1' THEN
hsync_next_state <= hsync_back_porch;
ELSE
hsync_next_state <= hsync_pulse1;
END IF;
-- Left border
WHEN hsync_back_porch =>
pixel_counter_data <= hsync_back_porch_width;
pixel_counter_load <= '1';
IF (force_blanking = '1' OR blank_vs = '0') THEN
hsync_next_state <= hsync_back_porch1;
-- Active video?
ELSE
IF line_buffer_dma_full = '1' THEN
line_buffer_toggle <= '1';
hsync_next_state <= hsync_back_porch1;
ELSE
hsync_next_state <= hsync_line_missed;
END IF;
END IF;
WHEN hsync_back_porch1 =>
IF pixel_counter_zero = '1' THEN
IF (blank_vs = '0' OR force_blanking = '1') THEN
hsync_next_state <= hsync_blank;
ELSE
line_begin <= '1';
hsync_next_state <= hsync_active_video1;
END IF;
ELSE
hsync_next_state <= hsync_back_porch1;
END IF;
-- First byte from line buffer
WHEN hsync_active_video1 =>
line_buffer_rd <= '1';
blank_hs <= '1';
colour_byte_sel <= 0;
hsync_next_state <= hsync_active_video2;
-- Second byte from line buffer
WHEN hsync_active_video2 =>
-- line_buffer_rd <= '1';
blank_hs <= '1';
colour_byte_sel <= 1;
hsync_next_state <= hsync_active_video3;
-- Third byte from line buffer
WHEN hsync_active_video3 =>
blank_hs <= '1';
colour_byte_sel <= 2;
hsync_next_state <= hsync_active_video4;
-- Fourth byte from line buffer
WHEN hsync_active_video4 =>
blank_hs <= '1';
colour_byte_sel <= 3;
-- line_buffer_rd <= '1';
IF line_buffer_video_empty = '1' THEN
hsync_next_state <= hsync_front_porch;
ELSE
hsync_next_state <= hsync_active_video1;
END IF;
-- Right border
WHEN hsync_front_porch =>
line_end <= '1';
pixel_counter_data <= hsync_front_porch_width;
pixel_counter_load <= '1';
hsync_next_state <= hsync_front_porch1;
WHEN hsync_front_porch1 =>
IF pixel_counter_zero = '1' THEN
hsync_next_state <= hsync_pulse;
ELSE
hsync_next_state <= hsync_front_porch1;
END IF;
-- Blank line
WHEN hsync_blank =>
pixel_counter_data <= horizontal_resolution - 1;
pixel_counter_load <= '1';
hsync_next_state <= hsync_blank1;
WHEN hsync_blank1 =>
IF pixel_counter_zero = '1' THEN
hsync_next_state <= hsync_front_porch;
ELSE
hsync_next_state <= hsync_blank1;
END IF;
-- New video data was not provided before the end of the active video line.
-- The state machine stays in this loop until the video was stopped.
WHEN hsync_line_missed =>
line_missed <= '1';
IF start_video = '0' THEN
hsync_next_state <= hsync_reset;
ELSE
hsync_next_state <= hsync_line_missed;
END IF;
WHEN others =>
hsync_next_state <= hsync_reset;
END CASE;
END PROCESS;
-------------------------
-- Vsync State Machine --
-------------------------
-- This state machine generates all the vsync timing.
-- It generates a vsync pulse. If the vertical line is in an active area of the screen, it releases the blank signal.
-- Also this process is looped forever.
PROCESS(VIDEO_CLK, reset_int)
BEGIN
IF reset_int = '1' THEN
vsync_current_state <= vsync_reset;
ELSIF (VIDEO_CLK'event AND VIDEO_CLK = '1') THEN
vsync_current_state <= vsync_next_state AFTER 5 ns;
END IF;
END PROCESS;
PROCESS(vsync_current_state, vsync_counter, control_reg,
vsync_pulse_width, vsync_back_porch_width, vertical_resolution, vsync_front_porch_width)
BEGIN
vs_int <= '1';
blank_vs <= '0';
vsync_counter_reset <= '0';
CASE vsync_current_state IS
-- Wait for video starts
WHEN vsync_reset =>
vsync_counter_reset <= '1';
IF start_video = '1' THEN
vsync_next_state <= vsync_pulse;
ELSE
vsync_next_state <= vsync_reset;
END IF;
-- Generate vsync pulse
WHEN vsync_pulse =>
vs_int <= '0';
IF vsync_counter > vsync_pulse_width THEN
vsync_next_state <= vsync_back_porch;
ELSE
vsync_next_state <= vsync_pulse;
END IF;
-- Top border
WHEN vsync_back_porch =>
IF (vsync_counter > vsync_pulse_width + vsync_back_porch_width) THEN
vsync_next_state <= vsync_active_video;
ELSE
vsync_next_state <= vsync_back_porch;
END IF;
-- Active video
WHEN vsync_active_video =>
blank_vs <= '1';
IF (vsync_counter > vsync_pulse_width + vsync_back_porch_width + vertical_resolution) THEN
vsync_next_state <= vsync_front_porch;
ELSE
vsync_next_state <= vsync_active_video;
END IF;
-- Bottom border
WHEN vsync_front_porch =>
IF (vsync_counter > vsync_pulse_width + vsync_back_porch_width + vertical_resolution + vsync_front_porch_width) THEN
vsync_counter_reset <= '1';
vsync_next_state <= vsync_pulse;
ELSE
vsync_next_state <= vsync_front_porch;
END IF;
WHEN others =>
vsync_next_state <= vsync_reset;
END CASE;
END PROCESS;
END behavior;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -