ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FPGA SPI协议解析

FPGA SPI协议解析 1 SPI协议基础spiSerial Peripheral Interface串行外设接口是同步、全双工、主从架构的串行通信总线由摩托罗拉提出广泛用于 FPGA/MCU 与 Flash、ADC、DAC、射频芯片、传感器之间短距离板内通信。特点同步时钟无需单独收发时钟线没有内置 ACK 应答无硬件校验可靠性依赖上层逻辑速率高一般 MHz~ 几十 MHz 级别。单主机多从机系统只能有 1 个 SPI 主机多个从机独立使用各自 CS 信号。全双工特性移位寄存器交换。主机和从机内部各有一个移位寄存器SCK 驱动下双方同时移位主机移出 bit 到 MOSI从机移出 bit 到 MISO每 1 个 SCK 脉冲双方交换 1bit。1.1硬件信号4 线标准 SPI变种3 线 SPIMOSI 与 MISO 合并为一根双向数据线半双工。QSPI4 根双向数据线并行传输用于 Flash带宽提升。1.2模式定义CPOL、CPHA4 种工作模式SPI 时序由两个参数决定主机、从机必须保持一致否则通信出错。CPOLClock Polarity时钟极性SCK 空闲状态电平CPOL0空闲 SCK 为低电平CPOL1空闲 SCK 为高电平CPHAClock Phase时钟相位数据采样沿CPHA0第一个时钟沿采样CPHA1第二个时钟沿采样2 仿真验证2.1 仿真环境仿真工具vivado Simulator2.2 仿真结果说明Spi master mode0Spi master mode1Spi master mode2Spi master mode33 实际应用3.1 spi做slave自定义寄存器读写本文截取实际项目中cpu与fpga中spi接口做寄存器读写由cpu通过spi总线读取fpga自定义的寄存器此处SPI 采用MODE0(CPOL0,CPHA0) 主时钟50MhzSPI 速率 5Mhz。1.SPI读写时序SPI读写指令由48二进制数D[47:0]组成其中读写标识控制位和地址位为16bit读写数据位宽为32bit。表1SPI指令格式Bits说明D[47] 读写控制字W/R1bit1: 写E数据 0 : 读数据D[46:44]字节控制INB3bit000~111分别代表读写1~8D[43:32]地址控制addr12bit12bits 地址D[31:0]写数据32bit写数据时在地址后紧跟的数据D[31:0]读数据32bit读数据时紧跟都地址后的数据读写时序如下3.2 spi做slave代码timescale 1ns/10ps // Company: // File: spi_slave.v // File history: // Description: 1bit W/R3bits INB 12bits ADDR 32bits DATA // CMD W:1 R:0 // INB: 000~111 指示 读写1~8字节此处固定为4字节 // 地址12bit 地址 共4096个寄存器 // 数据32bit 寄存器数据位宽 // Targeted device: Family::SmartFusion2 Die::M2S090T Package::484 FBGA // Description: SPI (Serial Peripheral Interface) Slave // Creates slave based on input configuration. // Receives a byte one bit at a time on MOSI // Will also push out byte data one bit at a time on MISO. // Any data on input byte will be shipped out on MISO. // Supports multiple bytes per transaction when CS_n is kept // low during the transaction. // // Note: i_Clk must be at least 4x faster than i_SPI_Clk // MISO is tri-stated when not communicating. Allows for multiple // SPI Slaves on the same interface. // // Parameters: SPI_MODE, can be 0, 1, 2, or 3. See above. // Can be configured in one of 4 modes: // Mode | Clock Polarity (CPOL/CKP) | Clock Phase (CPHA) // 0 | 0 | 0 // 1 | 0 | 1 // 2 | 1 | 0 // 3 | 1 | 1 /////////////////////////////////////////////////////////////////////////////// module SPI_Slave # ( parameter SPI_MODE 0, parameter DATA_WIDTH 8 ) ( // Control/Data Signals, input i_sys_rst, // FPGA Reset, active low input i_sys_clk, // FPGA Clock output reg o_rx_valid, // Data Valid pulse (1 clock cycle) output reg [DATA_WIDTH-1:0] o_rx_data, // Byte received on MOSI input i_tx_valid, // Data Valid pulse to register i_TX_Byte input [DATA_WIDTH-1:0] i_tx_data, // Byte to serialize to MISO. output o_spi_wr, output reg o_spi_rd, output reg [11:0] o_spi_rd_addr, // SPI Interface input i_spi_sclk, output o_spi_miso, input i_spi_mosi, input i_spi_cs // active low ); // SPI Interface (All Runs at SPI Clock Domain) wire w_CPOL; // Clock polarity wire w_CPHA; // Clock phase wire w_spi_clk; // Inverted/non-inverted depending on settings wire w_spi_miso_mux; reg [7:0] r_rxbit_cnt; reg [7:0] r_txbit_cnt; reg r_rx_done ; reg [3:0] r_rx_valid ; reg [DATA_WIDTH-1:0] r_rx_data_tmp,r_rx_data; reg r_RX_Done, r2_RX_Done, r3_RX_Done; reg [DATA_WIDTH-1:0] r_tx_data; reg r_spi_miso_bit, r_preload_miso; reg r_spi_rd; reg r_spi_rd_vld; reg [11:0]r_spi_rd_addr; // CPOL: Clock Polarity // CPOL0 means clock idles at 0, leading edge is rising edge. // CPOL1 means clock idles at 1, leading edge is falling edge. // CPHA: Clock Phase // CPHA0 means the out side changes the data on trailing edge of clock // the in side captures data on leading edge of clock // CPHA1 means the out side changes the data on leading edge of clock // the in side captures data on the trailing edge of clock assign w_CPOL (SPI_MODE 2) | (SPI_MODE 3); assign w_CPHA (SPI_MODE 1) | (SPI_MODE 3); assign w_spi_clk w_CPHA ? ~i_spi_sclk : i_spi_sclk; // Purpose: Recover SPI Byte in SPI Clock Domain // Samples line on correct edge of SPI Clock //SPI MOSI// always (posedge w_spi_clk or posedge i_spi_cs) begin if (i_spi_cs) begin r_rxbit_cnt 0; r_rx_done 1b0; r_spi_rd 0; r_spi_rd_vld0; r_spi_rd_addrd0; end else begin r_rxbit_cnt r_rxbit_cnt 1; // Receive in LSB, shift up to MSB r_rx_data_tmp {r_rx_data_tmp[DATA_WIDTH-2:0], i_spi_mosi}; r_spi_rd (~i_spi_mosi(r_rxbit_cntd0))?1:r_spi_rd; r_spi_rd_vld ( r_rxbit_cntd15)? 1b1:1b0; r_spi_rd_addr ( r_rxbit_cntd15)?{r_rx_data_tmp[10:0], i_spi_mosi}:r_spi_rd_addr; // addr[11:0] if (r_rxbit_cnt DATA_WIDTH-1) begin r_rx_done 1b1; r_rx_data {r_rx_data_tmp[DATA_WIDTH-1:0], i_spi_mosi}; end else r_rx_done 1b0; end // else: !if(i_SPI_CS_n) end // always (posedge w_SPI_Clk or posedge i_SPI_CS_n) always (posedge i_sys_clk or posedge i_sys_rst) begin if (i_sys_rst) begin r_rx_valid 3d0; o_rx_valid d0; o_rx_data d0; end else begin r_rx_valid {r_rx_valid[2:0],r_rx_done}; if (r_rx_valid[1] ~r_rx_valid[2]) // rising edge begin o_rx_valid 1b1; // Pulse Data Valid 1 clock cycle o_rx_data r_rx_data; end else o_rx_valid 1b0; end end assign o_spi_wr o_rx_validr_rx_data[47]; //SPI MISO// always (posedge i_sys_clk or posedge i_sys_rst) begin if (i_sys_rst) r_tx_data d0; else begin if (i_tx_valid) r_tx_data i_tx_data; else r_tx_data r_tx_data; end end // Control preload signal. Should be 1 when CS is high, but as soon as // first clock edge is seen it goes low. always (negedge w_spi_clk or posedge i_spi_cs) begin if (i_spi_cs) begin r_preload_miso 1b1; end else begin r_preload_miso 1b0; end end always (negedge w_spi_clk or posedge i_spi_cs) begin if (i_spi_cs) begin r_txbit_cnt DATA_WIDTH-2; // Send MSb first r_spi_miso_bit r_tx_data[DATA_WIDTH-1]; // Reset to MSb end else begin if(r_txbit_cntd0) r_txbit_cntDATA_WIDTH-1; else r_txbit_cnt r_txbit_cnt - 1; r_spi_miso_bit r_tx_data[r_txbit_cnt]; end end assign w_spi_miso_mux r_preload_miso ? r_tx_data[DATA_WIDTH-1] : r_spi_miso_bit; // Tri-state MISO when CS is high. Allows for multiple slaves to talk. assign o_spi_miso i_spi_cs ? 1bZ : w_spi_miso_mux; //W/R CMD // reg [3:0]spi_rd_vld; always (posedge i_sys_clk or posedge i_sys_rst) begin if (i_sys_rst)begin spi_rd_vldd0; o_spi_rd0; o_spi_rd_addrd0; end else begin spi_rd_vld{spi_rd_vld[2:0],r_spi_rd_vld}; if(spi_rd_vld[0]~spi_rd_vld[1])begin o_spi_rdr_spi_rd ; o_spi_rd_addrr_spi_rd_addr; end else begin o_spi_rd0; o_spi_rd_addrr_spi_rd_addr; end end end endmodule
返回列表