//
//				ethernet.v - Full-duplex Ethernet RMII Interface
//
//					(C) Copyright 2008-2010 John B. Stephensen
//
// This Verilog source file and all its derivatives are licensed only for
// personal non-profit educational use in the Amateur Radio Service and
// the license is not transferrable. The information is provided as-is for
// experimental purposes and the author does not warranty its freedom
// from defects or its suitability for any specific application.
//
// This module consists of a 4 kB transmit buffer RAM and a 2 kB receiver buffer RAM
// plus control logic. The PHY interface data is 2-bits wide and the interface to
// the CPU is 16 bits wide. The PHY and data I/O clocks are asynchronous.
//
// The CPU formats a frame into memory starting at 0. The length register is then set
// to the length of the frame and the frame is transmitted. The CPU must wait for the
// transmit empty flag before loading and sending another frame.
//
// The receiver stores received dibits in the buffer RAM and increments RIADDR. When
// RDV goes low, RIADDR is copied into the length register and the swap bit complement
// to exchange buffer halves. The received data may then be read out of the RAM buffer. 
//
// Command Registers:
//    15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
//   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 0 |                                                               | Receive Done
//   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 1 |                   |           TX Length (bytes)               | TX Length
//   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 2 |                                                   |DIR|CLK|DAT| MDI
//   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 3 |                                                           |ENA| Control
//   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//
//   DAT: MDIO data output
//   CLK: MDC clock bit
//   DIR: 0 = receive, 1 = transmit
//   ENA: 0 = disable, 1 = enable MAC & PHY
//
// Status Register:
//    15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
//   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//   |MDI|INT|TXE|RXR|RXE|           RX Length (bytes)               |
//   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//
// Status of Current received Frame:
//   RXE: receiver error (from runt dibits)
// Overall Status:
//   RXR: receiver ready
//   TXE: transmitter buffer empty
//   INT: management interrupt
//   MDI: MDIO data input
//
//	57 slices and 3 BRAM used. 449 Mhz max. CLK rate. 168 MHz max. REFCLK rate.
//
// Normal Warnings:
//		Input <din<15:11>> is never used.
//		Input <ioaddr<2>> is never used.
//		Input <iord> is never used.
//
// TIG required for status, saddr, sdata and synwr/t.
//
// History:
//		4-10-10	created from FIFO-based Ethernet interface (38 fewer slices)
//
module ethernet(
	 input mcs,				// select this module for memory access
    input [9:0] maddr,	// memory address
    input [15:0] min,	// memory data input
    output [15:0] mout,	// memory data output
	 input mwr,				// write to memory
    input mclk,			// memory clock
	 input iocs,			// select this module for I/O
    input [2:0] ioaddr,	// I/O port address
    input [15:0] din,	// data input
    output [15:0] dout,	// data output
	 input iowr,			// write to port
	 input iord,			// read from port
    input clk,				// I/O clock
    input rst,				// I/O reset
    input [1:0] rxd,		// received data
	 input rxer,			// receiver error
    input rxv,				// received data valid
    output [1:0] txd,	// transmit data
    output txv,			// transmit data valid
    input refclk,			// RMII transmit and receive clock
	 output rstn,			// reset external PHY when 0
	 output mdc,			// management clock
	 inout mdio,			// management data
	 input mint,			// interrupt
	 output esof,erxce,eeof,erxswap	// test
    );
