OpenSSD Cosmos+ Platform Firmware  0.0.2
The firmware of Cosmos+ OpenSSD Platform for TOSHIBA nand flash module.
address_translation.c File Reference
#include <assert.h>
#include "debug.h"
#include "xil_printf.h"
#include "memory_map.h"
Include dependency graph for address_translation.c:

Go to the source code of this file.

Functions

void InitAddressMap ()
 Initialize the translation related maps. More...
 
void InitSliceMap ()
 Initialize Logical and Virtual Slick Map. More...
 
void RemapBadBlock ()
 Try to remap the bad blocks in the main block space. More...
 
void InitDieMap ()
 Reset the free block list of each die. More...
 
void InitBlockMap ()
 Create V2P table and free block list. More...
 
void InitCurrentBlockOfDieMap ()
 Get a default free block for each die. More...
 
void ReadBadBlockTable (unsigned int tempBbtBufAddr[], unsigned int tempBbtBufEntrySize)
 Read the pages that should contain the bad block table. More...
 
void FindBadBlock (unsigned char dieState[], unsigned int tempBbtBufAddr[], unsigned int tempBbtBufEntrySize, unsigned int tempReadBufAddr[], unsigned int tempReadBufEntrySize)
 Build the bbt for those dies whose bbt doesn't exist. More...
 
void SaveBadBlockTable (unsigned char dieState[], unsigned int tempBbtBufAddr[], unsigned int tempBbtBufEntrySize)
 Persist the newly created bbt for those dies whose bbt not exists. More...
 
void RecoverBadBlockTable (unsigned int tempBufAddr)
 Read the bbt from flash and re-create if not exists. More...
 
void EraseTotalBlockSpace ()
 Erase all the blocks on user dies and wait until done. More...
 
void EraseUserBlockSpace ()
 Erase all the non-bad main blocks on user dies and wait until done. More...
 
void InitBlockDieMap ()
 Build the bad block table and V2P block mapping of each user die. More...
 
unsigned int AddrTransRead (unsigned int logicalSliceAddr)
 Get the virtual slice address of the given logical slice. More...
 
unsigned int AddrTransWrite (unsigned int logicalSliceAddr)
 Assign a new virtual (physical) page to the specified logical page. More...
 
unsigned int FindFreeVirtualSlice ()
 Select a free physical page (virtual slice). More...
 
unsigned int FindFreeVirtualSliceForGc (unsigned int copyTargetDieNo, unsigned int victimBlockNo)
 
unsigned int FindDieForFreeSliceAllocation ()
 Update and get the die number to serve the next write request. More...
 
void InvalidateOldVsa (unsigned int logicalSliceAddr)
 Invalidate the specified virtual page. More...
 
void EraseBlock (unsigned int dieNo, unsigned int blockNo)
 Erase the specified block of the specified die and discard its LSAs. More...
 
void PutToFbList (unsigned int dieNo, unsigned int blockNo)
 Append the given virtual block to the free block list of its die. More...
 
unsigned int GetFromFbList (unsigned int dieNo, unsigned int getFreeBlockOption)
 Pop the first block in the free block list of the specified die. More...
 
void UpdatePhyBlockMapForGrownBadBlock (unsigned int dieNo, unsigned int phyBlockNo)
 Mark the given physical block bad block and update the bbt later. More...
 
void UpdateBadBlockTableForGrownBadBlock (unsigned int tempBufAddr)
 Update the bad block table and persist to the specified block. More...
 

Variables

P_LOGICAL_SLICE_MAP logicalSliceMapPtr
 
P_VIRTUAL_SLICE_MAP virtualSliceMapPtr
 
P_VIRTUAL_BLOCK_MAP virtualBlockMapPtr
 
P_VIRTUAL_DIE_MAP virtualDieMapPtr
 
P_PHY_BLOCK_MAP phyBlockMapPtr
 
P_BAD_BLOCK_TABLE_INFO_MAP bbtInfoMapPtr
 
unsigned char sliceAllocationTargetDie
 
unsigned int mbPerbadBlockSpace
 

Function Documentation

◆ AddrTransRead()

unsigned int AddrTransRead ( unsigned int  logicalSliceAddr)

Get the virtual slice address of the given logical slice.

Parameters
logicalSliceAddrthe logical address of the target slice.
Returns
unsigned int the virtual address of the target slice.

Definition at line 871 of file address_translation.c.

872{
873 unsigned int virtualSliceAddr;
874
875 if (logicalSliceAddr < SLICES_PER_SSD)
876 {
877 virtualSliceAddr = logicalSliceMapPtr->logicalSlice[logicalSliceAddr].virtualSliceAddr;
878
879 if (virtualSliceAddr != VSA_NONE)
880 return virtualSliceAddr;
881 else
882 return VSA_FAIL;
883 }
884 else
885 assert(!"[WARNING] Logical address is larger than maximum logical address served by SSD [WARNING]");
886}
P_LOGICAL_SLICE_MAP logicalSliceMapPtr
#define VSA_FAIL
#define VSA_NONE
#define SLICES_PER_SSD
Definition: ftl_config.h:231
unsigned int virtualSliceAddr
LOGICAL_SLICE_ENTRY logicalSlice[SLICES_PER_SSD]
Here is the caller graph for this function:

◆ AddrTransWrite()

unsigned int AddrTransWrite ( unsigned int  logicalSliceAddr)

Assign a new virtual (physical) page to the specified logical page.

Before issuing the write request, we should allocate a physical page for the specified logical page and invalidate the old physical page if it exists.

Note
In current implementation, if the target page of a write request is not empty, a read request on the target page will be issued automatically before the write request, therefore, we don't have to handle data migration in this function.
See also
ReqTransSliceToLowLevel().
Parameters
logicalSliceAddrthe logical address of the target slice.
Returns
unsigned int the renewed virtual slice address for the given logical slice.

Definition at line 903 of file address_translation.c.

904{
905 unsigned int virtualSliceAddr;
906
907 if (logicalSliceAddr < SLICES_PER_SSD)
908 {
909 InvalidateOldVsa(logicalSliceAddr);
910
911 virtualSliceAddr = FindFreeVirtualSlice();
912
913 logicalSliceMapPtr->logicalSlice[logicalSliceAddr].virtualSliceAddr = virtualSliceAddr;
914 virtualSliceMapPtr->virtualSlice[virtualSliceAddr].logicalSliceAddr = logicalSliceAddr;
915
916 return virtualSliceAddr;
917 }
918 else
919 assert(!"[WARNING] Logical address is larger than maximum logical address served by SSD [WARNING]");
920}
unsigned int FindFreeVirtualSlice()
Select a free physical page (virtual slice).
void InvalidateOldVsa(unsigned int logicalSliceAddr)
Invalidate the specified virtual page.
P_VIRTUAL_SLICE_MAP virtualSliceMapPtr
unsigned int logicalSliceAddr
VIRTUAL_SLICE_ENTRY virtualSlice[SLICES_PER_SSD]
Here is the call graph for this function:
Here is the caller graph for this function:

◆ EraseBlock()

void EraseBlock ( unsigned int  dieNo,
unsigned int  blockNo 
)

Erase the specified block of the specified die and discard its LSAs.

This function will:

  • Send a ERASE request to erase the specified block
  • Move the specified block to free block list
  • Discard all the logical slice addresses of the origin block

    Todo:
    programmedPageCnt
Note
The specified block may not be invalidated immediately.
Parameters
dieNothe die number of the specified block.
blockNothe block number on the specified die.

Definition at line 1122 of file address_translation.c.

