一设计功能

对SPI_flash进行扇区擦除,分为写指令和扇区擦除两个时序部分。

二设计知识点

我简单理解flash,第一它是掉电不丢失数据的存储器,第二它每次写入新数据前首先得擦除数据,分为扇区擦除和全擦擦。

下面讲讲我自己亲自动手设计的原创代码过程:

自己设计过程:

第一步:就先看了SPI FLASH文档,了解SPI FLAHS的原理:先有写使能信号及其时序波形,然后是扇区擦除指令和地址及时序波形,再是延时3秒回到初始状态。

第二步:自己画出所有的状态及其转移图,还是就是用线性序列机表达写使能指令的时序和扇区擦除的时序,并且把他们用EXCEL表弄出相应的时间点和信号的变化(对输出信号赋值)。做完后再弄出模块框图。

第三步:根据自己的模块框图和状态转移图,还有线性序列机的EXCEL表格,描述对应的代码。

第四步:仿真验证是否出错。

我的设计思想如下图所示,三部分。

一是计数器(写使能时序的计数器,扇区擦除的计数器,延时三秒的计数器).

二是,状态机。(初始状态,写使能指令状态,擦除扇区的状态,延时三秒的状态)

三是,线性序列机。(由写指令和擦除扇区的时序图弄好计数值及信号变化)

计数器:

一是WF计数器,计到41,位宽为6。

二是SE计数器,计到128.,位宽为9

三是延时3秒的计数器, 计到150M,位宽为28

针对众多时序逻辑的编码方法:第一步在excel中,分成两列,一列是计数值(使用序列填充,等差),一列是对应的信号变化。第二步,直接在notepad++里使用列编辑加上分号,欧克勒。

线性序列机:

WE的时序逻辑:下图为WREN时序波形图。

时间ns

计数值

信号的操作

0

0

Begin sck<=0;cs_n<=1;sdi<=0;end

20

1

Begin sck<=0; cs_n<=0;end

80

4

sck<=1;

120

6

sck<=0;

160

8

sck<=1;

200

10

sck<=0;

240

12

sck<=1;

280

14

sck<=0;

320

16

sck<=1;

360

18

sck<=0;

400

20

sck<=1;

440

22

Begin sck<=0; sdi<=1;end

480

24

sck<=1;

520

26

Begin sck<=0; sdi<=1;end

560

28

sck<=1;

600

30

Begin sck<=0; sdi<=0;end

640

32

sck<=1;

680

34

sck<=0;

700

35

cs_n<=1;

800

40

SE的时序逻辑:下图为SE时序波形图。

下面为线性序列机的EXCEL描述SE时序:

时间点

计数值

信号的变化

40

2

Begin sck<=1;sdi<=1;end

80

4

sck<=0;

120

6

Begin sck<=1;sdi<=1;end

160

8

sck<=0;

200

10

Begin sck<=1;sdi<=0;end

240

12

sck<=0;

280

14

Begin sck<=1;sdi<=1;end

320

16

sck<=0;

360

18

Begin sck<=1;sdi<=1;end

400

20

sck<=0;

440

22

Begin sck<=1;sdi<=0;end

480

24

sck<=0;

520

26

sck<=1;

560

28

sck<=0;

600

30

sck<=1;

640

32

sck<=0;

680

34

Begin sck<=1;sdi<=SE_addr[23];end

720

36

sck<=0;

760

38

Begin sck<=1;sdi<=SE_addr[22];end

800

40

sck<=0;

840

42

Begin sck<=1;sdi<=SE_addr[21];end

880

44

sck<=0;

920

46

Begin sck<=1;sdi<=SE_addr[20];end

960

48

sck<=0;

1000

50

Begin sck<=1;sdi<=SE_addr[19];end

1040

52

sck<=0;

1080

54

Begin sck<=1;sdi<=SE_addr[18];end

1120

56

sck<=0;

1160

58

Begin sck<=1;sdi<=SE_addr[17];end

