Verilog 实现Spi 主机
Spi Master(Verilog) --后附完整代码
1.硬件接口(Port)
SPI = Serial Peripheral Interface,是串行外围设备接口,是一种高速,全双工,同步的通信总线。
SCK : 串行时钟,一般主机提供(Serial Clock)
MOSI : 主发从收(Master Output, Slave Input)
MISO : 主收从发(Master Input, Slave Output)
CS : 片选,一般低有效(Slave Select )
2.操作时序(Protocol)
不同的芯片可能有不同的操作时序,常见的不同如:地址位不同,有的32位,有的16位,有的8位;操作控制位;数据位不同等。
具体操作时序需要参考相关芯片手册。
本文依据以下时序编写。

3.Verilog 写SPI_Master
以下依次为:端口声明、reg和wire声明、状态机声明。
//端口声明
`timescale 1ns / 1ps
module SPI_Master(
input wire sclk , // System Clock.
input wire srstn , // System Reset. Low Active
//====== Control / Status ======================
input wire [ 7 : 0] sclk_divider , // SPI Clock Control / Divid
//====== Control / Status ======================
input wire wr_start , // Write Start
input wire rd_start , // Read Start
output wire wr_finish , // Write Finish
output wire rd_finish , // Read Finish
input wire [ 7 : 0] start_addr , // Write / Read Start Address
input wire [ 7 : 0] state_init , // slaver state initial
//====== Rx Interface =========================
input wire [ 7 : 0] rx_rd_data , // Rx Read Data
//====== Tx Interface =====================
output wire [ 7 : 0] tx_wr_data , // Tx Write Data
//====== ReRAM SPI Interface ===================
output wire SPI_SCLK , // SPI Clock
output wire SPI_CSN , // SPI Chip Select
output wire SPI_MOSI , // SPI Master Output
input wire SPI_MISO // SPI Master Input
);
//reg和wire声明
//==============================================================
// Local Registe Declare
//==============================================================
//control
reg r_wr_start ;// Write Start
reg r_rd_start ;// Read Start
//status
reg r_wr_finish ;// Write Finish
reg r_rd_finish ;// Read Finish
reg [ 7 : 0] r_start_addr ;// Write / Read Start Address
reg [ 7 : 0] r_state_init ;
reg r_wr_mode ;// Write / Read Mode. 1'b0: Write /1'b1: Read
// Tx
reg [ 7 : 0] r_tx_wr_data ;// Tx Write Data
// SPI Interface
reg r_sclk ;
reg r_sclk_d0 ;// SPI Clock
reg r_csn ;// SPI Chip Select
reg [ 3 : 0] r_csn_cnt ;// SPI Chip Select Count
reg r_sclk_enable ;//SPI Clock enable
reg [ 7 : 0] r_sclk_divider ;// SPI Clock Divider Count
reg [ 7 : 0] r_wr_data ;// SPI Write Data
reg [ 7 : 0] r_spi_addr_cnt ;// SPI Write / Read Address Count
//reg [11 : 0] r_spi_data_cnt ;// SPI Write / Read Data Count
// State
reg [ 7 : 0] state ;// FSM Current State
reg [ 7 : 0] state_nxt ;// --- Next State
//==============================================================
// Local Wire Declare
//==============================================================
wire sclk_pedge ;// SPI Clock Positive Edge
wire sclk_nedge ;// SPI Clock Negative Edge
//状态机声明
//==============================================================
// Local Parameter Declare
//==============================================================
localparam ST_IDLE = 8'h01; // Idle State
localparam ST_CSN_ENABLE = 8'h02; // Chip Select Enable
localparam ST_WRITE_INITIAL = 8'h04; // Write State Initial
localparam ST_WRITE_ADDR = 8'h08; // Write Address
localparam ST_WRITE_DATA = 8'h10; // Write Data
localparam ST_READ_DATA = 8'h20; // Read Data
localparam ST_CSN_DISABLE = 8'h40; // Chip Select Disable
localparam ST_FINISH = 8'h80; // Finish State
输入控制指令/buffer/状态
//==============================================================
// Input control / status
//==============================================================
//read / write start
always @(posedge sclk)begin
if(!srstn)begin
r_wr_start <= 1'b0;
r_rd_start <= 1'b0;
end
else begin
r_wr_start <= wr_start;
r_rd_start <= rd_start;
end
end
// Write / Read Mode. 1'b0: Write / 1'b1: Read
always @(posedge sclk)begin
if(!srstn)begin
r_wr_mode <= 1'b0;
end
else if(wr_start) begin
r_wr_mode <= 1'b0;
end
else if (rd_start)begin
r_wr_mode <= 1'b1;
end
end
//input buffer
always @(posedge sclk)begin
if(!srstn)begin
r_start_addr <= 8'h0;
r_state_init <= 8'h0;
end
else if(wr_start | rd_start)begin
r_start_addr <= start_addr;
r_state_init <= state_init;
end
end
生成SPI 时钟、片选使能
//==============================================================
// Generate SPI Clock
//==============================================================
always @ (posedge sclk)begin
if(!srstn)
r_sclk_enable <= 1'b0;
else if(state==ST_IDLE)
r_sclk_enable <= 1'b0;
else if(state==ST_CSN_ENABLE)
r_sclk_enable <= 1'b1;
end
//SPI_CLK divider
always @ (posedge sclk)begin
if(!srstn)
r_sclk_divider <= 8'h0;
else if(r_sclk_enable) begin
if(r_sclk_divider == sclk_divider)
r_sclk_divider <= 8'h0;
else
r_sclk_divider <= r_sclk_divider + 1'b1;
end
else
r_sclk_divider <= 8'h0;
end
always @ (posedge sclk)begin
if(~ srstn)
r_sclk <= 1'b0;
else if(r_sclk_enable) begin
if(r_sclk_divider==sclk_divider)
r_sclk <= ~ r_sclk;
end
else
r_sclk <= 1'b0;
end
always @ (posedge sclk)begin
if(~ srstn)
r_sclk_d0 <= 1'b0;
else
r_sclk_d0 <= r_sclk;
end
assign sclk_pedge = ~ r_sclk_d0 & r_sclk ; // SPI Clock Positive Edge (read /sample data at this edge)
assign sclk_nedge = r_sclk_d0 & (~ r_sclk); // SPI Clock Negative Edge (write data at this edge)
//==============================================================
// SPI Chip Select
//==================================================