RGBtoHDMI/vhdl_RGB_12bit_old_V8/RGBtoHDMI.vhdl

408 wiersze
16 KiB
VHDL

----------------------------------------------------------------------------------
-- Engineer: David Banks
--
-- Create Date: 15/7/2018
-- Module Name: RGBtoHDMI CPLD
-- Project Name: RGBtoHDMI
-- Target Devices: XC9572XL
--
-- Version: 1.0
--
----------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity RGBtoHDMI is
Port (
-- From RGB Connector
R3_I: in std_logic;
G3_I: in std_logic;
B3_I: in std_logic;
R2_I: in std_logic;
G2_I: in std_logic;
B2_I: in std_logic;
R1_I: in std_logic;
G1_I: in std_logic;
B1_I: in std_logic;
R0_I: in std_logic;
G0_I: in std_logic;
B0_I: in std_logic;
csync_I: in std_logic;
vsync_I: in std_logic;
analog: inout std_logic;
-- From Pi
clk: in std_logic;
sp_clk: in std_logic;
sp_clken: in std_logic;
sp_data: in std_logic;
-- To PI GPIO
quad: out std_logic_vector(11 downto 0);
psync: out std_logic;
csync: out std_logic;
-- User interface
version: in std_logic
);
end RGBtoHDMI;
architecture Behavorial of RGBtoHDMI is
subtype counter_type is unsigned(6 downto 0);
-- Version number: Design_Major_Minor
-- Design: 0 = BBC CPLD
-- 1 = Alternative CPLD
-- 2 = Atom CPLD
-- 3 = six bit CPLD (if required);
-- 4 = RGB CPLD (TTL)
-- C = RGB CPLD (Analog)
constant VERSION_NUM_RGB_TTL : std_logic_vector(11 downto 0) := x"485";
constant VERSION_NUM_RGB_ANALOG : std_logic_vector(11 downto 0) := x"C85";
-- Sampling points
constant INIT_SAMPLING_POINTS : std_logic_vector(25 downto 0) := "00000000011011011011011011";
signal shift_R : std_logic_vector(3 downto 0);
signal shift_G : std_logic_vector(3 downto 0);
signal shift_B : std_logic_vector(3 downto 0);
signal csync1 : std_logic;
signal csync2 : std_logic;
signal last : std_logic;
signal csync_counter : unsigned(1 downto 0);
-- The sampling counter runs at 96MHz
-- - In modes 0..6 it is 6x the pixel clock
-- - In mode 7 it is 8x the pixel clock
--
-- It serves several purposes:
-- 1. Counts the 12us between the rising edge of nCSYNC and the first pixel
-- 2. Counts within each pixel (bits 0, 1, 2)
-- 3. Counts counts pixels within a quad pixel (bits 3 and 4)
-- 4. Handles double buffering of alternative quad pixels (bit 5)
--
-- At the moment we don't count pixels with the line, the Pi does that
signal counter : counter_type;
-- Sample point register;
--
-- In Mode 7 each pixel lasts 8 clocks (96MHz / 12MHz). The original
-- pixel clock is a regenerated 6Mhz clock, and both edges are used.
-- Due to the way it is generated, there are three distinct phases,
-- each with different rising/falling edge speeds, hence six sampling
-- points are used.
--
-- In Modes 0..6 each pixel lasts 6 clocks (96MHz / 16MHz). The original
-- pixel clock is a clean 16Mhz clock, so only one sample point is needed.
-- To achieve this, all six values are set to be the same. This minimises
-- the logic in the CPLD.
signal sp_reg : std_logic_vector(25 downto 0) := INIT_SAMPLING_POINTS;
-- Break out of sp_reg
signal invert : std_logic;
signal rate : std_logic_vector(1 downto 0);
signal delay : unsigned(1 downto 0);
signal half : std_logic;
signal offset_A : std_logic_vector(2 downto 0);
signal offset_B : std_logic_vector(2 downto 0);
signal offset_C : std_logic_vector(2 downto 0);
signal offset_D : std_logic_vector(2 downto 0);
signal offset_E : std_logic_vector(2 downto 0);
signal offset_F : std_logic_vector(2 downto 0);
-- Pipelined offset mux output
signal offset : std_logic_vector(2 downto 0);
-- Index to cycle through offsets A..F
signal index : std_logic_vector(2 downto 0);
-- Sample pixel on next clock; pipelined to reduce the number of product terms
signal sample : std_logic;
-- New sample available, toggle psync on next cycle
signal toggle : std_logic;
-- RGB Input Mux
signal R3 :std_logic;
signal G3 :std_logic;
signal B3 :std_logic;
signal R2 :std_logic;
signal G2 :std_logic;
signal B2 :std_logic;
signal divider : std_logic;
signal mux : std_logic;
signal mux_sync : std_logic;
signal psync_pulse : std_logic;
signal clamp_output : std_logic;
signal clamp_pulse : std_logic;
signal clamp_enable : std_logic;
signal swap_bits_R :std_logic;
signal swap_bits_G :std_logic;
signal swap_bits_B :std_logic;
begin
offset_A <= sp_reg(2 downto 0);
offset_B <= sp_reg(5 downto 3);
offset_C <= sp_reg(8 downto 6);
offset_D <= sp_reg(11 downto 9);
offset_E <= sp_reg(14 downto 12);
offset_F <= sp_reg(17 downto 15);
half <= sp_reg(18);
delay <= unsigned(sp_reg(20 downto 19));
rate <= sp_reg(22 downto 21);
invert <= sp_reg(23);
divider <= sp_reg(24);
mux <= sp_reg(25);
mux_sync <= vsync_I when mux = '1' and version = '1' else csync_I;
-- sp_data is overloaded as clamp on/off when rate = 00 or 01 and multiplex on/off when rate = 10 or 11
-- rate = 00 is 3 bit capture with sp_data = clamp on/off
-- rate = 01 is 6 bit capture with sp_data = clamp on/off
-- rate = 10 and sp_data = 0 is 6 bit capture with 3 bit to 4 level encoding (clamp not usable in 4 level mode)
-- rate = 10 and sp_data = 1 is 6 bit capture with multiplex enabled for 12 bit capture
-- rate = 11 and mux = 0 is 12 bit capture
-- rate = 11 and mux = 1 is 9 bit capture using vsync_I as replacement G1 bit (will work on 8 bit board)
swap_bits_G <= vsync_I when rate = "10" and sp_data = '0' else '0';
swap_bits_B <= B1_I when rate = "10" and sp_data = '0' else '0';
swap_bits_R <= R1_I when rate = "10" and sp_data = '0' else '0';
G3 <= G2_I when swap_bits_G = '1' else G3_I;
G2 <= G3_I when swap_bits_G = '1' else G2_I;
B3 <= B2_I when swap_bits_B = '1' else B3_I;
B2 <= B3_I when swap_bits_B = '1' else B2_I;
R3 <= R2_I when swap_bits_R = '1' else R3_I;
R2 <= R3_I when swap_bits_R = '1' else R2_I;
-- Shift the bits in LSB first
process(sp_clk)
begin
if rising_edge(sp_clk) then
if sp_clken = '1' then
sp_reg <= sp_data & sp_reg(sp_reg'left downto sp_reg'right + 1);
end if;
end if;
end process;
process(clk)
begin
if rising_edge(clk) then
-- synchronize CSYNC to the sampling clock
-- if link fitted sync is inverted. If +ve vsync connected to link & +ve hsync to S then generate -ve composite sync
csync1 <= mux_sync xor invert;
-- De-glitch CSYNC
-- csync1 is the possibly glitchy input
-- csync2 is the filtered output
if csync1 = csync2 then
-- output same as input, reset the counter
csync_counter <= to_unsigned(0, csync_counter'length);
else
-- output different to input
csync_counter <= csync_counter + 1;
-- if the difference lasts for N-1 cycles, update the output
if csync_counter = 3 then
csync2 <= csync1;
end if;
end if;
-- Counter is used to find sampling point for first pixel
last <= csync2;
-- reset counter on the rising edge of csync
if last = '0' and csync2 = '1' then
counter(6 downto 3) <= "10" & delay;
if half = '1' then
counter(2 downto 0) <= "000";
elsif divider = '1' then
counter(2 downto 0) <= "100";
else
counter(2 downto 0) <= "011";
end if;
elsif divider = '1' or counter(2 downto 0) /= 5 then
if counter(counter'left) = '1' then
counter <= counter + 1;
else
counter(counter'left - 1 downto 0) <= counter(counter'left - 1 downto 0) + 1;
end if;
else
if counter(counter'left) = '1' then
counter <= counter + 3;
else
counter(counter'left - 1 downto 0) <= counter(counter'left - 1 downto 0) + 3;
end if;
end if;
-- Sample point offset index
if counter(counter'left) = '1' then
index <= "000";
else
-- so index offset changes at the same time counter wraps 7->0
-- so index offset changes at the same time counter wraps ->0
if (divider = '0' and counter(2 downto 0) = 4) or (divider = '1' and counter(2 downto 0) = 6) then
case index is
when "000" =>
index <= "001";
when "001" =>
index <= "010";
when "010" =>
index <= "011";
when "011" =>
index <= "100";
when "100" =>
index <= "101";
when others =>
index <= "000";
end case;
end if;
end if;
-- Sample point offset
case index is
when "000" =>
offset <= offset_B;
when "001" =>
offset <= offset_C;
when "010" =>
offset <= offset_D;
when "011" =>
offset <= offset_E;
when "100" =>
offset <= offset_F;
when others =>
offset <= offset_A;
end case;
-- sample/shift control
if counter(counter'left) = '0' and counter(2 downto 0) = unsigned(offset) then
sample <= '1';
else
sample <= '0';
end if;
if sample = '1' then
if rate = "11" and sp_data = '1' then
shift_R <= R1_I & G2_I & B3_I & B0_I; -- 12 bpp
elsif rate = "11" and sp_data = '0' then
shift_R <= R1_I & G2_I & B3_I & B3_I; -- 9 bpp
elsif rate = "10" and sp_data = '1' then
shift_R <= shift_B(3) & G2_I & B3_I & shift_B(0); -- 6x2 multiplex 12 bpp
elsif rate = "10" and sp_data = '0' then
shift_R <= R2 & R3 & shift_R(3 downto 2); -- 6 bpp 4 level
elsif rate = "01" then
shift_R <= R2_I & R3_I & shift_R(3 downto 2); -- 6 bpp
else
shift_R <= R3_I & shift_R(3 downto 1); -- 3 bpp
end if;
end if;
-- G Sample/shift register
if sample = '1' then
if rate = "11" and sp_data = '1' then
shift_G <= R2_I & G3_I & G0_I & B1_I; -- 12 bpp
elsif rate = "11" and sp_data = '0' then
shift_G <= R2_I & G3_I & G3_I & B1_I; -- 9 bpp
elsif rate = "10" and sp_data = '1' then
shift_G <= R2_I & G3_I & shift_R(2) & shift_R(1); -- 6x2 multiplex 12 bpp
elsif rate = "10" and sp_data = '0' then
shift_G <= G2 & G3 & shift_G(3 downto 2); -- 6 bpp 4 level
elsif rate = "01" then
shift_G <= G2_I & G3_I & shift_G(3 downto 2); -- 6 bpp
else
shift_G <= G3_I & shift_G(3 downto 1); -- 3 bpp
end if;
end if;
-- B Sample/shift register
if sample = '1' then
if rate = "11" and sp_data = '1' then
shift_B <= R3_I & R0_I & G1_I & B2_I; -- 12 bpp
elsif rate = "11" and sp_data = '0' then
shift_B <= R3_I & R3_I & vsync_I & B2_I; -- 9 bpp with G1 on vsync_I
elsif rate = "10" and sp_data = '1' then
shift_B <= R3_I & shift_G(3) & shift_G(2) & B2_I; -- 6x2 multiplex 12 bpp
elsif rate = "10" and sp_data = '0' then
shift_B <= B2 & B3 & shift_B(3 downto 2); -- 6 bpp 4 level
elsif rate = "01" then
shift_B <= B2_I & B3_I & shift_B(3 downto 2); -- 6 bpp
else
shift_B <= B3_I & shift_B(3 downto 1); -- 3 bpp
end if;
end if;
-- Pipeline when to update the quad
if counter(counter'left) = '0' and (
(rate = "00" and counter(4 downto 0) = 0) or -- single
(rate = "01" and counter(3 downto 0) = 0) or -- double
(rate = "10" and counter(3 downto 0) = 0) or -- double
(rate = "11" and counter(2 downto 0) = 0) ) then -- quadruple
-- toggle is asserted in cycle 1
toggle <= '1';
else
toggle <= '0';
end if;
-- Output quad register
if version = '0' then
if analog = '1' then
quad <= VERSION_NUM_RGB_ANALOG;
else
quad <= VERSION_NUM_RGB_TTL;
end if;
elsif counter(counter'left) = '1' then
quad <= (others => '0');
elsif toggle = '1' then
-- quad changes at the start of cycle 2
quad(11) <= shift_B(3);
quad(10) <= shift_G(3);
quad(9) <= shift_R(3);
quad(8) <= shift_B(2);
quad(7) <= shift_G(2);
quad(6) <= shift_R(2);
quad(5) <= shift_B(1);
quad(4) <= shift_G(1);
quad(3) <= shift_R(1);
quad(2) <= shift_B(0);
quad(1) <= shift_G(0);
quad(0) <= shift_R(0);
end if;
-- Output a skewed version of psync
if version = '0' then
psync <= vsync_I;
elsif counter(counter'left) = '1' then
psync <= '0';
elsif counter(2 downto 0) = 2 then -- comparing with N gives N-1 cycles of skew
if rate = "00" then
psync <= counter(5); -- normal
elsif rate = "11" then
psync <= counter(3); -- quadruple
else
psync <= counter(4); -- double
end if;
end if;
end if;
end process;
csync <= csync2; -- output the registered version to save a macro-cell
-- csync2 is cleaned but delayed so OR with csync1 to remove delay on trailing edge of sync pulse
-- clamp not usable in 4 LEVEL mode (rate = 10) or 8/12 bit mode (rate = 11) so use as multiplex signal instead
clamp_pulse <= not(sample) when rate = "10" or rate = "11" else not(csync1 or csync2);
clamp_enable <= '1' when mux = '1' else version;
-- spdata is overloaded as clamp on/off
analog <= 'Z' when clamp_enable = '0' else clamp_pulse and sp_data;
end Behavorial;