avr/module/flash: unified flash code for 1p/4p devices.

AtTiny 841 erases 4 pages at once, others only erase one.
Fixed a bug: only send ONE flash response to FLASH_START!!!
This commit is contained in:
Martin Preuss
2025-05-04 03:32:53 +02:00
parent b632a10fff
commit e25b0ad69d
6 changed files with 385 additions and 619 deletions

View File

@@ -12,7 +12,7 @@
.equ FLASH_ERROR_NONE = 0
.equ FLASH_ERROR_MSGERROR = 1
.equ FLASH_RECVBUFFER_MAXLEN = 128
.equ FLASH_RECVBUFFER_MAXLEN = 32
.equ FLASH_CMD_FLASH_RSP = 74

View File

@@ -16,7 +16,7 @@
.dseg
flashPageNum: .byte 2
flashPageStart: .byte 2
flashPageBuffer: .byte FLASH_PAGESIZE
@@ -28,253 +28,6 @@
; ---------------------------------------------------------------------------
; @routine Flash_Init
;
; @clobbers R16
Flash_Init:
ldi r16, 0xff
sts flashPageNum, r16
sts flashPageNum+1, r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine Flash_Fini
;
; @clobbers (R0, R1, R16, R18, R19, R20, R21, R24, R25, X)
Flash_Fini:
push r15
in r15, SREG
cli
rcall flash1pEndPage ; (R0, R1, R16, R18, R19, R20, R24, R25, X)
out SREG, r15
pop r15
ret
; @end
; ---------------------------------------------------------------------------
; @routine Flash_WriteData
;
; @param Z destination address in FLASH memory (byte address as for LPM!)
; @param Y source address
; @param r17 number of bytes to write
; @clobbers (R0, R1, R16, R18, R19, R20, R24, R25, X)
Flash_WriteData:
push r15
in r15, SREG
cli
Flash_WriteData_loop:
tst r17
breq Flash_WriteData_done
rcall flashWriteData ; (R0, R1, R16, R18, R19, R20, R24, R25, X)
rjmp Flash_WriteData_loop
Flash_WriteData_done:
out SREG, r15
pop r15
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashWriteData
;
; Interrupts must be disabled!
;
; @return Z next position
; @return R17 remainder of bytes to flash
; @param Z position to flash data to
; @param Y source pointer
; @param R17 number of bytes to flash
; @clobbers R16, R18, R24, R25, X (R0, R1, R19, R20)
flashWriteData:
rcall flashCheckCurrentValid ; (r18, r19)
brcc flashWriteData_beginPage
rcall flashWriteCheckPage ; (r18, r19)
brcs flashWriteData_calcPosAndLength ; still inside current page, jump
rcall flash1pEndPage ; (R0, R1, R16, R18, R19, R20, R24, R25, X)
flashWriteData_beginPage:
rcall flash1pBeginPage ; (r16, r24, r25, X)
flashWriteData_calcPosAndLength:
rcall flashCalcPosAndLength ; (r24, r25)
; X=abs pos in buffer, r18=bytes to read, r17=bytes initially requested
; prepare data for return
sub r17, r18 ; r17 holds remainder
add zl, r18 ; Z points to next data
adc zh, r18 ; only add carry flag
sub zh, r18
flashWriteData_copyLoop:
ld r16, Y+
st X+, r16
dec r18
brne flashWriteData_copyLoop
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashCalcPosAndLength
;
; @return X absolute write position inside buffer
; @return R18 bytes to write
; @param Z destination pos
; @param R17 number of bytes to write
; @clobbers r24, r25
flashCalcPosAndLength:
; calc offset into buffer
mov r24, zl
andi r24, (FLASH_PAGESIZE-1) ; r24=rel pos inside buffer
ldi r25, FLASH_PAGESIZE
sub r25, r24 ; r25=bytes left inside page
mov r18, r17
cp r25, r17 ; bytes to read > bytes left in page?
brcc flashCalcPosAndLength_l1 ; no: jump
mov r18, r25 ; yes: cut r18 to number of bytes left in page
flashCalcPosAndLength_l1:
ldi xl, LOW(flashPageBuffer) ; set X to pos within page buffer
ldi xh, HIGH(flashPageBuffer)
add xl, r24
adc xh, r24
sub xh, r24
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashWriteCheckPage
;
; check whether the given address is inside the current page in buffer
;
; @return CFLAG set if same page, cleared otherwise
; @param Z address to write to
; @clobbers r18, r19
flashWriteCheckPage:
; check low byte of page
mov r18, zl
andi r18, LOW(~(FLASH_PAGESIZE-1))
lds r19, flashPageNum
cp r18, r19
brne flashWriteCheckPage_ClcRet
; check high byte of page
mov r18, zh
andi r18, HIGH(~(FLASH_PAGESIZE-1))
lds r19, flashPageNum+1
cp r18, r19
brne flashWriteCheckPage_ClcRet
sec
ret
flashWriteCheckPage_ClcRet:
clc
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashCheckCurrentValid
;
; check whether the page buffer contains a valid page (e.g. flashPageNum is not 0xffff)
;
; @return CFLAG set if current page is valid, cleared otherwise
; @clobbers r18, r19
flashCheckCurrentValid:
lds r18, flashPageNum
lds r19, flashPageNum+1
and r18, r19
inc r18
breq flashCheckCurrentValid_ClcRet
sec
ret
flashCheckCurrentValid_ClcRet:
clc
ret
; @end
; ---------------------------------------------------------------------------
; @routine flash1pBeginPage
;
; Interrupts must be disabled!
;
; @param Z Address to read from (byte address as for LPM!)
; @clobbers (r16, r24, r25, X)
flash1pBeginPage:
push zl
push zh
andi zl, LOW(~(FLASH_PAGESIZE-1))
andi zh, HIGH(~(FLASH_PAGESIZE-1))
sts flashPageNum, zl
sts flashPageNum+1, zh
rcall flash1pReadPageIntoSram ; r16, r24, r25, X, Z
pop zh
pop zl
ret
; @end
; ---------------------------------------------------------------------------
; @routine flash1pEndPage
;
; Interrupts must be disabled!
;
; @clobbers (R0, R1, R16, R18, R19, R20, R24, R25, X)
flash1pEndPage:
rcall flashCheckCurrentValid ; (r18, r19)
brcs flash1pEndPage_write
ret
flash1pEndPage_write:
push zl
push zh
rcall flash1pWritePage ; (r0, r1, r20, r24, r25, X, Z)
pop zh
pop zl
ret
; @end
; ---------------------------------------------------------------------------
; @routine flash1pReadPageIntoSram
;
; Interrupts must be disabled!
;
; @param Z Address to read from (byte address as for LPM!)
; @clobbers r16, r24, X, Z
flash1pReadPageIntoSram:
ldi xl, LOW(flashPageBuffer)
ldi xh, HIGH(flashPageBuffer)
ldi r24, LOW(FLASH_PAGESIZE)
flash1pReadPageIntoSram_loop:
lpm r16, Z+
st X+, r16
adiw ZH:ZL, 1
dec r24
brne flash1pReadPageIntoSram_loop
ret
; @end
; ---------------------------------------------------------------------------
; flash1pWritePage
;
@@ -283,58 +36,30 @@ flash1pReadPageIntoSram_loop:
; @clobbers r0, r1, r20, r24, r25, X, Z
flash1pWritePage:
lds zl, flashPageNum
lds zh, flashPageNum+1
; erase page
ldi r20, (1<<PGERS) + (1<<SPMEN) ; enable next SPM, erase page (R1/R0 ignored)
rcall flash1pDoSpm ; (R16)
lds zl, flashPageStart
lds zh, flashPageStart+1
; copy from SDRAM into MCUs temporary page buffer
ldi xl, LOW(flashPageBuffer)
ldi xh, HIGH(flashPageBuffer)
ldi r24, LOW(PAGESIZE)
push zl
push zh
flash1pWritePages_loop:
ld r0, X+ ; read source data from buffer (low)
ld r1, X+ ; read source data from buffer (high)
ldi r20, (1<<SPMEN) ; enable next SPM, write R1:R0 into temp page buffer
rcall flash1pDoSpm ; (R16)
adiw zh:zl, 2
dec r24
brne flash1pWritePages_loop
pop zh
pop zl
ld r0, X+ ; read source data from buffer (low)
ld r1, X+ ; read source data from buffer (high)
ldi r20, (1<<SPMEN) ; enable next SPM, write R1:R0 into temp page buffer
rcall flashDoSpm ; (R16)
adiw zh:zl, 2
dec r24
brne flash1pWritePages_loop
subi zl, LOW(PAGESIZE*2) ; point back to begin of page
sbci zh, HIGH(PAGESIZE*2)
; transfer data from temporary page buffer into FLASH memory
ldi r20, (1<<PGWRT) + (1<<SPMEN) ; enable next SPM, write page (R1/R0 ignored)
rcall flash1pDoSpm ; (R16)
rcall flashDoSpm ; (R16)
ret
; @end
; ---------------------------------------------------------------------------
; flash1pDoSpm
;
; wait until possible previous SPM finished and then issue another SPM.
;
; IN:
; - R20: value for register SPMCR
; - R0: low value for SPM
; - R1: high value for SPM
; - Z : address for SPM (byte address!)
; OUT:
; - nothing
; REGS: R16
flash1pDoSpm:
flash1pDoSpm_wait: ; wait for possibly previous SPM to complete
in r16, SPMCSR
sbrc r16, SPMEN
rjmp flash1pDoSpm_wait
; SPM timed sequence
out SPMCSR, r20
spm
ret
.equ flashWritePage = flash1pWritePage

