I've created a little test program that runs fine in the simulator, but causes the hardware (80C390) to reset when a function with multiple long parameters is called. This is with v6.22. Am I missing something obvious? Running in the large memory model in a new project (I.E. all other settings as defaults.)
#include <REG390.H> #include <absacc.h> #include <stdio.h> void wait (void) { /* wait function */ int i; for (i=0;i<20000;i++) { ; } } void Serial0_Initialize(void) { // init serial interface 38400 baud @ 20MHz PCON |= 0x80; /* Double serial port 0 baud rate */ SCON0 = 0x50; /* Async receiver serial port 0 enable */ TMOD |= 0x21; /* Timer 0 16 bits, Timer 1 8 bits with auto reload mode */ TH1 = 0xF8; /* Load MSB of Timer 1 */ CKCON |= 0x10; /* Timer 1 clock select divide by 4 of xtal freq */ TCON = 0x50; /* Timer 0 run enable, Timer 1 run enable */ TI = 1; /* Serial port 0 transmitter interrupt flag set */ } unsigned char xdata* egdBufferGetInt(int buffer, int width, int height, int depth) { unsigned char xdata *curBuf = 0; return curBuf; } unsigned char xdata* egdBufferGet(long buffer, long width, long height, long depth) { unsigned char xdata *curBuf = 0; return curBuf; } long egdDrawInt(unsigned char xdata *buffer, int width, int height) { ; } long egdDraw(unsigned char xdata *buffer, long width, long height) { ; } void main (void) { unsigned char xdata *b; // Initialize serial port 0 Serial0_Initialize(); printf("Serial initialized!\n"); wait(); printf("Trying BufferGetInt...\n"); b=egdBufferGetInt(0, 320, 240, 1); printf("BufferGetInt success\n"); wait(); printf("Trying BufferGet...\n"); b=egdBufferGet(0, 320, 240, 1); printf("BufferGet success\n"); wait(); printf("Trying Draw Int...\n"); egdDrawInt(b, 320, 240); printf("Draw Int success\n"); wait(); printf("Trying Draw...\n"); egdDraw(b, 320, 240); printf("Draw success\n"); wait(); while (1) { printf("Waiting...\n"); wait(); } }
Serial initialized! Trying BufferGetInt... BufferGetInt success Trying BufferGet... erial initialized! Trying BufferGetInt... BufferGetInt success Trying BufferGet... erial initialized! Trying BufferGetInt... BufferGetInt success Trying BufferGet... erial initialized! Trying BufferGetInt... BufferGetInt success Trying BufferGet... erial initialized!
OK, more information. I noticed that our HEX file loader in EPROM is leaving the processor in 22 bit paged mode before jumping to my program in flash. I.E.
TA = 0xAA; TA = 0x55; ACON = 1;
TA = 0xAA; TA = 0x55; ACON = 0;
By the way, if the user is optimizing for speed, the compiler can get massive speed/size impovements by not calling routines like C?LSTKXDATA.
Here's a simple program with which Kevin can test the "jmp @a+dptr" instruction. File: Sub.c Options: Generate SRC, Assemble SRC
void PseudoRet( void ) { #pragma asm pop dph pop dpl clr a jmp @a+dptr #pragma endasm }
#include <REG390.H> #include <stdio.h> void Serial0_Initialize(void) { // init serial interface 38400 baud @ 20MHz PCON |= 0x80; /* Double serial port 0 baud rate */ SCON0 = 0x50; /* Async receiver serial port 0 enable */ TMOD |= 0x21; /* Timer 0 16 bits, Timer 1 8 bits with auto reload mode */ TH1 = 0xF8; /* Load MSB of Timer 1 */ CKCON |= 0x10; /* Timer 1 clock select divide by 4 of xtal freq */ TCON = 0x50; /* Timer 0 run enable, Timer 1 run enable */ TI = 1; /* Serial port 0 transmitter interrupt flag set */ } void PseudoRet( void ); void main (void) { Serial0_Initialize(); printf("Serial initialized!\n"); PseudoRet(); printf("Nope, this is not the problem.\n"); while (1) ; }
Ths point is that Kevin states that: "The target is a 80C390, but I'm not running in contiguous mode (yet), just in the large memory model." So the C?LSTKXDATA is being emitted. OK, then what's the problem with the JMP @A+DPTR instruction in 16-bit mode? Jon
Ths point is that Kevin states that: "The target is a 80C390, but I'm not running in contiguous mode (yet), just in the large memory model." So the C?LSTKXDATA is being emitted.
... the C390 has an internal 22 bit address bus and ... the long parameter calls "C?LSTKXDATA" ... uses a "JMP @A+DPTR" for a pseudo "RET" instruction. The address bits[21:16] are not set correctly for this pseudo "RET" instruction. So this "return" jumps off the end of the code and eventually the micro executes at 0x00000 and then soft reset. This is NOT technically accurate. 1. If you check your settings, you will find that the JMP @A+DPTR instruction is used in C?LSTKXDATA ONLY in the 16-bit mode. It is NOT used in the contiguous modes. So, the 22-bit address bus is not used. 2. According to the Dallas Data book: Data Pointer Extended Register 0 This register contains the high-order byte of the 22-bit address (or 23-bit address when CMA=1) when performing operations with Data Pointer 0. This register is ignored when addressing data memory in the 16-bit addressing mode. 3. In CONTIGUOUS mode, the C?LSTKXDATA function uses a RET statement to return from the function call. So, the 22-bus issue is solved there. 4. In the original posting, it is mentioned that the program works OK in the simulator. Since the simulator exactly simulates the instruction set of the 390 in every way, I think that there is some other issue at work here. 5. I have tested this program in ROM(LARGE), CONTIGUOUS: 512K, and CONTIGUOUS: 16M ROM models and it works AOK in the simulator. However, I do not have access to 390 hardware at this time. If some else does, let me know and I can send you my project to test. The last time we had a problem like this involved a government contractor who eventually sent a group of engineers to demonstrate the problem. After we cleaned up the 2.5 volt spikes on VCC, we requested that they buy the marguiritas. They did. :-) Jon
The reset problem is that the C390 has an internal 22 bit address bus and that the long parameter calls "C?LSTKXDATA" which uses a "JMP @A+DPTR" for a pseudo "RET" instruction. The address bits[21:16] are not set correctly for this pseudo "RET" instruction. So this "return" jumps off the end of the code and eventually the micro executes at 0x00000 and then soft reset. This problem could extend to "switch" statements and other C constructs. The bottom line is that the Keil compiler, as far as I know, does not support this chip. If all your addressing needs is less than 16k, you probably need just to set the PDX & AP registers to get things working.
There are a number of compiler warnings that are generated for your code. Make the following changes and let us know if it works on your hardware after that:
unsigned char xdata* egdBufferGetInt(int buffer, int width, int height, int depth) { unsigned char xdata *curBuf = 0; buffer = buffer; width = width; height = height; depth = depth; return curBuf; } unsigned char xdata* egdBufferGet(long buffer, long width, long height, long depth) { unsigned char xdata *curBuf = 0; buffer = buffer; width = width; height = height; depth = depth; return curBuf; } long egdDrawInt(unsigned char xdata *buffer, int width, int height) { buffer = buffer; width = width; height = height; ; return (0); } long egdDraw(unsigned char xdata *buffer, long width, long height) { buffer = buffer; width = width; height = height; ; return (0); }
OK, interesting. The difference now is that the int version of the call uses MOVX @DPTR to directly store the parms into XRAM, whereas the the long version uses the library function ?C?LSTKXDATA, which pops the return address into DPTR, copies the constant parms out of the code memory locations that immediately follow the call, advancing DPTR along the way, then returns "through" the DPTR (i.e., JMP @A+DPTR) to the instruction immediately following the constant parms. I'm familiar with some Dallas MCU's, but not the '390. Aren't there some considerations for DPTR-related instructions when dealing with code memory? I wonder if something is slightly askew in this area.
"it runs in the simulator fine..." Probably not the same thing then; in my case, the compiler was definitely generating bad code - the fault could be seen on both target and simulator.
Possibly. (I'm new to the hardware.) However, I'm able to successfully call a function with 8 integer params, which should use the same amout of param space as 4 longs...
One of the differences for the caller/callee of your functions with the long parameters as opposed to those with int parameters, is that the long versions pass the parms on the external memory "stack", whereas the int versions pass the parms in R0-R7. Are there any START390.A51 aspects that need to be configured for running on your target hardware that you are getting away with not configuring when simulating?
> What happens when you call egdBufferGet first? It always dies on that call, no matter when it is called. > What happens if you also put a printf statement in egdBufferGet It never prints out anything in the egdBufferGet() function.
What happens when you call egdBufferGet first? What happens if you also put a printf statement in egdBufferGet?
Hmm... two long args (8 bytes) causes problems. Interesting that passing two longs in registers would require the entire register bank. I wonder if there is a "spill over" bug. When a stack frame cannot fit into the arg. regs. then they must spill over into a stack frame memory area. In the short term, you might create a suitcase union of structs such that you have in it structs for both long an int vars and a type field. Then have one function that looks at the type field and does the correct work. Or you could write a var_args function and make your function work like printf which does work. - Mark
View all questions in Keil forum