Assistance needed with 18F27Q10 Sector Writes

Discuss PIC and electronic related things

Moderators: David Barker, Jerry Messina

Post Reply
bernardj
Posts: 13
Joined: Fri Jul 10, 2015 7:52 pm

Assistance needed with 18F27Q10 Sector Writes

Post by bernardj » Sat Jul 10, 2021 7:34 pm

Hi

I have been battling to get the PFM sector write functionality going on the 18F27Q10. The chip supports writing to program memory using either words or sectors. I successfully managed the writing to PFM using words (2 bytes) without hassles. Different story though with writing by sector.

Simplifying my code to the following code segment, it results in no change of the sector at address $10000, even though I have tried to implement the datasheet's ASM example as accurately as possible. I expect to see a whole sector filled with value 12 (arbitrary number), but all bytes remain at $FF (caused by the sector erase).

Code protection is off in the configuration settings.

Any help would be appreciated, thanks very much!!

Regards

Bernard

Code: Select all

Sub writeSector()
  Dim writeAddress as LongWord
  Dim i as Word
  
  INTCON       = 0 
  writeAddress = $10000 
  
  NVMCON0.7   = 1
  NVMADRU      = writeAddress.byte2  
  NVMADRH      = writeAddress.byte1
  NVMADRL      = writeAddress.byte0

  NVMCON2      = $BB
  NVMCON2      = $44
  NVMCON1.1    = 1        'SECRD
  while NVMCON1.1 = 1
  wend
  
  NVMCON2      = $CC
  NVMCON2      = $33
  NVMCON1.6    = 1        'SECER
  while NVMCON1.6 = 1
  wend
  
  if NVMCON0.4 = 1 then     ' if error occurred
    UART.Write("Err 1",13,10)
  endif
                  
  TBLPTRU      = writeAddress.byte2  
  TBLPTRH      = writeAddress.byte1
  TBLPTRL      = writeAddress.byte0
  
  for i = 0 to 255
    TABLAT = 12 
    asm
      TBLWT*+
    end asm
  next
  
  NVMCON2 = $DD
  NVMCON2 = $22
  NVMCON1.5 = 1        'SECWR
  while NVMCON1.5 = 1
  wend
  
  if NVMCON0.4 = 1 then     ' if error occurred
    UART.Write("Err 2",13,10)
  endif
  

end sub

Jerry Messina
Swordfish Developer
Posts: 1246
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Assistance needed with 18F27Q10 Sector Writes

Post by Jerry Messina » Sun Jul 11, 2021 12:01 am

Hi Bernard,

I think in order to use the sector write commands you need to use the special Buffer Ram locations, which you can find the address of in the 18F27Q10.bas device file...

Code: Select all

#define _nvm_bufferram_start = $0D00   // nvm buffer ram start address
#define _nvm_bufferram_end = $0DFF     // nvm buffer ram end address
I haven't actually tried that, so tomorrow I'll see if I can piece together an example and test it.

bernardj
Posts: 13
Joined: Fri Jul 10, 2015 7:52 pm

Re: Assistance needed with 18F27Q10 Sector Writes

Post by bernardj » Sun Jul 11, 2021 7:35 am

Thanks very much, sir.

My understanding is that using TBLPTR is used for program memory access. If one must use a special RAM area for sector writes, how do I access that RAM address - with TBLPTR as well? I would love to see your approach to the solution - I am sure I am going to learn something new :-).

Thanks very much in advance

Regards

Bernard

Jerry Messina
Swordfish Developer
Posts: 1246
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Assistance needed with 18F27Q10 Sector Writes

Post by Jerry Messina » Sun Jul 11, 2021 3:39 pm

Here's an example using the buffer ram to work with pfm sectors.
Supposedly you can use the TABLEPTR to load the ram too, but it just seems to make more sense to me to use it as ram since that's where it's located.
On the Q10 the buffer ram is a 256-byte block reserved for nvm sector operations. You can read it, change its contents, and write it pretty much like normal ram.

I tested this with a 25Q10, but it should work with the others as well. Just change the PFM_ADDR const to match (or set the variable pfmAddr)