1123{
1124 unsigned int pageNo, virtualSliceAddr, reqSlotTag;
1125
1126 reqSlotTag = GetFromFreeReqQ();
1127
1128 reqPoolPtr->reqPool[reqSlotTag].reqType = REQ_TYPE_NAND;
1134 reqPoolPtr->reqPool[reqSlotTag].nandInfo.virtualSliceAddr = Vorg2VsaTranslation(dieNo, blockNo, 0);
1136 virtualBlockMapPtr->block[dieNo][blockNo].currentPage;
1137
1138 SelectLowLevelReqQ(reqSlotTag);
1139
1140 // block map indicated blockNo initialization
1141 virtualBlockMapPtr->block[dieNo][blockNo].free = 1;
1142 virtualBlockMapPtr->block[dieNo][blockNo].eraseCnt++;
1143 virtualBlockMapPtr->block[dieNo][blockNo].invalidSliceCnt = 0;
1144 virtualBlockMapPtr->block[dieNo][blockNo].currentPage = 0;
1145
1146 PutToFbList(dieNo, blockNo);
1147
1148 for (pageNo = 0; pageNo < USER_PAGES_PER_BLOCK; pageNo++)
1149 {
1150 virtualSliceAddr = Vorg2VsaTranslation(dieNo, blockNo, pageNo);
1152 }
1153}
void PutToFbList(unsigned int dieNo, unsigned int blockNo)
Append the given virtual block to the free block list of its die.
P_VIRTUAL_BLOCK_MAP virtualBlockMapPtr
#define Vorg2VsaTranslation(dieNo, blockNo, pageNo)
Translate virtual NAND organization (location vector) into VSA.
#define LSA_NONE
#define USER_PAGES_PER_BLOCK
Definition: ftl_config.h:221
unsigned int GetFromFreeReqQ()
Get a free request from the free request queue.
P_REQ_POOL reqPoolPtr
#define REQ_OPT_BLOCK_SPACE_MAIN
for the 1 bit flag REQ_OPTION::blockSpace.
#define REQ_OPT_ROW_ADDR_DEPENDENCY_CHECK
#define REQ_OPT_DATA_BUF_NONE
#define REQ_OPT_NAND_ADDR_VSA
#define REQ_CODE_ERASE
#define REQ_TYPE_NAND
void SelectLowLevelReqQ(unsigned int reqSlotTag)
Dispatch given NVMe/NAND request to corresponding request queue.
unsigned int programmedPageCnt
unsigned int virtualSliceAddr
unsigned int dataBufFormat
Type of address stored in the SSD_REQ_FORMAT::dataBufInfo.
unsigned int nandAddr
Type of address stored in the SSD_REQ_FORMAT::nandInfo.
unsigned int blockSpace
unsigned int rowAddrDependencyCheck
SSD_REQ_FORMAT reqPool[AVAILABLE_OUNTSTANDING_REQ_COUNT]
REQ_OPTION reqOpt
unsigned int reqCode
NAND_INFO nandInfo
unsigned int reqType
unsigned int currentPage
unsigned int eraseCnt
unsigned int free
unsigned int invalidSliceCnt
VIRTUAL_BLOCK_ENTRY block[USER_DIES][USER_BLOCKS_PER_DIE]
Here is the call graph for this function:
Here is the caller graph for this function:

◆ EraseTotalBlockSpace()

void EraseTotalBlockSpace ( )

Erase all the blocks on user dies and wait until done.

Definition at line 744 of file address_translation.c.

745{
746 unsigned int blockNo, dieNo, reqSlotTag;
747
748 xil_printf("Erase total block space...wait for a minute...\r\n");
749
750 for (blockNo = 0; blockNo < TOTAL_BLOCKS_PER_DIE; blockNo++)
751 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
752 {
753 reqSlotTag = GetFromFreeReqQ();
754
761
764 reqPoolPtr->reqPool[reqSlotTag].nandInfo.physicalBlock = blockNo;
765 reqPoolPtr->reqPool[reqSlotTag].nandInfo.physicalPage = 0;
766
767 SelectLowLevelReqQ(reqSlotTag);
768 }
769
771 xil_printf("Done.\r\n");
772}
#define Vdie2PchTranslation(dieNo)
#define Vdie2PwayTranslation(dieNo)
#define USER_DIES
Definition: ftl_config.h:219
#define TOTAL_BLOCKS_PER_DIE
Definition: ftl_config.h:163
#define REQ_OPT_BLOCK_SPACE_TOTAL
#define REQ_OPT_NAND_ADDR_PHY_ORG
#define REQ_OPT_ROW_ADDR_DEPENDENCY_NONE
void SyncAllLowLevelReqDone()
Do schedule until all the requests are done.
unsigned int physicalBlock
unsigned int physicalWay
unsigned int physicalPage
unsigned int physicalCh
Here is the call graph for this function:
Here is the caller graph for this function:

◆ EraseUserBlockSpace()

void EraseUserBlockSpace ( )

Erase all the non-bad main blocks on user dies and wait until done.

Definition at line 777 of file address_translation.c.

778{
779 unsigned int blockNo, dieNo, reqSlotTag;
780
781 xil_printf("Erase User block space...wait for a minute...\r\n");
782
783 for (blockNo = 0; blockNo < USER_BLOCKS_PER_DIE; blockNo++)
784 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
785 if (!virtualBlockMapPtr->block[dieNo][blockNo].bad)
786 {
787 reqSlotTag = GetFromFreeReqQ();
788
795
796 reqPoolPtr->reqPool[reqSlotTag].nandInfo.virtualSliceAddr = Vorg2VsaTranslation(dieNo, blockNo, 0);
797
798 SelectLowLevelReqQ(reqSlotTag);
799 }
800
802 xil_printf("Done.\r\n");
803}
#define USER_BLOCKS_PER_DIE
Definition: ftl_config.h:233
unsigned int bad
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FindBadBlock()

void FindBadBlock ( unsigned char  dieState[],
unsigned int  tempBbtBufAddr[],
unsigned int  tempBbtBufEntrySize,
unsigned int  tempReadBufAddr[],
unsigned int  tempReadBufEntrySize 
)

Build the bbt for those dies whose bbt doesn't exist.

To determine whether a block is bad block, we should check 4 bytes in that block:

  • The first byte of the data region of the first page in this block
  • The first byte of the data region of the last page in this block
  • The first byte of the metadata region of the first page in this block
  • The first byte of the metadata region of the last page in this block

If only of the 4 bytes are not 0xFF, we can view this block as bad block.

Todo:

why check these 4 bytes, why ECC and row addr dep off?

the update in the second for is redundant, it can be merged to next part

Warning
tempBbtBufEntrySize and tempReadBufEntrySize not used
Bug:
blockChecker not initialized
Parameters
dieStatethe flags that indicate whether the bbt of that die should be rebuilt.
tempBbtBufAddrthe addresses of the bbt of each die.
tempBbtBufEntrySizethe size of the data and metadata region of a page.
tempReadBufAddrthe buffer addresses for storing the pages to be read.
tempReadBufEntrySizethe size of a whole page (data + metadata + ECC).

Check all the blocks on the SSD

Note
Check multiple dies at the same time to speed up and reduce the buffer size.

Definition at line 433 of file address_translation.c.

