Verilog分别实现SPI主机和从机


module  spi_master
(
    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效

    input   wire            data_in0    ,
    input   wire            data_in1    ,
    input   wire            data_in2    ,
    input   wire            data_in3    ,
    input   wire            data_in4    ,
    input   wire            data_in5    ,
    input   wire            data_in6    ,
    input   wire            data_in7    ,

    input   wire            pclk        ,
    input   wire            vsync       ,
    input   wire            hsync       ,
    
    output  wire    [7:0]   data_out,
    output  reg             rd_req,
    output  reg             wr_req,
    output  wire            rd_empty,
    output  wire            wr_full,

    output   reg    [7:0]   data_in     ,
    output  reg             cs_n        ,   //片选信号
    output  reg             sck         ,   //串行时钟
    output  reg             mosi        ,   //主输出从输入数据
    output  reg             handshake_start   ,   //开始传输标志信号
    output  reg             handshake_end   ,   //结束传输标志信号
    output  reg     [3:0]   cnt_sck     ,
    output  reg     [2:0]   cnt_bit     ,    //比特计数器
    output  reg     [3:0]   state

);

//parameter define
parameter   IDLE    =   4'b0001 ,   //初始状态
            WR_EN   =   4'b0010;   //写状态

//以pclk为时钟采样输入data0~7信号
always @(posedge pclk or negedge sys_rst_n) begin
    if(sys_rst_n == 1'b0)
        data_in <= 0;
    else begin
        data_in[0] <= data_in0;
        data_in[1] <= data_in1;
        data_in[2] <= data_in2;
        data_in[3] <= data_in3;
        data_in[4] <= data_in4;
        data_in[5] <= data_in5;
        data_in[6] <= data_in6;
        data_in[7] <= data_in7;
    end
end


fifo	fifo_inst (
	.data    ( data_in   ),
	.rdclk   ( sck       ),
	.rdreq   ( rd_req    ),
	.wrclk   ( pclk      ),
	.wrreq   ( wr_req    ),
	.q       ( data_out  ),
	.rdempty ( rd_empty  ),
	.wrfull  ( wr_full   )
	);


//cnt_sck:串行时钟计数器,用以生成串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_sck <=  2'd0;
    else    if(cnt_sck == 4'd9)
        cnt_sck <= 1'b0;
    else    if(state == WR_EN)
        cnt_sck <=  cnt_sck + 1'b1;
    else
        cnt_sck <= cnt_sck;

//cs_n:片选信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cs_n    <=  1'b1;
    else if(state == WR_EN)
        cs_n    <=  1'b0;
    else
        cs_n    <=  1'b1;


//sck:输出串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd0)
        sck <=  1'b0;
    else    if(cnt_sck == 4'd6)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd2)
        sck <=  1'b1;

//cnt_bit:高低位对调,控制mosi输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0) 
        cnt_bit <=  3'd0;
    else    if(cnt_sck == 2'd2)
        cnt_bit <=  cnt_bit + 1'b1;


//state:两段式状态机第一段,状态跳转
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0) begin
        state   <=  IDLE;
        wr_req <= 1'b0;
    end
    else
    case(state)
        IDLE:   if(sys_rst_n == 1'b1) begin
                state   <=  WR_EN;
                wr_req <= 1'b1; //按键按下状态跳转的同时使能FIFO写入信号
        end
        WR_EN:  state   <=  WR_EN;
        default:    state   <=  IDLE;
    endcase


//初始化FIFO读写使能信号,rd_req信号是状态变为IDLE的后一拍
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_req <= 1'b0;
    else if(state == WR_EN)
        rd_req <= 1'b1;
    else
        rd_req <= 1'b0;

//mosi:两段式状态机第二段,逻辑输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        mosi <= 1'b0;
    else if((state == WR_EN) && (cnt_sck == 4'b0110))
        mosi <= data_out[7-cnt_bit];    //写使能指令
    else if((state == WR_EN) && (rd_empty == 1'b1)) 
        mosi <= 8'b1111_1111;   //发送一个全1数据包表示摄像头传输结束
    else    if(state == IDLE)
        mosi    <=  1'b0;

//handshake_start信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        handshake_start <= 1'b0;
    else if((state == WR_EN) && (cnt_bit == 1'b1) && (cnt_sck == 1'b0))
        handshake_start <= 1'b1;
    else
        handshake_start <= 1'b0;

//handshake_end信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        handshake_end <= 1'b0;
    else if((state == WR_EN) && (cnt_bit == 1'b0) && (cnt_sck == 1'b0))
        handshake_end <= 1'b1;
    else
        handshake_end <= 1'b0;


endmodule
`timescale  1ns/1ns

module  spi_slave
(
    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire            key1        ,   //按键输入信号
    input   wire            key2        ,
    input   wire            data_in0    ,
    input   wire            data_in1    ,
    input   wire            data_in2    ,
    input   wire            data_in3    ,
    input   wire            data_in4    ,
    input   wire            data_in5    ,
    input   wire            data_in6    ,
    input   wire            data_in7    ,

    input   wire            pclk        ,
    input   wire            vsync       ,
    input   wire            hsync       ,
    
    output  wire    [7:0]   data_out,
    output  reg             rd_req,
    output  reg             wr_req,
    output  wire            rd_empty,
    output  wire            wr_full,

    output  reg    [7:0]    data_in     ,
    input   wire            cs_n        ,   //片选信号
    input   wire            sck         ,   //串行时钟
    output  reg             mosi        ,   //主输出从输入数据
    output  reg             handshake_start   ,   //开始传输标志信号
    output  reg             handshake_end   ,   //结束传输标志信号
    output  reg     [2:0]   cnt_bit     ,    //比特计数器
    output  reg     [3:0]   state

);

//parameter define
parameter   IDLE    =   4'b0001 ,   //初始状态
            WR_EN   =   4'b0010;   //写状态

parameter   WR_EN_INST  =   8'b0000_0110;   //写使能指令

reg     daq_clk;

//通过pclk在sys_clk下打拍产生一个采样时钟daq_clk来保证采样时数据已经稳定
//适用条件是f_pclk < f_sys_clk
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(sys_rst_n == 1'b0)
        daq_clk <= 1'b0;
    else if((pclk == 1'b1) && (state == WR_EN))
        daq_clk <= 1'b1;
    else
        daq_clk <= 1'b0;
end

//以pclk为时钟采样输入data0~7信号
always @(posedge daq_clk or negedge sys_rst_n) begin
    if(sys_rst_n == 1'b0)
        data_in <= 0;
    else begin
        data_in[0] <= data_in0;
        data_in[1] <= data_in1;
        data_in[2] <= data_in2;
        data_in[3] <= data_in3;
        data_in[4] <= data_in4;
        data_in[5] <= data_in5;
        data_in[6] <= data_in6;
        data_in[7] <= data_in7;
    end
end

fifo	fifo_inst (
	.data    ( data_in   ),
	.rdclk   ( sck       ),
	.rdreq   ( rd_req    ),
	.wrclk   ( daq_clk   ),
	.wrreq   ( wr_req    ),
	.q       ( data_out  ),
	.rdempty ( rd_empty  ),
	.wrfull  ( wr_full   )
	);


//cnt_bit:高低位对调,控制mosi输出
always@(posedge sck or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0) 
        cnt_bit <=  3'd0;
    else
        cnt_bit <=  cnt_bit + 1'b1;


//state:两段式状态机第一段,状态跳转
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0) begin
        state   <=  IDLE;
        wr_req <= 1'b0;
    end
    else
    case(state)
        IDLE:   if(sys_rst_n == 1'b1) begin
                state   <=  WR_EN;
                wr_req <= 1'b1; //按键按下状态跳转的同时使能FIFO写入信号
        end
        WR_EN:  state   <=  WR_EN;
        default:    state   <=  IDLE;
    endcase


//初始化FIFO读写使能信号,rd_req信号是状态变为IDLE的后一拍
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_req <= 1'b0;
    else if(state == WR_EN)
        rd_req <= 1'b1;
    else
        rd_req <= 1'b0;

//mosi:两段式状态机第二段,逻辑输出
always@(posedge sck or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        mosi <= 1'b0;
    else if(state == WR_EN)
        mosi <= data_out[7-cnt_bit];    //写使能指令
    else if((state == WR_EN) && (rd_empty == 1'b1)) 
        mosi <= 8'b1111_1111;   //发送一个全1数据包表示摄像头传输结束
    else    if(state == IDLE)
        mosi    <=  1'b0;

//handshake_start信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        handshake_start <= 1'b0;
    else if((state == WR_EN) && (cnt_bit == 3'b010))
        handshake_start <= 1'b1;
    else
        handshake_start <= 1'b0;

//handshake_end信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        handshake_end <= 1'b0;
    else if((state == WR_EN) && (cnt_bit == 3'b001))
        handshake_end <= 1'b1;
    else
        handshake_end <= 1'b0;


endmodule
`timescale  1ns/1ns

module  tb_spi_slave();

//wire  define
wire    cs_n;
reg    sck ;
wire    mosi ;

//reg   define
reg     clk     ;
reg     rst_n   ;
reg     key1     ;
reg     key2     ;

wire     [3:0]   state   ;   //状态机状态

wire     [2:0]   cnt_bit ;   //比特计数器

reg            data_in0    ;
reg            data_in1    ;
reg            data_in2    ;
reg            data_in3    ;
reg            data_in4    ;
reg            data_in5    ;
reg            data_in6    ;
reg            data_in7    ;

wire    [7:0]   data_out;
wire             rd_req;
wire             wr_req;
wire            rd_empty;
wire            wr_full;

reg            pclk        ;
reg            vsync       ;
reg            hsync       ;

wire             handshake_start   ;
wire             handshake_end   ;

wire    [7:0]   data_in;

//时钟、复位信号、模拟按键信号
initial
    begin
        clk =   0;
        sck = 0;
        rst_n   <=  0;
        key1 <=  0;
        key2 <=  0;
        data_in0 <= 1;
        data_in1 <= 0;
        data_in2 <= 0;
        data_in3 <= 1;
        data_in4 <= 0;
        data_in5 <= 0;
        data_in6 <= 1;
        data_in7 <= 0;
        pclk    <= 0;
        vsync   <= 0;
        hsync   <= 0;

        #100
        rst_n   <=  1;
        #1000
        key1 <=  1;
        #20
        key1 <=  0;

        #1000
        data_in0 <= 1;
        data_in1 <= 0;
        data_in2 <= 1;
        data_in3 <= 0;
        data_in4 <= 1;
        data_in5 <= 0;
        data_in6 <= 1;
        data_in7 <= 0;

        #10
        #1000
        data_in0 <= 1;
        data_in1 <= 0;
        data_in2 <= 0;
        data_in3 <= 0;
        data_in4 <= 0;
        data_in5 <= 0;
        data_in6 <= 1;
        data_in7 <= 0;

        #10000
        key2 <= 1;
        #20
        key2 <= 0;

    end

always  #10 clk <=  ~clk;
always  #20 pclk <=  ~pclk;
always  #50 sck <=  ~sck;



//-------------spi_flash_erase-------------
spi_slave    spi_slave_inst
(
    .sys_clk    (clk        ),  //系统时钟,频率50MHz
    .sys_rst_n  (rst_n      ),  //复位信号,低电平有效
    .key1    (key1       ),  //按键输入信号
    .key2    (key2       ),
    .data_in0   (data_in0   ),
    .data_in1   (data_in1   ),
    .data_in2   (data_in2   ),
    .data_in3   (data_in3   ),
    .data_in4   (data_in4   ),
    .data_in5   (data_in5   ),
    .data_in6   (data_in6   ),
    .data_in7   (data_in7   ),

    .pclk       (pclk),
    .vsync      (vsync),
    .hsync      (hsync),

    .data_out   (data_out),
    .rd_req     (rd_req),
    .wr_req     (wr_req),
    .rd_empty   (rd_empty),
    .wr_full    (wr_full),

    .data_in       (data_in       ),
    .sck        (sck        ),  //串行时钟
    .cs_n       (cs_n       ),  //片选信号
    .mosi       (mosi       ),   //主输出从输入数据
    .handshake_start  (handshake_start  ),
    .handshake_end  (handshake_end  ),
    .cnt_bit    (cnt_bit    ),
    .state      (state      )
);


endmodule

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
SPI主机是一种使用Verilog语言编写的硬件模块,用于控制SPI总线上的从设备。SPI(Serial Peripheral Interface)是一种高速的、全双工、同步通信总线,它通过主从方式工作。SPI主机通常需要至少4根线来进行通信,包括MISO(Master Input Slave Output)、MOSI(Master Output Slave Input)、SCLK(Serial Clock)和CS(Chip Select)。\[2\] 在Verilog中,可以使用assign语句来定义SPI主机的输出信号。例如,可以使用assign语句将主设备的数据输出连接到MOSI线上,将时钟信号连接到SCLK线上,将使能信号连接到CS线上。同时,可以使用assign语句将从设备的数据输入连接到MISO线上。\[3\] 需要注意的是,SPI主机的引脚连接应根据具体的硬件平台进行配置。在ESP8266(Node MCU)上,SPI主机的引脚应接HSCLK(GPIO14,D5)、HMISO(GPIO12,D6)、HMOSI(GPIO13,D7)和HCS(GPIO15,D8)。\[1\] 因此,如果你想在Verilog实现SPI主机,你可以使用assign语句来定义主设备的输出信号和从设备的输入信号,并根据具体的硬件平台配置引脚连接。 #### 引用[.reference_title] - *1* *2* [Verilog实现SPI通信](https://blog.csdn.net/weixin_41784968/article/details/128056864)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [用Verilog代码实现一个简易的SPI从机接口](https://blog.csdn.net/yalsim/article/details/107446786)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anndy.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值