View File

@@ -16,7 +16,7 @@
.dseg
flashPageNum: .byte 2
flashPageStart: .byte 2
flashPageBuffer: .byte FLASH_PAGESIZE
@@ -29,330 +29,47 @@
; ---------------------------------------------------------------------------
; @routine Flash_Init
;
; @clobbers R16
Flash_Init:
ldi r16, 0xff
sts flashPageNum, r16
sts flashPageNum+1, r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine Flash_Fini
;
; @clobbers (R0, R1, R16, R18, R19, R20, R21, R24, R25, X)
Flash_Fini:
push r15
in r15, SREG
cli
rcall flash4pEndPage ; (R0, R1, R16, R18, R19, R20, R24, R25, X)
out SREG, r15
pop r15
ret
; @end
; ---------------------------------------------------------------------------
; @routine Flash_WriteData
;
; @param Z destination address in FLASH memory (byte address as for LPM!)
; @param Y source address
; @param r17 number of bytes to write
; @clobbers (R0, R1, R16, R18, R19, R20, R24, R25, X)
Flash_WriteData:
push r15
in r15, SREG
cli
Flash_WriteData_loop:
tst r17
breq Flash_WriteData_done
rcall flashWriteData ; (R0, R1, R16, R18, R19, R20, R24, R25, X)
rjmp Flash_WriteData_loop
Flash_WriteData_done:
out SREG, r15
pop r15
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashWriteData
;
; Interrupts must be disabled!
;
; @return Z next position
; @return R17 remainder of bytes to flash
; @param Z position to flash data to
; @param Y source pointer
; @param R17 number of bytes to flash
; @clobbers R16, R18, R24, R25, X (R0, R1, R19, R20)
flashWriteData:
rcall flashWriteCheckPage ; (r18, r19)
brcs flashWriteData_calcPosAndLength
rcall flash4pEndPage ; (R0, R1, R16, R18, R19, R20, R24, R25, X)
rcall flash4pBeginPage ; (r16, r24, r25, X, Z)
flashWriteData_calcPosAndLength:
rcall flashCalcPosAndLength ; (r24, r25)
; X=abs pos in buffer, r18=bytes to read, r17=bytes initially requested
; prepare data for return
sub r17, r18 ; r17 holds remainder
add zl, r18 ; Z points to next data
adc zh, r18 ; only add carry flag
sub zh, r18
flashWriteData_copyLoop:
ld r16, Y+
st X+, r16
dec r18
brne flashWriteData_copyLoop
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashCalcPosAndLength
;
; @return X absolute write position inside buffer
; @return R18 bytes to write
; @param Z destination pos
; @param R17 number of bytes to write
; @clobbers r24, r25
flashCalcPosAndLength:
; calc offset into buffer
mov r24, zl
andi r24, (FLASH_PAGESIZE-1) ; r24=rel pos inside buffer
ldi r25, FLASH_PAGESIZE
sub r25, r24 ; r25=bytes left inside page
mov r18, r17
cp r25, r17
brcc flashCalcPosAndLength_l1
mov r18, r25
flashCalcPosAndLength_l1:
ldi xl, LOW(flashPageBuffer)
ldi xh, HIGH(flashPageBuffer)
add xl, r24
adc xh, r24
sub xh, r24
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashWriteCheckPage
;
; check whether the given address is inside the current page in buffer
;
; @return CFLAG set if same page, cleared otherwise
; @clobbers r18, r19
flashWriteCheckPage:
; check low byte of page
mov r18, zl
andi r18, LOW(~(FLASH_PAGESIZE-1))
lds r19, flashPageNum
cp r18, r19
brne flashWriteCheckPage_ClcRet
; check high byte of page
mov r18, zh
andi r18, HIGH(~(FLASH_PAGESIZE-1))
lds r19, flashPageNum+1
cp r18, r19
brne flashWriteCheckPage_ClcRet
sec
ret
flashWriteCheckPage_ClcRet:
clc
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashCheckCurrentValid
;
; check whether the page buffer contains a valid page (e.g. flashPageNum is not 0xffff)
;
; @return CFLAG set if current page is valid, cleared otherwise
; @clobbers r18, r19
flashCheckCurrentValid:
lds r18, flashPageNum
lds r19, flashPageNum+1
and r18, r19
inc r18
breq flashCheckCurrentValid_ClcRet
sec
ret
flashCheckCurrentValid_ClcRet:
clc
ret
; @end
; ---------------------------------------------------------------------------
; @routine flash4pBeginPage
;
; Interrupts must be disabled!
;
; @param Z Address to read from (byte address as for LPM!)
; @clobbers (r16, r24, r25, X)
flash4pBeginPage:
push zl
push zh
andi zl, LOW(~(FLASH_PAGESIZE-1))
andi zh, HIGH(~(FLASH_PAGESIZE-1))
sts flashPageNum, zl
sts flashPageNum+1, zh
rcall flash4pReadPagesIntoSram ; r16, r24, r25, X, Z
pop zh
pop zl
ret
; @end
; ---------------------------------------------------------------------------
; @routine flash4pEndPage
;
; Interrupts must be disabled!
;
; @clobbers (R0, R1, R16, R18, R19, R20, R24, R25, X)
flash4pEndPage:
rcall flashCheckCurrentValid ; (r18, r19)
brcs flash4pEndPage_write
ret
flash4pEndPage_write:
push zl
push zh
rcall flash4pErasePages ; (R16, R20, Z)
rcall flash4pWritePages ; (r0, r1, r20, r24, r25, X, Z)
pop zh
pop zl
ret
; @end
; ---------------------------------------------------------------------------
; @routine flash4pReadPagesIntoSram
;
; Interrupts must be disabled!
;
; @param Z Address to read from (byte address as for LPM!)
; @clobbers r16, r24, X, Z
flash4pReadPagesIntoSram:
ldi xl, LOW(flashPageBuffer)
ldi xh, HIGH(flashPageBuffer)
ldi r24, LOW(FLASH_PAGESIZE)
flash4pReadPagesIntoSram_loop:
lpm r16, Z+
st X+, r16
adiw ZH:ZL, 1
dec r24
brne flash4pReadPagesIntoSram_loop
ret
; @end
; ---------------------------------------------------------------------------
; @routine flash4pErasePages
;
; Interrupts must be disabled!
;
; @clobbers R16, R20, Z
flash4pErasePages:
lds zl, flashPageNum
lds zh, flashPageNum+1
ldi r20, (1<<PGERS) + (1<<SPMEN) ; enable next SPM, erase page (R1/R0 ignored)
rcall flash4pDoSpm ; (R16)
ret
; @end
; ---------------------------------------------------------------------------
; flash4pWritePages
; flash4pWritePage
;
; Interrupts must be disabled!
;
; @clobbers r0, r1, r20, r24, r25, X, Z
flash4pWritePages:
flash4pWritePage:
; copy from SDRAM into MCUs temporary page buffer
lds zl, flashPageNum
lds zh, flashPageNum+1
lds zl, flashPageStart
lds zh, flashPageStart+1
ldi xl, LOW(flashPageBuffer)
ldi xh, HIGH(flashPageBuffer)
ldi r25, 4 ; handle 4 pages at a time
flash4pWritePages_loop1:
flash4pWritePage_loop1:
ldi r24, LOW(PAGESIZE)
flash4pWritePages_loop2:
flash4pWritePage_loop2:
ld r0, X+ ; read source data from buffer (low)
ld r1, X+ ; read source data from buffer (high)
ldi r20, (1<<SPMEN) ; enable next SPM, write R1:R0 into temp page buffer
rcall flash4pDoSpm ; (R16)
rcall flashDoSpm ; (R16)
adiw zh:zl, 2
dec r24
brne flash4pWritePages_loop2
brne flash4pWritePage_loop2
sbiw zh:zl, (PAGESIZE*2) ; point back to begin of page
; transfer data from temporary page buffer into FLASH memory
ldi r20, (1<<PGWRT) + (1<<SPMEN) ; enable next SPM, write page (R1/R0 ignored)
rcall flash4pDoSpm ; (R16)
adiw zh:zl, (PAGESIZE*2) ; point forward to next page
push zl
push zh
subi zl, LOW(PAGESIZE*2) ; point back to begin of page
sbci zh, HIGH(PAGESIZE*2)
; transfer data from temporary page buffer into FLASH memory
ldi r20, (1<<PGWRT) + (1<<SPMEN) ; enable next SPM, write page (R1/R0 ignored)
rcall flashDoSpm ; (R16)
pop zh
pop zl
dec r25
brne flash4pWritePages_loop1
brne flash4pWritePage_loop1
ret
; @end
; ---------------------------------------------------------------------------
; flas4phDoSpm
;
; wait until possible previous SPM finished and then issue another SPM.
;
; IN:
; - R20: value for register SPMCR
; - R0: low value for SPM
; - R1: high value for SPM
; - Z : address for SPM (byte address!)
; OUT:
; - nothing
; REGS: R16
flash4pDoSpm:
flash4pDoSpm_wait: ; wait for possibly previous SPM to complete
in r16, SPMCSR
sbrc r16, SPMEN
rjmp flash4pDoSpm_wait
; SPM timed sequence
out SPMCSR, r20
spm
ret
.equ flashWritePage = flash4pWritePage

