/* * 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