News:

Let's find out together what makes a PIC Tick!

Main Menu

Flash Memory HEF confusion

Started by dave_gte, Nov 06, 2021, 08:34 PM

Previous topic - Next topic

dave_gte

Hi,
I'm changing PICs on a project cos the old PIC has gone long delivery.
I'm now using a 16F15355 (or 4, or 6)
I was happily using the EEPROM space in the old PIC but now need to tackle the HEF challenge.
I'm sure there's some gotchas I'm missing, but I'm finding the info about this in the manual incomplete and confusing (to me anyway)

Here's my cfg:
Device = 16F15355

Config1 FEXTOSC_OFF, RSTOSC_HFINT32, CLKOUTEN_OFF, CSWEN_OFF, FCMEN_OFF
Config2 MCLRE_OFF, PWRTE_OFF, LPBOREN_OFF, BOREN_OFF, BORV_HI, ZCD_OFF, PPS1WAY_OFF, STVREN_ON
Config3 WDTCPS_WDTCPS_31, WDTE_OFF, WDTCWS_WDTCWS_7, WDTCCS_SC
Config4 BBSIZE_BB512, BBEN_OFF, SAFEN_ON, WRTAPP_OFF, WRTB_OFF, WRTC_OFF, WRTSAF_ON, LVP_OFF
Config5 CP_OFF

All I want to do is read and write a couple of HEF bytes under program control.
 Dim HEF_Addr As Word = $1F80
CWrite HEF_Addr,[$01,$02,$03]  as a test, compiles with no error but examining the memory dump shows nothing is being written using the cfg above. The manual says CWrite - Write data to anywhere in flash memory on devices that support it

Then there's the issue of editing - I realise one needs to erase a block then re-write the lot but I can't find a command listed to do this.

Any suggestions appreciated
Thanks

trastikata

With FLASH you can't write in the memory unless it is cleared beforehand. Then writing in FLASH is done in blocks.

To write in FLASH memory you need to erase an entire block first. Usually FLASH erase and write blocks are different sizes.

For example in 18F26J50 you can write in blocks of say 64 bytes, however FLASH memory is erased in blocks of 1024 bytes.

Writing and erasing should start at the corresponding block boundaries - you need to check the datasheet for the device in question.


dave_gte

Ah, thanks. I'd assumed that 'cleared' was all '1's - which the ram is set to.
Which raises the question is there a command to clear/erase a block?
thanks

trastikata

Indeed by clearing the FLASH segment I mean all 1's.

QuoteWhich raises the question is there a command to clear/erase a block?

CErase BlockAddress

dave_gte

Hi, I can't find CErase in the manual under Flash memory and EEPROM commands and wasn't aware of it, though now I know the name I see it's a reserved word. I'll assume it needs the start address of the block and start from there. Thanks for the info.

dave_gte

tests:
Dim flashTable As Flash8 = $01,$02,$03,$04,$05,$06,$07 writes the correct data at the end of the program code. I can't see how to define where it goes - ie make it write to $1F80 which is the start of the HEF in the 16F15355.

meanwhile, CWrite $1F80,[$01,$02,$03] seems to do nothing, but compiles fine.

Can anyone offer some simple code that definitely works. The manual isn't helping me at the moment. Still can't find any reference to CErase.
All I want to do is read and write 2 bytes in HEF from the program code when it needs to.
Thanks

top204

#6
With the devices since about 2003, a single word cannot be erased then written, it needs to be erased in a block, then written in a block, which is a very silly thing to implement on devices, when the original flash devices, of 20 years ago, had a mechanism in place like an EEPROM, where singles could be written.

I added the CErase command many years ago, but I could see what was happening with the devices, so I did not add it to the compiler's manual because it would cause so much frustration, just as the, now outdated, CWrite command does. This is because a block of flash can only be erased/written when they sit on a specific address boundary... Again, a very silly paged mechanism. If they had made all devices with the same erase/write block size, it would have been acceptable, but every single device family has different erase/write block sizes, and as stated above, some have an erase block of 1024 words, but a write block of 64 words!!! How stupid is that when a large block of flash has to be read and stored, then a single byte altered, then written in multiple blocks to the flash memory??

The bottom line is that the HEF memory is a very, very silly mechanism and the whole block needs to be read into RAM, then the RAM altered, then the block of flash erased, then written with the RAM that as previously read!

It can be done, but each device has different requirements, so a procedure library will need to be created for the device in question that reads, erases, then writes a block of flash memory.