1200

60

sck<=0;

1240

62

Begin sck<=1;sdi<=SE_addr[16];end

1280

64

sck<=0;

1320

66

Begin sck<=1;sdi<=SE_addr[15];end

1360

68

sck<=0;

1400

70

Begin sck<=1;sdi<=SE_addr[14];end

1440

72

sck<=0;

1480

74

Begin sck<=1;sdi<=SE_addr[13];end

1520

76

sck<=0;

1560

78

Begin sck<=1;sdi<=SE_addr[12];end

1600

80

sck<=0;

1640

82

Begin sck<=1;sdi<=SE_addr[11];end

1680

84

sck<=0;

1720

86

Begin sck<=1;sdi<=SE_addr[10];end

1760

88

sck<=0;

1800

90

Begin sck<=1;sdi<=SE_addr[9];end

1840

92

sck<=0;

1880

94

Begin sck<=1;sdi<=SE_addr[8];end

1920

96

sck<=0;

1960

98

Begin sck<=1;sdi<=SE_addr[7];end

2000

100

sck<=0;

2040

102

Begin sck<=1;sdi<=SE_addr[6];end

2080

104

sck<=0;

2120

106

Begin sck<=1;sdi<=SE_addr[5];end

2160

108

sck<=0;

2200

110

Begin sck<=1;sdi<=SE_addr[4];end

2240

112

sck<=0;

2280

114

Begin sck<=1;sdi<=SE_addr[3];end

2320

116

sck<=0;

2360

118

Begin sck<=1;sdi<=SE_addr[2];end

2400

120

sck<=0;

2440

122

Begin sck<=1;sdi<=SE_addr[1];end

2480

124

sck<=0;

2520

126

Begin sck<=1;sdi<=SE_addr[0];end

2560

128

sck<=0;

二设计输入

Flash扇区擦除的功能模块

module flash_se(

input wire clk,

input wire rst_n,

input wire we_flag,

input wire [23:0]SE_addr,

output reg     sck,

output reg     sdi,

output reg     cs_n

);

reg we_en;            //进入写使能指令时序的标志信号

reg se_en;             //进入扇区擦除指令时序的标志信号

reg [3:0]state;//定义状态变量

reg sck0,sck1;

reg sdi0,sdi1;

reg cs_n0,cs_n1;

parameter idel = 4'b0001;           //初始状态

parameter WERN = 4'b0010;             //写使能指令状态

parameter SE = 4'b0100;             //扇区擦除

parameter DELAY = 4'b1000;//3秒延时状态

reg[5:0]we_cnt;

reg[7:0]se_cnt;

reg[27:0]delay_cnt;

//写使能指令时序的计数器

always@(posedge clk or negedge rst_n)

if(!rst_n)

we_cnt<=0;