435{
436 unsigned int phyBlockNo, chNo, wayNo, dieNo, reqSlotTag;
437 unsigned char blockChecker[USER_DIES];
438 unsigned char *markPointer0;
439 unsigned char *markPointer1;
440 unsigned char *bbtUpdater;
441
447 for (phyBlockNo = 0; phyBlockNo < TOTAL_BLOCKS_PER_DIE; phyBlockNo++)
448 {
449 // Read the first page of the specified block of each die
450 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
451 if (!dieState[dieNo])
452 {
453 blockChecker[dieNo] = BLOCK_STATE_NORMAL;
454
455 reqSlotTag = GetFromFreeReqQ();
456
465
466 reqPoolPtr->reqPool[reqSlotTag].dataBufInfo.addr = tempReadBufAddr[dieNo];
467
470 reqPoolPtr->reqPool[reqSlotTag].nandInfo.physicalBlock = phyBlockNo;
472
473 SelectLowLevelReqQ(reqSlotTag);
474 }
476
477 // Read the last page of the specified block of each die if needed
478 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
479 {
480 chNo = Vdie2PchTranslation(dieNo);
481 wayNo = Vdie2PwayTranslation(dieNo);
482
483 if (!dieState[dieNo])
484 {
485 markPointer0 = (unsigned char *)(tempReadBufAddr[dieNo] + BAD_BLOCK_MARK_BYTE0);
486 markPointer1 = (unsigned char *)(tempReadBufAddr[dieNo] + BAD_BLOCK_MARK_BYTE1);
487
488 if ((*markPointer0 == CLEAN_DATA_IN_BYTE) && (*markPointer1 == CLEAN_DATA_IN_BYTE))
489 {
490 reqSlotTag = GetFromFreeReqQ();
491
501
502 reqPoolPtr->reqPool[reqSlotTag].dataBufInfo.addr = tempReadBufAddr[dieNo];
503
504 reqPoolPtr->reqPool[reqSlotTag].nandInfo.physicalCh = chNo;
505 reqPoolPtr->reqPool[reqSlotTag].nandInfo.physicalWay = wayNo;
506 reqPoolPtr->reqPool[reqSlotTag].nandInfo.physicalBlock = phyBlockNo;
508
509 SelectLowLevelReqQ(reqSlotTag);
510 }
511 else
512 {
513 pr_info("C/W[%u/%u]: bad block at PBlk %u (0x%x)", chNo, wayNo, phyBlockNo, phyBlockNo);
514
515 blockChecker[dieNo] = BLOCK_STATE_BAD;
516 }
517 }
518 }
519
521
522 // determine whether these blocks are bad blocks and update the bbt
523 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
524 {
525 chNo = Vdie2PchTranslation(dieNo);
526 wayNo = Vdie2PwayTranslation(dieNo);
527
528 if (!dieState[dieNo])
529 {
530 markPointer0 = (unsigned char *)(tempReadBufAddr[dieNo] + BAD_BLOCK_MARK_BYTE0);
531 markPointer1 = (unsigned char *)(tempReadBufAddr[dieNo] + BAD_BLOCK_MARK_BYTE1);
532
533 if (!((*markPointer0 == CLEAN_DATA_IN_BYTE) && (*markPointer1 == CLEAN_DATA_IN_BYTE)))
534 if (blockChecker[dieNo] == BLOCK_STATE_NORMAL)
535 {
536
537 pr_info("C/W[%u/%u]: bad block at PBlk %u (0x%x)", chNo, wayNo, phyBlockNo, phyBlockNo);
538 blockChecker[dieNo] = BLOCK_STATE_BAD;
539 }
540
541 // update the bbt this block
542 bbtUpdater = (unsigned char *)(tempBbtBufAddr[dieNo] + phyBlockNo);
543 *bbtUpdater = blockChecker[dieNo];
544 phyBlockMapPtr->phyBlock[dieNo][phyBlockNo].bad = blockChecker[dieNo];
545 }
546 }
547 }
548}
P_PHY_BLOCK_MAP phyBlockMapPtr
#define BLOCK_STATE_NORMAL
#define CLEAN_DATA_IN_BYTE
#define BLOCK_STATE_BAD
#define pr_info(fmt,...)
Definition: debug.h:86
#define BAD_BLOCK_MARK_BYTE1
Definition: ftl_config.h:168
#define BAD_BLOCK_MARK_BYTE0
Definition: ftl_config.h:167
#define BAD_BLOCK_MARK_PAGE0
Definition: ftl_config.h:165
#define BAD_BLOCK_MARK_PAGE1
Definition: ftl_config.h:166
#define REQ_OPT_NAND_ECC_WARNING_OFF
#define REQ_OPT_DATA_BUF_ADDR
#define REQ_CODE_READ
#define REQ_OPT_NAND_ECC_OFF
unsigned int addr
unsigned int bad
PHY_BLOCK_ENTRY phyBlock[USER_DIES][TOTAL_BLOCKS_PER_DIE]
unsigned int nandEccWarning
unsigned int nandEcc
DATA_BUF_INFO dataBufInfo
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FindDieForFreeSliceAllocation()

unsigned int FindDieForFreeSliceAllocation ( )

Update and get the die number to serve the next write request.

To exploit the parallelism, the write request should first be interleaved on different channels to take advantage of the channel parallelism. If all the channels' same way are used, select the next way of each channel to use the die parallelism.

Warning
As paper the mentioned, may not perform well if latency largely varied.
Returns
unsigned int The target die number.

Definition at line 1051 of file address_translation.c.

1052{
1053 static unsigned char targetCh = 0;
1054 static unsigned char targetWay = 0;
1055 unsigned int targetDie;
1056
1057 targetDie = Pcw2VdieTranslation(targetCh, targetWay);
1058
1059 if (targetCh != (USER_CHANNELS - 1))
1060 targetCh = targetCh + 1;
1061 else
1062 {
1063 targetCh = 0;
1064 targetWay = (targetWay + 1) % USER_WAYS;
1065 }
1066
1067 return targetDie;
1068}
#define Pcw2VdieTranslation(chNo, wayNo)
Get die number from physical channel number and way number.
#define USER_CHANNELS
Definition: ftl_config.h:207
#define USER_WAYS
Definition: ftl_config.h:208
Here is the caller graph for this function:

◆ FindFreeVirtualSlice()

unsigned int FindFreeVirtualSlice ( )

Select a free physical page (virtual slice).

In current implementation, the target page to serve the write request is determined by checking the following three variables:

  • sliceAllocationTargetDie:

    The die where the next request should be issued to.

    To exploit the parallelism of each die, especially under write-intensive workload, the write requests will be interleaved to each die.

    Check FindDieForFreeSliceAllocation() for details.

  • VIRTUAL_DIE_ENTRY::currentBlock:

    The current working block of the target die.

    Each die maintains a current working block and will select a page from the current working block of target die to serve the write request. Once all the pages of the current working block are used, the fw will select a new free block from the free block list as the new current working block of that die.

    If there the free block list of that die is empty, the fw will try to release invalid blocks by doing GC.

    Check GetFromFbList() and GarbageCollection() for the details.

  • VIRTUAL_BLOCK_ENTRY::currentPage:

    The current working page of the current working block on the die.

    Current implementation just selects the free page sequentially from the current working block.

See also
VIRTUAL_DIE_ENTRY, VIRTUAL_BLOCK_ENTRY, FindDieForFreeSliceAllocation().
Warning
why the currentPage might be full after GC?
why assign dieNo before return? redundant?
Returns
unsigned int the VSA for the request.

Definition at line 965 of file address_translation.c.

