SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,中间靠三线或者四线连接(三线时为单向传输或者数据线双向传输)。所有基于SPI的设备共有的,它们是MISO、MOSI、SCLK、CS。
MISO–MasterInputSlaveOutput,主设备数据输入,从设备数据输出。
MOSI–MasterOutputSlaveInput,主设备数据输出,从设备数据输入。
CS–ChipSelect,从设备使能信号,由主设备控制。
cs是从芯片是否被主芯片选中的控制信号,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),主芯片对此从芯片的操作才有效。这就使在同一条总线上连接多个spi设备成为可能。
通讯是通过数据交换完成的,由sclk提供时钟脉冲,mosi、miso则基于此脉冲完成数据传输。数据输出通过mosi线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。因此,至少需要N次时钟信号的改变(上沿和下沿为一次),才能完成N位数据的传输。
spi通信有四种不同的模式,不同的从设备可能在出厂时就已经配置为某种模式。通信的双方必须是工作在同一模式下,所以我们可以对主设备的spi模式进行配置,通过CPOL(时钟极性)和CPHA(时钟相位)来控制我们主设备的通信模式。
mode0:CPOL=0,CPHA=0;
mode1:CPOL=0,CPHA=1;
mode2:CPOL=1,CPHA=0;
mode3:CPOL=1,CPHA=1;
时钟极性CPOL是用来配置SCLK在空闲时,应该处于的状态;时钟相位CPHA用来配置在第几个边沿进行采样。
CPOL=0,表示在空闲状态时,时钟SCLK为低电平。
CPOL=1,表示在空闲状态时,时钟SCLK为高电平。
CPHA=0,表示数据采样是在第1个边沿。
CPHA=1,表示数据采样是在第2个边沿。
即:
CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿。
CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。
CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。
CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。
在开发板上有一块flash(M25P16),用来保存FPGA的硬件配置信息,也可以用来存储用户的应用程序或数据。
M25P16是一款带有写保护机制和高速SPI总线访问的2M字节串行Flash存储器,该存储器主要特点:2M字节的存储空间,分32个扇区,每个扇区256页,每页256字节;能单个扇区擦除和整片擦除;每扇区擦写次数保证10万次、数据保存期限至少20年。
C(serialclock:串行时钟)为D和Q提供了数据输入或者输出的时序。D的数据总是在C的上升沿被采样。Q的数据在C的下降沿被输出。
ˉS(ChipSelect:芯片选择端),当输入为低时,该芯片被选中,可以允许进行读写操作。当输入为高时,该芯片被释放,不能够进行操作。
对于H——o——l——d——和W——,为保持功能和硬件写保护功能,在本设计中不使用此管脚,在硬件设计时,这两个管脚全部被拉高了,即全部失效。
flash采用spi的通信协议,flash当做从机。serialclcok等效于spi中的sclk,chipselect等效于spi中的cs,D等效于spi中的mosi,Q等效于spi中的miso。
flash可以支持mode0和mode3,这两种模式中,都是在时钟的上升沿采样,在时钟的下降沿发送数据。
flash的每一页都可以被写入,但是写入只能是把1改变为0。擦除可以把0改变为1。所以在正常写入数据之前,都要将flash进行擦除。
flash的命令表如下:
下面介绍几个常用的命令
状态寄存器的格式如下:
READ(ReadDATABytes:读数据):发送命令READ(03H),后续发送3个字节的地址,然后就可以接收数据,内部的地址会不断递增。一个读命令就可以把整个flash全部读完。
SE(SectorErase:扇区擦除):发送命令SE(D8H),接着发送3个字节的地址。
BE(BulkErase:整片擦除):发送命令BE(C7H)。
关于flash的其他的介绍,可以参考03_芯片手册->FLASH->M25P16.pdf。
设计要求
设计分析
根据M25P16的数据手册得知,其接口为spi接口,且支持模式0和模式3,本设计中选择模式0。
输入时序图如下:
输出时序如下:
时序图中所对应的符号说明:
在设计中,FPGA作为主机,M25P16作为从机。
架构设计和信号说明
此模块命名为m25p16_drive。
二级模块(分模块)(第一页)
二级模块(分模块)(第二页)
设计中,各个命令单独写出控制器,通过多路选择器选择出对应的命令,然后控制spi_8bit_drive将数据按照spi的协议发送出去。各个命令的脉冲通过ctrl模块进行控制各个命令控制器,写入的数据首先写入到写缓冲区,读出的数据读出后写入到读缓冲区。
各个模块的功能,和连接线的功能在各个模块设计中说明。
spi_8bit_drive设计实现
本模块负责将8bit的并行数据按照spi协议发送出去,以及负责按照spi协议接收数据,将接收的数据(8bit)并行传输给各个模块。
spi_send_en为发送数据使能信号(脉冲信号),spi_send_data为所要发送数据,spi_send_done为发送完成信号(脉冲信号)。
spi_read_en为接收数据使能信号(脉冲信号),spi_read_data为所接收的数据,spi_read_done为接收完成信号(脉冲信号)。
在发送逻辑控制中,全部的信号采用下降沿驱动。利用外部给予的spi_send_en作为启动信号,启动send_cnt。send_cnt在不发送数据时为8,发送数据时,从0到7。
在接收逻辑中,全部的信号采用上升沿驱动。利用外部给予的spi_read_en作为启动信号,启动rec_en,经过移位接收数据。
在spi_sclk输出时,采用组合逻辑。由于设计采用spi的模式0,故而spi_sclk不发送或者接收数据时为0,接收数据时为时钟信号。因为要求为模式0,所以在接收数据时,spi_sclk的输出不能够先有下降沿,即要求spi_sclk的控制信号不能由上升沿信号驱动,所以将rec_en同步到下降沿的rec_en_n。
在仿真中,将时钟设置为10MHz。
所有的信号采用上升沿驱动。发送一个8bit的随机数值,接收一个8bit的随机数值。
spi_miso信号为从机下降沿驱动信号。
通过RTL仿真,可以看出发送和接收全部正常。
mux7_1设计实现
本模块负责将7个命令模块发出的命令(写使能、写数据和读使能)经过选择发送给spi_8bit_drive模块。
在设计中,有的命令模块不需要进行读取(pp和se等等),此时将输出的读使能信号输出为低电平。
be设计实现
该模块接收到be_en(整片擦除的脉冲信号)信号后,发送对应的使能和数据,等待发送完成脉冲。发送完成后,输出擦除完成的脉冲。
整片擦除的命令为8’hc7。
wren设计实现
该模块接收到wren_en(打开flash内部的写使能的脉冲信号)信号后,发送对应的使能和数据,等待发送完成脉冲。发送完成后,输出擦除完成的脉冲。
打开flash内部写使能的命令码为8’h06。
se设计实现
该模块接收到se_en(擦除扇区的写使能的脉冲信号)信号后,发送对应的使能和数据,等待发送完成脉冲。发送完成后,接着发送高八位地址,中间八位地址和低八位地址。全部发送完成后,发送se_done信号。
该模块采用状态机实现。SE_STATE(扇区擦除命令发送)、H_ADDR(高八位地址发送)、M_ADDR(中间八位地址发送)、L_ADDR(低八位地址发送)、SE_DONE(扇区擦除完成)。所有的脉冲信号在未标注的时刻,输出全部为0。
设计代码为:
在发送过程中,由于是每8bit发送一次,所以在时序上将看到发送时,每8个脉冲一组,中间会有明显的间隔。
pp设计实现
该模块负责将外部写fifo中的数据写入到flash中。wr_fifo_rd为写fifo的读使能信号,wrdata为从写fifo中读出的数据,wr_len为需要写入flash中数据的长度,wr_addr为写入地址。
cnt为记录已经发送的数据个数。
rdsr设计实现
WIP(writeinprogress:正在进行写进程),该bie位表示了flash内部是否在进行写进程。如果处于写进程时,flash忽略外部所有的命令,所以建议在执行任何命令前,首先进行检测该位。1表示正在写进程中,0表示不处于写进程。
如果检测到正在写进程中,进行延迟1ms,然后再次读取该位状态。直到写进程结束。
rdid设计实现
该模块负责读取flash的ID(2015),验证ID的正确性。
该模块采用状态机的方式实现。IDLE(等待读取ID的命令)、IDSTATE1(读取高八位ID)、IDSTATE2(读取中间八位ID)、IDSTATE3(读取低八位ID)、ID_CHECK(检测ID的正确性)。
状态转移图如下:
read_ctrl设计实现
该模块负责将flash的数据读出,写入到输出缓存中。
wr_fifo和rd_fifo调用
两个fifo的宽度设置为8,深度设置为256,同步fifo,带有复位。
ctrl设计实现
该模块根据外部的命令,按照m25p16的执行规则,进行控制各个模块的执行。
该模块采用状态机实现。INIT_RDSR(读WIP),INIT_RDID(读ID),INIT_ID(判断ID),WIP(读WIP),WIP_DONE(等待WIP),IDLE(空闲状态),**STATE(执行对应的命令),**WREN(打开flash的写使能)。在进行任何命令前,都检查wip。
在不同的状态,mux_sel选择对应的命令通过。
drive_busy只有在IDLE状态才是低电平。
spi_cs_n信号,DLE状态为高电平、WIP_DONE(INIT_RDID)中spi_read_done信号为高时(保证能够多次读取状态寄存器)、在其他状态发生切换时,spi_cs_n为高电平,否则为低电平。
m25p16_drive设计实现
本模块负责连接所有二级模块,实现所有的功能。
RTL仿真
仿真代码如下:
在设置testbench时,注意将所有文件全部添加到文件中。
选择testbench时,注意选中设置的m25p16_drive_tb。
利用modelsim仿真,可以得出如下RTL仿真波形。
读到ID,以及检测WIP都是正确的。
板级测试
由于m25p16的时序原因,整个设计工作在10MHz(利用PLL产生)。
在进行测试控制时,对最后一个扇区进行擦除;对最后一个扇区的第一页进行写入数据100个(1至100);对最后一个扇区的第一个进行读取,验证数据是否为1至100。
测试的控制模块命名为test_ctrl。
此模块采用状态机实现。WRFIFO(将1至100写入wrfifo中)、SE(扇区擦除)、PP(写入flash)、RD(读出flash)、WAIT_RD(等待读取)、CHECK(检测读出的数据的正确性)。
将test模块设置为顶层。在test模块中,m25p16_drive例化中,对于整片擦除不做控制,对于addr直接指向最后一个扇区的第一页,len指定为100。
代码为:
由于开发板上的flash是为FPGA进行保存配置信息的,所以管脚都连接在专用管脚上,本次实验需要将这专用管脚配置为普通io。
右击器件型号,选择device。
选择Dual-purposepins,将其中所有的功能改为普通IO。
点击ok后,即可进行综合分析。
连接开发板和PC,打开逻辑分析仪。
采样时钟选择10MHz(PLL的c0),采样深度设置为2K。
观测信号如下图所示。
首先将wrfifo_wr的触发条件设置为上升沿。点击触发后,按下复位按键。触发后,可以看到写入数据1至100后,然后进行SE命令。
将rdfifo_rd的触发条件设置为上升沿(将wrfifo_wr触发条件修改为donotcare)。点击触发后,按下复位按键。
通过仿真和下板实测,验证控制器设计正确。
长沙市望城经济技术开发区航空路6号手机智能终端产业园2号厂房3层(0731-88081133)