Code: Select all

// Q10 PFM sector operations using BufferRam
device = 18F27Q10
clock = 64

include "intosc.bas"
include "usart.bas"

// 18FxxQ family nvm BufferRam definitions (from device file)
const BUF_RAM_ADDR = _nvm_bufferram_start
const BUF_RAM_SIZE = _nvm_bufferram_end - _nvm_bufferram_start + 1
dim buffer_ram(BUF_RAM_SIZE) as byte absolute (BUF_RAM_ADDR)

// NVMCON0 register bits
const
    NVMEN = 7,
    NVMERR = 4
    
// NVMCON1 register bits
const
    SECER = 6,
    SECWR = 5,
    WR = 4,
    SECRD = 1,
    RD = 0
    
// global INTCON IE register bit
const GIE = 7

// pfm sector to test (device-dependant)
const PFM_ADDR = $10000


//---------------------------
// PFM NVM sector functions
//---------------------------
// read pfm sector at 'addr' into buffer_ram. returns state of NVMERR
public function ReadSector(addr as longword) as bit
    dim t_intcon as byte  ' copy of INTCON

    ' get current intr enable and disable interrupts
    t_intcon = INTCON
    INTCON.bits(GIE) = 0

    ' clear BufferRam so we can see results of sector read (optional)
    clear(buffer_ram)
    
    ' read sector contents into BufferRam
    NVMADRU = addr.byte2  
    NVMADRH = addr.byte1  
    NVMADRL = addr.byte0  
    
    ' enable nvm operations
    NVMCON0.bits(NVMEN) = 1
    
    ' SECRD unlock
    NVMCON2 = $BB
    NVMCON2 = $44
    ' sector read
    NVMCON1.bits(SECRD) = 1
    while (NVMCON1.bits(SECRD) = 1)
    end while
    ' buffer_ram should now contain current sector data

    ' return state of NVMERR flag (remains set... user must clear)
    result = NVMCON0.bits(NVMERR)

    ' disable sector operations
    NVMCON0.bits(NVMEN) = 0
    
    ' restore GIE intr bit setting
    if (t_intcon.bits(GIE) = 1) then
      INTCON.bits(GIE) = 1
    end if
end function

// erase pfm sector at 'addr'. returns state of NVMERR
public function EraseSector(addr as longword) as bit
    dim t_intcon as byte  ' copy of INTCON

    ' get current intr enable and disable interrupts
    t_intcon = INTCON
    INTCON.bits(GIE) = 0

    ' set sector addr to erase
    NVMADRU = addr.byte2  
    NVMADRH = addr.byte1  
    NVMADRL = addr.byte0  
    
    ' enable nvm operations
    NVMCON0.bits(NVMEN) = 1
    
    ' SECER unlock
    NVMCON2 = $CC
    NVMCON2 = $33
    ' sector erase
    NVMCON1.bits(SECER) = 1
    while (NVMCON1.bits(SECER) = 1)
    end while

    ' return state of NVMERR flag (remains set... user must clear)
    result = NVMCON0.bits(NVMERR)

    ' disable sector operations
    NVMCON0.bits(NVMEN) = 0
    
    ' restore GIE intr bit setting
    if (t_intcon.bits(GIE) = 1) then
      INTCON.bits(GIE) = 1
    end if
end function

// write buffer_ram to pfm sector at 'addr'. returns state of NVMERR
public function WriteSector(addr as longword) as bit
    dim t_intcon as byte  ' copy of INTCON

    ' get current intr enable and disable interrupts
    t_intcon = INTCON
    INTCON.bits(GIE) = 0

    ' set sector addr to write
    NVMADRU = addr.byte2  
    NVMADRH = addr.byte1  
    NVMADRL = addr.byte0  
    
    ' enable nvm operations
    NVMCON0.bits(NVMEN) = 1
    
    ' SECWR unlock
    NVMCON2 = $DD
    NVMCON2 = $22
    ' sector write
    NVMCON1.bits(SECWR) = 1
    while (NVMCON1.bits(SECWR) = 1)
    end while

    ' return state of NVMERR flag (remains set... user must clear)
    result = NVMCON0.bits(NVMERR)

    ' disable sector operations
    NVMCON0.bits(NVMEN) = 0
    
    ' restore GIE intr bit setting
    if (t_intcon.bits(GIE) = 1) then
      INTCON.bits(GIE) = 1
    end if
