cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
stoddarg
Visitor
Visitor
6,046 Views
Registered: ‎01-06-2017

Break symbol changing values in case statement

Hi all,

 

I'm writing some code using SDK and I've run into a problem with a switch statement. I have an array, "data_array_holder" which holds int data that I want to sort. The data is a repeating sequence of 111111 and seven values. To sort this data, I have a switch statement which, once a the identifier within my data is found, it sorts the data into an array of structs, "data_array".

 

The problem I am having is that each time a 'break' symbol is reached in each case, it changes the value of a specific value within my array of structs. I figured this out by stepping through the loop using the SDK debugger and realized that this is likely the problem. When this value in the array of structs is reached, it then sets "array_index" back to 0 and it starts filling the array of structs again from the beginning. 

 

Here is the code:

#include "read_data_in.h"

/***** Function Definition *****/
int ReadDataIn( ){
	struct event_raw data_array[512];

	ii = 0;
	jj = 0;
	val = 0;
	nevents = 0;
	bl_sum = 0;
	bl_avg = 0;
	dram_addr = 0;
	int data_array_holder[4096];


	// Read only Adj Average data from DRAM
	int dram_base = 0xa000000;
	int dram_ceiling = 0xA004000; //read out just adjacent average (0xA004000 - 0xa000000 = 16384)

	Xil_DCacheInvalidateRange(0x00000000, 65536);

	XTime_GetTime(&tLap);
	dTime = tLap / (COUNTS_PER_SECOND / 1000000); 	// /Counts_per_second // this may not be correct
	xil_printf("000111000111\r\n");
	xil_printf("%d\r\n",dTime);

	for (dram_addr = dram_base; dram_addr <= dram_ceiling; dram_addr+=4){	//Read in data from the DRAM as we step through 32 bit addresses
		//if (!sw) { sw = XGpioPs_ReadPin(&Gpio, SW_BREAK_GPIO); } //read pin
		data = Xil_In32(dram_addr);
		data_array_holder[ii] = data;
		++ii;

		XUartPs_Recv(&Uart_PS, RecvBuffer, 32);		// Check a buffer to see if the user wants to quit, 'q'
		if ( RecvBuffer[0] == 'q' ) { sw = 1;  }
		if(sw) { return sw; }

	}

	for (jj = 0; jj <= 4095; ++jj) {	// sort the data which is now saved in the data_array

		switch( val ) {
		case 0:
			if ( data_array_holder[jj] == 111111) {
				val = 1;
			}
			break;
		case 1:
			data_array[array_index].time = data_array_holder[jj] * 262.144e-6;
			val = 2;
			break;
		case 2:
			data_array[array_index].total_events = data_array_holder[jj];
			val = 3;
			break;
		case 3:
			data_array[array_index].event_num = data_array_holder[jj];
			val = 4;
			break;
		case 4:
			data_array[array_index].bl = data_array_holder[jj] / ( 16.0 * 38.0 );
			val = 5;
			break;
		case 5:
			data_array[array_index].si = data_array_holder[jj] / 16.0;
			val = 6;
			break;
		case 6:
			data_array[array_index].li = data_array_holder[jj] / 16.0;
			val = 7;
			break;
		case 7:
			data_array[array_index].fi = data_array_holder[jj] / 16.0;
			data_array[array_index].psd = 0.0;
			data_array[array_index].energy = 0.0;
			val = 0;
			++array_index;
			break;
		default:
			xil_printf("There was a problem saving the array.");
			sw = 1;
			break;
		}
	}
return sw;
}

Why/how is my code changing the value each time it hits a 'break' ? Is this a bug within SDK? I'm currently using version 2015.3, if that is relevant. Let me know if I can provide any further info. Thanks for your help!

 

Regards,

Graham

 

0 Kudos
Reply
9 Replies
magnanisj
Visitor
Visitor
6,030 Views
Registered: ‎01-06-2017

Hi,

 

I'll ask the silly question...are you sure you're not blowing out the stack? The function you posted looks like it uses a huge amount of stack space for automatic variables.

 

Steve

0 Kudos
Reply
stoddarg
Visitor
Visitor
6,020 Views
Registered: ‎01-06-2017

Hi Steve,

 

How would I go about checking to see if I did that? I'm not familiar with how much stack space functions use.

 

Graham

0 Kudos
Reply
magnanisj
Visitor
Visitor
6,012 Views
Registered: ‎01-06-2017

