fpga4fun.comwhere FPGAs are fun

10BASE-T FPGA interface - Receiving packets

The receiver can be used for 2 things: Here's an example of packet that I sniffed on my local network:

55 55 55 55 55 55 55 D5 00 C0 02 37 57 28 00 10 A4 7B EA 80 08 00 45 00 00 3C 02 24 00 00 80 01 B7 47 C0 A8 00 04 C0 A8 00 01 08 00 42 5C 02 00 09 00 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 61 62 63 64 65 66 67 68 69 62 31 C5 4E

I'll let you dissect it (ping from "192.168.0.4" to "192.168.0.1").

10BASE-T receiver

A 10BASE-T receiver consists of the following:

Differential input

We receive a differential (2 wires) signal from RD+/RD-.
The signal is usually coupled magnetically (using a little transformer), and then converted to a common-mode signal (1 wire plus ground) using an integrated comparator or a couple of transistors.

I didn't have a transformer on hands, nor a fast enough integrated comparator. So I used a capacitive coupling scheme followed by transistors.

The schematic of my circuit is:


The 2K resistor on the collector of the second transistor is higher than the value on the first transistor (1K) to help getting a 50% duty cycle ratio signal on the output. Otherwise, the signal at the output is not square...

About the capacitive coupling: I believe that it works as well as inductive coupling but has one drawback: when you insert the cable to the receiver, the voltage potential difference between the receiver and the driver might send a current pulse into the receiver (if the driver is not isolated himself by an transformer). Just my idea, I haven't found any relevant information anywhere. Anybody has more info?

Clock extraction

The data in common-mode is still Manchester encoded.
The Manchester encoding scheme works in such a way that a transition is always created in the middle of a logical bit. Remember that: That means that a transition may also appear in between 2 bits (if the same logical bit is sent twice consecutively).

I know of three basic ways to do the clock extraction: I plan to try all three techniques. For now, let's try the first one.

Oversampling the signal

That's the "brute force" method. The advantage is that the complete decoding is done in the FPGA. The inconvenient is that you need a high frequency clock.

Bits extraction
I decided to use a 48MHz sampling frequency.
Doing the Manchester decoding takes 3 steps.
  1. Sample and synchronize the data coming in.
  2. Detect the edges and start a counter. The trick is that the counter is made so that it ignores any subsequent edge until it rolls-over (to detect only Manchester middle-bit transitions).
  3. Once an edge is detected, the next bit is available 3 counts away (at 48MHz, the period is about 21ns, so three counts are 63ns, right in the middle of the next bit). We move each bit into an 8-bits shift register.
reg [2:0] in_data;
always @(posedge clk48) in_data <= {in_data[1:0], manchester_data_in};

reg [1:0] cnt;
always @(posedge clk48) if(|cnt || (in_data[2] ^ in_data[1])) cnt<=cnt+1;

reg [7:0] data;
reg new_bit_avail;
always @(posedge clk48) new_bit_avail <= (cnt==3);
always @(posedge clk48) if(cnt==3) data<={in_data[1],data[7:1]};

The bits are coming in!
Preamble/SFD synchronization
So far, we just have bits synchronization (we know when each bit is coming, and its value). We know that a byte is starting every 8 bits, but starting at which bit?

To allow byte synchronization, the Ethernet frame starts with this 8 bytes sequence:
55 55 55 55 55 55 55 D5

In binary, 0x55 is 01010101, while 0xD5 is 11010101. Also Ethernet specifies that the LSB is sent first (for example 0xD5 is sent as 1, 0, 1, 0, 1, 0, 1, 1). So we receive an alternate pattern of 1's and 0's, and as soon as we detect 2 consecutives 1's, we know that the real data is coming next.

reg end_of_Ethernet_frame;

reg [4:0] sync1;
always @(posedge clk48)
if(end_of_Ethernet_frame)
  sync1 <= 0;
else
if(new_bit_avail)
begin
  if(!(data==8'h55 || data==8'hAA)) // not preamble?
    sync1 <= 0;
  else
  if(~&sync1) // if all bits of this "sync1" counter are one, we decide that enough of the preamble
                  // has been received, so stop counting and wait for "sync2" to detect the SFD
    sync1 <= sync1 + 1; // otherwise keep counting
end

reg [9:0] sync2;
always @(posedge clk48)
if(end_of_Ethernet_frame)
  sync2 <= 0;
else
if(new_bit_avail)
begin
  if(|sync2) // if the SFD has already been detected (Ethernet data is coming in)
    sync2 <= sync2 + 1; // then count the bits coming in
  else
  if(&sync1 && data==8'hD5) // otherwise, let's wait for the SFD (0xD5)
    sync2 <= sync2 + 1;
end

wire new_byte_available = new_bit_avail && (sync2[2:0]==3'h0) && (sync2[9:3]!=0);

Finally, Ethernet data is flowing in!
End of frame
If no clock transition is detected for some time, that's the end of the Ethernet frame.

reg [2:0] transition_timeout;

always @(posedge clk48)
if(in_data[2]^in_data[1]) // transition detected?
  transition_timeout <= 0;
else
if(~&cnt)
  transition_timeout <= transition_timeout + 1;

always @(posedge clk48) end_of_Ethernet_frame <= &transition_timeout;

Wrapping it up
A standalone application would require checking the CRC (present at the end of the Ethernet packet) for bad transmission errors.
Here, I just send the complete data to a PC - that displays it or does whatever it likes.

wire [7:0] q_fifo;
fifo myfifo(.data(data), .wrreq(new_byte_available), .wrclk(clk48),
                .q(q_fifo), .rdreq(rdreq), .rdclk(clk), .rdempty(rdempty));

wire TxD_busy;
wire TxD_start = ~TxD_busy & ~rdempty;
assign rdreq = TxD_start;

async_transmitter async_txd(.clk(clk), .TxD(TxD), .TxD_start(TxD_start), .TxD_busy(TxD_busy), .TxD_data(q_fifo));

The complete code is available here.



--- to be continued --- to be continued --- to be continued --- to be continued ---

Links