This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Byte can not be aligned in porting LWIP to TMS320F2812

Other Parts Discussed in Thread: TMS320F2812, CONTROLSUITE

Hi...

Porting LWIP to TMS320F2812 + ENC28J60 , the ethernet packet received by F2812 can not  match with the header struct which defined by LWIP.

in LWIP :      sizeof(unsigned char) = 1        sizeof(unsigned short) = 2

in F2812 :   sizeof(unsigned char) = 1        sizeof(unsigned short) = 1

So, the length of struct is different between lwip and F2812, if the struct usd unsigned shor data type.

In following code, I wish the length of eth_hdr is 14, just like defined by lwip, but it is 13 in TMS320F2812 + CCS.

sizeof(eth_hdr) = 13    // in TMS320F2812

sizeof(eth_hdr) = 14    // in LWIP

There are many struct like this in lwip, how can I resolve it ?

THANKS !

//--source code like this-------------------------------------------------------------------------

typedef unsigned char    u8_t;
typedef unsigned short   u16_t;

#define ETHARP_HWADDR_LEN 6
#define ETH_PAD_SIZE 0
PACK_STRUCT_BEGIN
struct eth_addr {
  PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END

PACK_STRUCT_BEGIN
struct eth_hdr {
#if ETH_PAD_SIZE
  PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]);
