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、M23P16顶层模块
-
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模块
-
//状态机实现按键消抖 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模块
-
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模块
-
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模块
-
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模块
-
//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
-
四、仿真波形
五、上板测试
在串口调试助手发送“成功驱动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)
发布评论