; -------------------------------------------------------------------------------- ; @Title: ST STM32L0xx Flash Dialog to program Option Bytes. ; ; @Description: ; Script arguments: ; ; do stm32l0xx-optionbyte [RDP=0|1|NODEBUG] ; [NBOOT1=0|1] ; [BFB2=0|1] [NRST_STDBY=0|1] [NRST_STOP=0|1] [WDG_SW=0|1] ; [BOR_LEV=] ; [WPRMOD=0|1] ; [WRPROT1=] ; [WRPROT2=] ; [RESETDEVICE] ; ; RDP=0|1|NODEBUG programs the read protection option byte ; 0: disables flash read protection ; 1: enables flash read protection ; NODEBUG: no debug ; ; NBOOT1=0|1 selects the boot source with the BOOT0 input pad ; X: When BOOT0 = 0, boot to flash program memory ; 0: When BOOT0 = 1, boot to RAM ; 1: When BOOT0 = 1, boot to system memory ; At RDP level 2 (NODEBUG), the boot source is always flash. ; ; BFB2=0|1 sets the bit to boot from bank 2 (see documentation) ; 0: Boot from Bank 1 (cat 5 device) or user flash memory ; 1: Boot from system memory ; ; NRST_STDBY=0|1 ; 0: Reset generated when entering Standby mode ; 1: No reset generated when entering Standby mode ; ; NRST_STOP=0|1 ; 0: Reset generated when entering Stop mode ; 1: No reset generated when entering Stop mode ; ; WDG_SW=0|1 ; 0: Hardware watchdog ; 1: Software watchdog ; ; BOR_LEV= sets the brown out threshold level ; 1000: BOR LEVEL 1 = reset threshold 1.8 V ; 1001: BOR LEVEL 2 = reset threshold 2.0 V ; 1010: BOR LEVEL 3 = reset threshold 2.5 V ; 1011: BOR LEVEL 4 = reset threshold 2.7 V ; 1100: BOR LEVEL 5 = reset threshold 3.0 V ; 0XXX: BOR OFF = reset threshold 1.45--1.55--1.65 V ; ; WPRMOD=0|1 sets the sector protection mode (either read or write) ; 0: PCROP disabled; WRPROT bits determine write protection ; 1: PCROP enabled; WRPROT bits determine read protection ; ; WRPROT1= sets a 32-bit write protection register (each bit 4 Kb) ; 0: (at any offset) sector unprotected ; 1: (at any offset) sector protected ; ; WRPROT2= sets a 16-bit write protection register (each bit 4 Kb) ; 0: (at any offset) sector unprotected ; 1: (at any offset) sector protected ; ; RESETDEVICE resets the device after option byte programming ; ; Calling the script without argument starts the Option Byte programming ; dialog window. ; ; @Author: BWR ; @Copyright: (C) 1989-2022 Lauterbach GmbH, licensed for use with TRACE32(R) only ; -------------------------------------------------------------------------------- ; $Rev: 10516 $ ; $Id: stm32l0xx-optionbyte.cmm 10516 2022-02-02 11:39:30Z bschroefel $ PRIVATE ¶meters ENTRY %LINE ¶meters ; PRIVATE macros used as script global macros LOCAL &FlashRegBase ; Flash controller base address LOCAL &FlashAddress ; Main flash start address LOCAL &OptionByteBase ; Option byte base address LOCAL &FlashSize ; Flash size &FlashRegBase="D:0x40022000" &OptionByteBase="D:0x1FF80000" &FlashAddress="0x08000000" ; used for calculation only therefore without D: LOCAL &OptByte_RDP &OptByte_USER &OptByte_WRPROT1 &OptByte_WRPROT2 &OptByte_RDP=0xAA &OptByte_USER=0x807000 &OptByte_WRPROT1=0x00000000 &OptByte_WRPROT2=0x0000 ; Look for any opened STM32L0xx dialog windows and close them WHILE DIALOG.EXIST(COMB_BOR_LEV) DIALOG.END ; Checking CPU selection IF !CPUIS(STM32L0*) SYStem.CPU STM32L0* ; Check system mode IF SYStem.MODE()<5. SYStem.Up ; Setup configuration for CPU derivative IF CPUIS("STM32L0???3*") ( &FlashSize=0x2000 ) ELSE IF CPUIS("STM32L0???4*") ( &FlashSize=0x4000 ) ELSE IF CPUIS("STM32L0???6*") ( &FlashSize=0x8000 ) ELSE IF CPUIS("STM32L0???8*") ( &FlashSize=0x10000 ) ELSE IF CPUIS("STM32L0???B*") ( &FlashSize=0x20000 ) ELSE IF CPUIS("STM32L0???Z*") ( &FlashSize=0x30000 ) ELSE ( DIALOG.OK SYStem.CPU()+" is not supported by the script" ENDDO ) ; Parse script arguments IF "¶meters"=="" ( ;PRINT "Call script with parameters: DO stm32l0xx-optionbyte [RDP=0|1|NODEBUG] [NBOOT1=0|1] [BFB2=0|1] [NRST_STDBY=0|1] [NRST_STOP=0|1] [WDG_SW=0|1] [BOR_LEV=] [WPRMOD=0|1] [WRPROT1=] [WRPROT2=] [RESETDEVICE]" GOSUB OptionByteDialog ) ELSE ( PRIVATE ¶m_WRPROT2 ¶m_WRPROT1 ¶m_WPRMOD ¶m_BOR_LEV ¶m_WDG_SW ¶m_NRST_STOP ¶m_NRST_STDBY ¶m_BFB2 ¶m_NBOOT1 ¶m_RDP ¶m_resetDevice ¶m_RDP=STRing.SCANAndExtract(STRing.UPpeR("¶meters"),"RDP=","") ¶m_NBOOT1=STRing.SCANAndExtract(STRing.UPpeR("¶meters"),"NBOOT1=","") ¶m_BFB2=STRing.SCANAndExtract(STRing.UPpeR("¶meters"),"BFB2=","") ¶m_NRST_STDBY=STRing.SCANAndExtract(STRing.UPpeR("¶meters"),"NRST_STDBY=","") ¶m_NRST_STOP=STRing.SCANAndExtract(STRing.UPpeR("¶meters"),"NRST_STOP=","") ¶m_WDG_SW=STRing.SCANAndExtract(STRing.UPpeR("¶meters"),"WDG_SW=","") ¶m_BOR_LEV=STRing.SCANAndExtract(STRing.UPpeR("¶meters"),"BOR_LEV=","") ¶m_WPRMOD=STRing.SCANAndExtract(STRing.UPpeR("¶meters"),"WPRMOD=","") ¶m_WRPROT1=STRing.SCANAndExtract(STRing.UPpeR("¶meters"),"WRPROT1=","") ¶m_WRPROT2=STRing.SCANAndExtract(STRing.UPpeR("¶meters"),"WRPROT2=","") ¶m_resetDevice=(STRing.SCAN(STRing.UPpeR("¶meters"),"RESETDEVICE",0)!=-1) GOSUB ReadOptionBytes IF "¶m_RDP"!="" ( IF "¶m_RDP"=="0" &OptByte_RDP=0xAA ELSE IF "¶m_RDP"=="1" &OptByte_RDP=0x00 ELSE IF "¶m_RDP"=="NODEBUG" &OptByte_RDP=0xCC ELSE ( PRINT %ERROR "Illegal parameter RDP="+"¶m_RDP" ENDDO ) IF (&OptByte_RDP==0xCC) ( GOSUB QueryNoDebug ENTRY &nodebug IF (!&nodebug) ENDDO ) ) IF "¶m_NBOOT1"!="" ( IF "¶m_NBOOT1"=="1" &OptByte_USER=&OptByte_USER|0x800000 ELSE IF "¶m_NBOOT1"=="0" &OptByte_USER=&OptByte_USER&0x7FFFFF ELSE ( PRINT %ERROR "Illegal parameter NBOOT1="+"¶m_NBOOT1" ENDDO ) ) IF "¶m_BFB2"!="" ( IF "¶m_BFB2"=="1" &OptByte_USER=&OptByte_USER|0x008000 ELSE IF "¶m_BFB2"=="0" &OptByte_USER=&OptByte_USER&0xFF7FFF ELSE ( PRINT %ERROR "Illegal parameter BFB2="+"¶m_BFB2" ENDDO ) ) IF "¶m_NRST_STDBY"!="" ( IF "¶m_NRST_STDBY"=="1" &OptByte_USER=&OptByte_USER|0x004000 ELSE IF "¶m_NRST_STDBY"=="0" &OptByte_USER=&OptByte_USER&0xFFBFFF ELSE ( PRINT %ERROR "Illegal parameter NRST_STDBY="+"¶m_NRST_STDBY" ENDDO ) ) IF "¶m_NRST_STOP"!="" ( IF "¶m_NRST_STOP"=="1" &OptByte_USER=&OptByte_USER|0x002000 ELSE IF "¶m_NRST_STOP"=="0" &OptByte_USER=&OptByte_USER&0xFFDFFF ELSE ( PRINT %ERROR "Illegal parameter NRST_STOP="+"¶m_NRST_STOP" ENDDO ) ) IF "¶m_WDG_SW"!="" ( IF "¶m_WDG_SW"=="1" &OptByte_USER=&OptByte_USER|0x001000 ELSE IF "¶m_WDG_SW"=="0" &OptByte_USER=&OptByte_USER&0xFFEFFF ELSE ( PRINT %ERROR "Illegal parameter WDG_SW="+"¶m_WDG_SW" ENDDO ) ) IF "¶m_BOR_LEV"!="" ( IF (¶m_BOR_LEV>0xC||¶m_BOR_LEV<0x0) ( PRINT %ERROR "Illegal parameter BOR_LEV="+"&PARAM_BOR_LEV" "! BOR_LEV is out of range! " "BOR_LEV must be a 4-bit unsigned numeric value" ENDDO ) ; Set any BOR OFF value to 0x0 IF (¶m_BOR_LEV<0x8) ¶m_BOR_LEV=0x0 &OptByte_USER=(&OptByte_USER&0xFFF0FF)|(¶m_BOR_LEV<<8.) ) IF "¶m_WPRMOD"!="" ( IF "¶m_WPRMOD"=="1" &OptByte_USER=&OptByte_USER|0x000001 ELSE IF "¶m_WPRMOD"=="0" &OptByte_USER=&OptByte_USER&0xFFFFFE ELSE ( PRINT %ERROR "Illegal parameter WPRMOD="+"¶m_WPRMOD" ENDDO ) ) IF "¶m_WRPROT1"!="" ( &OptByte_WRPROT1=¶m_WRPROT1&0xFFFFFFFF ) IF "¶m_WRPROT2"!="" ( &OptByte_WRPROT2=¶m_WRPROT2&0x0000FFFF ) ; Program option bytes into flash GOSUB ProgramOptionBytes ; Reset device to activate programmed option bytes IF ¶m_resetDevice GOSUB ResetDevice ) ENDDO ; -------------------------------------------------------------------------------- ; NVM bit programming dialog window OptionByteDialog: ( ; Creating the main dialog. (& after DIALOG command must be in first column! WinPOS 50. 5. 68. 18. DIALOG.view (& HEADER SYStem.CPU()+" option bytes" POS 1. 0. 66. 3. BOX "Read protection level" POS 3. 1. 20. 1. CHSB_RDP.OFF: CHOOSEBOX "no read protection" "" POS 24. 1. 20. 1. CHSB_RDP.ON: CHOOSEBOX "read protection" "" POS 45. 1. 20. 1. CHSB_RDP.NODBG: CHOOSEBOX "no debug" "GOSUB SelectNoDebug" POS 1. 3. 66. 13. BOX "User option bytes" POS 3. 4. 29. 1. CHSB_WPRMOD.W: CHOOSEBOX "WRPROT = sector write protection" "" CHSB_WPRMOD.R: CHOOSEBOX "WRPROT = sector read protection" "" POS 33. 4. 8. 1. TEXT "WRPROT1:" TEXT "WRPROT2:" POS 41. 4. 15. 1. EDIT_WRPROT1: EDIT "0x00000000" "" POS 41. 5. 10. 1. EDIT_WRPROT2: EDIT "0x0000" "" POS 3. 7. 20. 1. TEXT "Brownout Threshold Level:" POS 24. 7. 20. 1. COMB_BOR_LEV: COMBOBOX "Level 1: around 1.8 V,Level 2: around 2.0 V,Level 3: around 2.5 V,Level 4: around 2.7 V,Level 5: around 3.0 V,Off: below 1.65 V" "" POS 3. 9. 20. 1. CHSB_WDG.HW: CHOOSEBOX "Hardware watchdog" "" CHSB_WDG.SW: CHOOSEBOX "Software watchdog" "" POS 3. 12. 29. 1. CHK_NRST_STOP: CHECKBOX "Generate reset entering Stop mode" "" CHK_NRST_STDBY: CHECKBOX "Generate reset entering Standby mode" "" POS 33. 10. 31. 1. CHK_BFB2: CHECKBOX "Boot from Bank 2 (details in datasheet)" "" POS 33. 12. 31. 1. CHSB_NBOOT1.S: CHOOSEBOX "Boot in system memory when BOOT0=0" "" CHSB_NBOOT1.R: CHOOSEBOX "Boot in embedded SRAM when BOOT0=0" "" TEXT " (Boot in flash when BOOT0=1)" POS 1. 16. 13. 1. BUTTON "Program flash" "GOSUB ProgramFlashSettings" POS 21. 16. 13. 1. BUTTON "Reset device" "GOSUB ActivateFlashSettings" POS 41. 16. 10. 1. BUTTON "Refresh" "GOSUB UpdateWindowFromFlashContents" POS 58. 16. 9. 1. BUTTON "Exit" "CONTinue" CLOSE "CONTinue" ) ; Select/deselect each UI element GOSUB UpdateWindowFromFlashContents STOP DIALOG.END ENDDO ) SelectNoDebug: ( PRIVATE &result IF &OptByte_RDP!=0xCC ( GOSUB QueryNoDebug ENTRY &result IF !&result ( IF &OptByte_RDP==0xAA DIALOG.Set CHSB_RDP.OFF "ON" ELSE IF &OptByte_RDP==0xCC DIALOG.Set CHSB_RDP.NODBG "ON" ELSE DIALOG.Set CHSB_RDP.ON "ON" ) ) RETURN ) ; -------------------------------------------------------------------------------- ; Read out option bytes and set dialog entries UpdateWindowFromFlashContents: ( GOSUB ReadOptionBytes ; Get read protection IF &OptByte_RDP==0xAA DIALOG.Set CHSB_RDP.OFF "ON" ELSE IF &OptByte_RDP==0xCC DIALOG.Set CHSB_RDP.NODBG "ON" ELSE DIALOG.Set CHSB_RDP.ON "ON" ; Get user option byte settings IF (&OptByte_USER&0x1)==0x0 DIALOG.Set CHSB_WPRMOD.W "ON" ELSE DIALOG.Set CHSB_WPRMOD.R "ON" DIALOG.Set EDIT_WRPROT1 "&OptByte_WRPROT1" DIALOG.Set EDIT_WRPROT2 "&OptByte_WRPROT2" IF ((&OptByte_USER>>8.)&0xF)==0x8 DIALOG.Set COMB_BOR_LEV "Level 1: around 1.8 V" ELSE IF ((&OptByte_USER>>8.)&0xF)==0x9 DIALOG.Set COMB_BOR_LEV "Level 2: around 2.0 V" ELSE IF ((&OptByte_USER>>8.)&0xF)==0xA DIALOG.Set COMB_BOR_LEV "Level 3: around 2.5 V" ELSE IF ((&OptByte_USER>>8.)&0xF)==0xB DIALOG.Set COMB_BOR_LEV "Level 4: around 2.7 V" ELSE IF ((&OptByte_USER>>8.)&0xF)==0xC DIALOG.Set COMB_BOR_LEV "Level 5: around 3.0 V" ELSE DIALOG.Set COMB_BOR_LEV "Off: below 1.65 V" IF ((&OptByte_USER>>12.)&0x1)==0x0 DIALOG.Set CHSB_WDG.HW "ON" ELSE DIALOG.Set CHSB_WDG.SW "ON" IF ((&OptByte_USER>>13.)&0x1)==0x0 DIALOG.Set CHK_NRST_STOP "ON" ELSE DIALOG.Set CHK_NRST_STOP "OFF" IF ((&OptByte_USER>>14.)&0x1)==0x0 DIALOG.Set CHK_NRST_STDBY "ON" ELSE DIALOG.Set CHK_NRST_STDBY "OFF" IF ((&OptByte_USER>>15.)&0x1)==0x1 DIALOG.Set CHK_BFB2 "ON" ELSE DIALOG.Set CHK_BFB2 "OFF" IF ((&OptByte_USER>>23.)&0x1)==0x1 DIALOG.Set CHSB_NBOOT1.S "ON" ELSE DIALOG.Set CHSB_NBOOT1.R "ON" RETURN ) ; -------------------------------------------------------------------------------- ; Check user data value QueryNoDebug: PRIVATE &result DIALOG.YESNO "Are you really sure you want to disable DEBUG mode?" "Read protection level 2 cannot be removed at all:" "I T I S A N I R R E V E R S I B L E O P E R A T I O N !" ENTRY &result RETURN &result ; -------------------------------------------------------------------------------- ; Programming Option Bytes ProgramFlashSettings: ( PRIVATE &queryResult &value &programFlash &programFlash=FALSE() ; Set read protection option byte IF (DIALOG.BOOLEAN("CHSB_RDP.OFF")&&(&OptByte_RDP!=0xAA)) ( &OptByte_RDP=0xAA &programFlash=TRUE() ) ELSE IF DIALOG.BOOLEAN("CHSB_RDP.NODBG") ( PRIVATE &queryResult GOSUB QueryNoDebug ENTRY &queryResult IF &queryResult ( &OptByte_RDP=0xCC &programFlash=TRUE() ) ELSE ( PRINT %ERROR "Option byte programming aborted" RETURN ) ) ELSE IF (DIALOG.BOOLEAN("CHSB_RDP.ON")&&(&OptByte_RDP!=0x00)) ( &OptByte_RDP=0x00 &programFlash=TRUE() ) ; Set user option bytes PRIVATE ¤tUSER ¤tUSER=&OptByte_USER IF DIALOG.BOOLEAN(CHSB_WPRMOD.W) &OptByte_USER=&OptByte_USER&~0x000001 ELSE &OptByte_USER=&OptByte_USER|0x000001 &OptByte_USER=&OptByte_USER&~0x000F00 ; BOR_LEV mask IF DIALOG.STRing("COMB_BOR_LEV")=="Level 1: around 1.8 V" &value=0x8 ELSE IF DIALOG.STRing("COMB_BOR_LEV")=="Level 2: around 2.0 V" &value=0x9 ELSE IF DIALOG.STRing("COMB_BOR_LEV")=="Level 3: around 2.5 V" &value=0xA ELSE IF DIALOG.STRing("COMB_BOR_LEV")=="Level 4: around 2.7 V" &value=0xB ELSE IF DIALOG.STRing("COMB_BOR_LEV")=="Level 5: around 3.0 V" &value=0xC ELSE ; Off: below 1.65 V &value=0x0 &OptByte_USER=&OptByte_USER|(&value<<8.) IF DIALOG.BOOLEAN(CHSB_WDG.HW) &OptByte_USER=&OptByte_USER&~0x001000 ELSE &OptByte_USER=&OptByte_USER|0x001000 IF DIALOG.BOOLEAN("CHK_NRST_STOP") &OptByte_USER=&OptByte_USER&~0x002000 ELSE &OptByte_USER=&OptByte_USER|0x002000 IF DIALOG.BOOLEAN("CHK_NRST_STDBY") &OptByte_USER=&OptByte_USER&~0x004000 ELSE &OptByte_USER=&OptByte_USER|0x004000 IF DIALOG.BOOLEAN("CHK_BFB2") &OptByte_USER=&OptByte_USER|0x008000 ELSE &OptByte_USER=&OptByte_USER&~0x008000 IF DIALOG.BOOLEAN(CHSB_NBOOT1.S) &OptByte_USER=&OptByte_USER|0x800000 ELSE &OptByte_USER=&OptByte_USER&~0x800000 IF (&OptByte_USER!=¤tUSER) &programFlash=TRUE() IF DIALOG.STRing("EDIT_WRPROT1")!="" &value=DIALOG.STRing(EDIT_WRPROT1) ELSE &value=0x00000000 IF (&OptByte_WRPROT1!=&value) ( &OptByte_WRPROT1=&value &programFlash=TRUE() ) IF DIALOG.STRing("EDIT_WRPROT2")!="" &value=DIALOG.STRing(EDIT_WRPROT2) ELSE &value=0x0000 IF (&OptByte_WRPROT2!=&value) ( &OptByte_WRPROT2=&value &programFlash=TRUE() ) IF &programFlash==TRUE() GOSUB ProgramOptionBytes RETURN ) ; -------------------------------------------------------------------------------- ; Activate programmed flash settings by resetting device ActivateFlashSettings: ( GOSUB ResetDevice GOSUB UpdateWindowFromFlashContents RETURN ) ; -------------------------------------------------------------------------------- ; Reset device ResetDevice: ( PRIVATE &pecr &pecr=Data.Long(&FlashRegBase+0x04) ; Unlock the Flash Control register IF ((&pecr&0x1)==0x1) ( Data.Set &FlashRegBase+0x0C %Long 0x89ABCDEF // FLASH->KEYR = FLASH_KEY1; Data.Set &FlashRegBase+0x0C %Long 0x02030405 // FLASH->KEYR = FLASH_KEY2; ) ; Unlock the option bytes area IF ((&pecr&0x4)==0x4) ( Data.Set &FlashRegBase+0x14 %Long 0xFBEAD9C8 // FLASH->OPTKEYR = OPTKEY1; Data.Set &FlashRegBase+0x14 %Long 0x24252627 // FLASH->OPTKEYR = OPTKEY2; ) ; Set OBL_LAUNCH to power down core (necessary to save option bytes to flash registers) ON.ERROR CONTINUE Data.Set &FlashRegBase+0x04 %Long &pecr|(1.<<18.) WAIT 0.1s IF (SYStem.UP()) BREAK ELSE SYStem.UP ON.ERROR inherit RETURN ) ; -------------------------------------------------------------------------------- ; Read option byte values out of flash ReadOptionBytes: ( PRIVATE &TarOptByte_0 &TarOptByte_1 &TarOptByte_2 &TarOptByte_3 &TarOptByte_4 &TarOptByte_0=Data.Long(&OptionByteBase+0x00) &TarOptByte_1=Data.Long(&OptionByteBase+0x04) &TarOptByte_2=Data.Long(&OptionByteBase+0x08) &TarOptByte_3=Data.Long(&OptionByteBase+0x0C) &TarOptByte_4=Data.Long(&OptionByteBase+0x10) &OptByte_RDP=&TarOptByte_0&0x000000FF &OptByte_USER=(&TarOptByte_1&0xFFFF)<<8.|(&TarOptByte_0&0xFF00)>>8. &OptByte_WRPROT1=(&TarOptByte_3&0xFFFF)<<16.|(&TarOptByte_2&0xFFFF) &OptByte_WRPROT2=&TarOptByte_4&0xFFFF RETURN ) ; -------------------------------------------------------------------------------- ; Program option bytes ProgramOptionBytes: ( PRIVATE &pecr &value &pecr=Data.Long(&FlashRegBase+0x04) ; Unlock the Flash Control register IF ((&pecr&0x1)==0x1) ( Data.Set &FlashRegBase+0x0C %Long 0x89ABCDEF // FLASH->KEYR = FLASH_KEY1; Data.Set &FlashRegBase+0x0C %Long 0x02030405 // FLASH->KEYR = FLASH_KEY2; ) ; Unlock the option bytes area IF ((&pecr&0x4)==0x4) ( Data.Set &FlashRegBase+0x14 %Long 0xFBEAD9C8 // FLASH->OPTKEYR = OPTKEY1; Data.Set &FlashRegBase+0x14 %Long 0x24252627 // FLASH->OPTKEYR = OPTKEY2; ) ; Clear all pending flags Data.Set &FlashRegBase+0x18 %Long 0x00032F0E // FLASH->SR = FWWERR | NOTZEROERR | RDERR | OPTVERR | SIZERR | PGAERR | WRPERR | EOP ; Program option bytes by writing them directly on the address of option byte field + offset &value=&OptByte_RDP|((&OptByte_USER&0xFF)<<8.) Data.Set &OptionByteBase+0x00 %Long &value|(~&value<<16.) WAIT (Data.Long(&FlashRegBase+0x18)&0x00000001)!=0x00000001 &value=(&OptByte_USER&0xFFFF00)>>8. Data.Set &OptionByteBase+0x04 %Long &value|(~&value<<16.) WAIT (Data.Long(&FlashRegBase+0x18)&0x00000001)!=0x00000001 &value=&OptByte_WRPROT1&0xFFFF Data.Set &OptionByteBase+0x08 %Long &value|(~&value<<16.) WAIT (Data.Long(&FlashRegBase+0x18)&0x00000001)!=0x00000001 &value=(&OptByte_WRPROT1>>16.)&0xFFFF Data.Set &OptionByteBase+0x0C %Long &value|(~&value<<16.) WAIT (Data.Long(&FlashRegBase+0x18)&0x00000001)!=0x00000001 &value=&OptByte_WRPROT2&0xFFFF Data.Set &OptionByteBase+0x10 %Long &value|(~&value<<16.) WAIT (Data.Long(&FlashRegBase+0x18)&0x00000001)!=0x00000001 ; Restore FLASH_PECR register Data.Set &FlashRegBase+0x04 %Long &pecr RETURN )