基于verilog驱动M25P16(FALSH)

编程入门 行业动态 更新时间:2024-10-24 08:20:23

基于<a href=https://www.elefans.com/category/jswz/34/1767890.html style=verilog驱动M25P16(FALSH)"/>

基于verilog驱动M25P16(FALSH)

一、设计规范

1、设计框图

为了实现对M25P16(FLASH)的相关配置,设计了上图的相关的设计思路框图。

key_seshake:实现按键的消抖模块,在本次的设计中,使用了3个按键,key_in[0]控制数据的写入;key_in[1]控制数据的读取;key_in[2]控制扇区擦除。

uart_rx:串口数据接收模块,接收上位机发送来的数据,然后将 接收到的数据在发送的fifo中进行缓存。

fifo_rx:将uart_rx接收到的数据进行缓存,为FLASH的数据读取做准备。

spi_ctrl:主要是实现对M25P16的驱动,使用状态机进行驱动,主要实现了状态的转移。

spi:spi_ctrl模块的子模块,主要实现使用SPI协议对相关状态下数据的写入与读取。

fifo_tx:将从M25P16中读取到的数据缓存到该fifo中,为串口进行数据的发送做准备。

uart_tx:串口数据发送模块,主要是将fifo_tx模块中的数据发送给上位机。

2、spi_ctrl状态机

二、设计输入

1、顶层模块信号列表

信号

I/O

BIT

功能

clk

input

1

系统时钟

rst_n

input

1

复位

miso

input

1

主机输入,从机输出

uart_rx

input

1

Rx端口

key_in

input

3

按键输入

cs_n

output

1

片选

mosi

output

1

主机输出,从机输入

sclk

output

1

spi时钟

uart_tx

output

1

Tx端口

2、key_shake模块信号列表

信号

I/O

BIT

功能

clk

input

1

时钟信号

rst_n

input

1

复位信号

key_in

input

3

按键输入信号

key_out

output

3

按键输出信号

3、uart_rx模块信号列表

信号

I/O

BIT

功能

clk

input

1

系统时钟信号

rst_n

input

1

系统复位信号

uart_rx

input

1

数据接收

full_rx

input

1

fifo_rx的满信号

wrreq_rx

output

1

向fifo_rx发送写请求

rx_data

output

8

写入fifo_rx中的数据

4、fifo_rx模块信号列表(IP核)

信号

I/O

BIT

功能

aclr

input

1

fifo复位

clock

input

1

fifo时钟

data

input

8

fifo缓存uart_rx接收到的数据

rdreq

input

1

spi_ctrl读请求

wrreq

input

1

uart_rx写请求

empty

output

1

spi_ctrl读数据空标志

full

output

1

uart_rx写数据满标志

q    

output

8

fifo输出给spi_ctrl,将数据存储入flash

usedw

output

8

5、spi_ctrl模块信号列表

信号

I/O

BIT

功能

clk

input

1

系统

rst_n

input

1

复位

key_in

input

3

控制读、写 00:停止工作 01:写 10:读

data_rx

input

8

需要写入的数据(串口接收的数据)

miso

input

1

主机接收,从机输出

full_tx

input

1

fifo_tx的满信号

empty_rx

input

1

fifo_rx的空信号

mosi

output

1

主机输出,从机接收

cs_n

output

1

片选

sclk

output

1

SPI 时钟

wrreq_tx

output

1

向fifo_tx发送写请求

rdreq_rx

output

1

向fifo_rx发送读请求

data_tx

output

8

读取到的数据(串口发送的数据)

6、spi模块信号列表

信号

I/O

BIT

功能

clk

input

1

系统

rst_n

input

1

复位

miso

input

1

主机接收,从机输出

data_wr

input

8

写入的数据或指令

req

input

1

读写请求

rden

input

1

读数据使能信号

cs_n

output

1

片选

mosi

output

1

主机输出,从机接收

sclk

output

1

SPI 时钟

done

output

1

读写完1字节

data_rd

output

8

读取到的数据

7、fifo_tx模块信号列表(IP核)

信号

I/O

BIT

功能

aclr

input

1

fifo复位

clock

input

1

fifo时钟

data

input

8

从flash中将数据读取出来缓存进fifo

rdreq

input

1

uart_tx读请求

wrreq

input

1

spi_ctrl写请求

empty

output

1

uart_tx读数据空标志

full

output

1

spi_ctrl写数据满标志

q

output

8

将fifo中的数据发送给uart_tx进行输出

usedw

output

8

8、uart_tx模块信号列表

信号

I/O

BIT

功能

clk

input

1

时钟信号

rst_n

input

1

复位信号

tx_data

input

8

需要发送的数据

empty_tx

input

1

fifo_tx的空信号

rdreq_tx

output

1

向fifo_tx发送读请求

uart_tx

