flash模型
- MT25QU01GBBB8E0_VG13
该模型从Micron官方网站下载,搜索地址Micron仿真模型搜索 进入Micron官网,在搜索框输入‘sim-model’,搜索结果列表中会出现很多可以使用的模型。
仿真环境建立过程不谈。 - 准备软件
软件中实现spi-flash的各种操作,readid,读擦写等,编译成vcs需要的格式
执行流程
- read ID
[312777.000 ns ns] COMMAND DECODED: Read ID , withAddr=00000000, with2Addr=00000000, with4Addr=00000000, cmdcode=9f
[312777.000 ns ns] COMMAND RECOGNIZED: Read ID.
[312785.000 ns ns] Data are going to be output: 20. [Read ID, byte 0] 0 0
[312937.000 ns ns] Data are going to be output: ba. [Read ID, byte 1] 0 0
[313129.000 ns ns] Data are going to be output: 20. [Read ID, byte 2] 0 0
[313321.000 ns ns] Data are going to be output: 10. [Read ID, byte 3] 0 0
jedec: 0x20ba20
模型中识别spi送进来的指令,然后返回当前使用的flash型号。
- erase
[327733.000 ns ns] COMMAND DECODED: Write Enable , withAddr=00000000, with2Addr=00000000, with4Addr=00000000, cmdcode=06
[327733.000 ns ns] COMMAND RECOGNIZED: Write Enable.
[327813.000 ns ns] Command execution completed: WEL bit set.
[327973.000 ns ns] COMMAND DECODED: Subsector Erase , withAddr=00000001, with2Addr=00000000, with4Addr=00000000, cmdcode=20
[327973.000 ns ns] 1.COMMAND RECOGNIZED: Subsector Erase. Address expected ...
[328545.000 ns ns] Address latched: 0000000 (byte 0 of page 0, sector 0)
[328609.000 ns ns] Command execution begins: Subsector Erase
[328609.000 ns ns] isProtected_by_SR: 0 sectAddr: 000
[328609.000 ns ns] isProtected_by_lockReg: 0 sectAddr: 000
[328609.000 ns ns] isProtected_by_PPBReg: 1 sectAddr: 000
[328609.000 ns ns] isProtected_by_lockReg4Kb: 0 subsecAddr: 0000
[328767.000 ns ns] COMMAND DECODED: Read SR , withAddr=00000000, with2Addr=00000000, with4Addr=00000000, cmdcode=05
[328767.000 ns ns] COMMAND RECOGNIZED: Read SR.
[328775.000 ns ns] Data are going to be output: 00000011. [Read Status Register] 0 0
flash擦除过程中,先发送06命令解锁flash
然后执行20命令,对解锁的flash进行擦除。
当程序运行到这里,剩下的时间就是漫长的等待,以至于认为擦除操作失败。实际上只是delay的时间比较长,需要等待的时间比较久而已。以下通过代码分析其中的原因。
首先找到flash擦除的执行代码
//--------------
// Erase
//--------------
always @N25Qxxx.seqRecognized
if ((N25Qxxx.cmdRecName==="Sector Erase" || N25Qxxx.cmdRecName==="Subsector Erase" || N25Qxxx.cmdRecName==="Subsector Erase 32K" ||N25Qxxx.cmdRecName==="Subsector Erase 32K 4Byte" ||
N25Qxxx.cmdRecName==="Bulk Erase" || N25Qxxx.cmdRecName==="Die Erase") && (N25Qxxx.die_active == 1))begin : erase_operations
.......
`endif
end else
fork : erase_ops
begin : exe
@(posedge N25Qxxx.S);
disable reset;
operation = N25Qxxx.cmdRecName;
destAddr = N25Qxxx.addr;
if(Suspended == 0) destAddrSusp1 = destAddr;
N25Qxxx.latchingMode="N";
N25Qxxx.busy = 1;
startTime = $time;
$display(" [%0t ns] Command execution begins: %0s for3.", $time, operation);
if (operation=="Sector Erase") delay=erase_delay;
`ifdef Stack512Mb
else if (operation=="Die Erase") delay=erase_die_delay;
......
`ifdef SubSect
else if (operation=="Subsector Erase")
begin
delay=erase_ss_delay;
$display(" [%0t ns] Command execution begins: %0s for7.", $time, operation);
end
......
`endif
-> errorCheck;
@(noError)
begin
......
`ifdef SubSect
else if (operation=="Subsector Erase")
begin
mem.eraseSubsector(destAddr);
$display(" [%0t ns] Command execution begins: Subsector Erase %d.", $time,destAddr);
end
......
end
begin : reset
@N25Qxxx.resetEvent;
operation = "None";
disable exe;
end
join
end
删除大部分代码,保留关键部分。
以下是主要的流程:
着重看 fork : erase_ops 函数,首先判断operation,执行什么操作,通过log日志,可以看出已经准确
- 识别了Subsector Erase操作,
- 该操作下给delay参数赋值为erase_ss_delay,
- 并且将flash状态设置为busy。
- 接着触发errorCheck事件,该事件中主要判断将要操作的命令,以及flash是否解锁准备好执行该命令,
- 然后等待捕捉@(noError)事件,执行真正的擦除动作。
errorCheck中的实现
//------------------------
// Error check
//------------------------
// This process also models
// the operation delays
always @(errorCheck) fork : errorCheck_ops
......
if ( (operation=="Page Program" || operation=="Dual Program" || operation=="Dual Extended Program" ||
operation=="Quad Extended Program" || operation=="Quad Program" || operation=="Dual Command Page Program" || operation=="Quad Command Page Program" ||
operation=="Sector Erase" || operation=="Subsector Erase" || operation=="Subsector Erase 32K" || operation=="Subsector Erase 32K 4Byte")
&&
`ifdef Stack512Mb
//(isProtected_by_SR_stack(destAddr)!==0 || lock.isProtected_by_lockReg(destAddr)!==0) ) begin
(lock.isProtected_by_SR(destAddr)!==0 || lock.isProtected_by_lockReg(destAddr)!==0) ) begin
`else
`ifdef MEDT_PPB
((lock.isProtected_by_SR(destAddr)!==0 || lock.isProtected_by_lockReg(destAddr)!==0) || ppb.isProtected_by_PPBReg(destAddr)!==1 || lock4kb.isProtected_by_lockReg(destAddr)!==0))
begin
-> error;
.......
fork : dynamicCheck
......
// #delay begin
begin : main_ops
$display(" **INFO** start delay!");
#delay;
$display(" **INFO** delay over!");
N25Qxxx.busy=0;
if(!Suspended || !prog.prog_susp) -> stat.WEL_reset;
-> noError;
#1;
-> noError2;
disable dynamicCheck;
disable errorCheck_ops;
end
join
在errorCheck中,如果出现错误则会释放error的事件,如果没有错,则会执行main_ops。
这里开始delay,这个delay就是上面的 erase_ss_delay
若果这个值设置太大,会出现上文提到的仿真的等待的时间过长。
然后清除flash的busy状态,释放noError事件,flash接着执行正真的擦除动作。
通过查看时间配置参数,在TImingData.h中,找到参数配置
`ifdef SubSect
parameter time erase_ss_delay = tSSE;
`endif
parameter time tSSE = 3e9;
由此可见,等待的时间是过长了。
对应的flash手册中指出,flash页擦除的时间大概为300ms,所以官方的配置为3e9,此处为了减少仿真等待的时间,可以将此值改成一个合理的数值比如30.则会大大提高仿真的速率。