CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

write_virtual_eeprom() function does not work on PIC16F1779

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
apakSeO



Joined: 07 Dec 2016
Posts: 60
Location: Northeast USA

View user's profile Send private message

write_virtual_eeprom() function does not work on PIC16F1779
PostPosted: Mon Dec 17, 2018 11:11 am     Reply with quote

Target: PIC16F1779
CCS: v5.081

Few months ago, I posted a new topic asking for help using the virtual_eeprom.c driver with program flash memory.

Referencing this topic:
http://www.ccsinfo.com/forum/viewtopic.php?t=57436&highlight=virtualeeprom

I followed all advice on the above topic thread. I can initialize the PFM contents and read them back successfully using calls to read_virtual_eeprom(addr) , however, I cannot write back to the PFM. Calls using write_virtual_eeprom(addr, data) seem to achieve nothing in terms of modifying the contents of program memory at the specified address.

I have used the NOPROTECT fuse in order to allow writes to program memory.

Test code below:

Code:

#include <16F1779.h>
#device PIC16F1779
#device ADC=10
#use delay(INTERNAL=16MHZ, CLOCK=8MHZ)

#fuses INTRC_IO
#fuses NOWDT               
#fuses NOPROTECT
#fuses NOBROWNOUT
#fuses NOPUT 

#rom int16 0x3FC0 = {0, 0x12, 1, 0x34}

#define VIRTUAL_EEPROM_8BIT_ADDY
#define VIRTUAL_EEPROM_NUM_PAGES 2

#include "virtual_eeprom.c" 


#define pullupsPortA    0b00100100   // pushbuttons on A2, A5
#define initTrisA       0b00100100

#define TEST_LED        PIN_B1
#define TEST_LED2      PIN_B2

int1 writeSuccess;


void flashLED(void)
{
    for(int i =0; i<7; i++)
    {
        output_toggle(TEST_LED);
        delay_ms(100);
       
    }   
}

void main(void)
{
   
    init_virtual_eeprom();
    writeSuccess = 0;       // write_eeprom() returns '1' if successful
     
    port_a_pullups(pullupsPortA);
    delay_ms(1);   
    set_tris_a(initTrisA);
     
    while(1)
    {
         if(!input(PIN_A2))
         {
            delay_ms(50);       // quick n' dirty debounce
             
            if(!input(PIN_A2))
            {
                 
                flashLED();                      // we got into if{} block 
                writeSuccess = write_virtual_eeprom(0, 0xAB);
                writeSuccess = write_virtual_eeprom(1, 0xCD);

                if(writeSuccess)  output_high(TEST_LED2);
   
            }   
        }
                 
    }//while(1)   
}//main())






In debug mode, I stop execution prior to a button press and see the values in PFM as I have intialized them. Pushing the button results in the expected flash indication; also, writeSuccess flip to a 1. However, the values at 0x3FC1, 0x3FC3 never change despite code getting into the if {} block containing the calls to write_virtual_eeprom.



What seems to happen instead is that the NEXT contiguous areas of PFM are written to, rather than starting at the virtual addresses 0 and 1:





NOTES: (a) In another version of test code, I am actually piping out the byte values over UART and decoding with a scope. Results are the same. (b) I am using the modified version of virtual_eeprom.c, with edits done by Ttelmah, in order to place these values in specific region of high-endurance flash, of which this PIC has 128 bytes. The exact edits are specified in the link above.



What am I missing in my code above in order to get successful writes to PFM? Is there any reason that in this particular PIC, write_virtual_eeprom is not writing to the correct addresses?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Dec 17, 2018 11:40 pm     Reply with quote

Are you sure your posted code compiles ? I tried to compile it with
vs. 5.081 and I got:
Quote:

--- Info 300 "...\virtual_eeprom.c" Line 189(2,5): More info: Segment at 00000-007FF (0000 used)
--- Info 300 "...\virtual_eeprom.c" Line 189(2,5): More info: Segment at 00800-00FFF (0000 used)
--- Info 300 "...\virtual_eeprom.c" Line 189(2,5): More info: Segment at 01000-017FF (0000 used)
--- Info 300 "...\virtual_eeprom.c" Line 189(2,5): More info: Segment at 01800-01FFF (0000 used)
--- Info 300 "...\virtual_eeprom.c" Line 189(2,5): More info: Segment at 02000-027FF (0000 used)
--- Info 300 "...\virtual_eeprom.c" Line 189(2,5): More info: Segment at 02800-02FFF (0000 used)
--- Info 300 "...\virtual_eeprom.c" Line 189(2,5): More info: Segment at 03000-037FF (0000 used)
--- Info 300 "...\virtual_eeprom.c" Line 189(2,5): More info: Segment at 03800-03FBF (0000 used)
--- Info 300 "...\virtual_eeprom.c" Line 189(2,5): More info: Segment at 03FC0-03FC3 (0004 used) Priv
--- Info 300 "...\virtual_eeprom.c" Line 189(2,5): More info: Segment at 03FC4-03FFF (0000 used)
--- Info 300 "...\virtual_eeprom.c" Line 189(2,5): More info: Attempted to create: 03FC0-03FFF for #org
*** Error 126 "...\virtual_eeprom.c" Line 189(2,5): Invalid ORG range
1 Errors, 0 Warnings.
Build Failed.
Halting build on first failure as requested.
BUILD FAILED: Mon Dec 17 21:36:20 2018

I shortened the path for brevity.
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Tue Dec 18, 2018 12:15 am     Reply with quote

