--------------------------------------------------------------------------- -- (c) 2019 mark watson -- I am happy for anyone to use this for non-commercial use. -- If my vhdl files are used commercially or otherwise sold, -- please contact me for explicit permission at scrameta (gmail). -- This applies for source and binary form and derived works. --------------------------------------------------------------------------- LIBRARY ieee; USE ieee.std_logic_1164.all; use ieee.numeric_std.all; USE IEEE.STD_LOGIC_UNSIGNED.ALL; use IEEE.STD_LOGIC_MISC.all; ENTITY crtc IS PORT ( clk_pixel : in std_logic; reset_n : in std_logic; -- inputs resync_start_frame : in std_logic; -- outputs hsync : out std_logic; vsync : out std_logic; blank : out std_logic; inc_line : out std_logic; inc_frame : out std_logic; field2 : out std_logic; -- field 2 is lower clock_select : out std_logic_vector(2 downto 0); video_id_code : out std_logic_vector(7 downto 0); scaler_select : out std_logic; -- to set up params scl_in : in std_logic; sda_in : in std_logic; scl_wen : out std_logic; sda_wen : out std_logic ); END crtc; --TODO: start_frame, clock_select and sda/scl ARCHITECTURE vhdl OF crtc IS -- parameters signal param_h_syncLen_reg : unsigned(11 downto 0); --sync right at start signal param_h_preActiveLen_reg : unsigned(11 downto 0); --blank after sync signal param_h_activeLen_reg : unsigned(11 downto 0); --then active signal param_h_postActiveLen_reg : unsigned(11 downto 0); --blank after active signal param_v_syncLen_reg : unsigned(11 downto 0); --sync right at start signal param_v_preActiveLen_reg : unsigned(11 downto 0); --blank after sync signal param_v_activeLen_reg : unsigned(11 downto 0); --then active signal param_v_postActiveLen_reg : unsigned(11 downto 0); --blank after active signal param_interlace_reg : std_logic; signal param_interlaceDelayLen_reg : unsigned(11 downto 0); --delay sync by half a len signal param_video_id_code_reg : std_logic_vector(7 downto 0); -- CEA 864 id code signal param_scalar_select_reg : std_logic; signal param_clock_select_reg : std_logic_vector(2 downto 0); -- which clock to use signal param_active_reg : std_logic; signal params : std_logic_vector((13*12)-1 downto 0); -- state signal h_state_next: std_logic_vector(2 downto 0); signal h_state_reg : std_logic_vector(2 downto 0); signal v_state_next: std_logic_vector(3 downto 0); signal v_state_reg : std_logic_vector(3 downto 0); constant H_STATE_IDLE : std_logic_vector(2 downto 0) := "000"; constant H_STATE_SYNC : std_logic_vector(2 downto 0) := "001"; constant H_STATE_PREBLANK : std_logic_vector(2 downto 0) := "010"; constant H_STATE_ACTIVE : std_logic_vector(2 downto 0) := "011"; constant H_STATE_POSTBLANK : std_logic_vector(2 downto 0) := "100"; constant V_STATE_IDLE : std_logic_vector(3 downto 0) := "0000"; constant V_STATE_SYNC : std_logic_vector(3 downto 0) := "0001"; constant V_STATE_PREBLANK : std_logic_vector(3 downto 0) := "0010"; constant V_STATE_ACTIVE : std_logic_vector(3 downto 0) := "0011"; constant V_STATE_POSTBLANK : std_logic_vector(3 downto 0) := "0100"; constant V_STATE_PRESYNC_FIELD2 : std_logic_vector(3 downto 0) := "0101"; constant V_STATE_SYNC_FIELD2 : std_logic_vector(3 downto 0) := "0110"; constant V_STATE_POSTSYNC_FIELD2 : std_logic_vector(3 downto 0) := "0111"; constant V_STATE_PREBLANK_FIELD2 : std_logic_vector(3 downto 0) := "1000"; constant V_STATE_ACTIVE_FIELD2 : std_logic_vector(3 downto 0) := "1001"; constant V_STATE_POSTBLANK_FIELD2 : std_logic_vector(3 downto 0) := "1010"; signal h_delay_next : unsigned(11 downto 0); signal h_delay_reg : unsigned(11 downto 0); signal v_delay_next : unsigned(11 downto 0); signal v_delay_reg : unsigned(11 downto 0); signal v_delayh_next : unsigned(11 downto 0); signal v_delayh_reg : unsigned(11 downto 0); signal v_sync_inc : std_logic; signal resync : std_logic; signal h_blank_next : std_logic; signal h_sync_next : std_logic; signal v_blank_next : std_logic; signal v_sync_next : std_logic; signal blank_next : std_logic; signal h_blank_reg : std_logic; signal h_sync_reg : std_logic; signal v_blank_reg : std_logic; signal v_sync_reg : std_logic; signal blank_reg : std_logic; BEGIN --registers process(clk_pixel,reset_n) begin if (reset_n='0') then h_state_reg <= H_STATE_IDLE; v_state_reg <= V_STATE_IDLE; h_delay_reg <= (others=>'0'); v_delay_reg <= (others=>'0'); v_delayh_reg <= (others=>'0'); h_blank_reg <= '1'; h_sync_reg <= '0'; v_blank_reg <= '1'; v_sync_reg <= '0'; blank_reg <= '1'; elsif (clk_pixel='1' and clk_pixel'event) then h_state_reg <= h_state_next; v_state_reg <= v_state_next; h_delay_reg <= h_delay_next; v_delay_reg <= v_delay_next; v_delayh_reg <= v_delayh_next; h_blank_reg <= h_blank_next; h_sync_reg <= h_sync_next; v_blank_reg <= v_blank_next; v_sync_reg <= v_sync_next; blank_reg <= blank_next; end if; end process; -- parameters from i2c (todo) -- -- 720p@50 (temp until we get crtc plumbed to firmware) -- param_h_syncLen_next <= to_unsigned(40,12); -- param_h_preActiveLen_next <= to_unsigned(220,12); -- param_h_activeLen_next <= to_unsigned(1280,12); -- param_h_postActiveLen_next <= to_unsigned(440,12); -- param_v_syncLen_next <= to_unsigned(5,12); -- param_v_preActiveLen_next <= to_unsigned(20,12); -- param_v_activeLen_next <= to_unsigned(720,12); -- param_v_postActiveLen_next <= to_unsigned(5,12); -- param_interlace_next <= '0'; -- param_interlaceDelayLen_next <= (others=>'0'); -- param_clock_select_next <= (others=>'0'); -- 1080i@50 (temp until we get crtc plumbed to firmware) -- param_h_syncLen_next <= to_unsigned(44,12); -- param_h_preActiveLen_next <= to_unsigned(148,12); -- param_h_activeLen_next <= to_unsigned(1920,12); -- param_h_postActiveLen_next <= to_unsigned(528,12); -- param_v_syncLen_next <= to_unsigned(5,12); -- param_v_preActiveLen_next <= to_unsigned(15,12); -- param_v_activeLen_next <= to_unsigned(540,12); -- param_v_postActiveLen_next <= to_unsigned(2,12); -- param_interlace_next <= '1'; -- param_interlaceDelayLen_next <= to_unsigned(1320,12); -- param_clock_select_next <= (others=>'0'); -- param_active_next <= '1'; -- params set from i2c i2cregs : entity work.I2C_regs generic map ( SLAVE_ADDR => "0000010", regs => 13, bits => 12 ) port map ( scl_in => scl_in, sda_in => sda_in, scl_wen => scl_wen, sda_wen => sda_wen, clk => clk_pixel, rst => not(reset_n), reg => params ); param_h_syncLen_reg <= unsigned(params(1*12-1 downto 0*12)); param_h_preActiveLen_reg <= unsigned(params(2*12-1 downto 1*12)); param_h_activeLen_reg <= unsigned(params(3*12-1 downto 2*12)); param_h_postActiveLen_reg <= unsigned(params(4*12-1 downto 3*12)); param_v_syncLen_reg <= unsigned(params(5*12-1 downto 4*12)); param_v_preActiveLen_reg <= unsigned(params(6*12-1 downto 5*12)); param_v_activeLen_reg <= unsigned(params(7*12-1 downto 6*12)); param_v_postActiveLen_reg <= unsigned(params(8*12-1 downto 7*12)); param_interlace_reg <= params(8*12); param_interlaceDelayLen_reg <= unsigned(params(10*12-1 downto 9*12)); param_video_id_code_reg <= params(10*12+8-1 downto 10*12); param_scalar_select_reg <= params(10*12+9-1); param_clock_select_reg <= params(11*12+3-1 downto 11*12); param_active_reg <= params(12*12); -- state machine -- horizontal process(h_state_reg,h_delay_reg,h_blank_reg,h_sync_reg, resync_start_frame, param_active_reg, param_h_syncLen_reg, param_h_preActiveLen_reg, param_h_activeLen_reg, param_h_postActiveLen_reg ) variable delay_over : std_logic; begin h_state_next <= h_state_reg; h_delay_next <= h_delay_reg; h_blank_next <= h_blank_reg; h_sync_next <= h_sync_reg; v_sync_inc <= '0'; inc_line <= '0'; h_delay_next <= h_delay_reg - 1; delay_over := '0'; if (h_delay_reg="00000000001") then delay_over := '1'; end if; case h_state_reg is when H_STATE_IDLE => h_blank_next <= '1'; h_sync_next <= '1'; if (param_active_reg='1') then h_state_next <= H_STATE_ACTIVE; h_delay_next <= param_h_activeLen_reg; h_sync_next <= '0'; h_blank_next <= '0'; end if; when H_STATE_SYNC => if (delay_over='1') then h_state_next <= H_STATE_PREBLANK; h_delay_next <= param_h_preActiveLen_reg; h_sync_next <= '0'; end if; when H_STATE_PREBLANK => if (delay_over='1') then h_state_next <= H_STATE_ACTIVE; h_delay_next <= param_h_activeLen_reg; h_blank_next <= '0'; inc_line <= '1'; end if; when H_STATE_ACTIVE => if (delay_over='1') then h_state_next <= H_STATE_POSTBLANK; h_delay_next <= param_h_postActiveLen_reg; h_blank_next <= '1'; end if; when H_STATE_POSTBLANK => if (delay_over='1') then h_state_next <= H_STATE_SYNC; h_delay_next <= param_h_syncLen_reg; h_sync_next <= '1'; v_sync_inc <= '1'; end if; when others => h_state_next <= H_STATE_IDLE; end case; if (param_active_reg='0' or resync_start_frame='1') then h_state_next <= H_STATE_IDLE; end if; end process; -- vertical process(v_state_reg,v_delay_reg,v_delayh_reg,v_blank_reg,v_sync_reg, v_sync_inc, resync_start_frame, param_active_reg, param_v_syncLen_reg, param_v_preActiveLen_reg, param_v_activeLen_reg, param_v_postActiveLen_reg, param_interlace_reg, param_interlaceDelayLen_reg ) variable delay_over : std_logic; variable delayh_over : std_logic; begin v_state_next <= v_state_reg; v_delay_next <= v_delay_reg; v_delayh_next <= v_delayh_reg; v_blank_next <= v_blank_reg; v_sync_next <= v_sync_reg; inc_frame <= '0'; field2 <= '0'; if (v_sync_inc = '1') then v_delay_next <= v_delay_reg - 1; end if; v_delayh_next <= v_delayh_reg - 1; delay_over := '0'; if (v_delay_reg="0000000001" and v_sync_inc='1') then delay_over := '1'; end if; delayh_over := '0'; if (v_delayh_reg="00000000001") then delayh_over := '1'; end if; case v_state_reg is when V_STATE_IDLE => v_blank_next <= '1'; v_sync_next <= '1'; if (param_active_reg='1') then v_state_next <= V_STATE_ACTIVE; v_delay_next <= param_v_activeLen_reg; v_sync_next <= '0'; v_blank_next <= '0'; inc_frame <= '1'; end if; when V_STATE_SYNC => if (delay_over='1') then v_state_next <= V_STATE_PREBLANK; v_delay_next <= param_v_preActiveLen_reg; v_sync_next <= '0'; end if; when V_STATE_PREBLANK => if (delay_over='1') then v_state_next <= V_STATE_ACTIVE; v_delay_next <= param_v_activeLen_reg; v_blank_next <= '0'; inc_frame <= '1'; end if; when V_STATE_ACTIVE => if (delay_over='1') then v_state_next <= V_STATE_POSTBLANK; v_delay_next <= param_v_postActiveLen_reg; v_blank_next <= '1'; end if; when V_STATE_POSTBLANK => if (delay_over='1') then if (param_interlace_reg='1') then v_state_next <= V_STATE_PRESYNC_FIELD2; v_delayh_next <= param_interlaceDelayLen_reg; else v_sync_next <= '1'; v_state_next <= V_STATE_SYNC; v_delay_next <= param_v_syncLen_reg; end if; end if; when V_STATE_PRESYNC_FIELD2 => if (delayh_over='1') then v_sync_next <= '1'; v_state_next <= V_STATE_SYNC_FIELD2; v_delay_next <= param_v_syncLen_reg; end if; when V_STATE_SYNC_FIELD2 => if (delay_over='1') then v_state_next <= V_STATE_POSTSYNC_FIELD2; v_delayh_next <= param_interlaceDelayLen_reg; end if; when V_STATE_POSTSYNC_FIELD2 => if (delayh_over='1') then v_sync_next <= '0'; v_state_next <= V_STATE_PREBLANK_FIELD2; v_delay_next <= param_v_preActiveLen_reg+1; end if; when V_STATE_PREBLANK_FIELD2 => if (delay_over='1') then v_state_next <= V_STATE_ACTIVE_FIELD2; v_delay_next <= param_v_activeLen_reg; v_blank_next <= '0'; inc_frame <= '1'; field2 <= '1'; end if; when V_STATE_ACTIVE_FIELD2 => if (delay_over='1') then v_state_next <= V_STATE_POSTBLANK_FIELD2; v_delay_next <= param_v_postActiveLen_reg; v_blank_next <= '1'; end if; when V_STATE_POSTBLANK_FIELD2 => if (delay_over='1') then v_sync_next <= '1'; v_state_next <= V_STATE_SYNC; v_delay_next <= param_v_syncLen_reg; end if; when others => v_state_next <= V_STATE_IDLE; end case; if (param_active_reg='0' or resync_start_frame='1') then v_state_next <= V_STATE_IDLE; end if; end process; blank_next <= h_blank_next or v_blank_next; -- outputs blank <= blank_reg; hsync <= h_sync_reg; vsync <= v_sync_reg; clock_select <= param_clock_select_reg(2 downto 0); video_id_code <= param_video_id_code_reg; scaler_select <= param_scalar_select_reg; END vhdl;