966{
967 unsigned int currentBlock, virtualSliceAddr, dieNo;
968
970 currentBlock = virtualDieMapPtr->die[dieNo].currentBlock;
971
972 // if the currently used block is full, assign a free block as new current block
973 if (virtualBlockMapPtr->block[dieNo][currentBlock].currentPage == USER_PAGES_PER_BLOCK)
974 {
975 currentBlock = GetFromFbList(dieNo, GET_FREE_BLOCK_NORMAL);
976
977 if (currentBlock != BLOCK_FAIL)
978 virtualDieMapPtr->die[dieNo].currentBlock = currentBlock;
979 else
980 {
981 GarbageCollection(dieNo);
982 currentBlock = virtualDieMapPtr->die[dieNo].currentBlock;
983
984 // FIXME: why need to check whether `currentPage` is full?
985 if (virtualBlockMapPtr->block[dieNo][currentBlock].currentPage == USER_PAGES_PER_BLOCK)
986 {
987 currentBlock = GetFromFbList(dieNo, GET_FREE_BLOCK_NORMAL);
988 if (currentBlock != BLOCK_FAIL)
989 virtualDieMapPtr->die[dieNo].currentBlock = currentBlock;
990 else
991 assert(!"[WARNING] There is no available block [WARNING]");
992 }
993 else if (virtualBlockMapPtr->block[dieNo][currentBlock].currentPage > USER_PAGES_PER_BLOCK)
994 assert(!"[WARNING] Current page management fail [WARNING]");
995 }
996 }
997 else if (virtualBlockMapPtr->block[dieNo][currentBlock].currentPage > USER_PAGES_PER_BLOCK)
998 assert(!"[WARNING] Current page management fail [WARNING]");
999
1000 virtualSliceAddr =
1001 Vorg2VsaTranslation(dieNo, currentBlock, virtualBlockMapPtr->block[dieNo][currentBlock].currentPage);
1002 virtualBlockMapPtr->block[dieNo][currentBlock].currentPage++;
1003 sliceAllocationTargetDie = FindDieForFreeSliceAllocation(); // sliceAllocationTargetDie should be updated
1004 dieNo = sliceAllocationTargetDie; // don't merge the 2 lines
1005 return virtualSliceAddr;
1006}
unsigned char sliceAllocationTargetDie
unsigned int FindDieForFreeSliceAllocation()
Update and get the die number to serve the next write request.
unsigned int GetFromFbList(unsigned int dieNo, unsigned int getFreeBlockOption)
Pop the first block in the free block list of the specified die.
P_VIRTUAL_DIE_MAP virtualDieMapPtr
#define GET_FREE_BLOCK_NORMAL
#define BLOCK_FAIL
void GarbageCollection(unsigned int dieNo)
unsigned int currentBlock
VIRTUAL_DIE_ENTRY die[USER_DIES]
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FindFreeVirtualSliceForGc()

unsigned int FindFreeVirtualSliceForGc ( unsigned int  copyTargetDieNo,
unsigned int  victimBlockNo 
)

Definition at line 1008 of file address_translation.c.

1009{
1010 unsigned int currentBlock, virtualSliceAddr, dieNo;
1011
1012 dieNo = copyTargetDieNo;
1013 if (victimBlockNo == virtualDieMapPtr->die[dieNo].currentBlock)
1014 {
1017 assert(!"[WARNING] There is no available block [WARNING]");
1018 }
1019 currentBlock = virtualDieMapPtr->die[dieNo].currentBlock;
1020
1021 if (virtualBlockMapPtr->block[dieNo][currentBlock].currentPage == USER_PAGES_PER_BLOCK)
1022 {
1023
1024 currentBlock = GetFromFbList(dieNo, GET_FREE_BLOCK_GC);
1025
1026 if (currentBlock != BLOCK_FAIL)
1027 virtualDieMapPtr->die[dieNo].currentBlock = currentBlock;
1028 else
1029 assert(!"[WARNING] There is no available block [WARNING]");
1030 }
1031 else if (virtualBlockMapPtr->block[dieNo][currentBlock].currentPage > USER_PAGES_PER_BLOCK)
1032 assert(!"[WARNING] Current page management fail [WARNING]");
1033
1034 virtualSliceAddr =
1035 Vorg2VsaTranslation(dieNo, currentBlock, virtualBlockMapPtr->block[dieNo][currentBlock].currentPage);
1036 virtualBlockMapPtr->block[dieNo][currentBlock].currentPage++;
1037 return virtualSliceAddr;
1038}
#define GET_FREE_BLOCK_GC
Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetFromFbList()

unsigned int GetFromFbList ( unsigned int  dieNo,
unsigned int  getFreeBlockOption 
)

Pop the first block in the free block list of the specified die.

Note
Each die will reserve some free blocks (VIRTUAL_DIE_ENTRY::freeBlockCnt), so if the number of free blocks is less then the number of preserved blocks, BLOCK_FAIL will be returned.
Todo:
Difference between GET_FREE_BLOCK_NORMAL and GET_FREE_BLOCK_GC.
Parameters
dieNoThe target die number.
getFreeBlockOption//TODO
Returns
unsigned int The virtual block address of the evicted block.

Definition at line 1194 of file address_translation.c.

1195{
1196 unsigned int evictedBlockNo;
1197
1198 evictedBlockNo = virtualDieMapPtr->die[dieNo].headFreeBlock;
1199
1200 if (getFreeBlockOption == GET_FREE_BLOCK_NORMAL)
1201 {
1203 return BLOCK_FAIL;
1204 }
1205 else if (getFreeBlockOption == GET_FREE_BLOCK_GC)
1206 {
1207 if (evictedBlockNo == BLOCK_NONE)
1208 return BLOCK_FAIL;
1209 }
1210 else
1211 assert(!"[WARNING] Wrong getFreeBlockOption [WARNING]");
1212
1213 if (virtualBlockMapPtr->block[dieNo][evictedBlockNo].nextBlock != BLOCK_NONE)
1214 {
1215 virtualDieMapPtr->die[dieNo].headFreeBlock = virtualBlockMapPtr->block[dieNo][evictedBlockNo].nextBlock;
1216 virtualBlockMapPtr->block[dieNo][virtualBlockMapPtr->block[dieNo][evictedBlockNo].nextBlock].prevBlock =
1217 BLOCK_NONE;
1218 }
1219 else
1220 {
1223 }
1224
1225 virtualBlockMapPtr->block[dieNo][evictedBlockNo].free = 0;
1227
1228 virtualBlockMapPtr->block[dieNo][evictedBlockNo].nextBlock = BLOCK_NONE;
1229 virtualBlockMapPtr->block[dieNo][evictedBlockNo].prevBlock = BLOCK_NONE;
1230
1231 return evictedBlockNo;
1232}
#define RESERVED_FREE_BLOCK_COUNT
#define BLOCK_NONE
unsigned int prevBlock
unsigned int nextBlock
unsigned int headFreeBlock
unsigned int freeBlockCnt
unsigned int tailFreeBlock
Here is the caller graph for this function:

◆ InitAddressMap()

void InitAddressMap ( )

Initialize the translation related maps.

The following tasks will be finished in the function:

  • Initialize logical and virtual slice map (all entries map to NONE)
  • Read/Remake the bad block table of each die
  • Replace bad blocks with the reserved blocks in the same die
  • Initialize V2P table and free block list
  • Choose a free block as the current working block for each die

This function, only initialize the base addresses of these maps, the physical block map and some bad blocks info. The other maps will be initialized in InitBlockDieMap() and InitSliceMap(), check the two functions for further initialization.

Todo:
To initialize the physical block map,

Definition at line 82 of file address_translation.c.