He's doing the ROM reservation removal, I outlined in the thread he
points to at the top. Without this you can't use #rom to initialise the
memory, without the complaints you have.

Worryingly, this looks like it may be a problem with the
write_program_memory function. Ignore the virtual eeprom code
I've just tried an experiment with this chip, and the write doesn't
seem to be working. If so, the second chip in a couple of days with
a problem here.... Sad

Have just disassembled the code, and both this and the 12F1571
(which was the chip having the problem here), are using exactly
the same 'write' routine (except on this chip the page size is twice
as large), and both have the same fault, with the bit to switch from
erasing, to actually writing, not being cleared. So the write function
doesn't actually work... :(
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Tue Dec 18, 2018 2:53 am     Reply with quote

OK.

Code:

//Register and bit definitions
#byte PMADRH=getenv("SFR:PMADRH")
#byte PMADRL=getenv("SFR:PMADRL")
#byte PMDATH=getenv("SFR:PMDATH")
#byte PMDATL=getenv("SFR:PMDATL")
#bit CFGS=getenv("BIT:CFGS")
#bit WREN=getenv("BIT:WREN")
#bit FREE=getenv("BIT:FREE")
#bit LWLO=getenv("BIT:LWLO")
#BIT WR=getenv("BIT:WR")
#BIT RD=getenv("BIT:RD")
#byte PMCON2=getenv("SFR:PMCON2")

void unlock(void)
{
  PMCON2=0x55; //required sequence to unlock and trigger operation
  PMCON2=0xAA;
  WR=TRUE;
  delay_cycles(1);
  delay_cycles(1);
  WREN=FALSE;
}

void my_erase_program_eeprom(int16 address)
{
  //erases the page at the selected address
  disable_interrupts(GLOBAL);
  CFGS=FALSE; //program memory not config
  PMADRL=(make8(address,0) & (getenv("FLASH_ERASE_SIZE")-1)) ;
  PMADRH=make8(address,1);
  FREE=TRUE; //setup to erase
  WREN=TRUE;
  unlock(); //unlock and trigger the erase
  enable_interrupts(GLOBAL);
}

#if (getenv("FLASH_ERASE_SIZE") != getenv("FLASH_WRITE_SIZE"))
   #WARNING "This only works for chips with matching erase/write sizes"
#endif   

void my_write_program_memory(int16 address, int16 *data, int16 ctr)
{
  //Attempt to emulate the CCS behaviour. May not be right...
  int8 count, loops;
  disable_interrupts(GLOBAL);
  CFGS=FALSE; //program memory not config space
  LWLO=TRUE; //load latches only 
  do
  {
     loops=(ctr & (getenv("FLASH_WRITE_SIZE") - 1)); 
     FREE=FALSE;
     //First test if write is to a page boundary
     if ((address & (getenv("FLASH_ERASE_SIZE") - 1))==0)
        my_erase_program_eeprom(address); 
     else
     {   
        //Here we are writing 'into' a page, so need to read the existing
        //data before starting
        //Need to read the data into the physical data latches
        PMADRL=make8(address & (getenv("FLASH_WRITE_SIZE") - 1),0); //store start address for this pass
        PMADRH=make8(address,1);
        for (count=0;count<(getenv("FLASH_WRITE_SIZE")/2);count++)
        {
           RD=TRUE;
           delay_cycles(1);
           //data is now in the data registers
           //need to write back to the write latches
           WREN=TRUE;
           unlock();
           PMADRL++; //one word per pass           
        }
     }   
     FREE=FALSE; //ensure this is set back if an erase has been done.
     //Load latches with new data and write
     PMADRL=make8(address,0); //store start address for this pass
     PMADRH=make8(address,1);

     address+=loops; //update start address for next pass.
     ctr-=loops; //update counter
     loops/=2; //two bytes per loop
     for (count=0;count<loops;count++)
     {  //Loop writing to latches & trigger physical write on last pass.
        PMDATL=make8(data[count],0);
        PMDATH=make8(data[count],1);
        WREN=TRUE;       
        if (count==(loops-1)) //last pass
           LWLO=FALSE; //set to trigger physical write
        unlock();
        PMADRL++; //one word per pass
     }
  } while (ctr!=0);
  enable_interrupts(GLOBAL);
}


This is an attempt to write a replacement write routine.
I've done a couple of basic tests, and it appears to work, but
'no guarantees'...
Since the original poster has already edited the virtual_eeprom file,
simply add this to your code, and change all references to
write_program_memory, to be to my_write_program_memory, and see
if it then starts working....
apakSeO



Joined: 07 Dec 2016
Posts: 60
Location: Northeast USA

View user's profile Send private message

PostPosted: Tue Dec 18, 2018 8:24 am     Reply with quote

Thank you all for commenting back and verifying my observations. Indeed, the result is not optimal nor intuitive for the functionality described in the virtual_eeprom.c driver, modified or otherwise.

I contacted CCS support on this issue. CCS response:

Quote:
What you're seeing is exactly how the virtual eeprom driver is suppose to work, when you write new data to it is writes in the next free location. When you call the read_virtual_eeprom() function it starts at the end of the virtual eeprom page it's in and works it's way back until it finds the address you're trying to read, so the last value written for that address will always be what's found. It's done this way to avoid excessive erases of program memory and works well on devices where the write and erase page sizes don't match. For your device however the write and erase page sizes do match, and because of how the write_program_memory() function works on devices were the write and erase page match you don't really gain anything from using the virtual eeprom driver. If it was me for your device I wouldn't use the virtual eeprom driver and just use the write_program_eeprom() and read_program_eeprom() function to write and read the program memory address that you want to use like an eeprom.


