一设计功能

对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%就是记录整理勒。画好图纸比敲代码重要得多,代码只不过是思路的描述。

最新文章

  1. 16 BasicHashTable基本哈希表类(三)——Live555源码阅读(一)基本组件类
  2. KindEditor编辑器在ASP.NET中的使用
  3. 通过设置虚拟机(ubantu15.10)的分辨率达到全屏效果
  4. ASP.NET MVC5学习笔记之Controller同步执行架构分析
  5. hdu 4888
  6. js html5 仿微信摇一摇
  7. lpc1768usb端点响应以及描述符定义
  8. Java Stream API进阶篇
  9. python 数据类型 -- set
  10. hdu 2209 bfs+状压
  11. VIVO 手机重力传感器踩坑记录
  12. Fabric动态增加组织【资料】
  13. Linux下memcache编译安装与基本使用
  14. chrome内核浏览器插件的使用--Tampermonkey(油猴插件)
  15. C&amp;Cpp.CallGraph
  16. Android之apk优化
  17. ios 获得通讯录中联系人的所有属性 亲测,可行 兼容io6 和 ios 7
  18. P4173 残缺的字符串 fft
  19. [翻译] FeSpinner
  20. 2013-8-6 10:56:07 JAVA_WEB:员工号自动生成源代码

热门文章

  1. Solution -「51nod 1514」美妙的序列
  2. CreateEvent进程同步
  3. Linux爱情故事之如何以不一样的姿势(ssh)进入她的心
  4. c++ 字符串替换程序 p324
  5. MyBatis封装JDBC具体实现
  6. Javers 比较两个类的差异
  7. Harbor2.2.4在CentOS7.9安装、部署
  8. Clickhouse - Replication机制
  9. Java:Path与Paths
  10. Leaflet:Map类属性及方法