83{
84 unsigned int blockNo, dieNo;
85
92
93 // reset physical block mapping and bad block info
94 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
95 {
96 // the blocks should not be remapped to any other blocks before remmaping
97 for (blockNo = 0; blockNo < TOTAL_BLOCKS_PER_DIE; blockNo++)
98 phyBlockMapPtr->phyBlock[dieNo][blockNo].remappedPhyBlock = blockNo;
99
100 bbtInfoMapPtr->bbtInfo[dieNo].phyBlock = 0;
102 }
103
104 // by default, the request start from the first die
106
107 InitSliceMap();
109}
void InitSliceMap()
Initialize Logical and Virtual Slick Map.
P_BAD_BLOCK_TABLE_INFO_MAP bbtInfoMapPtr
void InitBlockDieMap()
Build the bad block table and V2P block mapping of each user die.
struct _PHY_BLOCK_MAP * P_PHY_BLOCK_MAP
struct _BAD_BLOCK_TABLE_INFO_MAP * P_BAD_BLOCK_TABLE_INFO_MAP
#define BBT_INFO_GROWN_BAD_UPDATE_NONE
struct _VIRTUAL_SLICE_MAP * P_VIRTUAL_SLICE_MAP
struct _VIRTUAL_BLOCK_MAP * P_VIRTUAL_BLOCK_MAP
struct _LOGICAL_SLICE_MAP * P_LOGICAL_SLICE_MAP
struct _VIRTUAL_DIE_MAP * P_VIRTUAL_DIE_MAP
#define VIRTUAL_BLOCK_MAP_ADDR
Definition: memory_map.h:101
#define PHY_BLOCK_MAP_ADDR
Definition: memory_map.h:102
#define BAD_BLOCK_TABLE_INFO_MAP_ADDR
Definition: memory_map.h:103
#define VIRTUAL_SLICE_MAP_ADDR
Definition: memory_map.h:100
#define VIRTUAL_DIE_MAP_ADDR
Definition: memory_map.h:104
#define LOGICAL_SLICE_MAP_ADDR
Definition: memory_map.h:99
unsigned int phyBlock
unsigned int grownBadUpdate
BAD_BLOCK_TABLE_INFO_ENTRY bbtInfo[USER_DIES]
unsigned int remappedPhyBlock
Here is the call graph for this function:
Here is the caller graph for this function:

◆ InitBlockDieMap()

void InitBlockDieMap ( )

Build the bad block table and V2P block mapping of each user die.

To create the V2P mapping, we have to:

  1. Build the bad block table by reading or remaking the bad block table
  2. Replace bad blocks with reserved blocks
  3. Map virtual blocks to available physical blocks
  4. Add available virtual blocks into free block list

Definition at line 815 of file address_translation.c.

816{
817 unsigned int dieNo;
818 unsigned char eraseFlag = 1;
819
820 xil_printf("Press 'X' to re-make the bad block table.\r\n");
821
822 char input = inbyte();
823 if (input == 'X')
824 {
825 xil_printf("[WARNING!!!] Start re-making bad block table\r\n");
827 eraseFlag = 0;
828 }
829 else
830 {
831 xil_printf("[WARNING!!!] Skip re-making bad block table\r\n");
832 }
833
834 // empty the free block list of each die
835 InitDieMap();
836
837 // read bbt from flash [, create bbt, persist new bbt to flash]
839
840 /*
841 * Since the block specified by `BAD_BLOCK_TABLE_INFO_ENTRY::phyBlock` is used for
842 * storing the bbt of that die, so we have to mark that block bad and let it to be
843 * remapped to another block for using.
844 *
845 * And because we have at least one bad block on each die, the remapping process thus
846 * should not be skipped.
847 */
848 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
851
852 // create V2P table and initialize free block list
853 InitBlockMap();
854
855 if (eraseFlag)
857
859}
void InitDieMap()
Reset the free block list of each die.
void EraseUserBlockSpace()
Erase all the non-bad main blocks on user dies and wait until done.
void InitBlockMap()
Create V2P table and free block list.
void EraseTotalBlockSpace()
Erase all the blocks on user dies and wait until done.
void InitCurrentBlockOfDieMap()
Get a default free block for each die.
void RecoverBadBlockTable(unsigned int tempBufAddr)
Read the bbt from flash and re-create if not exists.
void RemapBadBlock()
Try to remap the bad blocks in the main block space.
char inbyte()
Definition: bsp.c:4
#define RESERVED_DATA_BUFFER_BASE_ADDR
Definition: memory_map.h:85
Here is the call graph for this function:
Here is the caller graph for this function:

◆ InitBlockMap()

void InitBlockMap ( )

Create V2P table and free block list.

Assign a physical blocks for each virtual block:

  • The translation rule is basically static mapping, but the target physical block may be bad block, and thus point to another reserved block. Therefore, we should check the remappedPhyBlock to get the available physical block.

    Note
    If there is no enough reserved block to remap the bad physical block, the remappedPhyBlock will still points to the original PBA. So we must check the bad block flag to make sure the remapped block is available.
  • If the target physical block of the virtual block is non-bad block, add the virtual block into free block list; otherwise, ignore and avoid to use the virtual block.
See also
Todo:
Check used (neither free nor bad) blocks without updating the BBT.

Definition at line 300 of file address_translation.c.

301{
302 unsigned int dieNo, phyBlockNo, virtualBlockNo, remappedPhyBlock;
303
304 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
305 {
306 for (virtualBlockNo = 0; virtualBlockNo < USER_BLOCKS_PER_DIE; virtualBlockNo++)
307 {
308 phyBlockNo = Vblock2PblockOfTbsTranslation(virtualBlockNo);
309 remappedPhyBlock = phyBlockMapPtr->phyBlock[dieNo][phyBlockNo].remappedPhyBlock;
310 virtualBlockMapPtr->block[dieNo][virtualBlockNo].bad =
311 phyBlockMapPtr->phyBlock[dieNo][remappedPhyBlock].bad;
312
313 virtualBlockMapPtr->block[dieNo][virtualBlockNo].free = 1;
314 virtualBlockMapPtr->block[dieNo][virtualBlockNo].invalidSliceCnt = 0;
315 virtualBlockMapPtr->block[dieNo][virtualBlockNo].currentPage = 0;
316 virtualBlockMapPtr->block[dieNo][virtualBlockNo].eraseCnt = 0;
317
318 // bad block should not be added to free block list
319 if (virtualBlockMapPtr->block[dieNo][virtualBlockNo].bad)
320 {
321 virtualBlockMapPtr->block[dieNo][virtualBlockNo].prevBlock = BLOCK_NONE;
322 virtualBlockMapPtr->block[dieNo][virtualBlockNo].nextBlock = BLOCK_NONE;
323 }
324 else
325 PutToFbList(dieNo, virtualBlockNo);
326 }
327 }
328}
#define Vblock2PblockOfTbsTranslation(blockNo)
Get the PBA (in total block space) of the given VBN.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ InitCurrentBlockOfDieMap()

void InitCurrentBlockOfDieMap ( )

Get a default free block for each die.

Definition at line 333 of file address_translation.c.

334{
335 unsigned int dieNo, chNo, wayNo;
336
337 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
338 {
341 {
342 // assert(!"[WARNING] There is no free block [WARNING]");
343 chNo = Vdie2PchTranslation(dieNo);
344 wayNo = Vdie2PwayTranslation(dieNo);
345 xil_printf("[WARNING] There is no free block on Ch %d Way %d (Die %d)!\r\n", chNo, wayNo, dieNo);
346 }
347 }
348}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ InitDieMap()

void InitDieMap ( )

Reset the free block list of each die.

This function currently will be called before the BBT being recovered, in other words, when the function is called, the fw don't know which block is free, which block is bad yet. The fw therefore cannot build the V2P mapping and the free block list.

Definition at line 265 of file address_translation.c.

266{
267 unsigned int dieNo;
268
269 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
270 {
274 }
275}
Here is the caller graph for this function:

◆ InitSliceMap()

void InitSliceMap ( )

Initialize Logical and Virtual Slick Map.

This function simply initialize all the slice addresses in the both map to NONE.

Definition at line 116 of file address_translation.c.

117{
118 int sliceAddr;
119 for (sliceAddr = 0; sliceAddr < SLICES_PER_SSD; sliceAddr++)
120 {
123 }
124}
Here is the caller graph for this function:

◆ InvalidateOldVsa()

void InvalidateOldVsa ( unsigned int  logicalSliceAddr)

