;
;    Mecrisp - A native code Forth implementation for MSP430 microcontrollers
;    Copyright (C) 2011  Matthias Koch
;
;    This program is free software: you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation, either version 3 of the License, or
;    (at your option) any later version.
;
;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.
;
;    You should have received a copy of the GNU General Public License
;    along with this program.  If not, see <http://www.gnu.org/licenses/>.
;

; Routinen zum Schreiben und Löschen von Flash-Speicher

        ; Flash Memory Controller
        ; Flash Timing Generator frequency must be 257-476 kHz.
        ; 8 MHZ/17 = 470.59 kHz.   tFTG=2.125 msec.
        ; At 470 kHz, byte/word program time is 35*tFTG = 75 usec.
        ; Cumulative program time to any 64-byte block (between erasures)
        ; must not exceed 4 msec, thus 53 writes at 250 kHz.  Therefore,
        ; do not use exclusively byte writes in a 64-byte block.
        ; Also, "a flash word (low + high byte) must not
        ; be written more than twice between erasures."
        ; Program/Erase endurance is 10,000 cycles minimum.

        ; In meinem Fall 400kHz --> 2,5 us tFTG --> 35*tFTG = 87,5 us.
        ; Darf 10ms lang schreiben... --> Habe 10ms/87.5us = 114 Schreibzugriffe pro 64-Byte-Block.
        ; Alles paletti. Kann also im reinen Bytemodus brennen. Die Routinen achten darauf,
        ; dass nichts zweimal geschrieben wird.

;------------------------------------------------------------------------------
; Register & Konstanten für die Flashzugriffe !
;------------------------------------------------------------------------------

FCTL1 equ 0128h
FCTL2 equ 012Ah
FCTL3 equ 012Ch

; Schlüssel für alle Flashregister:
FWKEY equ 0A500h

;------------------------------------------------------------------------------
; Konstanten für FCTL1:
         ; 76543210
BLKWRT equ 10000000b ; Block Write Mode
WRT    equ  1000000b ; Write
                     ; Reserviert
EEIEX  equ    10000b ; Emergency Exit
EEI    equ     1000b ; Enable Erase Interrupts
MERAS  equ      100b ; Mass erase
ERASE  equ       10b ; Erase
                     ; Reserviert

;------------------------------------------------------------------------------
; Konstanten für FCTL2:
         ; 76543210
FSSEL_0 equ 00000000b ; ACLK
FSSEL_1 equ 01000000b ; MCLK
FSSEL_2 equ 10000000b ; SMCLK
FSSEL_3 equ 11000000b ; SMCLK

FSSEL0 equ FSSEL_1
FSSEL1 equ FSSEL_2

; In Bit 0-5 gehört der Taktteilerwert. Geteilt wird durch (Wert + 1).
FN0    equ          1b ; Teiler 2^0 = 1
FN1    equ         10b ; Teiler 2^1 = 2
FN2    equ        100b ; Teiler 2^2 = 4
FN3    equ       1000b ; Teiler 2^3 = 8
FN4    equ      10000b ; Teiler 2^4 = 16
FN5    equ     100000b ; Teiler 2^5 = 32

;------------------------------------------------------------------------------
; Konstanten für FCTL3:
         ; 76543210
FAIL  equ  10000000b ; Operation Failure
LOCKA equ   1000000b ; SegmentA and Info Lock. Writing 1 toggles state.
EMEX  equ    100000b ; Emergency Exit
LOCK  equ     10000b ; Lock. This bit unlocks flash memory for writing or erasing.
WAIT  equ      1000b ; Wait. Indicates Flash memory is being written to.
ACCVIFG equ     100b ; Access violation interrupt flag
KEYV  equ        10b ; Flash security key violation
BUSY  equ         1b ; Busy-Flag


;------------------------------------------------------------------------------
  Wortbirne Flag_visible, "eraseflash" ; Vergisst den Inhalt des Dictionarys.
                                        ; Macht einen Kaltstart. Initialisiert alles neu.
;------------------------------------------------------------------------------
  dint                  ; Vorsichtshalber nochmal alles, was stören könnte, deaktivieren.
  clr.b &IE1
  mov #5A80h, &WDTCTL

  ; Brauche nichts zu sichern, da ich danach einen Reset auslöse.
  mov #nFlashDictionaryAnfang, r10

- cmp #0FFFFh, @r10
  je +
    ; Segment Erase: Achtung, Lock und Löschen muss für jedes Segment individuell neu gesetzt werden !
    mov #0A500h, &FCTL3 ; Lock = 0   #FWKEY
    mov #0A502h, &FCTL1 ; Erase = 1  #FWKEY+ERASE

      mov #0FFFFh, @r10 ; Vielleicht auch 0 schreiben ? Egal. Dummy write kann jeden Wert haben, oder ?

    mov #0A500h, &FCTL1 ; Erase = 0  #FWKEY
    mov #0A510h, &FCTL3 ; Lock = 1   #FWKEY+LOCK

+ incd r10
  cmp #nFlashDictionaryEnde, r10
  jne -

  ; Fertig.
  clr &wdtctl ; Reset mit Kaltstart

;------------------------------------------------------------------------------
  Wortbirne Flag_visible, "cflash!"
flashcstore: ; ( n Adresse -- ) Fügt ein Byte mitten in das Dictionary ein.
;------------------------------------------------------------------------------
  push r10
  push r11
  popda r10 ; Adresse
  popda r11 ; Inhalt

  cmp #kernelstartadresse, r10 ; Schützt Forth.
  jhs ++

  cmp.b #0FFh, r11 ; FFFF wird nie gebrannt.
  je ++

  cmp.b #0FFh, @r10 ; Es wird nur gebrannt, wenn an der Stelle im Flash $FF steht.
  jne ++             ; In Kombination werden so die Schreibzyklen nie überschritten.

  ; Brennen.
            push sr
            dint
            mov.w   #FWKEY, &FCTL3           ; Clear LOCK bit
            mov.w   #FWKEY+WRT,&FCTL1        ; Set WRT bit for write operation

  mov.b r11, @r10
            jmp +

;------------------------------------------------------------------------------
  Wortbirne Flag_visible, "flash!"
flashstore: ; ( n Adresse -- ) Fügt ein Word mitten in das Dictionary ein.
;------------------------------------------------------------------------------
  push r10
  push r11
  popda r10 ; Adresse
  popda r11 ; Inhalt

  cmp #kernelstartadresse, r10 ; Schützt Forth.
  jhs ++

  bit #1, r10 ; Wordzugriffe nur an geraden Adressen
  jc ++

  cmp #0FFFFh, r11 ; FFFF wird nie gebrannt.
  je ++

  cmp #0FFFFh, @r10 ; Es wird nur gebrannt, wenn an der Stelle im Flash $FFFF steht.
  jne ++             ; In Kombination werden so die Schreibzyklen nie überschritten.

  ; Brennen.
            push sr
            dint
            mov.w   #FWKEY, &FCTL3           ; Clear LOCK bit
            mov.w   #FWKEY+WRT,&FCTL1        ; Set WRT bit for write operation
  mov r11, @r10
  ; Flash wieder schützen
+           mov.w   #FWKEY, &FCTL1           ; Clear WRT bit
            mov.w   #FWKEY+LOCK,&FCTL3       ; Set LOCK bit
            pop sr

+ pop r11
  pop r10
  ret
