;--------------------------------------------------------------------------------------------------
; Filename         : PGM_ALG.ASM	  
; Last Modified    : 28 Dec 2001.
; Version          : 1.3
; Originator       : Texas Instruments, DSP Digital Control Systems Group.
;--------------------------------------------------------------------------------------------------
; Description:
;
; This file contains the implementation of the core clear algorithm for 
; progamming user data into the LF240x Flash.
;--------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------
; Revision History
; 
; Ver 1.0 (25 May 2001)
; 
;           Comments: Derived from previous source code. Several modifications in place for 
;                     enhanced embeddability.
; Ver 1.1 (10 Aug 2001)
;           Comments: Added code to save/restore the hardware stack. This excludes the possibility
;                     of hardware stack overflow.
;
; Ver 1.2 (16 Oct 2001)
;           Comments: No changes to this file. Changed version number only.
; Ver 1.3 (28 Dec 2001)
;           Comments: 
;                   * Fixed a bug in this file:
;                     Near line 295 (now near line 315) a line of code had a direct address coded
;                     in for data space #0064h. This line is not in keeping with re-locatable 
;                     variables, and the functionality of this line was replaced with relocatable
;                     code when deriving v1.0.
;                     This line of code would corrupt the contents of memory location #0064h.
;                     Hence this line of code was removed.
;                     This line used to read as follows:
;;;;                  BLDD flashAlgoVars.DATA_PTR,#0064h ;Get the next data value to be programmed.
;                      
;                   * This program algorithm will work for DEV_TYPE LF2407 and LF2401A, see 
;                       ..\include\VAR.H on how to choose and set DEV_TYPE.

;                   * Modified the macros ACCESS_REGS and ACCESS_ARRAY to not use a hard coded data
;                     mem address. This is to bring these macros in line with the rest of the algo
;                     which uses allocated addresses. These macros still use a hard coded address
;                     for the i/o space address 0xff0f (0ff0fh) since that is a special address,
;                     which will always address the i/o latch which controls the flash memory
;                     mode.
;--------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------

;--------------------------------------------------------------------------------------------------
;
;--------------------------------------------------------------------------------------------------
; Symbols exported from this file
;--------------------------------------------------------------------------------------------------
        .globl  PROGRAM_FLASH

;--------------------------------------------------------------------------------------------------
; Import register definitions and symbol declarations.
;--------------------------------------------------------------------------------------------------
        .include ..\include\var.h

        .include ..\include\rundefs.h
;--------------------------------------------------------------------------------------------------
; Program sections generated by this file:
; All code in this algorithm implementation is assembled into the named section 'ALG_Text". 
; Any relocation sensitive code is placed in the named section 'PSPL_text'.
;--------------------------------------------------------------------------------------------------
            .sect     "PGM_text"

;--------------------------------------------------------------------------------------------------
; Define macros to set the Flash Mode.
; ACCESS_REGS gives access to the Flash Registers in the Pgm Mem Space and ACCESS_ARRAY  gives 
; access to the Flash ARRAY in Pgm Mem Space.
;
;--------------------------------------------------------------------------------------------------
ACCESS_REGS        .macro    
                OUT    flashAlgoVars.PAD,0ff0fh        
                .endm

ACCESS_ARRAY    .macro    
                IN     flashAlgoVars.PAD,0ff0fh
                .endm

;--------------------------------------------------------------------------------------------------
; Define Short DELAY loop macro. 
;  This will be used to generate a short delay upto 256 cycles.
;--------------------------------------------------------------------------------------------------
SDELAY          .macro  COUNT
                RPT     COUNT
                NOP
                .endm
;--------------------------------------------------------------------------------------------------



;--------------------------------------------------------------------------------------------------
; PROGRAM_FLASH:
;--------------------------------------------------------------------------------------------------
; This function is the implementation of the clear algorithm for the pre-erase preconditioning of 
; the flash array. This function is desginated as a LEVEL_1 subroutine.
;--------------------------------------------------------------------------------------------------
PROGRAM_FLASH:                          ; Call label for assembly-language client applications.
_programFlash:                          ; Call label for C-language client applications.
        .label  ProgramAlgoStartMain
        LDP     #flashAlgoVars.ADDR

STACK_SAVE:
        LAR     AR2,#flashAlgoVars.STACK0       ;Use AR2 to save the hardware stack.
        MAR     *,AR2                           ;Make AR2 the current AR

        RPT     #7                              ;Save all the 8 hardware stack locations
        POPD    *+                              ;into RAM block.
       
;--------------------------------------------------------------------------------------------------
; Enable the flash for programming:
; To accept programming commands the following must happen:
;   1. Place the flash in the register mode.
;   2. Copy the sector key to the SECT register.
;   3. Enable the core by setting Bit 0 of the ENAB Register
;--------------------------------------------------------------------------------------------------
ENABLE: ACCESS_REGS                                              ;Put the flash in register mode.

        LACC    #SECT                                            ;Enable sector.
        BLDD    #flashAlgoVars.SECTOR_CMD,flashAlgoVars.PAD      ;
        TBLW    flashAlgoVars.PAD       

        LACC    #ENAB                                            ;Enable core.
        SPLK    #0001h,flashAlgoVars.PAD                         ;
        TBLW    flashAlgoVars.PAD                                ;

;--------------------------------------------------------------------------------------------------
        SPLK    #0000,flashAlgoVars.FL_CMD                       ;
;--------------------------------------------------------------------------------------------------
;       BLDD    flashAlgoVars.DATA_PTR,#flashAlgoVars.DATA       ;Point to first data 
                                                                 ;to be programmed.
;--------------------------------------------------------------------------------------------------
; PGM:
;--------------------------------------------------------------------------------------------------
; This is the loop at the core of the program algorithm. This performs the following steps to 
; program the flash with the new contents of the flash
;   1.  Reads the flash.
;   2.  Compares the contents read out to those pointed to by the 
;       variable DATA_PTR.
;   3.  If any bits need to be programmed forms a bitmask and calls the PROG
;       routine to apply the program pulse.
;
;--------------------------------------------------------------------------------------------------
PGM:    SPLK    #MX_PCNT,flashAlgoVars.PLS_CNT  ;Initialise the pulse counter.

PGVERON:
        LACC    #WADDR                          ;Load WADDR with the address of 
                                                ;the word to be programmed.
        CALL    SETWADDR                        ;Call the routine to do so.

        LACC    flashAlgoVars.FL_CMD            ;Compose the flash command to read
                                                ;the user space in PROGVER mode.
        XOR     #0004h              
        SACL    flashAlgoVars.PAD    

        LACC    #CTRL                           ;Set up the PROGVER mode.
        TBLW    flashAlgoVars.PAD
        SDELAY  #T_pvsu                         ;Wait for T_pvsu(P)

        LACC    #PMPC                           ;Activate the PROGVER mode.
        SPLK    #0005h,flashAlgoVars.PAD    
        TBLW    flashAlgoVars.PAD
        SDELAY  #T_pva_e                        ;Wait T_pva(E)

        CALL    READWORD                        ;Read the word pointed to by ADDR.
    
PGVEROFF:
        SPLK    #0000h,flashAlgoVars.PAD1       ;Deactivate the PROGVER mode.
        CALL    CLRCMD

        SDELAY  #Tpv_h_P                        ; Wait for Program verify hold time

        SPLK    #0001h,flashAlgoVars.PAD1       ;Clear out PMPC and CTRL
        CALL    CLRCMD                          ;Since PMPC is cleared already, it is unchanged.

        SDELAY  #Tpv_h_C                        ;Hold the normal read mode.

;--------------------------------------------------------------------------------------------------
; Compare:
;--------------------------------------------------------------------------------------------------
; This section of code compares the data read out from the flash with the data
; intended to be programmed in the flash.
;   1.  Get the data read out during the read.
;   2.  Create a bitmask by XOR-ing the data read out during verify with the 
;       intended data pointed to by DATA_PTR.
;   3.  If not equal, builds the mask and calls the PROG routine.
;--------------------------------------------------------------------------------------------------
; Notes on building the mask:
;
; This implementation uses the Bitmask = DATA + !READ_Flash boolean expression for building the
; bitmask. (The + is the boolean operator OR, and ! is inversion.)
; The mask building works as follows:
;
;   1. Read the flash data. 
;   2. Invert this by XOR-ing this with 0xffff.
;   3. OR this with the reference data from the buffer.
;   4. Store the result as the mask into the DATA (subsequently sent to WDATA).
;
;   For example lets say the flash cells contain the 16 bits:  cccc cccc cccc cccc.
;
;   CCCC        cccc cccc cccc cccc  (flash contents)
;   XOR 
;   FFFF        1111 1111 1111 1111  (all ones mask)
;   ----        ---- ---- ---- ----
;   PPPP        pppp pppp pppp pppp  (intermediate result)
;   OR          
;   DDDD        dddd dddd dddd dddd  (reference data from RAM buffer)
;   ----        ---- ---- ---- ----
;   MMMM        mmmm mmmm mmmm mmmm  (mask for writing in WDATA)
;
;   Now the bits m are the result as p = ((C ^ 1) + y). The truth table for this is:
; 
;   d    c   c^1 or !c       m = p = !c + d  Action caused by m     Comments
;--------------------------------------------------------------------------------------------------
;   0    0    1              1               No pulse applied.      Note 1.
;   0    1    0              0               Pulse applied.         Note 2.
;   1    0    1              1               No pulse applied.      Note 3.
;   1    1    0              1               No pulse applied.      Note 4.
;
; 
; Note 1:  This is the case wheen the reference data is a 0 and the flash cell is a 0.
;          Clearly since Cell = Reference data, no pulse is needed, and this is what 
;          happens with WDATA = 1 for this bit.
;
; Note 2:  Case where the reference data is a 0, but the cell is a 1. So a pulse is 
;          applied, to program the cell to a 0.
;
; Note 3.  Case where the reference data is a 1, but the cell is a 0. This is a case
;          where the program routine is asked to 'program' a cell to a 1, while the
;          cell is already at a 0. This is a VIOLATION of the established flow, and 
;          no pulses are really required. So no pulse is applied. This will lead 
;          (rightly so) to an error condition.
; Note 4:  This is the case wheen the reference data is a 1 and the flash cell is a 1.
;          Clearly since Cell = Reference data, no pulse is needed, and this is what 
;          happens with WDATA = 1 for this bit.
;
;--------------------------------------------------------------------------------------------------
GET_DATA:
        LAR     AR2,flashAlgoVars.DATA_PTR              ;Load AR2 with the address of the data.
        BLDD    *,#flashAlgoVars.DATA                   ;Load DATA with the intended contents.
;--------------------------------------------------------------------------------------------------
; Zero Bit Error Check:
; A zero bit error is defined as occurring when a bit in flash is a zero, when its intended value
; as defined by the RAM buffer is a one. 
; The function used to detect this is err= (!READ).(DATA)
; If non zero, a zero bit error is defined as having occurred.
; This needs to be checked once per word. This avoids application of MAX_PCNT pulses to a cell
; that is a zero, but is a one in the RAM buffer.
;--------------------------------------------------------------------------------------------------
ZERO_BIT_ERROR_CHK:
        LACL    flashAlgoVars.READ                      ;Get the value read out during the verify.
        XOR     #0ffffh                                 ;Invert read out from flash.
        AND     flashAlgoVars.DATA                      ;ACC= (!READ).(DATA)

        BCND    ZERO_BIT_ERROR,NEQ                      ;Branch to the corresponding error handler
                                                        ;when this error happens.
;--------------------------------------------------------------------------------------------------
COMPARE:
        LACL    flashAlgoVars.READ                      ;Get the value read out during the verify.
        XOR     flashAlgoVars.DATA                      ;XOR the read out value with the desired
                                                        ;data.

        BCND    NEXTWORD,EQ                             ;If ==0 then this word is done pgm-ing,
                                                        ;if not start building the mask.

        LACC    flashAlgoVars.READ                      ;Get the contents READ from flash.
        XOR     #0ffffh                                 ;ACC = !Flash.
        OR      flashAlgoVars.DATA                      ;ACC = !FLASH + Data. i.e. ACC = MASK.
        SACL    flashAlgoVars.DATA                      ;Store mask in DATA, since PROG expects
                                                        ;the mask there.
        CALL    PROG              
;--------------------------------------------------------------------------------------------------
; PGCNT:
;--------------------------------------------------------------------------------------------------
;   This routine keeps track of the number of pulses applied to the flash memory location. If 
;   number of pulses allowed reaches zero, error handling is commenced. Otherwise the program 
;   pulse counter is decremented and one more cycle is commenced.
;--------------------------------------------------------------------------------------------------
PGCNT:  LACC    flashAlgoVars.PLS_CNT                   ;Get the number of pulses remaining.
        SUB     #1                                      ;Decrement the pulse counter
        SACL    flashAlgoVars.PLS_CNT                   ;and store it back.
        BCND    PULSE_LIMIT_ERROR,EQ                    ;If zero, then branch to error handling.
        B       PGVERON                                 ;else, verify the word programmed.

;--------------------------------------------------------------------------------------------------
; NEXTWORD
;--------------------------------------------------------------------------------------------------
; This section checks if the last address in the block is done.
; If not increments the address and loops back to program the next word.
;--------------------------------------------------------------------------------------------------
NEXTWORD:
        LACC    flashAlgoVars.FL_SECEND                 ;Get the counter.
        SUB     #1                                      ;and advance it.
        SACL    flashAlgoVars.FL_SECEND

        BCND    NW,NEQ                                  ;If the counter is non-zero, more words in
                                                        ;this block remain to be programmed. So
                                                        ;proceed to program the next word.

        B       END1                                    ;Other wise exit the programming routine.
;--------------------------------------------------------------------------------------------------
NW      LACC    flashAlgoVars.ADDR                      ;Get the address variable and increment
        ADD     #1                                      ;the address counter and store it back.
        SACL    flashAlgoVars.ADDR                      ;

        SPLK    #MX_PCNT,flashAlgoVars.PLS_CNT          ;Initialize the Program Pulse Counter.

        LACC    flashAlgoVars.DATA_PTR                  ;Increment the pointer into the buffer
        ADD     #1                                      ;containing the programming data.
        SACL    flashAlgoVars.DATA_PTR
    
        B       PGVERON                                 ;begin prog of next word
;--------------------------------------------------------------------------------------------------
; Program:
;--------------------------------------------------------------------------------------------------
;       This subroutine applies a single program pulse to the flash word at 
;       the address ADDR.
;       DATA contains the bitmask for the pulses. 
;--------------------------------------------------------------------------------------------------
PROG    LACC    #WDATA                                  ;Load data to be prog
        TBLW    flashAlgoVars.DATA

        LACC    flashAlgoVars.FL_CMD
        XOR     #0003h                                  ;PROG CMND
        SACL    flashAlgoVars.PAD

        LACC    #CTRL                                   ;Init prog mode
        TBLW    flashAlgoVars.PAD
        SDELAY  #T_psu_p                                ;Wait T_psu(P)

        LACC    #PMPC                                   ;Turn on prog voltages
        SPLK    #0005h,flashAlgoVars.PAD
        TBLW    flashAlgoVars.PAD

PPW     SDELAY  #T_prog_e1                              ;PROG PULSE WIDTH PART 1
        SDELAY  #T_prog_e2                              ;PROG PULSE WIDTH PART 2

        SPLK    #0000h,flashAlgoVars.PAD                ;TURN OFF PROG VOLTAGES
        TBLW    flashAlgoVars.PAD

        SDELAY  #Tph_P                                  ;Wait T_ph(P) (HOLD TIME).

        SPLK    #0001h,flashAlgoVars.PAD1               ;CLEAR PMPC AND CNTL
        CALL    CLRCMD


        SDELAY  #Tph_A                                  ;Wait in normal read mode.


        RET
