forked from ole00/afterburner
-
Notifications
You must be signed in to change notification settings - Fork 0
/
aftb_sparse.h
266 lines (236 loc) · 7.32 KB
/
aftb_sparse.h
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/*
* Sparse fusemap functions for Afterburner GAL project.
*
* The idea of sparse fuse map is to store non-zero fuse bits only.
* The reason is to fit big fusemaps into a size-limited SRAM.
* The fusemap is divided into groups of 16 bits and each group
* has a 'type' bit stored in fuseTypes array. Group type 0 has
* all 16 fuesmap bits 0 and is not stored in fusemap array.
* Group type 1 has at least 1 fusemap bit set and is stored in
* fusemap array. Position of group type '1' in the fusemap array
* may vary based on which fuses are written.
*
* Sparse fusemap supports:
* - random reads and writes
* - a simple cache to speed up index look-ups.
*/
#ifdef USE_SPARSE_FUSEMAP
// compacting statistics - disabled by default
#define COMPACT_STAT 0
#define SPFUSES 128
unsigned char fuseType[SPFUSES]; //sparse fuses index
uint16_t sparseFusemapStat = 0; //bit 15: use sparse fusemaps, bits 0-11 : sparse fusemap size in bytes
uint8_t sparseCompactCounter = 0;
uint16_t sparseCacheBitPos = 0;
uint16_t sparseCacheOffset = 0;
uint16_t sparseCacheHit = 0;
uint8_t sparseCacheIndex = 0;
#if COMPACT_STAT
uint8_t sparseCompactRun = 0;
uint8_t sparseCompactAct = 0;
#endif
// reverse search of the fuse group index based on the byte position in the sparse array
// returns the group index
static uint16_t getFuseGroupIndex(uint16_t fuseOffsetBytePos) {
uint16_t groupPos = 0;
uint16_t i = 0;
uint16_t fuseOffset = 0;
//calculate fusemap offset
while (1) {
uint8_t rec = fuseType[i];
// speed optimised special case: all 8 bits are 0 (4 * 32 bits)
if (rec == 0) {
groupPos += 4; //4 groups in the byte
} else {
uint8_t j = 0;
//4 types per byte
while (j < 4) {
if ((rec & 0b11) == 1) { // type 0 & 3 - no byte stored in fusemap
if (fuseOffset == fuseOffsetBytePos) {
return groupPos;
}
fuseOffset += 4; // 32 bits in the group, fuse byte offset advances by 4 bytes
}
groupPos++; //check next group
rec >>= 2;
j++;
}
}
i++; //next byte from the fuseTypes
}
}
// get position of the fuse bit in the sparse array
static uint16_t getFusePositionAndType(uint16_t bitPos) {
uint16_t counter = 0;
uint8_t i = 0;
uint8_t type;
uint16_t fuseOffset = (bitPos & 0b11000) >> 3; //set odd / even byte of the fuse offset
if (bitPos <= sparseCacheBitPos) {
sparseCacheBitPos = 0;
sparseCacheOffset = 0;
sparseCacheIndex = 0;
} else {
counter = sparseCacheBitPos;
fuseOffset += sparseCacheOffset & 0xFFC;
i = sparseCacheIndex;
sparseCacheHit++;
}
//calculate fusemap offset
while (1) {
uint8_t rec = fuseType[i];
// speed optimised special case: all 8 bits are 0 or all are 1 (4 * 32 bits)
if (rec == 0 || rec == 0xFF) {
counter += 128;
if (counter > bitPos) {
return (fuseOffset << 2) | (rec & 0b11); // type is 0 or 3
}
sparseCacheBitPos = counter;
sparseCacheOffset = fuseOffset;
sparseCacheIndex = i + 1;
}
else {
uint8_t j = 0;
//4 fuse types per byte
while (j < 4) {
counter += 32;
type = rec & 0b11;
if (counter > bitPos) {
return (fuseOffset << 2) | type;
}
if (type == 1) { // type 0 & 3 - no byte stored in fusemap
fuseOffset += 4;
}
rec >>= 2;
j++;
}
}
i++; //next byte from the fuseTypes
}
}
static void insertFuseGroup(uint16_t dataPos, uint16_t bitPos) {
int16_t i = bitPos >> 5; //group index
uint16_t totalFuseBytes = sparseFusemapStat & 0x7FF; // max is 2048 bytes
fuseType[i >> 2] |= (1 << ((i & 0b11) << 1)); // set type 1 at the fuse group record
//shift all data in the fuse map starting at data pos by 4 bytes (32 bits)
if (dataPos < totalFuseBytes) {
for (i = totalFuseBytes - 1; i >= dataPos; i--) {
fusemap[i + 4] = fusemap[i];
}
}
sparseFusemapStat = totalFuseBytes + 4; // we can ignore the sparse bit
//clean the emptied fusemap data
fusemap[dataPos++] = 0;
fusemap[dataPos++] = 0;
fusemap[dataPos++] = 0;
fusemap[dataPos] = 0;
}
static void sparseCompactFuseMap(void) {
uint16_t i, j;
uint16_t total = (sparseFusemapStat & 0x7FF) >> 2; // max is 2048 bytes, and convert to the group index
uint32_t* fuses = (uint32_t*) fusemap;
if (total < 2) {
return;
}
#if COMPACT_STAT
sparseCompactRun++; //statistics
#endif
i = total - 1;
while(i) {
// remove 4 fusemap bytes at a position when the bits are all 1's
if (fuses[i] == 0xFFFFFFFF) {
if (i < total - 1) {
uint16_t fuseGroup = getFuseGroupIndex(i << 2); //ensure the int32 index is converted to int8/byte index
#if COMPACT_STAT
sparseCompactAct++; //statistics
#endif
/// shift the fuses by 4 bytes to the left
for (j = i; j < total - 1 ; j++) {
fuses[j] = fuses[j + 1];
}
total--;
fuseType[fuseGroup >> 2] |= (3 << ((fuseGroup & 0b11) << 1)); //set type 3 at the fuse group record
sparseFusemapStat -= 4; // fuse map total size reduced by 4 bytes
}
}
i--;
}
//destory cache
sparseCacheBitPos = 0;
sparseCacheOffset = 0;
sparseCacheIndex = 0;
#if COMPACT_STAT
Serial.print(F("sp comp:"));
Serial.print(sparseCompactRun, DEC);
Serial.print(F(" total:"));
Serial.println(sparseFusemapStat & 0x7FF, DEC);
#endif
}
static inline uint16_t sparseSetFuseBit(uint16_t bitPos) {
uint8_t type;
uint16_t pos;
//try to reduce the size of the fuse map by finding and removing blocks with all 1's
sparseCompactCounter ++;
if (sparseCompactCounter == 255) {
sparseCompactFuseMap();
}
pos = getFusePositionAndType(bitPos);
type = pos & 0b11;
pos >>= 2; //trim the type to get the byte position in fuse map
if (type == 0) { //we need to write the bit into a group that has all bits 0 so far
insertFuseGroup(pos & 0x7FC, bitPos);
}
return pos;
}
static inline uint16_t sparseGetFuseBit(uint16_t bitPos) {
uint8_t type;
uint16_t pos = getFusePositionAndType(bitPos);
type = pos & 0b11;
if (!type) { //type is 0 - the block contains all zero bits
return 0xFF00;
}
if (type == 3) { // type is 3 - the block contains all 1 bits
return 0xFF01;
}
pos >>= 2; //trim the type to get byte position in fuse map
return pos;
}
static void sparsePrintStat() {
Serial.print(F("sp bytes="));
Serial.println(sparseFusemapStat & 0x7FF, DEC);
Serial.print(F("c_hit="));
Serial.println(sparseCacheHit, DEC);
#if COMPACT_STAT
Serial.print(F("compact run="));
Serial.print(sparseCompactRun, DEC);
Serial.print(F(" cnt="));
Serial.println(sparseCompactAct, DEC);
#endif
}
static void sparseInit(char clearArray) {
if (clearArray) {
uint8_t i;
for (i = 0; i < SPFUSES; i++) {
fuseType[i] = 0;
}
}
sparseFusemapStat = (1 << 15);
sparseCompactCounter = 0;
sparseCacheBitPos = 0;
sparseCacheOffset = 0;
sparseCacheIndex = 0;
#if COMPACT_STAT
sparseCompactRun = 0;
sparseCompactAct = 0;
#endif
}
static inline void sparseDisable(void) {
sparseFusemapStat = 0;
}
#else /* ! USE_SPARSE_FUSEMAP */
#define sparseDisable()
#define sparseInit(X)
#define sparseGetFuseBit(X) 0
#define sparseSetFuseBit(X) 0
#define sparsePrintStat()
#define sparseFusemapStat 0
#endif