initial stuff

This commit is contained in:
saji 2024-04-19 15:16:22 -05:00
parent 44dd9f79c7
commit d798122a87
4 changed files with 156 additions and 597 deletions

View file

@ -0,0 +1,135 @@
# Board definition file for the Colorlight 5A-75B.
# Mostly copied from the litex-boards repo, but we didn't pull that in
from litex.build.generic_platform import Pins, IOStandard, Subsignal, Misc
from litex.build.lattice import LatticeECP5Platform
from litex.build.openfpgaloader import OpenFPGALoader
from litex.gen import LiteXModule, ClockDomain, ClockSignal
from litex.build.io import DDROutput
from litex.soc.cores.clock import ECP5PLL
_io = [
# Clk
("clk25", 0, Pins("P6"), IOStandard("LVCMOS33")),
# Led
("user_led_n", 0, Pins("T6"), IOStandard("LVCMOS33")),
# Button
("user_btn_n", 0, Pins("R7"), IOStandard("LVCMOS33")),
# serial
("serial", 0,
Subsignal("tx", Pins("T6")), # led (J19 DATA_LED-)
Subsignal("rx", Pins("R7")), # btn (J19 KEY+)
IOStandard("LVCMOS33")
),
# SDR SDRAM (M12L64322A)
("sdram_clock", 0, Pins("C8"), IOStandard("LVCMOS33")),
("sdram", 0,
Subsignal("a", Pins(
"A9 B9 B10 C10 D9 C9 E9 D8",
"E8 C7 B8")),
Subsignal("dq", Pins(
"B2 A2 C3 A3 B3 A4 B4 A5",
"E7 C6 D7 D6 E6 D5 C5 E5",
"A11 B11 B12 A13 B13 A14 B14 D14",
"D13 E11 C13 D11 C12 E10 C11 D10")),
Subsignal("we_n", Pins("B5")),
Subsignal("ras_n", Pins("B6")),
Subsignal("cas_n", Pins("A6")),
#Subsignal("cs_n", Pins("")), # gnd
#Subsignal("cke", Pins("")), # 3v3
Subsignal("ba", Pins("B7 A8")),
#Subsignal("dm", Pins("")), # gnd
IOStandard("LVCMOS33"),
Misc("SLEWRATE=FAST")
),
# RGMII Ethernet (RTL8211FD)
("eth_clocks", 0,
Subsignal("tx", Pins("L1")),
Subsignal("rx", Pins("J1")),
IOStandard("LVCMOS33")
),
("eth", 0,
#Subsignal("rst_n", Pins("R6")),
Subsignal("mdio", Pins("T4")),
Subsignal("mdc", Pins("R5")),
Subsignal("rx_ctl", Pins("J2")),
Subsignal("rx_data", Pins("K2 J3 K1 K3")),
Subsignal("tx_ctl", Pins("L2")),
Subsignal("tx_data", Pins("M2 M1 P1 R1")),
IOStandard("LVCMOS33")
),
("eth_clocks", 1,
Subsignal("tx", Pins("J16")),
Subsignal("rx", Pins("M16")),
IOStandard("LVCMOS33")
),
("eth", 1,
#Subsignal("rst_n", Pins("R6")),
Subsignal("mdio", Pins("T4")),
Subsignal("mdc", Pins("R5")),
Subsignal("rx_ctl", Pins("P16")),
Subsignal("rx_data", Pins("M15 R16 L15 L16")),
Subsignal("tx_ctl", Pins("K14")),
Subsignal("tx_data", Pins("K16 J15 J14 K15")),
IOStandard("LVCMOS33")
),
]
_connectors = [
("j1", "C4 D4 E4 - D3 F5 E3 N4 N5 N3 P3 P4 M3 N1 M4 -"),
("j2", "F1 F2 G2 - G1 H2 H3 N4 N5 N3 P3 P4 M3 N1 M4 -"),
("j3", "B1 C2 C1 - D1 E2 E1 N4 N5 N3 P3 P4 M3 N1 M4 -"),
("j4", "P5 R3 P2 - R2 T2 N6 N4 N5 N3 P3 P4 M3 N1 M4 -"),
("j5", "T13 R12 R13 - R14 T14 P12 N4 N5 N3 P3 P4 M3 N1 M4 -"),
("j6", "R15 T15 P13 - P14 N14 H15 N4 N5 N3 P3 P4 M3 N1 M4 -"),
("j7", "G16 H14 G15 - F15 F16 E16 N4 N5 N3 P3 P4 M3 N1 M4 -"),
("j8", "D16 E15 C16 - B16 C15 B15 N4 N5 N3 P3 P4 M3 N1 M4 -"),
]
class _CRG(LiteXModule):
def __init__(self, platform, sys_clk_freq):
self.cd_sys = ClockDomain("sys")
self.cd_sdram = ClockDomain("sdram")
# Clk / Rst.
clk25 = platform.request("clk25")
rst_n = platform.request("user_btn_n", 0)
# PLL.
self.pll = pll = ECP5PLL()
self.comb = [pll.reset.eq(~rst_n)]
pll.register_clkin(clk25, 25e6)
pll.create_clkout(self.cd_sys, sys_clk_freq)
# for the sdram
pll.create_clkout(self.cd_sdram, sys_clk_freq, phase=180)
sdram_clk = ClockSignal("sdram")
self.specials = DDROutput(1,0, platform.request("sdram_clock"), sdram_clk)
class Platform(LatticeECP5Platform):
default_clk_name = "clk25"
default_clk_period = 1e9/25e6
def __init__(self, toolchain='trellis', **kwargs):
device = "LFE5U-25F-6BG256C"
LatticeECP5Platform.__init__(self, device, _io, connectors=_connectors, toolchain=toolchain)
def create_programmer(self):
return OpenFPGALoader(cable="cmsisdap")
def get_crg(self, sys_clk_freq) -> _CRG:
crg = _CRG(self, sys_clk_freq)
return crg
def do_finalize(self, fragment, *args, **kwargs):
LatticeECP5Platform.do_finalize(self, fragment)
self.add_period_constraint(self.lookup_request("clk25", loose=True), 1e9/25e6)
self.add_period_constraint(self.lookup_request("eth_clocks:rx", 0, loose=True), 1e9/125e6)
self.add_period_constraint(self.lookup_request("eth_clocks:rx", 1, loose=True), 1e9/125e6)

