Skip to content

Instantly share code, notes, and snippets.

@goran-mahovlic
Last active February 26, 2023 14:48
Show Gist options
  • Save goran-mahovlic/b2514ab6cd09e82695b824c6c0b9bf30 to your computer and use it in GitHub Desktop.
Save goran-mahovlic/b2514ab6cd09e82695b824c6c0b9bf30 to your computer and use it in GitHub Desktop.
LAN8720A - ULX4M
/*
** simple hex packet capture
** packet content will be printed from right to left
** 8 lines of 64-bits (64 bytes)
** adjust skip_bytes to see other parts of a longer packet
*/
`default_nettype none
module top_eth_hex_demo
#(
parameter datab2n = 9, // 2**n data bits memory size 9: 512 bits = 64 bytes
parameter skip_bytes = 0 // skip (ignore) data from beginning of packet
)
(
input wire clk_25mhz,
input wire [2:0] btn,
output wire [7:0] led,
input wire [1:0] RGMII_RXD,
output wire [1:0] RGMII_TXD,
output wire RGMII_TX_EN,
input wire RGMII_RX_DV,
input wire RGMII_REF_CLK,
output wire RGMII_TX_CLK,
inout wire ETH_MDIO,
output wire ETH_MDC,
output wire ETH_nRESET,
input wire GLOBAL_EN,
output wire [3:0] gpdi_dp
);
parameter C_color_bits = 16;
localparam reply_len = 82;
reg [7:0] reply[0:reply_len-1];
initial
$readmemh("arp_reply.mem", reply);
// assign led = 0;
// clock generator
wire clk_locked;
wire [3:0] clocks;
wire clk = clocks[0];
ecp5pll
#(
.in_hz( 25*1000000),
.out0_hz(125*1000000),
.out1_hz( 25*1000000),
.out2_hz( 50*1000000),
)
ecp5pll_inst
(
.clk_i(clk_25mhz),
.clk_o(clocks),
.locked(clk_locked)
);
wire clk_shift = clocks[0];
wire clk_pixel = clocks[1];
wire clk_eth = clocks[2];
// ETH RMII LAN8720 signals labelled on the PCB
wire rmii_tx_en ; assign RGMII_TX_EN = rmii_tx_en; // 0:RX 1:TX
wire rmii_tx0 ; assign RGMII_TXD[0] = rmii_tx0;
wire rmii_tx1 ; assign RGMII_TXD[1] = rmii_tx1;
wire rmii_crs = RGMII_RX_DV; // 0:IDLE 1:RX DATA VALID
wire rmii_rx0 = RGMII_RXD[0];
wire rmii_rx1 = RGMII_RXD[1];
wire rmii_nint = clk_eth; //RGMII_TX_CLK; //RGMII_REF_CLK; // clock 50MHz
wire rmii_mdio = ETH_MDIO; // bidirectional
wire rmii_mdc ; assign ETH_MDC = rmii_mdc;
assign RGMII_TX_CLK = clk_eth;
//assign led[6] = ETH_nRESET;
reg init_reset = 1'b0;
assign ETH_nRESET = init_reset;
reg [31:0] cnt = 0;
always @(posedge RGMII_TX_CLK)
begin
cnt <= cnt + 1;
if(cnt[26])
begin
if(!init_reset)
begin
init_reset <= 1'b1;
end
end
end
assign led[7] = cnt[24];
wire rmii_clk = rmii_nint;
assign rmii_mdc = 0; // management clock held 0
// assign gn13 = 0; // not necessary to hold data 0
reg [1:0] R_data[0:2**(datab2n-1)-1]; // collects data
reg [1:0] preamble = 1; // 0:data, 1:wait 5, 2:wait non-5, 3:skip
reg [datab2n-1:0] indx;
always @(posedge rmii_clk)
begin
if(rmii_crs)
begin // data valid
if(preamble==2'd1)
begin
if({rmii_rx1, rmii_rx0} == 2'b01) // 5-pattern
preamble <= 2;
end
else if(preamble==2'd2)
begin
if({rmii_rx1, rmii_rx0} != 2'b01) // end of 5-pattern, D pattern
begin
if(skip_bytes)
begin
indx <= 1-4*skip_bytes; // skip further bytes
preamble = 3;
end
else // nothing to skip, directly to data
begin
indx <= 0;
preamble <= 0;
end
end
end
else if(preamble==2'd3) // skip some data
begin
if(indx == 0)
preamble <= 0;
else
indx <= indx+1; // count skip
end
else // preamble=0, store data
begin
if(indx[datab2n-1]==0)
begin
R_data[indx[datab2n-2:0]] <= {rmii_rx1, rmii_rx0};
indx <= indx + 1;
end
end
end
else // not data valid
begin
preamble <= 1;
end
end
wire [2**datab2n-1:0] R_display; // wiring to display
generate
genvar i;
for(i=0; i<2**(datab2n-1); i++)
assign R_display[i*2+1:i*2] = R_data[i];
endgenerate
// RX LED blink
localparam led_on = 21;
reg [led_on:0] R_rxled, R_txled;
always @(posedge rmii_clk)
begin
if(rmii_crs)
begin
R_rxled <= -1;
end
else
begin
if(R_rxled[led_on])
R_rxled <= R_rxled - 1;
end
if(R_tx_en)
begin
R_txled <= -1;
end
else
begin
if(R_txled[led_on])
R_txled <= R_txled - 1;
end
end
wire [2:0] btn_rising;
btn_debounce
btn_debounce_inst
(
.clk(rmii_clk),
.btn(btn),
.rising(btn_rising)
);
reg [12:0] txindx=0; // 2-bit counter
reg [7:0] R_tx;
reg R_tx_en = 0;
always @(posedge rmii_clk)
begin
if(txindx != {reply_len,2'b00})
begin
if(txindx[1:0]==0)
R_tx <= reply[txindx[12:2]];
else
R_tx <= R_tx[7:2]; // shift
R_tx_en <= 1;
txindx <= txindx + 1;
end
else
begin
if(btn_rising[1])
txindx <= 0;
else
R_tx_en <= 0;
end
end
assign rmii_tx_en = R_tx_en;
assign rmii_tx0 = R_tx[0];
assign rmii_tx1 = R_tx[1];
assign led [5:0] = {R_txled[led_on], R_rxled[led_on]};
wire [7:0] x;
wire [7:0] y;
// for reverse screen:
//wire [7:0] ry = 239-y;
wire [C_color_bits-1:0] color;
hex_decoder_v
#(
.c_data_len(2**datab2n),
.c_row_bits(4),
.c_grid_6x8(1), // NOTE: TRELLIS needs -abc9 option to compile
.c_font_file("hex_font.mem"),
.c_color_bits(C_color_bits)
)
hex_decoder_v_inst
(
.clk(clk),
.data(R_display),
.x(x[7:1]),
.y(y[7:1]),
.color(color)
);
// allow large combinatorial logic
// to calculate color(x,y)
wire next_pixel;
reg [C_color_bits-1:0] R_color;
always @(posedge clk)
if(next_pixel)
R_color <= color;
wire [9:0] beam_x, beam_rx, beam_y;
wire [15:0] beam_color;
wire vga_hsync, vga_vsync, vga_blank;
wire [7:0] vga_r, vga_g, vga_b;
wire [1:0] dvid_red, dvid_green, dvid_blue, dvid_clock;
assign beam_rx = 636 - beam_x;
// HEX decoder needs reverse X-scan, few pixels adjustment for pipeline delay
hex_decoder_v
#(
.c_data_len(2**datab2n),
.c_row_bits(4), // 2**n digits per row (4*2**n bits/row) 3->32, 4->64, 5->128, 6->256
.c_grid_6x8(1), // NOTE: TRELLIS needs -abc9 option to compile
.c_font_file("hex_font.mem"),
.c_x_bits(8),
.c_y_bits(6),
.c_color_bits(16)
)
hex_decoder_dvi_instance
(
.clk(clk_pixel),
.data(R_display),
.x(beam_rx[9:2]),
.y(beam_y[7:2]),
.color(beam_color)
);
vga
vga_instance
(
.clk_pixel(clk_pixel),
.clk_pixel_ena(1'b1),
.test_picture(1'b0),
.beam_x(beam_x),
.beam_y(beam_y),
.vga_hsync(vga_hsync),
.vga_vsync(vga_vsync),
.vga_blank(vga_blank)
);
assign vga_r = {beam_color[15:11],beam_color[11],beam_color[11],beam_color[11]};
assign vga_g = {beam_color[10:5],beam_color[5],beam_color[5]};
assign vga_b = {beam_color[4:0],beam_color[0],beam_color[0],beam_color[0]};
vga2dvid
#(
.c_ddr(1'b1),
.c_shift_clock_synchronizer(1'b0)
)
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_hsync),
.in_vsync(vga_vsync),
.in_blank(vga_blank),
// single-ended output ready for differential buffers
.out_red(dvid_red),
.out_green(dvid_green),
.out_blue(dvid_blue),
.out_clock(dvid_clock)
);
// vendor specific DDR modules
// convert SDR 2-bit input to DDR clocked 1-bit output (single-ended)
ODDRX1F ddr_clock(
.D0(dvid_clock[0]),
.D1(dvid_clock[1]),
.Q(gpdi_dp[3]),
.SCLK(clk_shift),
.RST(1'b0));
ODDRX1F ddr_red(
.D0(dvid_red[0]),
.D1(dvid_red[1]),
.Q(gpdi_dp[2]),
.SCLK(clk_shift),
.RST(1'b0));
ODDRX1F ddr_green(
.D0(dvid_green[0]),
.D1(dvid_green[1]),
.Q(gpdi_dp[1]),
.SCLK(clk_shift),
.RST(1'b0));
ODDRX1F ddr_blue(
.D0(dvid_blue[0]),
.D1(dvid_blue[1]),
.Q(gpdi_dp[0]),
.SCLK(clk_shift),
.RST(1'b0));
endmodule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment