FTL的整个流程如下:

下面先来看写的流程:

写的代码如下:

 1         if((hostCmd.reqInfo.Cmd == IDE_COMMAND_WRITE_DMA) ||  (hostCmd.reqInfo.Cmd == IDE_COMMAND_WRITE))
 2             {
 3 //                xil_printf("write(%d, %d)\r\n", hostCmd.reqInfo.CurSect, hostCmd.reqInfo.ReqSect);
 4
 5                 PrePmRead(&hostCmd, RAM_DISK_BASE_ADDR);
 6
 7                 deviceAddr = RAM_DISK_BASE_ADDR + (hostCmd.reqInfo.CurSect % SECTOR_NUM_PER_PAGE)*SECTOR_SIZE;
 8                 reqSize = hostCmd.reqInfo.ReqSect * SECTOR_SIZE;
 9                 scatterLength = hostCmd.reqInfo.HostScatterNum;
10
11                 DmaHostToDevice(&hostCmd, deviceAddr, reqSize, scatterLength);
12
13                 PmWrite(&hostCmd, RAM_DISK_BASE_ADDR);
14
15                 CompleteCmd(&hostCmd);
16             }

首先来看PrePmRead,其中最开始会涉及一个FlushPageBuf函数,FlushPageBuf里面有个FindFreePage函数,所以我们先分析FindFreePage函数的功能

lpn = hostCmd->reqInfo.CurSect / SECTOR_NUM_PER_PAGE;

u32 dieNo = lpn % DIE_NUM;

这里传入一个dieNo参数

 1 int FindFreePage(u32 dieNo)
 2 {
 3     blockMap = (struct bmArray*)(BLOCK_MAP_ADDR);
 4     dieBlock = (struct dieArray*)(DIE_MAP_ADDR);
 5
 6     if(blockMap->bmEntry[dieNo][dieBlock->dieEntry[dieNo].currentBlock].currentPage == PAGE_NUM_PER_BLOCK-1)    //当前块已经写完最后一页,则用下一个block
 7     {
 8         dieBlock->dieEntry[dieNo].currentBlock++;
 9
10         int i;
11         for(i=dieBlock->dieEntry[dieNo].currentBlock ; i<(dieBlock->dieEntry[dieNo].currentBlock + BLOCK_NUM_PER_DIE) ; i++)  /*遍历整个die的所有block,到结尾之后又从开始找,直到找到一个可用的block*/
12         {
13             if((blockMap->bmEntry[dieNo][i % BLOCK_NUM_PER_DIE].free) && (!blockMap->bmEntry[dieNo][i % BLOCK_NUM_PER_DIE].bad))  //块free且不是坏块就可用
14             {
15                 blockMap->bmEntry[dieNo][i % BLOCK_NUM_PER_DIE].free = 0;
16                 dieBlock->dieEntry[dieNo].currentBlock = i % BLOCK_NUM_PER_DIE;
17
18 //                xil_printf("allocated free block: %4d at %d-%d\r\n", dieBlock->dieEntry[dieNo].currentBlock, dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
19
20                 return dieBlock->dieEntry[dieNo].currentBlock * PAGE_NUM_PER_BLOCK;      //返回页号
21             }
22         }
23
24         dieBlock->dieEntry[dieNo].currentBlock = GarbageCollection(dieNo);    //整个die没有可用块之后就进行垃圾回收
25
26 //        xil_printf("allocated free block by GC: %4d at %d-%d\r\n", dieBlock->dieEntry[dieNo].currentBlock, dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
27
28         return (dieBlock->dieEntry[dieNo].currentBlock * PAGE_NUM_PER_BLOCK) + blockMap->bmEntry[dieNo][dieBlock->dieEntry[dieNo].currentBlock].currentPage;
29     }
30     else    //当前块还有页可用就直接接着上一页继续写
31     {
32         blockMap->bmEntry[dieNo][dieBlock->dieEntry[dieNo].currentBlock].currentPage++;
33         return (dieBlock->dieEntry[dieNo].currentBlock * PAGE_NUM_PER_BLOCK) + blockMap->bmEntry[dieNo][dieBlock->dieEntry[dieNo].currentBlock].currentPage;
34     }
35 }

由此可见,FindFreePage这个函数其实就是找一个可用的页,没有空间了就进行垃圾回收操作

接下来看上一级的函数FlushPageBuf

 1 void FlushPageBuf(u32 lpn, u32 bufAddr)
 2 {
 3     if (lpn == 0xffffffff)            //最开始page缓存内是没有东西的,所以无需flush
 4         return;
 5
 6     u32 dieNo = lpn % DIE_NUM;          //计算出die number
 7     u32 dieLpn = lpn / DIE_NUM;          //计算出lpn在die中是第几个lpn,可以理解为die0上是lpn0,lpn16……对应为dieLpn0,dieLpn1
 8     u32 ppn = pageMap->pmEntry[dieNo][dieLpn].ppn;
 9
10     if (ppn == 0xffffffff)    //表示page缓存还没有写入ppn
11     {
12         u32 freePageNo = FindFreePage(dieNo);
13
14 //        xil_printf("free page: %6d(%d, %d, %4d)\r\n", freePageNo, dieNo%CHANNEL_NUM, dieNo/CHANNEL_NUM, freePageNo/PAGE_NUM_PER_BLOCK);
15
16         WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
17         SsdProgram(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, freePageNo, bufAddr);
18         WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
19
20         // pageMap update
21         pageMap->pmEntry[dieNo][dieLpn].ppn = freePageNo;
22         pageMap->pmEntry[dieNo][freePageNo].lpn = dieLpn;
23     }
24 }

继续来看再上一级PrePmRead函数

int PrePmRead(P_HOST_CMD hostCmd, u32 bufferAddr)
{u32 lpn;u32 dieNo;u32 dieLpn;pageMap = (struct pmArray*)(PAGE_MAP_ADDR);lpn = hostCmd->reqInfo.CurSect / SECTOR_NUM_PER_PAGE;if (lpn != pageBufLpn)  //新的请求和上个请求不是同一个lpn{FlushPageBuf(pageBufLpn, bufferAddr);

上面这一段进行了FlushPageBuf操作

        if((((hostCmd->reqInfo.CurSect)%SECTOR_NUM_PER_PAGE) != 0)|| ((hostCmd->reqInfo.CurSect / SECTOR_NUM_PER_PAGE) == (((hostCmd->reqInfo.CurSect)+(hostCmd->reqInfo.ReqSect))/SECTOR_NUM_PER_PAGE))){dieNo = lpn % DIE_NUM;dieLpn = lpn / DIE_NUM;if(pageMap->pmEntry[dieNo][dieLpn].ppn != 0xffffffff){
//                xil_printf("PrePmRead pdie, ppn = %d, %d\r\n", dieNo, pageMap->pmEntry[dieNo][dieLpn].ppn);
WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);SsdRead(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, pageMap->pmEntry[dieNo][dieLpn].ppn, bufferAddr);WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);pageBufLpn = lpn;}}}

疑问:这个判断条件第一个是请求开端不要是每个lpn的开始,第二个是请求的大小在同一页,那么这个条件就是只要满足不是请求跨页且从一个lpn的开始的就进入判断?有什么实际意义呢?

答:这里很关键,涉及一段数据,假如开始的地方没有和page对齐的话,那么在每个page里面请求开始前面的数据就得先读出来,如果是缓存命中的话,就无需操作,因为可以直接修改,那么没命中的话,即使是page对齐了,如果数据没有跨页的话,也还是要读出来,不然请求末尾内page的内容就丢失了。如下图

假如我要改写345678这些数据,因为数据是按页保存的,所以我只修改这些数据的话,我还得绿色部分的读取出来,然后修改后一起保存到一个页里面,所以请求的开始的lpn如果不是页对齐,我就得read-modify-write,同理,即使页对齐了,但是数据不足一页,那么一页后面几项数据也得先读出来。如果对齐且大小刚好等于一页的话,if失败,这个时候一页也是刚好可以直接修改。至于中间页的数据,本来就是一整页的,所以直接把原来的页无效,然后写入新页即可。这里这个判断条件是在页缓存没有命中的情况下,如果命中了,因为此页还没有刷新到nandflash,所以就无需取出而直接在SDRAM里面修改就行了

    if(((((hostCmd->reqInfo.CurSect)+(hostCmd->reqInfo.ReqSect))% SECTOR_NUM_PER_PAGE) != 0)&& ((hostCmd->reqInfo.CurSect / SECTOR_NUM_PER_PAGE) != (((hostCmd->reqInfo.CurSect)+(hostCmd->reqInfo.ReqSect))/SECTOR_NUM_PER_PAGE))){lpn = ((hostCmd->reqInfo.CurSect)+(hostCmd->reqInfo.ReqSect))/SECTOR_NUM_PER_PAGE;dieNo = lpn % DIE_NUM;dieLpn = lpn / DIE_NUM;if(pageMap->pmEntry[dieNo][dieLpn].ppn != 0xffffffff){//            xil_printf("PrePmRead pdie, ppn = %d, %d\r\n", dieNo, pageMap->pmEntry[dieNo][dieLpn].ppn);
WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);SsdRead(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, pageMap->pmEntry[dieNo][dieLpn].ppn,bufferAddr + ((((hostCmd->reqInfo.CurSect)% SECTOR_NUM_PER_PAGE) + hostCmd->reqInfo.ReqSect)/SECTOR_NUM_PER_PAGE*PAGE_SIZE));WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);}}return 0;
}

上面是头,那这一部分就是尾,尾部如果不是请求对齐到page尾的话,那么也会有数据得不到更新,就如同上图lpn1的9到15,要把page内没修改的数据一起读出来更新,这里不仅是要不是结尾,而且是要跨页,这里分几种情况,假如缓存命中,没有跨页的话直接更新缓存就行了,只有跨页了,才需要进行read-modify-write操作;假如缓存没有命中,系统先把之前的缓存flush到nandflash里面,如果此时数据没有跨页的话,那么上面的操作就已经会读取那个页,也就无需下面再多此一举了,具体的写操作可以看下一篇文档。


接下来来看真正的写操作PmWrite

 1 int PmWrite(P_HOST_CMD hostCmd, u32 bufferAddr)
 2 {
 3     u32 tempBuffer = bufferAddr;
 4
 5     u32 lpn = hostCmd->reqInfo.CurSect / SECTOR_NUM_PER_PAGE;
 6
 7     int loop = (hostCmd->reqInfo.CurSect % SECTOR_NUM_PER_PAGE) + hostCmd->reqInfo.ReqSect;
 8
 9     u32 dieNo;
10     u32 dieLpn;
11     u32 freePageNo;
12
13     pageMap = (struct pmArray*)(PAGE_MAP_ADDR);
14
15     // page buffer utilization
16     if (lpn != pageBufLpn)
17         pageBufLpn = lpn;
18
19     UpdateMetaForOverwrite(lpn);
20
21     // pageMap update
22     dieNo = lpn % DIE_NUM;
23     dieLpn = lpn / DIE_NUM;
24     pageMap->pmEntry[dieNo][dieLpn].ppn = 0xffffffff;    //写入一页不立即更新
25
26     lpn++;
27     tempBuffer += PAGE_SIZE;
28     loop -= SECTOR_NUM_PER_PAGE;
29
30     while(loop > 0)  //接下来还有页请求的话,寻找新页,写入,更新页表
31     {
32         dieNo = lpn % DIE_NUM;
33         dieLpn = lpn / DIE_NUM;
34         freePageNo = FindFreePage(dieNo);
35
36 //        xil_printf("free page: %6d(%d, %d, %4d)\r\n", freePageNo, dieNo%CHANNEL_NUM, dieNo/CHANNEL_NUM, freePageNo/PAGE_NUM_PER_BLOCK);
37
38         WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
39         SsdProgram(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, freePageNo, tempBuffer);
40
41         UpdateMetaForOverwrite(lpn);
42
43         // pageMap update
44         pageMap->pmEntry[dieNo][dieLpn].ppn = freePageNo;
45         pageMap->pmEntry[dieNo][freePageNo].lpn = dieLpn;
46
47         lpn++;
48         tempBuffer += PAGE_SIZE;
49         loop -= SECTOR_NUM_PER_PAGE;
50     }
51
52     int i;
53     for(i=0 ; i<DIE_NUM ; ++i)
54         WaitWayFree(i%CHANNEL_NUM, i/CHANNEL_NUM);
55
56     return 0;
57 }


