unsigned char FLASH_MAS[512] @0xF800; // rezervuojam page'a flasho rashymui
void WriteToFlash(byte data) { byte z=0; FCDIV = 39; // set flash clock , my bus is 8 MHz so 39 for (i=0;i<50;i++); // wait a bit, just in case if (!FSTAT_FACCERR) { FLASH_MAS[0] = data; // set flash page adress of interest FCMD = 0x40; FSTAT_FCBEF=1; // erase it if (FSTAT_FPVIOL || FSTAT_FACCERR); // error else while (!FSTAT_FCCF); // ok, wait for completion } StartTX(0x07); while(SensorState==sTX); if (!FSTAT_FACCERR) { FLASH_MAS[0] = data; // put my data to cerain address FCMD = 0x20; FSTAT_FCBEF=1; // write that data if (FSTAT_FPVIOL || FSTAT_FACCERR); // error else while (!FSTAT_FCCF); // ok, wait for write completion }}
Solved! Go to Solution.
Hello, and welcome to the forum.
The likely reason for the linker error is that you have not defined the ToCopyToStack segment within the PRM file for the project. This is the location of the code that will eventually be pushed to the stack, and executed from there. The size of this segment will need to closely match the size of the RAM based function, otherwise an excessive amount of the stack will be required if the segment is made too large.
Keep in mind that there is a total of 256 bytes only for the QD4 device. To maximize the available stack it is better to explicitly allocate as many of your global variables as possible to zeropage RAM, otherwise the allocation will be to default RAM in page 1.
An alternative method to the direct use of the stack for the RAM based function is to use a fixed location in RAM for the function (usually at the bottom of the stack segment). The size of the function using this method is only a few bytes. The attached code demonstrates this method for the QG8 device, and should be readily adaptable to the QD4. Maybe only the PRM file would differ.
Regards,
Mac
const unsigned char FLASH_MAS[512] @0xF800 = 0x55;
#pragma CODE_SEG ToCopyToStack // routine in ram void MyStackFunction (byte CMD) { FCMD = CMD; // write command (erase or write) FSTAT_FCBEF=1; // start execution asm(nop); // wait for completion asm(nop); asm(nop); asm(nop);}#pragma CODE_SEG DEFAULT void WriteToFlash(byte data) { DisableInterrupts; FCDIV = 39; // initialize clcok FSTAT = 0; // clear errors asm(nop); // just in case asm (lda #$55 ); // add some data to accumulator asm (ldhx #$F800); // put address of the page to erase in H:X asm (sta 0,x ); // write accumulator to that address MyStackFunction (0x40); for (i=0;i<255;i++); // just in case... wait FSTAT = 0; // clear errors asm(nop); // just in case asm (lda data ); // add some data to accumulator asm (ldhx #$F800); // put address of the page to erase in H:X asm (sta 0,x ); // write accumulator to that address MyStackFunction (0x20); for (i=0;i<255;i++); // just in case wait EnableInterrupts;}
... StartTX(0x33); // send "hello" message through ~rs232 while(SensorState == sTX); // wait till finished sending WriteToFlash(0x10); // write 0x10 to 0xF800 address COMMAND_CONSTANT = FLASH_MAS[0]; //take constant from 0xF800 (which is FLASH_MAS[0]) for(;;) { ...
extern char __SEG_START_ToCopyToStack[];extern char __SEG_SIZE_ToCopyToStack[];typedef char(* my_funtype)(char, char);#pragma CODE_SEG ToCopyToStack // sukasi steke viskas void MyStackFunction (char CMD, char data) { // PROBABLY SOMETHING WRONG HERE asm { lda data ldhx #$F800 sta ,x } FCMD = CMD; FSTAT_FCBEF = 1; asm { nop nop nop nop } FSTAT_FCBEF = 0; asm { nop nop nop nop }}#pragma CODE_SEG DEFAULTchar CopyToStackandExecute(char data) { /* this function will copy the function 'MyStackFunction' to stack and execute it */ char res; #define RAM_BUF_SIZE 40 /* enough to keep 'MyStackFunction' */ char ramBuf[RAM_BUF_SIZE]; char counter; char *srcPtr; if (sizeof(ramBuf) < (size_t)__SEG_SIZE_ToCopyToStack) { return 0; } srcPtr = (char *)__SEG_START_ToCopyToStack; for(counter=0; counter<(char)__SEG_SIZE_ToCopyToStack; counter++) { ramBuf[counter] = *srcPtr++;}/* call it! *///for (i=0;i<255;i++);StartTX(0x81);while(SensorState==sTX);res = ((my_funtype)ramBuf)(0x40, data); /* I don't receive 0x82 (processor resets) hence here program hangsasm (nop);asm (nop);asm (nop);StartTX(0x82);while(SensorState==sTX);res = ((my_funtype)ramBuf)(0x20, data);asm (nop);asm (nop);asm (nop);StartTX(0x83);while(SensorState==sTX);//for (i=0;i<255;i++);return res;}
; flash.asm file XDEF flashPDIS XDEF flashPEN XDEF flashErasePage XDEF flashWrite INCLUDE "mc9s08qd4.inc"MY_ZEROPAGE: SECTION SHORTSTAT DS.B 1 ;Flash STATUS interl useADRS DS.W 1 ;Address to program.ADRR DS.W 1 ;Data-to-program address.LEN DS.B 1 ;Data-to-program lengthSTACK DS.W 1 ;Stack pointer backup.DEFAULT_ROM: SECTIONflashErasePage: sthx ADRS ;Argument load. lda #(mFSTAT_FPVIOL+mFSTAT_FACCERR) ;mask sta FSTAT ;abort any command and clear errors mov #EraseSubSize, STAT ;length of flash erase routine to STAT tsx sthx STACK ldhx #EraseSubEnd-1 ;point at last byte to move to stack bra DoOnStack ;execute prog code from stack RAMEndErase: rts;*******************************************************************************************flashWrite: sthx ADRR ;Load Source Address from H:X sta LEN ;Load Length from A beq EndWrite lda 4,SP ;Load Dest from SP. sta ADRS+1 lda 3,SP sta ADRS lda #(mFSTAT_FPVIOL+mFSTAT_FACCERR) ;mask sta FSTAT ;abort any command and clear errors mov #ProgSubSize, STAT ;length of flash prog routine to STAT tsx sthx STACK ldhx #ProgSubEnd-1 ;point at last byte to move to stack bra DoOnStackEndWrite: rts; bra DoOnStack ;execute prog code from stack RAM ; fallthru to this routine;*******************************************************************************************DoOnStack: lda ,x ;read from flash psha ;move onto stack aix #-1 ;next byte to move dbnz STAT, DoOnStack tsx ;point to sub on stack jmp ,x ;execute the sub on the stack (will return on it's own);*******************************************************************************************EraseSub: ldhx ADRS ;get flash address sta 0,x ;write to flash; latch addr and data lda #mPageErase ;get flash command sta FCMD ;write the flash command lda #mFSTAT_FCBEF ;mask to initiate command sta FSTAT ;[pwpp] register command nop ;[p] want min 4~ from w cycle to rChkDoneErase: lda FSTAT ;[prpp] so FCCF is valid lsla ;FCCF now in MSB bpl ChkDoneErase ;loop if FCCF = 0 ldhx STACK txs rts ;refer status back to PCEraseSubEnd:EraseSubSize: equ (*-EraseSub);*******************************************************************************************ProgSub: lda FSTAT ;check FCBEF and #mFSTAT_FCBEF ;mask it beq ProgSub ;loop if not empty ldhx ADRR lda 0,x aix #1 sthx ADRR ldhx ADRS ;get flash address sta 0,x ;write to flash; latch addr and data aix #1 sthx ADRS lda #mBurstProg ;get flash command sta FCMD ;write the flash command lda #mFSTAT_FCBEF ;mask to initiate command sta FSTAT ;[pwpp] register commandComCMD: ;Missing from Original AN2295 lda FSTAT and #mFSTAT_FCCF ;Loop if FCCF not set. beq ComCMD dbnz LEN,ProgSub ;all bytes in a row—ChkDoneProg: lda FSTAT ;[prpp] so FCCF is valid lsla ;FCCF now in MSB bpl ChkDoneProg ;loop if FCCF = 0 ldhx STACK txs rtsProgSubEnd:ProgSubSize: equ (*-ProgSub);*******************************************************************************************flashPDIS: lda FPROT and #$7F sta FPROT rtsflashPEN: lda FPROT ora #$80 sta FPROT sta NVPROT rts
// flash.h/*Erase_Page and Write_Flash routines from AN2295*//*FlashInit routine from Motorola's Standard Software SGF Driver v3.0*/#define BUS_CLK 4000000 //Programed bus speed in Hz#if (BUS_CLK/200000)>64 #define FLASH_CLK 0x40|(BUS_CLK/(8*200000)-1)#else #define FLASH_CLK BUS_CLK/200000#endif/*Erase_PageParameter: unsigned char *dest - Any address in the page to be erased*/void flashErasePage(uchar *far dest);/*Write_FlashParameter: unsigned char *dest - First address to be programed. length - Number of data bytes to be written. unsigned char *source - Pointer to the Address containing the first data byte.*/void flashWrite(uchar *far dest,uchar length,uchar *far source);/*Flash_InitParameter: unsigned char FBusDiv - Value to program to the FCDIV register.usage: Flash_Init(FLASH_CLK);*/// flashInit: be sure to have programmed the correct value of BUS_CLKvoid flashInit(void) { // Clear FACCERR and FPVIOL if (FSTAT&(FSTAT_FACCERR_MASK|FSTAT_FPVIOL_MASK)) { FSTAT|=(FSTAT_FACCERR_MASK|FSTAT_FPVIOL_MASK); } do { FCDIV=FLASH_CLK; } while (FSTAT_FCBEF==0);}void flashPDIS(void);void flashPEN(void);#endif
// main.c file#include "flash.h"#define EEDATA_ADDR 0xfc00struct { /*something*/ } eedata; // data which should be stored // into flash at address 0xfc00void main(void) { // Initalize peripherals flashInit(); // Initialize Flash divisor for in-application programming // Write data into memory at address 0xfc00 DisableInterrupts; flashErasePage((uchar *far)EEDATA_ADDR); flashWrite((uchar *far)EEDATA_ADDR,sizeof(eedata),(uchar *far)&eedata); EnableInterrupts;}
flashErasePage((uchar *far)EEDATA_ADDR); flashWrite((uchar *far)EEDATA_ADDR,sizeof(eedata),(uchar *far)&eedata); I get lots of errors, like "illegal cast operation", "type mismatch" and so on... Is this code working for you? And are you testing it on QD4?
extern char __SEG_START_ToCopyToStack[];extern char __SEG_SIZE_ToCopyToStack[];typedef char(* my_funtype)(unsigned char, unsigned char);#pragma CODE_SEG ToCopyToStack // sukasi steke viskas void MyStackFunction (unsigned char CMD, unsigned char data) { asm { lda data ldhx #$F800 // my writing/erasing location sta ,x } FCMD = CMD; FSTAT = 0x80;//_FCBEF = 1; asm { nop nop nop nop } FSTAT = 0; while (!FSTAT_FCCF);}#pragma CODE_SEG DEFAULTchar CopyToStackandExecute(char data) { /* this function will copy the function 'MyStackFunction' to stack and execute it */ char res; /* return value of 'MyStackFunction' */ #define RAM_BUF_SIZE 37 /* enough to keep 'MyStackFunction' */ char ramBuf[RAM_BUF_SIZE]; char counter; char *srcPtr; if (sizeof(ramBuf) < (size_t)__SEG_SIZE_ToCopyToStack) { /* our buffer is too small to keep the function! */ return 0; /* failure */ } srcPtr = (unsigned char *)__SEG_START_ToCopyToStack; for(counter=0; counter<(unsigned char)__SEG_SIZE_ToCopyToStack; counter++) { ramBuf[counter] = *srcPtr++;}/* call it! */res = ((my_funtype)ramBuf)(0x40, data);// eraseres = ((my_funtype)ramBuf)(0x20, data);// write byte datareturn res;}
FCDIV = 39; FSTAT = 0; CopyToStackandExecute(0x10);
Hello All:
when I try to compile the code of Sauliuz (on CodeWarrior6.2.), I get this message every time:
L4024: No information available for segment ToCopyToStack
Where's my mistake?
Just to mention, my microcontroller is a S9S08QD2.
I have no problems in self-erasing and - writing to flash when I use the pre-made CodeBean IFsh from Proc Experts. My only problem is, that the functions in this bean are HUGE, eating almost 1kB of flash (another 0.5kb required for the page to be erased, so there are only 0.5kB for the code itself :smileysad:
Therefore I am looking for small and compact functions, allowing the µC to self-program...
Any hints?
Hello, and welcome to the forum.
The likely reason for the linker error is that you have not defined the ToCopyToStack segment within the PRM file for the project. This is the location of the code that will eventually be pushed to the stack, and executed from there. The size of this segment will need to closely match the size of the RAM based function, otherwise an excessive amount of the stack will be required if the segment is made too large.
Keep in mind that there is a total of 256 bytes only for the QD4 device. To maximize the available stack it is better to explicitly allocate as many of your global variables as possible to zeropage RAM, otherwise the allocation will be to default RAM in page 1.
An alternative method to the direct use of the stack for the RAM based function is to use a fixed location in RAM for the function (usually at the bottom of the stack segment). The size of the function using this method is only a few bytes. The attached code demonstrates this method for the QG8 device, and should be readily adaptable to the QD4. Maybe only the PRM file would differ.
Regards,
Mac
Hello BigMac,
as always, you've just solved my problem ;-))) Thank you very much indeed for your help, my code is working now!
I've read all about this topic on the forum earlier and I have been trying to adapt your QC8 routines to my application for a while . And yes, it is working now, after your explanation concerning the allocation of variables on the stack :-) Below is my .prm file for the QD2 (the QD2 has only 128 bytes RAM). I am still not quite sure if I really need 30 bytes for the SSTACK, but it is working now well.
The biggest advantage to me is, that your routine saves me some 450 bytes FLASH, compared to the readily available code beans (IFsh) from Processor Experts (which I got from this forum, too).
This is a huge improvement to me, since the QD has only 2kB FLASH, 0.5kB must be reserved for the page to be erased/written to and there's very little left for the actual code. Now I feel confident to finish my tasks even with this small µC.
(ups, I'm sorry, the code formatting tool here is not working for copy/paste snippets :smileywink:
/**************************************************************************************************
.prm file for S9S08QD2 with EEPROM emulation routines
**************************************************************************************************/
NAMES
END
SECTIONS
SSTACK = READ_WRITE 0x0080 TO 0x00B0; // software stack is 30 bytes wide
Z_RAM = READ_WRITE 0x00B0 TO 0x00FF;
FLASH_TO_RAM = READ_ONLY 0xFA00 TO 0xFA0F RELOCATE_TO 0x0080; //here resides the eeprom emulation routine
ROM = READ_ONLY 0xFA0F TO 0xFFA9;
PAGE_TO_BE_ERASED = READ_WRITE 0xF800 TO 0xF9FF; //reserve the page to be erased before eeprom emulation
END
PLACEMENT
DEFAULT_ROM, ROM_VAR, STRINGS INTO ROM;
DEFAULT_RAM, // non-zero page variables
_DATA_ZEROPAGE, // zero page variables
MY_ZEROPAGE INTO Z_RAM;
FLASH_ROUTINE INTO FLASH_TO_RAM; //the eeprom emulation routine will be copied here
END
//INIT _EntryPoint // The entry point of the application. This function is generated into the CPU module.
STACKSIZE 0x0030 // Size of the system stack. Value can be changed on the "Build options" tab
Hello,
With the use of STACKSIZE parameter, the stack may not be placed within the SSTACK segment, but may occupy decimal 48 bytes (not 30 bytes) immediately above your global variables, within DEFAULT_RAM. This may be problematic with the very small amount of RAM available (all within page 0). Check the project map file for the actual placement of the variables and the stack. To ensure the stack is placed within SSTACK, the STACKTOP parameter might be used in lieu of STACKSIZE.
However, with the position of SSTACK immediately above the MCU registers, if the stack should overflow, this will over-write the register settings, a situation that may be very difficult to debug. If global variables were to be over-written instead, the debug situation may be a little easier. I would therefore suggest placing the stack above DEFAULT_RAM.
You might also make use of the ROM1 segment for the code that is transfered to RAM - a minor saving of flash.
The following PRM arrangement may be more suitable.
/* Linker parameter file for mc9s08qd2 */
NAMES END
SEGMENTS
Z_RAM = READ_WRITE 0x0080 TO 0x00BF;
SSTACK = READ_WRITE 0x00C0 TO 0x00FF;
ROM = READ_ONLY 0xFA00 TO 0xFFA9;
ROM1 = READ_ONLY 0xFFC0 TO 0xFFCF RELOCATE_TO 0x00C0;
END
PLACEMENT
DEFAULT_RAM,
_DATA_ZEROPAGE, MY_ZEROPAGE INTO Z_RAM;
_PRESTART, /* startup code */
STARTUP, /* startup data structures */
ROM_VAR, /* constant variables */
STRINGS, /* string literals */
VIRTUAL_TABLE_SEGMENT, /* C++ virtual table segment */
DEFAULT_ROM,
COPY /* copy down information */
INTO ROM;
FLASH_ROUTINE INTO ROM1;
/* Pass option -OnB=b to compiler */
END
STACKTOP 0x00FF
The QD2 device is not really suited to C programming because of its very limited resources. Better coding efficiency would be achieved using assembler.
Regards,
Mac
The reason why the linker does not place the stack into the SSTACK segment with STACK_SIZE is the at all the linker predefined names are defined as section names (used before the INTO clause in th placement), not as segment names. Therefore the unused SSTACK segment entry is mostly ignored by the linker (may be used for memory overlap reporting).
Using a predefined linker section name as segment name confuses me, I would recommend not to do that and instead use another name like SSTACK_SEGMENT. When the SSTACK section is placed explicitely into the SSTAC_SEGMENT the STACKTOP entry should not be used anymore.
SEGMENTS SSTACK_SEGMENT = NO_INIT 0x00C0 TO 0x00FF; ... END PLACEMENT SSTACK INTO SSTACK_SEGMENT; ...
When placing SSTACK fully into a segment neither STACKTOP nor STACKSIZE are needed.
Daniel
BTW: The SSTACK_SEGMENT should be NO_INIT qualified. Using READ_WRITE means to initialize the objects allocated in there by the startup code, but the startup code is using the stack to do that.
The reason why it does not crash with READ_WRITE anyway is that the linker explicitely ignores READ_WRITE in the segment for the SSTACK section, using NO_INIT makes this explicit.
Hello.
I also need to write a few bytes to the flash using a QD4 microcontroller. I am using the recommeded solution posted from BigMac, and I was able to understand the code. I am also aware that I need to erase the flash before writing, etc. But for sure am I doing something wrong.
The problem I has was with the CopyInRAM function, from the file S08_Flash.c:
extern char __SEG_START_FLASH_ROUTINE[]; extern char __SEG_SIZE_FLASH_ROUTINE[]; (...) #define Start_data __SEG_START_FLASH_ROUTINE#define Size_data __SEG_SIZE_FLASH_ROUTINEvoid CopyInRAM(void){ char *srcPtr, *dstPtr; int count; srcPtr = (char *)Start_data; dstPtr = (char *)(void *)&Flash_Cmd; for (count = 0; count < (int)Size_data; count++) { *dstPtr = *srcPtr; dstPtr++; srcPtr++; }}
The srcPtr and dstPtr seems unitialized and the copy is not working as expected. The Start_Data and Size_Data may be the cause of the probem, however I can see the FLASH_ROUTINE correctly defined in the "map" file.
I edited the PRM linker file with the advertizements from other posts, as recommended by FSL moderators.
NAMESENDSECTIONS Z_RAM = READ_WRITE 0x0060 TO 0x00DF; FAR_RAM = READ_WRITE 0x0100 TO 0x015F; SSTACK_SEG = NO_INIT 0x00E0 TO 0x00FF; ROM = READ_ONLY 0xF000 TO 0xFBFF; FLASH_SECTOR = READ_ONLY 0xFC00 TO 0xFDFF; FLASH_FUNCT = READ_ONLY 0xFE00 TO 0xFE1F;ENDPLACEMENT SSTACK INTO SSTACK_SEG; DEFAULT_RAM INTO Z_RAM; DEFAULT_ROM, ROM_VAR, STRINGS INTO ROM; _DATA_ZEROPAGE, MY_ZEROPAGE INTO Z_RAM; MY_FAR_DATA INTO FAR_RAM; FLASH_ROUTINE INTO FLASH_FUNCT;ENDINIT _EntryPoint
Please, can someone help me to find the issue? I am still unable to write the flash using BigMac code.
I found one mistake, but code still do not work. In the PRM file we must use the RELOCATE so in the run-time the function can be executed from the correct address at RAM.
Unfortunately the code warrior seems to ignore the extern char statements and the copy to ram does not work. Ouch.
FLASH_FUNCT = READ_ONLY 0xFE00 TO 0xFE1F RELOCATE_TO 0x00E0;
Hello bigmac,
thank you for your advice!
One would rarely find such helping forum users like you!
The prm file you suggested is much more sofisticated than mine and after spending couple of days, thinking waht it actually does, I am really grateful that you have posted it. I wouldn't have be able to gather so much detail by myself.
Just one last question - do you think, that I need to specify the ROM page to be erased (by the ROM writing routine) like this:
PAGE_TO_BE_ERASED = READ_WRITE 0xF800 TO 0xF9FF; //reserve the page to be erased
or is it an obsolete thing to do?
You are right about the assembler and QD2, but I don't really have the time for learning the FSL assembler and I guess after this short trip into the beautiful FSL world I shall return back to reallity spending my whole time with the NEC V850 creatures (being unfortunately much cheaper in industrial quantities than FSL).
Hello,
fat.wombat wrote:Just one last question - do you think, that I need to specify the ROM page to be erased (by the ROM writing routine) like this:
PAGE_TO_BE_ERASED = READ_WRITE 0xF800 TO 0xF9FF; //reserve the page to be erased
or is it an obsolete thing to do?
Firstly, this segment should be READ_ONLY rather than READ_WRITE, since it is a page of flash memory. Its inclusion will do no harm, and may be useful for documentation purposes.
Whether it is actually necessary would depend on the actions required by the linker in allocating variables to the segment. If absolute addresses are handled by the code for both programming and reading the page, and no variables are used, the definition is probably not essential. Of course, the ROM segment must always exclude this flash page.
Regards,
Mac