OK, I get it. But to me, the write function behavior is still unexpected because the ( vAddr, data ) pair in program memory already exists so the fact that the driver creates a new entry under the same virtual address at the next contiguous program memory is both confusing and nonsensical.

My next steps will be to try the code Ttelmah has posted below and test it. Failing that, I'll look into write_program_eeprom() as CCS support suggests.

Again my kindest thanks to helping me out.
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Tue Dec 18, 2018 8:50 am     Reply with quote

It can't do it any other way.

Key point to understand, is that you can only erase an entire page.
Now visualise you have written a 'pair'. Perhaps a value of '1' say. You
cannot change this to any value other than '0', without erasing.
If you erase, you have to erase the whole page, and this also takes a very
long time.

Now what the driver does is 'flag' this value as 'finished' by setting the
address to the '0' value, and then writing a new pair with the address.
It 'walks' up the page doing this, until it has no space left in the page,
and at this point copies all 'in use' pairs to the second page, and erases
the first page. It then proceeds using the second page, until it is full.
When this happens it copies the 'in use' values back to this.
Doing it this way, it only has to erase, when the page is full. Hence a
massive saving in erase cycles, and in speed (since a write without erase
is dozens of times faster than the erase).

However on test, I was not seeing the EEPROM actually being written
when the update was done.

I have just tested with 'my_write_program_memory' replacing the CCS
function, and with this you do see values being written to the memory.
apakSeO



Joined: 07 Dec 2016
Posts: 60
Location: Northeast USA

View user's profile Send private message

PostPosted: Tue Dec 18, 2018 1:01 pm     Reply with quote

Were there any other changes you made in order for the newly-described function "my_write_program_memory()" to work?

I made the changes you described, but I still receive the same output after calling write_virtual_eeprom():



Code is now in 3 separate files.

main.c:
Code:

#include <16F1779.h>
#device PIC16F1779
#device ADC=10
#use delay(INTERNAL=16MHZ, CLOCK=8MHZ)

#fuses INTRC_IO
#fuses NOWDT               
#fuses NOPROTECT
#fuses NOBROWNOUT
#fuses NOPUT 

#rom int16 0x3FC0 = {0x00, 0x12, 0x01, 0x34}

#define VIRTUAL_EEPROM_8BIT_ADDY
#define VIRTUAL_EEPROM_NUM_PAGES 2

#include "my_virtual_eeprom.c"
#include "virtual_eeprom.c" 


#define pullupsPortA    0b00100100   // pushbuttons on A2, A5
#define initTrisA       0b00100100

#define TEST_LED        PIN_B1
#define TEST_LED2       PIN_B2

int1 writeSuccess;


void flashLED(void)
{
    for(int i =0; i<7; i++)
    {
        output_toggle(TEST_LED);
        delay_ms(100);       
    }   
}

void main(void)
{   
    init_virtual_eeprom();
    writeSuccess = 0;       // write_eeprom() returns '1' if successful
     
    port_a_pullups(pullupsPortA);
    delay_ms(1);   
    set_tris_a(initTrisA);

    while(1)
    {
        if(!input(PIN_A2))
        {
            delay_ms(50);       // quick n' dirty debounce
             
            if(!input(PIN_A2))
            {               
                flashLED();                      // we got into if{} block 
               
                writeSuccess = write_virtual_eeprom(0, 0xAB);
                writeSuccess = write_virtual_eeprom(1, 0xCD);
               
                if(writeSuccess) output_high(TEST_LED2);     
            }
        }               
    }//while(1)   
}//main())


virtual_eeprom.c ( which has been modified with changes described in this thread )

Code:


//  NOTE:  This is a modified version of the CCS included virtual_eeprom.c driver
//  See thread: 
//
//  http://www.ccsinfo.com/forum/viewtopic.php?p=221776#221776
//
// (1) #ORG statements commented out to allow for memory content initialization
// (2) original write_program_memory() called in this file replaced with
//     user forum provided "my_write_program_memory()"  included
//     in new file, my_virtual_eeprom.c
//
//
///////////////////////////////////////////////////////////////////////////////
////                      Virtual EEPROM Driver                            ////
////                                                                       ////
////  This driver uses two flash erase pages in program memory as virtual  ////
////  eeprom memory. This driver uses write-balancing to avoid excessive   ////
////  erasing of program memory.  This driver is optimized to use lower    ////
////  eeprom addresses first; however, it is not a requirement to use the  ////
////  available addresses in any particular order. The functions           ////
////  which drive operation of the driver are detailed below:              ////
////                                                                       ////
////  init_virtual_eeprom()       Initializes pointer and global variable  ////
////                              for use with write and read.  Does NOT   ////
////                              clear previous virtual eeprom memory.    ////
////                              Must be called at beginning of program   ////
////                              before write and read are used.          ////
////                                                                       ////
////  data = read_virtual_eeprom(addy)      Reads one byte from virtual    ////
////                                        eeprom address "addy". Default ////
////                                        return is 0xFF if no data is   ////
////                                        found for given address.       ////
////                                                                       ////
////  ok = write_virtual_eeprom(addy,data)  Writes "data" byte to virtual  ////
////                                        eeprom address "addy". Returns ////
////                                        true if write is successful,   ////
////                                        returns false if memory is     ////
////                                        full or an invalid address is  ////
////                                        used.                          ////
////                                        This process can take a lot    ////
////                                        of time, especially if large   ////
////                                        pages are being used.          ////
////                                                                       ////
////  clear_virtual_eeprom()      Clears and reinitializes all virtual     ////
////                              eeprom.                                  ////
////                                                                       ////
////  num = virtual_eeprom_max_entries()  This function is provided for    ////
////                               debugging.  It returns the total number ////
////                               number of entries that can be saved to  ////
////                               the flash memory.  To increase this     ////
////                               value, then increase                    ////
////                               VIRTUAL_EEPROM_NUM_PAGES.               ////
////                                                                       ////
////  VIRTUAL_EEPROM_NUM_PAGES can be defined, when defines the total      ////
////  number of the PIC's flash erase pages used for the virtual EEPROM.   ////
////  The actual value used is 2x this value (two pages are used for       ////
////  writing balancing algorithm).  The total number of entries that      ////
////  can be saved to the EE depends on this setting.                      ////
////                                                                       ////
////  If VIRTUAL_EEPROM_16BIT_ADDY is defined then you can use 16bit       ////
////  addresses for the EEPROM (if not defined 8bit addresses are used).   ////
////  This option is always used on PCD, PCM and PCB chips since these     ////
////  architecture have a multi-byte instruction word (however PCM will    ////
////  limit an address to 14bits and PCB will limit an address to 12bits). ////
////  Using this option reduces the total number of variables that can     ////
////  be stored because now more space is being used to store addresses.   ////
////  VIRTUAL_EEPROM_8BIT_ADDY can be used to force 8bit address on        ////
////  PCB, PCH and PCD chips.                                              ////
////                                                                       ////
//// EXAMPLE USAGE                                                         ////
//// --------------------------------------------------------------------  ////
//// The compiler's ex_extee.c example can be quickly modified to work     ////
//// as a demo of this driver.  The ex_extee.c example shows how to use    ////
//// an external EEPROM, but can be adapted to use this library as a       ////
//// virtual EEPROM.                                                       ////
////                                                                       ////
//// To accomplish this, first find this line:                             ////
////     #include "2416.c"                                                 ////
////                                                                       ////
//// Then replace it with these lines:                                     ////
////     #include "virtual_eeprom.c"                                       ////
////     #define init_ext_eeprom() init_virtual_eeprom()                   ////
////     #define read_ext_eeprom(a) read_virtual_eeprom(a)                 ////
////     #define write_ext_eeprom(a, v) write_virtual_eeprom(a, v)         ////
////     #define EEPROM_ADDRESS unsigned int8                              ////
////                                                                       ////
///////////////////////////////////////////////////////////////////////////////
////                                                                       ////
//// HISTORY                                                               ////
////                                                                       ////
//// March 10th, 2017 -                                                    ////
////  Speed optimizations.                                                 ////
////  Added workaround for PIC24FJxxxGx6xx families, which require writing ////
////     8 bytes at once else a double write trap is generated.            ////
////  Added virtual_eeprom_max_entries() function.                         ////
////  Added VIRTUAL_EEPROM_NUM_PAGES.  Changed older                       ////
////     VIRTUAL_EEPROM_DOUBLE_SPACE define to map to this newer setting.  ////
////  Added VIRTUAL_EEPROM_16BIT_ADDY.                                     ////
////  The PAGE0_START, PAGE0_END, PAGE1_START and                          ////
////     PAGE1_END were not being calculated correctly for PCD             ////
////     devices.  That means upgrading to this driver will cause your     ////
////     EEPROM values currently stored in memory to be lost since it      ////
////     will now be reading/writing to the correct space.  The old        ////
////     incorrect values are still there but commented out, in case you   ////
////     need backwards compatibility.                                     ////
////                                                                       ////
//// April 26th, 2016 -                                                    ////
////  Fixed issue causing driver not to compile with PCD compiler.         ////
////                                                                       ////
//// March 31st, 2016 -                                                    ////
////  The PAGE0_START, PAGE0_END, PAGE1_START and                          ////
////     PAGE1_END were not being calculated correctly for PCM and PCH     ////
////     devices.  That means upgrading to this driver will cause your     ////
////     EEPROM values currently stored in memory to be lost since it      ////
////     will now be reading/writing to the correct space.  The old        ////
////     incorrect values are still there but commented out, in case you   ////
////     need backwards compatibility.                                     ////
////  Fixed bugs with PIC16.                                               ////
////  Optimized ROM/RAM memory use with PIC16 and PIC18 devices.           ////
////                                                                       ////
///////////////////////////////////////////////////////////////////////////////
////            (C) Copyright 1996,2017 Custom Computer Services           ////
////                                                                       ////
//// This source code may only be used by licensed users of the CCS C      ////
//// compiler or to users who purchased the rights to use this code.  This ////
//// source code may only be distributed to other licensed users of the    ////
//// CCS C compiler or other users who purchased the rights to this code.  ////
//// The rights to use this code can only be granted by CCS.  No other     ////
//// use, reproduction or distribution is permitted without written        ////
//// permission.  Derivative programs created using this software in       ////
//// object code form are not restricted in any way.                       ////
////                                                                       ////
////                         http://www.ccsinfo.com                        ////
///////////////////////////////////////////////////////////////////////////////
#ifndef  VIRTUAL_EEPROM_C
#define  VIRTUAL_EEPROM_C

#ifndef debug_ve_printf
#define debug_ve_printf(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p)
#endif

#if (getenv("FLASH_ERASE_SIZE")<32)
#error Erase page size on this PIC is too small to use this library effectively
#endif

