内容用FPGA芯片Cylone IV E:EP4CE6F17C8 实现对flash读写操作,数据通过uart寄存在FIFO_0,按键_0按下读取数据通过SPI协议写入flash,再按键_1按下通过SPI协议读出数据寄存在FIFO_2经uart协议输出
SPI(Serial Peripheral interface)是由摩托罗拉公司定义的一种串行外围设备接口, 是一种全双工、同步的通信总线,只需要四根信号线即可,节约引脚,同时有利于 PCB 的布局。正是出于这种简单易用的特性,现在越来越多的芯片集成了 SPI 通信协议,如 FLASH、AD 转换器等。
SPI 协议支持一主多从、全双工、半双工模式。
SPI 通信需要四根线,分别为 cs_n、sclk、mosi 和 miso。其中 cs_n、sclk、mosi 是由主机输出给从机,而 miso 由从机输出给主机。cs_n 用于控制从机是否被选中,也就是说只有片选信号有效时,对从机的操作才有效;sclk 是由主机产生的同步时钟,用于同步数据;mosi 和 miso 是主机发送和接收的数据脚。传输数据时,以 MSB 的形式传输
SPI 通信一般有 4 种不同的模式,不同的从设备在出厂时被厂家配置为其中几种通信模式,通信模式是不允许用户修改的。主设备和从设备必须在同一模式下进行通信,否则数据会接收错误。SPI 的通信模式是由 CPOL(时钟极性)和 CPHA(时钟相位)来决定的;
模式 0:CPOL = 0,CPHA = 0;
模式 1:CPOL = 0,CPHA = 1;
模式 2:CPOL = 1,CPHA = 0;
模式 3:CPOL = 1,CPHA = 1。
当 CPOL = 1 时,sclk 在空闲时为高电平,发起通信后的第一个时钟沿为下降沿;
当 CPOL = 0 时,SPI 时钟信号 sclk 空闲时为低电平,发起通信后的第一个时钟沿为上升沿
当 CPHA = 1 时,时钟的第一个变化沿(上升沿或者下降沿)数据开始改变, 那么也就意味着时钟的第 2 个变化沿(与第一个变化沿相反)锁存数据;
当 CPHA = 0 时,数据在时钟的第一个变化沿之前就已经改变,并且保持稳定,也就意味着在时钟的第一个变化沿锁存数据。
M25P16 是一款带有写保护机制的串行 Flash 存储芯片,容量为 16Mbit(2MB);一共
分为 32 个 sector,每个 sector 共 256B 存储容量,每个 sector 擦写次数为 10 万次、数
据保存期限至少 20 年。支持 SPI 通信模式 0 和模式 3。在对 Flash 芯片写数据之前,需要先执行擦除操作,将所有数据变为 FF。
flash指令表
实现读写操作只用了WREN、RDID、WRSR、READ、PP、SE命令
在手册已经说了写使能必须在每个PP、SE、BE、WRSR指令前设置,在写使能发送结束后片选信号拉高
读取标识(RDID)指令允许读取8位的制造商标识,然后是两个字节的设备标识。制造商标识由JEDEC指定,意法半导体的识别值为20小时。设备标识由设备制造商指定,表示第一个字节的内存类型(20小时),以第二个字节表示设备的内存容量(15小时)。同样数据读取结束后片选信号拉高
读取状态寄存器(RDSR)指令允许读取状态寄存器。状态寄存器可以在任何时候被读取,即使在一个程序、擦除或写状态寄存器周期正在进行中。这个命令我认为是在读取其状态的一个命令可有可无有需要可以去看手册,其时序图如下
首先通过驱动芯片选择低来选择设备。读取数据字节(READ)指令的指令代码后面是一个3字节地址,每个位在串行时钟的上升边缘期间被锁定然后,在该地址的内存内容在串行数据输出上移出,在串行时钟的下降边缘期间
这里指出下flash只支持SPI的模式0、模式3,
时序图如下
页面程序(PP)指令允许在内存中编程字节(将位从1改为0)当页面程序周期正在进行时,可以读取状态寄存器以检查正在写入(WIP)位的值。在自定时页面程序周期中,正在写入(WIP)位为1,完成时为0。
扇区擦除(SE)指令将选定扇区内的所有位设置为1(FFh)。在接受它之前,必须先执行了写入启用(WREN)指令。在写入启用(WREN)指令被解码后,设备将设置写入启用锁存器(WEL)。扇区擦除(SE)指令通过驱动芯片选择低输入,然后是指令代码和串行数据输入.
下表有时钟频率、擦除、写入、片选信号拉高再次拉低的时间
always @(*)begin
case (spi_mode)
0 : begin if(spi_vld && (cnt_sck <= `SCK_HALF-1))begin
sck <= 0;
end
else if(cnt_sck > `SCK_HALF-1)begin
sck <= 1;
end
else begin
sck <= 0;
end
end
1 : begin if(spi_vld && (cnt_sck <= `SCK_HALF-1))begin
sck <= 1;
end
else if(cnt_sck > `SCK_HALF-1)begin
sck <= 0;
end
else begin
sck <= 0;
end
end
2 : begin if(spi_vld && (cnt_sck <= `SCK_HALF-1 ))begin
sck <= 1;
end
else if(cnt_sck > `SCK_HALF-1 )begin
sck <= 0;
end
else begin
sck <= 1;
end
end
3 : begin if(spi_vld && (cnt_sck <= `SCK_HALF-1))begin
sck <= 0;
end
else if(cnt_sck > `SCK_HALF-1 )begin
sck <= 1;
end
else begin
sck <= 1;
end
end
default: sck <= sck;
endcase
end
SPI时钟的产生思路是以时钟去对齐数据,这里SPI四种模式都写出来了的再数据读取只写了模式0
数据传输时序图
因为这次没画完时序图,代码写的时候有点小乱,整里了下
//参数定义
//spi时钟参数
`define SCK_PERIOD 20
`define SCK_HALF 10
//spi command
`define CMD_WREN 8'h06 //
`define CMD_WRDI 8'h04 //
`define CMD_RDID 8'h9f //
`define CMD_RDSR 8'h05 //
`define CMD_WRSR 8'h01 //
`define CMD_READ 8'h03 //
`define CMD_FAST_READ 8'h0b //
`define CMD_PP 8'h02 //
`define CMD_SE 8'hd8 //
`define CMD_BE 8'hc7 //
`define CMD_DP 8'hb9 //
`define CMD_RES 8'hab //
//spi byte
`define CMD_BYTE 1 // 命令1字节
`define ADDR_BYTE 3 // 地址3字节
`define ID_BYTE 3 // id3字节
`define DATA_BYTE 1 // id3字节
`define RDSR_BYTE 100 // 读状态寄存器最大可读1000次
`define DELAY_5MS 250_000 // 5ms 数据写入后需要等待5ms才能读出
//波特率
`define BAUD_9600 5208
`define BAUD_19200 2604
`define BAUD_38400 1302
`define BAUD_115200 434
`define STOP_BIT 1'b1 //数据停止位
`define START_BIT 1'b0 //数据开始位
module test(
input clk ,
input rst_n ,
input [2:0] key_in ,
input uart_rxd,
input miso ,
output sck ,
output mosi ,
output cs_n ,
output uart_txd
);
//中间信号定义
wire [2:0] key_out ;
wire [7:0] rx_byte ;
wire rx_byte_vld ;
wire [7:0] dout_m ;
wire [7:0] dout ;
wire dout_vld ;
wire spi_vld ;
wire en ;
wire [7:0] din_m ;
wire busy_m ;
wire wr_done ;
wire busy_t ;
//模块例化
key_filter_fsm # (.KEY_W(3),.TIME_20MS(1_000_000))u_key_filter_fsm
(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input [KEY_W - 1:0] */.key_in (key_in ),
/*output [KEY_W - 1:0] */.key_out (key_out )
);
uart_rx u_uart_rx
(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input */.rx_din (uart_rxd ),
/*input [1:0] */.baud_sel (2'b0 ),
/*output [7:0] */.rx_byte (rx_byte ),
/*output */.rx_byte_vld (rx_byte_vld )
);
wr_control u_wr_control(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input [2:0] */.key_in (key_out ),
/*input */.busy_m (busy_m ),
/*input */.busy_t (busy_t ),
/*input */.wr_done (wr_done ),
/*input [7:0] */.din (rx_byte ),
/*input */.din_vld (rx_byte_vld ),
/*input [7:0] */.din_m (din_m ),
/*output reg [7:0] */.dout_m (dout_m ),
/*output [7:0] */.dout (dout ),
/*output */.dout_vld (dout_vld ),
/*output */.en (en ),
/*output */.spi_vld (spi_vld )
);
spi_master u_spi_master(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input */.en (en ),
/*input [1:0] */.spi_mode (2'b00 ),
/*input */.spi_vld (spi_vld ),
/*input [7:0] */.din (dout_m ),
/*input */.miso (miso ),
/*output reg */.sck (sck ),
/*output reg */.cs_n (cs_n ),
/*output */.mosi (mosi ),
/*output reg [7:0] */.dout (din_m ),
/*output reg */.busy (busy_m ),
/*output */.wr_done (wr_done )
);
uart_tx u_uart_tx
(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input [1:0] */.baud_sel (2'b00 ),
/*input [7:0] */.tx_byte (dout ),
/*input */.tx_byte_vld (dout_vld ),
/*output */.tx_dout (uart_txd ),
/*output */.busy (busy_t )
);
endmodule
`include "param.v"
module spi_master(
input clk ,
input rst_n ,
input en ,
input [1:0] spi_mode,
input spi_vld ,
input [7:0] din ,
input miso ,
output reg sck ,
output reg cs_n ,
output mosi ,
output reg [7:0] dout ,
output reg busy ,
output wr_done
);
//中间信号定义
reg [4:0] cnt_bit ;
wire add_cnt_bit ;
wire end_cnt_bit ;
reg [8:0] cnt_sck ;
wire add_cnt_sck ;
wire end_cnt_sck ;
reg end_cnt_sck_r ;
//计数器
//cnt_sck
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_sck <= 0;
end
else if(add_cnt_sck)begin
if(end_cnt_sck | en)begin
cnt_sck <= 0;
end
else begin
cnt_sck <= cnt_sck + 1;
end
end
else begin
cnt_sck <= cnt_sck;
end
end
assign add_cnt_sck = spi_vld;
assign end_cnt_sck = add_cnt_sck && cnt_sck == `SCK_PERIOD-1;
//cnt_bit
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 0;
end
else if(add_cnt_bit )begin
if(end_cnt_bit | en)begin
cnt_bit <= 0;
end
else begin
cnt_bit <= cnt_bit + 1;
end
end
else begin
cnt_bit <= cnt_bit;
end
end
assign add_cnt_bit = (cs_n == 0)&& end_cnt_sck;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 8-1;
//sck
always @(*)begin
case (spi_mode)
0 : begin if(spi_vld && (cnt_sck <= `SCK_HALF-1))begin
sck <= 0;
end
else if(cnt_sck > `SCK_HALF-1)begin
sck <= 1;
end
else begin
sck <= 0;
end
end
1 : begin if(spi_vld && (cnt_sck <= `SCK_HALF-1))begin
sck <= 1;
end
else if(cnt_sck > `SCK_HALF-1)begin
sck <= 0;
end
else begin
sck <= 0;
end
end
2 : begin if(spi_vld && (cnt_sck <= `SCK_HALF-1 ))begin
sck <= 1;
end
else if(cnt_sck > `SCK_HALF-1 )begin
sck <= 0;
end
else begin
sck <= 1;
end
end
3 : begin if(spi_vld && (cnt_sck <= `SCK_HALF-1))begin
sck <= 0;
end
else if(cnt_sck > `SCK_HALF-1 )begin
sck <= 1;
end
else begin
sck <= 1;
end
end
default: sck <= sck;
endcase
end
//end_cnt_sck_r
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
end_cnt_sck_r <= 0;
end
else begin
end_cnt_sck_r <= end_cnt_sck;
end
end
//输出
//cs_n
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cs_n <= 1;
end
else if(en)begin
cs_n <= 0;
end
else if(cs_n == 0 && (!spi_vld) && end_cnt_sck_r)begin
cs_n <= 1;
end
end
//mosi
assign mosi = din[7-cnt_bit];
//dout
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout <= 0;
end
else if(cs_n == 0 && cnt_sck == `SCK_PERIOD-1)begin
dout[7-cnt_bit] <= miso;
end
else begin
dout <= dout;
end
end
//wr_done
assign wr_done = end_cnt_bit;
//busy
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
busy <= 0;
end
else if(add_cnt_bit && end_cnt_bit == 0)begin
busy <= 1;
end
else if(end_cnt_bit)begin
busy <= 0;
end
end
endmodule
`include "param.v"
module wr_control(
input clk ,
input rst_n ,
input [2:0] key_in ,
input busy_m ,
input busy_t ,
input wr_done ,
input [7:0] din ,
input din_vld ,
input [7:0] din_m ,
output reg [7:0] dout_m ,
output [7:0] dout ,
output dout_vld ,
output en ,
output reg spi_vld
);
//参数定义
//中间信号定义
reg flag ;
reg flag0 ;
reg en_r ;
wire en_dge_n ;
reg data_vld ;
//例化中间信号
wire data_req ;
wire [7:0] dout1 ;
wire [7:0] dout2 ;
wire [1:0] en_ ;
wire [1:0] data_m_vld ;
wire write_done ;
wire read_done ;
wire busy_write ;
wire rxf_rdreq ;
wire rxf_wrreq ;
wire rxf_empty ;
wire rxf_full ;
wire [7:0] rxf_dout ;
wire [8:0] rxf_usedw ;
wire [7:0] txf_din ;
wire txf_rdreq ;
wire txf_wrreq ;
wire txf_empty ;
wire txf_full ;
wire [8:0] txf_usedw ;
//标志信号
//flag 1: uart 发送数据 0:停止发送数据
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag <= 0;
end
else if(txf_usedw >= 1)begin
flag <= 1;
end
else if(txf_empty)begin
flag <= 0;
end
end
//flag0 0:fish读模块输出数据(命令、地址)1:fish写模块发送数据(命令、地址、写入数据)
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag0 <= 0;
end
else if(en_[0])begin
flag0 <= 1;
end
else if(en_[1])begin
flag0 <= 0;
end
end
//en_r 打拍 检测en信号边沿
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
en_r <= 0;
end
else begin
en_r <= en;
end
end
assign en_dge_n = !en & en_r;
//data_vld
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_vld <= 0;
end
else begin
data_vld <= (data_m_vld[0] | data_m_vld[1]);
end
end
//输出
//dout_m 数据输出给spi_master模块
always @(*)begin
if(flag0)begin
dout_m = dout1;
end
else if(flag0 == 0)begin
dout_m = dout2;
end
else begin
dout_m = dout_m;
end
end
//en
assign en = en_[0] | en_[1];
//spi_vld
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
spi_vld <= 0;
end
else if(en_dge_n)begin
spi_vld <= 1;
end
else if(write_done | read_done)begin
spi_vld <= 0;
end
else begin
spi_vld <= spi_vld;
end
end
flash_write u_flash_write
(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input [2:0] */.key_in (key_in ),
/*input */.done_m (wr_done ),
/*input [7:0] */.din (rxf_dout ),
/*output */.data_req (data_req ), //写入flash数据请求
/*output */.en (en_[0] ),
/*output */.done (write_done ),
/*output */.data_m_vld (data_m_vld[0] ),
/*output */.busy (busy_write ),
/*output reg [7:0] */.dout (dout1 )
);
flash_read u_flash_read
(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input [2:0] */.key_in (key_in ),
/*input */.busy (busy_write ),
/*input */.done_m (wr_done ),
/*output */.en (en_[1] ),
/*output */.done (read_done ),
/*output */.data_m_vld (data_m_vld[1] ),
/*output reg [7:0] */.dout (dout2 )
);
//fifo例化
rx_fifo rx_fifo_inst (
.aclr ( !rst_n ),
.clock ( clk ),
.data ( din ), //uart输入数据
.rdreq ( rxf_rdreq ),
.wrreq ( rxf_wrreq ),
.empty ( rxf_empty ),
.full ( rxf_full ),
.q ( rxf_dout ),
.usedw ( rxf_usedw )
);
assign rxf_wrreq = !rxf_full && din_vld;
assign rxf_rdreq = !rxf_empty && data_req;
fifo_tx fifo_tx_inst (
.aclr ( !rst_n ),
.clock ( clk ),
.data ( din_m ),
.rdreq ( txf_rdreq ),
.wrreq ( txf_wrreq ),
.empty ( txf_empty ),
.full ( txf_full ),
.q ( dout ), //uart输出数据
.usedw ( txf_usedw )
);
assign txf_wrreq = !txf_full && data_vld ;
assign txf_rdreq = flag && (!txf_empty) && (!busy_t);
assign dout_vld = txf_rdreq; //uart数据输出有效
endmodule
`include "param.v"
module flash_read(
input clk ,
input rst_n ,
input [2:0] key_in ,
input busy ,
input done_m ,
output en ,
output done ,
output data_m_vld ,
output reg [7:0] dout
);
//参数定义
parameter IDLE =7'b000_0001,
RDID_CMD =7'b000_0010,
RDID_DATA =7'b000_0100,
RDAD_CMD =7'b000_1000,
RDAD_ADDR =7'b001_0000,
RDAD_DATA =7'b010_0000,
RD_DONE =7'b100_0000;
//中间信号定义
reg [6:0] state_c ;
reg [6:0] state_n ;
reg [2:0] cnt_byte ;
wire add_cnt_byte ;
wire end_cnt_byte ;
reg [2:0] byte_num ;
reg [23:0] addr_rd ;
wire idle2rdid_cmd ;
wire idle2rdad_cmd ;
wire rdid_cmd2rdid_data ;
wire rdad_cmd2rdad_addr ;
wire rdad_addr2rdad_data ;
wire rdid_data2rd_done ;
wire rdad_data2rd_done ;
wire rd_done2idle ;
//状态机
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
always @(*)begin
case (state_c)
IDLE : begin if (idle2rdid_cmd) begin
state_n <= RDID_CMD;
end
else if(idle2rdad_cmd) begin
state_n <= RDAD_CMD;
end
else begin
state_n <= state_c;
end
end
RDID_CMD : begin if(rdid_cmd2rdid_data)begin
state_n <= RDID_DATA;
end
else begin
state_n <= state_c;
end
end
RDID_DATA : begin if (rdid_data2rd_done) begin
state_n <= RD_DONE;
end
else begin
state_n <= state_c;
end
end
RDAD_CMD : begin if (rdad_cmd2rdad_addr) begin
state_n <= RDAD_ADDR;
end
else begin
state_n <= state_c;
end
end
RDAD_ADDR : begin if (rdad_addr2rdad_data ) begin
state_n <= RDAD_DATA;
end
else begin
state_n <= state_c;
end
end
RDAD_DATA : begin if(rdad_data2rd_done)begin
state_n <= RD_DONE;
end
else begin
state_n <= state_c;
end
end
RD_DONE : begin if(rd_done2idle)begin
state_n <= IDLE;
end
else begin
state_n <= state_c;
end
end
default: state_n <= state_c;
endcase
end
assign idle2rdid_cmd = state_c == IDLE && key_in[0] && busy == 0;
assign idle2rdad_cmd = state_c == IDLE && key_in[1] && busy == 0;
assign rdid_cmd2rdid_data = state_c == RDID_CMD && end_cnt_byte;
assign rdad_cmd2rdad_addr = state_c == RDAD_CMD && end_cnt_byte;
assign rdad_addr2rdad_data = state_c == RDAD_ADDR && end_cnt_byte;
assign rdid_data2rd_done = state_c == RDID_DATA && end_cnt_byte;
assign rdad_data2rd_done = state_c == RDAD_DATA && end_cnt_byte;
assign rd_done2idle = state_c == RD_DONE && 1'b1;
//计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_byte <= 0;
end
else if(add_cnt_byte)begin
if(end_cnt_byte)begin
cnt_byte <= 0;
end
else begin
cnt_byte <= cnt_byte + 1;
end
end
else begin
cnt_byte <= cnt_byte;
end
end
assign add_cnt_byte = (state_c == RDID_CMD | state_c == RDID_DATA | state_c == RDAD_CMD | state_c == RDAD_ADDR |state_c == RDAD_DATA) && done_m;
assign end_cnt_byte = add_cnt_byte && cnt_byte == byte_num-1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
byte_num <= 0;
end
else if(state_c == RDID_CMD | state_c == RDAD_CMD )begin
byte_num <= `CMD_BYTE;
end
else if(state_c == RDID_DATA)begin
byte_num <= `ID_BYTE;
end
else if (state_c == RDAD_ADDR) begin
byte_num <= `ADDR_BYTE;
end
else if(state_c == RDAD_DATA)begin
byte_num <= `DATA_BYTE;
end
end
//addr_rd
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
addr_rd <= 24'h100;
end
// else if(rdad_data2rd_done)begin
// addr_rd <= addr_rd + 1;
// end
else begin
addr_rd <= addr_rd;
end
end
//输出
//dout
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout <= 0;
end
else if(state_c == RDID_CMD)begin
dout <= `CMD_RDID;
end
else if(state_c == RDAD_CMD)begin
dout <= `CMD_READ;
end
else if(state_c == RDAD_ADDR)begin
if(cnt_byte == 0)
begin
dout <= addr_rd[23:16];
end
else if(cnt_byte == 1)
begin
dout <= addr_rd[15:8];
end
else if(cnt_byte == 2)
begin
dout <= addr_rd[7:0];
end
end
else begin
dout <= dout;
end
end
//en done dtat_m_vld
assign en =(key_in[0] | key_in[1]) & state_c == IDLE;
assign done = rdid_data2rd_done | rdad_data2rd_done;
assign data_m_vld = done_m && (state_c == RDID_DATA | state_c == RDAD_DATA);
endmodule
`include "param.v"
module flash_write(
input clk ,
input rst_n ,
input [2:0] key_in ,
input done_m ,
input [7:0] din ,
output data_req , //写入flash数据请求
output en ,
output done ,
output busy ,
output data_m_vld ,
output reg [7:0] dout
);
//参数定义
parameter M_IDLE =5'b0_0001,
M_WREN =5'b0_0010,
M_SE =5'b0_0100,
M_RDSR =5'b0_1000,
M_PP =5'b1_0000;
parameter S_IDLE = 5'b00001,
S_CMD = 5'b00010,
S_ADDR = 5'b00100,
S_DATA = 5'b01000,
S_DELAY = 5'b10000;
//中间信号定义
reg [4:0] m_state_c ;
reg [4:0] m_state_n ;
reg [4:0] s_state_c ;
reg [4:0] s_state_n ;
reg [2:0] cnt_100ns ;
wire add_cnt_100ns ;
wire end_cnt_100ns ;
reg [18:0] cnt_pp ;
wire add_cnt_pp ;
wire end_cnt_pp ;
reg end_cnt_pp_r ;
reg [10:0] cnt_se ;
wire add_cnt_se ;
wire end_cnt_se ;
reg end_cnt_se_r ;
reg [2:0] cnt_byte ;
wire add_cnt_byte ;
wire end_cnt_byte ;
reg [2:0] byte_num ;
reg [23:0] addr_wr ;
reg [1:0] flag ;
reg req_r ;
//主
wire m_idle2m_wren ;
wire m_wren2m_rdsr ;
wire m_rdsr2m_wren ;
wire m_wren2m_se ;
wire m_wren2m_pp ;
wire m_se2m_wren ;
wire m_pp2m_idle ;
//从
wire s_idle2s_cmd ;
wire s_cmd2s_addr ;
wire s_cmd2s_data ;
wire s_cmd2s_delay ;
wire s_addr2s_data ;
wire s_addr2s_delay ;
wire s_data2s_idle ;
wire s_data2s_delay ;
wire s_delay2s_idle ;
//状态机
//主状态机
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
m_state_c <= M_IDLE;
end
else begin
m_state_c <= m_state_n;
end
end
always @(*)begin
case (m_state_c)
M_IDLE : begin if(m_idle2m_wren)begin
m_state_n = M_WREN;
end
else begin
m_state_n = m_state_c;
end
end
M_WREN : begin if (m_wren2m_rdsr) begin
m_state_n = M_RDSR;
end
else if (m_wren2m_se) begin
m_state_n = M_SE;
end
else if (m_wren2m_pp) begin
m_state_n = M_PP;
end
else begin
m_state_n = m_state_c;
end
end
M_SE : begin if (m_se2m_wren) begin
m_state_n = M_WREN;
end
else begin
m_state_n = m_state_c;
end
end
M_RDSR : begin if (m_rdsr2m_wren) begin
m_state_n = M_WREN;
end
else begin
m_state_n = m_state_c;
end
end
M_PP : begin if (m_pp2m_idle) begin
m_state_n = M_IDLE;
end
else begin
m_state_n = m_state_c;
end
end
default: m_state_n = m_state_c;
endcase
end
//从状态机
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
s_state_c <= S_IDLE;
end
else begin
s_state_c <= s_state_n;
end
end
always @(*)begin
case (s_state_c)
S_IDLE : begin if (s_idle2s_cmd) begin
s_state_n = S_CMD;
end
else begin
s_state_n = s_state_c;
end
end
S_CMD : begin if (s_cmd2s_addr) begin
s_state_n = S_ADDR;
end
else if (s_cmd2s_data) begin
s_state_n = S_DATA;
end
else if (s_cmd2s_delay) begin
s_state_n = S_DELAY;
end
else begin
s_state_n = s_state_c;
end
end
S_ADDR : begin if (s_addr2s_data) begin
s_state_n = S_DATA;
end
else if(s_addr2s_delay) begin
s_state_n = S_DELAY;
end
else begin
s_state_n = s_state_c;
end
end
S_DATA : begin if (s_data2s_idle) begin
s_state_n = S_IDLE;
end
else if(s_data2s_delay)begin
s_state_n = S_DELAY;
end
else begin
s_state_n = s_state_c;
end
end
S_DELAY : begin if(s_delay2s_idle)begin
s_state_n = S_IDLE;
end
else begin
s_state_n = s_state_c;
end
end
default: s_state_n = s_state_c;
endcase
end
//主状态机转换条件
assign m_idle2m_wren = m_state_c == M_IDLE && key_in[2];
assign m_wren2m_rdsr = m_state_c == M_WREN && s_delay2s_idle && flag == 2'b00;
assign m_rdsr2m_wren = m_state_c == M_RDSR && s_data2s_idle;
assign m_wren2m_se = m_state_c == M_WREN && s_delay2s_idle && flag == 2'b01;
assign m_wren2m_pp = m_state_c == M_WREN && s_delay2s_idle && flag == 2'b10;
assign m_se2m_wren = m_state_c == M_SE && s_delay2s_idle;
assign m_pp2m_idle = m_state_c == M_PP && s_delay2s_idle;
//从状态机转换条件
assign s_idle2s_cmd = s_state_c == S_IDLE && (!(m_state_c == M_IDLE) );
assign s_cmd2s_addr = s_state_c == S_CMD && end_cnt_byte && (m_state_c == M_SE | m_state_c == M_PP);
assign s_cmd2s_data = s_state_c == S_CMD && end_cnt_byte && m_state_c == M_RDSR;
assign s_addr2s_data = s_state_c == S_ADDR && end_cnt_byte && m_state_c == M_PP;
assign s_addr2s_delay = s_state_c == S_ADDR && end_cnt_byte && m_state_c == M_SE ;
assign s_data2s_idle = s_state_c == S_DATA && end_cnt_byte && m_state_c == M_RDSR;
assign s_data2s_delay = s_state_c == S_DATA && end_cnt_byte && (m_state_c == M_PP);
assign s_cmd2s_delay = s_state_c == S_CMD && end_cnt_byte && m_state_c == M_WREN;
assign s_delay2s_idle = s_state_c == S_DELAY && ((( (m_state_c == M_SE)?end_cnt_se_r : end_cnt_pp_r)
&& (m_state_c == M_SE || m_state_c == M_PP)) || (end_cnt_100ns && m_state_c == M_WREN));
//计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_100ns <= 0;
end
else if(add_cnt_100ns)begin
if(end_cnt_100ns)begin
cnt_100ns <= 0;
end
else begin
cnt_100ns <= cnt_100ns + 1;
end
end
else begin
cnt_100ns <= cnt_100ns;
end
end
assign add_cnt_100ns = s_state_c == S_DELAY && m_state_c == M_WREN;
assign end_cnt_100ns = add_cnt_100ns && cnt_100ns == 5;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_byte <= 0;
end
else if(add_cnt_byte)begin
if(end_cnt_byte)begin
cnt_byte <= 0;
end
else begin
cnt_byte <= cnt_byte + 1;
end
end
else begin
cnt_byte <= cnt_byte;
end
end
assign add_cnt_byte = done_m && (s_state_c == S_CMD | s_state_c == S_ADDR | s_state_c == S_DATA );
assign end_cnt_byte = add_cnt_byte && cnt_byte == byte_num-1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_pp <= 0;
end
else if(add_cnt_pp)begin
if(end_cnt_pp | s_delay2s_idle)begin
cnt_pp <= 0;
end
else begin
cnt_pp <= cnt_pp + 1;
end
end
else begin
cnt_pp <= cnt_pp;
end
end
assign add_cnt_pp = s_state_c == S_DELAY && (m_state_c == M_PP || m_state_c == M_SE);
assign end_cnt_pp = add_cnt_pp && cnt_pp == `DELAY_5MS-1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_se <= 0;
end
else if(add_cnt_se)begin
if(end_cnt_se)begin
cnt_se <= 0;
end
else begin
cnt_se <= cnt_se + 1;
end
end
else begin
cnt_se <= cnt_se;
end
end
assign add_cnt_se= s_state_c == S_DELAY && end_cnt_pp && m_state_c == M_SE;
assign end_cnt_se = add_cnt_se && cnt_se == 600-1;
//flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag <= 2'b00;
end
else if(m_state_c == M_RDSR)begin
flag <=2'b01 ;
end
else if(m_state_c == M_SE)begin
flag <= 2'b10;
end
else if(m_state_c == M_PP)begin
flag <= 2'b00;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
end_cnt_pp_r <= 0;
end_cnt_se_r <= 0;
end
else begin
end_cnt_pp_r <= end_cnt_pp;
end_cnt_se_r <= end_cnt_se;
end
end
//byte_num
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
byte_num <= 0;
end
else if(s_state_c == S_CMD)begin
byte_num <= `CMD_BYTE;
end
else if(s_state_c == S_ADDR)begin
byte_num <= `ADDR_BYTE;
end
else if(s_state_c == S_DATA)begin
byte_num <= `DATA_BYTE;
end
else begin
byte_num <= byte_num;
end
end
//addr_wr
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
addr_wr <= 24'h100;
end
// else if(m_pp2m_idle)begin
// addr_wr <= addr_wr +1;
// end
else begin
addr_wr <= addr_wr;
end
end
// always @(posedge clk or negedge rst_n)begin
// if(!rst_n)begin
// req_r <= 0;
// end
// else begin
// req_r <= done_m;
// end
// end
//输出
//dout
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout <= 0;
end
else if(s_state_c == S_CMD)begin
if(m_state_c == M_WREN)begin
dout <= `CMD_WREN;
end
else if(m_state_c == M_SE ) begin
dout <= `CMD_SE;
end
else if(m_state_c == M_RDSR)begin
dout <= `CMD_RDSR;
end
else if (m_state_c == M_PP ) begin
dout <= `CMD_PP;
end
else begin
dout <= dout;
end
end
else if(s_state_c == S_ADDR)begin
dout <=addr_wr[23-cnt_byte*8 -:8];
end
else if (s_state_c == S_DATA && m_state_c == M_PP) begin
dout <=din;
end
end
//data_req en done data_m_vld busy
assign data_req = m_state_c == M_PP && done_m && s_state_c == S_DATA;
assign en =(key_in[2] & m_state_c == M_IDLE) | (s_delay2s_idle && m_state_c == M_SE) | (s_delay2s_idle && m_state_c == M_WREN);
assign done = (s_data2s_delay && m_state_c == M_PP) | s_addr2s_delay | s_cmd2s_delay;
assign data_m_vld = done_m && s_state_c == S_DATA && m_state_c == M_RDSR;
assign busy= !(m_state_c==M_IDLE);
endmodule
https://blog.csdn.net/li_lys/article/details/121849916
https://blog.csdn.net/li_lys/article/details/122887251?spm=1001.2014.3001.5502
读ID
单字节读数据
写数据,读数据
工程链接:
https://blog.csdn.net/li_lys/article/details/124870664?utm_source=app&app_version=5.4.0&code=app_1562916241&uLinkId=usr1mkqgl919blen