Memory test problem. Don't know what to do...

Go To Last Post
29 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi folks,

it's me again.
Now I have my RAM test up and running in my application. And now there is a big problem which I don't know how to solve.
First thing I learned was, that the memory test destroys all global variable initialization, so I had to initialize them after the RAM test again.

Second thing I found ist a bigger problem and I kindly ask you to throw in your ideas about that problem.

Here we go:
I have a function which looks like this:

void write_val2buffer(uint8_t row, uint8_t col, uint8_t val)
{
	char str[4];	
	sprintf(str, "%3d", val);
	/* write string to display buffer */
	strncpy(display[row] + col, str, 3);

	return;
}

Now I send in a val =0;
Without the RAM Test, the str is filled correctly and the display buffer also. I can see in the memory window of AVR Studio that the display buffer is filled with 0x20 0x20 0x30 which results in " 0" So far so good.

But when using my RAM Test during startup, I see the following when this function is called:
The display buffer is filled with 0x00 0x00 0x00 which is not correct and leads to not displaying correctly the value.

This is only one example of the problem. It appears everywhere where I fill a string with some date / signs / etc. and write it to the display buffer.

It seems that something for all the sprintf and strncpy is placed in RAM before executing and with the RAM test it is destroyed. :shock:

I'm totally lost on this.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You need to do the same as the .data initialisation loop to copy the ENTIRE .data initialisers out of flash and into SRAM. Probably easiest to just steal the code from the pre-amble.

But why not either:

a) put the RAM test in .init3 so it is run BEFORE the .data and .bss setup or

b) make the RAM test non-destructive. So instead of:

*ptr = 0xAA;
if (*ptr!=0xAA) scream();

it does something like

uint8_t old_val = *ptr;
*ptr = 0xAA;
if (*ptr!=0xAA) scream();
*ptr = old_val

which will maintain the existing RAM contents as it works through memory.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And to add some explanation to Cliffs advice:

Every literal string that you have in your code will of-course be part of the FLASH contents. A piece of knowlege you are probably missing is that on startup all those strings are copied to RAM so that they can be handled just like ordinary strings (eg. we need no special FLASH-enabled version of sprintf() to deal with a format string in FLASH as the format string will have been copied to RAM). This copying is done in .init4 so that is why Cliff advices to do the memory test before that, in .init3. More on memory sections eg here: http://www.nongnu.org/avr-libc/u...

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh and another possible solution is sprintf_P() and PSTR() to keep all the strings in PROGMEM in the first place (which makes sense anyway)

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

THanks for the answers.

Just a question.

Quote:
Probably easiest to just steal the code from the pre-amble.

How to steal it, or better, where can I find it.

Or other way around:
Is is possible to call the .init4 again after the SRAM test?

Or I have to rewrite the test, so that it works in .init3. Because as it is, it does not work in .init3. I'm investigating that.