Invalidate the specified virtual page.

Overwriting cannot perform on physical flash cell, so the fw must handle out-of-place update.

This function is used for invalidate the corresponding physical page of the specified logical page, but in case there was no physical page allocated on the logical page before, we should check if the corresponding physical page exists before doing GC on the invalidated physical page.

Parameters
logicalSliceAddrLSA that specifies the virtual slice to be invalidated.

Definition at line 1083 of file address_translation.c.

1084{
1085 unsigned int virtualSliceAddr, dieNo, blockNo;
1086
1087 virtualSliceAddr = logicalSliceMapPtr->logicalSlice[logicalSliceAddr].virtualSliceAddr;
1088
1089 if (virtualSliceAddr != VSA_NONE)
1090 {
1091 if (virtualSliceMapPtr->virtualSlice[virtualSliceAddr].logicalSliceAddr != logicalSliceAddr)
1092 return;
1093
1094 dieNo = Vsa2VdieTranslation(virtualSliceAddr);
1095 blockNo = Vsa2VblockTranslation(virtualSliceAddr);
1096
1097 // unlink
1098 SelectiveGetFromGcVictimList(dieNo, blockNo);
1099 virtualBlockMapPtr->block[dieNo][blockNo].invalidSliceCnt++;
1101
1102 PutToGcVictimList(dieNo, blockNo, virtualBlockMapPtr->block[dieNo][blockNo].invalidSliceCnt);
1103 }
1104}
#define Vsa2VdieTranslation(virtualSliceAddr)
#define Vsa2VblockTranslation(virtualSliceAddr)
void PutToGcVictimList(unsigned int dieNo, unsigned int blockNo, unsigned int invalidSliceCnt)
void SelectiveGetFromGcVictimList(unsigned int dieNo, unsigned int blockNo)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PutToFbList()

void PutToFbList ( unsigned int  dieNo,
unsigned int  blockNo 
)

Append the given virtual block to the free block list of its die.

Parameters
dieNothe die number of the given block.
blockNoVBN of the specified block.

Definition at line 1161 of file address_translation.c.

1162{
1164 {
1166 virtualBlockMapPtr->block[dieNo][blockNo].nextBlock = BLOCK_NONE;
1168 virtualDieMapPtr->die[dieNo].tailFreeBlock = blockNo;
1169 }
1170 else
1171 {
1172 virtualBlockMapPtr->block[dieNo][blockNo].prevBlock = BLOCK_NONE;
1173 virtualBlockMapPtr->block[dieNo][blockNo].nextBlock = BLOCK_NONE;
1174 virtualDieMapPtr->die[dieNo].headFreeBlock = blockNo;
1175 virtualDieMapPtr->die[dieNo].tailFreeBlock = blockNo;
1176 }
1177
1179}
Here is the caller graph for this function:

◆ ReadBadBlockTable()

void ReadBadBlockTable ( unsigned int  tempBbtBufAddr[],
unsigned int  tempBbtBufEntrySize 
)

Read the pages that should contain the bad block table.

The bbt of that die is stored in the LSB page (or pages) of the block specified by the BAD_BLOCK_TABLE_INFO_ENTRY::phyBlock, which is default to 0 in InitAddressMap().

Each block on this die (including the extended blocks), use 1 Byte to store the bad block info, so we have to read TOTAL_BLOCKS_PER_DIE / PAGE_SIZE_IN_BYTES pages from the flash.

Todo:
why ECC and row addr dependency can be turned off ??
Parameters
tempBbtBufAddrthe addresses of the bbt of each die.
tempBbtBufEntrySizethe size of the data and metadata region of a page.

Definition at line 365 of file address_translation.c.

366{
367 unsigned int tempPage, reqSlotTag, dieNo;
368 int loop, dataSize;
369
370 loop = 0;
373
374 while (dataSize > 0)
375 {
376 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
377 {
378 reqSlotTag = GetFromFreeReqQ();
379
388
389 reqPoolPtr->reqPool[reqSlotTag].dataBufInfo.addr = tempBbtBufAddr[dieNo] + loop * tempBbtBufEntrySize;
390
395
396 SelectLowLevelReqQ(reqSlotTag);
397 }
398
399 tempPage++;
400 loop++;
402 }
403
404 xil_printf("[INFO] %s: bbt size: %d pages per die.\r\n", __FUNCTION__, loop);
406}
#define Vpage2PlsbPageTranslation(pageNo)
Map the virtual page to the physical LSB page in SLC mode.
#define DATA_SIZE_OF_BAD_BLOCK_TABLE_PER_DIE
#define PlsbPage2VpageTranslation(pageNo)
Get the corresponding virtual page number of the given LSB page number.
#define START_PAGE_NO_OF_BAD_BLOCK_TABLE_BLOCK
The start physical page number that stored the bbt of this die.
#define BYTES_PER_DATA_REGION_OF_PAGE
Definition: ftl_config.h:179
#define REQ_OPT_NAND_ECC_ON
Here is the call graph for this function:
Here is the caller graph for this function:

◆ RecoverBadBlockTable()

void RecoverBadBlockTable ( unsigned int  tempBufAddr)

Read the bbt from flash and re-create if not exists.

This function is used for checking and recover the bad block table from the flash.

To do this, we have several things to do:

  1. Read the flash pages that is used for storing the bbt info
  2. Check whether the specific pages contains the bbt info

    a. if bbt info exists, check the bad blocks in this die. b. otherwise, mark the bbt this die should be rebuilt.

  3. Rebuilt bbt if needed
    1. read all the blocks in the target dies
    2. determine whether those blocks are bad blocks
    3. update the temp bbt
  4. Persist the newly created bbt to the flash
Warning
The fw should not always use same page for storing the BBT.
Todo:
Use bitmap and magic number to record bad blocks and identify BBT
Parameters
tempBufAddrthe base address for buffering the pages that contain the bbt.

Definition at line 664 of file address_translation.c.

