commit 112785d363e382214bfff21d6d064884d1781406
parent 6ef5a58eb0c8908a70335e429ebca27207e4e5d7
Author: Brian Swetland <swetland@frotz.net>
Date: Wed, 22 Jan 2020 18:22:58 -0800
ethernet: rgmii rx and colorlight demo project
- eth_rgmii_rx handles the relatively simple processing
of inbound gigabit ethernet packets, including discarding
the preamble and validating the FCS
- colorlight project dumps packets on to VGA display
connected to J1 (2bpp resistor-ladder dac style)
- packets are dumped in hex, the inverted byte at the end
of each packet indicates FCS status (green is ok, red is not)
- tested on colorlight 5A-75E
Diffstat:
6 files changed, 392 insertions(+), 0 deletions(-)
diff --git a/hdl/colorlight.lpf b/hdl/colorlight.lpf
@@ -0,0 +1,42 @@
+FREQUENCY NET "phy_clk" 25 MHZ;
+FREQUENCY NET "phy0_rxc" 125 MHZ;
+
+fREQUENCY NET "clk250m" 250 MHZ;
+FREQUENCY NET "clk125m" 125 MHZ;
+
+LOCATE COMP "phy_clk" SITE "P3";
+LOCATE COMP "phy_reset_n" SITE "P4";
+
+LOCATE COMP "btn" SITE "R16";
+
+LOCATE COMP "phy_mdc" SITE "N5";
+LOCATE COMP "phy_mdio" SITE "P5";
+
+LOCATE COMP "phy0_gtxclk" SITE "G1";
+LOCATE COMP "phy0_txd[0]" SITE "G2";
+LOCATE COMP "phy0_txd[1]" SITE "H1";
+LOCATE COMP "phy0_txd[2]" SITE "J1";
+LOCATE COMP "phy0_txd[3]" SITE "J3";
+LOCATE COMP "phy0_tx_en" SITE "K1";
+LOCATE COMP "phy0_rxc" SITE "H2";
+LOCATE COMP "phy0_rxd[0]" SITE "K2";
+LOCATE COMP "phy0_rxd[1]" SITE "L1";
+LOCATE COMP "phy0_rxd[2]" SITE "N1";
+LOCATE COMP "phy0_rxd[3]" SITE "P1";
+LOCATE COMP "phy0_rx_dv" SITE "P2";
+
+LOCATE COMP "j1r0" SITE "B3";
+LOCATE COMP "j1g0" SITE "A2";
+LOCATE COMP "j1b0" SITE "B2";
+LOCATE COMP "j1r1" SITE "B1";
+LOCATE COMP "j1g1" SITE "C2";
+LOCATE COMP "j1b1" SITE "C1";
+LOCATE COMP "glb_e" SITE "J17";
+LOCATE COMP "glb_a" SITE "F1";
+LOCATE COMP "glb_b" SITE "F2";
+LOCATE COMP "glb_c" SITE "E1";
+LOCATE COMP "glb_d" SITE "E2";
+LOCATE COMP "glb_clk" SITE "C18";
+LOCATE COMP "glb_alat" SITE "J18";
+LOCATE COMP "glb_bln" SITE "H16";
+
diff --git a/hdl/colorlight.sv b/hdl/colorlight.sv
@@ -0,0 +1,95 @@
+`default_nettype none
+
+module top(
+ input wire phy_clk,
+ output wire phy_reset_n,
+ input wire phy0_rxc,
+ input wire [3:0]phy0_rxd,
+ input wire phy0_rx_dv,
+// output wire glb_clk,
+// output wire glb_bln,
+ output wire j1r0,
+ output wire j1r1,
+ output wire j1g0,
+ output wire j1g1,
+ output wire j1b0,
+ output wire j1b1,
+ output wire glb_a,
+ output wire glb_b,
+ input wire btn
+);
+
+wire clk25m = phy_clk;
+
+`ifdef PLL
+wire clk125m;
+wire clk250m;
+
+pll_25_125_250 pll(
+ .clk25m_in(phy_clk_in),
+ .clk125m_out(clk125m),
+ .clk250m_out(clk250m),
+ .locked()
+);
+`endif
+
+wire [7:0]rx_data;
+wire rx_valid;
+wire rx_sop;
+wire rx_eop;
+wire rx_crc_ok;
+
+eth_rgmii_rx eth_rx(
+ .rx_clk(phy0_rxc),
+ .pin_rx_dv(phy0_rx_dv),
+ .pin_rx_data(phy0_rxd),
+ .data(rx_data),
+ .valid(rx_valid),
+ .sop(rx_sop),
+ .eop(rx_eop),
+ .crc_ok(rx_crc_ok)
+);
+
+assign j1r1 = j1r0;
+assign j1b1 = j1b0;
+assign j1g1 = j1g0;
+
+reg [31:0]reset = 32'd0;
+always @(posedge clk25m)
+ reset <= { reset[30:0], 1'b1 };
+
+assign phy_reset_n = reset[31];
+
+reg [11:0]waddr = 12'd0;
+
+reg [27:0]color = 28'h1234567;
+
+always_ff @(posedge phy0_rxc) begin
+ color <= rx_eop ? { color[23:0], color[27:24] } : color;
+ waddr <= (rx_eop | rx_valid) ? (waddr + 12'd2) : waddr;
+end
+
+wire [15:0]mark = rx_crc_ok ? { 16'h0200 } : { 16'h04FF };
+
+display #(
+ .BPP(1),
+ .RGB(1),
+ .WIDE(0),
+ .HEXMODE(1)
+ ) display0 (
+ .clk(clk25m),
+ .red(j1r0),
+ .grn(j1g0),
+ .blu(j1b0),
+ .hsync(glb_a),
+ .vsync(glb_b),
+ .active(),
+ .frame(),
+ .wclk(phy0_rxc),
+ .waddr(waddr),
+ .wdata(rx_eop ? mark : { color[27:24], 4'h0, rx_data }),
+ .we((rx_eop | rx_valid) & btn)
+);
+
+endmodule
+
diff --git a/hdl/ethernet/eth_rgmii_rx.sv b/hdl/ethernet/eth_rgmii_rx.sv
@@ -0,0 +1,128 @@
+// Copyright 2020 Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0.
+
+`default_nettype none
+
+module eth_rgmii_rx (
+ // 2.5MHz / 25MHz / 125MHz RGMII clock
+ input wire rx_clk,
+
+ // RGMII input
+ input wire pin_rx_dv,
+ input wire [3:0]pin_rx_data,
+
+ // Packet byte data output
+ output reg [7:0] data = 0,
+
+ // Active when data is a valid byte
+ output reg valid = 0,
+
+ // Active for one rx_clk cycle when start of packet detected
+ // (start of preamble). Typically 8 rx_clk's before the first
+ // valid data byte arrives.
+ output reg sop = 0,
+
+ // Active for one rx_clk cycle when the packet has ended
+ // (*after) the last valid data byte arrives
+ output reg eop = 0,
+
+ // Active when eop is active if packet FCS was valid
+ output reg crc_ok = 0
+);
+
+// NOTES:
+// 1. This is only functional for 1Gbe rates (125MHz clock) at present
+// 2. It considers any packet preamble consisting of one or more 0x55s
+// and ending with an 0xD5 valid
+
+wire rx_dv;
+wire rx_err;
+wire [7:0]rx_data;
+
+// hardware-specific io buffers, delays, etc.
+eth_rgmii_rx_glue glue (
+ .rx_clk(rx_clk),
+ .pin_rx_dv(pin_rx_dv),
+ .pin_rx_data(pin_rx_data),
+ .rx_dv(rx_dv),
+ .rx_err(rx_err),
+ .rx_data(rx_data)
+);
+
+wire [31:0]crc;
+
+localparam IDLE = 2'd0;
+localparam PREAMBLE = 2'd1;
+localparam PACKET = 2'd2;
+localparam INVALID = 2'd3;
+
+reg [1:0]state = IDLE;
+reg [1:0]next_state;
+reg next_valid;
+reg [7:0]next_data;
+reg next_sop;
+reg next_eop;
+reg next_crc_ok;
+reg crc_en;
+
+always_comb begin
+ next_state = state;
+ next_data = data;
+ next_valid = 1'b0;
+ next_sop = 1'b0;
+ next_eop = 1'b0;
+ next_crc_ok = crc_ok;
+ crc_en = 1'b0;
+
+ case (state)
+ IDLE: if (rx_dv) begin
+ if (rx_data != 8'h55) begin
+ next_state = INVALID;
+ end else begin
+ next_state = PREAMBLE;
+ next_crc_ok = 1'b0;
+ next_sop = 1'b1;
+ end
+ end
+ PREAMBLE: begin // .. 55 55 55 D5
+ if (rx_data == 8'hD5) begin
+ next_state = PACKET;
+ end else if (rx_data != 8'h55) begin
+ next_state = INVALID;
+ end
+ end
+ PACKET: begin
+ if (rx_dv == 1'b1) begin
+ crc_en = 1'b1;
+ next_data = rx_data;
+ next_valid = 1'b1;
+ end else begin
+ next_crc_ok = (crc == 32'hDEBB20E3);
+ next_eop = 1'b1;
+ next_state = IDLE;
+ end
+ end
+ INVALID: if (rx_dv == 1'b0) begin
+ next_state = IDLE;
+ end
+ endcase
+end
+
+always_ff @(posedge rx_clk) begin
+ state <= next_state;
+ data <= next_data;
+ valid <= next_valid;
+ sop <= next_sop;
+ eop <= next_eop;
+ crc_ok <= next_crc_ok;
+end
+
+eth_crc32_8 crc32(
+ .clk(rx_clk),
+ .en(crc_en),
+ .rst(sop),
+ .din(rx_data),
+ .crc(crc)
+);
+
+endmodule
diff --git a/hdl/ethernet/eth_rgmii_rx_glue_ecp5.sv b/hdl/ethernet/eth_rgmii_rx_glue_ecp5.sv
@@ -0,0 +1,60 @@
+// Copyright 2020 Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0.
+
+`default_nettype none
+
+module eth_rgmii_rx_glue (
+ input wire rx_clk,
+ input wire pin_rx_dv,
+ input wire [3:0]pin_rx_data,
+ output wire rx_dv,
+ output wire rx_err,
+ output wire [7:0]rx_data
+);
+
+`ifndef verilator
+wire delay_rx_dv;
+wire [3:0]delay_rx_data;
+
+DELAYF #(
+ .DEL_MODE("SCLK_CENTERED"),
+ .DEL_VALUE(80) // units of ~25ps
+ ) ctrl_delay (
+ .LOADN(1),
+ .MOVE(0),
+ .DIRECTION(0),
+ .A(pin_rx_dv),
+ .Z(delay_rx_dv)
+);
+IDDRX1F ctrl_ddr (
+ .D(delay_rx_dv),
+ .SCLK(rx_clk),
+ .RST(0),
+ .Q0(rx_dv),
+ .Q1(rx_err)
+);
+
+genvar i;
+
+generate for (i = 0; i < 4; i++) begin
+DELAYF #(
+ .DEL_MODE("SCLK_CENTERED"),
+ .DEL_VALUE(80) // units of ~25ps
+ ) data_delay (
+ .LOADN(1),
+ .MOVE(0),
+ .DIRECTION(0),
+ .A(pin_rx_data[i]),
+ .Z(delay_rx_data[i])
+);
+IDDRX1F data_ddr (
+ .D(delay_rx_data[i]),
+ .SCLK(rx_clk),
+ .RST(0),
+ .Q0(rx_data[i]),
+ .Q1(rx_data[i+4])
+);
+end endgenerate
+`endif
+
+endmodule
diff --git a/hdl/lattice/ecp5_pll_25_125_250.v b/hdl/lattice/ecp5_pll_25_125_250.v
@@ -0,0 +1,58 @@
+// $ ecppll -i 25 -o 250 --clkout1 125
+// 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 pll_25_125_250
+(
+ input clk25m_in, // 25 MHz, 0 deg
+ output clk250m_out, // 250 MHz, 0 deg
+ output clk125m_out, // 125 MHz, 0 deg
+ output locked
+);
+
+`ifndef verilator
+(* FREQUENCY_PIN_CLKI="25" *)
+(* FREQUENCY_PIN_CLKOP="250" *)
+(* FREQUENCY_PIN_CLKOS="125" *)
+(* 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),
+ .FEEDBK_PATH("CLKOP"),
+ .CLKFB_DIV(10)
+ ) pll_i (
+ .RST(1'b0),
+ .STDBY(1'b0),
+ .CLKI(clk25m_in),
+ .CLKOP(clk250m_out),
+ .CLKOS(clk125m_out),
+ .CLKFB(clk250m_out),
+ .CLKINTFB(),
+ .PHASESEL0(1'b0),
+ .PHASESEL1(1'b0),
+ .PHASEDIR(1'b1),
+ .PHASESTEP(1'b1),
+ .PHASELOADREG(1'b1),
+ .PLLWAKESYNC(1'b0),
+ .ENCLKOP(1'b0),
+ .LOCK(locked)
+ );
+`endif
+
+endmodule
diff --git a/project/colorlight.def b/project/colorlight.def
@@ -0,0 +1,9 @@
+PROJECT_TYPE := nextpnr-ecp5
+
+PROJECT_SRCS := hdl/colorlight.sv hdl/colorlight.lpf
+PROJECT_SRCS += hdl/lattice/ecp5_pll_25_125_250.v
+PROJECT_SRCS += hdl/ethernet/eth_rgmii_rx.sv hdl/ethernet/eth_rgmii_rx_glue_ecp5.sv
+PROJECT_SRCS += hdl/ethernet/eth_crc32_8.sv
+PROJECT_SRCS += hdl/display/display.sv hdl/display/display_timing.sv
+
+PROJECT_NEXTPNR_OPTS := --25k --package CABGA381 --speed 6