Ulx3s HDMI version

This commit is contained in:
lawrie 2019-12-29 10:44:29 +00:00
parent 402be08794
commit d2be2acb7f
9 changed files with 731 additions and 1 deletions

View File

@ -53,7 +53,11 @@ $(BUILDDIR)/apple1.json: $(SOURCEDIR)/apple1.v \
$(SOURCEDIR)/vga/font_rom.v \
$(SOURCEDIR)/ps2keyboard/debounce.v \
$(SOURCEDIR)/ps2keyboard/ps2keyboard.v \
$(SOURCEDIR)/boards/ulx3s/apple1_ecp5.v
$(SOURCEDIR)/boards/ulx3s/apple1_dvi.v \
$(SOURCEDIR)/boards/ulx3s/vga2dvid.v \
$(SOURCEDIR)/boards/ulx3s/clk_25_250_125_25.v \
$(SOURCEDIR)/boards/ulx3s/tmds_encoder.v \
$(SOURCEDIR)/boards/ulx3s/fake_differential.v
prog: dir $(BUILDDIR)/apple1.bit
ujprog $(filter-out $<,$^)

View File

@ -49,6 +49,7 @@ module apple1 #(
output vga_grn, // green VGA signal
output vga_blu, // blue VGA signal
input vga_cls, // clear screen button
output vga_blank, // set when vga not in active area
// Debugging ports
output [15:0] pc_monitor // spy for program counter / debugging
@ -223,6 +224,7 @@ module apple1 #(
.mode(font_mode),
.fg_colour(fg_colour),
.bg_colour(bg_colour),
.vga_blank(vga_blank),
.clr_screen(vga_cls)
);

View File

