2908|6

290

帖子

0

TA的资源

一粒金砂(高级)

楼主
 

【国产FPGA高云GW1N-4系列开发板测评】FLASH操作 [复制链接]

  本帖最后由 怀揣少年梦 于 2021-12-28 14:12 编辑

一、目的

使用高云GW1N-4系列开发板擦除、页写FLASHFLASH使用的型号为W25Q64JV

二、W25Q64JVSSIQ的特征

1支持3种通信方式,SPIDual SPIQuad SPIFLASH的存储单元无法写入bit 1,只能写入bit 0,所以写入数据之前要将原来的数据擦除(FFh),遇到写入bit 1的情况不作处理。W25QQ64JV的特征为如下图所示

 

2FLASH的存储结构

W25Q64JV Block0~Block127128Block组成,容量大小为64M-bit,64M=128*64*1024*8bit/1024/1024。每个BlockSector0~Sector1516Sector组成,每个Sector大小为4KB,由16Page组成。以第一个Sector为例,第1Page地址从xx0000h~xx00FF开始,第2Page地址从xx0100~xx01FF开始,第3Page地址从xx0200~xx02FF开始,以此类推...,第16Page地址从xx0F00~xx0FFF开始。每个Page的大小为256Byte组成,后面会看到Page Programd最大支持256Byte。说明框图如下:

        


  1. FLASH的相关操作时序图

1)写使能时序

2)全擦除时序

3)页写时序

根据以上的时序绘制波形图,也就是SPI模拟时序波形图。因为FPGA时钟50MHz,所以把50M进行四分频,并且32个时钟周期延时640ns。正好可以作为两次数据间隔的延时周期。

三、程序编写

1、全擦除程序

1)全擦除波形图绘制

2)编写代码

//`timescale  1ns/1ns
/*
Module Name   : flash_ce
Project Name  : flash
Description   : flash全擦除模块
把主时钟四分频成12.5MHz,作为spi的输入时钟。
写一次数据与下一次数据之间必须等待50ns,每个时钟周期只能写入1bit数据,写一个字节即需要8个spi时钟周期,即32个系统时钟周期,说明一条完整指令需要640ns

*/

module flash_ce(
    input wire sys_clk,      //系统时钟,频率50MHz         
    input wire sys_rst_n,    //复位信号,低电平有效
    input wire key,          //按键输入信号 

    output reg spi_cs_n,    //片选信号           
    output reg spi_clk,     //串行时钟           
    output reg spi_mosi     //主输出从输入数据  
);



//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   IDLE    =   4'b0001 ,   //初始状态 状态机四个状态
            WR_EN   =   4'b0010 ,   //写状态
            DELAY   =   4'b0100 ,   //等待状态
            CE      =   4'b1000 ;   //全擦除状态
parameter   WR_EN_INST  =   8'b0000_0110,   //写使能指令  06
            CE_INST     =   8'b1100_0111;   //全擦除指令  C7



//reg   define
reg     [2:0]   cnt_byte;   //字节计数器   记录输出字节个数和等待时间
reg     [3:0]   state   ;   //状态机状态
reg     [4:0]   cnt_clk ;   //系统时钟计数器,用以记录单子字节
reg     [1:0]   cnt_sck ;   //串行时钟计数器,生成系统时钟
reg     [2:0]   cnt_bit ;   //比特计数器,产生高低位,控制MOSI输出

//除了在空闲状态,其他状态都进行系统时钟计数器,如果只使用cnt_clk作为状态跳转的约束条件不充分,
always@(posedge sys_clk or negedge sys_rst_n)
begin
    if(!sys_rst_n)
        cnt_clk <= 5'd0;
    else if(state != IDLE)
        cnt_clk <= cnt_clk + 1'b1;
end

