基于AM335X与FPGA的SPI通讯设计
2017-11-09 16:49
190 查看
在2013年的工作中,涉及到了AM3359与XC7K325T之间的相互通信,其目的是为了获取FPGA设计版本号,该FPGA版本号保存在FPGA的寄存器0xFFFF中,FPGA的版本值随着加载程序发生变化,当时的版本信息为0x1003.
需要说明的是,在本文中的代码风格是刚工作两年的时候的代码风格,现在回看,这些代码风格实在难以阅读。尤其是SPI的verilog程序等。并不代表现在的编程水平与代码风格。
设计框图如下:
本文主要从三个方面介绍AM3359与FPGA的通讯
第一部分:SPI通信的基础定义
第二部分:AM335x的SPI通信编程
第三部分:FPGA从机SPI设计
SPI接口是一种典型的全双工接口。通过同步时钟SCLK的脉冲将数据一位一位地在主机和从机之间交换。所以在开始通信之前,Master首先需要配置接口时钟,在Master配置时钟的时候,不需要通知从机它所配置的时钟频率具体是多少,设计人员只需要确保通讯频率是从机所支持的即可。
当Master通过片选信号(SS,低电平有效)选定一个Slave的时候,每向Slave发送一个周期的SCLK信号,都会有1bit的数据从MOSI引脚发送给Slave,Slave只需要在对应的引脚接收数据即可;同时,slave每收到一个周期的SCLK信号,都会从MISO想Master发送1bit的数据。
从上段描述中可以分析出一下两点:
1.无论Master还是Slave,都是按照bit来传输的,那么对于需要发送或者接收的数据,必须在Master或Slave中有一个移位寄存器,这些是由硬件来保证的,普通的SPI接口设计者不需要考虑移位寄存器的因素。
2.在通信中,Master发送数据后,一般需要保证Slave收到数据,这样才能确定数据在收发的过程中不发生因为硬件而导致的bit丢失。在SPI中,数据传输以“位交换”的方式传输,这样能根据从机返回的数据来确定从机已经收到数据了。SPI同样与其他基础通信方式(USB,I2C,UART等)一样无法确保传输数据的正确性。
SPI是一个相对比较开放的接口,具体表现在时钟极性/相位、帧大小、传输速度、LSB/MSB等规则没有一个确定的定义,需要根据不同的通信环境由设计开发者进行定义。
SPI的接口时序
在实际开发使用SPI的时候,需要注意使Master和Slave处于相同的Mode下工作。不同mode的定义主要是针对时钟的相关特性。
SCLK极性(CPOL):clock Polarity
SCLK相位(CPHA):clock Phase
CPOL
在解释CPOL之前先要介绍什么是SCLK的空闲时刻。在SPI通讯传输的时候,SCLK并不是时刻都有。在SCLK发送数据之前和发送数据之后,都会回到空闲状态,这个状态下,SCLK要么保持在高电平,要么保持在低电平。这个是需要设计者来指定的,CPOL的作用就是来指定SPI在IDLE状态下的点评状态。
CPOL = 0 :时钟空闲状态(IDLE)的电平为低电平(0)。
CPOL = 1 :时钟空闲状态(IDLE)的点评是高电平(1)。
CPHA
CPHA表示数据采样,数据有效的时刻。对应的数据采样是在第几个边沿进行采样。
CPHA = 0 :在时钟第一个边沿采样。
对于CPOL = 0 :因为IDLE为低电平,那么第一个边沿就是从低电平到高电平,即为上升沿。
对于CPOL = 1 :因为IDLE为高电平,那么第一个边沿就是从高电平到低电平,即为下降沿。
CPHA = 1 :在时钟第二个边沿采样。
对于CPOL = 0 :因为IDLE为低电平,那么第二个边沿就是从高电平到低电平,即为下降沿。
对于CPOL = 1 :因为IDLE为高电平,那么第二个边沿就是从低电平到高电平,即为上升沿。
需要注意的是:采样一定是需要先准备好数据,才用时钟的有效沿将数据打到对应的引脚上。
Mode选择参考
SPI没有一个通用的推荐模式,但是基于工程设计的时候是否有一个推荐的Mode选择呢?在CSDN博友的一篇《SPI接口扫盲 SPI定义/SPI时序(CPHA CPOL)》中,作者从功耗的角度分析,建议应该多选择SCLK在空闲状态下处于低电平,即CPOL保持在IDLE状态下为0。这是一个很好的分析方法。对于CPHA的选择分析,我更赞成根据实际的应用来做设计,而不是根据习惯来设计。
open()
write()
read()
close()
相关的函数说明,请参考网络,在此不赘述。
在编写SPI驱动的时候,还需用到ioctl()函数,下面对ioctl做一些简要介绍。
什么是ioctl
ioctl是设备驱动程序中对设备的IO通道进行管理的函数。即是对设备的一些特性进行控制,例如对串口设备设置波特率,对SPI设备设置字长,通讯速率等。函数的调用方式如下:
其中fd是用户程序打开设备是使用open()函数返回的文件标识符(句柄),cmd是用户程序对设备的控制命令,后面的省略号,表示该函数可能还有其他参数,该参数与cmd相关。
对于ioctl的详细描述,可以参考文末链接1来详细阅读。
AM335x驱动程序源码设计
附:spidev的ioctl命令。
SPI_IOC_RD_MODE: 读取spi_device的mode。
SPI_IOC_RD_LSB_FIRST: 如果是SPI_LSB_FIRST的方式则返回1。
SPI_IOC_RD_BITS_PER_WORD: 读取spi_device的bits_per_word.
SPI_IOC_RD_MAX_SPEED_HZ: 读取spi_device的max_speed_hz.
SPI_IOC_WR_MODE: 设置spi_device的mode,并调用spi_setup立即使设置生效。
SPI_IOC_WR_LSB_FIRST: 设置spi使用SPI_LSB_FIRST的传输模式。立即生效。
SPI_IOC_WR_BITS_PER_WORD: 读取字长。
SPI_IOC_WR_MAX_SPEED_HZ: 设置时钟速率。
AM335x应用程序设计
FPGA的SPI command verilog设计
以上项目总结仅作参考,适用于本公司后继者对此工程的修改参考。
链接1 :点击打开链接
需要说明的是,在本文中的代码风格是刚工作两年的时候的代码风格,现在回看,这些代码风格实在难以阅读。尤其是SPI的verilog程序等。并不代表现在的编程水平与代码风格。
设计框图如下:
本文主要从三个方面介绍AM3359与FPGA的通讯
第一部分:SPI通信的基础定义
第二部分:AM335x的SPI通信编程
第三部分:FPGA从机SPI设计
SPI通信基础定义
通信是怎么发生SPI接口是一种典型的全双工接口。通过同步时钟SCLK的脉冲将数据一位一位地在主机和从机之间交换。所以在开始通信之前,Master首先需要配置接口时钟,在Master配置时钟的时候,不需要通知从机它所配置的时钟频率具体是多少,设计人员只需要确保通讯频率是从机所支持的即可。
当Master通过片选信号(SS,低电平有效)选定一个Slave的时候,每向Slave发送一个周期的SCLK信号,都会有1bit的数据从MOSI引脚发送给Slave,Slave只需要在对应的引脚接收数据即可;同时,slave每收到一个周期的SCLK信号,都会从MISO想Master发送1bit的数据。
从上段描述中可以分析出一下两点:
1.无论Master还是Slave,都是按照bit来传输的,那么对于需要发送或者接收的数据,必须在Master或Slave中有一个移位寄存器,这些是由硬件来保证的,普通的SPI接口设计者不需要考虑移位寄存器的因素。
2.在通信中,Master发送数据后,一般需要保证Slave收到数据,这样才能确定数据在收发的过程中不发生因为硬件而导致的bit丢失。在SPI中,数据传输以“位交换”的方式传输,这样能根据从机返回的数据来确定从机已经收到数据了。SPI同样与其他基础通信方式(USB,I2C,UART等)一样无法确保传输数据的正确性。
SPI是一个相对比较开放的接口,具体表现在时钟极性/相位、帧大小、传输速度、LSB/MSB等规则没有一个确定的定义,需要根据不同的通信环境由设计开发者进行定义。
SPI的接口时序
在实际开发使用SPI的时候,需要注意使Master和Slave处于相同的Mode下工作。不同mode的定义主要是针对时钟的相关特性。
SCLK极性(CPOL):clock Polarity
SCLK相位(CPHA):clock Phase
CPOL
在解释CPOL之前先要介绍什么是SCLK的空闲时刻。在SPI通讯传输的时候,SCLK并不是时刻都有。在SCLK发送数据之前和发送数据之后,都会回到空闲状态,这个状态下,SCLK要么保持在高电平,要么保持在低电平。这个是需要设计者来指定的,CPOL的作用就是来指定SPI在IDLE状态下的点评状态。
CPOL = 0 :时钟空闲状态(IDLE)的电平为低电平(0)。
CPOL = 1 :时钟空闲状态(IDLE)的点评是高电平(1)。
CPHA
CPHA表示数据采样,数据有效的时刻。对应的数据采样是在第几个边沿进行采样。
CPHA = 0 :在时钟第一个边沿采样。
对于CPOL = 0 :因为IDLE为低电平,那么第一个边沿就是从低电平到高电平,即为上升沿。
对于CPOL = 1 :因为IDLE为高电平,那么第一个边沿就是从高电平到低电平,即为下降沿。
CPHA = 1 :在时钟第二个边沿采样。
对于CPOL = 0 :因为IDLE为低电平,那么第二个边沿就是从高电平到低电平,即为下降沿。
对于CPOL = 1 :因为IDLE为高电平,那么第二个边沿就是从低电平到高电平,即为上升沿。
需要注意的是:采样一定是需要先准备好数据,才用时钟的有效沿将数据打到对应的引脚上。
Mode选择参考
SPI没有一个通用的推荐模式,但是基于工程设计的时候是否有一个推荐的Mode选择呢?在CSDN博友的一篇《SPI接口扫盲 SPI定义/SPI时序(CPHA CPOL)》中,作者从功耗的角度分析,建议应该多选择SCLK在空闲状态下处于低电平,即CPOL保持在IDLE状态下为0。这是一个很好的分析方法。对于CPHA的选择分析,我更赞成根据实际的应用来做设计,而不是根据习惯来设计。
AM335x的SPI通信编程
在本工程中所使用的SPI为AM335x的SPI外设,即在Linux下,只需要对spidev进行文件操作。所用到的文件操作函数有以下四个:open()
write()
read()
close()
相关的函数说明,请参考网络,在此不赘述。
在编写SPI驱动的时候,还需用到ioctl()函数,下面对ioctl做一些简要介绍。
什么是ioctl
ioctl是设备驱动程序中对设备的IO通道进行管理的函数。即是对设备的一些特性进行控制,例如对串口设备设置波特率,对SPI设备设置字长,通讯速率等。函数的调用方式如下:
int ioctl(int fd,unsigned long cmd,...); /* fd:文件描述符 cmd:控制命令 fecc ...:可选参数:插入*argp,具体内容依赖于cmd */
其中fd是用户程序打开设备是使用open()函数返回的文件标识符(句柄),cmd是用户程序对设备的控制命令,后面的省略号,表示该函数可能还有其他参数,该参数与cmd相关。
对于ioctl的详细描述,可以参考文末链接1来详细阅读。
AM335x驱动程序源码设计
static uint8_t mode = 0; static uint8_t bits = 8; static uint32_t speed = 16000000; static uint8_t cs = 1; int fpga_fd = 0; uint8_t file_name_buf[FILE_NAME_MAX] = "/dev/spidev2.1"; //== int fpga_config(uint8_t *file_name) { if (strlen(file_name) > FILE_NAME_MAX) { printf("File name length error\r\n"); return 0; } strcpy(file_name_buf, file_name); return 1; } int fpga_spi_open(uint8_t *file_name) { int ret = 0; fpga_fd = open(file_name, O_RDWR); if (fpga_fd < 0) { printf("in fpga_spi_open, can't open device\r\n"); return nQAM_ERROR_CAS_SPI_OPEN; } else { printf("in fpga_spi_open, open device success\r\n"); } /* * spi mode */ ret = ioctl(fpga_fd, SPI_IOC_WR_MODE, &mode); if (ret == -1) { printf("in fpga_spi_open, can't set spi mode\r\n"); return nQAM_ERROR_CAS_SPI_CONFIG; } else { printf("in fpga_spi_open, set spi mode success\r\n"); } /* * bits per word */ ret = ioctl(fpga_fd, SPI_IOC_WR_BITS_PER_WORD, &bits); if (ret == -1) { printf("in fpga_spi_open, can't set bits per word\r\n"); return nQAM_ERROR_CAS_SPI_CONFIG; } else { printf("in fpga_spi_open, set bits per word success\r\n"); } /* * max speed hz */ ret = ioctl(fpga_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); if (ret == -1) { printf("in fpga_spi_open, can't set max speed hz\r\n"); return nQAM_ERROR_CAS_SPI_CONFIG; } else { printf("in fpga_spi_open, set max speed success\r\n"); } /* * chip select */ ret = ioctl(fpga_fd,SPI_IOC_WR_CHIP_SELECT,&cs); if(ret == -1) { printf("in fpga_spi_configure,can't set chip select\r\n"); return nQAM_ERROR_CAS_SPI_CONFIG; } return nQAM_ERROR_NOERROR; } int fpga_spi_close() { return close(fpga_fd); // close device } //数据交换 uint8_t transfer(uint8_t data) { uint8_t sbuf = data; if(write(fpga_fd,&sbuf,1) != -1) { if(read(fpga_fd, &sbuf, 1) != -1) { printf("In transfer, transfer data over\r\n"); return sbuf; } else { printf("In transfer, read data from spi device failed!\r\n"); } } else { printf("In transfer, write data to spi device failed!\r\n"); } return 0; } //通过SPI获取版本号 uint8_t get_fpga_version() { uint8_t buf; if(fpga_spi_open(file_name_buf) != nQAM_ERROR_NOERROR) { printf("in get_fpga_vesion, fpga spi open failed\r\n"); return 0; } buf = transfer(READ_VESION); fpga_spi_close(); printf("Read version done.\r\n"); return buf; } //测试物理连接状态 uint8_t test_fpga_connect() { uint8_t buf; if(fpga_spi_open(file_name_buf) != nQAM_ERROR_NOERROR) { printf("in test_spi_open, fpga spi open failed\r\n"); return 0; } buf = transfer(TEST_CONNECT); fpga_spi_close(); printf("FPGA spi test connect done.\r\n"); return buf; }
附:spidev的ioctl命令。
SPI_IOC_RD_MODE: 读取spi_device的mode。
SPI_IOC_RD_LSB_FIRST: 如果是SPI_LSB_FIRST的方式则返回1。
SPI_IOC_RD_BITS_PER_WORD: 读取spi_device的bits_per_word.
SPI_IOC_RD_MAX_SPEED_HZ: 读取spi_device的max_speed_hz.
SPI_IOC_WR_MODE: 设置spi_device的mode,并调用spi_setup立即使设置生效。
SPI_IOC_WR_LSB_FIRST: 设置spi使用SPI_LSB_FIRST的传输模式。立即生效。
SPI_IOC_WR_BITS_PER_WORD: 读取字长。
SPI_IOC_WR_MAX_SPEED_HZ: 设置时钟速率。
AM335x应用程序设计
int main(int argc, char *argv ) { uint8_t connect; uint8_t version; connect = test_fpga_connect(); printf("Test Value is %02X\r\n", connect); //测试连接状态 version = get_fpga_version(); printf("the fpga version is %02X\r\n", version); printf("****fpga app test run over!****\r\n"); return 1; }
FPGA从机SPI设计
SPI从机的Verilog实现module spi_slave(clk,rst,data_i,wr_en_i,data_o,tx_valid,valid_o,start_o,end_o,we_ack_o,ss_i,sclk_i,mosi_i,miso_o ); input clk; // master clock input input rst; // synchronous active high reset input [7:0] data_i; // data bus input input wr_en_i; // write enable input output [7:0] data_o; // data bus output output valid_o; // request signal output output tx_valid; output start_o; output end_o; output reg we_ack_o; //spi signals input ss_i; // slave select input sclk_i; // serial clock input mosi_i; output miso_o; reg [7:0] tx_data; reg [7:0] rx_data; reg tx_tip; //tx in progress reg rx_tip; //rx in progress wire rx_negedge; //miso is sampled on negative edge wire tx_negedge; //mosi is driven on nesedge edge wire [2:0] len; //char length wire lsb; //lsb first on line wire pos_edge; //recognize posedge of sclk wire neg_edge; //recognize negdege of sclk reg s_out; reg s_in; reg [2:0] s_sel; reg [2:0] s_clk; assign rx_negedge = 0; // decided by CPOL and CPHA assign tx_negedge = 1; // means mode == 00 assign len = 7; assign lsb = 0; assign miso_o = s_out; assign valid_o = rst? 1'b0 : (!rx_tip); assign data_o = rst? 8'h00 : rx_data; //sync SCK to the FPGA clock using a 3-bits shift register always @ (posedge clk) begin s_clk <= {s_clk[1:0], sclk_i}; //sample the sclk_i using clk,when finding the first posedge the value is 001,when finding the first negedge the value is 110 or 010 end assign pos_edge = (s_clk[1:0] == 2'b01); // posedge when s_clk[1:0] == 2'b01 or s_clk[2:0] = 3'b001 assign neg_edge = (s_clk[1:0] == 2'b10); // negedge when s_clk[1:0] == 2'b10 or s_clk[2:0] = 3'b110 //SSEL always @ (posedge clk) begin s_sel <= {s_sel[1:0], ss_i}; //sample the ss signal end wire sel_active; // from start_o is high to end_o is high sel_active is active assign sel_active = ~s_sel[1]; // sel[2:0] = 000 001 011 111 110 100 000 when 100 000 001 .0 is high assign start_o = (s_sel[1:0] == 2'b10); // start_o when s_sel[1:0] = 2'b10 or s_sel[2:0]= 3'b110 assign end_o = (s_sel[2:1] == 2'b01); // end_o when s_sel[2:1] = 2'b01 or s_sel[2:0] = 3'b011 //---------------------receiving bits from line--------------------- wire rx_clk; wire rx_lst_bit; reg [2:0] rx_cnt; // rx data bit count assign rx_clk = rx_negedge? neg_edge : pos_edge; // question is the beginning value is "X" assign rx_lst_bit = !(|rx_cnt); always @(posedge clk) begin s_in <= mosi_i; end always @(posedge clk or posedge rst) begin if (rst) rx_cnt <= len; else begin if(!rx_tip || end_o || start_o) rx_cnt <= len; else rx_cnt <= rx_clk ? (rx_cnt - 1'b1) : rx_cnt; //question is the rx_cnt always is 7? end end //Transfer in process always @(posedge clk or posedge rst) begin if(rst) rx_tip <= 1'b0; else if(!rx_tip) rx_tip <= 1'b1; else if(rx_tip && rx_lst_bit && rx_clk) rx_tip <= 1'b0; end always @(posedge clk or posedge rst) begin if(rst) rx_data <= {8{1'b0}}; else begin if(sel_active && rx_clk) rx_data <= lsb ? {s_in,rx_data[7:1]} : {rx_data[6:0],s_in}; // if lsb = 0 rx_data = {rx_data[6:0],s_in} // if lsb = 1 rx_data = {s_in,rx_data[7:1]} end // {s_in,rx_data[7:1]} shift right end // {rx_data[6:0],s_in} shift left //---------------------sending bits to line--------------------- wire tx_clk; wire tx_lsb_bit; reg [2:0] tx_cnt; assign tx_clk = tx_negedge? neg_edge : pos_edge; // tx_negedge = 1 negedge transfer data assign tx_lsb_bit = !(|tx_cnt); assign tx_valid = ((s_sel[2:0] == 3'b111) && (tx_tip == 1'b0)) ? 1'b1 : 1'b0; // always @(posedge clk or posedge rst) // begin // if(rst) // tx_valid <= 1'b0; // else if((s_sel[2:0] == 3'b111) && (tx_tip == 1'b0)) // tx_valid <= 1'b1; // else // tx_valid <= 1'b0; // end // character bit counter always @(posedge clk or posedge rst) begin if(rst) tx_cnt <= len; else begin if(!tx_tip || end_o || start_o) tx_cnt <= len; else tx_cnt <= tx_clk? (tx_cnt-1'b1):tx_cnt; end end //transfer in process always @(posedge clk or posedge rst) begin if(rst) begin tx_tip <= 1'b0; end else if(wr_en_i && (!tx_tip)) begin //wr_en_i is high when transfer the data tx_tip <= 1'b1; end else if(tx_tip && tx_lsb_bit && tx_clk) begin tx_tip <= 1'b0; end end always @(posedge clk or posedge rst) begin if(rst) begin tx_data <= 8'hff; we_ack_o <= 1'b0; end else begin we_ack_o <= 1'b0; if(wr_en_i && (!tx_tip)) begin tx_data[7:0] <= data_i[7:0]; we_ack_o <= 1'b1; end else begin if(sel_active && rx_clk) begin s_out <= lsb? tx_data[0] : tx_data[7]; tx_data <= lsb? {1'b1,tx_data[7:1]} : {tx_data[6:0],1'b1}; end end end end endmodule
FPGA的SPI command verilog设计
`timescale 1 ns / 1 ps module spi_cmd #( parameter BUS_DATA_WIDTH = 8, parameter BUS_ADDR_WIDTH = 16 )( input clk, input rst, input [BUS_DATA_WIDTH-1:0] rx_data, input rx_valid, input rx_start, input rx_end, input tx_valid, input tx_ack, output reg [BUS_DATA_WIDTH-1:0] tx_data, output reg tx_req ); localparam CON_WR_REG = 8'h51; localparam CON_RD_REG = 8'h52; localparam RD_DATA_REG = 8'h96; localparam RST_CMD = 8'h55; localparam ADDR_FPGA_VERSION = 16'hFFFF; localparam ADDR_SPI_TEST = 16'hF000; localparam FPGA_VERSION = 16'h1003; localparam REC_NO_ERR = 8'h90; localparam REC_INS_ERR = 8'hF1; localparam REC_LEN_ERR = 8'hF2; localparam REC_BUSY_ERR = 8'hF3; localparam REC_WR_ERR = 8'hF4; localparam REC_START = 1; localparam REC_TYPE_REG = 2; localparam REC_ADDR_REGH = 3; localparam REC_ADDR_REGL = 4; localparam REC_ADDR_LEN = 5; localparam REC_CON_WRH = 6; localparam REC_CON_WRL = 7; localparam RSP_STATUS = 8; localparam RSP_LEN = 9; localparam RSP_DIN = 10; localparam RSP_DOUT = 11; localparam READ_DATAH = 12; localparam READ_DATAL = 13; localparam READ_DONE = 14; reg [7:0] rec_type; reg [15:0] rec_addr; reg [7:0] addr_len; reg [7:0] len_count; reg [3:0] cmd_state; reg [7:0] err_code; reg [15:0] rsp_dout; reg rsp_rd; reg [15:0] spi_test; reg [3:0] test_flag; always@(posedge clk or posedge rst) begin if(rst) begin rec_type <= 0; rec_addr <= 0; addr_len <= 0; len_count <= 0; err_code <= REC_NO_ERR; tx_data <= 0; tx_req <= 0; rsp_rd <= 1'b0; cmd_state <= REC_START; test_flag <= 4'h0; end else begin if(tx_ack) begin tx_req <= 1'b0; end case(cmd_state) REC_START : begin len_count <= 0; if(rx_start) begin cmd_state <= REC_TYPE_REG; test_flag <= 4'h1; end end REC_TYPE_REG : begin if(rx_valid) begin case(rx_data) CON_WR_REG, CON_RD_REG : begin err_code <= REC_NO_ERR; rec_type <= rx_data; cmd_state <= REC_ADDR_REGH; test_flag <= 4'h2; end RD_DATA_REG : begin cmd_state <= RSP_STATUS; test_flag <= 4'h6; end default : cmd_state <= REC_START; endcase end end REC_ADDR_REGH : begin if(rx_valid) begin rec_addr[15:8] <= rx_data; cmd_state <= REC_ADDR_REGL; test_flag <= 4'h3; end end REC_ADDR_REGL : begin if(rx_valid) begin rec_addr[7:0] <= rx_data; cmd_state <= REC_ADDR_LEN; test_flag <= 4'h4; end end REC_ADDR_LEN : begin if(rx_valid) begin if(rx_data != 0) begin addr_len <= rx_data; test_flag <= 4'h5; case(rec_type) CON_WR_REG : cmd_state <= REC_CON_WRH; CON_RD_REG : cmd_state <= REC_START; default : cmd_state <= REC_START; endcase end else begin err_code <= REC_LEN_ERR; cmd_state <= REC_START; end end end REC_CON_WRH : begin if(len_count == addr_len) begin cmd_state <= REC_START; end else begin if(rx_valid) begin if(rec_addr == ADDR_FPGA_VERSION) begin err_code <= REC_WR_ERR; cmd_state <= REC_START; end else if(rec_addr == ADDR_SPI_TEST) begin spi_test[15:8] <= rx_data; end // else begin // rec_buf_data[15:8] <= rx_data; // end cmd_state <= REC_CON_WRL; end end end REC_CON_WRL : begin if(rx_valid) begin if(rec_addr == ADDR_SPI_TEST) begin spi_test[7:0] <= rx_data; end // else begin // rec_buf_data[7:0] <= rx_data; // rec_addr <= rec_addr + 1'b1; // end len_count <= len_count + 1'b1; end end RSP_STATUS : begin if(tx_valid) begin if(tx_ack) begin tx_req <= 1'b0; end else begin tx_req <= 1'b1; end rsp_rd <= 1'b1; tx_data <= err_code; cmd_state <= RSP_LEN; test_flag <= 4'h7; end end RSP_LEN : begin if(tx_valid) begin if(tx_ack) begin tx_req <= 1'b0; end else begin tx_req <= 1'b1; end case(rec_type) CON_WR_REG : begin tx_data <= 0; cmd_state <= REC_START; end CON_RD_REG : begin tx_data <= addr_len; cmd_state <= RSP_DIN; end default : begin tx_data <= 0; cmd_state <= REC_START; end endcase test_flag <= 4'h8; end end RSP_DIN : begin if(tx_valid) begin if(tx_ack) begin tx_req <= 1'b0; end else begin tx_req <= 1'b1; end if(len_count == addr_len) begin cmd_state <= READ_DONE; end else begin cmd_state <= RSP_DOUT; rsp_rd <= 1'b0; end test_flag <= 4'h9; end end RSP_DOUT : begin if(tx_valid) begin if(tx_ack) begin tx_req <= 1'b0; end else begin tx_req <= 1'b1; end cmd_state <= READ_DATAH; end end READ_DATAH : begin if(tx_valid) begin if(tx_ack) begin tx_req <= 1'b0; end else begin tx_req <= 1'b1; end tx_data <= rsp_dout[15:8]; cmd_state <= READ_DATAL; test_flag <= 4'hA; end end READ_DATAL : begin if(tx_valid) begin if(tx_ack) begin tx_req <= 1'b0; end else begin tx_req <= 1'b1; end tx_data <= rsp_dout[7:0]; len_count <= len_count + 1'b1; cmd_state <= RSP_DIN; test_flag <= 4'hB; end end READ_DONE : begin if(tx_valid) begin if(tx_ack) begin tx_req <= 1'b0; end else begin tx_req <= 1'b1; end tx_req <= 1'b0; len_count <= 0; tx_data <= 0; rec_type <= 0; cmd_state <= REC_START; end end endcase end end always @(posedge clk or posedge rst) begin if(rst == 1'b1) begin rsp_dout <= {16{1'b0}}; end else if((rsp_rd == 1'b1) && (test_flag == 4'h7)) begin case(rec_addr) ADDR_FPGA_VERSION : rsp_dout <= FPGA_VERSION; ADDR_SPI_TEST : rsp_dout <= ~spi_test; default : ; endcase end end endmodule
以上项目总结仅作参考,适用于本公司后继者对此工程的修改参考。
链接1 :点击打开链接
相关文章推荐
- 基于FPGA的串口通讯设计与实现
- 基于fpga的处理器核心电路设计
- 基于FPGA的RapidIO节点设计和实现
- 基于FPGA的多通道数据采集系统设计
- 【iCore3 双核心板_FPGA】实验二十八:基于SDRAM 的VGA 驱动器的设计
- 基于iocp的SOCKET通讯软件的设计
- [置顶] 基于FPGA的卷积网络加速设计
- 基于XC2V1000型FPGA的FIR抽取滤波器的设计
- 基于fpga设计9/7小波变换的解交织过程详解
- 基于FPGA的以太网MII接口扩展设计与实现
- 基于FPGA的UART接口协议设计
- C#基于事件驱动的多串口多线程串口通讯软件架构设计
- 基于FPGA的呼吸灯设计
- 基于FPGA的音频处理芯片的设计
- 基于Android 平台简易即时通讯的研究与设计[转]
- 基于FPGA 的简化UART 电路设计【转载】
- 基于Windows CE的SPI驱动程序设计
- 基于FPGA/CPLD设计与实现UART
- 基于FPGA的以太网MAC子层协议设计实现
- 基于AT91SAM9263的ARM9核心模块-- 集ARM处理器、FPGA于一体的紧凑结构设计