21
groovylight/soc.py Normal file
View file

@ -0,0 +1,21 @@
from migen import *
from litex.gen import LiteXModule, ClockDomain, ClockSignal
from litex.soc.cores.cpu import vexriscv
from litex.soc.integration.soc_core import SoCCore
from litex.soc.integration.builder import Builder
from litedram.modules import M12L64322A
from liteeth.phy.ecp5rgmii import LiteEthPHYRGMII
class GroovySoC(SoCCore):
def __init__(self, platform, sys_clk_freq, **kwargs):
SoCCore.__init__(self, platform, sys_clk_freq, ident="LiteX SoC for GroovyLight", **kwargs)
self.crg = platform.get_crg(sys_clk_freq)
self.submodules += self.crg

View file

@ -1,27 +0,0 @@
LOCATE COMP "clk_25mhz" SITE "P3";
IOBUF PORT "clk_25mhz" IO_TYPE=LVCMOS33;
FREQUENCY PORT "clk_25mhz" 25 MHZ;
SYSCONFIG COMPRESS_CONFIG=ON;
LOCATE COMP "led_o" SITE "L2";
IOBUF PORT "led_o" IO_TYPE=LVCMOS33;
# HDMI
LOCATE COMP "gpdi_dp[0]" SITE "G19"; # Blue +
LOCATE COMP "gpdi_dn[0]" SITE "H20"; # Blue -
LOCATE COMP "gpdi_dp[1]" SITE "E20"; # Green +
LOCATE COMP "gpdi_dn[1]" SITE "F19"; # Green -
LOCATE COMP "gpdi_dp[2]" SITE "C20"; # Red +
LOCATE COMP "gpdi_dn[2]" SITE "D19"; # Red -
LOCATE COMP "gpdi_dp[3]" SITE "J19"; # Clock +
LOCATE COMP "gpdi_dn[3]" SITE "K19"; # Clock -
IOBUF PORT "gpdi_dp[0]" IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "gpdi_dn[0]" IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "gpdi_dp[1]" IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "gpdi_dn[1]" IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "gpdi_dp[2]" IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "gpdi_dn[2]" IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "gpdi_dp[3]" IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "gpdi_dn[3]" IO_TYPE=LVCMOS33 DRIVE=4;

View file

