OpenSSD Cosmos+ Platform Firmware  0.0.2
The firmware of Cosmos+ OpenSSD Platform for TOSHIBA nand flash module.
address_translation.c
Go to the documentation of this file.
1
2// address_translation.c for Cosmos+ OpenSSD
3// Copyright (c) 2017 Hanyang University ENC Lab.
4// Contributed by Yong Ho Song <yhsong@enc.hanyang.ac.kr>
5// Jaewook Kwak <jwkwak@enc.hanyang.ac.kr>
6// Sangjin Lee <sjlee@enc.hanyang.ac.kr>
7//
8// This file is part of Cosmos+ OpenSSD.
9//
10// Cosmos+ OpenSSD is free software; you can redistribute it and/or modify
11// it under the terms of the GNU General Public License as published by
12// the Free Software Foundation; either version 3, or (at your option)
13// any later version.
14//
15// Cosmos+ OpenSSD is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18// See the GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Cosmos+ OpenSSD; see the file COPYING.
22// If not, see <http://www.gnu.org/licenses/>.
24
26// Company: ENC Lab. <http://enc.hanyang.ac.kr>
27// Engineer: Jaewook Kwak <jwkwak@enc.hanyang.ac.kr>
28//
29// Project Name: Cosmos+ OpenSSD
30// Design Name: Cosmos+ Firmware
31// Module Name: Address Translator
32// File Name: address translation.c
33//
34// Version: v1.0.0
35//
36// Description:
37// - translate address between address space of host system and address space of NAND device
38// - manage bad blocks in NAND device
40
42// Revision History:
43//
44// * v1.0.0
45// - First draft
47
48#include <assert.h>
49#include "debug.h"
50#include "xil_printf.h"
51
52#include "memory_map.h"
53
60
61unsigned char sliceAllocationTargetDie; // the destination die of next slice command
62unsigned int mbPerbadBlockSpace;
63
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}
110
117{
118 int sliceAddr;
119 for (sliceAddr = 0; sliceAddr < SLICES_PER_SSD; sliceAddr++)
120 {
123 }
124}
125
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}
257
266{
267 unsigned int dieNo;
268
269 for (dieNo = 0; dieNo < USER_DIES; dieNo++)
270 {
274 }
275}
276
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}
329
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}
349
365void ReadBadBlockTable(unsigned int tempBbtBufAddr[], unsigned int tempBbtBufEntrySize)
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}
407
433void FindBadBlock(unsigned char dieState[], unsigned int tempBbtBufAddr[], unsigned int tempBbtBufEntrySize,
434 unsigned int tempReadBufAddr[], unsigned int tempReadBufEntrySize)
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}
549
563void SaveBadBlockTable(unsigned char dieState[], unsigned int tempBbtBufAddr[], unsigned int tempBbtBufEntrySize)
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}
635
664void RecoverBadBlockTable(unsigned int tempBufAddr)
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}
740
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}
773
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}
804
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}
860
861/* -------------------------------------------------------------------------- */
862/* Utility Functions for Translation */
863/* -------------------------------------------------------------------------- */
864
871unsigned int AddrTransRead(unsigned int logicalSliceAddr)
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}
887
903unsigned int AddrTransWrite(unsigned int logicalSliceAddr)
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}
921
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}
1007
1008unsigned int FindFreeVirtualSliceForGc(unsigned int copyTargetDieNo, unsigned int victimBlockNo)
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}
1039
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}
1069
1083void InvalidateOldVsa(unsigned int logicalSliceAddr)
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}
1105
1122void EraseBlock(unsigned int dieNo, unsigned int blockNo)
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}
1154
1161void PutToFbList(unsigned int dieNo, unsigned int blockNo)
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}
1180
1194unsigned int GetFromFbList(unsigned int dieNo, unsigned int getFreeBlockOption)
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}
1233
1240void UpdatePhyBlockMapForGrownBadBlock(unsigned int dieNo, unsigned int phyBlockNo)
1241{
1242 phyBlockMapPtr->phyBlock[dieNo][phyBlockNo].bad = BLOCK_STATE_BAD;
1244}
1245
1254void UpdateBadBlockTableForGrownBadBlock(unsigned int tempBufAddr)
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}
unsigned char sliceAllocationTargetDie
unsigned int AddrTransRead(unsigned int logicalSliceAddr)
Get the virtual slice address of the given logical slice.
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.
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 InitSliceMap()
Initialize Logical and Virtual Slick Map.
void InitDieMap()
Reset the free block list of each die.
void InitAddressMap()
Initialize the translation related maps.
void SaveBadBlockTable(unsigned char dieState[], unsigned int tempBbtBufAddr[], unsigned int tempBbtBufEntrySize)
Persist the newly created bbt for those dies whose bbt not exists.
P_BAD_BLOCK_TABLE_INFO_MAP bbtInfoMapPtr
void UpdatePhyBlockMapForGrownBadBlock(unsigned int dieNo, unsigned int phyBlockNo)
Mark the given physical block bad block and update the bbt later.
void ReadBadBlockTable(unsigned int tempBbtBufAddr[], unsigned int tempBbtBufEntrySize)
Read the pages that should contain the bad block table.
P_LOGICAL_SLICE_MAP logicalSliceMapPtr
void EraseUserBlockSpace()
Erase all the non-bad main blocks on user dies and wait until done.
unsigned int FindFreeVirtualSlice()
Select a free physical page (virtual slice).
void InitBlockMap()
Create V2P table and free block list.
void EraseBlock(unsigned int dieNo, unsigned int blockNo)
Erase the specified block of the specified die and discard its LSAs.
P_VIRTUAL_DIE_MAP virtualDieMapPtr
void PutToFbList(unsigned int dieNo, unsigned int blockNo)
Append the given virtual block to the free block list of its die.
void UpdateBadBlockTableForGrownBadBlock(unsigned int tempBufAddr)
Update the bad block table and persist to the specified block.
void EraseTotalBlockSpace()
Erase all the blocks on user dies and wait until done.
void InitCurrentBlockOfDieMap()
Get a default free block for each die.
P_PHY_BLOCK_MAP phyBlockMapPtr
unsigned int FindFreeVirtualSliceForGc(unsigned int copyTargetDieNo, unsigned int victimBlockNo)
void InvalidateOldVsa(unsigned int logicalSliceAddr)
Invalidate the specified virtual page.
P_VIRTUAL_SLICE_MAP virtualSliceMapPtr
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.
void InitBlockDieMap()
Build the bad block table and V2P block mapping of each user die.
P_VIRTUAL_BLOCK_MAP virtualBlockMapPtr
unsigned int mbPerbadBlockSpace
unsigned int AddrTransWrite(unsigned int logicalSliceAddr)
Assign a new virtual (physical) page to the specified logical page.
struct _PHY_BLOCK_MAP * P_PHY_BLOCK_MAP
#define Vpage2PlsbPageTranslation(pageNo)
Map the virtual page to the physical LSB page in SLC mode.
#define GET_FREE_BLOCK_NORMAL
#define BLOCK_STATE_NORMAL
struct _BAD_BLOCK_TABLE_INFO_MAP * P_BAD_BLOCK_TABLE_INFO_MAP
#define BBT_INFO_GROWN_BAD_UPDATE_NONE
#define DATA_SIZE_OF_BAD_BLOCK_TABLE_PER_DIE
#define Vdie2PchTranslation(dieNo)
#define DIE_STATE_BAD_BLOCK_TABLE_EXIST
#define DIE_STATE_BAD_BLOCK_TABLE_NOT_EXIST
#define VSA_FAIL
#define Vorg2VsaTranslation(dieNo, blockNo, pageNo)
Translate virtual NAND organization (location vector) into VSA.
#define USED_PAGES_FOR_BAD_BLOCK_TABLE_PER_DIE
#define Pcw2VdieTranslation(chNo, wayNo)
Get die number from physical channel number and way number.
#define RESERVED_FREE_BLOCK_COUNT
#define LSA_NONE
#define GET_FREE_BLOCK_GC
#define BLOCK_NONE
#define Vsa2VdieTranslation(virtualSliceAddr)
#define BLOCK_FAIL
#define CLEAN_DATA_IN_BYTE
#define PlsbPage2VpageTranslation(pageNo)
Get the corresponding virtual page number of the given LSB page number.
#define BAD_BLOCK_TABLE_MAKER_IDLE
struct _VIRTUAL_SLICE_MAP * P_VIRTUAL_SLICE_MAP
#define Vblock2PblockOfTbsTranslation(blockNo)
Get the PBA (in total block space) of the given VBN.
#define DIE_STATE_BAD_BLOCK_TABLE_UPDATE
struct _VIRTUAL_BLOCK_MAP * P_VIRTUAL_BLOCK_MAP
#define VSA_NONE
struct _LOGICAL_SLICE_MAP * P_LOGICAL_SLICE_MAP
#define BLOCK_STATE_BAD
#define Vsa2VblockTranslation(virtualSliceAddr)
#define BAD_BLOCK_TABLE_MAKER_TRIGGER
#define Vdie2PwayTranslation(dieNo)
#define START_PAGE_NO_OF_BAD_BLOCK_TABLE_BLOCK
The start physical page number that stored the bbt of this die.
#define DIE_STATE_BAD_BLOCK_TABLE_HOLD
struct _VIRTUAL_DIE_MAP * P_VIRTUAL_DIE_MAP
#define BBT_INFO_GROWN_BAD_UPDATE_BOOKED
char inbyte()
Definition: bsp.c:4
#define pr_info(fmt,...)
Definition: debug.h:86
#define BYTES_PER_DATA_REGION_OF_PAGE
Definition: ftl_config.h:179
#define USER_CHANNELS
Definition: ftl_config.h:207
#define TOTAL_BLOCKS_PER_LUN
Definition: ftl_config.h:155
#define BYTES_PER_NAND_ROW
Definition: ftl_config.h:144
#define BYTES_PER_SPARE_REGION_OF_PAGE
Definition: ftl_config.h:180
#define BAD_BLOCK_MARK_BYTE1
Definition: ftl_config.h:168
#define USER_DIES
Definition: ftl_config.h:219
#define USER_BLOCKS_PER_LUN
Definition: ftl_config.h:206
#define BAD_BLOCK_MARK_BYTE0
Definition: ftl_config.h:167
#define USER_BLOCKS_PER_DIE
Definition: ftl_config.h:233
#define BAD_BLOCK_MARK_PAGE0
Definition: ftl_config.h:165
#define LUNS_PER_DIE
Definition: ftl_config.h:160
#define BAD_BLOCK_MARK_PAGE1
Definition: ftl_config.h:166
#define MB_PER_BLOCK
Definition: ftl_config.h:237
#define USER_PAGES_PER_BLOCK
Definition: ftl_config.h:221
#define TOTAL_BLOCKS_PER_DIE
Definition: ftl_config.h:163
#define USER_WAYS
Definition: ftl_config.h:208
#define SLICES_PER_SSD
Definition: ftl_config.h:231
void PutToGcVictimList(unsigned int dieNo, unsigned int blockNo, unsigned int invalidSliceCnt)
void GarbageCollection(unsigned int dieNo)
void SelectiveGetFromGcVictimList(unsigned int dieNo, unsigned int blockNo)
#define RESERVED_DATA_BUFFER_BASE_ADDR
Definition: memory_map.h:85
#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 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_BLOCK_SPACE_TOTAL
#define REQ_OPT_NAND_ECC_WARNING_OFF
#define REQ_OPT_DATA_BUF_ADDR
#define REQ_CODE_WRITE
#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_CODE_READ
#define REQ_OPT_NAND_ADDR_PHY_ORG
#define REQ_TYPE_NAND
#define REQ_OPT_ROW_ADDR_DEPENDENCY_NONE
#define REQ_OPT_NAND_ECC_ON
#define REQ_OPT_NAND_ECC_OFF
void SyncAllLowLevelReqDone()
Do schedule until all the requests are done.
void SelectLowLevelReqQ(unsigned int reqSlotTag)
Dispatch given NVMe/NAND request to corresponding request queue.
unsigned int phyBlock
unsigned int grownBadUpdate
BAD_BLOCK_TABLE_INFO_ENTRY bbtInfo[USER_DIES]
unsigned int addr
unsigned int virtualSliceAddr
The Logical -> Virtual Slice Address mapping table.
LOGICAL_SLICE_ENTRY logicalSlice[SLICES_PER_SSD]
unsigned int physicalBlock
unsigned int programmedPageCnt
unsigned int physicalWay
unsigned int physicalPage
unsigned int virtualSliceAddr
unsigned int physicalCh
unsigned int bad
unsigned int remappedPhyBlock
The metadata table for all the physical blocks.
PHY_BLOCK_ENTRY phyBlock[USER_DIES][TOTAL_BLOCKS_PER_DIE]
unsigned int nandEccWarning
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
unsigned int nandEcc
SSD_REQ_FORMAT reqPool[AVAILABLE_OUNTSTANDING_REQ_COUNT]
REQ_OPTION reqOpt
DATA_BUF_INFO dataBufInfo
unsigned int reqCode
NAND_INFO nandInfo
unsigned int reqType
unsigned int currentPage
unsigned int prevBlock
unsigned int eraseCnt
unsigned int bad
unsigned int free
unsigned int nextBlock
unsigned int invalidSliceCnt
The block metadata table for all the blocks.
VIRTUAL_BLOCK_ENTRY block[USER_DIES][USER_BLOCKS_PER_DIE]
unsigned int headFreeBlock
unsigned int currentBlock
unsigned int freeBlockCnt
unsigned int tailFreeBlock
The metadata table for all user dies.
VIRTUAL_DIE_ENTRY die[USER_DIES]
unsigned int logicalSliceAddr
The Virtual -> Logical Slice mapping table.
VIRTUAL_SLICE_ENTRY virtualSlice[SLICES_PER_SSD]