学FPGA没多久,希望看到的有缘人可以一起讨论学习 0.o
代码:https://gitee.com/hywhj34343453/spi-host-driver
1.总体说明
这是一个四线标准SPI主机驱动。
可以通过设置参数CPOL和CPHA来控制SPI传输模式。可以通过修改spi_settings.v来自定义指令,地址,写数据,读数据位宽。接口如图所示:
2.接口说明
(1)系统时钟和复位:i_clk , i_rst
(2)用于指示要与哪个从机通讯的指示信号: [1:0] i_slave_mem
(3)用户指令接口,包括:
①用于指示当前指令类型的指示信号:[1:0] i_user_op_type,目前支持3种指令类型:
OP_INS = 2'd0;
OP_READ = 2'd1;
OP_WRITE = 2'd2;
其中:
INS为纯指令型,当指令输入完毕后,片选信号就会拉高。
READ为读指令类型,当指令输入完毕后,还要接收地址。当地址接收完毕后,主机会采集MISO线上的数据作为读数据。
WRITE为写指令类型,当指令输入完毕后,还要接收地址,地址接收完毕后,还要接收写数据。
②接收的指令,地址信号 : i_user_op_ins , i_user_op_addr
③用户指令和SPI主机的握手信号: i_user_op_valid , o_user_op_ready
- 用户数据接口,包括:
①写数据相关的:
i_user_wr_len : 写数据长度信号,用于控制主机将接收的写数据个数。
i_user_wr_data: 写数据
o_user_data_req:写数据请求信号,当写请求拉高后,用户需要向主机发送数据。即当写请求拉高后的一个周期的i_user_wr_data会被作为输入信号,写入MOSI。
②读数据相关:
i_user_rd_len:读数据长度,用户控制将要从MISO线上读取的数据个数。
i_user_rd_data:从MISO线上读取的数据
o_user_rd_data_valid:读取数据有效信号
- SPI接口
四线标准SPI接口,SPI_CLK,MISO,MOSI,SS
3.内部实现逻辑
状态机
基本逻辑是:当用户模块与SPI主机驱动模块指令握手成功后,进入START状态,START状态下,作为指示SPI主机驱动开始工作的running信号被拉高。这个running信号将控制:
①片选信号的拉低。
②spi_clk_cnt的跳变。后者是用来对SPI_CLK的边沿进行监测的信号。SPI_CLK将在片选信号拉低后,开始跳变。
③MOSI将在running有效的情况下,按照传输时序开始采样,发送数据。
START状态仅仅一个周期,状态机自动跳转到INS状态,寄存的指令数据将要按照传输模式的需求,利用spi_clk_cnt信号移位。MOSI也利用spi_clk_cnt信号,在特定的时刻采样。期间引入r_bit_cnt信号,作为采样数据的计数器,每采样1bit加1,当在INS状态下,r_bit_cnt等于INS_WIDTH - 1时,则说明指令输入完毕。根据传输模式的不同,在不同时刻进入下一状态。如果当前指令类型是纯指令类型,则状态跳转到FINISH,如果是读写指令,则跳转到ADDR。ADDR状态下,需要传输的是地址信息,这个工程目前默认只有一个地址。它的传输逻辑和指令类似。当地址传输完成后,会判断当前指令类型是读还是写。如果是写,就进入DATA,如果是读,就进入READ。对于这两个状态,引入新的计数器:r_byte_cnt.当状态是DATA时,在状态跳转前,先会拉高一次写请求,之后会在每次r_bit_cnt等于DATA_WIDTH - 1的时候,根据传输模式在不同周期拉高一次。DATA状态将持续到r_byte_cnt达到最大值i_user_wr_len - 1,且r_bit_cnt也计数到最大值时,跳转到FINISH状态。READ状态同理,不同的是,读数据是根据不同传输模式,对MISO移位拼接而成,当成功组成位宽为RD_WIDTH - 1的数据后,拉高o_user_rd_data_valid。当读取数据的r_byte_cnt计数到最大值i_user_rd_len - 1后,状态进入到FINISH。
FINISH状态下,running信号被拉低。相关联的,片选信号拉高,SPI_CLK,SPI_CLK_CNT恢复默认值。主机驱动准备信号拉高。
4.功能仿真
写了一个回环测试,用另一个相同的主机驱动来临时充当一下从机。
先看回环读写是否正确:
下图是作为主机的写操作,可以看到,当req拉高后,数据一次是1,2,3,4,5,6
然后下图是作为从机的读操作,可以看到,读取数据也是1,2,3,4,5.....
然后看具体的主机驱动时序是否合规,以写数据为例:
测试指令和地址如下图,分别是8’b10101010 9’b111001100
测试写数据就是回环读写用的自增数据。
下面是CPOL=0,CPHA=0的写指令下的功能时序图:
这张图是指令和地址,可以看到,在第一个时钟沿,采样数据是:10101010111001100,和测试用指令和地址是一样的。
这是数据部分,采样的数据是:0000000001_0000000010(因为我测试用数据位宽是10)也就是1和2,数据写正确。
接着看CPOL = 0,CPHA=1是否正确。
可以看到结果也是正确的。