else if(we_cnt=='d41)

we_cnt<=0;

else if(we_en)

we_cnt<=we_cnt+1'b1;

//扇区擦除时序的计数器

always@(posedge clk or negedge rst_n)

if(!rst_n)

se_cnt<=0;

else if(se_cnt=='d128)

se_cnt<=0;

else if(se_en)

se_cnt<=se_cnt+1'b1;

parameter T3S = 28'd150_000_000-1;

always@(posedge clk or negedge rst_n)begin

if(rst_n==1'b0)begin

state<=idel;

delay_cnt<=0;

we_en<=0;

se_en<=0;

end

else begin

case(state)

idel: if(we_flag)    //投入0.5元

state<=WERN;

else

state<=idel;

WERN:if(we_cnt=='d41)begin

state<=SE;

we_en<=0;

end

else begin

state<=state;

we_en<=1;

end

SE:if(se_cnt=='d128)begin

state<=DELAY;

se_en<=0;

end

else begin

state<=state;

se_en<=1;

end

DELAY:if(delay_cnt==T3S)begin

state<=idel;

delay_cnt<=0;

end

else begin

delay_cnt<=delay_cnt+1'b1;

state<=state;

end

default:state<=idel;

endcase

end

end

//写使能信号时序通过线性序列机

always@(posedge clk or negedge rst_n)

if(!rst_n)begin

sck0<=0;cs_n0<=1;sdi0<=0;

end

else begin

case(we_cnt)

0 :cs_n0<=0;

1 :sck0<=1;

4 :sck0<=0;

6 :sck0<=1;

8 :sck0<=0;

10:sck0<=1;

12:sck0<=0;

14:sck0<=1;

16:sck0<=0;

18:sck0<=1;

20:sck0<=0;

22:begin sck0<=1; sdi0<=1;end

24:sck0<=0;

26:begin sck0<=1; sdi0<=1;end

28:sck0<=0;

30:begin sck0<=1; sdi0<=0;end

32:sck0<=0;

34:sck0<=1;

35:sck0<=0;

40:begin sck0<=0;cs_n0<=1;sdi0<=0;end

default: ;

endcase

end

//扇区擦除时序通过线性序列机

always@(posedge clk or negedge rst_n)

if(!rst_n)begin

sck1<=0;cs_n1<=1;sdi1<=0;

end

else begin

case(se_cnt)

0:begin sck1<=0;cs_n1<=1;sdi1<=0;end

2     :begin sck1<=1;cs_n1<=0;end

4     :sck1<=0;

6     :begin sck1<=1;sdi1<=1;end

8     :sck1<=0;

10   :begin sck1<=1;sdi1<=0;end

12   :sck1<=0;

14   :begin sck1<=1;sdi1<=1;end

16   :sck1<=0;

18   :begin sck1<=1;sdi1<=1;end

20   :sck1<=0;

22   :begin sck1<=1;sdi1<=0;end

24   :sck1<=0;

26   :sck1<=1;

28   :sck1<=0;

30   :sck1<=1;

32   :sck1<=0;

34   :begin sck1<=1;sdi1<=SE_addr[23];end

36   :sck1<=0;

38   :begin sck1<=1;sdi1<=SE_addr[22];end

40   :sck1<=0;

42   :begin sck1<=1;sdi1<=SE_addr[21];end

44   :sck1<=0;

46   :begin sck1<=1;sdi1<=SE_addr[20];end

48   :sck1<=0;

50   :begin sck1<=1;sdi1<=SE_addr[19];end

52   :sck1<=0;

54   :begin sck1<=1;sdi1<=SE_addr[18];end

56   :sck1<=0;

58   :begin sck1<=1;sdi1<=SE_addr[17];end

60   :sck1<=0;

62   :begin sck1<=1;sdi1<=SE_addr[16];end

64   :sck1<=0;

66   :begin sck1<=1;sdi1<=SE_addr[15];end

68   :sck1<=0;

70   :begin sck1<=1;sdi1<=SE_addr[14];end

72   :sck1<=0;

74   :begin sck1<=1;sdi1<=SE_addr[13];end

76   :sck1<=0;

78   :begin sck1<=1;sdi1<=SE_addr[12];end

80   :sck1<=0;

82   :begin sck1<=1;sdi1<=SE_addr[11];end

84   :sck1<=0;

86   :begin sck1<=1;sdi1<=SE_addr[10];end

88   :sck1<=0;

90   :begin sck1<=1;sdi1<=SE_addr[9];end

92   :sck1<=0;

94   :begin sck1<=1;sdi1<=SE_addr[8];end

96   :sck1<=0;

98   :begin sck1<=1;sdi1<=SE_addr[7];end

100  :sck1<=0;

102  :begin sck1<=1;sdi1<=SE_addr[6];end

104  :sck1<=0;

106  :begin sck1<=1;sdi1<=SE_addr[5];end

108  :sck1<=0;

110  :begin sck1<=1;sdi1<=SE_addr[4];end

112  :sck1<=0;

114  :begin sck1<=1;sdi1<=SE_addr[3];end

116  :sck1<=0;

118  :begin sck1<=1;sdi1<=SE_addr[2];end

120  :sck1<=0;

122  :begin sck1<=1;sdi1<=SE_addr[1];end

124  :sck1<=0;

126  :begin sck1<=1;sdi1<=SE_addr[0];end

128  :begin sck1<=0;cs_n1<=1;sdi1<=0;end

default: ;

endcase

end

//对于输出避免冒险通过选择器

always@(posedge clk or negedge rst_n)

if(!rst_n)

begin

sck<=0;cs_n<=1;sdi<=0;

end

else if(state==WERN)begin

sck<=sck0;cs_n<=cs_n0;sdi<=sdi0;

end

else if(state==SE)begin

sck<=sck1;cs_n<=cs_n1;sdi<=sdi1;

end

endmodule

Flash扇区擦除的测试模块

`timescale 1ns/1ns

`define clk_period 20

module flash_se_tb;

reg   clk   ;

reg   rst_n ;

reg   we_fla;

reg[23:0]SE_add;

wire  sck   ;

wire  sdi   ;

wire  cs_n  ;

initial clk =1;

always#10 clk =~clk;

initial begin

rst_n =0;

we_fla =0;

SE_add = 0;

#(`clk_period*2);

rst_n =1;

we_fla =1;

SE_add = 24'b0100_0010_0000_0000_0000_0000;

#(`clk_period);we_fla =0;

#(`clk_period*400);

rst_n =0;

#(`clk_period*20);

$stop;

end

flash_se #(.T3S(199)) inst_flash_se (

.clk     (clk),

.rst_n   (rst_n),

.we_flag (we_fla),

.SE_addr (SE_add),

.sck     (sck),

.sdi     (sdi),

.cs_n    (cs_n)

);

endmodule

三FLASH的扇区擦除的仿真波形如下

我的收获是:

首先是第一次遇到陌生有难度的东西,不要怂,要静下心来亲自动手做和思考,一步步的。

其次是,弄懂设计原理和设计思路,这可能占了整个设计的40%,调试代码占了40%,敲代码只占了10%,还有10%就是记录整理勒。画好图纸比敲代码重要得多,代码只不过是思路的描述。

16经典的SPI Flash的扇区擦除flash_se功能的更多相关文章

  1. RTT下spi flash+elm fat文件系统移植小记

    背景: MCU:STM32F207 SPI flash: Winbond W25Q16BV OS: RTT V1.1.1 bsp: STM32F20x 1 将spi_core.c,spi_dev.c及 ...

  2. spi flash 操作

    W25Q16V 是华邦出的一颗 spi flash. 25系列是比较通用的一个系列. 后面的数字 16 跟容量有关, 16 表示 16Mbits, 相当于 2MB. 与此类似的还有 W25Q128V ...

  3. 基于FPGA的SPI FLASH控制器设计

    1.SPI FLASH的基本特征 本文实现用FPGA来设计SPI FLASH,FLASH型号为W25Q128BV.支持3种通信方式,SPI.Dual SPI和Quad SPI.FLASH的存储单元无法 ...

  4. Arduino SPI + SPI Flash芯片W25Q80BV

    W25Q80BV是台湾华邦电子(Winbond)生产的8M-bit串行flash芯片.主要特性有: 工作电压:2.5 ~ 3.6 V 功耗:读写(active)时4mA,低功耗(power-down) ...

  5. RTThread DFS文件系统使用: 基于使用SFUD驱动的SPI FLASH之上的ELM FATFS文件系统

    参考博文: 博文很长,但是实际要操作的步骤没几下. http://m.elecfans.com/article/730878.html  为了防止几年后文章链接找不到,我把文章复制过来了 /***** ...

  6. OpenRisc-32-ORPSoC烧写外部spi flash

    引言 经过前面的分析和介绍,我们对ORPSoC的启动过程(http://blog.csdn.net/rill_zhen/article/details/8855743)和 ORpSoC的debug子系 ...

  7. SPI Flash(W25Q16DV) 驱动

    大体上可分为以下几个部分: 1.注册设备驱动 spi_register_driver 2.分配 mtd_info 结构体 3.配置 mtd_info 结构体 4.注册 mtd_info 结构体 构建 ...

  8. SPI Flash(W25Q16DV) 基本操作

    读取厂家\设备 ID 发送 90H 指令,再发送 00h 的地址,然后接收即可. 代码如下: void SPIFlashReadID(int *pMID, int *pDID) { SPIFlash_ ...

  9. (电工基地笔记)Vivado固化至SPI Flash

    如果从头开始做SPI Flash固化是有一些麻烦的,要在完成综合之后,打开 synthesized Design (图) (图) 然后在synthesized Design打开状态下,选择Tools- ...

随机推荐

  1. Solution -「Gym 102759C」Economic One-way Roads

    \(\mathcal{Description}\)   Link.   给定一个含 \(n\) 个点 \(m\) 条边的简单无向图,每条边的两种定向方法各有权值,求使得图强连通且定向权值和最小的方法. ...

  2. Solution -「CF 804F」Fake bullions

    \(\mathcal{Description}\)   Link.   给定 \(n\) 个点的竞赛图,第 \(i\) 个点代表了 \(s_i\) 个人,每个人(0-based)可能有真金条.此后在 ...

  3. C1 能力认证——Web进阶

    C1 能力认证--Web进阶 DOM节点操作-上 名称 描述 getElementById() 获取带有指定id的节点 getElementsByTagName() 获取带有指定标签名的节点集合 qu ...

  4. Dubbo基础一之实战初体验

    本以为写这个小作文没什么难度的,可是好像并不是.前段时间重心放在驾考科目二,就想着小作文科二考过了再写也不是事,因为都实战过了.今天想着写却发现脑袋里啥都想不起来了,得翻项目和笔记回忆一下.所以还是那 ...

  5. [自动化]基于kolla的自动化部署ceph集群

    kolla-ceph来源: 项目中的部分代码来自于kolla和kolla-ansible kolla-ceph的介绍: 1.镜像的构建很方便, 基于容器的方式部署,创建.删除方便 2.kolla-ce ...

  6. RENIX发送固定个数报文——网络测试仪实操

    在使用RENIX软件时,有时候我们需要发送固定个数报文,那么该如何操作呢?以下为您讲解具体操作步骤. 第一步:预约测试资源 打开Renix软件,连接机箱, 预约端口 第二步:发送固定个数的报文 选中流 ...

  7. 【windows 操作系统】窗口指针 和 窗口句柄 有什么区别

    句柄是指针的"指针" 指针对应着一个数据在内存中的地址,得到了指针就可以自由地修改该数据.Windows并不希望一般程序修改其内部数据结构,因为这样太不安全.所以Windows给每 ...

  8. 【windows 操作系统】协程

    ◆ 协程 由程序员手动切换. 属于线程,是在线程里面跑的,因此协程又称微线程和线程等.由于不用进行线程上下文切换,因此效率会更高. 资源: 拥有自己的栈空间,大小动态调整. 安全性: 拥有自己的寄存器 ...

  9. 【C# IO 操作 】详解去掉字符顺序标记(BOM)头的方法

    类似WINDOWS自带的记事本等软件,在保存一个以UTF-8编码的文件时,会在文件开始的地方插入三个不可见的字符(0xEF 0xBB 0xBF,即BOM).它是一串隐藏的字符,用于让记事本等编辑器识别 ...

  10. C# typeof() 和object.GetType() 、Type..GetType()使用和区别

    进行学习到表达树了,用动Tpye了.所以整理了以下他们区别和用法 总得来说他们都是为了获取某个实例具体引用的数据类型System.Type.1.GetType()方法继承自Object,所以C#中任何 ...