Sunday, July 8, 2012

ENC28J60 - PSoC3 - Part 4 Packet Handling & ARP

We'll talk about the first steps into networking with the ENC28J60.

For Transmitting a packet,
You create a packet (based on its established structure,depending on the protocol etc) and then write that to the ENC28J60's buffer,and have it sent.Check the status vectors to see how that transmission went.

For processing replies to packets that you may have sent,you poll the EPKTCNT register,and if that happens to be non zero,read in the packet adjusting the necessary buffer pointers,checking the quality of the received packet using the status vectors.

So those will be our two basic functions,apart from the initialization one,of course.

For a great introduction to the Address Resolution protocol (popularly shortened to 'ARP') and its packet format,I suggest you visit this page from the 'The TCP/IP Guide'.Its truly a one-of-a-kind reference for TCP/IP stuff on the internet,and I highly recommend reading it whenever in doubt.

What ARP basically does is,it finds the MAC Address for a particular IP address on a local area network.
So,say my router(or any connected network device.The router is just an example) has the IP address,and my ENC28J60 is 192.168.153.If I(the ENC chip),wants to find out the MAC address of the router,I send out an ARP Request,to which I get an ARP reply from the router,which will bear its MAC Address.

Why would I(the ENC chip) need to know the MAC Address of the connected router? Well,future packet transactions will require this basic information.

Since packets are basically structured data,a very natural way to represent them in code,is via structures.For an ARP packet,we define the following structs:

The ethernet header will have the DestAddrs as FF:FF:FF:FF:FF:FF,since we 'broadcast' our ARP request.The SrcAddrs will be our ENC chip's MAC address,since it is the source of the request.The type field will be set to 0x0806,since its a packet of type ARP.

For the rest of the fields,after the IP header,we set the hardware to 6,since we are talking about a LAN ..basically an IEEE 802 network.
The protocol will be IPv4,which means we set protocol to 0x800 which corresponds to the EtherType code for the Internet Protocol.
hardwareSize is the length of the MAC Address in bytes.It shall be 6.
protocolSize is the length of the IP Address in bytes.For us,its IPv4,so 4.
opCode decides if its an ARP Request or Reply.Since we will be putting in a request,it should be 0x0001.For a Reply,that is 0x0002.
senderMAC is the ENC's MAC Address.senderIP is the ENC's IP.
targetMAC will be kept at 00:00:00:00:00:00,since we do not know this,at this point.The last field,is the targetIP one,which will be the IP Address of the device whose MAC Address you want to find.

So,after declaring a variable of type ARP and filling its fields with relevant info,all we need to do is call the MACWrite function,and have that packet sent.
After we've sent it out,we wait for a reply in an endless loop,and process the reply when it comes in.Heres what the code for that looks like:

Thats all theres to it,for an ARP Request.
Heres what a Wireshark session with that ARP request looks like
You may check out the ARP example in my github repo,here.

We'll do ICMP(Ping) and then UDP in the coming posts.

ENC28J60 - PSoC3 - Part 3 - Initialization and Packet Handling

With the basic read/write functions done,we'll look into the Initialization of the chip,and how we'll handle packets.

Before we get into that,we need to decide how to divide that 8kb buffer.Lets have,
0x0000-0x0FFF be the RX Buffer,and
0x1000-0x1FFF be the TX Buffer.

There are 6 pointers that need to be set,based on that information.
(See page 20,Figure 3-2 of the datasheet) The following diagram is indicative of how things are laid out.
Image credits to the ENC28J60 datasheet.Edited a bit.
The ERXSTH:ERXSTL and ERXNDH:ERXNDL define the limits of the RX buffer,and the buffer wraps around.What I mean is,if there is data being written,and we happen to come to the end of the RX buffer,then the writing continues,but the location is from ERXST onwards.In two words,its a Circular FIFO.Note that we should not modify these when the chip is in action(RX/TX is on.).If we somehow need to,then we should stop the operations by clearing the ECON1.RXEN bit,and then play with these.

Similarly with the ETXST and ETXND. The ERXWRPTH:ERXWRPTL(not shown above) registers define a location within the FIFO where the chip will write bytes that it receives. The pointer is read-only and is automatically updated by the hardware whenever a new packet is successfully received.

Initialization Steps:
  1. Reset the chip via the designated SPI command(0xFF).
  2. Setup the pointers discussed above. 
  3.  Configure the ERXFCON,the receive filter control register.The ANDOR bit will be left at its initial 0.That means, Packets will be accepted unless all enabled filters reject the packet.So,if the filters dont chuck the packet out,the packet will be accepted.We will enforce Unicast,Pattern Match and the post filter CRC Check bits.See page 50 of the datasheet.
  4. Configure the MAC Registers.This part I've borrowed from the tuxgraphics code,So credits to them for that.
  5.  Assign the MAC Address to the ENC chip,using the MAADR1,2,3,4,5 registers.
  6. Enable reception of frames by setting the RXEN bit in ECON1. 
The writing of a packet(to be sent by the ENC chip) to the TX buffer is also a multi step process.Heres how it goes:
  1. Set the buffer pointers to the right places.ETXST and EWRPT(buffer write pointer) both to 0x1000,which is where we decided will be the start of our TX buffer space.
  2. Now,when transmitting packets,we need to first write a 'Per packet control byte',then our packet bytes,and set the ETXND(End of transmit buffer) pointer to the last byte of our packet.Heres what it looks like in code: 

Heres where I'd like to draw attention to a point in the Errata,
So,we'll do 
SetBitField(ECON1, ECON1_TXRST);
ClrBitField(ECON1, ECON1_TXRST);
ClrBitField(EIR, EIR_TXERIF | EIR_TXIF);  and then set TXRTS bit in the ECON1 to command a packet transmit. Very important.

We can check the status of the transmit(done or underway) based on the TXIF bit in the EIR Register.Ofcourse,we'd need to enable interrupts on packet transmitted before hand.
We clear the TXRTS bit,when we exit the above waiting routine.Now after the packet is sent,the chip writes a bunch of bytes of status vectors after our packet data.We read those status vector bytes into a variable we declared based on a union we define in the enc28j60.h header file.This awesome organization of status vectors and more using structures is the work of Duncan Frost,and I've merely followed on with it.Credits to him.
We can then check the status vectors to see what happened to the packet we wanted to send.

For reception of packets, we must
  1. First check the EPKTCNT register,which contains the count of packets received.If thats non-zero,that means we have something to read.
  2. This packet would be written to the start of the RX buffer,so we set the Read pointers to the start of the RX buffer.
  3. See the diagram on page 45 of the datasheet to see how packets received are written into the buffer.Theres the next packet pointer(2 bytes) and then the receive status vectors(32 bits,or 4 bytes) after which we have our actual packet data.Note that the next packet pointer,is stored in the buffer in  Little Endian way.
  4. We read the status vectors to see whats the status on the packet received,and read it in if the RxOk bit is set.We can get the length of the packet,by subtracting 4 from the ByteCount field in the status vectors.We subtract 4,since the CRC(that the chip appends) is of 4 bytes.
  5. If we are done reading the packet,we set the ERXRDPT to the Next packet's start,and decrement EPKTCNT,to indicate that we finished processing that packet.
Heres another place where I'd like to point out something in the Errata.
You may refer to the code linked here,to see how that has been implemented in the MACRead function.

Thats all for this part,and in the next blog post in this series,we'll look at ARP Requests and packet structure.