I cannot make the RAM test non desructive, because I use the March-B test which starts with setting all RAM to 0 :(

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The stuff in init4 is 'naked' so is not a callable function. Basically the reset vector just jumps into it and when it completes it jumps into 'main'. So no chance of calling it a second time. From the .map file of a build you can see where it's coming from:

 *(.init0)
 .init0         0x00000054        0x0 c:/winavr-20081118rc2/bin/../lib/gcc/avr/4.3.2/../../../../avr/lib/avr5/crtm16.o
                0x00000054                __init
 *(.init0)
 *(.init1)
 *(.init1)
 *(.init2)
 .init2         0x00000054        0xc c:/winavr-20081118rc2/bin/../lib/gcc/avr/4.3.2/../../../../avr/lib/avr5/crtm16.o
 *(.init2)
 *(.init3)
 *(.init3)
 *(.init4)
 .init4         0x00000060       0x16 c:/winavr-20081118rc2/bin/../lib/gcc/avr/4.3.2/avr5\libgcc.a(_copy_data.o)
                0x00000060                __do_copy_data
 .init4         0x00000076       0x10 c:/winavr-20081118rc2/bin/../lib/gcc/avr/4.3.2/avr5\libgcc.a(_clear_bss.o)
                0x00000076                __do_clear_bss

and from the .lss you can see what it does:

00000054 <__ctors_end>:
  54:	11 24       	eor	r1, r1
  56:	1f be       	out	0x3f, r1	; 63
  58:	cf e5       	ldi	r28, 0x5F	; 95
  5a:	d4 e0       	ldi	r29, 0x04	; 4
  5c:	de bf       	out	0x3e, r29	; 62
  5e:	cd bf       	out	0x3d, r28	; 61

00000060 <__do_copy_data>:
  60:	10 e0       	ldi	r17, 0x00	; 0
  62:	a0 e6       	ldi	r26, 0x60	; 96
  64:	b0 e0       	ldi	r27, 0x00	; 0
  66:	e2 ea       	ldi	r30, 0xA2	; 162
  68:	f0 e0       	ldi	r31, 0x00	; 0
  6a:	02 c0       	rjmp	.+4      	; 0x70 <.do_copy_data_start>

0000006c <.do_copy_data_loop>:
  6c:	05 90       	lpm	r0, Z+
  6e:	0d 92       	st	X+, r0

00000070 <.do_copy_data_start>:
  70:	ac 36       	cpi	r26, 0x6C	; 108
  72:	b1 07       	cpc	r27, r17
  74:	d9 f7       	brne	.-10     	; 0x6c <.do_copy_data_loop>

00000076 <__do_clear_bss>:
  76:	10 e0       	ldi	r17, 0x00	; 0
  78:	ac e6       	ldi	r26, 0x6C	; 108
  7a:	b0 e0       	ldi	r27, 0x00	; 0
  7c:	01 c0       	rjmp	.+2      	; 0x80 <.do_clear_bss_start>

0000007e <.do_clear_bss_loop>:
  7e:	1d 92       	st	X+, r1

00000080 <.do_clear_bss_start>:
  80:	ab 38       	cpi	r26, 0x8B	; 139
  82:	b1 07       	cpc	r27, r17
  84:	e1 f7       	brne	.-8      	; 0x7e <.do_clear_bss_loop>
  86:	0e 94 49 00 	call	0x92	; 0x92 
8a: 0c 94 4f 00 jmp 0x9e ; 0x9e <_exit>

The problem with trying to duplicate this in C code are those hard coded 0x0060, 0x00A2 and 0x006C in there. You need to be able to access those hardcoded values symbolically - you can see how this is done in avr-libc by looking at the source of gcrt1.S:

http://cvs.savannah.gnu.org/view...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1. I cannot really see the point of a SRAM test. Other components are far more likely to fail.

2. It seems to be a candidate for an ASM routine called before the C startup code as suggested by Cliff and others.

3. You can of course do a complete C startup, run a C RAM test and then repeat the C startup.

Bald David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1) I think OP said previously that it's simply a requirement of his customer that it pass certain safety standards however pointless it may actually be

I really think that the "simple" answer here is positioning the routine into one of the .initN sections this will probably also involve making it "naked" and clearly the routine itself will not be able to rely on any pre-initialised global value or that any unitialised global contains 0x00. If it's placed before .init2 it couldn't even rely on SP being set or that R1 contains 0.

There's also a potential for "rug from under feet" problems if the compiler chooses to place any automatic locals on the stack rather than optimising them into register only usage!IN fact, for the same reason nothing in .data or .bss could be used anyway and I guess a lot of "register var asm("Rn")" is going to be needed to ensure the code really does just use machine registers.

Cliff

PS of course R00..R31 are really just RAM locations 0x0000..0x001F anyway! Are those part of the test too? If not, shouldn't they be, if they are then how on earth can they be tested?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@clawson

OK, now I have something like this behind my SRAM test:

__asm("copy_data_again:		\n"
	 "	ldi	r17, hi8(__data_end)	\n"
	 "	ldi	r26,  lo8(__data_start)	\n"
	 "	ldi	r27, hi8(__data_start)	\n"
	 "	ldi	r30, lo8(__data_load_start)	\n"
	 "	ldi	r31, hi8(__data_load_start)	\n"
	 "	ldi	r16, hh8(__data_load_start)	\n"
	 "	out	AVR_RAMPZ_ADDR, r16	\n"
	 "	rjmp	.+4     \n" //<__do_copy_data+0x14>
	 "	elpm	r0, Z+	\n"
	 "	st	X+, r0		\n"
	 "	cpi	r26, lo8(__data_end)	\n"
	 "	cpc	r27, r17	\n"
	 "	brne	.-10	\n"
);

Which results in
C:\TEMP/cciW9Ygk.s:1515: Error: constant value required

Which seems to come from:

"	out	AVR_RAMPZ_ADDR, r16	\n"

