SPI-Flash全擦除实验

1,SPI简介

  SPI(Serial Peripheral Interface)串行外围设备接口通信协议,是Motorola公司提出的一种同步串行接口技术,是一种高速、全双工、同步通信总线;只使用4条线来控制数据传输;常用于EEPROM、Flash、RTC、ADC、DSP以及数字信号解码器上。
  SPI通信协议的优点是支持全双工通信,通信方式简单,且相对数据传输速率较快;缺点是没有指定的流控制,没有应答机制确认数据是否接收,在数据可靠性上存在一定缺陷。SPI通信协议一般分为物理层、协议层两部分。

2,物理层

  SPI的物理层即通信设备的连接方式和引脚的功能描述。SPI通信设备的通信模式是主从通信模式,通信双方有主从之分,根据从机设备的数量,SPI通信设备之间的连接方式可分为一主一从、一主多从,如下图所示。
在这里插入图片描述
在这里插入图片描述
  SPI通信协议包含一条时钟信号线、2条数据总线、一条片选信号线,具体介绍如下:

(1)SCK (Serial Clock):时钟信号线,用于同步通信数据。由通信主机产生,决定了通信的速率,不同设备支持的最高时钟频率不同,两个设备之间通信时,通信速率受限于低速设备。

(2)MOSI(Master Output,Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,数据方向由主机到从机。

(3)MISO(Master Input,Slave Output)主设备输入/从设备输出引脚,主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,数据方向由从机到主机。

(4)/CS(Chip Select):片选信号线,也称CS_N,当有多个从设备时,所有从设备共同使用SCK,MOSI,MISO三条总线;但是对于每一个从设备CS_N都是独立的,即每一个从设备有一个片选信号线。所以SPI通信以CS_N线置低电平为开始信号,以CS_N线被拉高作为结束信号。

3,协议层

  SPI通信协议有四种通信模式,模式0、1、2、3;这四种模式分别由时钟机型(CPOL, Clock Polarity)和时钟相位(CPHA, Clock Phase)来定义。其中CPOL参数规定了空闲状态(CS_N为高电平,设备未被选中)时SCK时钟信号的电平状态,CPHA规定了数据采样是在SCK时钟的奇数边沿还是偶数边沿。
  SPI的通信协议的四种模式如下:

CPOLCPHA空闲时SCK状态数据采样数据更新
模式0CPOL=0CPHA=0低电平奇数边沿(上升沿)偶数边沿(下降沿)
模式1CPOL=0CPHA=1低电平偶数边沿(下降沿)奇数边沿(上升沿)
模式2CPOL=1CPHA=0高电平奇数边沿(下降沿)偶数边沿(上升沿)
模式3CPOL=1CPHA=1高电平偶数边沿(上升沿)奇数边沿(下降沿)

SPI通讯模式时序图
SPI通讯模式时序图
CPHA = 0 时的SPI通信模式
在这里插入图片描述
CPHA = 0 时的SPI通信模式
在这里插入图片描述

4,SPI通信过程

  在四种模式中0和3较常用,下面内容以模式0为例讲解SPI的基本通讯过程;SPI模式0的通讯时序图如下(主机视角的通讯时序):
在这里插入图片描述
  在上图中SCK,MOSI,CS_N信号均由主机控制产生,SCK是时钟信号,用来同步数据;MOSI是主机输出从机输入信号,主机通过此信号线传输数据给从机;CS_N为片选信号,用以选定从机设备,低电平有效;而MISO信号由从机产生,主机通过该信号线读取从机数据。MOSI,MISO的信号只在CS_N为低电平时有效,在SCK的每个时钟周期MOSI,MISO传输一位数据。

5,通信的起始和停止信号

  CS_N信号线由高变低,是SPI的起始信号。CS_N是每个从机各自独占的信号线,当从机在自己的CS_N线检测到起始信号后,开始准备与主机通信。CS_N信号由低变高,是SPI通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

6,数据有效性

  MOSI和MISO数据线在SCK的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时一般采用MSB先行的模式。SPI每次数据传输可以以8位或16位为单位,每次传输的单位数不受限制。

7,SPI-Flash全擦除实验

  Flash芯片是一种非易失性存储芯片,即掉电后数据不会丢失。对flash的全擦除就是将Flash所有的存储空间进行擦除操作,使各存储空间内存储数据恢复到初始值。FPGA实现Flash的全擦除有两种方式;一是利用FPGA编译软件,通过Quartus软件的“programmer”窗口,将烧录到Flash的xxx.jic文件擦除;而是通过编写全擦除程序。

操作时序
  全擦除(Bulk Erase)操作,简称BE,操作指令为8’b1100_0111(C7h),在这里插入图片描述
全擦除指令指将所有存储单元设置为1,在Flash芯片写入全擦除指令之前,需要先写入使能(WREN)指令,将芯片设置为写使能锁存(WEL)状态,随后要拉低片选信号,写入全擦除指令,在指令写入过程中,片选信号始终保持低电平,待指令被芯片锁存后,将片选信号拉高;全擦除指令被锁存并执行后,需要等待一个完整的全擦除周期(tBE),才能完成全擦除操作。
在这里插入图片描述

  写使能(Write Enable)指令, 简称 WREN,操作指令为 8’b0000_0110(06h),具体见图
在这里插入图片描述
写使能指令可将 Flash 芯片设置为写使能锁存(WEL)状态;在每一次页写操作(PP)、扇区擦除(SE)、全擦除(BE)和写状态寄存器(WRSR)操作之前,都需要先进行写使能指令写入操作。操作时序为先拉低片选信号,写入写使能指令,在指令写入过程中,片选信号始终保持低电平,指令写入完成后,将片选信号拉高。时序如下:
在这里插入图片描述

串行输入时序图
在这里插入图片描述

  如图所示,相关操作指令在写入芯片之前需要先拉低片选信号,在片选信号保持低电平时将指令写入数据输入端口,指令写入完毕,拉高片选信号,数据输出端口在指令写入过程中始终保持高阻态。片选信号自下降沿始到第一个有效数据写入时止,这一段等待时间定义为片选信号有效建立时间 tSLCH,这一时间段必须大于等于 5ns;片选信号自最后一个有效数据写入时始到片选信号上升沿止,这一段等待时间定义为片选信号有效保持时间 tCHSH,这一时间段必须大于等于5ns;片选信号自上一个上升沿始到下一个下降沿止,这一段等待时间定义为片选信号高电平等待时间 tSHSL,这一时间段必须大于等于 100ns。所以完整的全擦除操作时序图如下:

在这里插入图片描述

8,实现思路

(1)Flash全擦除波形图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(2)整体设计思路
  由设计的要求,我们可以使用状态机来实现,定义状态机的各状态分别为初始状态(IDLE)、写使能状态(WR_EN)、两指令间等待状态(DELAY)、全擦除状态(BE)。状态机的跳转流程如下:系统上电后,状态机状态变量 state 一直处于初始状态(IDLE);当传入的全擦除触发信号 key 有效时,表示实验工程开始执行对 Flash 芯片的全擦除操作, 状态机跳转到写使能状态(WR_EN),同时片选信号拉低,选中要进行全擦除操作的 Flash 芯片;状态跳转到写使能状态且片选信号拉低后,要进行 tSLCH≥5ns 的等待时间,等待时间过后对主输出从输入信号写入写使能指令,指令写入完成后需要进行tCHSH≥5ns的等待时间,等待时间过后拉高片选信号,取消对 Flash 芯片的选择,同时状态机跳转到两指令间等待状态(DELAY);在此状态等待时间 tSHSL≥ 100ns 后,状态机跳转到全擦除状态(BE),同时片选信号拉低,选中已写入写使能指令的 Flash 芯片;状态机跳转到全擦除状态且片选信号拉低后,要进行 tSLCH≥5ns 的等待时间,等待时间过后对主输出从输入信号写入全擦除指令,指令写入完成后需要进行 tCHSH≥5ns 的等待时间,等待时间过后拉高片选信号,取消对 Flash 芯片的选择,同时状态机跳回初始状(IDLE),一次完整的全擦除操作完成
  在此实验中输出的Flash芯片的时钟频率为12.5Mhz,由于指令为串行传输,每个时钟周期只能写入1bit数据,要写入一个完整的单字节指令需要8各完整的SCK时钟周期即32个完整的系统时钟,系统时钟为50Mhz,完整指令的写入需要640ns,所以将各等待时间统一设置为640ns

(3)各信号作用
key: 按键输入信号,当检测到按键按下开始擦除操作。

cnt_clk: 640ns计数,初值为0,在0-31计数范围内循环计数,在状态机处于初始状态时,始终保持为0;在状态机在状态机处于初始状态之外的其他状态时,每个系统时钟周期自加 1,计到最大值清 0,重新计数。

cnt_byte: 此计数器对cnt_clk的计数周期进行计数,当状态机处于初始状态时(IDLE)时,计数器的值始终保持0;在其他状态,当cnt_clk完成一个完整的循环计数时,计数器的值加1,其他时刻保持当前值不变。

state: 状态寄存器

cnt_sck: 四分频计数器,在0-3之间循环计数,计数到2时对sck信号取反。

cnt_bit:左右时实现指令或数据的高低位对调,计数器初值为 0,在 0-7范围内循环计数,计数时钟为串行时钟 sck,每个时钟周期自加 1,其他时刻恒为 0。

(4)关键部分代码

module flash_be_ctrl
(
	input 	   sys_clk	,
	input 	   sys_rst_n,
	input 	   key		,

	output reg sck		,
	output reg cs_n		,
	output reg mosi
);

parameter   IDLE  = 4'b0001,	//初始状态
			WR_EN = 4'b0010,	//写状态
			DELAY = 4'b0100,	//等待状态
			BE 	  = 4'b1000;	//全擦除状态

parameter	WR_EN_INST = 8'b0000_0110,	//写使能指令
			BE_INST    = 8'b1100_0111;	//全擦除指令

reg [2:0] cnt_byte	;	//字节计数器
reg [3:0] state		;	//状态机状态
reg [4:0] cnt_clk	;	//系统始终计数器
reg [1:0] cnt_sck	;	//串行时钟计数器
reg [2:0] cnt_bit	;	//比特计数器

//系统时钟计数器用来记录单个字节
always @(posedge sys_clk,negedge sys_rst_n) begin
	if(!sys_rst_n)
		cnt_clk <= 5'd0;
	else if(state != IDLE)
		cnt_clk <= cnt_clk + 1'b1;
	else
		cnt_clk <= cnt_clk;
end

//记录输出字节个数和等待时间
always @(posedge sys_clk,negedge sys_rst_n) begin
	if(!sys_rst_n)
		cnt_byte <= 3'd0;
	else if((cnt_clk == 5'd31) && (cnt_byte == 3'd6))
		cnt_byte <= 3'd0;
	else if(cnt_clk == 5'd31)
		cnt_byte <= cnt_byte + 1'b1;
	else
		cnt_byte <= cnt_byte;
end

//串行时钟计数器,用来生成串行时钟
always @(posedge sys_clk,negedge sys_rst_n) begin
	if(!sys_rst_n)
		cnt_sck <= 2'd0;
	else if((state == WR_EN) && (cnt_byte == 1'b1))
		cnt_sck <= cnt_sck + 1'b1;
	else if((state == BE) && (cnt_byte == 3'd5))
		cnt_sck <= cnt_sck + 1'b1;
	else
		cnt_sck <= cnt_sck;
end

//片选信号
always @(posedge sys_clk,negedge sys_rst_n) begin
	if(!sys_rst_n)
		cs_n <= 1'b1;
	else if(key == 1'b1)
		cs_n <= 1'b0;
	else if((cnt_byte == 3'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
		cs_n <= 1'b1;
	else if((cnt_byte == 3'd3) && (cnt_clk == 5'd31) && (state == DELAY))
		cs_n <= 1'b0;
	else if((cnt_byte == 3'd6) && (cnt_clk == 5'd31) && (state == BE))
		cs_n <= 1'b1;
	else
		cs_n <= cs_n;
end

//输出串行时钟
always @(posedge sys_clk,negedge sys_rst_n) begin
	if(!sys_rst_n)
		sck = 1'b0;
	else if(cnt_sck == 2'd0)
		sck <= 1'b0;
	else if(cnt_sck == 2'd2)
		sck <= 1'b1;
	else
		sck <= sck;
end

//高低位对调,控制mosi输出
always @(posedge sys_clk,negedge sys_rst_n) begin
	if(!sys_rst_n)
		cnt_bit <= 3'd0;
	else if(cnt_sck == 2'd2)
		cnt_bit <= cnt_bit + 1'b1;
	else
		cnt_bit <= cnt_bit;
end

//状态跳转
always @(posedge sys_clk,negedge sys_rst_n) begin
	if(!sys_rst_n)
		state <= IDLE;
	else begin
		case(state)
			IDLE: begin
					if(key == 1'b1)
						state <= WR_EN;
				end
			WR_EN: begin 
					if((cnt_byte == 3'd2) && (cnt_clk == 5'd31))
						state <= DELAY;
				end
			DELAY: begin
					if((cnt_byte == 3'd3) && (cnt_clk == 5'd31))
						state <= BE;
				end
			BE:    begin
					if((cnt_byte == 3'd6) && (cnt_clk == 5'd31))
						state <= IDLE;
				end
			default: state <= IDLE;
		endcase
	end
end

//逻辑输出
always @(posedge sys_clk,negedge sys_rst_n) begin
	if(!sys_rst_n)
		mosi <= 1'b0;
	else if((state == WR_EN) && (cnt_byte == 3'd2))
		mosi <= 1'b0;
	else if((state == BE) && (cnt_byte == 3'd6))
		mosi <= 1'b0;
	else if((state == WR_EN) && (cnt_byte == 3'd1) && (cnt_sck == 5'd0))
		mosi <= WR_EN_INST[7 - cnt_bit];
	else if((state == BE) && (cnt_byte == 3'd5) && (cnt_sck == 5'd0))
		mosi <= BE_INST[7 - cnt_bit];
	else
		mosi <= 1'b0;
end

endmodule

9,参考内容:

野火, FPGA Verilog 开发实战指南

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值