There is a method of setting the EEPROM memory area to the HEF memory area, which I posted on this forum a while back, that uses some declares, for a different purpose, but the block will still need reading, erasing, writing.

The PIC16F15355 device has a block erase size of 64 bytes, and a block write of 64 bytes, so it is not as bad as some devices, so 64 bytes will need to be read into a RAM array and the element in question altered, then the 64-byte block erased, then the RAM written back to flash memory as a 64 byte block.

Below is a piece of code I wrote a while ago to read/write HEF memory on a slightly different device, but it should give a clue as to how to do it. Also, see the datasheet's section 13.

    Device = 16F1503
    Declare Xtal = 16

    Declare Serial_Baud = 9600
    Declare RsOut_Pin   = PORTC.0
    Declare RsOut_Mode  = 0
    Declare RsOut_Pace  = 1000
    Declare RsIn_Pin    = PORTC.1
    Declare RsIn_Mode   = 0

    Symbol cWrite_Flash_BlockSize = 16
    Symbol cErase_Flash_BlockSize = 16
    Symbol cFlash_BlockSize = 16
    Symbol cHEF_Start_Address = $0780
    Symbol cHEF_End_Address = $07FF

    Symbol cErase_FlashBlockSizeMin1 = (cErase_Flash_BlockSize - 1)
    Symbol cWrite_Flash_BlockSizeMin1 = (cWrite_Flash_BlockSize - 1)
    Symbol cBlockOffset = (cHEF_End_Address ^ (cFlash_BlockSize - 1))

    Dim HEF_tGIEBitSave     As Bit
    Dim HEF_bIndex          As Byte
    Dim HEF_wBlockStartAddr As Word
    Dim HEF_bOffset         As Byte

    Dim MyWord As Word
    Dim WriteData As Word
    Dim wFlashAddr As Word
    Dim wBuffer[cFlash_BlockSize] As Word

'---------------------------------------------------------------------------------
Main:
    Flash_WriteWord($0780, wBuffer, $1234)


'---------------------------------------------------------------------------------
' Read a word from a given Flash address
' Input     : pFlashAddr - Flash program memory location from which data has to be read
' Output    : Data word read from given Flash address
' Notes     :  Example
'               Dim MyWord as Word
'               Dim wFlashAddr as Word= $01C0
'               MyWord = Flash_ReadWord(wFlashAddr)
'
Proc Flash_ReadWord(pFlashAddr As Word), Word

    Dim wPMADR As PMADRL.Word
    Dim wPMDAT As PMDATL.Word

    wPMADR = pFlashAddr

    PMCON1bits_CFGS = 0                 ' Deselect Configuration space
    PMCON1bits_RD = 1                   ' Initiate Read
    DelayCS 1
    DelayCS 1

    Result = wPMDAT
EndProc

'---------------------------------------------------------------------------------
' Write a word into a Flash address
' Input     : pFlashAddr - Flash program memory location to which data has to be written
'           : pRamBufAddr - Pointer to an array of size 'cErase_Flash_BlockSize' at least
'           : pValue - Word to be written in Flash
' Output    : None
' Notes     : Example
'               Dim WriteData as Word = $55AA
'               Dim wFlashAddr as Word = $01C0
'               Dim wBuffer[cErase_Flash_BlockSize]
'               Flash_WriteWord(wFlashAddr, wBuffer, WriteData)
'
Proc Flash_WriteWord(pFlashAddr As Word, ByRef pRamBufAddr As Word, pValue As Word)
    Dim wAddress As Word = pRamBufAddr

    HEF_wBlockStartAddr = pFlashAddr & cBlockOffset
    HEF_bOffset         = pFlashAddr & cErase_FlashBlockSizeMin1
'
' Entire row will be erased, so read and save the existing data
'
    For HEF_bIndex = cErase_FlashBlockSizeMin1 To 0 Step -1
        Ptr16(wAddress++) = Flash_ReadWord((HEF_wBlockStartAddr + HEF_bIndex))
    Next
'
' Write at HEF_bOffset
'
    wAddress = pRamBufAddr + HEF_bOffset
    Ptr16(wAddress) = pValue
'
' Writes pRamBufAddr to current block
'
    Flash_WriteBlock(HEF_wBlockStartAddr, pRamBufAddr)
EndProc