#if (defined(__PCM__) || defined(__PCB__) || defined(__PCD__)) && !defined(VIRTUAL_EEPROM_8BIT_ADDY)
   #define VIRTUAL_EEPROM_16BIT_ADDY
#endif

#if defined(VIRTUAL_EEPROM_8BIT_ADDY) && defined(VIRTUAL_EEPROM_16BIT_ADDY)
   #error cant use both at the same time
#endif

#if defined(VIRTUAL_EEPROM_16BIT_ADDY)
   typedef unsigned int16 ve_virtual_address_t;
#else
   typedef unsigned int8 ve_virtual_address_t;
#endif

#if !defined(VIRTUAL_EEPROM_16BIT_ADDY)
   #define _VE_INVALID_ADDRESS   -1
#else
   #if defined(__PCM__)
      #define _VE_INVALID_ADDRESS   (unsigned int16)0x3fff
   #elif defined(__PCB__)
      #define _VE_INVALID_ADDRESS   (unsigned int16)0xfff
   #else
      #define _VE_INVALID_ADDRESS   -1
   #endif
#endif

#if defined(__PCH__)
   #define _VE_BPA  1
#else
   #define _VE_BPA  2
#endif

#if defined(VIRTUAL_EEPROM_DOUBLE_SPACE)
   #define VIRTUAL_EEPROM_NUM_PAGES 2
#endif

#ifndef VIRTUAL_EEPROM_NUM_PAGES
   #define VIRTUAL_EEPROM_NUM_PAGES 1
#endif

#define _VE_PAGE_BYTES (getenv("FLASH_ERASE_SIZE")*VIRTUAL_EEPROM_NUM_PAGES)

#if ((getenv("PROGRAM_MEMORY") % (getenv("FLASH_ERASE_SIZE")/_VE_BPA)) != 0)
   //go back one page, because this page is being used for config bits
   #define _VE_FLASH_SIZE_INSTRUCTIONS  (getenv("PROGRAM_MEMORY") - (getenv("PROGRAM_MEMORY") % (getenv("FLASH_ERASE_SIZE")/_VE_BPA)))
#else
   #define _VE_FLASH_SIZE_INSTRUCTIONS  getenv("PROGRAM_MEMORY")
#endif

#define _VE_PAGE0_START (_VE_FLASH_SIZE_INSTRUCTIONS - _VE_PAGE_BYTES/_VE_BPA)
#define _VE_PAGE0_END (_VE_FLASH_SIZE_INSTRUCTIONS)
#define _VE_PAGE1_START (_VE_FLASH_SIZE_INSTRUCTIONS - (2*_VE_PAGE_BYTES/_VE_BPA))
#define _VE_PAGE1_END _VE_PAGE0_START

// See:  https://www.ccsinfo.com/forum/viewtopic.php?t=57436   as to why these lines are commented out below.
// This file virtual_eeprom.c is modified to allow #ROM initiation before program runtime   
//#org _VE_PAGE0_START,(_VE_PAGE0_END-1) {}
//#org _VE_PAGE1_START,(_VE_PAGE1_END-1) {}

typedef unsigned int32 _VE_ADDRESS_;  //an address for the flash memory

/*
 //old values, are broken
 #define _VE_PAGE0_START (_VE_FLASH_SIZE_INSTRUCTIONS-(2*_VE_PAGE_BYTES/_VE_BPA))
 #define _VE_PAGE0_END (_VE_FLASH_SIZE_INSTRUCTIONS - _VE_PAGE_BYTES)
 #define _VE_PAGE1_START (_VE_FLASH_SIZE_INSTRUCTIONS-(3*_VE_PAGE_BYTES/_VE_BPA))
 #define _VE_PAGE1_END (_VE_FLASH_SIZE_INSTRUCTIONS-(2*_VE_PAGE_BYTES))
*/

struct
{
   _VE_ADDRESS_ next;
   int1 page;
} _g_VE;

#if defined(__PCM__) && (getenv("FLASH_ERASE_SIZE") == getenv("FLASH_WRITE_SIZE"))
   static void _ve_erase_program_eeprom(_VE_ADDRESS_ addy)
   {
      char b[_VE_BPA] = {0xFF, 0xFF};
      unsigned int16 n;
   
      n = _VE_PAGE_BYTES/_VE_BPA;
     
      // change addy to the start of the erase block
      addy &= ~(_VE_ADDRESS_)(_VE_PAGE_BYTES/_VE_BPA-1);
     
      debug_ve_printf(debug_putc, "_ve_erase_program_eeprom(%LX) n=%lu\r\n", addy, n);
     
      while(n-- != 0)
      {
         //write_program_memory(addy++, b, sizeof(b));
         my_write_program_memory(addy++, b, sizeof(b));
         
      }
   }
#elif (VIRTUAL_EEPROM_NUM_PAGES>1)
   void _ve_erase_program_eeprom(_VE_ADDRESS_ addy)
   {
      int i = VIRTUAL_EEPROM_NUM_PAGES;
     
      while(i--)
      {
        #if defined(__PCD__)
         erase_program_memory(addy);
        #else
         erase_program_eeprom(addy);
        #endif
         addy += getenv("FLASH_ERASE_SIZE")/_VE_BPA;
      }
   }
#else
   #if defined(__PCD__)
      #define _ve_erase_program_eeprom(addy) erase_program_memory(addy)
   #else
      #define _ve_erase_program_eeprom(addy) erase_program_eeprom(addy)
   #endif
#endif

static int1 _find_virtual_eeprom(ve_virtual_address_t addy, int8 *pRet);
static void _VE_move_page(void);