665{
666 unsigned int chNo, wayNo, dieNo, phyBlockNo;
667 unsigned int bbtMaker, tempBbtBufBaseAddr, tempBbtBufEntrySize, tempReadBufBaseAddr, tempReadBufEntrySize;
668 unsigned int tempBbtBufAddr[USER_DIES]; // buffer addresses for storing the bbt pages
669 unsigned int tempReadBufAddr[USER_DIES]; // buffer addresses for finding bad blocks
670 unsigned char dieState[USER_DIES]; // whether the bbt of this die should be rebuilt
671 unsigned char *bbtTableChecker;
672
673 // data buffer allocation
674 tempBbtBufBaseAddr = tempBufAddr;
676 tempReadBufBaseAddr =
677 tempBbtBufBaseAddr + USER_DIES * USED_PAGES_FOR_BAD_BLOCK_TABLE_PER_DIE * tempBbtBufEntrySize;
678 tempReadBufEntrySize = BYTES_PER_NAND_ROW;
679 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
680 {
681 tempBbtBufAddr[dieNo] =
682 tempBbtBufBaseAddr + dieNo * USED_PAGES_FOR_BAD_BLOCK_TABLE_PER_DIE * tempBbtBufEntrySize;
683 tempReadBufAddr[dieNo] = tempReadBufBaseAddr + dieNo * tempReadBufEntrySize;
684 }
685
686 // read the bbt of each die into bbt buffer
687 ReadBadBlockTable(tempBbtBufAddr, tempBbtBufEntrySize);
688
689 // check bad block tables
691 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
692 {
693 chNo = Vdie2PchTranslation(dieNo);
694 wayNo = Vdie2PwayTranslation(dieNo);
695 bbtTableChecker = (unsigned char *)(tempBbtBufAddr[dieNo]);
696
697 /*
698 * Each block on this die use 1 byte to store the bad block info, but only use 1
699 * bit to indicate whether that block is a bad block. So here just determine if
700 * the bbt exists by checking the first byte.
701 */
702 if ((*bbtTableChecker == BLOCK_STATE_NORMAL) || (*bbtTableChecker == BLOCK_STATE_BAD))
703 {
704 xil_printf("[ bad block table of ch %d way %d exists.]\r\n", Vdie2PchTranslation(dieNo),
705 Vdie2PwayTranslation(dieNo));
706
707 dieState[dieNo] = DIE_STATE_BAD_BLOCK_TABLE_EXIST;
708 for (phyBlockNo = 0; phyBlockNo < TOTAL_BLOCKS_PER_DIE; phyBlockNo++)
709 {
710 bbtTableChecker = (unsigned char *)(tempBbtBufAddr[dieNo] + phyBlockNo);
711
712 phyBlockMapPtr->phyBlock[dieNo][phyBlockNo].bad = *bbtTableChecker;
713 if (phyBlockMapPtr->phyBlock[dieNo][phyBlockNo].bad == BLOCK_STATE_BAD)
714 pr_info("C/W[%u/%u]: bad block at PBlk %u (0x%x)", chNo, wayNo, phyBlockNo, phyBlockNo);
715 }
716
717 xil_printf("[ bad blocks of ch %d way %d are checked. ]\r\n", Vdie2PchTranslation(dieNo),
718 Vdie2PwayTranslation(dieNo));
719 }
720 else
721 {
722 xil_printf("[ bad block table of ch %d way %d does not exist.]\r\n", Vdie2PchTranslation(dieNo),
723 Vdie2PwayTranslation(dieNo));
724 dieState[dieNo] = DIE_STATE_BAD_BLOCK_TABLE_NOT_EXIST;
726 }
727 }
728
729 // Create bbt for those dies whose bbt not found.
730 if (bbtMaker == BAD_BLOCK_TABLE_MAKER_TRIGGER)
731 {
732 FindBadBlock(dieState, tempBbtBufAddr, tempBbtBufEntrySize, tempReadBufAddr, tempReadBufEntrySize);
733 SaveBadBlockTable(dieState, tempBbtBufAddr, tempBbtBufEntrySize);
734 }
735
736 // bbt initialization done, no need to update the bbt until new bad block found
737 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
739}
void FindBadBlock(unsigned char dieState[], unsigned int tempBbtBufAddr[], unsigned int tempBbtBufEntrySize, unsigned int tempReadBufAddr[], unsigned int tempReadBufEntrySize)
Build the bbt for those dies whose bbt doesn't exist.
void SaveBadBlockTable(unsigned char dieState[], unsigned int tempBbtBufAddr[], unsigned int tempBbtBufEntrySize)
Persist the newly created bbt for those dies whose bbt not exists.
void ReadBadBlockTable(unsigned int tempBbtBufAddr[], unsigned int tempBbtBufEntrySize)
Read the pages that should contain the bad block table.
#define DIE_STATE_BAD_BLOCK_TABLE_EXIST
#define DIE_STATE_BAD_BLOCK_TABLE_NOT_EXIST
#define USED_PAGES_FOR_BAD_BLOCK_TABLE_PER_DIE
#define BAD_BLOCK_TABLE_MAKER_IDLE
#define BAD_BLOCK_TABLE_MAKER_TRIGGER
#define BYTES_PER_NAND_ROW
Definition: ftl_config.h:144
#define BYTES_PER_SPARE_REGION_OF_PAGE
Definition: ftl_config.h:180
Here is the call graph for this function:
Here is the caller graph for this function:

◆ RemapBadBlock()

void RemapBadBlock ( )

Try to remap the bad blocks in the main block space.

If the number of user blocks is configured as less than the number of total blocks on each die, there may be some redundant blocks can be used for replacing the bad blocks in the user blocks.

Therefore, this function will sequentially search the redundant (reserved) blocks on each die and try to replace the bad block in the user blocks on that die with the reserved blocks.

Todo:
use array to simplify lun0 and lun1

Definition at line 139 of file address_translation.c.