'---------------------------------------------------------------------------------
' Write data to complete block of Flash
' Input     : pWriteAddr         - A valid block starting address in Flash
'           : pFlashArray  - Address to an array of size 'cWrite_Flash_BlockSize' at least
' Output    : 0, if the given address is not a valid block starting address of Flash
'           : 1, in case of valid block starting address
' Notes     : Example:
'               $define Flash_Row_Address $0780
'               Dim wBlockData[16] as Word = $0000, $0001, $0002, $0003, $0004, $0005, $0006, $0007,_
'                                            $0008, $0009, $000A, $000B, $000C, $000D, $000D, $000F
'
'               Flash_WriteBlock(Flash_Row_Address, wBlockData)
'
Proc Flash_WriteBlock(pWriteAddr As Word, ByRef pFlashArray As Word), Bit

    Dim wPMADR As PMADRL.Word
    Dim wPMDAT As PMDATL.Word

    HEF_wBlockStartAddr = pWriteAddr & cBlockOffset
'
' Flash write must start at the beginning of a row
'
    If pWriteAddr <> HEF_wBlockStartAddr Then
        Result = 0
        ExitProc
    EndIf
'
' Block erase sequence
'
    Flash_EraseBlock(pWriteAddr)
'
' Block write sequence
'
    PMCON1bits_CFGS = 0                     ' Deselect Configuration space
    PMCON1bits_WREN = 1                     ' Enable wrties
    PMCON1bits_LWLO = 1                     ' Only load write latches

    For HEF_bIndex = 0 To cWrite_Flash_BlockSizeMin1
        wPMADR = pWriteAddr                 ' Load write address
        '
        ' Load data in current address
        '
        wPMDAT = Ptr16(pFlashArray++)

        If HEF_bIndex = cWrite_Flash_BlockSizeMin1 Then
            PMCON1bits_LWLO = 0             ' Start Flash program memory write
        EndIf

        PMCON2 = $55
        PMCON2 = $AA
        PMCON1bits_WR = 1
        DelayCS 1
        DelayCS 1
        Inc pWriteAddr
    Next

    PMCON1bits_WREN = 0                     ' Disable writes
    Result = 1
EndProc

'---------------------------------------------------------------------------------
' Erase complete Flash program memory block
' Input     : pStartAddr - A valid block starting address in Flash program memory
' Output    : None
' Notes     : Example
'               Dim FlashBlockStartAddr as Word = $0780
'               Flash_EraseBlock(FlashBlockStartAddr)

Proc Flash_EraseBlock(pStartAddr As Word)
    Dim wPMADR As PMADRL.Word
    Dim wPMDAT As PMDATL.Word
'
' Load erase address boundary
'
    wPMADR = pStartAddr
'
' Block erase sequence
'
    PMCON1bits_CFGS = 0                         ' Deselect Configuration space
    PMCON1bits_FREE = 1                         ' Specify an erase operation
    PMCON1bits_WREN = 1                         ' Allows erase cycles
'
' Start of required sequence to initiate erase
'
    PMCON2 = $55
    PMCON2 = $AA
    PMCON1bits_WR = 1                           ' Set WR bit to begin erase
    DelayCS 1
    DelayCS 1

    PMCON1bits_WREN = 0                         ' Disable writes
EndProc

trastikata

Les' example is excellent reference point. While he was writing his post, I also prepared a simplified example for your device.   

Device = 16F15355
Declare Xtal = 12

Declare Optimiser_Level = 0

    'Block read/write buffer
Dim bArray [64] As Byte     
Dim i As Byte

Main:
    'Read first block in HEF
    For i = 0 UpTo 63
        bArray[i] = CRead (i + $1F80)
    Next
   
    'Modify data in the buffer
    For i = 0 UpTo 2
        bArray[i] = bArray[i] + 10
    Next
   
    'Erase the first block
    CErase $1F80
    DelayMS 8
   
    'Write back data in HEF
    For i = 0 UpTo 63
        CWrite ($1F80 + i), [bArray[i]]
    Next
    DelayMS 8

    'Create data table at the beginning of HEF
Org $1F80
    CData $01,$02,$03,$04,$05,$06,$07

top204

#8
A nice piece of coding Trastikata. However, with procedures in place, the assembler's Org directive is not allowed and will cause problems in the assembler listing.

Here is a way of placing data in the flash memory address used by HEF. It uses some unofficial declares that I added for my testing regime.

    Device = 16F15355
    Declare Xtal = 16
   
    Declare EEPROM_Size = 64                ' Indicate that there are 64 bytes of EEPROM (HEF in this case)
    Declare EEPROM_Address = $1F80          ' Set the address of the EEPROM (HEF in this case)