接下来看读

 1 int PmRead(P_HOST_CMD hostCmd, u32 bufferAddr)
 2 {
 3     u32 tempBuffer = bufferAddr;
 4
 5     u32 lpn = hostCmd->reqInfo.CurSect / SECTOR_NUM_PER_PAGE;
 6     int loop = (hostCmd->reqInfo.CurSect % SECTOR_NUM_PER_PAGE) + hostCmd->reqInfo.ReqSect;
 7
 8     u32 dieNo;
 9     u32 dieLpn;
10
11     pageMap = (struct pmArray*)(PAGE_MAP_ADDR);
12
13     if (lpn == pageBufLpn)  //缓存命中,就无需读取第一页,直接就在内存里面
14     {
15         lpn++;
16         tempBuffer += PAGE_SIZE;
17         loop -= SECTOR_NUM_PER_PAGE;
18     }
19     else
20     {
21         dieNo = lpn % DIE_NUM;
22         dieLpn = lpn / DIE_NUM;
23
24         if(pageMap->pmEntry[dieNo][dieLpn].ppn != 0xffffffff)    //防止第一次读空页
25         {
26             FlushPageBuf(pageBufLpn, bufferAddr);
27             pageBufLpn = lpn;
28         }
29     }
30
31     while(loop > 0)    //把接下来的页一次读取出来
32     {
33         dieNo = lpn % DIE_NUM;
34         dieLpn = lpn / DIE_NUM;
35
36 //        xil_printf("requested read lpn = %d\r\n", lpn);
37 //        xil_printf("read pdie, ppn = %d, %d\r\n", dieNo, pageMap->pmEntry[dieNo][dieLpn].ppn);
38
39         if(pageMap->pmEntry[dieNo][dieLpn].ppn != 0xffffffff)
40         {
41 //            xil_printf("read at (%d, %2d, %4x)\r\n", dieNo%CHANNEL_NUM, dieNo/CHANNEL_NUM, pageMap->pmEntry[dieNo][dieLpn].ppn);
42
43             WaitWayFree(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM);
44             SsdRead(dieNo % CHANNEL_NUM, dieNo / CHANNEL_NUM, pageMap->pmEntry[dieNo][dieLpn].ppn, tempBuffer);
45         }
46
47         lpn++;
48         tempBuffer += PAGE_SIZE;
49         loop -= SECTOR_NUM_PER_PAGE;
50     }
51
52     int i;
53     for(i=0 ; i<DIE_NUM ; ++i)
54         WaitWayFree(i%CHANNEL_NUM, i/CHANNEL_NUM);
55
56     return 0;
57 }

转载于:https://www.cnblogs.com/losing-1216/p/4930523.html