//由于状态跳转的约束条件不充分,所以使用cnt_byte 计数器对cnt_clk的计数周期进行计数,同时使用cnt_byte增加一次表示延时640ns
always@(posedge sys_clk or negedge sys_rst_n)
begin
    if(!sys_rst_n)
        cnt_byte <= 3'd0;
    else if((cnt_clk == 5'd31) && (cnt_byte == 3'd6))//当写完擦除指令后,等待640ns,片选拉高,此时cnt_byte = 6,并且刚好cnt_clk进行一个计数循环,就清零
        cnt_byte <= 3'd0;
    else if(cnt_clk == 5'd31)//cnt_clk每完成一个循环周期cnt_byte就加1,其他不变
        cnt_byte <= cnt_byte + 1'b1;
end

//在进行写使能指令以及全擦除指令,并且已经发送一个字节时,记录系统时钟的个数
always@(posedge sys_clk or negedge sys_rst_n)
begin
    if(!sys_rst_n)
        cnt_sck <= 2'd0;
    else if((state == WR_EN) && (cnt_byte == 1'b1)) // 当进行写使能时,先等待640ns,即cnt_byte =1
        cnt_sck <= cnt_sck + 1'b1;
    else if((state == CE) && (cnt_byte == 3'd5)) //当写完使能信号,等待640ns,再片选拉高,继续等待640ns,再片选拉低,继续等待640ns,此时cnt_byte =5,此时又开始写擦除指令
        cnt_sck <= cnt_sck + 1'b1;
end

//进行四分频,产生12.5MHz时钟.根据cnt_sck 记录系统时钟的个数,2个cnt_sck为低,2个cnt_sck为高
always@(posedge sys_clk or negedge sys_rst_n)
begin 
    if(!sys_rst_n)
        spi_clk <= 1'b0;
    else if(cnt_sck == 2'd0)
        spi_clk <= 1'b0;
    else if(cnt_sck == 2'd2)
        spi_clk <= 1'b1;

end

//片选信号输出
always@(posedge sys_clk or negedge sys_rst_n)
begin
    if(!sys_rst_n)
        spi_cs_n <= 1;
    else if(key == 1'b1)
        spi_cs_n <= 0;
    else if((cnt_byte == 3'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
        spi_cs_n <= 1;
    else if((cnt_byte == 3'd3) && (cnt_clk == 5'd31) && (state == DELAY))
        spi_cs_n <= 0;   
    else if((cnt_byte == 3'd6) && (cnt_clk == 5'd31) && (state == CE))
        spi_cs_n <= 1;  
end

//cnt_bit 高低位对调,控制MOSI输出。用于输出MOSI的哪一位
always@(posedge sys_clk or negedge sys_rst_n)
begin   
    if(!sys_rst_n)
        cnt_bit <= 3'd0;
    else if(cnt_clk == 2'd2)
        cnt_bit <= cnt_bit + 1'b1;
end

//state 状态机跳转
always@(posedge sys_clk or negedge sys_rst_n)
begin   
    if(!sys_rst_n)
        state <= IDLE;
    else 
        case(state)
            IDLE:if(key == 1'b1)
                state <= WR_EN;
            WR_EN:if((cnt_byte == 3'd2) && (cnt_clk == 5'd31))
                state <= DELAY;
            DELAY:if((cnt_byte == 3'd3) && (cnt_clk == 5'd31))
                state <= CE;
            CE:if((cnt_byte == 3'd6) && (cnt_clk == 5'd31))
                state <= IDLE;
            default:
                state <= IDLE;
        endcase
end


//MOSI输出
always@(posedge sys_clk or negedge sys_rst_n)
begin   
    if(!sys_rst_n)
        spi_mosi <= 1'b0;
    else if((state == WR_EN) && (cnt_byte == 3'd2))
        spi_mosi <= 1'b0;
    else if((state == CE) && (cnt_byte == 3'd6))
        spi_mosi <= 1'b0;
    else if((state == WR_EN) && (cnt_byte == 3'd1) && (cnt_sck == 5'd0))
        spi_mosi <= WR_EN_INST[7-cnt_bit];
    else if((state == CE) && (cnt_byte == 3'd5) && (cnt_sck == 5'd0))
        spi_mosi <= CE_INST[7-cnt_bit]; 
end


endmodule

 

    3)和Modelsim联合仿真。(联合仿真视频可以参考B站高云半导体视频教程高云半导体FPGA课程 - 软件篇_哔哩哔哩_bilibili

创建工程文件,把相关文件添加工程,取消优化。这个仿真使用了M25P16仿真模块进行仿真。仿真波形如图:

写使能

擦除指令

擦除完成

4)在线逻辑仪波形

a、设置引脚时,使用的是MSPI引脚,所以注意在Process页面右键选中configraion,如图,在Dual-purpose 中选择MSPI 作为常规引脚。否则编译时就会出现如图示错误。

编译错误

b、建立GAO文件;详细步骤参考论坛大佬的教程。链接如下:

【国产FPGA高云GW1N-4系列开发板测评】——4、内嵌逻辑分析仪的使用 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)

c、在线逻辑分析页面,在这个自己犯了一个打错,就是提前把跳线帽跳到JTAG模式,而此时还是使用USB下载,结果出现下载不了程序,并且提示code 不匹配。实际上GWIN1-4B MINI开发板已经有JTAG下载,就是使用FT232H芯片来支持JTAG下载模式。这个错误让自己停滞了几天。感谢论坛大佬@gs001588  提供了帮助。多谢大佬。

d、在线逻辑波形由于没有截图就不上传了

2、页写代码编写

1)页写波形图绘制,参考时序绘制

2)根据波形图进行代码编写

module  flash_pp_ctrl(

    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire            key         ,   //按键输入信号

    output  reg             cs_n        ,   //片选信号
    output  reg             sck         ,   //串行时钟
    output  reg             mosi            //主输出从输入数据

);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//parameter define
parameter   IDLE    =   4'b0001 ,   //初始状态
            WR_EN   =   4'b0010 ,   //写状态
            DELAY   =   4'b0100 ,   //等待状态
            PP      =   4'b1000 ;   //页写状态
parameter   WR_EN_INST      =   8'b0000_0110,   //写使能指令
            PP_INST         =   8'b0000_0010;   //页写指令
parameter   SECTOR_ADDR     =   8'b0000_0000,   //扇区地址
            PAGE_ADDR       =   8'b0000_0100,   //页地址
            BYTE_ADDR       =   8'b0010_0101;   //字节地址
parameter   NUM_DATA        =   8'd100      ;   //页写数据个数(0-99)

//reg   define
reg     [7:0]   cnt_byte        ;   //字节计数器
reg     [3:0]   state           ;   //状态机状态
reg     [4:0]   cnt_clk         ;   //系统时钟计数器
reg     [1:0]   cnt_sck         ;   //串行时钟计数器
reg     [2:0]   cnt_bit         ;   //比特计数器
reg     [7:0]   data            ;   //页写入数据

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//cnt_clk:系统时钟计数器,用以记录单个字节
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk  <=  5'd0;
    else    if(state != IDLE)
        cnt_clk  <=  cnt_clk + 1'b1;

//cnt_byte:记录输出字节个数和等待时间
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_byte    <=  8'd0;
    else    if((cnt_clk == 5'd31) && (cnt_byte == NUM_DATA + 8'd9))
        cnt_byte    <=  8'd0;
    else    if(cnt_clk == 5'd31)
        cnt_byte    <=  cnt_byte + 1'b1;

//cnt_sck:串行时钟计数器,用以生成串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_sck <=  2'd0;
    else    if((state == WR_EN) && (cnt_byte == 8'd1))
        cnt_sck <=  cnt_sck + 1'b1;
    else    if((state == PP) && (cnt_byte >= 8'd5)
                && (cnt_byte <= NUM_DATA + 8'd9 - 1'b1))
        cnt_sck <=  cnt_sck + 1'b1;

//cs_n:片选信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cs_n    <=  1'b1;
    else    if(key == 1'b1)
        cs_n    <=  1'b0;
    else    if((cnt_byte == 8'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
        cs_n    <=  1'b1;
    else    if((cnt_byte == 8'd3) && (cnt_clk == 5'd31) && (state == DELAY))
        cs_n    <=  1'b0;
    else    if((cnt_byte == NUM_DATA + 8'd9) && (cnt_clk == 5'd31) && (state == PP))
        cs_n    <=  1'b1;

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

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

//data:页写入数据
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data <=  8'd0;
    else    if((cnt_clk == 5'd31) && ((cnt_byte >= 8'd9)
                && (cnt_byte < NUM_DATA + 8'd9 - 1'b1)))
        data <=  data + 1'b1;

//state:两段式状态机第一段,状态跳转
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else
    case(state)
        IDLE:   if(key == 1'b1)
                state   <=  WR_EN;
        WR_EN:  if((cnt_byte == 8'd2) && (cnt_clk == 5'd31))
                state   <=  DELAY;
        DELAY:  if((cnt_byte == 8'd3) && (cnt_clk == 5'd31))
                state   <=  PP;
        PP:     if((cnt_byte == NUM_DATA + 8'd9) && (cnt_clk == 5'd31))
                state   <=  IDLE;
        default:    state   <=  IDLE;
    endcase

//mosi:两段式状态机第二段,逻辑输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte== 8'd2))
        mosi    <=  1'b0;
    else    if((state == PP) && (cnt_byte == NUM_DATA + 8'd9))
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte == 8'd1) && (cnt_sck == 5'd0))
        mosi    <=  WR_EN_INST[7 - cnt_bit];  //写使能指令
    else    if((state == PP) && (cnt_byte == 8'd5) && (cnt_sck == 5'd0))
        mosi    <=  PP_INST[7 - cnt_bit];    //页写指令
    else    if((state == PP) && (cnt_byte == 8'd6) && (cnt_sck == 5'd0))
        mosi    <=  SECTOR_ADDR[7 - cnt_bit];  //扇区地址
    else    if((state == PP) && (cnt_byte == 8'd7) && (cnt_sck == 5'd0))
        mosi    <=  PAGE_ADDR[7 - cnt_bit];    //页地址
    else    if((state == PP) && (cnt_byte == 8'd8) && (cnt_sck == 5'd0))
        mosi    <=  BYTE_ADDR[7 - cnt_bit];    //字节地址
    else    if((state == PP) && ((cnt_byte >= 8'd9)
                && (cnt_byte <= NUM_DATA + 8'd9 - 1'b1)) && (cnt_sck == 5'd0))
        mosi    <=  data[7 - cnt_bit];  //页写入数据

endmodule

 

3在线逻辑图波形

写使能

 

写指令

写地址

 

 

 

 

 

最新回复

赞,赞,评测文章帮助到其他坛友了:)   详情 回复 发表于 2021-12-30 09:36
点赞 关注
个人签名

阅读改变人生

 
 

回复
举报

1366

帖子

6

TA的资源

版主

沙发
 

清晰明了,羡慕会fpga的

 
 
 

回复

664

帖子

104

TA的资源

纯净的硅(中级)

板凳
 

不错,说干就干,这效率杠杠的呀!

时序图是用啥软件画的,效果很好啊

点评

还是得益于大佬您的帮助啊。使用visio软件画的  详情 回复 发表于 2021-12-29 08:12
 
 
 

回复

6828

帖子

0

TA的资源

五彩晶圆(高级)

4
 

时序图很专业

点评

过奖过奖  详情 回复 发表于 2021-12-29 08:12
 
 
 

回复

290

帖子

0

TA的资源

一粒金砂(高级)

5
 
gs001588 发表于 2021-12-28 21:38 不错,说干就干,这效率杠杠的呀! 时序图是用啥软件画的,效果很好啊

还是得益于大佬您的帮助啊。使用visio软件画的

点评

赞,赞,评测文章帮助到其他坛友了:)  详情 回复 发表于 2021-12-30 09:36
个人签名

阅读改变人生

 
 
 

回复

290

帖子

0

TA的资源

一粒金砂(高级)

6
 

过奖过奖

个人签名

阅读改变人生

 
 
 

回复

2万

帖子

74

TA的资源

管理员

7
 
怀揣少年梦 发表于 2021-12-29 08:12 还是得益于大佬您的帮助啊。使用visio软件画的

赞,赞,评测文章帮助到其他坛友了:)

加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
个人签名

加油!在电子行业默默贡献自己的力量!:)

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表