【CPLD+Verilog】CPLD实现SPI接口 下载本文

CPLD 实现SPI 接口 1 实现原理

CPLD 实现SPI 接口模块,通过对寄存器的操作,实现SPI 接口功能,对外部SPI 设备进行访问。CPLD 内部SPI 模块逻辑框图如下所示。 CLK_DIV

RX_SHIFT_REG STATUS LOGIC spi_clk

spi_cs spi_do

spi_tx_data_we_pulse

reset_n spi_tx_data spi_rx_data spi_clk_div spi_rx_data_rd_pulse spi_di

spi_tx_data_we_busy spi_rx_data_rd_ready spi_data_tx_we_overflow spi_data_rx_rd_nop spi_data_rx_rd_overtime cpld_clk CONTROL LOGIC COUNT LOGIC

通过对主时钟分频,得到的SPI 接口时钟,其分频值可通过spi_clk_div 值设定,最小为6分频,当设置分频值低于6分频时,默认6分频。

SPI 接口的片选信号spi_cs 在写数据传输寄存器spi_tx_data 时,通过自动产生写脉冲spi_tx_data_we_pulse ,逻辑综合生成。spi_rx_data_rd_pulse 在读取接收数据spi_rx_data 的数据时自动产生。

spi_di 和spi_do 分别由数据移位寄存器通过数据移位产生和接收。

spi_clk 由内部分频时钟和传输状态逻辑综合输出,在有数据时输出,无数据时保持高电 平。

状态逻辑spi_tx_data_we_busy 信号分别表示数据正在传输,无法写入spi_tx_data 寄存器; spi_rx_data_rd_ready 信号表示数据接收完成,可读取spi_rx_data 寄存器的值。

错误状态逻辑spi_data_tx_we_overflow 信号表示在spi_tx_data 寄存器中写入了待传输值 后,还未传输就又写入新的传输数据,表示写数据溢出。spi_data_rx_rd_nop 信号表示在 spi_rx_data寄存器中没有接收值时,去读取了无效的数据,表示读空数据。

spi_data_rx_rd_overtime信号表示在spi_rx_data寄存器中有接收数据时,在下一次接收数据接收完成时都没有读走,表示读取数据超时。

产生接口错误时,通过reset_n复位SPI接口模块,可清楚错误。 最终传输生成的时序图如下。

在数据发送时,将待发送数据写入spi_tx_data,数据将自动发送,之后检测

spi_tx_data_we_busy状态值,待此信号无效(0)时,写入下一个待发送的数据,重复此动作,直到数据发送完成,在正在发送的数据传输完成都没有新数据写入时,传输自动完成。

在数据接收时,将任意数据写入spi_tx_data,然后等待spi_rx_data_rd_ready信号有效(1),然后读取接收到的数据,重复此动作,直到需要接收的数据完成,当不在写入后,传输完成。 2 CPLD代码 module spi( reset_n, cpld_clk, spi_clk_div, spi_tx_data,

spi_tx_data_we_pulse, spi_tx_data_we_busy, spi_rx_data,

spi_rx_data_rd_pulse, spi_rx_data_rd_ready, spi_do, spi_clk, spi_cs, spi_di,

spi_data_tx_we_overflow, spi_data_rx_rd_nop,

spi_data_rx_rd_overtime,

///////////////////////for debug///////////////////////////////// spi_clk_inner,

spi_clk_posedge_pulse, spi_clk_negedge_pulse, data_shift_reg, spi_busy, count_spi, spi_enable, spi_en_sync,

spi_busy_end_pulse, spi_busy_start_pulse );

//////////////////////////内部接口信号//////////////////////////////// input [7:0] spi_clk_div; //设置时钟分频值,得到SPI时钟 input reset_n; //模块复位信号 input cpld_clk; //接口全局时钟

input [7:0] spi_tx_data; //SPI数据传输寄存器。

input spi_tx_data_we_pulse; /*SPI传输数据写入脉冲,脉冲宽度为1个cpld_clk时钟 周期*/

output spi_tx_data_we_busy; //SPI传输数据寄存器写忙标志,1表示不能写 output [7:0] spi_rx_data; //SPI数据接收寄存器

input spi_rx_data_rd_pulse; /*SPI数据接收寄存器读取标志,脉冲宽度为1个cpld_clk时钟周期*/

output spi_rx_data_rd_ready; //SPI接收数据ready标志,1表示可以读取接收数据。

output spi_data_tx_we_overflow; //SPI传输数据写溢出标志,1表示有错误产生output spi_data_rx_rd_nop; //SPI接收数据读无效标志,1表示有错误产生output spi_data_rx_rd_overtime; //SPI接收数据读超时标志,1表示有错误产生 //////////////////////////外出接口信号//////////////////////////////// output spi_do; //SPI接口数据输出信号,MOSI output spi_clk; //SPI接口时钟信号 output spi_cs; //SPI接口片选信号

input spi_di; //SPI接口数据接收信号,MISO

/////////////////////////////for debug/////////////////////////////////

output spi_clk_inner; //分频出的SPI时钟

output spi_clk_posedge_pulse; //分频的SPI时钟上升沿 output spi_clk_negedge_pulse;//分频的SPI时钟下降沿 output [7:0] data_shift_reg; //数据移位寄存器 output spi_busy; //接口忙信号,表示数据正在传输 output [3:0] count_spi; //SPI数据移位传输计数 output spi_enable; //SPI数据传输开始信号

output spi_en_sync; //SPI数据传输开始信号与分频时钟同步信号

output spi_busy_start_pulse; //接口忙开始标志,一个cpld_clk时钟宽度output spi_busy_end_pulse; //接口忙结束标志,一个cpld_clk时钟宽度 //////////////////////////spi reference clock//////////////////////////// reg [7:0] counter_div;

reg spi_clk_inner; //内部分频得到的SPI时钟。 wire[7:0] spi_clk_div;

always@(posedge cpld_clk or negedge reset_n) begin

if(reset_n == 1'b0) begin

counter_div <= 8'h01; spi_clk_inner <= 1'b0; end

else if(spi_clk_div > 8'h06) //当分频倍数大于6时,以分频值分频begin if(counter_div == (spi_clk_div&8'hfe)>>1) //确保偶数倍分频 begin

spi_clk_inner <= ~spi_clk_inner; counter_div <= counter_div+8'h01; end

else if(counter_div == (spi_clk_div&8'hfe)) begin

spi_clk_inner <= ~spi_clk_inner; counter_div <= 8'h01; end

else //当分频倍数小于或等于6时,默认6分频 counter_div <= counter_div+8'h01; end begin

if(counter_div == 8'h03) begin

spi_clk_inner <= ~spi_clk_inner; counter_div <= counter_div+8'h01; end

else if(counter_div == 8'h06) begin