// internal signals
reg phyen;				// PHY enable (off state resets external PHY)
wire reset;				// MAC and PHY reset
wire txce;				// transmitter dibit count enable
wire [1:0] txd1;		// transmit RAM output
reg [1:0] txd2;		// synchronized RAM output
reg txv1,txv2;			// transmit RAM output valid
reg [12:0] toaddr;	// transmitter output address (dibits)
reg [12:0] tcount;	// transmitter limit register (dibits)
reg [12:0] riaddr;	// receiver input address counter (dibits)
reg [12:0] rsize;		// received frame size
reg swap,mswap;		// receiver buffer address MSB
wire zero;				// no received frame available
wire done;				// user done with current received frame
reg [3:0] rxd0,rxd1;	// received data shift registers
reg rxvs;				// synchronized received data valid
wire [1:0] rxds;		// synchronized received data dibit
wire sync;				// sync byte received
reg rxen;				// receiver enable
wire rxce;				// receiver dibit count enable
wire sof,eof;			// start and end of received frame
reg rxrdy,rxerr;		// receiver ready and error latches
wire rxswap;			// swap buffer halves
reg mdd;					// management data direction (0=in, 1=out)
reg mdo,mco;			// management output bit and clock bit
wire mdi;				// management data input
wire w0,w1,w3,csw,s2;// port selects
wire swr;				// synchronized write enable, data and address
reg [1:0] saddr;
reg [10:0] sdata;
// I/O port selects
assign csw = iocs & iowr;					// I/O write to this module
assign s2 = iocs & (ioaddr[1:0] == 2);	// management port
assign s3 = iocs & (ioaddr[1:0] == 3);	// configuration port
// TEST
assign esof = sof;
assign erxce = rxce;
assign eeof = eof;
assign erxswap = rxswap;
// configuration register
always @ (posedge clk)
begin
	if (rst) phyen <= 0;		// default is PHY disabled
	else if (s3 & iowr) phyen <= din[0];
end
assign rstn = phyen;
// I/O write synchronization
always @ (posedge clk)
begin
	if (csw) saddr <= ioaddr[1:0];	// needs TIG
	if (csw) sdata <= din[10:0];		// needs TIG
end
async2h synwr (
	.a(csw),
	.aclk(clk),
	.arst(rst),
	.b(swr),
	.bclk(refclk),
	.brst(rst)
	);