I usually run 'mb-objdump --disassemble TheProgram.elf' to look at the assembly code. Functions with automatic variables usually have an "addik   r1, r1, -xxx" instruction near the beginning where they allocate xxx bytes of stack space. You can cross check that against the size of the .stack segment reported by "mb-objdump -x TheProgram.elf". If you're close to the limit on stack usage this simple cross-check won't catch it (it's not accounting for stack space used by functions above you in the call stack), but if something's grossly out of whack you'll see it.

 

Steve

0 Kudos
Reply
magnanisj
Visitor
Visitor
6,011 Views
Registered: ‎01-06-2017

You can also set a breakpoint at the beginning of the function and use XMD's "dis" command to disassemble the first few instructions.

Steve
0 Kudos
Reply
stoddarg
Visitor
Visitor
5,993 Views
Registered: ‎01-06-2017

Hi Steve,

 

I have realized that the xmd command you said was for a microblaze, but I'm using a zynq. It turns out that there is an equivalent command "arm-xilinx-eabi-objdump -D" which does the same thing. That being said, I tried taking a look at the file, but I don't find any "addik" instructions. Would this be due to differences in the two systems? If so, do you happen to know what I should be looking for instead? I attached the file that was dumped, in case you would like to take a look. 

 

I also ran my code in debug mode with the disassembly view open and tried to pick out what instructions were sent when variables were declared, defined, etc, but I didn't get much from that either. 

 

Would it perhaps be worth it to just increase the stack size?

 

Graham

0 Kudos
Reply
magnanisj
Visitor
Visitor
5,986 Views
Registered: ‎01-06-2017

Graham -

 

ARM has a dedicated Stack Pointer (SP) register. In ReadDataIn() there is this instruction:

sub	sp, sp, #53248	; 0xd000

 

...which I think is reserving 0xD000 bytes of stack. Next question is how much stack you have;  based on this I don't think it's enough to cover what the function wants (never mind how much its callers/callees need), but objdump -x would help clear that up:

 

00126bf0 <_stack_end>:
	...

00128bf0 <__stack>:

 

Steve

0 Kudos
Reply
stoddarg
Visitor
Visitor
5,982 Views
Registered: ‎01-06-2017

Hi Steve,

 

I found that instruction and I see what you are saying. In my linker script, lscript.ld, I have a stack size of 1kB, so that is definitely not enough to cover what the function wants. I have attached the objdump -x. 

 

So, if the problem is with the ReadDataIn() function reserving way more stack space than I could give it, what can I do to identify what part of the function is being so demanding? 

 

Graham

0 Kudos
Reply
whelm
Explorer
Explorer
5,879 Views
Registered: ‎05-15-2014

Another trick that I've found useful is to fill the stack with some marker value (as far as I'm concerned the startup code for every C compiler should do this intrinsically when the space is allocated.  But since they generally don't, the following will work, at the beginning of main()

 

        {
                int i;
                for (i = &_stack_end; i < &_stack - 32; i+=4) {
                        *(unsigned long*) i = 0x5A5A5A5A;
                }
        }

Note that it stops 32 bytes (8 words) shy of the top of the stack, because getting into main is already going to have allocated some space and I don't want to trash anything already initialized (which is why this really belongs in the startup code).

 

So as the stack grows downward more and more of the 0x5A bytes get overwritten with other things.  Now from the debugger, you can spot check from the bottom up to find where the 0x5As stop.  Sometimes they may re-appear briefly at higher addresses, because sometimes stack space is allocated without being used, but you want to look for the first address above the bottom of the stack that does NOT contain 0x5A.  Knowing where the stack top and bottom are is easily determined from the elf file, but for the lazy (like me)

        printf("%0X %0X\n\r", &_stack_end, &_stack);

saves you the bother.

 

Finally, my programs generally have some textual UI, often RS-232 or USB VCom, so I include a couple commands such as VB and VL shown here

                    <VB>:      // Set base address
                        BaseAddr = ah2U32(&inptr) & 0xFFFFFFFC;
                        break;
                    <VL>      // Read memory
                        ULparam1 = (ah2U32(&inptr) + BaseAddr) & 0xFFFFFFFC;
                        xil_printf(" %08X", *(U32*)ULparam1);
                        break;

The first sets a base address to simplify accessing addresses at offset from that (default is 0)

The second accesses a word of memory, rendering it as 8 hex digits.  Now the real live running application can be used to see how much stack space is being consumed.  One word of warning:  On a processor with MMU such as this, reading from an inappropriate or nonexistent address triggers an MMU Data fault.  If not handled, it can lock up the program.  If my program ships with this command in it, it will be password protected so the customer doesn't accidentally shoot themselves in the foot.

I also have a password protected <UL> command as well, that writes to a word:

                        ULparam1 = (ah2U32(&inptr) + BaseAddr) & 0xFFFFFFFC;
                        ULparam2 = ah2U32(&inptr);
                        *(U32*)ULparam1 = ULparam2;

Even more dangerous, but extremely useful for debugging, particularly on real-time systems where setting breakpoints, etc. would destroy what you are trying to capture.  But don't allow it on something like an industrial machine that could kill someone if a mistake was made!

 

These commands can also look at (and alter) CPU registers, IP registers, etc.  In real time, on a running system.

magnanisj
Visitor
Visitor
5,863 Views
Registered: ‎01-06-2017

Well, my reply last week seems to have gone into the bit bucket, so here it is again:

 

The heavy hitters are the arrays: data_array is 512 * sizeof(struct event_raw) and data_array_holder is 4096 * sizeof(int). Together the very minimum these two could be is 16.5 KiB.

 

If ReadDataIn() is not called by more than one thread at a time, prefacing the declaration of these two arrays with 'static' will move them off the stack and into .bss. Otherwise I think you will either need to increase the stack depth or redesign the function.

 

whelm's suggestion of seeding the stack is an excellent one - it can be tricky to figure out how deep the stack needs to be, so empirical measurement following exercise of the system can give you some idea.

 

gcc (now) has several -f options related to stack protection and usage - I haven't tried using them in an embedded environment so I don't know whether they are practical.

 

Steve

0 Kudos
Reply