Yes, I found that information, but it doesn't say if code protection extends to SAF or not.
Anyway, I specified _CP_ON, and it turns out that code protection indeed extends to the contents of the SAF area. There appears to be no way to protect the program code, yet still read some persistent data -- something that is possible in many other PIC devices. So I can't log errors in flash, at least not in a way that I can read back using a programmer device.
Also, I still have some questions with regard to FSR0 and the location of my variables, in particular the assembler's choice for my data with GPR_VAR UDATA.
Here's the relevant code:
Constant and variable definitions:
SAFADD_L equ 0x80 ; High-endurance flash memory (SAF) starts at 0x3F80
SAFADD_H equ 0x3F
; The following ten bytes must be saved at power-down
GPR_VAR UDATA
CUR_STG res 1 ; Current position; closed = 0, open = 1 (1 motor) or 3 (2 motors)
XENCL res 1 ; Current X encoder count low byte, from open position
XENCH res 1 ; Current X encoder count high byte, from open position
XMAXL res 1 ; Maximum calibrated X encoder count low byte
XMAXH res 1 ; Maximum calibrated X encoder count high byte
XDIV8 res 1 ; Single-byte X encoder count
XMAX8 res 1 ; Single-byte maximum calibrated X encoder count
XREM8 res 1 ; Single-byte remaining X encoder counts
COMMAND res 1 ; Broadcast command byte
#define CMD_OPEN COMMAND,0 ; Open command
#define CMD_CLS COMMAND,1 ; Close command
FLAGS res 1
#define FL_RCREC FLAGS,0 ; Signals RC receive in progress
#define FL_RCVAL FLAGS,1 ; Signals valid RC command
#define FL_ENCFB FLAGS,2 ; Signals encoder feedback speed control
#define FL_STEND FLAGS,3 ; Signals end position reached
#define FL_CALD FLAGS,4 ; Signals calibrated stage
#define FL_XONLY FLAGS,5 ; Signals system without Z direction
Erase + write procedure:
; Power-down sequence
powerdn
bcf LED_GN ; Switch off all current consumers
bcf PWM_EN
bsf LED_RED
movlw 0x0A
movwf COUNTER
; Erase flash user data
erasef
bcf INTCON, GIE ; Stop all interrupts
movlw low CUR_STG ; Load first data address in FSR0 (CUR_STG) <- THIS DOESN'T WORK?
movwf FSR0L
movlw high CUR_STG
movwf FSR0H
banksel NVMADRL
movlw SAFADD_L ; Load first SAF address (=0x3F80)
movwf NVMADRL
movlw SAFADD_H
movwf NVMADRH
bcf NVMCON1,NVMREGS
bsf NVMCON1,FREE
bsf NVMCON1,WREN
call unlockf
bcf NVMCON1,WREN
; Flash write routine
fwrite
movlw SAFADD_L ; Load first SAF address (=0x3F80)
movwf NVMADRL
movlw SAFADD_H
movwf NVMADRH
bcf NVMCON1,NVMREGS ; Set Program Flash Memory as write location
bsf NVMCON1,WREN ; Enable writes
bsf NVMCON1,LWLO ; Load only write latches
writelp
moviw FSR0++
movwf NVMDATL ; Load data byte into NVM
decf COUNTER
btfsc STATUS,Z ; 10 bytes saved in latch?
goto writedo ; Yes: write latches into memory
call unlockf ; No: continue loading latch
incf NVMADRL,F ; Increment address
goto writelp
writedo
bcf NVMCON1,LWLO ; Latch writes complete, now write memory
call unlockf ; Perform required unlock sequence
bcf NVMCON1,WREN ; Disable writes
sleep ; No further action until POR
; NVM Unlock sequence
unlockf
movlw 0x55
movwf NVMCON2
movlw 0xAA
movwf NVMCON2
bsf NVMCON1,WR ; Set WR bit to begin write/erase
return
And a simple check of the first variable address at the end:
ORG 0x3F80
retlw low CUR_STG
retlw high CUR_STG
END
I noticed the following:
- Immediately after programming, the first SAF addresses (0x3F80/1) shows that my first variable is apparently located at address 0x06A0, so in Bank 13, not in Bank 0 as I would expect. Is this normal?
- The erase and write procedures work OK, but my registers aren't saved. Instead, only 0x00 values are written to SAF - so it appears that FSR0 doesn't point to the start of my data, but to some unused part of RAM - which would be consistent with the previous point.
- However, if I force 0x0020 as the start of user data (GPR_VAR UDATA 0x0020), things work as expected: at 0x3F80 I see 0x0020 as the address for CUR_STG, and after running the code, I see the ten register values I expect.
So do I need to explicitly specify the start address of my user data if I want to refer to it via FSR0, or have I made some other mistake? I think that it is important that I fully understand what is going on.
Thanks once again for any explanation.