SPI(Serial Peripheral Interface,串行外围设备接口)通讯协议,是Motorola公司提出的一种同步串行接口技术,是一种高速、全双工、同步通信总线,在芯片中只占用四根管脚用来控制及数据传输。
应用:EEPROM、Flash、RTC、ADC、DSP等。
优缺点:全双工通信,通讯方式较为简单,相对数据传输速率较快;没有应答机制确认数据是否接收,在数据可靠性上有一定缺陷(与I2C相比)。

SCK (Serial Clock):时钟信号线,用于同步通讯数据;
MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚;
MISO (Master Input,Slave Output):主设备输入/从设备输出引脚;
𝐶𝑆(CS) ̅ (Chip Select):片选信号线,也称为CS_N。


spi通讯协议有四种模式:模式0和模式3,从设备在sck上升沿采样。
模式1和模式2在时钟下降沿采样。
模式0和模式1,在cs_n==1时,sck==0。
模式2和模式3,在cs_n==1时,sck==1。
比较常用的就是模式0和模式3。
芯片手册是必须要看的。



- module spi (
- input wire sys_clk ,
- input wire sys_rst_n ,
- input wire key_start ,
-
- output wire mosi ,
- output wire miso ,
- output reg cs_n ,
- output reg sck
- );
- // parameter
- parameter COMD_W = 8'h06 ,
- COMD_B = 8'hc7 ;
- parameter IDLE = 4'b0001 ,
- WREN = 4'b0010 ,
- WEL = 4'b0100 ,
- BE = 4'b1000 ;
- // wire signal degine
- wire IDLEtoWREN;
- wire WRENtoWEL ;
- wire WRENtoBE ;
- wire BEtoIDLE ;
- // reg signal define
- reg [3:0] state_c ;
- reg [3:0] state_n ;
- reg [3:0] cnt_20ns ;
- reg [3:0] cnt_bit ;
- reg flag_bit ;
- reg f_b_reg ; // flag_bit_reg的缩写
- reg [7:0] comd ;
- /****************************************************************************/
- // 三段式状态机
- // 现态与次态描述
- // state_c
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(~sys_rst_n)
- state_c <= IDLE ;
- else
- state_c <= state_n ;
- end
- // state_n
- always @(*) begin
- case (state_c)
- IDLE :if(IDLEtoWREN)
- state_n <= WREN ;
- else
- state_n <= IDLE ;
- WREN :if(WRENtoWEL)
- state_n <= WEL ;
- else
- state_n <= WREN ;
- WEL :if(WRENtoBE)
- state_n <= BE ;
- else
- state_n <= WEL ;
- BE :if(BEtoIDLE)
- state_n <= IDLE ;
- else
- state_n <= BE ;
- default: state_n <= IDLE ;
- endcase
- end
- // 状态转移描述
- assign IDLEtoWREN = ( state_c == IDLE) && ( key_start ) ;
- assign WRENtoWEL = ( state_c == WREN) && ( f_b_reg ) ;
- assign WRENtoBE = ( state_c == WEL ) && ( cnt_20ns == 6 ) ;
- assign BEtoIDLE = ( state_c == BE ) && ( f_b_reg ) ;
- // 相关信号描述
- // reg [3:0] cnt_20ns ;
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(~sys_rst_n)
- cnt_20ns <= 4'd0 ;
- else
- case (state_c)
- IDLE : cnt_20ns <= 4'd0 ;
- WREN : if(cnt_20ns || f_b_reg)
- cnt_20ns <= 4'd0 ;
- else
- cnt_20ns <= cnt_20ns + 1'b1 ;
- WEL : if(cnt_20ns == 6) // 60x20ns==120ns
- cnt_20ns <= 4'd0 ;
- else
- cnt_20ns <= cnt_20ns + 1'b1 ;
- BE : if(cnt_20ns || f_b_reg)
- cnt_20ns <= 4'd0 ;
- else
- cnt_20ns <= cnt_20ns + 1'b1 ;
- default: cnt_20ns <= 4'd0 ;
- endcase
- end
- // reg [3:0] cnt_bit ;
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(~sys_rst_n)
- cnt_bit <= 4'd0 ;
- else
- case (state_c)
- IDLE : cnt_bit <= 4'd0 ;
- WREN : if(!cnt_20ns && sck && cnt_bit == 7)
- cnt_bit <= 4'd0 ;
- else if(!cnt_20ns && sck)
- cnt_bit <= cnt_bit + 1'b1 ;
- WEL : cnt_bit <= 4'd0 ;
- BE : if(!cnt_20ns && sck && cnt_bit == 7)
- cnt_bit <= 4'd0 ;
- else if(!cnt_20ns && sck)
- cnt_bit <= cnt_bit + 1'b1 ;
- default: cnt_bit <= 4'd0 ;
- endcase
- end
- // reg flag_bit ;
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(~sys_rst_n)
- flag_bit <= 1'b0 ;
- else
- case (state_c)
- IDLE : flag_bit <= 1'b0 ;
- WREN : if(cnt_bit == 7 && sck && !cnt_20ns)
- flag_bit <= 1'b1 ;
- else
- flag_bit <= flag_bit ;
- WEL : flag_bit <= 1'b0 ;
- BE : if(cnt_bit == 7 && sck && !cnt_20ns)
- flag_bit <= 1'b1 ;
- else
- flag_bit <= flag_bit ;
- default: flag_bit <= 1'b0 ;
- endcase
- end
- // reg f_b_reg ;
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(~sys_rst_n) begin
- f_b_reg <= 1'b0 ;
- end else begin
- f_b_reg <= flag_bit ;
- end
- end
- // reg [7:0] comd ;
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(~sys_rst_n)
- comd <= 8'd0 ;
- else
- case (state_c)
- IDLE : comd <= 8'd0 ;
- WREN : if(cnt_20ns && sck)
- comd <= (COMD_W << cnt_bit) ;
- else if(!cnt_bit)
- comd <= COMD_W ;
- else
- comd <= comd ;
- WEL : comd <= 8'd0 ;
- BE : if(cnt_20ns && sck)
- comd <= (COMD_B << cnt_bit) ;
- else if(!cnt_bit)
- comd <= COMD_B ;
- else
- comd <= comd ;
- default : comd <= 8'd0 ;
- endcase
- end
- // output signal
- // wire mosi ,
- assign mosi = comd[7] ;
- // wire miso ,
- assign miso = 1'bz ;
- // reg cs_n ,
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(~sys_rst_n) begin
- cs_n <= 1'b1 ;
- end else begin
- case (state_c)
- IDLE : if(key_start)
- cs_n <= 1'b0 ;
- else
- cs_n <= 1'b1 ;
- WREN : if(f_b_reg)
- cs_n <= 1'b1 ;
- else
- cs_n <= cs_n ;
- WEL : if(cnt_20ns == 6)
- cs_n <= 1'b0 ;
- else
- cs_n <= cs_n ;
- BE : if(f_b_reg)
- cs_n <= 1'b1 ;
- else
- cs_n <= cs_n ;
- default: cs_n <= 1'b1 ;
- endcase
- end
- end
- // reg sck
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(~sys_rst_n)
- sck <= 1'b0 ;
- else
- case (state_c)
- IDLE : sck <= 1'b0 ;
- WREN : if(cnt_20ns)
- sck <= ~sck ;
- else
- sck <= sck ;
- WEL : sck <= 1'b0 ;
- BE : if(cnt_20ns)
- sck <= ~sck ;
- else
- sck <= sck ;
- default: sck <= 1'b0 ;
- endcase
- end
-
- endmodule
- module key_filter
- #(
- parameter MAX_CNT_20MS = 20'd100_0000
- )(
- input wire sys_clk ,
- input wire sys_rst_n ,
- input wire key_in ,
- output wire key_out
- );
- reg key_r_0 ;
- reg key_r_1 ;
- wire nege ;
- wire pose ;
- reg [19:00] cnt_20ms ;
- wire add_cnt_20ms ;
- wire end_cnt_20ms ;
- reg add_cnt_flag ;
- // key_r_0 key_r_1
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(~sys_rst_n) begin
- key_r_0 <= 1'b1 ;
- end else begin
- key_r_0 <= key_in ;
- end
- end
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(~sys_rst_n) begin
- key_r_1 <= 1'b1 ;
- end else begin
- key_r_1 <= key_r_0 ;
- end
- end
- // nege pose
- assign nege = ~key_r_0 && key_r_1 ;
- assign pose = key_r_0 && ~key_r_1 ;
- // add_cnt_flag
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(~sys_rst_n) begin
- add_cnt_flag <= 1'b0 ;
- end else begin
- if(nege) begin
- add_cnt_flag <= 1'b1 ;
- end else begin
- if( pose || end_cnt_20ms ) begin
- add_cnt_flag <= 1'b0 ;
- end else begin
- add_cnt_flag <= add_cnt_flag ;
- end
- end
- end
- end
-
- // cnt_20ms add_cnt_20ms end_cnt_20ms
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(~sys_rst_n) begin
- cnt_20ms <= 20'd0 ;
- end else begin
- if(add_cnt_20ms) begin
- if(end_cnt_20ms) begin
- cnt_20ms <= 20'd0 ;
- end else begin
- cnt_20ms <= cnt_20ms + 20'd1 ;
- end
- end else begin
- cnt_20ms <= 20'd0 ;
- end
- end
- end
- assign add_cnt_20ms = add_cnt_flag ;
- assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == ( MAX_CNT_20MS - 1'b1 ) ;
- // key_out
- // always @(posedge sys_clk or negedge sys_rst_n) begin
- // // always @(*) begin // 这样的话 会综合成 数据选择器
- // if(~sys_rst_n) begin
- // key_out <= 1'b0 ;
- // end else begin
- // if(end_cnt_20ms) begin
- // key_out <= 1'b1 ;
- // end else begin
- // key_out <= 1'b0 ;
- // end
- // end
- // end
- assign key_out = end_cnt_20ms ;
- endmodule
- module top(
- input wire sys_clk ,
- input wire sys_rst_n ,
- input wire key_in ,
-
- output wire cs_n ,
- output wire sck ,
- output wire mosi
- );
- // 例化间连线
- wire key_flag ;
-
- key_filter key_filter_inst(
- .sys_clk ( sys_clk ) ,
- .sys_rst_n ( sys_rst_n ) ,
- .key_in ( key_in ) ,
-
- .key_out ( key_flag )
- );
-
- spi spi_inst(
- .sys_clk ( sys_clk ) ,
- .sys_rst_n ( sys_rst_n ) ,
- .key_start ( key_flag ) ,
-
- .mosi ( mosi ) ,
- .miso ( ) ,
- .cs_n ( cs_n ) ,
- .sck ( sck )
- );endmodule
- `timescale 1ns/1ns
- module test_top();
- reg sys_clk ;
- reg sys_rst_n ;
- reg key_in ;
-
- wire cs_n ;
- wire sck ;
- wire mosi ;
- wire miso ;
-
- top top_inst(
- .sys_clk ( sys_clk ) ,
- .sys_rst_n ( sys_rst_n ) ,
- .key_in ( key_in ) ,
-
- .cs_n ( cs_n ) ,
- .sck ( sck ) ,
- .mosi ( mosi ) ,
- .miso ( miso )
- );
-
- parameter CYCLE = 20 ;
- defparam top_inst.key_filter_inst.MAX_CNT_20MS = 20'd100 ;
- initial begin
- sys_clk = 1'b1 ;
- sys_rst_n <= 1'b0 ;
- key_in <= 1'b1 ;
- #(CYCLE) ;
- sys_rst_n <= 1'b1 ;
- #(CYCLE * 30) ;
- key_in <= 1'b0 ;
- #(CYCLE * 130) ;
- key_in <= 1'b1 ;
- #(CYCLE * 100) ;
- $stop;
- end
- always #(CYCLE/2) sys_clk = ~sys_clk ;
- endmodule

先往板子上固化一个流水灯程序,也就是把生成的.jic文件,写进flash。
重启开发板,流水灯正常工作,说明程序写进了flash。
然后把全擦除程序(.sof),写进开发板。
重启开发板,流水灯效果消失。
说明上板成功。