#if defined(__PCD__) && (getenv("MIN_FLASH_WRITE") != 4)
   #if (getenv("MIN_FLASH_WRITE") == 8)
      #define VE_PIC_WORKAROUND_RECORD_ALIGNMENT   8  //workaround for PIC24FJxxxGx6xx
   #else
      #error do
   #endif
#endif

typedef union
{
   struct
   {
      union
      {
         ve_virtual_address_t  address;
         
        #if (_VE_BPA==1)
         unsigned int8 addAsInstruction;
        #elif (_VE_BPA==2)
         unsigned int16 addAsInstruction;
        #else
         #error do
        #endif
      };
      union
      {
         unsigned int8 value;
         
        #if (_VE_BPA==1)
         unsigned int8 valAsInstruction;
        #elif (_VE_BPA==2)
         unsigned int16 valAsInstruction;
        #else
         #error do
        #endif
      };
   };
  #if defined(VE_PIC_WORKAROUND_RECORD_ALIGNMENT)
   unsigned int8 alignment[VE_PIC_WORKAROUND_RECORD_ALIGNMENT];
  #endif
} _g_ve_record_t;

#define __VE_RECORD_ISNT_BLANK(rec)   ((rec.address != (ve_virtual_address_t)_VE_INVALID_ADDRESS) || (rec.value != (unsigned int8)-1))

void init_virtual_eeprom(void)
{
   _g_ve_record_t record;
   _VE_ADDRESS_ end;

   read_program_memory(_VE_PAGE0_START, &record, sizeof(record));
   
   debug_ve_printf(debug_putc, "init_virtual_eeprom() %X %X\r\n", record.address, record.value);
   //debug_ve_printf(debug_putc, "P0 start=%LX end=%LX    P1 start=%LX end=%LX\r\n", _VE_PAGE0_START, _VE_PAGE0_END, _VE_PAGE1_START, _VE_PAGE1_END);
   
   #if 0
   _g_VE.next = _VE_PAGE0_START;
   read_program_memory(_g_VE.next, &record, sizeof(record));
   debug_ve_printf(debug_putc, "P0 read_ram %X %X\r\n", record.address, record.value);
   read_program_memory(_VE_PAGE0_START, &record, sizeof(record));
   debug_ve_printf(debug_putc, "P0 read_rom %X %X\r\n", record.address, record.value);
   #endif
 
   if (__VE_RECORD_ISNT_BLANK(record))
   {
      _g_VE.page = 0;
      _g_VE.next = _VE_PAGE0_START;
      end = _VE_PAGE0_END;
     
      read_program_memory(_VE_PAGE1_START, &record, sizeof(record));
      if (__VE_RECORD_ISNT_BLANK(record))
      {
         // both pages aren't blank - something funny happened.  erase the other one.
         _ve_erase_program_eeprom(_VE_PAGE1_START);
         
         debug_ve_printf(debug_putc, "init_virtual_eeprom() %X %X BOTH_PAGES_ARENT_BLANK\r\n", record.address, record.value);
      }
   }
   else
   {
     #if 0
      read_program_memory(_VE_PAGE1_START, &record, sizeof(record));
      if (!__VE_RECORD_ISNT_BLANK(record))
      {
         debug_ve_printf(debug_putc, "Both pages blank, clear again\r\n");
         clear_virtual_eeprom();
      }
     #endif
     
      _g_VE.page = 1;
      _g_VE.next = _VE_PAGE1_START;
      end = _VE_PAGE1_END;
   }
   
   //debug_ve_printf(debug_putc, "init_virtual_eeprom() using page=%u next=%LX end=%LX\r\n", _g_VE.page, _g_VE.next, end);
   
   while(_g_VE.next < end)
   {
      read_program_memory(_g_VE.next, &record, sizeof(record));
     
      if (!__VE_RECORD_ISNT_BLANK(record))
      {
         break;
      }
     
      //debug_ve_printf(debug_putc, ".");
      //debug_ve_printf(debug_putc, "%LX=%LX=%X ", _g_VE.next, (unsigned int16)record.address, record.value);


      _g_VE.next += sizeof(record)/_VE_BPA;
   }
   
   debug_ve_printf(debug_putc, "init_virtual_eeprom() page=%u next=%LX\r\n", _g_VE.page, _g_VE.next);
}

#if defined(VE_PIC_WORKAROUND_ROM_WRITE_BYTES)
   void _ve_write_program_memory(_VE_ADDRESS_ romStart, unsigned int8 *ramPtr, size_t n)
   {
      char data[VE_PIC_WORKAROUND_ROM_WRITE_BYTES];
      _VE_ADDRESS_ romLoc;
      int frag, i;
     
      romLoc = romStart & ~((_VE_ADDRESS_)(VE_PIC_WORKAROUND_ROM_WRITE_BYTES/_VE_BPA)-1);
      frag = ((_VE_ADDRESS_)romStart - (_VE_ADDRESS_)romLoc) * (_VE_ADDRESS_)_VE_BPA;
     
      debug_ve_printf(debug_putc, "_ve_min(rom=%LX, n=%u) frag=%u", romStart, n, frag);
     
      while(n > 0)
      {
         read_program_memory(romLoc, data, VE_PIC_WORKAROUND_ROM_WRITE_BYTES);
         //memset(&data, 0xFF, sizeof(data));

         i = VE_PIC_WORKAROUND_ROM_WRITE_BYTES - frag;
         if (i > n)
            i = n;

         debug_ve_printf(debug_putc, " rom=%LX,i=%u", romLoc, i);

         memcpy(&data[frag], ramPtr, i);
         
         //write_program_memory(romLoc, data, VE_PIC_WORKAROUND_ROM_WRITE_BYTES);
         my_write_program_memory(romLoc, data, VE_PIC_WORKAROUND_ROM_WRITE_BYTES);
         
         
         
         romLoc += VE_PIC_WORKAROUND_ROM_WRITE_BYTES / _VE_BPA;
         
         n -= i;
         ramPtr += i;
         frag = 0;
      }
     
      debug_ve_printf(debug_putc, "\r\n");
   }
