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; }
"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); }
Address = 0x0000fff3 Address = 0x0000fffa Address = 0x00010001 Address = 0x00010008 Address = 0x0001000f Address = 0x00010016 . . .
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
void main (void) { unsigned char far *from_here; unsigned char char_here; char_here = *(from_here + 7L); }
; 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)
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