end function


//---------------------------
// main program
//---------------------------
dim pfmAddr as longword
dim nvmStat as bit
dim ix as byte

main:
    ' set nvm params
    pfmAddr = PFM_ADDR

    ' read current sector into BufferRam
    nvmStat = ReadSector(pfmAddr)
    ' buffer_ram should now contain current sector data
    if (nvmStat = 1) then
      USART.Write("ReadSector Err",13,10)
      NVMCON0.bits(NVMERR) = 0        ' user must clear error flag
    endif

    ' erase sector
    nvmStat = EraseSector(pfmAddr)
    if (nvmStat = 1) then
      USART.Write("EraseSector Err",13,10)
      NVMCON0.bits(NVMERR) = 0        ' user must clear error flag
    endif
    
    ' verify erase by re-reading sector
    nvmStat = ReadSector(pfmAddr)
    ' BufferRam should now contain sector data (all FF)
    for ix = 0 to bound(buffer_ram)
      if (buffer_ram(ix) <> $FF) then
        USART.Write("EraseSector Verify Err",13,10)
      endif
    next    

    ' fill BufferRam with contents to write (0-255 in this example)
    for ix = 0 to bound(buffer_ram)
      buffer_ram(ix) = ix
    next
    ' write sector
    nvmStat = WriteSector(pfmAddr)
    if (nvmStat = 1) then
      USART.Write("WriteSector Err",13,10)
      NVMCON0.bits(NVMERR) = 0        ' user must clear error flag
    endif
    ' verify write by re-reading sector
    nvmStat = ReadSector(pfmAddr)
    ' BufferRam should now contain sector data (0-255)
    for ix = 0 to bound(buffer_ram)
      if (buffer_ram(ix) <> ix) then
        USART.Write("WriteSector Verify Err",13,10)
      endif
    next    

    while (true)
    end while

    end

bernardj
Posts: 13
Joined: Fri Jul 10, 2015 7:52 pm

Re: Assistance needed with 18F27Q10 Sector Writes

Post by bernardj » Sun Jul 11, 2021 10:16 pm

Hi Jerry

Again, I bestow onto thee tons of gratitude and appreciation! Thanks very much for the example. Your experience and aptitude shows clearly again.

Your interpretation of the datasheet is exceptional. The datasheet is clearly very misleading, if not downright just incorrect, with its inclusion of the use of TBLPTR for the sector writes. If one replaces the TBLPTR code with yours as in your example, the sector writes works perfectly. I also tried populating the sector RAM with FSR and POSTINC, which also works.

Any way, thanks again. Without your guidance, I would not have been able to resolve this!! As expected, I did learn something new ;-).

Best wishes!

Bernard

Jerry Messina
Swordfish Developer
Posts: 1246
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Assistance needed with 18F27Q10 Sector Writes

Post by Jerry Messina » Mon Jul 12, 2021 10:18 am

The datasheet is clearly very misleading, if not downright just incorrect, with its inclusion of the use of TBLPTR for the sector writes.
I agree, it is confusing. I think if you're only writing a byte or two then using the TBLPTR method to load the write latches is probably simpler.

The thing that caught my eye was this statement in the datasheet:
Data can be written directly into PFM one 16-bit word at a time using the NVMADR and NVMCON1 controls or as a full sector from sector RAM,
which is also referred to as the holding registers. These 8-bit registers are located in the RAM bank following the last GPR RAM bank.
The holding registers are directly accessible as any other SFR/GPR register and also may be loaded via sequential writes using the TABLAT and TBLPTR registers.
The datasheet doesn't talk much about the BufferRam and its use. When I first did the device files for the Q10 I couldn't figure out what this section in RAM was for until I saw that paragraph.

Post Reply