'
' Place some data at the EEPROM address, which is now used to store HEF data
'
    EData 1, 2, 3, 4, 5, 6, 7, 8, 9, 0

Within the assembler listing, you will see the data as:

;---------------------------------------------
; EEPROM DATA
    org 0X1F80
    de 1,2,3
    de 4,5,6
    de 7,8,9
    de 0


And notice the address where the data is placed?

The EEPROM commands cannot be used with the HEF data, but either my routines or Trastikata's can.

Also... Add the Heap directive to the array that holds the flash memory, so it will not get in the way of standard variables, and will be created above them. This will help minimise the RAM bank switching that has to be carried out with these devices because of their fragmented RAM banks, and will make the program smaller and faster to operate.

trastikata

As per Les' recommendations the modified code that would allow procedures looks like this:

Device = 16F15355
Declare Xtal = 12

Declare Optimiser_Level = 0

Declare EEPROM_Size = 64                ' Indicate that there are 64 bytes of EEPROM (HEF in this case)
Declare EEPROM_Address = $1F80          ' Set the address of the EEPROM (HEF in this case)

    'Block read/write buffer
Dim bArray [64] As Byte Heap   
Dim i As Byte

Main:
    'Read first block in HEF
    For i = 0 UpTo 63
        bArray[i] = CRead (i + $1F80)
    Next
   
    'Modify data in the buffer
    For i = 0 UpTo 2
        bArray[i] = bArray[i] + 10
    Next
   
    'Erase the first block
    CErase $1F80
    DelayMS 8
   
    'Write back data in HEF
    For i = 0 UpTo 63
        CWrite ($1F80 + i), [bArray[i]]
    Next
    DelayMS 8
   
    DummyProc()
   
    'Infinite sleep procedure for demonstration purposes   
Proc DummyProc()
    While 1 = 1
        Sleep
    Wend
EndProc

    'Create data table at the begining of HEF
EData $01,$02,$03,$04,$05,$06,$07

dave_gte

Many thanks Les and trastikata, you've given me hope when there was no hope  :)

Clearly Microchip did this for their own convenience not their customer's.

Great Cow in their docs claim to handle all this automatically behind the scenes but I have my doubts, and haven't tried it - I'm a Proton boy !

I'll implement what you've both been kind enough to help me with and report back.

Thanks again

dave_gte

Looking promising!

only thing confusing me is the readout of 1F80-1F82 from MPLAB IPE memory dump
my code:
   ' test for HEF modify
    EData $01, $02, $03, $04, $05, $06, $07, $08, $09, $0A

which gives 1F80: 000B?, 000C?, 000D? then 0004 - 000A as expected. Tried a few values and it seems there's an offset of +10 added to the first 3 words

trastikata

Quote from: dave_gte on Nov 07, 2021, 04:24 PMwhich gives 1F80: 000B?, 000C?, 000D? then 0004 - 000A as expected. Tried a few values and it seems there's an offset of +10 added to the first 3 words

If you look in the code, after reading the values from the data table, I am modifying the first three values and writing back the entire block, including modifications, to demonstrate the changes, or otherwise said to be able to verify that the code works.

dave_gte

Oops red face!   :-[

I was locked on to tweaking the EData, but in the final version I'll change just the two array values I need to alter directly, like

 bArray[0] = IO_Config :  bArray[1] = IO_Delay. Then erase block and re-write.

All ok now
Cheers

dave_gte

As a final post on this thread - all working fine now on my target app.

Many thanks again

top204

Adding HEF read/write commands is on my list of things to do with the compilers. It will use a re-usable block of RAM on the device for storage, if it is available, and operate just like the ERead and EWrite commands and HEdata, will operate the same as Edata, but in the HEF flash area.

The compilers already support the HEdata directive, but I need to add to the PPI files where the HEF data is within the flash memory and its size, because these change from device to device, so it will give the error "No HEF memory on this device". I then need to create generic read and write library subroutines. Easier said than done. LOL

The compilers already have the block sizes for standard flash memory in their PPI files for write and erase, which is what the CWrite command uses.

atomix

#16
Les I am very impressed by you as a professional with a capital letter. You have done so much to the compiler positron was the best of all. You are the best of all the talented people I know.

JohnB

#17
Is there still a plan to support HEF Read/Write?
HEData is supported although I cannot see the start location for HEdata in the PPI file.
If I add "HEData value, value" at the start of my app it compiles but will the first 2 locations of the HEF block be programmed?
Can I use CRead to access these locations and do I have to use ORG to define a label for the address of the HEF block?
JohnB