如何做网站吸引广告商,wordpress绑定百家号,网页设计需要学编程吗,wordpress 动态主题下载文章目录 实现思路具体实现子模块实现top模块 测试Something 实现思路 建议读者先对 S25FL-S 系列 FLASH 进行了解#xff0c;我之前的博文中有详细介绍。 笔者的芯片具体型号为 S25FL256SAGNFI00#xff0c;存储容量 256Mb#xff0c;增强高性能 EHPLC#xff0c;4KB 与 6… 文章目录 实现思路具体实现子模块实现top模块 测试Something 实现思路 建议读者先对 S25FL-S 系列 FLASH 进行了解我之前的博文中有详细介绍。 笔者的芯片具体型号为 S25FL256SAGNFI00存储容量 256Mb增强高性能 EHPLC4KB 与 64KB 混合 Sector 的存储阵列256 Byte 的 Page Programming Buffer 大小最高支持 133MHz无硬复位 RESET# 引脚。 为简单起见采用 SDR 时钟模式为了兼顾读写速度采用 Quad mode同时考虑到 Quad Page Programming 地址只能通过 SI 单线传输因此读、写 FLASH 分别采用 Quad Output Read、Quad Page Programming以实现时序格式的统一简化编程。 由于 S25FL-S 在 SCK 上升沿锁存数据在 SCK 下降沿转换数据因此主控端应在 SCK 下降沿转换数据在 SCK 上升沿锁存数据。 由于写 FLASH 需要先进行写使能以及擦除操作而擦除操作需要检查 WIP bitSR1[0]要使用 Quad 读写模式需要置位 Quad bitCR1[1]要判断地址映射类型和四元读模式下的 Dummy 长度需要实现读写寄存器。因此需要实现以下功能写使能 WREN、写失能 WRDI、写寄存器 WRR、清除状态寄存器 CLSR、读状态寄存器 RDSR1/RDSR2、读配置寄存器 RDCR、擦除操作扇区擦除 4SE、批量擦除 BE、四元编程操作 4QPP、Quad Output Read 操作 4QOR 等。 为每一种功能单独写一个模块当然也是可行的思路但过于繁杂观察到在时序层面上述指令可以归类为简单的 5 种单 8bit 指令如 WREN、WRDI、CLSR、BE 等、写寄存器8bit 指令后跟随 1~4Byte 数据SI 单线传输如 WRR、ABWR、BRWR 等甚至 8bit 指令 4Byte 地址的 4SE 也可归于此类、读寄存器8bit 指令SI后跟随 1~4Byte 输出SO如 RDSR1、RDSR2、RDCR1、ABRD、BRRD 等、四元写 FLASH 8bit 指令SI 32bit 地址SI 1~256Byte 数据IO0~IO3写如 4QPP、四元读 FLASH 8bit 指令SI 32bit 地址SI xbit Dummy xByte 数据IO0~IO3读回如 4QOR。 因此可以首先实现以上几个基础模块然后根据需要在上层模块中用状态机控制几个基础模块的运行。
具体实现 由于本示例实现中每个子模块都涉及 FLASH_IO 这组 inout 线的操作因此有注意事项如下 每个 FPGA 管脚上都要有 IBUF、OBUF 或 IOBUFinput/output 管脚上 IBUF/OBUF 会自动生成而 inout 管脚需要用户编写要么用 IOBUF要么直接用 link? xx_OBUF : 1’bz 这种形式其实后者也是生成了一个 OBUF 和一个 IBUF。 对于每个 FPGA 管脚只能由一个 OBUF 驱动因此如果多个子模块要用 inout 操作同一根线会出问题这种情况下 vivado 会自动生成 IBUF导致模块大部分逻辑无效化进而在综合后整个模块被优化掉即使强制关闭 IBUF/OBUF 自动插入功能也会因为多个 OBUF 驱动同一管脚而综合失败。 因此子模块不能再保有 inout而是通过操作顶层模块的 IOBUF 实现数据读写具体实现方式为子模块关于 FLASH_IO 的接口设计为两个单向接口FLASH_IO_IBUF、FLASH_IO_OBUF并给出何时使能 O_BUF 的 link 信号顶层模块根据状态仲裁接通哪路子模块并根据对应的 link 决定驱动方向。
子模块实现
单条指令
/* * file : flash_instruction.v* author : 今朝无言* Lab : WHU-EIS-LMSWE* date : 2023-11-15* version : v2.0* description : 单条 8bit 指令从而支持诸如 WREN、WRDI、Bulk Erase 等指令* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
default_nettype none
module flash_instruction(
input wire clk,
input wire rst_n,output wire FLASH_SCK,
output reg FLASH_nCS,output reg [3:0] link,
output reg [3:0] FLASH_IO_OBUF,
input wire [3:0] FLASH_IO_IBUF,//usr interface
input wire send_en, //上升沿有效
input wire [7:0] instruction,
output reg busy
);reg FLASH_nCS 1b1;
assign FLASH_SCK FLASH_nCS? 1b1 : clk; //SPI mode 3reg [3:0] link 4h0;
reg [3:0] FLASH_IO_OBUF 4hf;//--------------------------------------------------
wire send_en_pe;
reg send_en_d0;
reg send_en_d1;always (posedge clk) beginsend_en_d0 send_en;send_en_d1 send_en_d0;
endassign send_en_pe send_en_d0 (~send_en_d1);//--------------------FSM---------------------------
localparam S_IDLE 8h01;
localparam S_COMMAND 8h02;
localparam S_STOP 8h04;reg [7:0] state S_IDLE;
reg [7:0] next_state;reg [2:0] cnt 3d0;always (posedge clk or negedge rst_n) beginif(~rst_n) beginstate S_IDLE;endelse beginstate next_state;end
endalways (*) begincase(state)S_IDLE: beginif(send_en_pe) beginnext_state S_COMMAND;endelse beginnext_state S_IDLE;endendS_COMMAND: beginif(cnt 3d7) beginnext_state S_STOP;endelse beginnext_state S_COMMAND;endendS_STOP: beginnext_state S_IDLE;enddefault: beginnext_state S_IDLE;endendcase
end//FLASH_nCS
always (negedge clk) begincase(state)S_COMMAND: beginFLASH_nCS 1b0;enddefault: beginFLASH_nCS 1b1;endendcase
end//cnt
always (posedge clk) begincase(state)S_IDLE: begincnt 3d0;endS_COMMAND: beginif(~FLASH_nCS) begincnt cnt 1b1;endelse begincnt 3d0;endendS_STOP: begincnt 3d0;enddefault: begincnt cnt;endendcase
end//FLASH_IO_OBUF
always (negedge clk) begin //在SCK下降沿转换数据case(state)S_COMMAND: beginFLASH_IO_OBUF[0] instruction[3d7-cnt]; //首先移出MSBFLASH_IO_OBUF[3:1] 3b111;enddefault: beginFLASH_IO_OBUF 4hf;endendcase
end//link
always (negedge clk) begincase(state)S_COMMAND: beginlink 4b1101;//指令阶段SO应维持高阻WP#、HOLD#应拉高//而WP#、HOLD#内部有上拉电阻因此IO1~IO3可以直接释放掉//不过为保险起见这里还是强制拉高IO2/IO3而IO1可以释放掉enddefault: beginlink 4h0;endendcase
end//busy
always (*) begincase(state)S_IDLE: beginbusy 1b0;endS_COMMAND, S_STOP: beginbusy 1b1;enddefault: beginbusy 1b0;endendcase
endendmodule读寄存器
/* * file : flash_RDR.v* author : 今朝无言* Lab : WHU-EIS-LMSWE* date : 2023-11-15* version : v2.0* description : 读寄存器支持1~4Byte读取从而支持对SR1、SR2、CR1、ABR、BAR等寄存器的读取* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
default_nettype none
module flash_RDR(
input wire clk,
input wire rst_n,output wire FLASH_SCK,
output reg FLASH_nCS,output reg [3:0] link,
output reg [3:0] FLASH_IO_OBUF,
input wire [3:0] FLASH_IO_IBUF,//usr interface
input wire read_en, //上升沿有效
input wire [7:0] instruction,input wire [3:0] Register_Len, //寄存器长度1/2/4 Byte
output reg [31:0] Reg, //低位对齐。即1Byte的寄存器占用Reg[7:0]4Byte的寄存器占用Reg[31:0]output reg busy
);reg FLASH_nCS 1b1;
assign FLASH_SCK FLASH_nCS? 1b1 : clk; //SPI mode 3reg [3:0] link 4h0;
reg [3:0] FLASH_IO_OBUF 4hf;wire read_en_pe;
reg read_en_d0;
reg read_en_d1;always (posedge clk) beginread_en_d0 read_en;read_en_d1 read_en_d0;
endassign read_en_pe read_en_d0 (~read_en_d1);//--------------------FSM---------------------------
localparam S_IDLE 8h01;
localparam S_COMMAND 8h02;
localparam S_RDR 8h04;
localparam S_STOP 8h08;reg [7:0] state S_IDLE;
reg [7:0] next_state;reg [2:0] cnt 3d0; //Byte内bit计数
reg [3:0] cnt_Byte 4d0;always (posedge clk or negedge rst_n) beginif(~rst_n) beginstate S_IDLE;endelse beginstate next_state;end
endalways (*) begincase(state)S_IDLE: beginif(read_en_pe) beginnext_state S_COMMAND;endelse beginnext_state S_IDLE;endendS_COMMAND: beginif(cnt 3d7) beginnext_state S_RDR;endelse beginnext_state S_COMMAND;endendS_RDR: beginif(cnt 3d7 cnt_Byte Register_Len - 1b1) beginnext_state S_STOP;endelse beginnext_state S_RDR;endendS_STOP: beginnext_state S_IDLE;enddefault: beginnext_state S_IDLE;endendcase
end//FLASH_nCS
always (negedge clk) begincase(state)S_COMMAND, S_RDR: beginFLASH_nCS 1b0;enddefault: beginFLASH_nCS 1b1;endendcase
end//cnt
always (posedge clk) begincase(state)S_IDLE: begincnt 3d0;endS_COMMAND, S_RDR: begin //将cnt设计为3bit位宽可实现模8加if(~FLASH_nCS) begincnt cnt 1b1;endelse begincnt 3d0;endendS_STOP: begincnt 3d0;enddefault: begincnt cnt;endendcase
end//cnt_Byte
always (posedge clk) begincase(state)S_RDR: beginif(cnt3d7) begincnt_Byte cnt_Byte 1b1;endelse begincnt_Byte cnt_Byte;endenddefault: begincnt_Byte 4d0;endendcase
end//FLASH_IO_OBUF
always (negedge clk) begin //在SCK下降沿转换数据case(state)S_COMMAND: beginFLASH_IO_OBUF[0] instruction[3d7-cnt]; //首先移出MSBFLASH_IO_OBUF[3:1] 3b111;enddefault: beginFLASH_IO_OBUF 4hf;endendcase
end//link
always (negedge clk) begincase(state)S_COMMAND: beginlink 4b1101;endS_RDR: beginlink 4h0;enddefault: beginlink 4h0;endendcase
end//read reg
wire SO FLASH_IO_IBUF[1];
always (posedge clk or negedge rst_n) begin //须在SCK上升沿锁存数据if(~rst_n) beginReg 32d0;endelse begincase(state)S_RDR: beginReg {Reg[30:0], SO}; //移位寄存来自SO的值enddefault: beginReg Reg;endendcaseend
end//busy
always (*) begincase(state)S_IDLE: beginbusy 1b0;enddefault: beginbusy 1b1;endendcase
endendmodule写寄存器
/* * file : flash_WRR.v* author : 今朝无言* Lab : WHU-EIS-LMSWE* date : 2023-11-15* version : v2.0* description : 写寄存器支持 1Byte ~ 4Byte 的写入* 从而支持对 SR1、CR1、ABR、BAR 等寄存器的写入操作* 以及Sector Erase擦除命令* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
default_nettype none
module flash_WRR(
input wire clk,
input wire rst_n,output wire FLASH_SCK,
output reg FLASH_nCS,output reg [3:0] link,
output reg [3:0] FLASH_IO_OBUF,
input wire [3:0] FLASH_IO_IBUF,//usr interface
input wire send_en, //上升沿有效
input wire [7:0] instruction,input wire [3:0] Register_Len, //寄存器长度1/2/4 Byte
input wire [7:0] Byte1,
input wire [7:0] Byte2,
input wire [7:0] Byte3,
input wire [7:0] Byte4,output reg busy
);
//使用示例对于单写SR1寄存器令Reg_Len1并在Byte1给出要写入SR1的值
//对于写CR1需要用到2Byte的形式令Reg_Len2Byte1SR1Byte2CR1
//对于Autiboot ReisterLen4Byte1~4分别为ABR[31:24]、ABR[23:16]、ABR[15:8]、ABR[7:0]
//其余写寄存器指令依此类推
//甚至对于4SE擦除操作Byte1~4可直接用作Sector地址使用reg FLASH_nCS 1b1;
assign FLASH_SCK FLASH_nCS? 1b1 : clk; //SPI mode 3reg [3:0] link 4h0;
reg [3:0] FLASH_IO_OBUF 4hf;wire send_en_pe;
reg send_en_d0;
reg send_en_d1;always (posedge clk) beginsend_en_d0 send_en;send_en_d1 send_en_d0;
endassign send_en_pe send_en_d0 (~send_en_d1);//--------------------FSM---------------------------
localparam S_IDLE 8h01;
localparam S_COMMAND 8h02;
localparam S_WRR 8h04;
localparam S_STOP 8h08;reg [7:0] state S_IDLE;
reg [7:0] next_state;reg [2:0] cnt 3d0; //Byte内bit计数
reg [3:0] cnt_Byte 4d0;always (posedge clk or negedge rst_n) beginif(~rst_n) beginstate S_IDLE;endelse beginstate next_state;end
endalways (*) begincase(state)S_IDLE: beginif(send_en_pe) beginnext_state S_COMMAND;endelse beginnext_state S_IDLE;endendS_COMMAND: beginif(cnt 3d7) beginnext_state S_WRR;endelse beginnext_state S_COMMAND;endendS_WRR: beginif(cnt 3d7 cnt_Byte Register_Len - 1b1) beginnext_state S_STOP;endelse beginnext_state S_WRR;endendS_STOP: beginnext_state S_IDLE;enddefault: beginnext_state S_IDLE;endendcase
end//FLASH_nCS
always (negedge clk) begincase(state)S_COMMAND, S_WRR: beginFLASH_nCS 1b0;enddefault: beginFLASH_nCS 1b1;endendcase
end//cnt
always (posedge clk) begincase(state)S_IDLE: begincnt 3d0;endS_COMMAND, S_WRR: begin //将cnt设计为3bit位宽可实现模8加if(~FLASH_nCS) begincnt cnt 1b1;endelse begincnt 3d0;endendS_STOP: begincnt 3d0;enddefault: begincnt cnt;endendcase
end//cnt_Byte
always (posedge clk) begincase(state)S_WRR: beginif(cnt3d7) begincnt_Byte cnt_Byte 1b1;endelse begincnt_Byte cnt_Byte;endenddefault: begincnt_Byte 4d0;endendcase
end//FLASH_IO_OBUF
always (negedge clk) begin //在SCK下降沿转换数据case(state)S_COMMAND: beginFLASH_IO_OBUF[0] instruction[3d7-cnt]; //首先移出MSBFLASH_IO_OBUF[3:1] 3b111;endS_WRR: begincase(cnt_Byte)4d0: FLASH_IO_OBUF[0] Byte1[3d7-cnt];4d1: FLASH_IO_OBUF[0] Byte2[3d7-cnt];4d2: FLASH_IO_OBUF[0] Byte3[3d7-cnt];4d3: FLASH_IO_OBUF[0] Byte4[3d7-cnt];default: FLASH_IO_OBUF[0] 1b1;endcaseFLASH_IO_OBUF[3:1] 3b111;enddefault: beginFLASH_IO_OBUF 4hf;endendcase
end//link
always (negedge clk) begincase(state)S_COMMAND, S_WRR: beginlink 4b1101;enddefault: beginlink 4h0;endendcase
end//busy
always (*) begincase(state)S_IDLE: beginbusy 1b0;enddefault: beginbusy 1b1;endendcase
endendmodulePage Programming
/* * file : flash_4QPP.v* author : 今朝无言* Lab : WHU-EIS-LMSWE* date : 2023-11-16* version : v2.0* description : 实现 4QPP 指令32bit AddrQuad Page Programming* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
default_nettype none
module flash_4QPP(
input wire clk, //S25FL256SAGNFI00 在 4QPP 下最大支持 80M
input wire rst_n,output wire FLASH_SCK,
output reg FLASH_nCS,output reg [3:0] link,
output reg [3:0] FLASH_IO_OBUF,
input wire [3:0] FLASH_IO_IBUF,//usr interface
input wire program_start, //上升沿有效input wire [31:0] addr, //起始地址可以是任意字节地址但建议是 Page 起始地址S25FL256SAGNFI00 的 Page 大小为 256Byte
input wire [9:0] Byte_Len, //一次写多少字节数据Page Programming 只能在当前 Page 内进行写入超出的将被忽略建议一次写一整个 Pageoutput wire data_rd_clk, //读数据的驱动时钟若使用FIFO请用这个时钟是clk的二分频时钟
output reg data_rden, //读数据请求可用作 FIFO 的 rdenFIFO 应采用 First Word Fall Through
input wire [7:0] data, //字节数据output reg busy
);localparam instruction 8h34; //4QPP的指令码为 0x34reg FLASH_nCS 1b1;
assign FLASH_SCK FLASH_nCS? 1b1 : clk; //SPI mode 3reg [3:0] link 4h0;
reg [3:0] FLASH_IO_OBUF 4hf;wire program_start_pe;
reg program_start_d0;
reg program_start_d1;always (posedge clk) beginprogram_start_d0 program_start;program_start_d1 program_start_d0;
endassign program_start_pe program_start_d0 (~program_start_d1);clkdiv #(.N(2))
clkdiv_2(.clk_in (clk),.clk_out (data_rd_clk)
);//--------------------FSM---------------------------
localparam S_IDLE 8h01;
localparam S_COMMAND 8h02;
localparam S_ADDR 8h04;
localparam S_QUAD_WR 8h08;
localparam S_STOP 8h10;reg [7:0] state S_IDLE;
reg [7:0] next_state;reg [2:0] cnt 3d0; //Byte内bit计数
reg [9:0] cnt_Byte 10d0;always (posedge clk or negedge rst_n) beginif(~rst_n) beginstate S_IDLE;endelse beginstate next_state;end
endalways (*) begincase(state)S_IDLE: beginif(program_start_pe) beginnext_state S_COMMAND;endelse beginnext_state S_IDLE;endendS_COMMAND: beginif(cnt 3d7) beginnext_state S_ADDR;endelse beginnext_state S_COMMAND;endendS_ADDR: beginif(cnt 3d7 cnt_Byte 4d3) beginnext_state S_QUAD_WR;endelse beginnext_state S_ADDR;endendS_QUAD_WR: beginif(cnt 3d4 (Byte_Len 10d0 || cnt_Byte Byte_Len - 1b1)) begin //Len0时视作Len1next_state S_STOP;endelse beginnext_state S_QUAD_WR;endendS_STOP: beginnext_state S_IDLE;enddefault: beginnext_state S_IDLE;endendcase
end//FLASH_nCS
always (negedge clk) begincase(state)S_COMMAND, S_ADDR, S_QUAD_WR: beginFLASH_nCS 1b0;enddefault: beginFLASH_nCS 1b1;endendcase
end//cnt
always (posedge clk) begincase(state)S_IDLE: begincnt 3d0;endS_COMMAND, S_ADDR: beginif(~FLASH_nCS) begincnt cnt 1b1;endelse begincnt 3d0;endendS_QUAD_WR: beginif(~FLASH_nCS) begincnt cnt 3d4; //Quad WR 阶段一次传送4bitendelse begincnt 3d0;endendS_STOP: begincnt 3d0;enddefault: begincnt cnt;endendcase
end//cnt_Byte
always (posedge clk) begincase(state)S_ADDR: beginif(cnt3d7) beginif(cnt_Byte 16d3) begincnt_Byte 10d0;endelse begincnt_Byte cnt_Byte 1b1;endendelse begincnt_Byte cnt_Byte;endendS_QUAD_WR: beginif(cnt3d4) begincnt_Byte cnt_Byte 1b1;endelse begincnt_Byte cnt_Byte;endenddefault: begincnt_Byte 10d0;endendcase
end//link
always (negedge clk) begincase(state)S_COMMAND, S_ADDR: beginlink 4b1101;endS_QUAD_WR: beginlink 4b1111;enddefault: beginlink 4h0;endendcase
end//FLASH_IO_OBUF
always (negedge clk) begin //在SCK下降沿转换数据case(state)S_COMMAND: beginFLASH_IO_OBUF[0] instruction[3d7-cnt]; //首先移出MSBFLASH_IO_OBUF[3:1] 3b111;endS_ADDR: begincase(cnt_Byte[3:0])4d0: FLASH_IO_OBUF[0] addr[5d31-cnt];4d1: FLASH_IO_OBUF[0] addr[5d23-cnt];4d2: FLASH_IO_OBUF[0] addr[5d15-cnt];4d3: FLASH_IO_OBUF[0] addr[5d7-cnt];default: FLASH_IO_OBUF[0] 1b1;endcaseFLASH_IO_OBUF[3:1] 3b111;endS_QUAD_WR: begincase(cnt)4d0: FLASH_IO_OBUF[3:0] data[7:4];4d4: FLASH_IO_OBUF[3:0] data[3:0];default: FLASH_IO_OBUF[3:0] 4hf;endcaseenddefault: beginFLASH_IO_OBUF 4hf;endendcase
end//data_rden
always (posedge clk) begincase(state)S_QUAD_WR: begindata_rden 1b1;enddefault: begindata_rden 1b0;endendcase
end//busy
always (*) begincase(state)S_IDLE: beginbusy 1b0;enddefault: beginbusy 1b1;endendcase
endendmodule读 FLASH 主存储器
/* * file : flash_4QOR.v* author : 今朝无言* Lab : WHU-EIS-LMSWE* date : 2023-11-17* version : v2.0* description : 4QOR读flash32bit AddrQuad Output Read* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
default_nettype none
module flash_4QOR(
input wire clk,
input wire rst_n,output wire FLASH_SCK,
output reg FLASH_nCS,output reg [3:0] link,
output reg [3:0] FLASH_IO_OBUF,
input wire [3:0] FLASH_IO_IBUF,//usr interface
input wire read_start, //上升沿有效input wire [31:0] addr, //起始地址可以是任意字节地址
input wire [31:0] Byte_Len, //一次读多少字节数据读取过程中flash会自动地址1达到最大地址后将从0x00地址继续读取output wire data_wr_clk, //写数据的驱动时钟若使用FIFO请用这个时钟是clk的二分频时钟
output reg data_wren, //wren可用作 FIFO 的 wren
output reg [7:0] data, //读到的字节数据output reg busy,//LC
input wire [1:0] LC //LC bitCR1[7:6]
);
//LC确定Dummy的长度对于HPLC和PLC在Quad Output Read下表现一致
//都没有mode字段(mode len0)除LC11对应dummy len0外最大支持50MHz其余都是dummy len8localparam instruction 8h6C; //4QOR的指令码为 0x6Creg FLASH_nCS 1b1;
assign FLASH_SCK FLASH_nCS? 1b1 : clk; //SPI mode 3reg [3:0] link 4h0;
reg [3:0] FLASH_IO_OBUF 4hf;wire read_start_pe;
reg read_start_d0;
reg read_start_d1;always (posedge clk) beginread_start_d0 read_start;read_start_d1 read_start_d0;
endassign read_start_pe read_start_d0 (~read_start_d1);clkdiv #(.N(2))
clkdiv_2(.clk_in (clk),.clk_out (data_wr_clk)
);//--------------------FSM---------------------------
localparam S_IDLE 8h01;
localparam S_COMMAND 8h02;
localparam S_ADDR 8h04;
localparam S_DUMMY 8h08;
localparam S_QUAD_RD 8h10;
localparam S_STOP 8h20;reg [7:0] state S_IDLE;
reg [7:0] next_state;reg [2:0] cnt 3d0; //Byte内bit计数
reg [31:0] cnt_Byte 32d0;always (posedge clk or negedge rst_n) beginif(~rst_n) beginstate S_IDLE;endelse beginstate next_state;end
endalways (*) begincase(state)S_IDLE: beginif(read_start_pe) beginnext_state S_COMMAND;endelse beginnext_state S_IDLE;endendS_COMMAND: beginif(cnt 3d7) beginnext_state S_ADDR;endelse beginnext_state S_COMMAND;endendS_ADDR: beginif(cnt 3d7 cnt_Byte 4d3) begincase(LC) //根据LC判断Dummy的长度2b11: beginnext_state S_QUAD_RD;end2b00, 2b01, 2b10: beginnext_state S_DUMMY;enddefault: ;endcaseendelse beginnext_state S_ADDR;endendS_DUMMY: beginif(cnt 3d7) beginnext_state S_QUAD_RD;endelse beginnext_state S_DUMMY;endendS_QUAD_RD: beginif(cnt 3d4 (Byte_Len 32d0 || cnt_Byte Byte_Len - 1b1)) begin //Len0时视作Len1next_state S_STOP;endelse beginnext_state S_QUAD_RD;endendS_STOP: beginif(cnt1) begin //维持在STOP两个clk以保持data和wren保持一个wr_clknext_state S_IDLE;endelse beginnext_state S_STOP;endenddefault: beginnext_state S_IDLE;endendcase
end//FLASH_nCS
always (negedge clk) begincase(state)S_COMMAND, S_ADDR, S_DUMMY, S_QUAD_RD: beginFLASH_nCS 1b0;enddefault: beginFLASH_nCS 1b1;endendcase
end//cnt
always (posedge clk) begincase(state)S_IDLE: begincnt 3d0;endS_COMMAND, S_ADDR: beginif(~FLASH_nCS) begincnt cnt 1b1;endelse begincnt 3d0;endendS_DUMMY: beginif(cnt 3d7) begin //这里设置Bummy长度由于4QOR只有0/8的Dummy长度因该case实际可以和上面合并cnt 3d0;endelse begincnt cnt 1b1;endendS_QUAD_RD: begincnt cnt 3d4; //Quad RD 阶段一次读回4bitendS_STOP: begincnt 3d1;enddefault: begincnt cnt;endendcase
end//cnt_Byte
always (posedge clk) begincase(state)S_ADDR: beginif(cnt3d7) beginif(cnt_Byte 32d3) begincnt_Byte 32d0;endelse begincnt_Byte cnt_Byte 1b1;endendelse begincnt_Byte cnt_Byte;endendS_DUMMY: begincnt_Byte 32d0;endS_QUAD_RD: beginif(cnt3d4) begincnt_Byte cnt_Byte 1b1;endelse begincnt_Byte cnt_Byte;endenddefault: begincnt_Byte 32d0;endendcase
end//link
always (negedge clk) begincase(state)S_COMMAND, S_ADDR: beginlink 4b1101;endS_DUMMY, S_QUAD_RD: begin //为防止主控端与flash端的驱动器冲突Dummy期间主控端应释放总线link 4b0000;enddefault: beginlink 4h0;endendcase
end//FLASH_IO_OBUF
always (negedge clk) begin //在SCK下降沿转换数据case(state)S_COMMAND: beginFLASH_IO_OBUF[0] instruction[3d7-cnt]; //首先移出MSBFLASH_IO_OBUF[3:1] 3b111;endS_ADDR: begincase(cnt_Byte[3:0])4d0: FLASH_IO_OBUF[0] addr[5d31-cnt];4d1: FLASH_IO_OBUF[0] addr[5d23-cnt];4d2: FLASH_IO_OBUF[0] addr[5d15-cnt];4d3: FLASH_IO_OBUF[0] addr[5d7-cnt];default: FLASH_IO_OBUF[0] 1b1;endcaseFLASH_IO_OBUF[3:1] 3b111;enddefault: beginFLASH_IO_OBUF 4hf;endendcase
end//data_tmp
reg [7:0] data_tmp;
always (posedge clk) begin //须在SCK上升沿锁存数据case(state)S_QUAD_RD: begincase(cnt)3d0: begindata_tmp[7:4] FLASH_IO_IBUF;end3d4: begindata_tmp[3:0] FLASH_IO_IBUF;enddefault: begindata_tmp data_tmp;endendcaseenddefault: begindata_tmp data_tmp;endendcase
end//data_wren data
reg data_wren_buf;
reg [7:0] data_buf;
always (posedge clk) begincase(state)S_QUAD_RD: beginif(cnt0 cnt_Byte1) begindata_wren_buf 1b1;data_buf data_tmp;endelse begindata_wren_buf data_wren_buf;data_buf data_buf;endendS_STOP: begin //S_STOP时锁存输出最后一个数据if(cnt0) begindata_wren_buf 1b1;data_buf data_tmp;endelse begindata_wren_buf data_wren_buf;data_buf data_buf;endenddefault: begindata_wren_buf 1b0;data_buf 8d0;endendcase
endalways (posedge data_wr_clk) begin //同步到data_wr_clk时钟域data_wren data_wren_buf;data data_buf;
end//busy
always (*) begincase(state)S_IDLE: beginbusy 1b0;enddefault: beginbusy 1b1;endendcase
endendmoduletop模块
FLASH_top.v
/* * file : FLASH_top.v* author : 今朝无言* Lab : WHU-EIS-LMSWE* date : 2023-11-18* version : v2.0* description : S25FL256SAGNFI00 的读写控制实现 SDR 时钟模式下的 Quad 读写模式* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
default_nettype none
module FLASH_top(
input wire clk,
input wire rst_n,output reg FLASH_SCK,
output reg FLASH_nCS,
inout wire [3:0] FLASH_IO,//----------------user interface---------------------
//wr FLASH
input wire WR_req, //Page Programminginput wire [31:0] WR_addr, //起始编程地址对于S25FL256S可用地址为0~1FFFFFF25bit
input wire [9:0] WR_Byte_Len, //编程字节数单次只能在一个Page里进行写入256Byte Programming Buffer Size
// 最好一次写一个完整的Page低8位地址为0Len256output wire data_rd_clk, //读wFIFO的时钟
output wire data_rden, //读wFIFO的使能信号
input wire [7:0] data_PP, //从wFIFO读到的数据将写入FLASH//rd FLASH
input wire RD_req,
input wire [1:0] LC, //LC bits, CR1[7:6]input wire [31:0] RD_addr, //起始读取地址
input wire [31:0] RD_Byte_Len, //读取字节数output wire data_wr_clk, //写rFIFO的clk
output wire data_wren, //写rFIFO的使能信号
output wire [7:0] data_4QOR, //从FLASH读到的数据//WREN/WRDI/CLSR/RESET
input wire WREN_req, //置位WEL bit
input wire WRDI_req, //复位WEL bit
input wire CLSR_req, //清空SR1只复位P_ERR、E_ERR这两个bit
input wire RESET_req, //软复位//erase
input wire bulk_erase_req, //批量擦除input wire sector_erase_req, //Sector擦除一次擦除一个标准Sector64KB
input wire [31:0] sector_erase_addr, //低16位直接置零即可//RD SR1/CR1/SR2/BAR/ABR
input wire rd_SR1_req, //Status Register 1
output reg [7:0] SR1_rd,input wire rd_CR1_req, //Configuration Register
output reg [7:0] CR1_rd,input wire rd_SR2_req, //Status Register 2
output reg [7:0] SR2_rd,input wire rd_BAR_req, //Bank Address Register
output reg [7:0] BAR_rd,input wire rd_ABR_req, //Autoboot Register
output reg [31:0] ABR_rd,//WR SR1/CR1/BAR/ABR
input wire wr_SR1_req, //发起WR_SR1只需要给入SR1
input wire wr_CR1_req, //发起WR_CR1请求时要同时给入SR1、CR1两个值
input wire [7:0] SR1_wr,
input wire [7:0] CR1_wr,input wire wr_BAR_req,
input wire [7:0] BAR_wr,input wire wr_ABR_req,
input wire [31:0] ABR_wr,output reg busy,//debug
output reg [3:0] link,
output reg [3:0] FLASH_IO_OBUF,
output wire [3:0] FLASH_IO_IBUF,
output reg [23:0] state
);
//注意为避免操作冲突所有req信号请最多同时启用一个本模块已经做了优先编码
//所有req高电平有效请发起req后检测busy若busyH则置低req避免重复读写
//所有req均应在busyL时才可发起//---------------------------------COMMAND----------------------------------------
localparam I_WREN 8h06; //置位WEL
localparam I_WRDI 8h04; //复位WEL
localparam I_CLSR 8h30; //复位P_ERR、E_ERR
localparam I_RESET 8hF0; //软复位localparam I_WRR 8h01; //写SR1、CR1
localparam I_RDSR1 8h05; //读SR1
localparam I_RDSR2 8h07; //读SR2
localparam I_RDCR1 8h35; //读CR1localparam I_RDABR 8h14; //读Autoboot Register
localparam I_WRABR 8h15; //写ABRlocalparam I_RDBAR 8h16; //读Bank Address Register
localparam I_WRBAR 8h17; //写BARlocalparam I_BE 8h60; //bulk erase
localparam I_SE 8hDC; //4SEErase 64KB Sector (4-byte address)localparam I_4QPP 8h34; //Quad Page Programming (4-byte address)
localparam I_4QOR 8h6C; //Quad Output Read (4-byte address)
//4QPP、4QOR的指令码在子模块里写好了这里只是罗列一下除此之外的指令码都在本模块内用到//----------------------------------SPI x4----------------------------------------
reg [3:0] link 4h0;
reg [3:0] FLASH_IO_OBUF 4hf;
wire [3:0] FLASH_IO_IBUF;genvar i;
generatefor(i0; i4; ii1) beginIOBUF IOBUF_FLASH_IO( //IOBUF由一个IBUF和一个OBUF组成.O (FLASH_IO_IBUF[i]), //O为IBUF的输出.IO (FLASH_IO[i]), //IO为OBUF的输出、IBUF的输入.I (FLASH_IO_OBUF[i]), //I为OBUF的输入.T (~link[i]) //T为OBUF的三态门使能低电平有效);end
endgenerateassign FLASH_IO_IBUF1 FLASH_IO_IBUF;
assign FLASH_IO_IBUF2 FLASH_IO_IBUF;
assign FLASH_IO_IBUF3 FLASH_IO_IBUF;
assign FLASH_IO_IBUF4 FLASH_IO_IBUF;
assign FLASH_IO_IBUF5 FLASH_IO_IBUF;//********重要**********
//注意每个FPGA管脚上都要有IBUF、OBUF或IOBUFinput/output管脚上IBUF/OBUF会自动生成
//而inout管脚需要用户编写要么用IOBUF要么直接用 link? xx_OBUF : 1bz 这种形式其实后者也是生成了一个OBUF和一个IBUF
//对于每个FPGA管脚只能由一个OBUF驱动因此如果多个子模块要用inout操作同一根线会出问题
//这种情况下vivado会自动生成IBUF导致模块大部分逻辑无效化进而在综合后整个模块被优化掉
// 即使强制关闭IBUF/OBUF自动插入功能也会因为多个OBUF驱动同一管脚而综合失败
//因此子模块不能再保有inout而是通过操作顶层模块的IOBUF实现数据读写
//**********************//--------------------------------几个子模块--------------------------------------
//---------------单条8bit指令发送模块---------------
wire FLASH_SCK_1;
wire FLASH_nCS_1;wire [3:0] link1;
wire [3:0] FLASH_IO_OBUF1;
wire [3:0] FLASH_IO_IBUF1;reg start_1;
reg [7:0] instruction_1;
wire busy_1;flash_instruction flash_instruction_inst(.clk (clk),.rst_n (rst_n),.FLASH_SCK (FLASH_SCK_1),.FLASH_nCS (FLASH_nCS_1),.link (link1),.FLASH_IO_OBUF (FLASH_IO_OBUF1),.FLASH_IO_IBUF (FLASH_IO_IBUF1),//usr interface.send_en (start_1),.instruction (instruction_1),.busy (busy_1)
);//-------------写寄存器指令支持1~4Byte-------------
wire FLASH_SCK_2;
wire FLASH_nCS_2;wire [3:0] link2;
wire [3:0] FLASH_IO_OBUF2;
wire [3:0] FLASH_IO_IBUF2;reg start_2;
reg [7:0] instruction_2;
wire busy_2;reg [3:0] Register_Len_WRR;
reg [7:0] WRR_Byte1, WRR_Byte2, WRR_Byte3, WRR_Byte4;flash_WRR flash_WRR_inst(.clk (clk),.rst_n (rst_n),.FLASH_SCK (FLASH_SCK_2),.FLASH_nCS (FLASH_nCS_2),.link (link2),.FLASH_IO_OBUF (FLASH_IO_OBUF2),.FLASH_IO_IBUF (FLASH_IO_IBUF2),//usr interface.send_en (start_2),.instruction (instruction_2),.Register_Len (Register_Len_WRR),.Byte1 (WRR_Byte1),.Byte2 (WRR_Byte2),.Byte3 (WRR_Byte3),.Byte4 (WRR_Byte4),.busy (busy_2)
);//------------------读寄存器------------------
wire FLASH_SCK_3;
wire FLASH_nCS_3;wire [3:0] link3;
wire [3:0] FLASH_IO_OBUF3;
wire [3:0] FLASH_IO_IBUF3;reg start_3;
reg [7:0] instruction_3;
wire busy_3;reg [3:0] Register_Len_RDR;
wire [31:0] RDR_Reg;flash_RDR flash_RDR_inst(.clk (clk),.rst_n (rst_n),.FLASH_SCK (FLASH_SCK_3),.FLASH_nCS (FLASH_nCS_3),.link (link3),.FLASH_IO_OBUF (FLASH_IO_OBUF3),.FLASH_IO_IBUF (FLASH_IO_IBUF3),//usr interface.read_en (start_3),.instruction (instruction_3),.Register_Len (Register_Len_RDR),.Reg (RDR_Reg),.busy (busy_3)
);//---------------Page Programming---------------
wire FLASH_SCK_4;
wire FLASH_nCS_4;wire [3:0] link4;
wire [3:0] FLASH_IO_OBUF4;
wire [3:0] FLASH_IO_IBUF4;reg start_4;
wire busy_4;reg [31:0] addr_PP;
reg [9:0] Byte_Len_PP;wire data_rd_clk;
wire data_rden;
wire [7:0] data_PP;flash_4QPP flash_4QPP_inst(.clk (clk),.rst_n (rst_n),.FLASH_SCK (FLASH_SCK_4),.FLASH_nCS (FLASH_nCS_4),.link (link4),.FLASH_IO_OBUF (FLASH_IO_OBUF4),.FLASH_IO_IBUF (FLASH_IO_IBUF4),//usr interface.program_start (start_4),.addr (addr_PP),.Byte_Len (Byte_Len_PP),.data_rd_clk (data_rd_clk), //读wFIFO将数据写入FLASH.data_rden (data_rden),.data (data_PP), //从wFIFO读到的数据.busy (busy_4)
);//-------------------read flash-------------------
wire FLASH_SCK_5;
wire FLASH_nCS_5;wire [3:0] link5;
wire [3:0] FLASH_IO_OBUF5;
wire [3:0] FLASH_IO_IBUF5;reg start_5;
wire busy_5;reg [31:0] addr_4QOR;
reg [31:0] Byte_Len_4QOR;wire data_wr_clk;
wire data_wren;
wire [7:0] data_4QOR;wire [1:0] LC;flash_4QOR flash_4QOR_inst(.clk (clk),.rst_n (rst_n),.FLASH_SCK (FLASH_SCK_5),.FLASH_nCS (FLASH_nCS_5),.link (link5),.FLASH_IO_OBUF (FLASH_IO_OBUF5),.FLASH_IO_IBUF (FLASH_IO_IBUF5),//usr interface.read_start (start_5),.addr (addr_4QOR),.Byte_Len (Byte_Len_4QOR),.data_wr_clk (data_wr_clk), //读FLASH并将数据写入rFIFO.data_wren (data_wren),.data (data_4QOR), //写到rFIFO的数据.busy (busy_5),//LC.LC (LC) //LC bitCR1[7:6]
);//--------------------------------通道仲裁--------------------------------------
localparam M_NONE 8h01;
localparam M_instruction 8h02;
localparam M_WRR 8h04;
localparam M_RDR 8h08;
localparam M_PP 8h10;
localparam M_4QOR 8h20;reg [7:0] module_arb M_NONE;
reg submodule_busy;always (*) begincase(module_arb)M_NONE: beginsubmodule_busy 1b0;FLASH_SCK 1b1;FLASH_nCS 1b1;link 4h0;FLASH_IO_OBUF 4hf;endM_instruction: beginsubmodule_busy busy_1;FLASH_SCK FLASH_SCK_1;FLASH_nCS FLASH_nCS_1;link link1;FLASH_IO_OBUF FLASH_IO_OBUF1;endM_WRR: beginsubmodule_busy busy_2;FLASH_SCK FLASH_SCK_2;FLASH_nCS FLASH_nCS_2;link link2;FLASH_IO_OBUF FLASH_IO_OBUF2;endM_RDR: beginsubmodule_busy busy_3;FLASH_SCK FLASH_SCK_3;FLASH_nCS FLASH_nCS_3;link link3;FLASH_IO_OBUF FLASH_IO_OBUF3;endM_PP: beginsubmodule_busy busy_4;FLASH_SCK FLASH_SCK_4;FLASH_nCS FLASH_nCS_4;link link4;FLASH_IO_OBUF FLASH_IO_OBUF4;endM_4QOR: beginsubmodule_busy busy_5;FLASH_SCK FLASH_SCK_5;FLASH_nCS FLASH_nCS_5;link link5;FLASH_IO_OBUF FLASH_IO_OBUF5;enddefault: beginsubmodule_busy 1b0;FLASH_SCK 1b1;FLASH_nCS 1b1;link 4h0;FLASH_IO_OBUF 4hf;endendcase
end//----------------------------------FSM----------------------------------------
localparam S_IDLE 24h000001;
localparam S_ARB 24h000002; //仲裁对哪一个req进行响应
localparam S_WAIT 24h000004; //等待子模块工作完成
localparam S_STOP 24h000008;localparam S_WREN 24h000010; //执行WREN指令置位WEL bit
localparam S_WRDI 24h000020; //执行WRDI指令复位WEL bit
localparam S_CLSR 24h000040; //执行CLSR复位P_ERR、E_ERR bit
localparam S_BE 24h000080; //Bulk Eraselocalparam S_WRSR1 24h000100; //写Status Register 1
localparam S_WRCR1 24h000200; //写Configurate Register 1
localparam S_WRBAR 24h000400; //写Bank Address Register
localparam S_WRABR 24h000800; //写Autoboot Register
localparam S_SE 24h001000; //Sector Eraselocalparam S_RDSR1 24h002000; //读SR1
localparam S_RDSR2 24h004000; //读SR2
localparam S_RDCR1 24h008000; //读CR1
localparam S_RDBAR 24h010000; //读Bank Address Register
localparam S_RDABR 24h020000; //读Autoboot Registerlocalparam S_4QPP 24h040000; //Page Programming
localparam S_4QOR 24h080000; //Quad Output Readlocalparam S_RESET 24h100000; //flash software resetreg [23:0] state S_IDLE;
reg [23:0] next_state;always (posedge clk or negedge rst_n) beginif(~rst_n) beginstate S_IDLE;endelse beginstate next_state;end
endwire [16:0] all_req;
reg [16:0] all_req_buf;
assign all_req {RESET_req, WREN_req, WRDI_req, CLSR_req, bulk_erase_req, sector_erase_req,rd_SR1_req, rd_CR1_req, rd_SR2_req, rd_BAR_req, rd_ABR_req,wr_SR1_req, wr_CR1_req, wr_BAR_req, wr_ABR_req,WR_req, RD_req};always (posedge clk) beginall_req_buf all_req;
endalways (*) begincase(state)S_IDLE: beginnext_state S_ARB;endS_ARB: begincasex(all_req_buf)17b1_xxxx_xxxx_xxxx_xxxx: next_state S_RESET;17b0_1xxx_xxxx_xxxx_xxxx: next_state S_WREN;17b0_01xx_xxxx_xxxx_xxxx: next_state S_WRDI;17b0_001x_xxxx_xxxx_xxxx: next_state S_CLSR;17b0_0001_xxxx_xxxx_xxxx: next_state S_BE;17b0_0000_1xxx_xxxx_xxxx: next_state S_SE;17b0_0000_01xx_xxxx_xxxx: next_state S_RDSR1;17b0_0000_001x_xxxx_xxxx: next_state S_RDCR1;17b0_0000_0001_xxxx_xxxx: next_state S_RDSR2;17b0_0000_0000_1xxx_xxxx: next_state S_RDBAR;17b0_0000_0000_01xx_xxxx: next_state S_RDABR;17b0_0000_0000_001x_xxxx: next_state S_WRSR1;17b0_0000_0000_0001_xxxx: next_state S_WRCR1;17b0_0000_0000_0000_1xxx: next_state S_WRBAR;17b0_0000_0000_0000_01xx: next_state S_WRABR;17b0_0000_0000_0000_001x: next_state S_4QPP;17b0_0000_0000_0000_0001: next_state S_4QOR;default: next_state S_ARB;endcaseendS_RESET, S_WREN, S_WRDI, S_CLSR, S_BE, S_SE,S_RDSR1, S_RDCR1, S_RDSR2, S_RDBAR, S_RDABR,S_WRSR1, S_WRCR1, S_WRBAR, S_WRABR,S_4QPP, S_4QOR: beginif(submodule_busy) beginnext_state S_WAIT;endelse beginnext_state state;endendS_WAIT: beginif(~submodule_busy) beginnext_state S_STOP;endelse beginnext_state S_WAIT;endendS_STOP: beginnext_state S_IDLE;enddefault: beginnext_state S_IDLE;endendcase
endreg [3:0] update_register 4d0; //在RD REG操作中判断要更新哪一个Reg
//1:SR1, 2:CR1, 3:SR2, 4:BAR, 5:ABRalways (posedge clk) begincase(state)S_IDLE: beginmodule_arb M_NONE;start_1 1b0;start_2 1b0;start_3 1b0;start_4 1b0;start_5 1b0;update_register 4d0;endS_ARB: beginmodule_arb M_NONE;start_1 1b0;start_2 1b0;start_3 1b0;start_4 1b0;start_5 1b0;endS_RESET: beginmodule_arb M_instruction;start_1 1b1;instruction_1 I_RESET;endS_WREN: beginmodule_arb M_instruction;start_1 1b1;instruction_1 I_WREN;endS_WRDI: beginmodule_arb M_instruction;start_1 1b1;instruction_1 I_WRDI;endS_CLSR: beginmodule_arb M_instruction;start_1 1b1;instruction_1 I_CLSR;endS_BE: beginmodule_arb M_instruction;start_1 1b1;instruction_1 I_BE;endS_SE: beginmodule_arb M_WRR;start_2 1b1;instruction_2 I_SE;Register_Len_WRR 4d4;WRR_Byte1 sector_erase_addr[31:24];WRR_Byte2 sector_erase_addr[23:16];WRR_Byte3 sector_erase_addr[15:8];WRR_Byte4 sector_erase_addr[7:0];endS_RDSR1: beginmodule_arb M_RDR;start_3 1b1;instruction_3 I_RDSR1;Register_Len_RDR 4d1;update_register 4d1;endS_RDCR1: beginmodule_arb M_RDR;start_3 1b1;instruction_3 I_RDCR1;Register_Len_RDR 4d1;update_register 4d2;endS_RDSR2: beginmodule_arb M_RDR;start_3 1b1;instruction_3 I_RDSR2;Register_Len_RDR 4d1;update_register 4d3;endS_RDBAR: beginmodule_arb M_RDR;start_3 1b1;instruction_3 I_RDBAR;Register_Len_RDR 4d1;update_register 4d4;endS_RDABR: beginmodule_arb M_RDR;start_3 1b1;instruction_3 I_RDABR;Register_Len_RDR 4d4;update_register 4d5;endS_WRSR1: beginmodule_arb M_WRR;start_2 1b1;instruction_2 I_WRR;Register_Len_WRR 4d1;WRR_Byte1 SR1_wr;WRR_Byte2 8d0;WRR_Byte3 8d0;WRR_Byte4 8d0;endS_WRCR1: beginmodule_arb M_WRR;start_2 1b1;instruction_2 I_WRR;Register_Len_WRR 4d2;WRR_Byte1 SR1_wr;WRR_Byte2 CR1_wr;WRR_Byte3 8d0;WRR_Byte4 8d0;endS_WRBAR: beginmodule_arb M_WRR;start_2 1b1;instruction_2 I_WRBAR;Register_Len_WRR 4d1;WRR_Byte1 BAR_wr;WRR_Byte2 8d0;WRR_Byte3 8d0;WRR_Byte4 8d0;endS_WRABR: beginmodule_arb M_WRR;start_2 1b1;instruction_2 I_WRABR;Register_Len_WRR 4d4;WRR_Byte1 ABR_wr[31:24];WRR_Byte2 ABR_wr[23:16];WRR_Byte3 ABR_wr[15:8];WRR_Byte4 ABR_wr[7:0];endS_4QPP: beginmodule_arb M_PP;start_4 1b1;addr_PP WR_addr;Byte_Len_PP WR_Byte_Len;endS_4QOR: beginmodule_arb M_4QOR;start_5 1b1;addr_4QOR RD_addr;Byte_Len_4QOR RD_Byte_Len;endS_WAIT: beginstart_1 1b0;start_2 1b0;start_3 1b0;start_4 1b0;start_5 1b0;endS_STOP: beginmodule_arb M_NONE;case(update_register)4d1: SR1_rd RDR_Reg[7:0];4d2: CR1_rd RDR_Reg[7:0];4d3: SR2_rd RDR_Reg[7:0];4d4: BAR_rd RDR_Reg[7:0];4d5: ABR_rd RDR_Reg;default: ;endcaseenddefault: beginmodule_arb M_NONE;start_1 1b0;start_2 1b0;start_3 1b0;start_4 1b0;start_5 1b0;endendcase
endalways (*) begincase(state)S_IDLE, S_ARB: beginbusy 1b0;enddefault: beginbusy 1b1;endendcase
endendmodule测试 编写测试代码如下并下载到板子进行测试注意我的板子上的 FLASH 的 QUAD bitCR1[1]已经被置位了所以这里只执行了擦除、写入、读取流程如果你的不是需要多加一个 WRR 步骤
// FLASH 测试主存读写测试
default_nettype none
module test_flash_mainMemory(
input wire clk_sys, //OXCO_10Moutput wire FLASH_nCS,
inout wire [3:0] FLASH_IO,input wire [3:0] Key,
output wire [3:0] LED
);wire clk_100M;
wire clk_flash;
wire clk_1k;
wire clk_1Hz;reg rst_n 1b1;clk_wiz_0 clk_wiz(.clk_in1 (clk_sys),.clk_out1 (clk_100M), .reset (1b0), .locked ()
);clkdiv #(.N(3))
clkdiv_flash(.clk_in (clk_100M),.clk_out (clk_flash) //测试发现50M下寄存器写操作可能出现错误因此降为33M
);clkdiv #(.N(1000_00))
clkdiv_1k(.clk_in (clk_100M),.clk_out (clk_1k)
);clkdiv #(.N(100_000_000))
clkdiv_1Hz(.clk_in (clk_100M),.clk_out (clk_1Hz)
);wire usrdone;
set_CCLK set_CCLK_inst(.usrcclk (FLASH_SCK),.usrdone (usrdone),.cfgclk (),.cfgmclk (),.eos ()
);assign usrdone clk_1Hz;//-------------------------------------FLASH------------------------------------------------------
wire FLASH_SCK;
wire FLASH_nCS;
wire [3:0] FLASH_IO;//wr FLASH
reg WR_req 1b0; //Page Programmingreg [31:0] WR_addr 32d0; //起始编程地址对于S25FL256S可用地址为0~1FFFFFF25bit
reg [9:0] WR_Byte_Len 10d1; //编程字节数wire data_rd_clk; //读wFIFO的时钟
wire data_rden; //读wFIFO的使能信号
reg [7:0] data_PP 8d0; //从wFIFO读到的数据将写入FLASH//rd FLASH
reg RD_req 1b0;
reg [1:0] LC 2b00; //LC bits, CR1[7:6]reg [31:0] RD_addr 32d0; //起始读取地址
reg [31:0] RD_Byte_Len 32d1; //读取字节数wire data_wr_clk; //写rFIFO的clk
wire data_wren; //写rFIFO的使能信号
wire [7:0] data_4QOR; //从FLASH读到的数据//WREN/WRDI/CLSR/RESET
reg WREN_req 1b0; //置位WEL bit
reg WRDI_req 1b0; //复位WEL bit
reg CLSR_req 1b0; //清空SR1只复位P_ERR、E_ERR这两个bit
reg RESET_req 1b0; //软复位//erase
reg bulk_erase_req 1b0; //批量擦除reg sector_erase_req 1b0; //Sector擦除一次擦除一个标准Sector64KB
reg [31:0] sector_erase_addr 32d0; //低16位直接置零即可//RD SR1/CR1/SR2/BAR/ABR
reg rd_SR1_req 1b0; //Status Register 1
wire [7:0] SR1_rd;reg rd_CR1_req 1b0; //Configuration Register
wire [7:0] CR1_rd;reg rd_SR2_req 1b0; //Status Register 2
wire [7:0] SR2_rd;reg rd_BAR_req 1b0; //Bank Address Register
wire [7:0] BAR_rd;reg rd_ABR_req 1b0; //Autoboot Register
wire [31:0] ABR_rd;//WR SR1/CR1/BAR/ABR
reg wr_SR1_req 1b0; //发起WR_SR1只需要给入SR1
reg wr_CR1_req 1b0; //发起WR_CR1请求时要同时给入SR1、CR1两个值
reg [7:0] SR1_wr 8d0;
reg [7:0] CR1_wr;reg wr_BAR_req 1b0;
reg [7:0] BAR_wr;reg wr_ABR_req 1b0;
reg [31:0] ABR_wr;wire busy;FLASH_top FLASH_top_inst(.clk (clk_flash),.rst_n (rst_n),.FLASH_SCK (FLASH_SCK),.FLASH_nCS (FLASH_nCS),.FLASH_IO (FLASH_IO),//----------------user interface---------------------//wr FLASH.WR_req (WR_req),.WR_addr (WR_addr),.WR_Byte_Len (WR_Byte_Len),.data_rd_clk (data_rd_clk),.data_rden (data_rden),.data_PP (data_PP),//rd FLASH.RD_req (RD_req),.LC (LC),.RD_addr (RD_addr),.RD_Byte_Len (RD_Byte_Len),.data_wr_clk (data_wr_clk),.data_wren (data_wren),.data_4QOR (data_4QOR),//WREN/WRDI/CLSR/RESET.WREN_req (WREN_req),.WRDI_req (WRDI_req),.CLSR_req (CLSR_req),.RESET_req (RESET_req),//erase.bulk_erase_req (bulk_erase_req),.sector_erase_req (sector_erase_req),.sector_erase_addr (sector_erase_addr),//RD SR1/CR1/SR2/BAR/ABR.rd_SR1_req (rd_SR1_req),.SR1_rd (SR1_rd),.rd_CR1_req (rd_CR1_req),.CR1_rd (CR1_rd),.rd_SR2_req (rd_SR2_req),.SR2_rd (SR2_rd),.rd_BAR_req (rd_BAR_req),.BAR_rd (BAR_rd),.rd_ABR_req (rd_ABR_req),.ABR_rd (ABR_rd),//WR SR1/CR1/BAR/ABR.wr_SR1_req (wr_SR1_req),.wr_CR1_req (wr_CR1_req),.SR1_wr (SR1_wr),.CR1_wr (CR1_wr),.wr_BAR_req (wr_BAR_req),.BAR_wr (BAR_wr),.wr_ABR_req (wr_ABR_req),.ABR_wr (ABR_wr),.busy (busy),//debug.link (link),.FLASH_IO_OBUF (FLASH_IO_OBUF),.FLASH_IO_IBUF (FLASH_IO_IBUF),.state (state)
);//debug
wire [3:0] link;
wire [3:0] FLASH_IO_OBUF;
wire [3:0] FLASH_IO_IBUF;
wire [23:0] state;//-----------------------------test------------------------------------
wire PPS_pe;
reg PPS_d0;
reg PPS_d1;reg PPS_pe_d1;
reg PPS_pe_d2;assign PPS_pe PPS_d0 (~PPS_d1);reg [7:0] cnt 8d0;always (posedge clk_flash) beginPPS_d0 clk_1k;PPS_d1 PPS_d0;if(PPS_pe) beginif(cnt1 || cnt11) beginif(SR1_rd[1]) begin //检查WELcnt cnt 1b1;endelse begincnt cnt;endendelse if(cnt3 || cnt13) beginif(~SR1_rd[0]) begin //检查WIPcnt cnt 1b1;endelse begincnt cnt;endendelse begincnt cnt 1b1;endendPPS_pe_d1 PPS_pe;PPS_pe_d2 PPS_pe_d1;
endlocalparam WR_RD_ADDR 32h0100_0000;reg [7:0] data_PP_tmp 8d0;
always (posedge data_rd_clk) beginif(data_rden) begindata_PP_tmp data_PP_tmp 1b1;endelse begindata_PP_tmp data_PP_tmp;end
endalways (posedge clk_100M) begincase(cnt)//---------------erase-------------------------8d0: WREN_req PPS_pe_d2;8d1: rd_SR1_req PPS_pe_d2;8d2: beginsector_erase_req PPS_pe_d2;sector_erase_addr WR_RD_ADDR;end8d3: rd_SR1_req PPS_pe_d2;8d4: rd_CR1_req PPS_pe_d2;//------------wr main mem----------------------8d10: WREN_req PPS_pe_d2;8d11: rd_SR1_req PPS_pe_d2;8d12: beginWR_req PPS_pe_d2;WR_addr WR_RD_ADDR;WR_Byte_Len 10d16;data_PP data_PP_tmp;end8d13: rd_SR1_req PPS_pe_d2;//--------------get LC--------------------------8d20: rd_CR1_req PPS_pe_d2;8d21: LC CR1_rd[7:6];//------------rd main mem----------------------8d30: beginRD_req PPS_pe_d2;RD_addr WR_RD_ADDR;RD_Byte_Len 10d16;enddefault: ;endcase
end//-----------------------------ILA------------------------------------
ila_test ila(.clk (clk_100M),.probe0 (cnt),.probe1 (busy),.probe2 (FLASH_SCK),.probe3 (FLASH_nCS),.probe4 (link),.probe5 (FLASH_IO_IBUF),.probe6 (SR1_rd),.probe7 (CR1_rd),.probe8 (data_rd_clk),.probe9 (data_rden),.probe10 (data_PP),.probe11 (data_wr_clk),.probe12 (data_wren),.probe13 (data_4QOR)
);endmodule用户控制 CCLK 主要用到 STARTUPE2 原语我这里封装为了一个代码模块具体可看这篇博文
/* * file : set_CCLK.v* author : 今朝无言* Lab : WHU-EIS-LMSWE* date : 2023-11-02* version : v1.0* description : 使用原语设置CCLK* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
default_nettype none
module set_CCLK(
input wire usrcclk,
input wire usrdone,output wire cfgclk,
output wire cfgmclk,
output wire eos
);//-------------------STARTUPE2---------------------
STARTUPE2 #(.PROG_USR (FALSE),.SIM_CCLK_FREQ (0.0)
)
STARTUPE2_inst(.CFGCLK (cfgclk),.CFGMCLK (cfgmclk),.EOS (eos),.PREQ (),.CLK (0),.GSR (0),.GTS (0),.KEYCLEARB (1),.PACK (1),.USRCCLKO (usrcclk),.USRCCLKTS (0),.USRDONEO (usrdone),.USRDONETS (0)
);endmodule在该测试代码中循环向 FLASH 写入自增 1 的数据然后观察从 FLASH 读取到的数据如下 可以看到读取到正确的数据。
Something 在测试 FLASH 读写中踩到了好多坑主要是写入/擦除操作方面的写寄存器、写主存、擦除等记录如下 WREN 操作后WEL bit 不是立即置位的如果执行 WREN 后立即执行写寄存器、擦除、写主存等操作都会失败这些操作都需要写使能位 WEL 为高才能执行。精细测量发现在执行 WREN 后约 800us WEL 才被置位且这个时间不是很固定因此强烈建议在执行 WREN 后周期检查 WEL bit待 WEL1 后再执行擦除、写入操作。 WRR 命令执行后若只存在把某位某些位从 0 置 1 的操作则执行非常快小于 1ms而如果存在把某些位从 1 置 0 的操作时设备会陷入长时间的忙碌状态WIP1测试表明约 383ms。若在 WIP1 的状态执行新的写入、擦除操作时这些指令都会被忽略。因此在执行 WRR 后也需要检查 WIP待 WIP0 后才能退回空闲状态。即写寄存器应当遵循 ‘WREN - check WEL - WR Reg - check WIP - return IDLE’ 的流程。 Erase、Page Program 等操作执行后时间也很长也应当遵循 ‘WREN - check WEL - Erase/PP - check WIP - return IDLE’ 的流程。
完