140{
141 unsigned int blockNo, dieNo, remapFlag, maxBadBlockCount;
142 unsigned int reservedBlockOfLun0[USER_DIES]; // PBA of first reserved block on lun 0
143 unsigned int reservedBlockOfLun1[USER_DIES]; // PBA of first reserved block on lun 1
144 unsigned int badBlockCount[USER_DIES]; // non-remmappable blocks on this die
145
146 xil_printf("Bad block remapping start...\r\n");
147
148 // view the blocks after user blocks as reserved blocks for remapping
149 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
150 {
151 reservedBlockOfLun0[dieNo] = USER_BLOCKS_PER_LUN;
152 reservedBlockOfLun1[dieNo] = TOTAL_BLOCKS_PER_LUN + USER_BLOCKS_PER_LUN;
153 badBlockCount[dieNo] = 0;
154 }
155
156 for (blockNo = 0; blockNo < USER_BLOCKS_PER_LUN; blockNo++)
157 {
158 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
159 {
160 // lun0
161 if (phyBlockMapPtr->phyBlock[dieNo][blockNo].bad)
162 {
163 if (reservedBlockOfLun0[dieNo] < TOTAL_BLOCKS_PER_LUN)
164 {
165 // sequentially find a non-bad reserved block to replace the bad user block
166 remapFlag = 1;
167 while (phyBlockMapPtr->phyBlock[dieNo][reservedBlockOfLun0[dieNo]].bad)
168 {
169 reservedBlockOfLun0[dieNo]++;
170
171 // no available non-bad reserved block
172 if (reservedBlockOfLun0[dieNo] >= TOTAL_BLOCKS_PER_LUN)
173 {
174 remapFlag = 0;
175 break;
176 }
177 }
178
179 // whether we found a free block to replace the bad block
180 if (remapFlag)
181 {
182 phyBlockMapPtr->phyBlock[dieNo][blockNo].remappedPhyBlock = reservedBlockOfLun0[dieNo];
183 reservedBlockOfLun0[dieNo]++;
184 }
185 else
186 {
187 xil_printf("No reserved block - Ch %d Way %d virtualBlock %d is bad block \r\n",
188 Vdie2PchTranslation(dieNo), Vdie2PwayTranslation(dieNo), blockNo);
189 badBlockCount[dieNo]++;
190 }
191 }
192 else
193 {
194 xil_printf("No reserved block - Ch %d Way %d virtualBlock %d is bad block \r\n",
195 Vdie2PchTranslation(dieNo), Vdie2PwayTranslation(dieNo), blockNo);
196 badBlockCount[dieNo]++;
197 }
198 }
199
200 if (LUNS_PER_DIE > 1)
201 {
202 // lun1
203 if (phyBlockMapPtr->phyBlock[dieNo][blockNo + TOTAL_BLOCKS_PER_LUN].bad)
204 {
205 if (reservedBlockOfLun1[dieNo] < TOTAL_BLOCKS_PER_DIE)
206 {
207 remapFlag = 1;
208 while (phyBlockMapPtr->phyBlock[dieNo][reservedBlockOfLun1[dieNo]].bad)
209 {
210 reservedBlockOfLun1[dieNo]++;
211 if (reservedBlockOfLun1[dieNo] >= TOTAL_BLOCKS_PER_DIE)
212 {
213 remapFlag = 0;
214 break;
215 }
216 }
217
218 if (remapFlag)
219 {
221 reservedBlockOfLun1[dieNo];
222 reservedBlockOfLun1[dieNo]++;
223 }
224 else
225 {
226 xil_printf("No reserved block - Ch %x Way %x virtualBlock %d is bad block \r\n",
228 blockNo + USER_BLOCKS_PER_LUN);
229 badBlockCount[dieNo]++;
230 }
231 }
232 else
233 {
234 xil_printf("No reserved block - Ch %x Way %x virtualBlock %d is bad block \r\n",
236 blockNo + USER_BLOCKS_PER_LUN);
237 badBlockCount[dieNo]++;
238 }
239 }
240 }
241 }
242 }
243
244 xil_printf("Bad block remapping end\r\n");
245
246 maxBadBlockCount = 0;
247 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
248 {
249 xil_printf("[WARNING!!!] There are %d bad blocks on Ch %d Way %d.\r\n", badBlockCount[dieNo],
251 if (maxBadBlockCount < badBlockCount[dieNo])
252 maxBadBlockCount = badBlockCount[dieNo];
253 }
254
255 mbPerbadBlockSpace = maxBadBlockCount * USER_DIES * MB_PER_BLOCK;
256}
unsigned int mbPerbadBlockSpace
#define TOTAL_BLOCKS_PER_LUN
Definition: ftl_config.h:155
#define USER_BLOCKS_PER_LUN
Definition: ftl_config.h:206
#define LUNS_PER_DIE
Definition: ftl_config.h:160
#define MB_PER_BLOCK
Definition: ftl_config.h:237
Here is the caller graph for this function:

◆ SaveBadBlockTable()

void SaveBadBlockTable ( unsigned char  dieState[],
unsigned int  tempBbtBufAddr[],
unsigned int  tempBbtBufEntrySize 
)

Persist the newly created bbt for those dies whose bbt not exists.

Similar to ReadBadBlockTable, but now we have to write the bbt to the pages.

Note
The bbt should be saved at lsb pages.
Bug:
why dataSize++ ?? tempPage++ ?
Parameters
dieStatethe flags that indicate whether the bbt of that die should be rebuilt.
tempBbtBufAddrthe addresses of the bbt of each die.
tempBbtBufEntrySizethe size of the data and metadata region of a page.

Definition at line 563 of file address_translation.c.

564{
565 unsigned int dieNo, reqSlotTag;
566 int loop, dataSize, tempPage;
567
568 loop = 0;
570 tempPage =
571 PlsbPage2VpageTranslation(START_PAGE_NO_OF_BAD_BLOCK_TABLE_BLOCK); // bad block table is saved at lsb pages
572
573 while (dataSize > 0)
574 {
575 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
576 if ((dieState[dieNo] == DIE_STATE_BAD_BLOCK_TABLE_NOT_EXIST) ||
577 (dieState[dieNo] == DIE_STATE_BAD_BLOCK_TABLE_UPDATE))
578 {
579 /* before writing the bbt to flash, we should do erase first. */
580 if (loop == 0)
581 {
582 reqSlotTag = GetFromFreeReqQ();
583
591
596 reqPoolPtr->reqPool[reqSlotTag].nandInfo.physicalPage = 0; // dummy
597
598 SelectLowLevelReqQ(reqSlotTag);
599 }
600
601 reqSlotTag = GetFromFreeReqQ();
602
611
612 reqPoolPtr->reqPool[reqSlotTag].dataBufInfo.addr =
613 tempBbtBufAddr[dieNo] + loop * tempBbtBufEntrySize;
614
619
620 SelectLowLevelReqQ(reqSlotTag);
621 }
622
623 loop++;
624 dataSize++;
626 }
627
629
630 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
631 if (dieState[dieNo] == DIE_STATE_BAD_BLOCK_TABLE_NOT_EXIST)
632 xil_printf("[ bad block table of Ch %d Way %d is saved. ]\r\n", dieNo % USER_CHANNELS,
633 dieNo / USER_CHANNELS);
634}
#define DIE_STATE_BAD_BLOCK_TABLE_UPDATE
#define REQ_CODE_WRITE
Here is the call graph for this function:
Here is the caller graph for this function:

◆ UpdateBadBlockTableForGrownBadBlock()

void UpdateBadBlockTableForGrownBadBlock ( unsigned int  tempBufAddr)

Update the bad block table and persist to the specified block.

A little bit similar to FindBadBlock(), but this function use the bad block flag to mark bad blocks, instead of reading the bad block marks.

Parameters
tempBufAddrthe base address of the bad block tables.

Definition at line 1254 of file address_translation.c.

1255{
1256 unsigned int dieNo, phyBlockNo, tempBbtBufBaseAddr, tempBbtBufEntrySize;
1257 unsigned int tempBbtBufAddr[USER_DIES];
1258 unsigned char dieState[USER_DIES];
1259 unsigned char *bbtUpdater;
1260
1261 // data buffer allocation
1262 tempBbtBufBaseAddr = tempBufAddr;
1264 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
1265 tempBbtBufAddr[dieNo] =
1266 tempBbtBufBaseAddr + dieNo * USED_PAGES_FOR_BAD_BLOCK_TABLE_PER_DIE * tempBbtBufEntrySize;
1267
1268 // create new bad block table
1269 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
1270 {
1272 {
1273 for (phyBlockNo = 0; phyBlockNo < TOTAL_BLOCKS_PER_DIE; phyBlockNo++)
1274 {
1275 bbtUpdater = (unsigned char *)(tempBbtBufAddr[dieNo] + phyBlockNo);
1276
1277 if (phyBlockNo != bbtInfoMapPtr->bbtInfo[dieNo].phyBlock)
1278 *bbtUpdater = phyBlockMapPtr->phyBlock[dieNo][phyBlockNo].bad;
1279 else
1280 *bbtUpdater = BLOCK_STATE_NORMAL;
1281 }
1282
1283 dieState[dieNo] = DIE_STATE_BAD_BLOCK_TABLE_UPDATE;
1284 }
1285 else
1286 dieState[dieNo] = DIE_STATE_BAD_BLOCK_TABLE_HOLD;
1287 }
1288
1289 // update bad block tables in flash
1290 SaveBadBlockTable(dieState, tempBbtBufAddr, tempBbtBufEntrySize);
1291}
#define DIE_STATE_BAD_BLOCK_TABLE_HOLD
#define BBT_INFO_GROWN_BAD_UPDATE_BOOKED
Here is the call graph for this function:
Here is the caller graph for this function:

◆ UpdatePhyBlockMapForGrownBadBlock()

void UpdatePhyBlockMapForGrownBadBlock ( unsigned int  dieNo,
unsigned int  phyBlockNo 
)

Mark the given physical block bad block and update the bbt later.

Parameters
dieNothe die number of the given block.
phyBlockNothe physical address of the block to be marked as bad block.

Definition at line 1240 of file address_translation.c.

Here is the caller graph for this function:

Variable Documentation

◆ bbtInfoMapPtr

Definition at line 59 of file address_translation.c.

◆ logicalSliceMapPtr

P_LOGICAL_SLICE_MAP logicalSliceMapPtr

Definition at line 54 of file address_translation.c.

◆ mbPerbadBlockSpace

unsigned int mbPerbadBlockSpace

Definition at line 62 of file address_translation.c.

◆ phyBlockMapPtr

P_PHY_BLOCK_MAP phyBlockMapPtr

Definition at line 58 of file address_translation.c.

◆ sliceAllocationTargetDie

unsigned char sliceAllocationTargetDie

Definition at line 61 of file address_translation.c.

◆ virtualBlockMapPtr

P_VIRTUAL_BLOCK_MAP virtualBlockMapPtr

Definition at line 56 of file address_translation.c.

◆ virtualDieMapPtr

P_VIRTUAL_DIE_MAP virtualDieMapPtr

Definition at line 57 of file address_translation.c.

◆ virtualSliceMapPtr

P_VIRTUAL_SLICE_MAP virtualSliceMapPtr

Definition at line 55 of file address_translation.c.