@ -1,570 +0,0 @@
`default_nettype none
module blink (
input clk_i,
output reg led_o
);
localparam MAX = 25_000_000;
localparam WIDTH = $clog2(MAX);
wire rst_s;
wire clk_s;
assign clk_s = clk_i;
//pll_12_16 pll_inst (.clki(clk_i), .clko(clk_s), .rst(rst_s));
// rst_gen rst_inst (.clk_i(clk_s), .rst_i(1'b0), .rst_o(rst_s));
reg [WIDTH-1:0] cpt_s;
wire [WIDTH-1:0] cpt_next_s = cpt_s + 1'b1;
wire end_s = cpt_s == MAX-1;
initial begin
led_o <= 0;
end
always @(posedge clk_s) begin
// if we're at the end, reset to 0, else set to next state.
cpt_s <= end_s ? {WIDTH{1'b0}} : cpt_next_s;
if (end_s)
led_o <= ~led_o;
end
endmodule
module topcore (
input clk_25mhz,
output [3:0] gpdi_dp, gpdi_dn,
);
wire clk_25MHz, clk_250MHz;
clock clock_instance(
.clkin_25MHz(clk_25mhz),
.clk_25MHz(clk_25MHz),
.clk_250MHz(clk_250MHz)
);
wire [7:0] red, grn, blu;
wire [23:0] pixel;
assign red= pixel[23:16];
assign grn= pixel[15:8];
assign blu= pixel[7:0];
wire o_red;
wire o_grn;
wire o_blu;
wire o_rd, o_newline, o_newframe;
// A reset line that goes low after 16 ticks
reg [2:0] reset_cnt = 0;
wire reset = ~reset_cnt[2];
always @(posedge clk_25mhz)
if (reset) reset_cnt <= reset_cnt + 1;
llhdmi llhdmi_instance(
.i_tmdsclk(clk_250MHz), .i_pixclk(clk_25MHz),
.i_reset(reset), .i_red(red), .i_grn(grn), .i_blu(blu),
.o_rd(o_rd), .o_newline(o_newline), .o_newframe(o_newframe),
.o_red(o_red), .o_grn(o_grn), .o_blu(o_blu));
vgatestsrc #(.BITS_PER_COLOR(8))
vgatestsrc_instance(
.i_pixclk(clk_25MHz), .i_reset(reset),
.i_width(640), .i_height(480),
.i_rd(o_rd), .i_newline(o_newline), .i_newframe(o_newframe),
.o_pixel(pixel));
OBUFDS OBUFDS_red(.I(o_red), .O(gpdi_dp[2]), .OB(gpdi_dn[2]));
OBUFDS OBUFDS_grn(.I(o_grn), .O(gpdi_dp[1]), .OB(gpdi_dn[1]));
OBUFDS OBUFDS_blu(.I(o_blu), .O(gpdi_dp[0]), .OB(gpdi_dn[0]));
OBUFDS OBUFDS_clock(.I(clk_25MHz), .O(gpdi_dp[3]), .OB(gpdi_dn[3]));
endmodule
module OBUFDS(
input I, // input
output O, // positive output
output OB // negative output
);
assign O = I;
assign OB = ~I;
endmodule
module clock
(
input clkin_25MHz,
output clk_125MHz,
output clk_250MHz,
output clk_25MHz,
output clk_83M333Hz,
output locked
);
wire int_locked;
(* ICP_CURRENT="9" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *)
EHXPLLL
#(
.PLLRST_ENA("DISABLED"),
.INTFB_WAKE("DISABLED"),
.STDBY_ENABLE("DISABLED"),
.DPHASE_SOURCE("DISABLED"),
.CLKOS_FPHASE(0),
.CLKOP_FPHASE(0),
.CLKOS3_CPHASE(5),
.CLKOS2_CPHASE(0),
.CLKOS_CPHASE(1),
.CLKOP_CPHASE(3),
.OUTDIVIDER_MUXD("DIVD"),
.OUTDIVIDER_MUXC("DIVC"),
.OUTDIVIDER_MUXB("DIVB"),
.OUTDIVIDER_MUXA("DIVA"),
.CLKOS3_ENABLE("ENABLED"),
.CLKOS2_ENABLE("ENABLED"),
.CLKOS_ENABLE("ENABLED"),
.CLKOP_ENABLE("ENABLED"),
.CLKOS3_DIV(0),
.CLKOS2_DIV(20),
.CLKOS_DIV(2),
.CLKOP_DIV(4),
.CLKFB_DIV(5),
.CLKI_DIV(1),
.FEEDBK_PATH("CLKOP")
)
pll_i
(
.CLKI(clkin_25MHz),
.CLKFB(clk_125MHz),
.CLKOP(clk_125MHz),
.CLKOS(clk_250MHz),
.CLKOS2(clk_25MHz),
.CLKOS3(clk_83M333Hz),
.RST(1'b0),
.STDBY(1'b0),
.PHASESEL0(1'b0),
.PHASESEL1(1'b0),
.PHASEDIR(1'b0),
.PHASESTEP(1'b0),
.PLLWAKESYNC(1'b0),
.ENCLKOP(1'b0),
.ENCLKOS(1'b0),
.ENCLKOS2(1'b0),
.ENCLKOS3(1'b0),
.LOCK(locked),
.INTLOCK(int_locked)
);
endmodule
module vgatestsrc(i_pixclk, i_reset,
// External connections
i_width, i_height,
i_rd, i_newline, i_newframe,
// VGA connections
o_pixel);
parameter BITS_PER_COLOR = 4,
HW=12, VW=12;
//HW=13,VW=11;
localparam BPC = BITS_PER_COLOR,
BITS_PER_PIXEL = 3 * BPC,
BPP = BITS_PER_PIXEL;
//
input wire i_pixclk, i_reset;
input wire [HW-1:0] i_width;
input wire [VW-1:0] i_height;
//
input wire i_rd, i_newline, i_newframe;
//
output reg [(BPP-1):0] o_pixel;
wire [BPP-1:0] white, black, purplish_blue, purple, dark_gray,
darkest_gray, mid_white, mid_cyan, mid_magenta,
mid_red, mid_green, mid_blue, mid_yellow;
wire [BPC-1:0] midv, mid_off;
assign midv = { 2'b11, {(BPC-2){1'b0}} };
assign mid_off = { (BPC){1'b0} };
assign white = {(BPP){1'b1}};
assign black = {(BPP){1'b0}};
assign purplish_blue = {
{(BPC){1'b0}},
3'b001, {(BPC-3){1'b0}},
2'b01, {(BPC-2){1'b0}} };
assign purple = { {2'b00, {(BPC-2){1'b1}} }, {(BPC){1'b0}},
{ 1'b0, {(BPC-1){1'b1}} } };
assign dark_gray = {(3){ { 4'b0010, {(BPC-4){1'b0}} } }};
assign darkest_gray = {(3){ { 4'b0001, {(BPC-4){1'b0}} } }};
assign mid_white = { midv, midv, midv };
assign mid_yellow = { midv, midv, mid_off };
assign mid_red = { midv, mid_off, mid_off };
assign mid_green = { mid_off, midv, mid_off };
assign mid_blue = { mid_off, mid_off, midv };
assign mid_cyan = { mid_off, midv, midv };
assign mid_magenta = { midv, mid_off, midv };
reg [HW-1:0] hpos, hedge;
reg [VW-1:0] ypos, yedge;
reg [3:0] yline, hbar;
//
//
// 1 Border
// 8 BARS
// 1 short bar
// 3 fat bars
// 1 border
// 1 gradient bar
// 1 border
//
reg dline;
always @(posedge i_pixclk)
if ((i_reset)||(i_newframe)||(i_newline))
dline <= 1'b0;
else if (i_rd)
dline <= 1'b1;
always @(posedge i_pixclk)
if ((i_reset)||(i_newframe))
begin
ypos <= 0;
yline <= 0;
yedge <= { 4'h0, i_height[(VW-1):4] };
end else if (i_newline)
begin
ypos <= ypos + { {(VW-1){1'h0}}, dline };
if (ypos >= yedge)
begin
yline <= yline + 1'b1;
yedge <= yedge + { 4'h0, i_height[(VW-1):4] };
end
end
initial hpos = 0;
initial hbar = 0;
initial hedge = 0; // { 4'h0, i_width[(HW-1):4] };
always @(posedge i_pixclk)
if ((i_reset)||(i_newline))
begin
hpos <= 0;
hbar <= 0;
hedge <= { 4'h0, i_width[(HW-1):4] };
end else if (i_rd)
begin
hpos <= hpos + 1'b1;
if (hpos >= hedge)
begin
hbar <= hbar + 1'b1;
hedge <= hedge + { 4'h0, i_width[(HW-1):4] };
end
end
reg [BPP-1:0] topbar, midbar, fatbar, gradient, pattern;
always @(posedge i_pixclk)
case(hbar[3:0])
4'h0: topbar <= black;
4'h1: topbar <= mid_white;
4'h2: topbar <= mid_white;
4'h3: topbar <= mid_yellow;
4'h4: topbar <= mid_yellow;
4'h5: topbar <= mid_cyan;
4'h6: topbar <= mid_cyan;
4'h7: topbar <= mid_green;
4'h8: topbar <= mid_green;
4'h9: topbar <= mid_magenta;
4'ha: topbar <= mid_magenta;
4'hb: topbar <= mid_red;
4'hc: topbar <= mid_red;
4'hd: topbar <= mid_blue;
4'he: topbar <= mid_blue;
4'hf: topbar <= black;
endcase
always @(posedge i_pixclk)
case(hbar[3:0])
4'h0: midbar <= black;
4'h1: midbar <= mid_blue;
4'h2: midbar <= mid_blue;
4'h3: midbar <= black;
4'h4: midbar <= black;
4'h5: midbar <= mid_magenta;
4'h6: midbar <= mid_magenta;
4'h7: midbar <= black;
4'h8: midbar <= black;
4'h9: midbar <= mid_cyan;
4'ha: midbar <= mid_cyan;
4'hb: midbar <= black;
4'hc: midbar <= black;
4'hd: midbar <= mid_white;
4'he: midbar <= mid_white;
4'hf: midbar <= black;
endcase
always @(posedge i_pixclk)
case(hbar[3:0])
4'h0: fatbar <= black;
4'h1: fatbar <= purplish_blue;
4'h2: fatbar <= purplish_blue;
4'h3: fatbar <= purplish_blue;
4'h4: fatbar <= white;
4'h5: fatbar <= white;
4'h6: fatbar <= white;
4'h7: fatbar <= purple;
4'h8: fatbar <= purple;
4'h9: fatbar <= purple;
4'ha: fatbar <= darkest_gray;
4'hb: fatbar <= black;
4'hc: fatbar <= dark_gray;
4'hd: fatbar <= darkest_gray;
4'he: fatbar <= black;
4'hf: fatbar <= black;
endcase
reg [(HW-1):0] last_width;
always @(posedge i_pixclk)
last_width <= i_width;
// Attempt to discover 1/i_width in h_step
localparam FRACB=16;
//
reg [(FRACB-1):0] hfrac, h_step;
always @(posedge i_pixclk)
if ((i_reset)||(i_newline))
hfrac <= 0;
else if (i_rd)
hfrac <= hfrac + h_step;
always @(posedge i_pixclk)
if ((i_reset)||(i_width != last_width))
h_step <= 1;
else if ((i_newline)&&(hfrac > 0))
begin
if (hfrac < {(FRACB){1'b1}} - { {(FRACB-HW){1'b0}}, i_width })
h_step <= h_step + 1'b1;
else if (hfrac < { {(FRACB-HW){1'b0}}, i_width })
h_step <= h_step - 1'b1;
end
always @(posedge i_pixclk)
case(hfrac[FRACB-1:FRACB-4])
4'h0: gradient <= black;
// Red
4'h1: gradient <= { 1'b0, hfrac[(FRACB-5):(FRACB-3-BPC)], {(2){mid_off}} };
4'h2: gradient <= { 1'b1, hfrac[(FRACB-5):(FRACB-3-BPC)], {(2){mid_off}} };
4'h3: gradient <= black;
// Green
4'h4: gradient <= { mid_off, 1'b0, hfrac[(FRACB-5):(FRACB-3-BPC)], mid_off };
4'h5: gradient <= { mid_off, 1'b1, hfrac[(FRACB-5):(FRACB-3-BPC)], mid_off };
4'h6: gradient <= black;
// Blue
4'h7: gradient <= { {(2){mid_off}}, 1'b0, hfrac[(FRACB-5):(FRACB-3-BPC)] };
4'h8: gradient <= { {(2){mid_off}}, 1'b1, hfrac[(FRACB-5):(FRACB-3-BPC)] };
4'h9: gradient <= black;
// Gray
4'ha: gradient <= {(3){ 2'b00, hfrac[(FRACB-5):(FRACB-2-BPC)] }};
4'hb: gradient <= {(3){ 2'b01, hfrac[(FRACB-5):(FRACB-2-BPC)] }};
4'hc: gradient <= {(3){ 2'b10, hfrac[(FRACB-5):(FRACB-2-BPC)] }};
4'hd: gradient <= {(3){ 2'b11, hfrac[(FRACB-5):(FRACB-2-BPC)] }};
4'he: gradient <= black;
//
4'hf: gradient <= black;
endcase
always @(posedge i_pixclk)
case(yline)
4'h0: pattern <= black;
4'h1: pattern <= topbar; //
4'h2: pattern <= topbar;
4'h3: pattern <= topbar;
4'h4: pattern <= topbar;
4'h5: pattern <= topbar;
4'h6: pattern <= topbar;
4'h7: pattern <= topbar;
4'h8: pattern <= topbar;
4'h9: pattern <= midbar; //
4'ha: pattern <= fatbar; //
4'hb: pattern <= fatbar;
4'hc: pattern <= fatbar;
4'hd: pattern <= black;
4'he: pattern <= gradient;
4'hf: pattern <= black;
endcase
always @(posedge i_pixclk)
if (i_newline)
o_pixel <= white;
else if (i_rd)
begin
if (hpos == i_width-12'd3)
o_pixel <= white;
else if ((ypos == 0)||(ypos == i_height-1))
o_pixel <= white;
else
o_pixel <= pattern;
end
endmodule
module llhdmi(
i_tmdsclk, i_pixclk,
i_reset, i_red, i_grn, i_blu,
o_rd, o_newline, o_newframe,
`ifdef VERILATOR
o_TMDS_red, o_TMDS_grn, o_TMDS_blu,
`endif
o_red, o_grn, o_blu);
input wire i_tmdsclk; // TMDS clock
input wire i_pixclk; // Pixel clock, 10 times slower than i_tmdsclk
input wire i_reset; // Reset this module when strobed high
input wire [7:0] i_red; // Red green and blue colour values
input wire [7:0] i_grn; // for each pixel
input wire [7:0] i_blu;
output wire o_rd; // True when we can accept pixel data
output reg o_newline; // True on last pixel of each line
output reg o_newframe; // True on last pixel of each frame
output wire o_red; // Red TMDS pixel stream
output wire o_grn; // Green TMDS pixel stream
output wire o_blu; // Blue TMDS pixel stream
`ifdef VERILATOR
output wire [9:0] o_TMDS_red, o_TMDS_grn, o_TMDS_blu;
assign o_TMDS_red= TMDS_red;
assign o_TMDS_grn= TMDS_grn;
assign o_TMDS_blu= TMDS_blu;
`endif
reg [9:0] CounterX, CounterY;
reg hSync, vSync, DrawArea;
// Keep track of the current X/Y pixel position
always @(posedge i_pixclk)
if (i_reset)
CounterX <= 0;
else
CounterX <= (CounterX==799) ? 0 : CounterX+1;
always @(posedge i_pixclk)
if (i_reset)
CounterY <= 0;
else if (CounterX==799) begin
CounterY <= (CounterY==524) ? 0 : CounterY+1;
end
// Signal end of line, end of frame
always @(posedge i_pixclk) begin
o_newline <= (CounterX==639) ? 1 : 0;
o_newframe <= (CounterX==639) && (CounterY==479) ? 1 : 0;
end
// Determine when we are in a drawable area
always @(posedge i_pixclk)
DrawArea <= (CounterX<640) && (CounterY<480);
assign o_rd= ~i_reset & DrawArea;
// Generate horizontal and vertical sync pulses
always @(posedge i_pixclk)
hSync <= (CounterX>=656) && (CounterX<752);
always @(posedge i_pixclk)
vSync <= (CounterY>=490) && (CounterY<492);
// Convert the 8-bit colours into 10-bit TMDS values
wire [9:0] TMDS_red, TMDS_grn, TMDS_blu;
TMDS_encoder encode_R(.clk(i_pixclk), .VD(i_red), .CD(2'b00),
.VDE(DrawArea), .TMDS(TMDS_red));
TMDS_encoder encode_G(.clk(i_pixclk), .VD(i_grn), .CD(2'b00),
.VDE(DrawArea), .TMDS(TMDS_grn));
TMDS_encoder encode_B(.clk(i_pixclk), .VD(i_blu), .CD({vSync,hSync}),
.VDE(DrawArea), .TMDS(TMDS_blu));
// Strobe the TMDS_shift_load once every 10 i_tmdsclks
// i.e. at the start of new pixel data
reg [3:0] TMDS_mod10=0;
reg TMDS_shift_load=0;
always @(posedge i_tmdsclk) begin
if (i_reset) begin
TMDS_mod10 <= 0;
TMDS_shift_load <= 0;
end else begin
TMDS_mod10 <= (TMDS_mod10==4'd9) ? 4'd0 : TMDS_mod10+4'd1;
TMDS_shift_load <= (TMDS_mod10==4'd9);
end
end
// Latch the TMDS colour values into three shift registers
// at the start of the pixel, then shift them one bit each i_tmdsclk.
// We will then output the LSB on each i_tmdsclk.
reg [9:0] TMDS_shift_red=0, TMDS_shift_grn=0, TMDS_shift_blu=0;
always @(posedge i_tmdsclk) begin
if (i_reset) begin
TMDS_shift_red <= 0;
TMDS_shift_grn <= 0;
TMDS_shift_blu <= 0;
end else begin
TMDS_shift_red <= TMDS_shift_load ? TMDS_red: {1'b0, TMDS_shift_red[9:1]};
TMDS_shift_grn <= TMDS_shift_load ? TMDS_grn: {1'b0, TMDS_shift_grn[9:1]};
TMDS_shift_blu <= TMDS_shift_load ? TMDS_blu: {1'b0, TMDS_shift_blu[9:1]};
end
end
// Finally output the LSB of each color bitstream
assign o_red= TMDS_shift_red[0];
assign o_grn= TMDS_shift_grn[0];
assign o_blu= TMDS_shift_blu[0];
endmodule
module TMDS_encoder(
input clk, // 250 MHz
input [7:0] VD, // video data (red, green or blue)
input [1:0] CD, // control data
input VDE, // video data enable, to choose between CD (when VDE=0) and VD (when VDE=1)
output reg [9:0] TMDS = 0
);
wire [3:0] Nb1s = {3'b0, VD[0]} + {3'b0, VD[1]} + {3'b0, VD[2]}
+ {3'b0, VD[3]} + {3'b0, VD[4]} + {3'b0, VD[5]}
+ {3'b0, VD[6]} + {3'b0, VD[7]};
wire XNOR = (Nb1s>4'd4) || (Nb1s==4'd4 && VD[0]==1'b0);
// To keep Verilator happy, we create individual wires, determine
// their values and then merge them into q_m[]
wire QM0, QM1, QM2, QM3, QM4, QM5, QM6, QM7, QM8;
assign QM0= VD[0];
assign QM1= QM0 ^ VD[1] ^ XNOR;
assign QM2= QM1 ^ VD[2] ^ XNOR;
assign QM3= QM2 ^ VD[3] ^ XNOR;
assign QM4= QM3 ^ VD[4] ^ XNOR;
assign QM5= QM4 ^ VD[5] ^ XNOR;
assign QM6= QM5 ^ VD[6] ^ XNOR;
assign QM7= QM6 ^ VD[7] ^ XNOR;
assign QM8= ~XNOR;
wire [8:0] q_m = { QM8, QM7, QM6, QM5, QM4, QM3, QM2, QM1, QM0 };
reg [3:0] balance_acc = 0;
wire [3:0] balance = {3'b0, q_m[0]} + {3'b0, q_m[1]} + {3'b0, q_m[2]}
+ {3'b0, q_m[3]} + {3'b0, q_m[4]} + {3'b0, q_m[5]}
+ {3'b0, q_m[6]} + {3'b0, q_m[7]} - 4'd4;
wire balance_sign_eq = (balance[3] == balance_acc[3]);
wire invert_q_m = (balance==0 || balance_acc==0) ? ~q_m[8] : balance_sign_eq;
wire [3:0] balance_acc_inc = balance
- {3'b0,
({q_m[8] ^ ~balance_sign_eq} & ~(balance==0 || balance_acc==0)) };
wire [3:0] balance_acc_new = invert_q_m ? balance_acc-balance_acc_inc : balance_acc+balance_acc_inc;
wire [9:0] TMDS_data = {invert_q_m, q_m[8], q_m[7:0] ^ {8{invert_q_m}}};
wire [9:0] TMDS_code = CD[1] ? (CD[0] ? 10'b1010101011 : 10'b0101010100) : (CD[0] ? 10'b0010101011 : 10'b1101010100);
always @(posedge clk) TMDS <= VDE ? TMDS_data : TMDS_code;
always @(posedge clk) balance_acc <= VDE ? balance_acc_new : 4'h0;
endmodule