output

1

数据输出

  1. 三、代码片段

  2. 1、M23P16顶层模块

  3. module M25P16 #(parameter KEY_W = 3)(   //按键位宽input                  clk         ,//系统时钟input                  rst_n       ,//复位input                  miso        ,//主机输入,从机输出input                  uart_rx     ,//Rx端口input  [KEY_W - 1:0]   key_in      ,//按键output                 cs_n        ,//片选output                 mosi        ,//主机输出,从机输入output                 sclk        ,//spi时钟output                 uart_tx      //Tx端口
    );wire    [KEY_W - 1:0]   key     ;
    wire    [7:0]           rx_data ;
    wire    [7:0]           data_rx ;
    wire                    wrreq_rx;
    wire                    full_rx ;
    wire                    empty_rx;
    wire    [7:0]           tx_data ;
    wire    [7:0]           data_tx ;
    wire                    full_tx ;
    wire                    empty_tx;key_deshake #(.KEY_W(KEY_W))key_deshake_inst(/* input                             */.clk     (clk     ),//时钟信号/* input                             */.rst_n   (rst_n   ),//复位信号/* input           [KEY_W - 1:0]     */.key_in  (key_in  ),//按键输入信号/* output  reg     [KEY_W - 1:0]     */.key_out (key     ) //按键输出信号
    );uart_rx uart_rx_inst(/* input                */.clk     (clk     ),//系统时钟信号/* input                */.rst_n   (rst_n   ),//系统复位信号/* input                */.uart_rx (uart_rx ),//数据接收/* input                */.full_rx (full_rx ),//fifo_rx的满信号/* output reg           */.wrreq_rx(wrreq_rx),//向fifo_rx发送写请求/* output     [7:0]     */.rx_data (rx_data ) //数据串行接并行出给fifo_rx
    );fifo_rx fifo_rx_inst (.aclr   (!rst_n     ),//fifo复位.clock  (clk        ),//fifo时钟.data   (rx_data    ),//fifo缓存uart_rx接收到的数据.rdreq  (rdreq_rx   ),//spi_ctrl读请求.wrreq  (wrreq_rx   ),//uart_rx写请求.empty  (empty_rx   ),//spi_ctrl读数据空标志.full   (full_rx    ),//uart_rx写数据满标志.q      (data_rx    ),//fifo输出给spi_ctrl,将数据存储入flash.usedw  (           )
    );spi_ctrl spi_ctrl_inst(/* input                */.clk     (clk     ),//系统    /* input                */.rst_n   (rst_n   ),//复位/* input       [01:0]   */.key_in  (key     ),//控制读、写 00:停止工作 01:写 10:读/* input       [07:0]   */.data_rx (data_rx ),//需要写入的数据(串口接收的数据)/* input                */.miso    (miso    ),//主机接收,从机输出/* input                */.empty_rx(empty_rx),//fifo_rx的空信号/* input                */.full_tx (full_tx ),//fifo_tx的满信号   /* output               */.mosi    (mosi    ),//主机输出,从机接收/* output               */.cs_n    (cs_n    ),//片选/* output               */.sclk    (sclk    ),//SPI 时钟/* output  reg [07:0]   */.data_tx (data_tx ),//读取到的数据(串口发送的数据)/* output               */.rdreq_rx(rdreq_rx),//向fifo_rx发送读请求/* output               */.wrreq_tx(wrreq_tx) //向fifo_tx发送写请求
    );fifo_tx fifo_tx_inst (.aclr   (!rst_n     ),//fifo复位.clock  (clk        ),//fifo时钟.data   (data_tx    ),//从flash中将数据读取出来缓存进fifo.rdreq  (rdreq_tx   ),//uart_tx读请求.wrreq  (wrreq_tx   ),//spi_ctrl写请求.empty  (empty_tx   ),//uart_tx读数据空标志.full   (full_tx    ),//spi_ctrl写数据满标志.q      (tx_data    ),//将fifo中的数据发送给uart_tx进行输出.usedw  (           )
    );uart_tx uart_tx_inst(/* input                */.clk     (clk     ),//时钟信号/* input                */.rst_n   (rst_n   ),//复位信号/* input       [07:0]   */.tx_data (tx_data ),//需要发送的数据/* input                */.empty_tx(empty_tx),//fifo_tx的空信号/* output  reg          */.rdreq_tx(rdreq_tx),//向fifo_tx发送读请求   /* output  reg          */.uart_tx (uart_tx ) //数据输出
    );endmodule

    2、key_shake模块

  4. //状态机实现按键消抖																		
    module key_deshake #(parameter KEY_W = 3)//按键位宽
    (input                            clk     ,//时钟信号input                            rst_n   ,//复位信号input           [KEY_W - 1:0]    key_in  ,//按键输入信号output  reg     [KEY_W - 1:0]    key_out  //按键输出信号
    );parameter TIME_20ms = 1_000_000; //20ms延时常量声明parameter IDLE = 4'b0001,   //初始状态 DOWN = 4'b0010,   //检测到按键按下HOLD = 4'b0100,   //按键按下稳定的状态UP   = 4'b1000;   //按键弹起的状态reg [19:0]          cnt;
    wire                add_cnt;
    wire                end_cnt;reg [KEY_W - 1:0]   key_in_ff0; //同步打拍
    reg [KEY_W - 1:0]   key_in_ff1;
    reg [KEY_W - 1:0]   key_in_ff2;wire        posdge; //上升沿
    wire        negdge; //下降沿reg [3:0]   state_c  ;	//现态
    reg [3:0]   state_n  ;	//次态
    wire        idle2down;	//IDLE跳转到DOWN的转移条件
    wire        down2hold;	//DOWN跳转到HOLD的转移条件
    wire        hold2up  ;	//HOLD跳转到UP的转移条件
    wire        up2idle  ;	//UP跳转到IDLE的转移条件//同步 打拍 检测上升沿、下降沿
    always @(posedge clk or negedge rst_n) beginif(!rst_n) beginkey_in_ff0 <= {KEY_W{1'b1}};key_in_ff1 <= {KEY_W{1'b1}};key_in_ff2 <= {KEY_W{1'b1}};endelse beginkey_in_ff0 <= key_in; key_in_ff1 <= key_in_ff0;key_in_ff2 <= key_in_ff1;end
    end
    assign posdge = (&key_in_ff1) && ~(&key_in_ff2);//检测按键上升沿
    assign negdge = ~(&key_in_ff1) && (&key_in_ff2);//检测按键下降沿//消抖20ms计数器
    always @(posedge clk or negedge rst_n) beginif(!rst_n) begincnt <= 20'b0;endelse if(add_cnt) beginif(end_cnt) begincnt <= 20'b0;endelse begincnt <= cnt + 20'b1;endendelsecnt <= 20'b0;	//只有在DOWN和UP状态进行计数,其他状态清零
    end
    assign  add_cnt = state_c == DOWN || state_c == UP;      //计数器使能信号
    assign  end_cnt = add_cnt && cnt == TIME_20ms - 1; //状态机描述按键消抖
    //同步时序描述状态转移
    always @(posedge clk or negedge rst_n) beginif(!rst_n) beginstate_c <= IDLE;endelse beginstate_c <= state_n;end
    end//组合逻辑描述状态转移的条件
    always @(*) begincase(state_c)IDLE:beginif(idle2down) begin	//当满足条件时进行状态转移state_n = DOWN;endelse beginstate_n = state_c;endendDOWN:beginif(down2hold) beginstate_n = HOLD;endelse beginstate_n = state_c;endendHOLD:beginif(hold2up) beginstate_n = UP;endelse beginstate_n = state_c;endendUP  :beginif(up2idle) beginstate_n = IDLE;endelse beginstate_n = state_c;endendendcase
    end//状态转移的条件
    assign  idle2down = state_c == IDLE && negdge	; //在初始状态IDLE检测到下降沿时跳转到DOWN
    assign  down2hold = state_c == DOWN && end_cnt	; //在DOWN下20ms计数器开始计数,计数到最大值时跳转到HOLD
    assign  hold2up   = state_c == HOLD && posdge	; //在HOLD下检测到上升沿时跳转到UP
    assign  up2idle   = state_c == UP   && end_cnt	; //在UP下20ms计数器开始计数,计数到最大值时跳转到IDLE//时序逻辑描述输出
    always @(posedge clk or negedge rst_n) beginif(!rst_n) beginkey_out <= {KEY_W{1'b0}};endelse if(hold2up) begin		//在DOWN跳转到HOLD或者在HOLD跳转到UP时进行输出,输出对输入进行取反key_out <= ~key_in_ff2;endelse beginkey_out <= {KEY_W{1'b0}};end
    endendmodule		

    3、uart_rx模块

  5. module uart_rx(input               clk     ,   //系统时钟信号input               rst_n   ,   //系统复位信号input               uart_rx ,   //数据接收input               full_rx ,   //fifo_rx的满信号output              wrreq_rx,   //向fifo_rx发送写请求output     [7:0]    rx_data     //数据串行接并行出);/**********************************相关常量定义***********************************
    *********************************************************************************/
    parameter   COUNT = 50_000_000; //系统时钟频率 周期20ns
    parameter   BPS   = 115200;     //波特率
    parameter   CNT   = COUNT / BPS;      
    parameter   BIT   = 10  ; //接收1帧的bit数parameter   IDLE    =   4'b0001,START   =   4'b0010,DATA    =   4'b0100,DONE    =   4'b1000;/**********************************相关变量定义***********************************
    *********************************************************************************/
    reg [3:0]   state_c;
    reg [3:0]   state_n;reg [15:0]  cnt_clk;
    wire        add_cnt_clk;
    wire        end_cnt_clk;reg [03:0]  cnt_bit;
    wire        add_cnt_bit;
    wire        end_cnt_bit;wire        rx_start;    //rx数据接收开始信号
    reg         rx_done ;      
    reg         data_reg;    //寄存uart_rx接收的数据
    reg [7:0]   data_out_reg;
    reg [7:0]   data    ;reg         uart_rx_reg0;//同步打拍
    reg         uart_rx_reg1;
    reg         uart_rx_reg2;reg         check       ;//判断接收到的数据是否正确/******************************向fifo_rx发送写请求********************************
    *********************************************************************************/
    assign  wrreq_rx = full_rx ? 'd0 : rx_done;/****************************对发送的数据进行同步打拍******************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) beginif(!rst_n) beginuart_rx_reg0 <= 1'b1;           uart_rx_reg1 <= 1'b1;           uart_rx_reg2 <= 1'b1;               endelse beginuart_rx_reg0 <= uart_rx;uart_rx_reg1 <= uart_rx_reg0;uart_rx_reg2 <= uart_rx_reg1;end
    end
    assign rx_start = (~uart_rx_reg1 && uart_rx_reg2) && cnt_bit == 0; //检测到下降沿/**********************************相关计数器设计*********************************
    *********************************************************************************/
    //发送1bit所需要的周期数cnt_clk
    always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_clk <= 0;endelse if(state_c == IDLE) begincnt_clk <= 0;end else if(add_cnt_clk)begin if(end_cnt_clk)begin cnt_clk <= 0;endelse begin cnt_clk <= cnt_clk + 1;end end
    end 
    assign add_cnt_clk = state_c == DATA;
    assign end_cnt_clk = add_cnt_clk && cnt_clk == CNT - 1;//发送一字节所需要的bit数cnt_bit
    always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit <= 0;endelse if(state_c == IDLE) begincnt_bit <= 0;end     else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 0;endelse begin cnt_bit <= cnt_bit + 1;end end
    end 
    assign add_cnt_bit = end_cnt_clk;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == BIT - 1;/**********************************rx_done信号设计********************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) begin if(!rst_n) beginrx_done <= 'd0;end else if(state_c == DATA && end_cnt_bit) begin rx_done <= 'd1;end else begin rx_done <= 'd0;end 
    end/*********************************uart_rx状态机设计*******************************
    *********************************************************************************/
    //状态机第一段
    always @(posedge clk or negedge rst_n) begin if(!rst_n) beginstate_c <= IDLE;end else begin state_c <= state_n;end 
    end//第二段
    always @(*) begincase(state_c)IDLE:   beginif(rx_start) beginstate_n = START;endelse beginstate_n = IDLE;endendSTART:  beginstate_n = DATA;endDATA:   begin   if(end_cnt_bit) beginstate_n = DONE;endelse if(check) beginstate_n = IDLE;endelse beginstate_n = state_c;endendDONE:   beginstate_n = IDLE;enddefault:state_n = IDLE;endcase 
    end//第三段
    //对接收到的数据进行寄存
    always @(posedge clk or negedge rst_n) begin if(!rst_n) begindata_reg <= 'd0;end else if(state_c == DATA) begin data_reg <= uart_rx_reg2;end else begin data_reg <= 'd0;end 
    end/*********************************检测数据是否是误接*******************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) begin if(!rst_n) begincheck <= 'd0;end else if(state_c == DATA && cnt_clk == CNT / 2 && cnt_bit == 0) begin check <= data_reg;end else if(state_c == DATA && cnt_clk == CNT / 2 && cnt_bit == 9)begin check <= !data_reg;endelse begincheck <= 'd0;end 
    end/********************************接收到的数据寄存发送******************************
    *********************************************************************************/
    //对接收到的数据进行发送的寄存器 逐位寄存
    always @(posedge clk or negedge rst_n)begin if(!rst_n)begindata_out_reg <= 8'h0;end else if(state_c == DATA && cnt_clk == CNT / 2 && cnt_bit > 0 && cnt_bit < 9)begin  //只有在数据位才接收数据data_out_reg <= {data_reg,data_out_reg[7:1]};                                  //并且在每位数据传输时间的一般进行接收end else if(state_c == DONE || state_c == IDLE) begin  //当接收数据完成时,对接收寄存器进行清零操作data_out_reg <= 8'h0;endelse begin data_out_reg <= data_out_reg;end 
    end//对接收到的数据进行发送 
    always @(posedge clk or negedge rst_n)begin if(!rst_n)begindata <= 8'h0;end else if(state_c == DATA && end_cnt_bit)begin //当bit计数器计数到最大值时,将接收到完整的数据赋值给数据发送端  data <= data_out_reg;end else begin data <= data;end 
    endassign rx_data = data;endmodule

    4、spi_ctrl模块

  6. module spi_ctrl(input               clk         ,//系统input               rst_n       ,//复位input       [02:0]  key_in      ,//控制读、写 00:停止工作 01:写 10:读input       [07:0]  data_rx     ,//需要写入的数据(串口接收的数据)input               miso        ,//主机接收,从机输出input               full_tx     ,//fifo_tx的满信号input               empty_rx    ,//fifo_rx的空信号output              mosi        ,//主机输出,从机接收output              cs_n        ,//片选output              sclk        ,//SPI 时钟output  reg         wrreq_tx    ,//向fifo_tx发送写请求output              rdreq_rx    ,//向fifo_rx发送读请求output  reg [07:0]  data_tx      //读取到的数据(串口发送的数据)
    );parameter   WAIT_WR =   100         ,   //两个指令间等待100ns  WAIT_PP =   250_000     ,   //页编程所需时间5msWAIT_SE =   150_000_000 ;   //扇区擦除所需时间3s parameter   BYTE_CNT_MAX =  'd260;parameter   IDLE    =   'd1,WREN    =   'd2,WAIT    =   'd3,SEPP    =   'd4,READ    =   'd5,DONE    =   'd6;reg [03:0]  state_c     ;
    reg [03:0]  state_n     ;reg [11:0]  BYTE_CNT    ;//字节数
    reg [11:0]  cnt_byte    ;//字节计数器
    wire        add_cnt_byte;
    wire        end_cnt_byte;reg [27:0]  WAIT_CNT    ;//等待时间
    reg [27:0]  cnt_wait    ;//等待计数器
    wire        add_cnt_wait;
    wire        end_cnt_wait;reg         sepp_flag   ;//0:执行D8h扇区擦除 1:执行02h页面写入
    reg [01:0]  wr_rd       ;//读写判断
    reg         req         ;//读写请求 相当于片选
    wire        done        ;//读写完一个字节
    reg         wr_rd_done  ;//读、写数据结束
    wire[07:0]  data_rd     ;//读取到的数据
    reg [07:0]  data_wr     ;//数据写入
    reg         rden        ;//读数据使能信号
    reg         wrreq_tx_reg;reg [07:0]  wr_addr     ;spi spi_inst(/* input                */.clk         (clk       ),//系统/* input                */.rst_n       (rst_n     ),//复位/* input                */.miso        (miso      ),//主机接收,从机输出/* input       [07:0]   */.data_wr     (data_wr   ),//写入的数据或指令/* input                */.req         (req       ),//读写请求/* input                */.rden        (rden      ),//读数据使能信号/* output               */.cs_n        (cs_n      ),//片选/* output               */.mosi        (mosi      ),//主机输出,从机接收/* output               */.sclk        (sclk      ),//SPI 时钟/* output               */.done        (done      ),//读写完1字节/* output      [07:0]   */.data_rd     (data_rd   ) //读取到的数据
    );/********************************fifo_rx/tx的读写请求*****************************
    *********************************************************************************/
    assign rdreq_rx = empty_rx ? 'd0 : state_c == SEPP && sepp_flag == 'd1 && cnt_byte >= 'd4 && done;
    // assign wrreq_tx = full_tx  ? 'd0 : state_c == READ && cnt_byte >= 'd4 && done; 
    always @(posedge clk or negedge rst_n) begin if(!rst_n) beginwrreq_tx_reg <= 'd0;end else if(!full_tx && state_c == READ && cnt_byte >= 'd4 && done) begin wrreq_tx_reg <= 'd1;end else begin wrreq_tx_reg <= 'd0;end 
    endalways @(posedge clk or negedge rst_n) begin if(!rst_n) beginwrreq_tx <= 'd0;end else begin wrreq_tx <= wrreq_tx_reg;end 
    end
    /*************************************字节计数器**********************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n)begin if(!rst_n) begincnt_byte <= 'd0;end else if(state_c == SEPP && sepp_flag == 'd1 && empty_rx || wr_addr == 'd255 && rdreq_rx) begincnt_byte <= 'd0;endelse if(add_cnt_byte) begin if(end_cnt_byte) begin cnt_byte <= 'd0;endelse begin cnt_byte <= cnt_byte + 'd1;end end
    end 
    assign add_cnt_byte = done && (state_c == WREN || state_c == SEPP || state_c == READ);
    assign end_cnt_byte = add_cnt_byte && cnt_byte == BYTE_CNT - 'd1;always @(posedge clk or negedge rst_n) begin if(!rst_n) beginwr_addr <= 'd0;endelse if(wr_addr == 'd255 && rdreq_rx) beginwr_addr <= 'd0;endelse if(rdreq_rx) begin wr_addr <= wr_addr + 'd1;end else begin wr_addr <= wr_addr;end 
    endalways @(*) begin if(!rst_n) beginBYTE_CNT <= 'd0;end else begin case(state_c)WREN   :BYTE_CNT = 'd1;SEPP   :if(sepp_flag)BYTE_CNT = BYTE_CNT_MAX; //1字节指令 + 3字节地址 + 最多256的写入数据elseBYTE_CNT = 'd4;          //1字节指令 + 3字节地址READ   :BYTE_CNT =  BYTE_CNT_MAX;    //1字节指令 + 3字节知道 + 最多256的读取数据default:BYTE_CNT = 'd0;endcaseend 
    end/***********************************延迟等待计数器*********************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n)begin if(!rst_n) begincnt_wait <= 'd0;end else if(add_cnt_wait) begin if(end_cnt_wait) begin cnt_wait <= 'd0;endelse begin cnt_wait <= cnt_wait + 'd1;end end
    end 
    assign add_cnt_wait = state_c == WAIT;
    assign end_cnt_wait = add_cnt_wait && cnt_wait == WAIT_CNT - 1;always @(posedge clk or negedge rst_n) begin if(!rst_n) beginWAIT_CNT <= 'd0;endelse begincase(state_c)WREN   :WAIT_CNT <= WAIT_WR;SEPP   :if(sepp_flag)WAIT_CNT <= WAIT_PP;elseWAIT_CNT <= WAIT_SE;         default:WAIT_CNT <= WAIT_CNT;endcase end
    end/**********************************读/写判断及请求********************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) begin if(!rst_n) beginwr_rd <= 2'b0;end else if(key_in == 3'b001) begin wr_rd <= 2'b01;end else if(key_in == 3'b010) begin wr_rd <= 2'b10;end else if(wr_rd_done) beginwr_rd <= 2'b00;endelse beginwr_rd <= wr_rd;end
    endalways @(*) begin if(!rst_n) beginreq <= 'd1;end else if(state_c == WREN || state_c == SEPP || state_c == READ) begin //读、写状态req <= 'd0;end else if(state_c == IDLE || state_c == DONE || state_c == WAIT) begin //初始、等待、接收状态req <= 'd1;endelse beginreq <= req;end
    end/***********************************读/写结束信号*********************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) begin if(!rst_n) beginwr_rd_done <= 'd0;end else if((state_c == SEPP && sepp_flag == 'd1 && (end_cnt_byte || empty_rx)) || (state_c == READ && end_cnt_byte)) begin wr_rd_done <= 'd1;end else begin wr_rd_done <= 'd0;end 
    end/*************************************SE/PP选择***********************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) begin if(!rst_n) beginsepp_flag <= 'd0;end else if(state_c == WAIT && WAIT_CNT == WAIT_SE && end_cnt_wait) begin //擦除结束,sepp_flag转为pp模式sepp_flag <= 'd1;end else if(key_in == 3'b100/* state_c == WAIT && WAIT_CNT == WAIT_PP && end_cnt_wait */) begin //写数据结束,sepp_flag转化为SE模式sepp_flag <= 'd0;end else beginsepp_flag <= sepp_flag;end
    end/***************************************状态机************************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) begin if(!rst_n) beginstate_c <= IDLE;end else begin state_c <= state_n;end 
    endalways @(*) begin case(state_c)IDLE   :beginif(wr_rd == 2'b01)state_n = WREN;else if(wr_rd == 2'b10)state_n = READ;elsestate_n = IDLE;endWREN   :beginif(done)state_n = WAIT;elsestate_n = WREN;endWAIT   :beginif(end_cnt_wait && WAIT_CNT == WAIT_WR)state_n = SEPP;else if(end_cnt_wait && (WAIT_CNT == WAIT_PP || WAIT_SE))state_n = DONE;elsestate_n = WAIT;end SEPP   :beginif(sepp_flag == 'd0 && end_cnt_byte && BYTE_CNT == 'd4) //扇区擦除state_n = WAIT;else if(sepp_flag == 'd1 && BYTE_CNT == BYTE_CNT_MAX && (end_cnt_byte || empty_rx)) //数据写入state_n = WAIT;elsestate_n = SEPP;endREAD   :beginif(end_cnt_byte && BYTE_CNT ==  BYTE_CNT_MAX)state_n = DONE;elsestate_n = READ;endDONE   :beginstate_n = IDLE;enddefault:state_n = IDLE;endcase 
    end/***********************************写入接口的数据********************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) begin if(!rst_n) begindata_wr <= 'h0;end else if(state_c == WREN) begin data_wr <= 8'h06;end else if(state_c == SEPP && sepp_flag == 'd0)begin //扇区擦除case(cnt_byte) 0:data_wr <= 8'hD8;1:data_wr <= 8'h00;2:data_wr <= 8'h00;3:data_wr <= 8'h00;endcaseend else if(state_c == SEPP && sepp_flag == 'd1) begin //写数据case(cnt_byte)0:data_wr <= 8'h02;1:data_wr <= 8'h00;2:data_wr <= 8'h00;3:data_wr <= wr_addr;default:data_wr <= data_rx;endcaseendelse if(state_c == READ) begincase(cnt_byte)0:data_wr <= 8'h03;1:data_wr <= 8'h00;2:data_wr <= 8'h00;3:data_wr <= 8'h00;default:data_wr <= 8'h0;endcaseendelse begindata_wr <= 'h0;end
    end/***********************************读取接口的数据********************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) begin if(!rst_n) beginrden    <= 'd0;end else if(state_c == READ) begin case(cnt_byte)0,1,2,3:begin rden <= 'd0;enddefault:begin rden <= 'd1;endendcaseend else begin rden <= 'h0;end 
    endalways @(*) begin if(!rst_n) begindata_tx <= 'd0;end else if(wrreq_tx) begin data_tx <= data_rd;        end else begin data_tx <= 'd0;end 
    endendmodule

    5、spi模块

  7. module spi (input               clk         ,//系统input               rst_n       ,//复位input               miso        ,//主机接收,从机输出input       [07:0]  data_wr     ,//写入的数据或指令input               req         ,//读写请求,低电平有效input               rden        ,//读数据使能信号output  reg         cs_n        ,//片选output  reg         mosi        ,//主机输出,从机接收output  reg         sclk        ,//SPI 时钟output  reg         done        ,//读写完1字节output  reg [07:0]  data_rd      //读取到的数据
    );reg [01:0]  cnt_sclk;
    reg [03:0]  cnt_bit ;
    reg [07:0]  data_rd_reg;/**************************************sclk时钟***********************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) begin if(!rst_n) begincnt_sclk <= 'd0;end else if(req) begincnt_sclk <= 'd0;endelse if(cnt_sclk == 4 - 1) begin cnt_sclk <= 'd0;end else if(!req)begin cnt_sclk <= cnt_sclk + 'd1;end else begincnt_sclk <= cnt_sclk;end
    endalways @(posedge clk or negedge rst_n) begin if(!rst_n) beginsclk <= 'd1;endelse if(cs_n) beginsclk <= 'd1;end else if(cnt_sclk == 1) begin sclk <= 'd0;end else if(cnt_sclk == 3)begin sclk <= 'd1;end else beginsclk <= sclk;end
    end/**************************************cs_n片选***********************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) begin if(!rst_n) begincs_n <= 'd0;end else begin cs_n <= req;end 
    end/***********************************数据--位计数器********************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) begin if(!rst_n) begincnt_bit <= 'd0;end else if(cnt_bit == 8 - 1 && cnt_sclk == 'd3) begin cnt_bit <= 'd0;end else if(cnt_sclk == 'd3)begin cnt_bit <= cnt_bit + 'd1;end else begincnt_bit <= cnt_bit;end
    endalways @(posedge clk or negedge rst_n) begin if(!rst_n) begindone <= 'd0;end else if(cnt_bit == 8 - 1 && cnt_sclk == 'd2) begin done <= 'd1;end else begin done <= 'd0;end 
    end/**************************************mosi信号***********************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) begin if(!rst_n) beginmosi <= 'd0;end else if(cnt_sclk == 'd1) begin mosi <= data_wr[7 - cnt_bit];end else begin mosi <= mosi;end 
    end/**************************************data_rd***********************************
    *********************************************************************************/
    always @(posedge clk or negedge rst_n) begin if(!rst_n) begindata_rd_reg <= 'd0;end else if(rden && cnt_sclk == 'd3) begin data_rd_reg[7 - cnt_bit] <= miso;end else begin data_rd_reg <= data_rd_reg;end 
    endalways @(*) begin if(!rst_n) begindata_rd <= 'd0;end else if(cnt_bit == 'd0) begin data_rd <= data_rd_reg;end else begin data_rd <= 'd0;end 
    endendmodule
    

    6、uart_tx模块

  8. //UART发送端  无校验位 结束为1位
    module uart_tx(input           clk     ,//时钟信号input           rst_n   ,//复位信号input  [07:0]   tx_data ,//需要发送的数据input           empty_tx,//fifo_tx的空信号output          rdreq_tx,//向fifo_tx发送读请求 output  reg     uart_tx  //数据输出
    );/**********************************相关常量定义***********************************
    *********************************************************************************/
    parameter   COUNT = 50_000_000; //系统时钟频率 周期20ns
    parameter   BPS   = 115200;     //波特率
    parameter   CNT   = COUNT / BPS;//传输1bit所需计数次数 COUNT / BPS; 
    parameter   BIT   = 10  ;       //传输一个字节(1位起始位,8位数据位,1位结束为) 从低位开始发
    parameter   IDLE  = 4'b0001,START = 4'b0010,DATA  = 4'b0100,DONE  = 4'b1000;/**********************************相关信号定义***********************************
    *********************************************************************************/
    reg [15:0]  cnt_clk;
    wire        add_cnt_clk;
    wire        end_cnt_clk;reg [3:0]   cnt_bit;
    wire        add_cnt_bit;
    wire        end_cnt_bit;reg [3:0]   state_c;
    reg [3:0]   state_n;reg [7:0]   tx_data_reg ; 
    reg [7:0]   data    ; 
    wire        tx_start;
    wire        tx_done ;/*******************************向fifo_tx发送读请求********************************
    *********************************************************************************/
    assign rdreq_tx = ~empty_tx && tx_done;
    assign tx_start = tx_done ? ~empty_tx && tx_done : ~empty_tx;/*****************************对要输出的数据进行寄存*******************************
    *********************************************************************************/
    //tx_data_reg 对要输出的数据进行打拍
    always @(posedge clk or negedge rst_n) begin if(!rst_n) begintx_data_reg <= 'd0;end else if(tx_start) begin tx_data_reg <= tx_data;end else begin tx_data_reg <= 'd0;end 
    end//data 对打拍后的数据进行寄存,为后续数据的输出做准备
    always @(posedge clk or negedge rst_n) begin if(!rst_n) begindata <= 'd0;endelse if(state_c == DONE) begindata <= 'd0;end else if(state_c == START)begin data <= tx_data_reg;end else begindata <= data;end
    end/**********************************相关计数器设计*********************************
    *********************************************************************************/
    //发送1bit所需要的周期数cnt_clk
    always @(posedge clk  or negedge rst_n) beginif(!rst_n) begincnt_clk <= 13'b0;endelse if(add_cnt_clk) beginif(end_cnt_clk) begincnt_clk <= 13'b0;endelse begincnt_clk <= cnt_clk + 13'b1;endendelse begincnt_clk <= 13'b0;end
    end
    assign add_cnt_clk = state_c == DATA;
    assign end_cnt_clk = add_cnt_clk && cnt_clk == CNT - 1;//发送一字节所需要的bit数cnt_bit
    always @(posedge clk or negedge rst_n) beginif(!rst_n) begincnt_bit <= 4'b0;endelse if(add_cnt_bit) beginif(end_cnt_bit) begincnt_bit <= 4'b0;endelse begincnt_bit <= cnt_bit + 4'b1;endendelse begincnt_bit <= cnt_bit;end
    end
    assign add_cnt_bit = end_cnt_clk;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == BIT - 1;//tx_done
    assign tx_done = end_cnt_bit;/**********************************Tx状态机设计***********************************
    *********************************************************************************/
    //状态机第一段
    always @(posedge clk or negedge rst_n) begin if(!rst_n) beginstate_c <= IDLE;end else begin state_c <= state_n;end 
    end//第二段 状态转移
    always @(*) begin case(state_c)IDLE:   beginif(tx_start) beginstate_n = START;endelse beginstate_n =IDLE;endend START:  beginstate_n = DATA;endDATA :  beginif(end_cnt_bit) beginstate_n = DONE;endelse beginstate_n = state_c;endendDONE :  beginstate_n = IDLE;enddefault:state_n <= IDLE;endcase
    end//第三段 uart_tx输出
    always @(posedge clk or negedge rst_n) begin if(!rst_n) beginuart_tx <= 'd1;end else if(state_c == DATA) begin case(cnt_bit) 0:uart_tx <= 1'b0          ;1:uart_tx <= data[0];2:uart_tx <= data[1];3:uart_tx <= data[2];4:uart_tx <= data[3];5:uart_tx <= data[4];6:uart_tx <= data[5];7:uart_tx <= data[6];8:uart_tx <= data[7];9:uart_tx <= 1'b1          ;default:uart_tx <= 1'b1;endcaseend else begin uart_tx <= 1'b1;end 
    endendmodule

  9. 四、仿真波形

五、上板测试

在串口调试助手发送“成功驱动M25P16!(含回车)”,然后按下key_in[0],等待3s先进行扇区擦除,然后才进行数据的写入。

再按下key_in[1],读取FLASH中的数据。

再按下key_in[0],写入数据“再次写入数据成功!(含回车)”,再按下key_in[1]读取FLASH中的数据。

按下key_in[2],进行扇区擦除,在按下key_in[1]读取数据全为FF,说明擦除成功。

更多推荐

基于verilog驱动M25P16(FALSH)

本文发布于:2024-02-27 08:36:53,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1705902.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:verilog   FALSH

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!