;--------------------------------------------------------------------------------------------------
; Handles the error code generation for the case when bit(s) cannot be programmed even after
; the application of the maximum allowed pulses.
;--------------------------------------------------------------------------------------------------
PULSE_LIMIT_ERROR:
        SPLK    #0005h,flashAlgoVars.PAD1
        CALL    CLRCMD

        SPLK    #3,flashAlgoVars.ALGO_STATUS    
        ACCESS_ARRAY
        MAR     *,AR1
        B       STACK_RESTORE
;--------------------------------------------------------------------------------------------------
; Handles the error code generation for the case when a zero bit error has occurred.
; A zero bit error is defined as occurring when a bit in flash is a zero, when its intended value
; as defined by the RAM buffer is a one. 
;--------------------------------------------------------------------------------------------------
ZERO_BIT_ERROR:
        SPLK    #0005h,flashAlgoVars.PAD1
        CALL    CLRCMD

        SPLK    #4,flashAlgoVars.ALGO_STATUS    
        ACCESS_ARRAY
        MAR     *,AR1
        B       STACK_RESTORE

;--------------------------------------------------------------------------------------------------
; READWORD
;--------------------------------------------------------------------------------------------------
; This routine performs the following operations
;    1.    Reads the word at the location pointed to by ADDR.
;    2.  Stores the word in the variable READ.
;    3.  Returns to the caller in Register Mode.
;--------------------------------------------------------------------------------------------------
READWORD
        ACCESS_ARRAY

        LACC    flashAlgoVars.ADDR                        ;Read word flash 
        TBLR    flashAlgoVars.READ                        ;store word in READ (data space)
        ACCESS_REGS
        RET

;--------------------------------------------------------------------------------------------------
; END1:                                       
;--------------------------------------------------------------------------------------------------
;     This routine performs the following operations
;     1.  Clears the control registers PMPC,CNTL,WADDR,WDATA,ENABLE AND SECTOR)
;    2.  Returns to the caller in ARRAY Mode.
;--------------------------------------------------------------------------------------------------
END1:   LACC    flashAlgoVars.ADDR                      ;Get the address variable and increment
        ADD     #1                                      ;the address counter and store it back.
        SACL    flashAlgoVars.ADDR                      ;

        SPLK    #0006h,flashAlgoVars.PAD1               ;Clear 6 regs.
        CALL    CLRCMD
        
        SPLK    #0,flashAlgoVars.ALGO_STATUS            ;Zero error code, since programming
                                                        ;completed successfully.
        ACCESS_ARRAY                                    
        B       STACK_RESTORE


;--------------------------------------------------------------------------------------------------
; Restore the hardware stack and exit to caller.
;--------------------------------------------------------------------------------------------------
STACK_RESTORE:
        LAR     AR2,#flashAlgoVars.STACK7       ;Point AR2 to the last of the stored stack contents
        MAR     *,AR2                           ;Make AR2 the current AR
        
        RPT     #7                              ;Restore ALL stack locations.
        PSHD    *-
        MAR     *,AR1                           ;Make AR1 the current AR
        RET                                     ;Return to caller








;--------------------------------------------------------------------------------------------------
; CLRCMD
;--------------------------------------------------------------------------------------------------
;     This routine performs the following operations
;   1.     Places the flash in normal read mode by writing 0000 to two control 
;        registers PMPC,CNTL=>0000)
;    2.  Returns to the caller in Register Mode.
;--------------------------------------------------------------------------------------------------
CLRCMD: ACCESS_REGS
        SPLK     #0,flashAlgoVars.PAD
        LACC     #0
        RPT     flashAlgoVars.PAD1
        TBLW     flashAlgoVars.PAD
        RET
        .label  ProgramAlgoEndMain

        .sect   "PSPL_text"
SETWADDR:
        .label  ProgramAlgoStartSpl
        TBLW    flashAlgoVars.ADDR        
        RET
        .label  ProgramAlgoEndSpl