#else
   //#define _ve_write_program_memory(_romLoc, _ramPtr, _n)   write_program_memory(_romLoc, _ramPtr, _n)
   #define _ve_write_program_memory(_romLoc, _ramPtr, _n)   my_write_program_memory(_romLoc, _ramPtr, _n)
#endif

static void _VE_move_page(void)
{
   _g_ve_record_t record;
   _VE_ADDRESS_ curr, stop;

   curr = _g_VE.next;
   if (_g_VE.page == 0)
   {
      stop = _VE_PAGE0_START;
      _g_VE.next = _VE_PAGE1_START;
   }
   else
   {
      stop = _VE_PAGE1_START;
      _g_VE.next = _VE_PAGE0_START;
   } 
   
   debug_ve_printf(debug_putc, "_VE_move_page() oldPage=%u oldPtr=%LX newPtr=%LX\r\n", _g_VE.page, stop, _g_VE.next);
   
   _g_VE.page = !_g_VE.page;
   
   while(curr > stop)
   {
      curr -= sizeof(record)/_VE_BPA;
     
      read_program_memory(curr, &record, sizeof(record));
               
      if (!_find_virtual_eeprom(record.address, 0))
      {
         //debug_ve_printf(debug_putc, "w_%u@%LX@%LX ", record.value, (unsigned int16)record.address, _g_VE.next);
         _ve_write_program_memory(_g_VE.next, &record, sizeof(record));
         _g_VE.next += sizeof(record)/_VE_BPA;
         //debug_ve_printf(debug_putc, "n%LX\r\n", _g_VE.next);
      }
   }
   
   _ve_erase_program_eeprom(curr);

   debug_ve_printf(debug_putc, "_VE_move_page() DONE newPage=%u stopReadd=%LX nextPtr=%LX\r\n", _g_VE.page, curr, _g_VE.next);
}

unsigned int1 write_virtual_eeprom(ve_virtual_address_t addy, int8 val) {
   _g_ve_record_t record;
   
   debug_ve_printf(debug_putc, "write_virtual_eeprom(%LX,%X) ", addy, val);
   
   if((addy & _VE_INVALID_ADDRESS) == _VE_INVALID_ADDRESS)
   {
      debug_ve_printf(debug_putc, "FAIL_MAX_EEPROM\r\n");
      return(FALSE);
   }
 
   if (_g_VE.page == 0){
      if (_g_VE.next >= _VE_PAGE0_END){
         _VE_move_page();
         if (_g_VE.next >= _VE_PAGE1_END)
         {
            debug_ve_printf(debug_putc, "\r\nFAIL_PAGES_FULL_01\r\n");
            return FALSE;
         }
      }
   }
   else {
      if (_g_VE.next >= _VE_PAGE1_END){
         _VE_move_page();
         if (_g_VE.next >= _VE_PAGE0_END)
         {
            debug_ve_printf(debug_putc, "\r\nFAIL_PAGES_FULL_10\r\n");
            return FALSE;
         }
      }
   }
   
  #if (_VE_BPA != 1)
   record.addAsInstruction = 0;
   record.valAsInstruction = 0;
  #endif

   record.address = addy;
   record.value = val;
   
   //debug_ve_printf(debug_putc, "write_virtual_eeprom() %LX.%LX page=%u ptr=%LX next=%LX\r\n", record.addAsInstruction, record.valAsInstruction, _g_VE.page, _g_VE.next, (_g_VE.next+sizeof(record)/_VE_BPA));
   debug_ve_printf(debug_putc, "write_virtual_eeprom() page=%u ptr=%LX\r\n", _g_VE.page, _g_VE.next);
   
   _ve_write_program_memory(_g_VE.next, &record, sizeof(record));
   
   _g_VE.next += sizeof(record)/_VE_BPA;
   
   return(TRUE);
}

int8 read_virtual_eeprom(ve_virtual_address_t addy) {
   int8 ret = -1;
   _find_virtual_eeprom(addy, &ret);
   return(ret);
}

static int1 _find_virtual_eeprom(ve_virtual_address_t addy, int8 *pRet)
{
   _VE_ADDRESS_ ptr;
   _VE_ADDRESS_ end;
   _VE_ADDRESS_ start;
   
   _g_ve_record_t record;
   
   if((addy & _VE_INVALID_ADDRESS) == _VE_INVALID_ADDRESS)
   {
      debug_ve_printf(debug_putc, "read_virtual_eeprom(%X) FAIL_MAX_EEPROM\r\n", addy);
      return(0);
   }

   if (_g_VE.page == 0)
   {
      end = _VE_PAGE0_END - sizeof(record)/_VE_BPA;
      start = _VE_PAGE0_START;
   }
   else
   {
      end = _VE_PAGE1_END - sizeof(record)/_VE_BPA;
      start = _VE_PAGE1_START;
   }

   for (ptr=end; ptr>=start; ptr=ptr-(sizeof(record)/_VE_BPA))
   {
      read_program_memory(ptr, &record, sizeof(record));
     
      if (record.address==addy)
      {
         debug_ve_printf(debug_putc, "read_virtual_eeprom(%X) ret=%X page=%u ptr=%LX\r\n", addy, record.value, _g_VE.page, ptr);
         if (pRet != 0)
         {
            *pRet = record.value;
         }
         return(1);
      }
   }
   
   debug_ve_printf(debug_putc, "read_virtual_eeprom(%X) FAIL_NO_MATCH\r\n", addy);
   return(0);
}