If I put a hex value for AVR_RAMPZ_ADDR there is no error.
Why the compiler thinks AVR_RAMPZ_ADDR is not constant?

@David

Quote:
1. I cannot really see the point of a SRAM test. Other components are far more likely to fail.

I don't want to debate this again. So please tell it the german safety inspector. :wink:

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hint:

bool RAM_test(void)
{
    return true;
}

I still reckon that you will be better off with a straight ASM function in a straight .s file.

Anything is preferable to the appalling inline syntax. If you write it into crts.s then all your programs can use it forever. Or you ask Make to use a crts_special.o

Bald David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Regarding the RAMPZ thing - silly question but is this for an AVR that actually has RAMPZ then? Otherwise ignore that bit of the code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's an Atmega1280. And the init4 in my .lss file has the "out" line inside.

     fe6:	13 e0       	ldi	r17, 0x03	; 3
     fe8:	a0 e0       	ldi	r26, 0x00	; 0
     fea:	b2 e0       	ldi	r27, 0x02	; 2
     fec:	ec e4       	ldi	r30, 0x4C	; 76
     fee:	f8 e9       	ldi	r31, 0x98	; 152
     ff0:	00 e0       	ldi	r16, 0x00	; 0
     ff2:	0b bf       	out	0x3b, r16	; 59
     ff4:	02 c0       	rjmp	.+4      	; 0xffa <__do_copy_data+0x14>
     ff6:	07 90       	elpm	r0, Z+
     ff8:	0d 92       	st	X+, r0
     ffa:	a4 3f       	cpi	r26, 0xF4	; 244
     ffc:	b1 07       	cpc	r27, r17
     ffe:	d9 f7       	brne	.-10   

I just checked the Atmega1280 specification and is has the RAMPZ thing.

Quote:
7.6.1 RAMPZ – Extended Z-pointer Register for ELPM/SPM

But I also can see that there is written that RAMPZ is at 0x3b(0x5b). So maybe I can just put inside 0x3b instead of AVR_RAMPZ_ADDR

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would forget about doing the data initialization twice, as there are better options available as has been suggested.

Maybe give us an idea how 'complicated' the sram test is, if its written in C or asm, when you want it to run, how often, etc.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
So maybe I can just put inside 0x3b instead of AVR_RAMPZ_ADDR

Indeed I imagine you can!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Indeed I imagine you can!

In the mean time I tried it and it works :D

Quote:
Maybe give us an idea how 'complicated' the sram test is, if its written in C or asm, when you want it to run, how often, etc.

Int the moment it is the RAM test from Atmel Application Note 998. It is a first test writing reading and checking 0x55 and 0xAA to all RAM locations. This test is non destructive beacause the ram value can be saved before writing 0x55 or 0xaa to the ram location.
After that there is a "March B" test running through the RAM. This test is destructive to the RAM.
The test is written in C (I ported it from the application note example source which was for IAR)

I will extend the test to "March C-" later because it also checks for AF faults.

This test will be only used during startup. A periodical test will be a segmented RAM test or maybe I can avoid a periodic ram test by using redundant self-monitored variables.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
This test will be only used during startup
then why not just put your ram test code in .init3-

void my_ram_test(void) __attribute__(naked,section((".init3")));
void my_ram_test(void){
//test here
}

since its naked, it will just fall into .init4, where the normal startup code will continue.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
since its naked, it will just fall into .init4, where the normal startup code will continue.

What does that mean? What happens without naked?

Somethign funny: If I search with google for "avr-gcc" and "naked" our company proxy is blocking my search :lol: :roll:

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
our company proxy is blocking my search
but letting naked talk from avrfreaks go through. Although not quite as interesting.

The naked attribute will eliminate the prologue/epilogue from the function, including the return. Since no return, the function will simply 'continue' into the next instruction after the function (.init4 code in this case).

Since .init2 code clears r1, you can use C code in .init3 and not have to worry about r1 (which the compiler expects to be 0). Which is why .init3 is the right place for your C code in this case.

.init2- clear r1 and setup stack pointer
.init3- your sram test
.init4- initialize data

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
The naked attribute will eliminate the prologue/epilogue from the function, including the return. Since no return, the function will simply 'continue' into the next instruction after the function (.init4 code in this case).

Ok, understand. Thanks.

Quote:
but letting naked talk from avrfreaks go through. Although not quite as interesting.

