1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
;
; Copyright 2020 Daniel Friesel
;
; SPDX-License-Identifier: BSD-2-Clause
;
.global asm_save_all
.global asm_load_all
.global asm_load_mem
#define SRAM_BASE #1c00h
#define SRAM_SIZE 4096
; SRAM and stack pointer backup space
; two backup areas allow for consistency in case of a power loss during backup
sp_backup1:
.space 2
sram_backup1:
.space SRAM_SIZE
sp_backup2:
.space 2
sram_backup2:
.space SRAM_SIZE
; Backup Cookie: Do we have valid data (and if yes, where?)
; or has the FRAM been wiped?
backup_cookie:
.space 2
; save entire SRAM and CPU register state to persistent FRAM.
; Must be called with interrupts disabled
asm_save_all:
; r4 to r11 are callee saved -> push them to the stack.
; As we will save the entire SRAM (including stack), they are
; included in the SRAM backup and can be popped when restoring it.
.irp reg,4,5,6,7,8,9,10,11
push r\reg
.endr
mov &backup_cookie, r10 ; content of backup_cookie -> r10
mov #sp_backup1, r11 ; address of sp_backup1 -> r11
cmp r10, r11 ; backup_cookie == addr. of sp_backup1?
jne do_save_all ; if not, the previous backup went to space 2 or never happened -> write backup to space 1
; otherwise, the previous backup went to space 1 -> write backup to space 2
mov #sp_backup2, r11
do_save_all:
dint
mov r1, 0(r11) ; store stack pointer in sp_backup(1|2)
mov r11, r9 ; backup location -> r9
mov SRAM_BASE, r10 ; SRAM area start -> r10
save_sram_word:
add #2, r11
mov @r10+, 0(r11)
cmp SRAM_BASE+SRAM_SIZE, r10
jlo save_sram_word
mov r9, &backup_cookie ; save backup location (addr. of sp_backup(1|2)) in backup_cookie
eint
; revert changes to callee-saved registers
pop r11
pop r10
pop r9
; remove unchanged registers from stack
add #10, r1
ret
; load entire SRAM and CPU register state from persistent FRAM,
; if it contains valid backup data. Execution will resume at the
; last place where asm_save_all() was called as if nothing in between
; had happened. Does not take the state of hardware peripherals into account.
asm_load_all:
; check if we have backup data
push r11
push r10
mov &backup_cookie, r10
; ... in location 1?
mov #sp_backup1, r11
cmp r10, r11
jeq do_load_all
; ... in location 2?
mov #sp_backup2, r11
cmp r10, r11
jeq do_load_all
; no? -> too bad, resume with normal startup
pop r10
pop r11
ret
do_load_all:
dint
; restore stack pointer
mov @r11, r1
add #2, r11
; restore SRAM from backup
mov SRAM_BASE, r10
load_sram_word:
mov @r11+, 0(r10)
add #2, r10
cmp SRAM_BASE+SRAM_SIZE, r10
jlo load_sram_word
; restore registers
.irp reg,11,10,9,8,7,6,5,4
pop r\reg
.endr
eint
; The return address on the stack has been restored from FRAM backup
; -> execution will continue where asm_save_all was called
ret
; Load global objects from persistent FRAM, if it contains valid backup data.
; Stack and CPU registers are left as-is, the program flow is not altered.
asm_load_mem:
; check if we have backup data
push r11
push r10
mov &backup_cookie, r10
; ... in location 1?
mov #sp_backup1, r11
cmp r10, r11
jeq do_load_mem
; ... in location 2?
mov #sp_backup2, r11
cmp r10, r11
jeq do_load_mem
; no? -> too bad, resume with normal startup
pop r10
pop r11
ret
do_load_mem:
dint
; restore SRAM from backup, excluding stack
; -> everything from SRAM start (inclusive) to @sp (exclusive). Reminder: SP == R1 on MSP430
add #2, r11
mov SRAM_BASE, r10
load_sram_word2:
mov @r11+, 0(r10)
add #2, r10
cmp r1, r10
jlo load_sram_word2
pop r10
pop r11
eint
ret
|