diff --git a/groovylight/platform/colorlight_5a_75b_8_0.py b/groovylight/platform/colorlight_5a_75b_8_0.py new file mode 100644 index 0000000..bf04305 --- /dev/null +++ b/groovylight/platform/colorlight_5a_75b_8_0.py @@ -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) diff --git a/groovylight/soc.py b/groovylight/soc.py new file mode 100644 index 0000000..c2b69c8 --- /dev/null +++ b/groovylight/soc.py @@ -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 + diff --git a/src/blink.lpf b/src/blink.lpf deleted file mode 100644 index 242885f..0000000 --- a/src/blink.lpf +++ /dev/null @@ -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; diff --git a/src/blink.v b/src/blink.v deleted file mode 100644 index fc55ec8..0000000 --- a/src/blink.v +++ /dev/null @@ -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