FPGA|Verilog-自己写的SPI驱动

  1. 状态变量设置

localparam IDLE = 6'b00_0001;

localparam GEN_DCLK = 6'b00_0010;

localparam ACK = 6'b00_0100;

这里采用状态独热编码(One-Hot Encoding)

在 FPGA 开发中,独热编码能简化组合逻辑、提升时序性能

  1. 两段式状态机,明晰跳转条件
  1. 采用end_cnt_clk和end_cnt_num结合的方式方便时序控制,准确进行clk_div的分频
  1. 仿真效果展示

仿真上板通过

  1. 全部代码
cpp 复制代码
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 
end

always @(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 
end

reg [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
    endcase
end

wire 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;
    end
end

reg 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 
end

reg 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;
    end
end

always @(posedge clk or posedge rst) begin
    if(rst)begin
        cstate <= IDLE;
    end else begin
        cstate <= nstate;
    end
end

always @(*) 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;
    endcase
end



endmodule
相关推荐
自小吃多7 小时前
【Verilog/FPGA】存储器(数组)
嵌入式硬件·fpga开发
szxinmai主板定制专家7 小时前
RK3588+FPGA异构架构|高速数据采集+边缘AI落地应用全解析
人工智能·嵌入式硬件·fpga开发·架构·zynq
明德扬12 小时前
FPGA MIPI 多路视频聚合方案
fpga开发·mipi视频拼接·mipi多路拼接·mipi协议·mipi视频拼接方案·fpga mipi方案·mipi csi
XMAIPC_Robot12 小时前
为什么大型储能需要边缘 AI 协调控制器?RK3588+FPGA 高速采集方案
人工智能·嵌入式硬件·fpga开发·arm+fpga·rk3588+fpga·协调控制器
szxinmai主板定制专家12 小时前
RK3576+CODESYS+RK182X+FPGA异构架构:半导体精密设备一体化控制器方案
人工智能·fpga开发·架构·rk3576+codesys
szxinmai主板定制专家13 小时前
半导体设备控制器:RK3576+CODESYS+RK182X异构架构,实现晶圆视觉检测与运动联动闭环
人工智能·fpga开发·架构·视觉检测·边缘计算·codesys·rk3576
szxinmai主板定制专家14 小时前
FPGA高速IO+RK182X AI算力|RK3576+CODESYS工业自动化运动控制平台设计
运维·人工智能·嵌入式硬件·fpga开发·自动化·zynq
FPGA小徐14 小时前
原理图分析:Type-C 转串口(CH340C)最小系统板 CSDN 博客
fpga开发
智感子14 小时前
FPGA采集:高采样率数据流与存储
fpga开发
狂奔蜗牛(bradley)1 天前
拒绝SOEM黑盒!死磕EtherCAT主站:ARM+FPGA架构、DC同步与状态机修复等,50+篇实战开发文档全公开
arm开发·fpga开发·架构