FPGA|Verilog-自己写的SPI驱动_spi驱动 verilog
1. 状态变量设置
localparam IDLE = 6\'b00_0001;
localparam GEN_DCLK = 6\'b00_0010;
localparam ACK = 6\'b00_0100;
这里采用状态独热编码(One-Hot Encoding)
在 FPGA 开发中,独热编码能简化组合逻辑、提升时序性能
2. 两段式状态机,明晰跳转条件
3. 采用end_cnt_clk和end_cnt_num结合的方式方便时序控制,准确进行clk_div的分频
4. 仿真效果展示
仿真上板通过
5. 全部代码
module spi_driver1( input clk , input rst , input MISO , output reg MOSI ,// 1 input CPOL , input CPHA , input [ 7:0] data_in , output reg [ 7:0] data_out ,// input [ 15:0] clk_div , output nCS ,// 1 input nCTRL, output reg DCLK ,// 1 input wr_req , output wr_ack// 1);localparam IDLE = 6\'b00_0001;localparam GEN_DCLK = 6\'b00_0010;localparam ACK = 6\'b00_0100;reg [5:0]cstate;reg [5:0]nstate;reg [25:0]cnt_clk;reg [7:0]cnt_num;reg [7:0]num;wire [15:0]MAX_CNT = clk_div;wire add_cnt_clk = cstate != IDLE;wire end_cnt_clk = add_cnt_clk && cnt_clk == MAX_CNT - 1;wire add_cnt_num = end_cnt_clk;wire end_cnt_num = add_cnt_num && cnt_num == num - 1;assign nCS = nCTRL;assign wr_ack = cstate == ACK && end_cnt_clk;always @(posedge clk or posedge rst) begin if(rst)begin cnt_clk <= 26\'d0; end else if(add_cnt_clk)begin if(end_cnt_clk) cnt_clk <= 26\'d0; else cnt_clk <= cnt_clk + 26\'d1; end endalways @(posedge clk or posedge rst) begin if(rst)begin cnt_num <= 8\'d0; end else if(add_cnt_num)begin if(end_cnt_num) cnt_num <= 8\'d0; else cnt_num <= cnt_num + 8\'d1; end endreg [8*20-1:0]state_name;always @(*) begin case (cstate) IDLE :begin num = 1; state_name = \"IDLE \"; end GEN_DCLK:begin num = 8; state_name = \"GEN_DCLK\"; end ACK :begin num = 1; state_name = \"ACK \"; end default :begin num = 1; state_name = \"IDLE \"; end endcaseendwire IDLE_GEN_DCLK = (cstate == IDLE) && wr_req ;wire GEN_DCLK_ACK = (cstate == GEN_DCLK) && end_cnt_num ; wire ACK_IDLE = (cstate == ACK) && end_cnt_num ; always @(posedge clk or posedge rst) begin if(rst)begin DCLK <= (CPOL == 1\'b0)?1\'b0:1\'b1; end else if(cstate == GEN_DCLK && (cnt_clk == MAX_CNT/2 - 1 || cnt_clk == MAX_CNT - 1))begin DCLK <= ~DCLK; endendreg flag_mosi;always @(posedge clk or posedge rst) begin if(rst)begin MOSI <= 1\'b0; flag_mosi <= 1\'b0; end else if(cstate == GEN_DCLK)begin if(CPOL == 1\'b0 && cnt_clk == 0)begin MOSI <= data_in[7 - cnt_num]; flag_mosi <= 1\'b1; end else if(CPOL == 1\'b1 && cnt_clk == MAX_CNT/2 - 1)begin MOSI <= data_in[7 - cnt_num]; flag_mosi <= 1\'b1; end else begin flag_mosi <= 1\'b0; end end else if(cstate == ACK)begin MOSI <= 1\'b0; flag_mosi <= 1\'b0; end endreg flag_data_out;always @(posedge clk or posedge rst) begin if(rst)begin data_out <= 8\'d0; flag_data_out <= 1\'b0; end else if(cstate == GEN_DCLK)begin if(CPOL == 1\'b0 && cnt_clk == MAX_CNT/2 - 1)begin data_out <= {data_out[6:0],MISO}; flag_data_out <= 1\'b1; end else if(CPOL == 1\'b1 && cnt_clk == MAX_CNT - 1)begin data_out <= {data_out[6:0],MISO}; flag_data_out <= 1\'b1; end else begin flag_data_out <= 1\'b0; end end else begin flag_data_out <= 1\'b0; endendalways @(posedge clk or posedge rst) begin if(rst)begin cstate <= IDLE; end else begin cstate <= nstate; endendalways @(*) begin case (cstate) IDLE : if(IDLE_GEN_DCLK) nstate = GEN_DCLK; else nstate = cstate; GEN_DCLK: if(GEN_DCLK_ACK) nstate = ACK; else nstate = cstate; ACK : if(ACK_IDLE) nstate = IDLE; else nstate = cstate; default : nstate = IDLE; endcaseendendmodule