#endif
  PACK_STRUCT_FIELD(struct eth_addr dest);
  PACK_STRUCT_FIELD(struct eth_addr src);
  PACK_STRUCT_FIELD(u16_t type);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END

  • Hi Jim,

    I'm going to do my best to answer your question, but I'm not all that familiar with lwip so I could be wrong on this.

    The C2000 line of microcontrollers have a 16 bit memory word.  This is the smallest piece of memory that is individually addressable on our achitecture.  This means that the smallest variable you can have in a C environment is also 16 bits wide.  This is why char, short, and int are all the same construct in essence.  Technically on our architecture a byte is 16 bits, but for the purposes of this dialog I'm going to refer to a byte as 8 bits and a word as 16 bits.

    I actually just finished up porting another protocol stack (not tcp/ip) from an 8 bit word architecture to the C28x's 16 bit word architecture, so I have a few ideas for you.  The stack I ported also had packed structs and used sizeof operators.  This really limits the route you can take when you undergo the porting effort. 

    lwIP is a protocol layer that sits on top of a ethernet MAC.  Most of the logic within lwIP ought to be ok without any changes from you.  The area you likely need to concentrate your porting efforts on is the interface between lwIP and the eMAC.  If you can get data into lwIP in the format that it expects you ought to be ok.  For example, on the protocol stack I'm working on, recieved data is passed to the protocol layer in the form of a pointer to a char buffer.  On the original architecture each byte in the char buffer array contained a single data byte.  In order to minimize changes to the protocol stack on the C28x architecture, the recived data is still passed to the protocol layer in the form of a pointer to a char buffer, but each char in the buffer is now 16 bits wide.  The lower byte of each 16 bit word contains the same data that would have been there in the buffer on the original architecture, but the upper byte is all 0s.  In this way the data seen by the protocol layer is effectively the same. 

    The other thing to take into consideration is how the data is interpretted by your protocol layer.  On the stack I've been working on after the protocol layer got the pointer to the char buffer, it typecast the buffer with a packed struct and then accesses the struct.  As I'm sure you can imagine if the fields of the struct are chars they get the same value they would have on the original architecture.  However, if they are a short or a long, the data is now split accross 2 or 4 sixteen bit memory locations and must be reassembled.  The easiest way (yes it was a really a pain, but easy is a relative term...) to fix this problem in my stack was to modify every access to these struct fields to split or reassembled the data depending on whether it was a write or a read.  There is an example of this further down (code section below with the #defines).

    One last thing, be careful with your structs or your sizeof operations will return the "wrong" value.  Let's look at two structs which contain the same amount of data, but have different sizes in memory:

    Code:

    typedef struct{
        unsigned short a;
        unsigned short b;
        unsigned short c;
    }struct1;

    typedef struct{
        unsigned short a;
        unsigned long bc;
    }struct2;

    sizeof(struct1) on a C28x device will return 3, as you would expect given the 16 bit memory word of the architecture.  sizeof(struct2) actually returns 4 in this case on our device even though we can only store a total of 3 words in this structure.  Whats going on you might ask? Well, in order for the CPU/ALU to operate on all 32 bits of variable bc in a single cycle it must be aligned to a 2 word boundary.  The compiler knows this and pads the structure by adding an empty word in between variable a and bc.  This puts bc on a 2 word boundary, but also increases the size of your struct when you didn't tell it to.  I ran into this same issue on the stack I'm working on.  My solution was to simply only use char or short types within the predefined structures I was modifying, and then use a macro to access the memory as needed.  Below is an example:

    Code:

    #define writeShort(ptr,value)    {((tShort *)ptr)->LSB = value & 0xFF; ((tShort *)ptr)->MSB = (value & 0xFF00) >>8 ;}
    #define readShort(ptr) (unsigned short)(((tShort *)ptr)->LSB | ((tShort *)ptr)->MSB << 8)

    typedef struct
    {
        unsigned short LSB;
        unsigned short MSB;
    }tShort;

    Those are my two cents on porting protocol stacks for the 28x architecture.  Like I said I'm not too familiar with lwIP, but I'm happy to take a look at it some more.  I have the sources for lwIP.  Perhaps you could point me in a more specific direction where you are having trouble (i.e. filename) to investigate.

     

    Hope this helps,

    Trey German

  • Hi, Trey

    Thank a lot for your help and code. Your exposition is very useful to me, especially the code.

    ENC28J60 received ethernet packets and stored it in its buffer by byte(8 bits wide). TMS320F2812 readed the packects by byte and save in pbufs(defined by lwip) . Now the packets was stored by byte(16 bits wide). LWIP passed the pointer that pointed to pbufs to upper procotols(IP, ARP, TCP, UDP). Upper protocols defined the different struct for different packets head. Trouble came from here,  the struct's members can not align with the packet head, because the legth of unsigned char is different between TMS320F2812 and LWIP. This condition arise in many other place, such as the head struct of ARP, TCP, UDP, and other functions which need the data in packect.

    I traced the code in CCS, this  problem firstly arose at function : err_t ethernet_input(struct pbuf *p, struct netif *netif)   //defined in etharp.c

    lwip defined data type u8_t   :    typedef unsigned  char    u8_t;

    If the struct used u8_t type, and the struct has relation with packets, the trouble would appear.

    /* the following functions and struct declared and defined in etharp.h , ehtarp.c*/

    err_t
    ethernet_input(struct pbuf *p, struct netif *netif)
    {
      struct eth_hdr* ethhdr;

      /* points to packet payload, which starts with an Ethernet header */
      ethhdr = p->payload;
     
      switch (htons(ethhdr->type)) {     
        /* IP packet? */
        case ETHTYPE_IP:
    #if ETHARP_TRUST_IP_MAC
          /* update ARP table */
          etharp_ip_input(netif, p);
    #endif /* ETHARP_TRUST_IP_MAC */
          /* skip Ethernet header */
          if(pbuf_header(p, -(s16_t)sizeof(struct eth_hdr))) {
            LWIP_ASSERT("Can't move over header in packet", 0);
            pbuf_free(p);
            p = NULL;
          } else {
            /* pass to IP layer */
            ip_input(p, netif);
          }
          break;
         
        case ETHTYPE_ARP:
          /* pass p to ARP module */
          etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p);
          break;

    #if PPPOE_SUPPORT
        case ETHTYPE_PPPOEDISC: /* PPP Over Ethernet Discovery Stage */
          pppoe_disc_input(netif, p);
          break;

        case ETHTYPE_PPPOE: /* PPP Over Ethernet Session Stage */
          pppoe_data_input(netif, p);
          break;
    #endif /* PPPOE_SUPPORT */

        default:
          pbuf_free(p);
          p = NULL;
          break;
      }

      /* This means the pbuf is freed or consumed,
         so the caller doesn't have to free it again */
      return ERR_OK;
    }

     

    Thanks

    Jim

  • Jim,

    I just took a look at the sources and I see what you mean.  The way the buffers and structs are passed around is almost exactly the same as the other stack I've been working on.  I think the best way to fix this is to make the data align the way it ought to as soon as you copy it out of the SPI data register.

    lwIP uses a void pointer that it moves around to expose different parts of each packet to each different layer of the protocol stack.  As long as the addresses that the void pointer points to stay constant across the port with relationship to the data in the payload, you ought to be alright.

    So, in order to accomplish this you need to check a few things and fix a few others.  I'm warning you now, you are going to have to get down and dirty with the stack.  The modifications you will have to make are not difficult, but there will be many of them.  As long as you are methodical and careful, you shouldn't have too much trouble.

    The first thing you need to check is how the data is arranged in the pbuf->payload.  Because lwIP was originally written to expect a single 8 bit byte at each address, we would like to mimic this on the F2812.  I don't know where the SPI functions are in lwIP, but after reading a packet make the memory looks like:

    Address:    Data:

    0x00           0x0012

    0x01           0x0034

    0x02           0x0056

    This is wasteful of memory, but for now let just get something working first.  Also, if you need to do 8 bit access on the C28x you can via the __byte intrinsic (http://processors.wiki.ti.com/index.php/Byte_Accesses_with_the_C28x_CPU)

    The next thing we need to take care of is the structures.  I'm just going to discuss the eth_hdr structure, but this discussion applies to all structures that are used to read or write to the pbuf payload.  Right now if you compile lwIP with the C28x compiler, each field of the struct (eth_hdr) will sit at a address.  This is fine for the fields struct eth_addr dest and struct eth_addr dest, but is a problem for u16_t type.  You see, address 0 contains a single byte which is what eth_addr dest consists of, so when compiled and ran this will be interpretted correctly.  However, u16_t is now split across two address locations which now must be shifted and OR'ed together to form the correct value.  The fix to this problem is a two step solution and this is the messy part.  Every struct that is used to read or write to the pbuf payload field must be modified to use the tShort type that I defined in my last message.  So for instance the eth_hdr would become:

    PACK_STRUCT_BEGIN
    struct eth_hdr {
    #if ETH_PAD_SIZE
      PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]);
    #endif
      PACK_STRUCT_FIELD(struct eth_addr dest);
      PACK_STRUCT_FIELD(struct eth_addr src);
      PACK_STRUCT_FIELD(tShort type);
    } PACK_STRUCT_STRUCT;
    PACK_STRUCT_END

    u8_t's do not need to be changed.  Only u16_t and u32_t.  I think it goes without saying that you will have to expand on my tShort struct for a u32_t to have 4 fields (one for each byte of the variable).  DO NOT, I REPEAT, DO NOT change how u16_t is typedef'ed.  If you do this, you will a) waste alot more memory and b)waste alot more processor time converting between a normal 16 bit format and the fake 8 bit/16 bit memory format and c) waste alot of time because you will have to change EVERY spot where a u16_t is read from or written to.

    Continuing on, the final thing you must change is how the fields which you have now changed to tShort's are accessed.  This is what those #define readShort and writeShort macros were for.  So for instance in the ethernet_input function the first switch statement must be modified from:

    switch (htons(ethhdr->type)) {

    to:

    switch (htons(readShort(&(ethhdr->type)))) {

     

    These should be all the tools you need to get the data to line up and lwIP to interpret it correctly.  If you change the structs first, and then compile without changing where those fields are accessed, CCS will throw errors on every place where the fields are accessed.  This makes it significantly easier to find each place where you need to add the readShort and writeShort.

    I'm sorry there isn't a cleaner solution.  We at TI are fully aware that this is a pain to do, and we are actually looking at a few ways to solve this problem.  If you do get the stack working, if you don't mind, we would appreciate it if you would post a copy of it here on the forums.  I'm sure many of our peers would find it useful.  As always if you need more help along the way feel free to ask!


    Trey German

  • Hi all,

    I am only an observer to this thread - I think these answers should be awarded, never seen this much attention for problem up until now. Respect Trey and thanks very much!

    Andreas

  • Thanks all.

    Trey, thank you very much, thank your detailed and patient exposition.

    I will try to modefiy the code as you said. If the protocol could work , I will post the code here.

    Because so many fields will be involved, I think other MCU meight be well to fit LWIP.

    ENC28J60 use SPI to communicate with TMS320F2812,  But TMS320F2812 did not use his SPI module in my program, I use GPIO simulate SPI.

    There are many caculation in my interrupte routine, when the code run into interruption, time sequence of SPI of F2812 can not satisfied ENC28J60's specification. This can be observed in oscilloscope. Using GPIO simulated SPI  is also slowing down the system speed.

    TMS320F2812, LWIP and ENC28J60 are all not match well each other. So I think it should add a MCU between F2812 and ENC28J60. Just a supposition

    Thanks you, Trey !

     

    Jim

  • No problem, Its my job to help.

    I agree that the F2812 and the ENC28J60 are not a match made in heaven, that being said there is no reason you shouldn't be able to get them to work.

    May I ask why you are not using the onboard SPI peripheral?  When you bit bang SPI using GPIO this wastes a significant amount of processor time which could be spent doing more productive things like decoding packet headers.  Moving to use the real SPI peripheral shouldn't be too difficult.  If you already have the controlSuite software, you ought to be able to find an example of how to use our SPI peripheral in:  (assuming you installed in the default location)

    C:\TI\controlSUITE\device_support\<DEVICE VARIANT>\<LIBRARY VERSION>\DSP2802x_examples_ccsv4\spi_loopback

    Using the onboard SPI peripheral ought to fix the timing issues you are having.

    May I ask what the main application of the F2812 is?  I don't need anything too specific, but are you doing motor control? power supply control? DSP?  Is this a personal project or for work?  If I have a better idea of what you are doing, I can make better suggestions on solutions.  If you don't need the real time control capabilities of our part, perhaps TI's Stellaris micro controller would be a better fit.  They have an onboard ethernet controller, lwIP is already ported and runs on the core, not to mention you get USB and several other useful peripherals.  If you do need real time control capability and ethernet, we are working on a solution.  Keep watching the TI site specifically the C2000 area, I think you'll find we've got you covered.

    I will say this before I go, adding a micro in between the F2812 and the ethernet controller is a bad idea.  If you did this, you are just going to have twice the number of problems.  I live my engineering life by the phrase "Keep it simple".  I can't even begin to tell you the trouble this has saved me in the past.

     

    Trey

  • I use F2812 for motor control(PMSM). It is a personal project. I want the driving board of the motor could communicate with computer by ethernet. Real time control and ethernet are all important function in the case. So I choose F2812 for his PWM module and ENC28J60.

    At first, I use SPI of F218 link to ENC28J60. When SPI reads or writes the ENC28J60, if the main program enters interrupt routine at this time, the clock signal of SPI keeps in hight or low until the interrupt is over. The wave is oberved in oscilloscope. Clock signal becomes a long straight line.  I do not know how to resolve it and I find a example using GPIO. So I abort the SPI, and use GPIO for substitute. It does waste a lots of CPU time.

    Jim

  • Well since you are doing motor control the F2812 is an excellent choice.

    From your description of the problem it sounds like you have written to the SPIDAT register, and then received an interrupt while the SPI peripheral was shifting out the data you had just written.  When you received the interrupt, the SPI peripheral stopped shifting in/out data in the middle of a transmission/receipt, correct?

    If this is the case, I believe you are correct in your frustration.  To the best of my knowledge the SPI peripheral should keep doing whatever it was last told to do, even if an interrupt comes into the processor.  I will double check this behavior with my colleagues here.

    The other thing I think could be happening is SPI is running out of data.  If there is a single byte in the transmit queue, and SPI finishes transmitting either right before or during the interrupt routine in question, no more bytes would be transferred until after the interrupt because there is nothing waiting to be transferred.  I have a feeling that this is actually what is happening.  Perhaps you could implement a small state machine in the SPI interrupt to always keep the SPI buffers full?

     

    Trey German

  • I confirmed with my colleagues that the SPI will not pause transmission or reception during an ISR. 

    What is happening is that the SPI has finished transmitting/recieving the data that you have requested it to, as I described in the previous post.  Try using the SPI peripheral again with this in mind.


    Trey German

  • Trey or Jim,

    Did you ever get this working. I need to implement an TCP/IP stack for the c28 and was considering using lwIP. If so, I'd be interested in any info or code you could share.

    Regards,

    - Clint

  • Clint,

    No, I haven't heard anything.  You might want to post in the SYS/BIOS forum.  The guys in there maintain a TCP/IP stack called the NDK which I think might run on the C28 (no guarantees).  Its got a lot more functionality than lwIP, but with that comes code size.  I think this will be your best bet.

    Trey

  • Hello Trey,

    > We at TI are fully aware that this is a pain to do, and we are actually looking at a few ways to solve this problem.

    Any progress so far? The issue is still relevant.

    Regards,
    Andrey
  • Andrey,

    Adding byte addressability to the architecture is a huge undertaking.  The soonest this would probably come about is 2-3 years.

    BR,

  • Hello Jim,

    I am trying to implement TCP/IP stack on TMS320F2812 to use with ENC28j60. Have you implemented this project? It would be helpful for me if you could share the code or other details.
  • Hello Krish,
    I couldn't find way to resolve the byte problem between F2812 and LwIP, the work didn't have result. If you noly want run TCP/IP on MCU, it may be a substitute by F28335 + W5300. I think it might work well by F2812 + W5300. Hard TCP/IP stack may simplify the network routine.