View File

@@ -47,11 +47,11 @@ checkFlash:
rcall flashProcessWriteFlashReady ; (R16, R17, R18, R19, R20, Y, Z)
rcall ioRawSendMsg ; (r16, r17, X)
ldi r16, CPRO_CMD_FLASH_START
ldi r16, NETMSG_CMD_FLASH_START
rcall ioWaitForGivenMsg ; (r16, r17, r18, r19, r20, r22, X)
brcc checkFlash_end
cpi r16, CPRO_CMD_FLASH_START
cpi r16, NETMSG_CMD_FLASH_START
clc
brne checkFlash_end ; no FLASH_START msg received
@@ -60,10 +60,6 @@ checkFlash:
ldi xh, HIGH(flashRecvBuffer)
rcall flashProcessHandleFlashStart
brcc checkFlash_end
; prepare and send response
clr r16
rcall flashProcessSendFlashResponse ; (R15, R16, R17, R18, R19, R20, R21, R22, X)
sec
checkFlash_end:
ret
@@ -77,21 +73,21 @@ checkFlash_end:
; handling receiving FLASH_DATA and FLASH_END messages, flash new firmware
;
; @return CFLAG set if okay, cleared otherwise
; @clobbers r16, r20
; @clobbers r16, r20 (R0, R1, R15, R16, R17, R18, R19, R22, R24, Z)
flashProcess:
rcall Flash_Init
flashProcess_loop1:
; wait up to 10s for incoming FLASH_DATA message
ldi r16, CPRO_CMD_FLASH_DATA
ldi r16, NETMSG_CMD_FLASH_DATA
rcall ioWaitForGivenMsg ; (r16, r17, r18, r19, r20, r22, X)
brcc flashProcess_end ; no FLASH_DATA or FLASH_END msg
; either FLASH_DATA or FLASH_END received
ldi xl, LOW(flashRecvBuffer)
ldi xh, HIGH(flashRecvBuffer)
cpi r16, CPRO_CMD_FLASH_DATA ; not FLASH_DATA, flashing ended/aborted
cpi r16, NETMSG_CMD_FLASH_DATA ; not FLASH_DATA, flashing ended/aborted
brne flashProcess_endReceived ; FLASH_END received, done
rcall flashProcessHandleFlashData ; (R0, R1, R15, R16, R17, R18, R19, R20, Z)
rcall flashProcessHandleFlashData ; (R0, R1, R15, R16, R17, R18, R19, R20, R22, R24, Z)
rjmp flashProcess_loop1
flashProcess_endReceived:
rcall flashProcessHandleFlashEnd
@@ -161,27 +157,39 @@ flashProcessCheckFlashStart_notMe:
;
; @return CFLAG set if okay, cleared otherwise
; @param X buffer containing the message
; @clobbers R0, R1, R18, Z (R15, R16, R17, R19, R20)
; @clobbers R0, R1, R18, Z (R15, R16, R17, R19, R20, R22, R24)
flashProcessHandleFlashData:
mov yl, xl
mov yh, xh
adiw yh:yl, FLASH_MSG_OFFS_MSGLEN
ld r17, Y ; length (subtract 6)
cpi r17, 6 ; cmd(1), src(1), addr(4)
brcs flashProcessHandleFlashData_badData
subi r17, 6 ; remaining length
adiw yh:yl, FLASH_PACKET_DATA_OFFS_ADDR-FLASH_MSG_OFFS_MSGLEN
ld zl, Y+ ; address (low)
ld zh, Y+ ; address (high)
adiw yh:yl, 2 ; ignore high bytes, points to first data byte now
ldd r17, Y+FLASH_MSG_OFFS_MSGLEN ; msg length
subi r17, 6 ; remaining length (sub cmd(1), src(1), addr(4))
brcs flashProcessHandleFlashData_badData ; too few payload bytes
breq flashProcessHandleFlashData_badData ; no payload bytes
ldd zl, Y+FLASH_PACKET_DATA_OFFS_ADDR ; only read lower two address bytes
ldd zh, Y+(FLASH_PACKET_DATA_OFFS_ADDR+1)
adiw yh:yl, FLASH_PACKET_DATA_OFFS_ADDR+4
rcall Flash_WriteData ; (R0, R1, R16, R18, R19, R20, R24, R25, X)
clr r16
rjmp flashProcessHandleFlashData_sendResponse
flashProcessHandleFlashData_badData:
ldi r16, FLASH_ERROR_MSGERROR
flashProcessHandleFlashData_sendResponse:
#if 0
push r16
; send flash message back (debugging)
ldi r24, 50
rcall flashWaitForMillisecs ; (R22, R24)
ldi xl, LOW(flashRecvBuffer)
ldi xh, HIGH(flashRecvBuffer)
rcall ioRawSendPacket ; (r16, r17, X)
pop r16
#endif
ldi r24, 50
rcall flashWaitForMillisecs ; (R22, R24)
rcall flashProcessSendFlashResponse ; (R16, R17, R18, R19, R20, X)
sbi LED_PIN, LED_PINNUM ; toggle
sec
ret
; @end
@@ -216,7 +224,7 @@ flashProcessHandleFlashEnd:
; @clobbers X (R16, R17, R18, R19, R20)
flashProcessSendFlashResponse:
; send flash ready message
; send flash response message
ldi xl, LOW(flashSendBuffer)
ldi xh, HIGH(flashSendBuffer)
rcall flashProcessWriteFlashRsp ; (R16, R17, R18, R19, R20, X)
@@ -238,7 +246,7 @@ flashProcessWriteFlashRsp:
st X+, r18 ; dest address (unused)
ldi r18, 3 ; msg code+src address+one payload byte
st X+, r18 ; msg len
ldi r17, CPRO_CMD_FLASH_RSP
ldi r17, NETMSG_CMD_FLASH_RSP
st X+, r17 ; msg code
clr r17
st X+, r17 ; src address (not used)
@@ -262,7 +270,7 @@ flashProcessWriteFlashReady:
st X+, r16 ; dest address (unused)
ldi r16, 20 ; msg code+src address+18 payload bytes
st X+, r16 ; msg len
ldi r16, CPRO_CMD_FLASH_READY
ldi r16, NETMSG_CMD_FLASH_READY
st X+, r16 ; msg code
ldi r16, COM2_MAINTENANCE_ADDR
st X+, r16 ; src address