void clear_virtual_eeprom(void){
   debug_ve_printf(debug_putc, "clear_virtual_eeprom() next=%X\r\n", _VE_PAGE0_START);
   _ve_erase_program_eeprom(_VE_PAGE0_START);
   _ve_erase_program_eeprom(_VE_PAGE1_START);
   _g_VE.next = _VE_PAGE0_START;
   _g_VE.page = 0;
}

unsigned int32 virtual_eeprom_max_entries(void)
{
   unsigned int32 i, j;
   i = _VE_PAGE_BYTES;
   j = sizeof(_g_ve_record_t);
   return((unsigned int32)_VE_PAGE_BYTES/sizeof(_g_ve_record_t));
}

#endif


and finally, my_virtual_eeprom.c:

Code:

//
//   Using the included/modified virtual_eeprom.c driver is not working
//   insofar as #rom initialized data is not being modified in situ
//   
//   See http://www.ccsinfo.com/forum/viewtopic.php?p=221776#221776
//   
//   User forum member Ttelmah provides this code included in this file,
//   "my_virtual_eeprom.c"
//
//

//Register and bit definitions

#byte PMCON2=getenv("SFR:PMCON2")
#byte PMADRH=getenv("SFR:PMADRH")
#byte PMADRL=getenv("SFR:PMADRL")
#byte PMDATH=getenv("SFR:PMDATH")
#byte PMDATL=getenv("SFR:PMDATL")
#bit CFGS=getenv("BIT:CFGS")
#bit WREN=getenv("BIT:WREN")
#bit FREE=getenv("BIT:FREE")
#bit LWLO=getenv("BIT:LWLO")
#bit WR=getenv("BIT:WR")
#bit RD=getenv("BIT:RD")



void unlock(void)
{
  PMCON2=0x55; //required sequence to unlock and trigger operation
  PMCON2=0xAA;
  WR=TRUE;
  delay_cycles(1);
  delay_cycles(1);
  WREN=FALSE;
}

void my_erase_program_eeprom(int16 address)
{
  //erases the page at the selected address
  disable_interrupts(GLOBAL);
  CFGS=FALSE; //program memory not config
  PMADRL=(make8(address,0) & (getenv("FLASH_ERASE_SIZE")-1)) ;
  PMADRH=make8(address,1);
  FREE=TRUE; //setup to erase
  WREN=TRUE;
  unlock(); //unlock and trigger the erase
  enable_interrupts(GLOBAL);
}

#if (getenv("FLASH_ERASE_SIZE") != getenv("FLASH_WRITE_SIZE"))
   #WARNING "This only works for chips with matching erase/write sizes"
#endif   

void my_write_program_memory(int16 address, int16 *data, int16 ctr)
{
  //Attempt to emulate the CCS behaviour. May not be right...
  int8 count, loops;
  disable_interrupts(GLOBAL);
  CFGS=FALSE; //program memory not config space
  LWLO=TRUE; //load latches only 
  do
  {
     loops=(ctr & (getenv("FLASH_WRITE_SIZE") - 1)); 
     FREE=FALSE;
     //First test if write is to a page boundary
     if ((address & (getenv("FLASH_ERASE_SIZE") - 1))==0)
        my_erase_program_eeprom(address); 
     else
     {   
        //Here we are writing 'into' a page, so need to read the existing
        //data before starting
        //Need to read the data into the physical data latches
        PMADRL=make8(address & (getenv("FLASH_WRITE_SIZE") - 1),0); //store start address for this pass
        PMADRH=make8(address,1);
        for (count=0;count<(getenv("FLASH_WRITE_SIZE")/2);count++)
        {
           RD=TRUE;
           delay_cycles(1);
           //data is now in the data registers
           //need to write back to the write latches
           WREN=TRUE;
           unlock();
           PMADRL++; //one word per pass           
        }
     }   
     FREE=FALSE; //ensure this is set back if an erase has been done.
     //Load latches with new data and write
     PMADRL=make8(address,0); //store start address for this pass
     PMADRH=make8(address,1);

     address+=loops; //update start address for next pass.
     ctr-=loops; //update counter
     loops/=2; //two bytes per loop
     for (count=0;count<loops;count++)
     {  //Loop writing to latches & trigger physical write on last pass.
        PMDATL=make8(data[count],0);
        PMDATH=make8(data[count],1);
        WREN=TRUE;       
        if (count==(loops-1)) //last pass
           LWLO=FALSE; //set to trigger physical write
        unlock();
        PMADRL++; //one word per pass
     }
  } while (ctr!=0);
  enable_interrupts(GLOBAL);
}



I checked the SFR / bit mappings in my IDE, and they are correct ( I am using MPLAB, I think you use the CCS IDE ) because I've had issues in the past with wrong defines in the SFR's for MPLAB, but it did not help matters.
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Tue Dec 18, 2018 1:50 pm     Reply with quote

If you look, at 0x3Fc8, that has written the two new values as it should.
On your original code, it had not written the new values.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group