mirror of
https://github.com/ole00/afterburner.git
synced 2024-11-25 10:31:01 +00:00
267 lines
7.3 KiB
C
267 lines
7.3 KiB
C
/*
|
|
* 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
|