This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Reinhard, Jon can you answer: Struct across 64 boundary

At this time when the two of you are active here, can I get an answer to the following which I have sougtht an answer to for some time both through support and this forum.

Is there - or will there be - a means of allowing a struct to cross a 64k boundary. The code generated by the compiler does 16 bit math so members in the second page are erroneously accessed. This is for address calculation, not actual access since my system has a "flash page" port and a "read flash" function that set that port.

Note" I am NOT talking of "placing in any 64k page", I AM talking about accessing a member of a struct that happen to be in the following 64k page.

PLEASE no words about banks, I am not using it since 99.99% of the run time my program is happy in 64k code and data.

Erik

  • I think we have this answer already on:

    http://www.keil.com/support/docs/2800.htm

    It should work this way also on the classic 8051.

    In case that I am wrong, just provide your programming example.


    Reinhard

  • In case that I am wrong, just provide your programming example.
    I will try it when time permits (other thigs are sizzling right now) and come back with a programming example if it does not work.

    Thanx,

    Erik

  • You may define objects in assembly language (using HDATA or HCONST segments).
    I want to stick to the small model because the project is barely fast enough with a Silabs f122 at 100MHz when processing the 99.99% of the time stuff. Thus I can not handle any added overhead for this part of the code.

    How do I combine processing SRAM and code at full blast and then accessing HDATA once in a while.

    Erik

  • Banking has zero overhead when you don't explicitly invoke it. Only the pointers declared "far" (or generic that happen to be far) are going to cause the libraries to poke the bank registers. Memory space-specific pointers will continue to work as they do when the banking checkbox in uVision is off. Far access to your mass storage won't significantly impact the size or speed of the rest of the code if coded properly.

    Take a look in XBANKING.A51. The code is pretty straightforward. There are some special-case tweaks you could make to run faster -- setting the bank register once for a block of accesses, for example -- but it's reasonably efficient.

    Or perhaps you might consider something with a little more horsepower. 8051s aren't the best solution for all problems, particularly when your problems involve processing speed and shuffling large amounts of data.

  • Drew,

    I DO NOT WANT BANKING,
    do I need to make it clearer!!!! OK,
    I DO NOT WANT BANKING

    I DO NOT NEED BANKING,
    do I need to make it clearer!!!! OK,
    I DO NOT NEED BANKING

    THIS IS NOT ABOUT BANKING,
    do I need to make it clearer!!!! OK,
    THIS IS NOT ABOUT BANKING

    How do you, with banking, have a struct that start in one bank and end in another, That is PAGING, NOT BANKING.

    The overhead I refer to is the (possible) overhead if I need to use the HUGE model or any such in order to access HDATA or will the calculations of offsets then, in all cases, be more than 16 bit.

    Or perhaps you might consider something with a little more horsepower. 8051s aren't the best solution for all problems, particularly when your problems involve processing speed and shuffling large amounts of data.
    I agree totally; however, in this unique case a lot of legacy code must be used for TTM reasons. I am, however, a little bit considering to make the next with a LPC2xxx when this generation is out the door and the time to market pressure is gone.

    The reason for this thread is that I will need to make the PC people that make the input data file (which is a memory image of a bunch of structs) jump through hoops (they claim) if no struct can cross a 64k boundary. Struct size is not a problem, none will be even close to 64k in size.

    Erik

  • ///////////////////////////////////////////////////////////////////////////
    //
    // EXAMPLE.C
    //
    // try HDATA
    //
    
    typedef struct
    {
      unsigned char        SGaddr1   ; // 485 address of first sign this type
      unsigned char        SGaddr2   ; // 485 address of 2nd sign this type
      unsigned char        SGaddr3   ; // 485 address of 3rd sign this type
      unsigned char        SGaddr4   ; // 485 address of 4th sign this type
      unsigned char        SGfPage   ; // flash page for data
      unsigned short       SGfAddr   ; // flash addr for data
    } ACTIVE_SIGNS;
    
    extern ACTIVE_SIGNS hdata  from_here;
    extern ACTIVE_SIGNS xdata  to_here;
    
    void TryIt(void)
    {
       to_here.SGaddr4  = from_here.SGaddr4;
    }
    if using xdata in line 10 all is ok (test syntax) using hdata in line 10 gives error (; before from_here required).

    Am using CX51

    what's wrong

    Erik

  • "what's wrong"

    If you think that 'hdata' is a keyword then that would be what is wrong.

    I can't tell you what would be right, unfortunately, but I suspect it may have something to do with 'far' and 'HDATA segments'.

  • I got hdata from the appnote Reinhard referred to

    Erik

  • There is indeed no "hdata" keyword in C51, even though that's a linker class. Try "far". You might also have a use for the FVAR and FARRAY macros in absacc.h.

    And though I hate to possibly set off another rage, I feel obliged to mention that XBANKING.A51 holds the code that allows you to customize C51 to your particular hardware that controls the address lines for a data space larger than 64K. I don't care whether you call it (data) banking or paging; it's the same mechanism, and the same code. The 8051 has many memory address spaces, and more than one of them can be banked, or paged, or segmented; whatever term you prefer.

    L51_BANK.A51 has the code banking routines, along with data banking routines. XBANKING is just the xdata address extension library. Even if your code is not banked, you still need to modify one or the other of these files to get the data bank routines. Otherwise, the ability to calculate large addresses becomes meaningless.

    It's also useful to peek at XBANKING.A51 because this file has comments which detail the 3-byte pointer format, which includes generic and far pointers. It's in the manual, too, but for some reason I always found the description in the code a bit more informative. If you declare an item far and take its address, C51 is going to generate a pointer in this format.

    In particular, note that the tag byte starts at 1 for xdata. Address 010000H is the first byte of xdata with a generic/far pointer. (Tag byte 0 denotes a data pointer. Unfortunate bit of history there.)

    You can, of course, roll your own code to do the data banking and insert the proper calls by hand, leaving the compiler completely ignorant of your hardware scheme. In that case, you might just as easily declare all your hand-rolled "far pointers" as U32s, since you'll be doing all the arithmetic yourself. You won't get much help from the compiler for doing structure and field assignments, though, since it's been denied any knowledge about the extended addressing.

  • which includes generic and far pointers

    Drew,
    I appreciate you attempts to help, but please understand I have ZERO, NONE, NADA problems with long pointers
    The problems are with structs that cross a 64k boundary. The value is added to the pointer using 16 bit arithmetic which, of course mislocates the higher entries in the struct when the struct cross a 64k (OK, if you insist, you may say "bank") boundary.

    e.g. locate a struct at, say, 0x1fffc, the pointer is fine, but members beyond offset 3 are not accessaible.

    obliged to mention that XBANKING.A51 holds the code that allows you to customize C51 to your particular hardware that controls the address lines for a data space larger than 64K. I don't care whether you call it (data) banking or paging; it's the same mechanism, and the same code. The 8051 has many memory address spaces, and more than one of them can be banked, or paged, or segmented; whatever term you prefer.
    I can NOT use banking because I switch memory operation between 16 bit (SRAM) 22 bit (flash). If I were to use banking I would need to control the bits beyond 16 in "fast SRAM mode".


    I am working with a data set that is set up by the customer using a PC program that store the processed result of the customers input as structs. Then all the data is transferred to the '51 to be processed there.

    I acces this data for a second or two an hour, so I have no particular interest in the efficiency of that process. This can not be done 'streamlined since the endianness is "wrong". However when the interesting (selected) part is transferred to a SRAM (and the endianness corrected), I need to run absolute full speed (some assembler to achieve this).

    The PC people balk at having to make corrections to cut where 64k is crossed.

    the whole shebang does not have structs that are particularily big; but the build is

    struct a
    * struct b
    * struct b
    % struct b

    struct b
    * struct c
    * struct c
    * struct c

    etc for about 12 levels

    As you see, the recalculation, in order to push the crossing struct up n bytes, would be a total nightmare.

    Erik

  • I used the following example:

    #include <REG51M.H>
    #include <stdio.h>
    
    
    typedef struct active_signs_st
    {
      unsigned char        SGaddr1   ; // 485 address of first sign this type
      unsigned char        SGaddr2   ; // 485 address of 2nd sign this type
      unsigned char        SGaddr3   ; // 485 address of 3rd sign this type
      unsigned char        SGaddr4   ; // 485 address of 4th sign this type
      unsigned char        SGfPage   ; // flash page for data
      unsigned short       SGfAddr   ; // flash addr for data
    } ACTIVE_SIGNS_T;
    
    ACTIVE_SIGNS_T far *from_here = 0;  /* Treat like an array */
    ACTIVE_SIGNS_T xdata to_here;
    
    void main (void)
    {
    unsigned long i;
    
    SCON  = 0x50;
    TMOD |= 0x20;
    TH1   = 221;
    TR1   = 1;
    TI    = 1;
    
    for (i = 9360; i < 10000; i++)
      {
      void far *p;
    
      p = &from_here[i].SGaddr4;
      printf ("Address = 0x%8.8lx\n", (unsigned long) p);
      to_here.SGaddr4 = from_here[i].SGaddr4;
      }
    
    while(1);
    }
    

    To get the following output:

    Address = 0x0000fff3
    Address = 0x0000fffa
    Address = 0x00010001
    Address = 0x00010008
    Address = 0x0001000f
    Address = 0x00010016
    .
    .
    .
    

    I checked the code and it appears to do what you want.

    Jon

  • unsigned char far *from_here;
    unsigned char      char_here;
    
       char_here = *(from_here + 7);
    
    0000 AB00        R     MOV     R3,from_here
    0002 AA00        R     MOV     R2,from_here+01H
    0004 A900        R     MOV     R1,from_here+02H
    0006 E9                MOV     A,R1
    0007 2407              ADD     A,#07H
    0009 F9                MOV     R1,A
    000A EA                MOV     A,R2
    000B 3400              ADDC    A,#00H
    000D FA                MOV     R2,A
    000E A548              EMOV    A,@PR0
    0010 FF                MOV     R7,A
    0011 8F00        R     MOV     char_here,R7

    if the far pointer (from_here) is less than 7 from a page boundary, this will fail

    Erik

  • void main (void)
    {
    unsigned char far *from_here;
    unsigned char      char_here;
    
       char_here = *(from_here + 7L);
    }
    

    Generates the following:

                 ; FUNCTION main (BEGIN)
                                               ; SOURCE LINE # 42
                                               ; SOURCE LINE # 43
                                               ; SOURCE LINE # 47
    0000 AB00        R     MOV     R3,from_here
    0002 E500        R     MOV     A,from_here+02H
    0004 2407              ADD     A,#07H
    0006 F9                MOV     R1,A
    0007 E4                CLR     A
    0008 3500        R     ADDC    A,from_here+01H
    000A FA                MOV     R2,A
    000B E4                CLR     A
    000C 3B                ADDC    A,R3
    000D FB                MOV     R3,A
    000E A548              EMOV    A,@PR0
    0010 F500        R     MOV     char_here,A
                                               ; SOURCE LINE # 48
    0012 22                RET
                 ; FUNCTION main (END)
    

    Jon

  • The above because I must define one struct as offsets in order to use two different layouts from different versions of the PC software.

    Anyhow I can not see me being the only one with a problem with this

    Erik

  • I wasn't really clear, but my implementation of your example does work across 64K boundaries.

    Jon