I'm using silabs parts. The 040 has a vmon protection that resets the process when the supply voltage drops to a point where flash could be corrupted. That is enabled in my code. We are seeing devices come back with apparently corrupted flash (these are locked so we actually cannot see the flash contents, but they appear to be locked in reset loops or partially run and then get crazy.
In order to ensure that rouge code doesn't execute the flash write routines, I'd like to be able to alter the flash by using code to overwrite the flash enable lines with nops so it will physically be impossible for the code to alter flags required to initiate a flash write.
Generating code that can overwrite a function is proving to be difficult. How can I force Keil 7.5 C51 compiler to do this?
To enable the flash writes on the silab part, you have to set a bit that enables flash write/erase, then you set a bit that changes the target of the MOVX instruction so that it targets flash. you need code that looks like this: CBYTE can also be used, but it is throwing some strange error messages. unsigned char code* ptr;
ptr=&lcation; // point to a byte in flash boo(); .... boo() { FLSCL=1; PCTL=1; // *ptr=0xAA; // generates a MOVX which, due to the bits above, will target flash PCTL=0; FLSCL=0; // now movx instructions access xram, and writes are disabled. }
What I want to do is to add in a line of code after the bits are set, that will reference foo + some offset. For example, foo+3 happens the be the FLSCL instruction. The CBYTE macro throws an error. When I do a manual replace like this:
*((unsigned char volatile code * &(foo))=0x00;
The compiler swallows it nicely, and completely eliminates the above line. I thought about using a label and generating a point to it, but C doesn't allow that. Then the thought occurred : "Well you can take the address of a function:..."
Currently I have code running that does something similar to this as part of a personalization/configuration procedure:
but I can't make the compiler generate a pointer to foo (see above) CBYTE[foo] throws an error. CBYTE[&foo()] throws an error.....
This is from working code:
void configure_me() { unsigned char volatile xdata * write_ptr; unsigned int t; saved_ie = IE; EA = 0; // disable interrupts (precautionary) write_ptr=&(security[0]); // it is critical that this comes before flash // enable. The damn compiler uses movx // otherwise and screws things up FLSCL=0x01; // enable flash write/erase PSCTL=0x01; // NOT 2, which would erase flash, but 1 write to flash *write_ptr=CBYTE[0xFA00]; PSCTL = 0x00; // MOVX writes target XRAM FLSCL=0; // disable flash write write_flash(&(security[1]),0xFA00,20); // copy serial number FLSCL=0x01; // enable flash write/erase PSCTL = 0x03; // MOVX writes target FLASH memory, and erase is ENabled XBYTE[0xFA00]=0; // erase the flash page PSCTL=0x00; FLSCL=0x00; // disable flash write IE=saved_ie; } idata unsigned char byt2wrt; void write_flash( unsigned char * dest, unsigned char code* srce, int len) { char EA_save; // saves the current state of the interrupts register volatile unsigned char code * source; register volatile unsigned char xdata * destination; EA_save = EA; EA = 0; // disable interrupts (precautionary) source=srce; destination=dest; FLSCL=0x01; // enable flash write/erase do { // copy until len is 0 byt2wrt=*srce; // ensure that no MOVX is used. Variable is in idata. PSCTL = 0x01; // MOVX writes target FLASH memory, and erase is disabled *destination=byt2wrt; PSCTL=0; // MOVX writes to xdata srce++; // advance pointers destination++; len--; } while (len != 0); PSCTL = 0x00; // MOVX writes target XRAM FLSCL=0x00; // disable flash write EA = EA_save; // re-enable interrupts }
Oh, and before I forget, this:
We are seeing devices come back with apparently corrupted flash (these are locked so we actually cannot see the flash contents, but they appear to be locked in reset loops or partially run and then get crazy.
is way insufficient evidence to justify the kind of voodoo you're trying to implement to fix this effect. At the very least, you would have to have taken one of those parts, erased the whole chip and flashed it again with a hex file containing exactly those data it was supposed to have contained, and see if that fixes it.
And even if that did fix it, meaning that some flash contents were not what they were supposed to be: how the heck did you make the leap from those symptoms to a diagnosis like "this flash-write routine must have been called when it's not allowed to be used any longer", not to mention how did you arrive at "let's make it crash deliberately" (which is what overwriting it with zeroes will most likely do) as the supposed cure?
And you seriously didn't expect an access via a (code *) (like in CBYTE[n] = 0, or your original code * ptr) to generate the MOVX operation that will end up writing to code memory, did you?
The original code is: unsigned char volatile xdata * write_ptr; *write_ptr=CBYTE[0xFA00];
You also need to ensure interrupts are disabled, so some interrupt handler won't inadvertantly corrupt flash by issuing a MOVX when the redirect bit is on.
Since write_pointer is a pointer to xdata, the compiler will generate a movc to read the CBYTE, but generate a MOVX for the write.
You can use CBYTE to read flash, but you have to use XBYTE to write to flash if you are not using an intermediate pointer conversion, explicit or implicit.
The Silabs part allows flash programming by the following trick.
"The FLASH memory can be programmed by software using the MOVX write instruction with the address and data byte to be programmed provided as normal operands. Before writing to FLASH memory using MOVX, FLASH write operations must be enabled by setting the PSWE Program Store Write Enable bit (PSCTL.0) to logic 1. This directs the MOVX writes to FLASH memory instead of to XRAM, which is the default target. The PSWE bit remains set until cleared by software. To avoid errant FLASH writes, it is recommended that interrupts be disabled while the PSWE bit is logic 1. FLASH memory is read using the MOVC instruction. MOVX reads are always directed to XRAM, regardless of the state of PSWE." " In 'C', the pointer used to de-reference FLASH writes should NOT be located in xdata space (data and idata are appropriate)."
Disable interrupts before setting PSWE to ‘1’ to prevent interrupt service routines, which may access variables in xdata space, from generating MOVX writes which could corrupt FLASH memory.
MOVX A,@dptr will read xdata MOVX @dptr,a will write code space. In this case their hardware reads and writes flash.
To prevent errors when actually writing something from XRAM into flash, it is recommended that you move the data to data space or idata space first. The above code is writing reading from flash with a movc generated by the CBYTE and writing to flash via movx generated by the pointer.
if you want to write a byte from data space directly, you have to use XBYTE so
XBYTE[checksum] will generate code to load checksum of of data space, and then do a MOVX, which if the redirect bit is turned on, will directly write to flash. if it is not turned on, then it will directly write to XRAM.
CBYTE[checksum] will generate code to load the byte from data space and do a movc to codespace which will result in nothing happening to code space on a silabs flash based part. IF it were a ram based part, then it would modify ram. BUT the silabs parts won't execute out of ram.
Some of the original code was just compiler test cases, to see what the compiler produced.
NOW: you can actually write to flash, without erasing it, as long as you are just clearing bits and you don't do enough of them to fatigue the flash cell.
I hope this clears up your confusion on the issue...
The original post deals with how to generate a pointer that the CBYTE or XBYTE routines can work with that points to a function...
of course reflashing fixes it. That is why we suspect flash is getting corrupted, even though we have no visibility due to locking.
Overwriting the flash enable bit settings is simply one more way to make sure that it is not wild code that is happening to hit the flash write function. If wild code happens, and you have defanged it, then it won't be an issue. It is called bullet-proofing your code.
If the flash corruption is not caused by code executing the flash write enable, then there is a hardware issue.
I suspect that Silabs does not hold the processor in reset until voltage increases, and some time has passed, when the VDD mon sees a low voltage. I'm seeking clarification of that from Silabs.
Don't you understand that a 0x00 is a nop? and overwriting the 3 bytes that set the flag with 3 nops is not going to make the code crash? of course the checksum will also have to be corrected, but that is a given, and trivial to do.
Overwriting the flash enable bit settings is simply one more way to make sure that it is not wild code that is happening to hit the flash write function. If wild code happens, and you have defanged it, then it won't be an issue. It is called bullet-proofing your code. That would require that the chip[ was designed with "modifiable DFR function" NOT likely
If the flash corruption is not caused by code executing the flash write enable, then there is a hardware issue. as said the "hardware issue" is due to you not 'helping' the reset with a pullup. I have 10 thousands of units running a f12x with a pullup on reset and NO problems.
I suspect that Silabs does not hold the processor in reset until voltage increases, and some time has passed, when the VDD mon sees a low voltage. I'm seeking clarification of that from Silabs. they will, like me, tell you it is static
Don't you understand that a 0x00 is a nop? and overwriting the 3 bytes that set the flag with 3 nops is not going to make the code crash? of course the checksum will also have to be corrected, but that is a given, and trivial to do. add that resistor (handgun) or tour supervisor chip (RPG) and stop Mickey Mousing with your code.
Erik
PS if you absolutotally insist, there is a far easier way. use the scratchpad Which, by the way makes sense regardless)
at least your code will set SFLE rather than PSWE so even SFLE need be screwed up by the runaway. of course, then a runaway (if you still insist on no resistor or supervisor) my overwrite your configuration, but that is far less fatal than overwriting the code.
um. you obviously missed my line that said we were adding the reset supervisor and the pullup. Silabs tells me that the part will hold the reset for 100 ms after the vdd rises to 2.7 volts.
The idea with the scratchpad is a good one. Have you ever used the SFLE? I think I'll implement that. That fixes the problem of potentially run away code. Then there is no way that code can overwrite flash. Thanks.
One of the problems we have seen with the 040 unit, is the battery voltage sags, the processor does a reset. Gets through enough of the code typically to get part of a message on the display, and then resets again, over and over. This means that the voltage pops up above 2.7 volts, but as the processor starts running intensive code, there is enough current draw to make the voltage sag. Constant sagging, resetting and sagging over and over again seems to me to be a pure recipe for flash corruption.
Silabs says you can't debug when the SFLE bit is set. I'll have to dig into that a bit, it seems to me that by setting the SFLE bit, that according to the data sheet, the first 128 bytes of flash will be overlayed by the scratch pad...what happens if you get an interrupt during that time? does it continue to execute code from the underlying flash? I assume that it does, but it is not clear from the data sheet.
we probably have over 10,000 units out there with F320 and F310 processors, and we get maybe 20 flash corrupted units back every year. We have slightly over 100 of the 040 based units out there. They are a *lot* more expensive than the 310 units, since they have multiple modes, and a LCD display. we have had a several of them come back...
Silabs tells me that the part will hold the reset while below and for 100 ms after the vdd rises to 2.7 volts.
The idea with the scratchpad is a good one. Have you ever used the SFLE? yes, but not recently (been doing ARMs)
One of the problems we have seen with the 040 unit, is the battery voltage sags that should not happen FIX IT
what happens if you get an interrupt during that time? DO NOT DO NOT DO NOT do anything to the flash without disabling interrupts.
a LCD display. do you stutter? :)
DO NOT DO NOT DO NOT do anything to the flash except reading without disabling interrupts.
Chuckle. I hate it when people say LED lights....
I have interrupts turned off for all flash operations, globally. I'm more concerned with the way the Keil compiler likes to put short character strings in unused space between interrupt jump vectors. What happens if I am trying to copy a short string to the SFLE, and it just happens to live in the first 128 bytes of the main flash page...or the proclivity of the compiler to actually put executing code in the space between interrupt vectors..which I also have seen it do....
Do you have any snippets of code that specifically read data from the scratch page? I am assuming from what I have read, that
SFLE=1; c=CBYTE[00]; SFLE=0;
is the way to read the scratch pad. yes, CBYTE, which generates a movc, but don't know whether to do a EA=0 as well.
and
EA=0; PSCTL=5; FLSCL=1; XBYTE[0]=0xAA; // XBYTE generates a movx FLSCL=0; PSCTL=0; EA=1;
is the way to write to it.
Well see what Silabs tells me about this: but it might be problematic.
SFLE=1; XBYTE[0]=foo(); // where the compiler puts foo in the first 128 bytes of flash SFLE=0;
I'm more concerned with the way the Keil compiler likes to put short character strings in unused space between interrupt jump vectors. What happens if I am trying to copy a short string to the SFLE, and it just happens to live in the first 128 bytes of the main flash page...or the proclivity of the compiler to actually put executing code in the space between interrupt vectors..which I also have seen it do....
A vary astute observation (copying 'normal' flash to the scratchpad would be easily overlooked). You have two options a) locate such strings by absolute address in flash b) copying the string to RAM before enabling the scratchpad
now I get curious (cant investigate, do not have a SILabs board while working ARM) what happens if the scratchpad write (or read?) code happens to end up in the first 128 bytes of flash?
since you have been working on this since 2004 do you have the time to try it out, it should be fairly simle to set up main() { write/read scratchpad while (1)
which should stay within 128 bytes and then see in the debugger what happens.
AH, even simpler, get whatever code in the debugger, rim a bit, stop, open a code memory window 0-7F, in the SFR window, set SFLE, step, did the contents of the code window change? you may have to change the address in the code window and change it back to get current contnts
"Chuckle. I hate it when people say LED lights...."
But isn't that a good expression, when you make a complete lamp from one or more LED, some armature etc?
Nearly as bad as LCD display, PIN number or (my real hated but oh so common one) baud rate.
ROTFLMAO!
Erik was jabbing me just for the LCD bit :>
mine is functionable I've heard that on a major automobile manufactures' ad. shakes head...
and Erik, I may try that...
Silabs guys say:
" The info page is data only, so the interrupt will vector correctly. However if the interrupt access any data in that page of flash it will get results from the info page. "
So it looks like the code will execute under the scratch pad page, but any movx a,#dptr where dptr is pointing to 0-x7f will get set A to what ever is in the scratch pad page, not the underlying main flash page.
That also implies that code cannot execute from the scratch pad page.
So your idea of writing the stuff to the scratch pad page in the 040 instead of flash bullet proofs the code, so that if rouge code gets loose, it can only muck with the scratch pad because the bit for accessing the main flash won't even be in the code. Of course that will cause my checksum to fail... It solves the issue (unless rouge code happens to start executing in the middle of an instruction like the old Microsoft basic interpreters used to do).
Billy boy was fond of doing a branch into the second byte of a 3 byte instruction to execute the remaining two bytes as code, even if they were data, in order to save a byte or two.
I've been working with micros since 1976 and one of my hobbies was disassembling Microsoft Basics. I did the 6809 coco basic, the commodore PET basic, and the trs-80 basic. Completely disassembled and completely commented them.
Later I got a peek at the old microsoft CPM basic, and was amused at some of the comments. "Bill Gates wrote some fast stuff, and Paul Allen wrote some faster stuff" or something very close to that...Been a long time. In 1985 I learned C and rarely did assembly after that, but I did sell a set of cross assemblers under the name CrossSoft out of the old computer shopper.
Light Emitting Diode light.... Liquid Crystal Display display... Personal Identification Number number.... 's why Erik asked me if I stuttered.... :}
any movx a,#dptr where dptr is pointing to 0-x7f will get set A to what ever is in the scratch pad page, not the underlying main flash page. I doubt that very much, however a MOVC, will do so
right. sigh. Silabs says "to read from the scratch page, you have to access it with a code pointer, and to write to it you have to write it with a xdata pointer." So I take it that means after you have set the re-direct bit to send xdata writes to the flash..
I'm having some issues getting this to work. I can write from code space to cratch pad space. Got that working, I can copy a fixed string to the scratch pad flash page.
Right now, what is confusing me, is trying to copy data from one area of the scratch page to another area of the scratch page... it seems like I should set the flag to access the scratch page, read the byte into an data variable (not a xdata variable) then set the write enable, and redirect flag and write to xdata. It just is not working quite that way. What is happening is that I am reading data at the correct address, but instead of it being the scratch pad, it is the main code flash page, cause in trying to read 20 bytes from location 0 and program them into location 0x60, I'm copying a portion of the vector table into the scratch pad area.
I'll ultimately get it, but it doesn't seem like I can use the Keil CBYTE macro XBYTE macro to do this.