Cosmos OpenSSD--greedy_ftl1.2.0(二)相关推荐

  1. VUE3.0 二. vue-cli3 配置指南

    VUE3.0 二. vue-cli3配置指南 一.vue.config.js 详解 官网地址: https://cli.vuejs.org/zh/config/ 使用vue3 的时候需要添加一个vue ...

  2. Tensorlfow2.0 二分类和多分类focal loss实现和在文本分类任务效果评估

    Tensorlfow2.0 二分类和多分类focal loss实现和在文本分类任务效果评估 前言 二分类 focal loss 多分类 focal loss 测试结果 二分类focal_loss结果 ...

  3. 使用Express开发小说API接口服务1.0(二)

    使用Express开发小说API接口服务1.0(二) 线上访问地址api.langpz.com/ 之前完成了首页和搜索的接口,现在就开始写剩下的接口. 获取小说源 因为追书神器正版源是收费加密的,所以 ...

  4. PROE4.0二次开发基视频教程从入门到精通教程

    PROE4.0二次开发基视频教程从入门到精通教程 链接:https://pan.baidu.com/s/19UVFQz351Qa4tpWfPw3jRg 提取码:x7g8

  5. scratch3.0 二次开发-基本介绍(第一章)

    scratch3.0系列章节列表 scratch3.0 二次开发-基本介绍(第一章) scratch3.0二次开发运行scratch-gui项目并了解工程结构(第二章) scratch3.0二次自定义 ...

  6. ASW3642替代TS3DV642方案|ASW3642 HDMI 2.0二进一出切换器设计方案

    ASW3642 是一款 12 通道 1:2 或 2:1 双向多路复用器/多路解复用器.ASW3642 可由 2.6V 至4.5V 的电源供电,适用于电池供电的应用.该器件的导通电阻(RON)较低并且 ...

  7. HDMI2.0二进一出切换器方案ASW3642和TS3DV642设计电路对比

    ASW3642和TS3DV642都支持HDMI2.0二进一出方案或者一进二出方案,两者中ASW3642性能稳定,性价比高,设计更简洁.其电路对比如下: ASW3642替代TS3DV642支持HDMI/ ...

  8. 泛微 e-cology 9.0 二次开发

    泛微 e-cology 9.0 二次开发 1.前端开发环境搭建(ecode代码编辑器) 请访问地址:链接 2.后端开发环境搭建 请访问地址:链接 3.E9流程表单前端接口API 请访问地址:链接

  9. wap六感程序二次开发_Cscms v4.0 二次开发y2002音乐网站程序

    Cscms v4.0 二次开发y2002音乐网站程序 源码简介: 修复了多处问题,比网上流传的版本要完整很多. 程序包括pc+wap,页面功能和原y2002基本一样. 程序比较完整了,但还是会有bug ...

  10. cosmos源码分析之二整体流程

    cosmos主要的源码其实是在SDK部分,听名字也可以理解出来,直接用这个SDK就可以写出一条不考虑底层的区块链来,但是,做为中继链的一个代表,理想和现实并不是那么完美的结合在一起. 目前区块链的跨链 ...

最新文章

  1. 在macOS Sierria 10.12.2上升级默认的vim
  2. 全国计算机等级考试二级vb上机模拟软件,(全国计算机等级考试二级Vb上机模拟9-13.doc...
  3. 查询数据的排序的位置_简单选择排序C++实现
  4. 看到关于java资料比较全的,自己收藏
  5. win10 uwp 使用 msbuild 命令行编译 UWP 程序
  6. 前端提示框定位在鼠标的右下_前端基础高频面试题(更新中)
  7. Linux服务器挂死案例分析
  8. 另类SEO优化推广之百度下拉词框优化推广是怎么做的?
  9. itchat 运行记录
  10. 柳传志二十年驭人成就柳氏联想
  11. Android内存优化大全(二)
  12. jarvisoj_typo
  13. 2022/7/14小记
  14. Praat脚本-004 | 替换标注内容
  15. 微信小程序:云开发表情包制作源码
  16. Centos7登陆颜色修改#PS1
  17. 2021-10-27 基于电影数据库的简单查询(数据库第二次实验)
  18. .php on line 0,PHP Fatal error: Could not queue new timer in Unknown on line 0
  19. 【MySQL基础教程】事务详细介绍
  20. css多行文字溢出省略号显示

热门文章

  1. Spring实战之Cache
  2. java判断对象已经被回收_Java中JVM判断对象已死的基本算法分析
  3. ios sdk 穿山甲_GitHub - ArthurKnight/flutter_ad_pangolin_plugin: iOS flutter 穿山甲插件
  4. css之背景图固定大小不变、不重复、充满整个页面
  5. 跨浏览器共享数据_可以让跨域要数据的模式:jsonp
  6. 二叉树学习笔记之利用前序遍历递归创建二叉树
  7. 报告:2020年NFT总市值达5200万美元
  8. SAP License:SAP与中国电子技术标准化研究院开展交流活动
  9. SAP License:进项税的合理管理缩减成本
  10. Elasticsearch 实战1:ES 项目实战(一)Java 集成 Spring Data Elasticsearch(一):简介及环境搭建