View File

@@ -0,0 +1,304 @@
; ***************************************************************************
; copyright : (C) 2025 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
; ***************************************************************************
; code
.cseg
; ---------------------------------------------------------------------------
; @routine Flash_Init
;
; @clobbers R16
Flash_Init:
ldi r16, 0xff
sts flashPageStart, r16
sts flashPageStart+1, r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine Flash_Fini
;
; @clobbers (R0, R1, R16, R18, R19, R20, R21, R24, R25, X)
Flash_Fini:
push r15
in r15, SREG
cli
rcall flashEndPage ; (R0, R1, R16, R18, R19, R20, R24, R25, X)
out SREG, r15
pop r15
ret
; @end
; ---------------------------------------------------------------------------
; @routine Flash_WriteData
;
; @param Z destination address in FLASH memory (byte address as for LPM!)
; @param Y source address
; @param r17 number of bytes to write
; @clobbers (R0, R1, R16, R18, R19, R20, R24, R25, X)
Flash_WriteData:
push r15
in r15, SREG
cli
Flash_WriteData_loop:
rcall flashWriteData ; (R0, R1, R16, R18, R19, R20, R24, R25, X)
tst r17
brne Flash_WriteData_loop
out SREG, r15
pop r15
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashWriteData
;
; Interrupts must be disabled!
;
; @return Z next dest position
; @return Y next source position
; @return R17 remainder of bytes to flash
; @param Z position to flash data to
; @param Y source pointer
; @param R17 number of bytes to flash
; @clobbers R16, R18, R24, R25, X (R0, R1, R19, R20)
flashWriteData:
rcall flashCheckCurrentValid ; (r18, r19)
brcc flashWriteData_beginPage ; jump if this is the first call (no page in work)
rcall flashWriteCheckPage ; (r18, r19)
brcs flashWriteData_calcPosAndLength ; still inside current page, jump
rcall flashEndPage ; (R0, R1, R16, R18, R19, R20, R24, R25, X)
flashWriteData_beginPage:
rcall flashBeginPage ; (r16, r24, r25, X)
flashWriteData_calcPosAndLength:
rcall flashCalcPosAndLength ; r18=bytes_to_write, x=pos_in_buffer (r24, r25)
; X=abs pos in buffer, r18=bytes to read, r17=bytes initially requested
; prepare data for return
sub r17, r18 ; r17 holds remainder
add zl, r18 ; Z points to next data
adc zh, r18 ; only add carry flag
sub zh, r18
; actually write data into buffer
flashWriteData_copyLoop:
ld r16, Y+
st X+, r16
dec r18
brne flashWriteData_copyLoop
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashCalcPosAndLength
;
; @param Z destination pos
; @param R17 number of bytes to write
; @return X absolute write position inside buffer
; @return R18 bytes to write
; @clobbers r24, r25
flashCalcPosAndLength:
; calc offset into buffer
mov r24, zl
andi r24, (FLASH_PAGESIZE-1) ; r24=rel pos inside buffer
ldi r25, FLASH_PAGESIZE
sub r25, r24 ; r25=bytes left inside page
mov r18, r17
cp r25, r17 ; bytes to read > bytes left in page?
brcc flashCalcPosAndLength_l1 ; no: jump
mov r18, r25 ; yes: cut r18 to number of bytes left in page
flashCalcPosAndLength_l1:
ldi xl, LOW(flashPageBuffer) ; set X to pos within page buffer
ldi xh, HIGH(flashPageBuffer)
add xl, r24
adc xh, r24
sub xh, r24
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashWriteCheckPage
;
; check whether the given address is inside the current page in buffer
;
; @return CFLAG set if same page, cleared otherwise
; @param Z address to write to
; @clobbers r18, r19
flashWriteCheckPage:
; check low byte of page
mov r18, zl
lds r19, flashPageStart
eor r18, r19
cbr r18, LOW(FLASH_PAGESIZE-1)
brne flashWriteCheckPage_ClcRet
; check high byte of page
mov r18, zh
lds r19, flashPageStart+1
eor r18, r19
cbr r18, HIGH(FLASH_PAGESIZE-1)
brne flashWriteCheckPage_ClcRet
sec
ret
flashWriteCheckPage_ClcRet:
clc
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashCheckCurrentValid
;
; check whether the page buffer contains a valid page (e.g. flashPageStart is not 0xffff)
;
; @return CFLAG set if current page is valid, cleared otherwise
; @clobbers r18, r19
flashCheckCurrentValid:
lds r18, flashPageStart
lds r19, flashPageStart+1
and r18, r19
inc r18
breq flashCheckCurrentValid_ClcRet
sec
ret
flashCheckCurrentValid_ClcRet:
clc
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashBeginPage
;
; Interrupts must be disabled!
;
; @param Z Address to read from (byte address as for LPM!)
; @clobbers (r16, r24, r25, X)
flashBeginPage:
push zl
push zh
cbr zl, LOW(FLASH_PAGESIZE-1)
cbr zh, HIGH(FLASH_PAGESIZE-1)
sts flashPageStart, zl
sts flashPageStart+1, zh
rcall flashReadPageIntoSram ; (r16, r24, r25, X, Z)
pop zh
pop zl
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashEndPage
;
; Interrupts must be disabled!
;
; @clobbers (R0, R1, R16, R18, R19, R20, R24, R25, X)
flashEndPage:
rcall flashCheckCurrentValid ; (r18, r19)
brcs flashEndPage_write
ret
flashEndPage_write:
push zl
push zh
rcall flashErasePage ; (R16, R20, Z)
rcall flashWritePage ; (r0, r1, r20, r24, r25, X, Z)
pop zh
pop zl
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashReadPageIntoSram
;
; @param Z Address to read from (byte address as for LPM!)
; @clobbers r16, r24, X, Z
flashReadPageIntoSram:
ldi xl, LOW(flashPageBuffer)
ldi xh, HIGH(flashPageBuffer)
ldi r24, LOW(FLASH_PAGESIZE)
flashReadPageIntoSram_loop:
lpm r16, Z+
st X+, r16
; adiw ZH:ZL, 1
dec r24
brne flashReadPageIntoSram_loop
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashErasePage
;
; Interrupts must be disabled!
;
; @clobbers R16, R20, Z
flashErasePage:
lds zl, flashPageStart
lds zh, flashPageStart+1
ldi r20, (1<<PGERS) + (1<<SPMEN) ; enable next SPM, erase page (R1/R0 ignored)
rcall flashDoSpm ; (R16)
ret
; @end
; ---------------------------------------------------------------------------
; @routine flashDoSpm
;
; wait until possible previous SPM finished and then issue another SPM.
;
; @param R20 value for register SPMCR
; @param R0 low value for SPM
; @param R1 high value for SPM
; @param Z address for SPM (byte address!)
; @clobbers R16
flashDoSpm:
flashDoSpm_wait: ; wait for possibly previous SPM to complete
in r16, SPMCSR
sbrc r16, SPMEN
rjmp flashDoSpm_wait
; SPM timed sequence
out SPMCSR, r20
spm
ret
; @end

View File

@@ -68,13 +68,25 @@ flashWaitForMulti100ms_loop:
flashWaitFor100ms:
ldi r24, 100
flashWaitFor100ms_loop:
rjmp flashWaitForMillisecs
; ---------------------------------------------------------------------------
; @routine flashWaitForMillisecs
; wait for multiples of 100 milliseconds.
;
; @param R24 time to wait in milliseconds
; @clobbers R24 (R22)
flashWaitForMillisecs:
push r24
rcall flashWaitFor1ms ; (R22, R24)
pop r24
dec r24
brne flashWaitFor100ms_loop
brne flashWaitForMillisecs
ret
; @end