Yes. But when I do a search on this forum and than clicking on one of the results, will be blocked, because the link to that page has a "&highlight=naked" in the address. Deleting that, the link is not blocked.
I love this security system. It brings me often some laughes. :D

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK. Copying the .data is working correctly now. I'm thinking if I need to also make the __do_clear_bss again. Seems to me, that it will be better to do it also again. But this is not so simple as the gcrt1.S doesn't state anything about it and neither the whole avr-libc source gives me a hint.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I'm thinking if I need to also make the __do_clear_bss again

Not if you now have the code in .init3 as the wipe of .bss like the copy of .data doesn't happen ulti .init so will be AFTER the RAM test. But the BSS one is easy anyway - just wipe the whole of RAM to 0 - it doesn't matter if you wipe MORE than the size of .bss (unless you were relying on .noinit?) as long as .data is copied after RAM is wiped.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

'__do_clear_bss' code always comes from libgcc.a library. Summary-
https://www.avrfreaks.net/index.p...

You are creating way too much work for yourself. A simple function attribute is sure a lot simpler.

You could even do this-

void call_sram_test(void) __attribute__((naked,section(".init3")));
void call_sram_test(void){
  if(sram_test()){
    panic();
  }
}

Since it appears your sram test saves enough of the sram to at least return from a call, you could simply call it in .init3

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@clawson
the Ram test is still in main(). I put my __do_copy_data after the RAM test and everything works fine :D
I was just curious about the __do_clear_bss because I can see it in my .lss file but couldn't find where it comes from and how to implement it by myself.

Quote:
'__do_clear_bss' code always comes from libgcc.a library.

OK

Quote:

void call_sram_test(void) __attribute__((naked,section(".init3")));
void call_sram_test(void){
  if(sram_test()){
    panic();
  }
} 

I think I have to do it without naked, because I read in the gcc manual that

Quote:
The only statements that can be safely included in naked functions are asm statements that do not have operands. All other statements, including declarations of local variables, if statements, and so forth, should be avoided. Naked functions should be used to implement the body of an assembly function, while allowing the compiler to construct the requisite function declaration for the assembler.
and my RAM test is using variables, if statements, for loops and so forth.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Another alternative is to test the RAM one section at a time. If you know where the data you want to keep is located, you can save it in one of the sections that has already been tested and restore it when you're done.

If you think education is expensive, try ignorance.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
and my RAM test is using variables, if statements, for loops and so forth.
but your ram test is not naked (in my latest call_sram_test version), only the function in .init3 that calls the test. I don't see any problem with the simple naked function in .init3. A call, a test of r24, then either continue to .init4, or call some panic function (or whatever).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Which was my previous point - use 'register' to bind local variables to registers (and check the .lss that it actually worked!). In that case you CAN ignore that warning from the manual - what it's warning you about are locals created on a stack frame when the function has no prologue to create a stack frame (and anyway you are going to wipe the stack memory so that couldn't work anyway). Like Curt says just use something like:

void sram_test(void) __attribute__((naked,section(".init3"))); 
void sram_test(void){ 
  register uint8_t i asm("r16");
  register uint8_t j asm("r17");
  etc.

  // test of RAM using i, j, etc. 
}

int main(void) {
 // rest of program
}

You don't even need a call to invoke sram_test() anywhere in the program. The very fact that it is 'naked' and in section '.init3' means that after the jmp from the reset vector the code will first pass through the stuff from the runtime library that disables interrupts, sets the stack pointer and puts 0 in R1. It will then fall into sram_test() in .init3 and (again because of 'naked') fall out of the bottom of it and into the code from the runtime in .init4 that does the _clear_bss, _copy_data things and that in turn will eventually fall into the "call main; jmp exit" stuff.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah ok, now I understand the 'naked' thing :)

I allready put the variables into register, otherwise the RAM test fails because the variables are overwritten during the test.
I did it like this:

  	volatile register unsigned char *p_val,*p_sav,i asm("r2")
	,sav asm("r3");

  	volatile register unsigned char val asm("r4");
  	volatile register signed  char h asm("r5"); 

The stack is saved during the test. The Memory test is divided in 2 memory sections and after testing section 1 the stack is copied there and than section 2 is tested. Works pretty good.

I will try out using the init3 for the ram test. You don't let me any choice :wink:

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The reason I mentioned asm("r16") / asm("r17") in the above rather than r4/r5 like you use is that LDI (and some other ops) can be used with R16 and above where it's more convoluted to do that kind of thing on the lower numbered registers. So you might want to move up to some of the later registers in that.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK. I will move them up.