assign w0 = swr & (saddr == 0);		// done with received frame
assign w1 = swr & (saddr == 1);		// load transmit frame size
assign w3 = swr & (saddr == 3);		// configuration
assign done = w0;
assign reset = rst | w3;	// internal reset
//
// Ethernet Transmit Logic
//
// transmit frame when tcount > 0
// transmit output address counter and data dibit counter
always @ (posedge refclk)
begin
	if (reset) tcount <= 0;		// number of dibits to transmit
	else if (w1) tcount <= {sdata,2'b00};
	else if (txce) tcount <= tcount - 1;
	if (reset|w1) toaddr <= 0;	// transmitter buffer address for reads
	else if (txce) toaddr <= toaddr + 1;
	txv1 <= txce;
end
// count until length counter is zero
assign txce = |tcount;
// Transmit FIFO - 2-bit output and 16-bit input
RAMB16_S2_S18 txram (
	.ADDRA(toaddr),	// transmit data output port
	.DIA(2'b11),
	.DOA(txd1),
	.CLKA(refclk),
	.WEA(1'b0),			// read bits
	.ENA(1'b1),
	.SSRA(1'b0),
	.ADDRB(maddr),		// transmitter data input port
	.DIB(min),
	.DIPB(2'b11),
	.CLKB(mclk),
	.WEB(mwr),			// write words
	.ENB(mcs),
	.SSRB(1'b0)
	);
// change data on negative edge of reference clock
always @ (negedge refclk)
begin
	txd2 <= txd1;
	txv2 <= txv1;
end
// connect outputs
assign txd = txd2;
assign txv = txv2;
//
// Ethernet Receive Logic
//
// synchronize dibits to internal clock and assemble bytes from dibits arriving LSD first
always @ (posedge refclk)
begin
	rxd0 <= {rxd[0],rxd0[3:1]};	// asmbl. byte
	rxd1 <= {rxd[1],rxd1[3:1]};
	rxvs <= rxv;
end
assign rxds = {rxd1[3],rxd0[3]};	// first shift register bits
// detect sync byte
assign sync = ({rxd1[3],rxd0[3],rxd1[2],rxd0[2],rxd1[1],rxd0[1],rxd1[0],rxd0[0]} == 8'b11010101) & rxvs;
// enable receiver when sync. byte detected and RXV true and disable when RXV false (end of frame)
// set receiver ready at end of good frame and reset when previous frame read or new frame starts
// the RXV signal is valid only during odd dibits at the end of the frame so all even dibits are accepted
always @ (posedge refclk)
begin
	if (reset) rxen <= 0;
	else rxen <= (rxen|sync) & ~eof;
	if (reset) rxrdy <= 0;
	else rxrdy <= (rxrdy|eof) & ~sof & ~zero;
end
assign sof = sync & ~rxen;								// strobe at start of frame
assign eof = rxen & ~rxvs & riaddr[0] & ~rxerr;	// strobe at end of frame if no dibit errors
assign rxce = rxen & (rxvs|~riaddr[0]);			// count after sync detect until data valid disappears
assign rxswap = zero & (eof|rxrdy) & ~sof;		// swap buffers at end of frame or later if CPU not ready
// receiver input address counter and received frame size register
always @ (posedge refclk)
begin
	if (reset) swap <= 0;						// reset when receiver disabled
	else if (rxswap) swap <= ~swap;			// exchange buffers to read new frame
	if (sof) riaddr <= 0;						// reset receiver buffer address at start of frame
	else if (rxce) riaddr <= riaddr + 1;	// then increment while valid data
	if (reset|done) rsize <= 0;				// reset size when CPU finished reading buffer
	else if (rxswap) rsize <= riaddr;		// set size of new frame
end
assign zero = ~|rsize;	// received size latch is zero when buffer available
// Receive buffer - 2-bit input and 16-bit output
always @ (posedge mclk) mswap <= ~swap;	// sync MSB address to memory clock
RAMB16_S1_S9 rxram0 (
	.ADDRA({swap,riaddr}),	// received data input port
	.DIA(rxds[0]),				// even received dibits
	.CLKA(refclk),
	.WEA(rxce), 				// write dibit
	.ENA(1'b1),
	.SSRA(1'b0),
	.ADDRB({mswap,maddr}),	// received data output port
	.DIB(8'hFF),
	.DIPB(1'b1),
	.DOB({mout[14],mout[12],mout[10],mout[8],mout[6],mout[4],mout[2],mout[0]}),
	.CLKB(mclk),
	.WEB(1'b0),					// read-only
	.ENB(mcs),
	.SSRB(1'b0)
	);
RAMB16_S1_S9 rxram1 (
	.ADDRA({swap,riaddr}),	// received data input port
	.DIA(rxds[1]),				// odd received dibits
	.CLKA(refclk),
	.WEA(rxce), 				// write dibit
	.ENA(1'b1),
	.SSRA(1'b0),
	.ADDRB({mswap,maddr}),	// received data output port
	.DIB(8'hFF),
	.DIPB(1'b1),
	.DOB({mout[15],mout[13],mout[11],mout[9],mout[7],mout[5],mout[3],mout[1]}),
	.CLKB(mclk),
	.WEB(1'b0),					// read-only
	.ENB(mcs),
	.SSRB(1'b0)
	);
// latch any receiver error during frame and clear when new frame detected
always @ (posedge refclk)
begin
	if (reset) rxerr <= 0;
	else rxerr <= (rxerr|rxer) & ~sof;	// save dibit error
end
// construct status register
// the number of dibits accepted is always odd due to CRS/DV multiplexing in RMII
assign dout = {mdi,mint,~txv1,~zero,|rsize[1],rsize[12:2]};
// connect management I/O
always @ (posedge clk)
begin
	if (rst) mco <= 0;	// clock
	else if (iowr & s2) mco <= din[1];
	if (rst) mdo <= 0;	// data
	else if (iowr & s2) mdo <= din[0];
	if (rst) mdd <= 0;	// 0 = receive, 1 = transmit
	else if (iowr & s2) mdd <= din[2];
end
// tristate I/O buffer and clock buffer
IOBUF mdiobuf (
	.I(mdo),		// data to outside world
	.O(mdi),		// data from outside world
	.IO(mdio),	// FPGA pin
	.T(mdd)		// enable output (I -> IO)
	);
OBUF mdcbuf (
	.I(mco),	// clock
	.O(mdc)
	);
endmodule