@ -0,0 +1,181 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: Apple 1 implementation for the Blackeice II ICE40HX8K +
//
// Author.....: Lawrie Griffiths and Alan Garfield
// Date.......: 31-3-2018
//
module apple1_top #(
parameter BASIC_FILENAME = "../../../roms/basic.hex",
parameter FONT_ROM_FILENAME = "../../../roms/vga_font_bitreversed.hex",
parameter RAM_FILENAME = "../../../roms/ram.hex",
parameter VRAM_FILENAME = "../../../roms/vga_vram.bin",
parameter WOZMON_ROM_FILENAME = "../../../roms/wozmon.hex"
) (
input clk_25mhz, // 25 MHz board clock
// I/O interface to computer
input uart_rx, // asynchronous serial data input from computer
output uart_tx, // asynchronous serial data output to computer
output uart_cts, // clear to send flag to computer - not used
// I/O interface to keyboard
input ps2_clk, // PS/2 keyboard serial clock input
input ps2_din, // PS/2 keyboard serial data input
output usb_fpga_pu_dp,
output usb_fpga_pu_dn,
output [3:0] gpdi_dp, gpdi_dn,
// Debugging ports
output [3:0] led,
input [1:0] button // 2 buttons on board
);
parameter C_ddr = 1'b1; // 0:SDR 1:DDR
// clock generator
wire clk_250MHz, clk_125MHz, clk_25MHz, clk_locked;
clk_25_250_125_25
clock_instance
(
.clki(clk_25mhz),
.clko(clk_250MHz),
.clks1(clk_125MHz),
.clks2(clk_25MHz),
.locked(clk_locked)
);
// shift clock choice SDR/DDR
wire clk_pixel, clk_shift;
assign clk_pixel = clk_25MHz;
generate
if(C_ddr == 1'b1)
assign clk_shift = clk_125MHz;
else
assign clk_shift = clk_250MHz;
endgenerate
assign usb_fpga_pu_dp = 1;
assign usb_fpga_pu_dn = 1;
assign led[0] = 1;
assign led[1] = reset_n;
assign led[2] = clr_screen_n;
assign led[3] = 0;
wire vga_bit;
// VGA signal generator
wire [7:0] vga_r, vga_g, vga_b;
wire vga_h_sync, vga_v_sync, vga_blank;
// set the monochrome base colour here..
assign vga_r = vga_bit ? 8'b10000000 : 8'b00000000;
assign vga_g = vga_bit ? 8'b11111111 : 8'b00000000;
assign vga_b = vga_bit ? 8'b10000000 : 8'b00000000;
// debounce reset button
wire reset_n;
debounce reset_button (
.clk25(clk_25mhz),
.rst(1'b0),
.sig_in(button[0]),
.sig_out(reset_n)
);
// debounce clear screen button
wire clr_screen_n;
debounce clr_button (
.clk25(clk_25mhz),
.rst(~reset_n),
.sig_in(~button[1]),
.sig_out(clr_screen_n)
);
// apple one main system
apple1 #(
.BASIC_FILENAME (BASIC_FILENAME),
.FONT_ROM_FILENAME (FONT_ROM_FILENAME),
.RAM_FILENAME (RAM_FILENAME),
.VRAM_FILENAME (VRAM_FILENAME),
.WOZMON_ROM_FILENAME (WOZMON_ROM_FILENAME)
) my_apple1(
.clk25(clk_25mhz),
.rst_n(reset_n),
.uart_rx(uart_rx),
.uart_tx(uart_tx),
.uart_cts(uart_cts),
.ps2_clk(ps2_clk),
.ps2_din(ps2_din),
.ps2_select(1'b1), // PS/2 enabled, UART TX disabled
//.ps2_select(1'b0), // PS/2 disabled, UART TX enabled
.vga_h_sync(vga_h_sync),
.vga_v_sync(vga_v_sync),
.vga_red(vga_bit),
//.vga_grn(vga_bit),
//.vga_blu(vga_bit),
.vga_blank(vga_blank),
.vga_cls(~clr_screen_n),
);
// VGA to digital video converter
wire [1:0] tmds[3:0];
vga2dvid
#(
.C_ddr(C_ddr),
.C_shift_clock_synchronizer(1'b1)
)
vga2dvid_instance
(
.clk_pixel(clk_pixel),
.clk_shift(clk_shift),
.in_red(vga_r),
.in_green(vga_g),
.in_blue(vga_b),
.in_hsync(vga_h_sync),
.in_vsync(vga_v_sync),
.in_blank(vga_blank),
.out_clock(tmds[3]),
.out_red(tmds[2]),
.out_green(tmds[1]),
.out_blue(tmds[0])
);
// output TMDS SDR/DDR data to fake differential lanes
fake_differential
#(
.C_ddr(C_ddr)
)
fake_differential_instance
(
.clk_shift(clk_shift),
.in_clock(tmds[3]),
.in_red(tmds[2]),
.in_green(tmds[1]),
.in_blue(tmds[0]),
.out_p(gpdi_dp),
.out_n(gpdi_dn)
);
endmodule

View File

@ -0,0 +1,60 @@
// diamond 3.7 accepts this PLL
// diamond 3.8-3.9 is untested
// diamond 3.10 or higher is likely to abort with error about unable to use feedback signal
// cause of this could be from wrong CPHASE/FPHASE parameters
module clk_25_250_125_25
(
input clki, // 25 MHz, 0 deg
output clko, // 250 MHz, 0 deg
output clks1, // 125 MHz, 0 deg
output clks2, // 25 MHz, 0 deg
output locked
);
(* FREQUENCY_PIN_CLKI="25" *)
(* FREQUENCY_PIN_CLKOP="250" *)
(* FREQUENCY_PIN_CLKOS="125" *)
(* FREQUENCY_PIN_CLKOS2="25" *)
(* ICP_CURRENT="12" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *)
EHXPLLL #(
.PLLRST_ENA("DISABLED"),
.INTFB_WAKE("DISABLED"),
.STDBY_ENABLE("DISABLED"),
.DPHASE_SOURCE("DISABLED"),
.OUTDIVIDER_MUXA("DIVA"),
.OUTDIVIDER_MUXB("DIVB"),
.OUTDIVIDER_MUXC("DIVC"),
.OUTDIVIDER_MUXD("DIVD"),
.CLKI_DIV(1),
.CLKOP_ENABLE("ENABLED"),
.CLKOP_DIV(2),
.CLKOP_CPHASE(0),
.CLKOP_FPHASE(0),
.CLKOS_ENABLE("ENABLED"),
.CLKOS_DIV(4),
.CLKOS_CPHASE(0),
.CLKOS_FPHASE(0),
.CLKOS2_ENABLE("ENABLED"),
.CLKOS2_DIV(20),
.CLKOS2_CPHASE(0),
.CLKOS2_FPHASE(0),
.FEEDBK_PATH("CLKOP"),
.CLKFB_DIV(10)
) pll_i (
.RST(1'b0),
.STDBY(1'b0),
.CLKI(clki),
.CLKOP(clko),
.CLKOS(clks1),
.CLKOS2(clks2),
.CLKFB(clko),
.CLKINTFB(),
.PHASESEL0(1'b0),
.PHASESEL1(1'b0),
.PHASEDIR(1'b1),
.PHASESTEP(1'b1),
.PHASELOADREG(1'b1),
.PLLWAKESYNC(1'b0),
.ENCLKOP(1'b0),
.LOCK(locked)
);
endmodule

View File

@ -0,0 +1,63 @@
// DDR mode uses Lattice ECP5 vendor-specific module ODDRX1F
module fake_differential
(
input clk_shift, // used only in DDR mode
// [1:0]:DDR [0]:SDR TMDS
input [1:0] in_clock, in_red, in_green, in_blue,
// [3]:clock [2]:red [1]:green [0]:blue
output [3:0] out_p, out_n
);
parameter C_ddr = 1'b0; // 0:SDR 1:DDR
wire [1:0] tmds[3:0];
assign tmds[3] = in_clock;
assign tmds[2] = in_red;
assign tmds[1] = in_green;
assign tmds[0] = in_blue;
// register stage to improve timing of the fake differential
reg [1:0] R_tmds_p[3:0], R_tmds_n[3:0];
generate
genvar i;
for(i = 0; i < 4; i++)
begin : TMDS_pn_registers
always @(posedge clk_shift) R_tmds_p[i] <= tmds[i];
always @(posedge clk_shift) R_tmds_n[i] <= ~tmds[i];
end
endgenerate
// output SDR/DDR to fake differential
generate
genvar i;
if(C_ddr == 1'b1)
for(i = 0; i < 4; i++)
begin : DDR_output_mode
ODDRX1F
ddr_p_instance
(
.D0(R_tmds_p[i][0]),
.D1(R_tmds_p[i][1]),
.Q(out_p[i]),
.SCLK(clk_shift),
.RST(0)
);
ODDRX1F
ddr_n_instance
(
.D0(R_tmds_n[i][0]),
.D1(R_tmds_n[i][1]),
.Q(out_n[i]),
.SCLK(clk_shift),
.RST(0)
);
end
else
for(i = 0; i < 4; i++)
begin : SDR_output_mode
assign out_p[i] = R_tmds_p[i][0];
assign out_n[i] = R_tmds_n[i][0];
end
endgenerate
endmodule

View File

@ -0,0 +1,155 @@
// File hdl/tmds_encoder.vhd translated with vhd2vl v3.0 VHDL to Verilog RTL translator
// vhd2vl settings:
// * Verilog Module Declaration Style: 2001
// vhd2vl is Free (libre) Software:
// Copyright (C) 2001 Vincenzo Liguori - Ocean Logic Pty Ltd
// http://www.ocean-logic.com
// Modifications Copyright (C) 2006 Mark Gonzales - PMC Sierra Inc
// Modifications (C) 2010 Shankar Giri
// Modifications Copyright (C) 2002-2017 Larry Doolittle
// http://doolittle.icarus.com/~larry/vhd2vl/
// Modifications (C) 2017 Rodrigo A. Melo
//
// vhd2vl comes with ABSOLUTELY NO WARRANTY. Always check the resulting
// Verilog for correctness, ideally with a formal verification tool.
//
// You are welcome to redistribute vhd2vl under certain conditions.
// See the license (GPLv2) file included with the source for details.
// The result of translation follows. Its copyright status should be
// considered unchanged from the original VHDL.
//--------------------------------------------------------------------------------
// Engineer: Mike Field <hamster@snap.net.nz>
//
// Description: TMDS Encoder
// 8 bits colour, 2 control bits and one blanking bits in
// 10 bits of TMDS encoded data out
// Clocked at the pixel clock
//
//--------------------------------------------------------------------------------
// See: http://hamsterworks.co.nz/mediawiki/index.php/Dvid_test
// http://hamsterworks.co.nz/mediawiki/index.php/FPGA_Projects
//
// Copyright (c) 2012 Mike Field <hamster@snap.net.nz>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// no timescale needed
module tmds_encoder(
input wire clk,
input wire [7:0] data,
input wire [1:0] c,
input wire blank,
output reg [9:0] encoded
);
wire [8:0] xored;
wire [8:0] xnored;
wire [3:0] ones;
reg [8:0] data_word;
reg [8:0] data_word_inv;
wire [3:0] data_word_disparity;
reg [3:0] dc_bias = 1'b0;
// Work our the two different encodings for the byte
assign xored[0] = data[0];
assign xored[1] = data[1] ^ xored[0];
assign xored[2] = data[2] ^ xored[1];
assign xored[3] = data[3] ^ xored[2];
assign xored[4] = data[4] ^ xored[3];
assign xored[5] = data[5] ^ xored[4];
assign xored[6] = data[6] ^ xored[5];
assign xored[7] = data[7] ^ xored[6];
assign xored[8] = 1'b1;
assign xnored[0] = data[0];
assign xnored[1] = ~(data[1] ^ xnored[0]);
assign xnored[2] = ~(data[2] ^ xnored[1]);
assign xnored[3] = ~(data[3] ^ xnored[2]);
assign xnored[4] = ~(data[4] ^ xnored[3]);
assign xnored[5] = ~(data[5] ^ xnored[4]);
assign xnored[6] = ~(data[6] ^ xnored[5]);
assign xnored[7] = ~(data[7] ^ xnored[6]);
assign xnored[8] = 1'b0;
// Count how many ones are set in data
assign ones = 4'b0000 + data[0] + data[1] + data[2] + data[3] + data[4] + data[5] + data[6] + data[7];
// Decide which encoding to use
always @(ones, data[0], xnored, xored) begin
if(ones > 4 || (ones == 4 && data[0] == 1'b0)) begin
data_word <= xnored;
data_word_inv <= ~(xnored);
end
else begin
data_word <= xored;
data_word_inv <= ~(xored);
end
end
// Work out the DC bias of the dataword;
assign data_word_disparity = 4'b1100 + data_word[0] + data_word[1] + data_word[2] + data_word[3] + data_word[4] + data_word[5] + data_word[6] + data_word[7];
// Now work out what the output should be
always @(posedge clk) begin
if(blank == 1'b1) begin
// In the control periods, all values have and have balanced bit count
case(c)
2'b00 : begin
encoded <= 10'b1101010100;
end
2'b01 : begin
encoded <= 10'b0010101011;
end
2'b10 : begin
encoded <= 10'b0101010100;
end
default : begin
encoded <= 10'b1010101011;
end
endcase
dc_bias <= {4{1'b0}};
end
else begin
if(dc_bias == 5'b00000 || data_word_disparity == 0) begin
// dataword has no disparity
if(data_word[8] == 1'b1) begin
encoded <= {2'b01,data_word[7:0]};
dc_bias <= dc_bias + data_word_disparity;
end
else begin
encoded <= {2'b10,data_word_inv[7:0]};
dc_bias <= dc_bias - data_word_disparity;
end
end
else if((dc_bias[3] == 1'b0 && data_word_disparity[3] == 1'b0) || (dc_bias[3] == 1'b1 && data_word_disparity[3] == 1'b1)) begin
encoded <= {1'b1,data_word[8],data_word_inv[7:0]};
dc_bias <= dc_bias + data_word[8] - data_word_disparity;
end
else begin
encoded <= {1'b0,data_word};
dc_bias <= dc_bias - data_word_inv[8] + data_word_disparity;
end
end
end
endmodule

262
rtl/boards/ulx3s/vga2dvid.v Normal file
View File

@ -0,0 +1,262 @@
// File vga2dvid.vhd translated with vhd2vl v3.0 VHDL to Verilog RTL translator
// vhd2vl settings:
// * Verilog Module Declaration Style: 2001
// vhd2vl is Free (libre) Software:
// Copyright (C) 2001 Vincenzo Liguori - Ocean Logic Pty Ltd
// http://www.ocean-logic.com
// Modifications Copyright (C) 2006 Mark Gonzales - PMC Sierra Inc
// Modifications (C) 2010 Shankar Giri
// Modifications Copyright (C) 2002-2017 Larry Doolittle
// http://doolittle.icarus.com/~larry/vhd2vl/
// Modifications (C) 2017 Rodrigo A. Melo
//
// vhd2vl comes with ABSOLUTELY NO WARRANTY. Always check the resulting
// Verilog for correctness, ideally with a formal verification tool.
//
// You are welcome to redistribute vhd2vl under certain conditions.
// See the license (GPLv2) file included with the source for details.
// The result of translation follows. Its copyright status should be
// considered unchanged from the original VHDL.
//------------------------------------------------------------------------------
// Engineer: Mike Field <hamster@snap.net.nz>
// Description: Converts VGA signals into DVID bitstreams.
//
// 'clk_shift' 10x clk_pixel for SDR
// 'clk_shift' 5x clk_pixel for DDR
//
// 'blank' should be asserted during the non-display
// portions of the frame
//------------------------------------------------------------------------------
// See: http://hamsterworks.co.nz/mediawiki/index.php/Dvid_test
// http://hamsterworks.co.nz/mediawiki/index.php/FPGA_Projects
//
// Copyright (c) 2012 Mike Field <hamster@snap.net.nz>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// takes VGA input and prepares output
// for SDR buffer, which send 1 bit per 1 clock period output out_red(0), out_green(0), ... etc.
// for DDR buffers, which send 2 bits per 1 clock period output out_red(1 downto 0), ...
// EMARD unified SDR and DDR into one module
// no timescale needed
module vga2dvid(
input wire clk_pixel,
input wire clk_shift,
input wire [C_depth - 1:0] in_red,
input wire [C_depth - 1:0] in_green,
input wire [C_depth - 1:0] in_blue,
input wire in_blank,
input wire in_hsync,
input wire in_vsync,
output wire [9:0] outp_red,
output wire [9:0] outp_green,
output wire [9:0] outp_blue,
output wire [1:0] out_red,
output wire [1:0] out_green,
output wire [1:0] out_blue,
output wire [1:0] out_clock
);
parameter C_shift_clock_synchronizer=1'b1;
parameter C_parallel=1'b1;
parameter C_serial=1'b1;
parameter C_ddr=1'b0;
parameter [31:0] C_depth=8;
// VGA pixel clock, 25 MHz for 640x480
// SDR: 10x clk_pixel, DDR: 5x clk_pixel, in phase with clk_pixel
// parallel outputs
// serial outputs
wire [9:0] encoded_red; wire [9:0] encoded_green; wire [9:0] encoded_blue;
reg [9:0] latched_red = 1'b0; reg [9:0] latched_green = 1'b0; reg [9:0] latched_blue = 1'b0;
reg [9:0] shift_red = 1'b0; reg [9:0] shift_green = 1'b0; reg [9:0] shift_blue = 1'b0;
parameter C_shift_clock_initial = 10'b0000011111;
reg [9:0] shift_clock = C_shift_clock_initial;
reg R_shift_clock_off_sync = 1'b0;
reg [7:0] R_shift_clock_synchronizer = 1'b0;
reg [6:0] R_sync_fail; // counts sync fails, after too many, reinitialize shift_clock
parameter c_red = 1'b0;
parameter c_green = 1'b0;
wire [1:0] c_blue;
wire [7:0] red_d;
wire [7:0] green_d;
wire [7:0] blue_d;
assign c_blue = {in_vsync,in_hsync};
assign red_d[7:8 - C_depth] = in_red[C_depth - 1:0];
assign green_d[7:8 - C_depth] = in_green[C_depth - 1:0];
assign blue_d[7:8 - C_depth] = in_blue[C_depth - 1:0];
// fill vacant low bits with value repeated (so min/max value is always 0 or 255)
generate if (C_depth < 8) begin: G_vacant_bits
genvar i;
generate for (i=0; i <= 8 - C_depth - 1; i = i + 1) begin: G_bits
assign red_d[i] = in_red[0];
assign green_d[i] = in_green[0];
assign blue_d[i] = in_blue[0];
end
endgenerate
end
endgenerate
generate if (C_shift_clock_synchronizer == 1'b1) begin: G_shift_clock_synchronizer
// sampler verifies is shift_clock state synchronous with pixel_clock
always @(posedge clk_pixel) begin
// does 0 to 1 transition at bits 5 downto 4 happen at rising_edge of clk_pixel?
// if shift_clock = C_shift_clock_initial then
if(shift_clock[5:4] == C_shift_clock_initial[5:4]) begin
// same as above line but simplified
R_shift_clock_off_sync <= 1'b0;
end
else begin
R_shift_clock_off_sync <= 1'b1;
end
end
// every N cycles of clk_shift: signal to skip 1 cycle in order to get in sync
always @(posedge clk_shift) begin
if(R_shift_clock_off_sync == 1'b1) begin
if(R_shift_clock_synchronizer[(7)] == 1'b1) begin
R_shift_clock_synchronizer <= {8{1'b0}};
end
else begin
R_shift_clock_synchronizer <= R_shift_clock_synchronizer + 1;
end
end
else begin
R_shift_clock_synchronizer <= {8{1'b0}};
end
end
end
endgenerate
// shift_clock_synchronizer
tmds_encoder u21(
.clk(clk_pixel),
.data(red_d),
.c(c_red),
.blank(in_blank),
.encoded(encoded_red));
tmds_encoder u22(
.clk(clk_pixel),
.data(green_d),
.c(c_green),
.blank(in_blank),
.encoded(encoded_green));
tmds_encoder u23(
.clk(clk_pixel),
.data(blue_d),
.c(c_blue),
.blank(in_blank),
.encoded(encoded_blue));
always @(posedge clk_pixel) begin
latched_red <= encoded_red;
latched_green <= encoded_green;
latched_blue <= encoded_blue;
end
generate if (C_parallel == 1'b1) begin: G_parallel
assign outp_red = latched_red;
assign outp_green = latched_green;
assign outp_blue = latched_blue;
end
endgenerate
generate if ((C_serial & ~C_ddr) == 1'b1) begin: G_SDR
always @(posedge clk_shift) begin
//if shift_clock = "0000011111" then
if(shift_clock[5:4] == C_shift_clock_initial[5:4]) begin
// same as above line but simplified
shift_red <= latched_red;
shift_green <= latched_green;
shift_blue <= latched_blue;
end
else begin
shift_red <= {1'b0,shift_red[9:1]};
shift_green <= {1'b0,shift_green[9:1]};
shift_blue <= {1'b0,shift_blue[9:1]};
end
if(R_shift_clock_synchronizer[(7)] == 1'b0) begin
shift_clock <= {shift_clock[0],shift_clock[9:1]};
end
else begin
// synchronization failed.
// after too many fails, reinitialize shift_clock
if(R_sync_fail[(6)] == 1'b1) begin
shift_clock <= C_shift_clock_initial;
R_sync_fail <= {7{1'b0}};
end
else begin
R_sync_fail <= R_sync_fail + 1;
end
end
end
end
endgenerate
generate if ((C_serial & C_ddr) == 1'b1) begin: G_DDR
always @(posedge clk_shift) begin
//if shift_clock = "0000011111" then
if(shift_clock[5:4] == C_shift_clock_initial[5:4]) begin
// same as above line but simplified
shift_red <= latched_red;
shift_green <= latched_green;
shift_blue <= latched_blue;
end
else begin
shift_red <= {2'b00,shift_red[9:2]};
shift_green <= {2'b00,shift_green[9:2]};
shift_blue <= {2'b00,shift_blue[9:2]};
end
if(R_shift_clock_synchronizer[(7)] == 1'b0) begin
shift_clock <= {shift_clock[1:0],shift_clock[9:2]};
end
else begin
// synchronization failed.
// after too many fails, reinitialize shift_clock
if(R_sync_fail[(6)] == 1'b1) begin
shift_clock <= C_shift_clock_initial;
R_sync_fail <= {7{1'b0}};
end
else begin
R_sync_fail <= R_sync_fail + 1;
end
end
end
end
endgenerate
// SDR: use only bit 0 from each out_* channel
// DDR: 2 bits per 1 clock period,
// (one bit output on rising edge, other on falling edge of clk_shift)
generate if (C_serial == 1'b1) begin: G_serial
assign out_red = shift_red[1:0];
assign out_green = shift_green[1:0];
assign out_blue = shift_blue[1:0];
assign out_clock = shift_clock[1:0];
end
endgenerate
endmodule

View File

@ -38,6 +38,7 @@ module vga #(
input [1:0] mode, // 2-bit mode setting for pixel doubling
input [2:0] bg_colour, // 3 bit background colour
input [2:0] fg_colour, // 3 bit foreground colour
output vga_blank, // set when out of active regions
input clr_screen // clear screen button
);
@ -94,6 +95,8 @@ module vga #(
assign h_active = (h_cnt >= hbp && h_cnt < hfp);
assign v_active = (v_cnt >= vbp && v_cnt < vfp);
assign vga_blank = !(h_active && v_active);
//////////////////////////////////////////////////////////////////////////
// VGA Sync Generation
//