【S031】verilog 读写spi flash S25fl128L

SPI4种模式 - fedorayang - 博客园

flash解释:

W25Q128BV-datasheet中文资料 - 夜下青灯 - 博客园

极性的定义

在芯片资料上极性和相位一般表示为CPOL(Clock POLarity)和CPHA(Clock PHAse), 极性和相位组合成4种工作模式。
                CPOL     CPHA
MODE0   0             0
MODE1   0             1
MODE2   1             0
MODE3   1             1
CPOL: SPI空闲时的时钟信号电平(1:高电平, 0:低电平)
CPHA: SPI在时钟第几个边沿采样(1:第二个边沿开始, 0:第一个边沿开始

MODE0 :时钟默认是0,第0个沿
MODE1 :时钟默认是1,第1个沿


S25fl128L芯片特性

目的实现最高速率133MHz
SDR:在上升沿采样输入数据,在下降沿输出数据。

DDR:command/instruction还是在上升沿采样输入数据,
在command/instruction完成之后的第一个上升沿采样address/rxdata输入数据,
紧接着是下降沿采样输入数据。
在dummy完成后的第一个下降沿输出数据是。

command protocol
command =8bit instruction  + address + modifier + latency +data transfer
1-1-1 command protocol: single bit data 传输 instruction+addr+data

之前本计划让时钟一直有效,控制CS#来操作flash,结果发现行不通,因为一个完整的command不允许中途CS#变化。

为什么 instruction是单线传输的?

因为flash需要兼容x1/2/4模式,只能通过 instruction来区分不同模式,
如果 instruction也采用4线模式,那么在x1/x2是无法识别的。
如果 instruction都采用x1,那么兼容x1/2/4都可识别。

Continuous Read Mode是什么?

FPGA在address之后发送 instruction modifer bits,暗示下一个命令与当前命令一样,下一次就不用传输 instruction,只需要传输address+ mode bits;
mode bits 发起或者结束 Continuous Read Mode;


所有的都是MSB高字节先进入flash

时序图

 

各种命令时序

SDR x1

SDR x4

DDR x1

DDR x4


如何访问寄存器?

通过特定的instruction。

如何访问Security Regions?

Size=1024B ,分成4个regions.如果regions没有保护和锁定,那么可以正常读写。CR1NV[5:2]:LockBits,只能写入一次OTP(OneTimePragram),写入后永久有效。
region2&3可以通过PR(ProtectionRsgister)临时保护不被擦写,操作IRP寄存器[2],当密码正确可以解除保护。

如何访问ID Address Space?


通过RDID Command(9FH)来读取ID值。

什么是SFDP ?如何访问SFDP ?

RSFDP Command (5AH)
SFDP的数据在出厂时就被固定住,无法修改,只供查询使用。厂商识别码等一系列的功能和参数信息
https://blog.csdn.net/bingquan3333/article/details/81872475
https://www.taterli.com/2984/

1-1-1表示什么意思?

数字表示的是使用的线宽,顺序是
instruction-address-data
Single    1-1-1  instruction (SI) + addr (SI) + data (SO     )
Dual O    1-1-2  instruction (SI) + addr (SI) + data (IO0&IO1)
Dual IO   1-2-2  ... 
Quad O    1-1-4  ... 
Quad IO   1-4-4  ... 
QPI       4-4-4  ... 

Non-volatile寄存器的作用:

1.在上电或者复位flash芯片之后,给volatile寄存器提供初始值。
2.保护bits一次写入
配置寄存器1的内容:
1.设置Security Rigion Lock Bits
2.设置默认Quad
配置寄存器2的内容:
1.QPI模式
2.默认地址长度24|32

需求:

对FLASH进行读写擦除操作。

读操作能否只读单个Byte?


可以读任意1B,读完当前Byte只要CS#没有拉高,不用重新给地址,地址会自动+1,后续数据会持续移出。
read和fastread的区别在于支持的最大的时钟频率不同。

READ

FAST_READ

写操作能否只写单个Byte?


可以
page program 02H
single Byte program必须先WREN使能;

Page Program

写之前是否要擦除?


sector Erase 20H
Half Block Erase 52H
Block Erase D8H
擦除需要时间,FPGA通过查看WIP bit在状态寄存器1 来确定擦除是否完成。1:未完成,0:完成


command summary 

具体实现

WREN 06H

WRDI 04H

RDSR 05H

WRSR 01H

RDCMD 03H

F_RD 0BH

PP 02H

PP命令,每次只能连续写入1page 256Byte

SE D8h

BE C7H

 DP B9H

RES ABH

RDID 9FH

verilog实现

spi_ctrl.v


//spi_ctrl.v
//0H 发送数据(写)/接收数据(读)
//1H 命令(写)/状态(读)
//2H 地址低(写) add_l
//3H 地址中(写) add_m
//4H 地址高(写) add_h
// 写数据到地址0x40_0000:
// 1.告知FPGA地址:先写40h到4H地址,然后写00h到3H地址,最后写00h到2H地址;
// 2.告知FPGA数据:将要写的数据写入0H地址;
// 3.告知FPGA命令:将写命令告知FPGA;
//拆分成两部分:
//1. 控制数据的产生
//2. 单Byte数据发送
`timescale 1ns/100ps
`define SIM_OPEN 1
module spi_ctrl (  
input            clk         ,
input            rst         ,
//管脚信号       
output           spi_clk     ,
output           spi_csn     ,
input            spi_din     ,
output           spi_dout    ,
//内部管理口     
input            m_cs        ,
input            m_wr        ,
input   [2:0]    m_addr      ,
input   [7:0]    m_din       ,
output reg [7:0] m_dout
);

reg [7:0] tx_data0  ;//待发送的数据寄存器
reg [7:0] rx_data0  ;//已接收的数据寄存器
reg [7:0] cmd       ;//命令寄存器
reg [7:0] state     ;//状态寄存器
reg [7:0] add_h     ;//地址高寄存器
reg [7:0] add_m     ;//地址中寄存器
reg [7:0] add_l     ;//地址低寄存器

//检测到操作
wire  rd_data   ;
wire  wr_data   ;
reg   wr_data_1d;
wire  rd_stata  ;
wire  wr_cmd    ;
wire  rd_add_h  ;
wire  rd_add_m  ;
wire  rd_add_l  ;
wire  wr_add_h  ;
wire  wr_add_m  ;
wire  wr_add_l  ;

reg       spi_clk_int   ;
wire      spi_csn_int   ;//由外部控制,因为一次操作不一定只有1B,此处透传
wire      spi_din_int   ;
reg       spi_dout_int  ;

// ST:state状态机
reg        ST_cs      ;//当前1B发送完毕
reg        ST_wr      ;//当前1B发送完毕
reg  [7:0] ST_wdata   ;//当前1B发送完毕
wire       ST_wrvld   ;//当前1B发送完毕
wire [7:0] ST_rdata   ;//当前收到的1B有效数据

parameter   div_max = 8'd4  ;
reg [2:0]   clk_cnt         ;
reg         clk_en          ;//每次分频计数==0的这一拍有效
//判断是什么操作
assign rd_data   =   ({m_cs,m_wr,m_addr[2:0]} ==  {1'b1,1'b0,3'd0})?1'b1 :1'b0 ;    
assign wr_data   =   ({m_cs,m_wr,m_addr[2:0]} ==  {1'b1,1'b1,3'd0})?1'b1 :1'b0 ;    
assign rd_stata  =   ({m_cs,m_wr,m_addr[2:0]} ==  {1'b1,1'b0,3'd1})?1'b1 :1'b0 ;    
assign wr_cmd    =   ({m_cs,m_wr,m_addr[2:0]} ==  {1'b1,1'b1,3'd1})?1'b1 :1'b0 ;    
assign rd_add_h  =   ({m_cs,m_wr,m_addr[2:0]} ==  {1'b1,1'b0,3'd2})?1'b1 :1'b0 ;    
assign rd_add_m  =   ({m_cs,m_wr,m_addr[2:0]} ==  {1'b1,1'b0,3'd3})?1'b1 :1'b0 ;    
assign rd_add_l  =   ({m_cs,m_wr,m_addr[2:0]} ==  {1'b1,1'b0,3'd4})?1'b1 :1'b0 ;    
assign wr_add_h  =   ({m_cs,m_wr,m_addr[2:0]} ==  {1'b1,1'b1,3'd2})?1'b1 :1'b0 ;    
assign wr_add_m  =   ({m_cs,m_wr,m_addr[2:0]} ==  {1'b1,1'b1,3'd3})?1'b1 :1'b0 ;    
assign wr_add_l  =   ({m_cs,m_wr,m_addr[2:0]} ==  {1'b1,1'b1,3'd4})?1'b1 :1'b0 ;    
//锁存数据到寄存器
always @(posedge clk or posedge rst)if(rst) tx_data0   <=#1 8'b0;else if(wr_data )tx_data0 <=#1 m_din;
always @(posedge clk or posedge rst)if(rst) cmd        <=#1 8'b0;else if(wr_cmd  )cmd      <=#1 m_din;
always @(posedge clk or posedge rst)if(rst) add_h      <=#1 8'b0;else if(wr_add_h)add_h    <=#1 m_din;
always @(posedge clk or posedge rst)if(rst) add_m      <=#1 8'b0;else if(wr_add_m)add_m    <=#1 m_din;
always @(posedge clk or posedge rst)if(rst) add_l      <=#1 8'b0;else if(wr_add_l)add_l    <=#1 m_din;

always @(posedge clk or posedge rst)if(rst) wr_data_1d <=#1 1'b0;else  wr_data_1d    <=#1 wr_data;
// 读数据接口
always @(posedge clk or posedge rst)begin 
    if(rst) 
        m_dout      <=#1 8'b0;
    else  begin
        case({rd_data,rd_stata,rd_add_h,rd_add_m,rd_add_l})
            5'b00001: m_dout <=#1 add_l;
            5'b00010: m_dout <=#1 add_m;
            5'b00100: m_dout <=#1 add_h;
            5'b01000: m_dout <=#1 state;
            5'b10000: m_dout <=#1 rx_data0;
            default : m_dout <=#1 8'b0;
        endcase
        end
end

parameter   NOP     = 8'hff;
parameter   WREN    = 8'h06;//md0
parameter   WRDI    = 8'h04;//md0
parameter   RDSR    = 8'h05;//md6
parameter   WRSR    = 8'h01;//md6
parameter   RDCMD   = 8'h03;//md7
parameter   F_RD    = 8'h0B;//md8
parameter   PP      = 8'h02;//md4
parameter   SE      = 8'hD8;//md9
parameter   BE      = 8'hC7;//md0
parameter   DP      = 8'hB9;//md0
parameter   RES     = 8'hAB;//md0
parameter   RDID    = 8'h9F;//md6

wire md0,md1,md2,md3,md4,md5,md6,md7,md8,md9,mdstop;
assign md0 = (cmd==WREN)|(cmd==WRDI)|(cmd==BE)|(cmd==DP)|(cmd==RES) ;
assign md4 = (cmd==PP);
assign md6 = (cmd==RDSR)|(cmd==WRSR)|(cmd==RDID);
assign md7 = (cmd==RDCMD);
assign md8 = (cmd==F_RD);
assign md9 = (cmd==SE);
assign mdstop = (cmd==NOP);
//判断CMD的总体路径:In/out wr 都是fpga 角度
//0. md0  :   cmd
//1. md1  :  cmd+wdata--nouse
//2. md2  :  cmd+addr+wdata--nouse
//3. md3  :  cmd+wdata+...--nouse
//4. md4  :  cmd+addr+wdata+...
//5. md5  :  cmd+rdata--nouse
//6. md6  :  cmd+rdata+...
//7. md7  :  cmd+addr+rdata+...
//8. md8  :  cmd+addr+dummy+rdata+rdata+...
//9. md9  :  cmd+addr 
//定义设计中使用到的参数
parameter DLY = 1;
`ifdef SIM_OPEN
parameter IDLE      = "IDLE   ",
          SDCMD     = "SDCMD  ",
          SDDATA    = "SDDATA ",
          SDADDH    = "SDADDH ",
          SDADDM    = "SDADDM ",
          SDADDL    = "SDADDL ",
          SDDMY     = "SDDMY  ",
          RXDATA    = "RXDATA ";
reg[127:0] cur_state,next_state,cur_state_1d;
`else
parameter  IDLE  = 8'b00000000,
           SDCMD = 8'b00000001,
           SDDATA= 8'b00000010,
           SDADDH= 8'b00000100,
           SDADDM= 8'b00001000,
           SDADDL= 8'b00010000,
           SDDMY = 8'b00100000,
           RXDATA= 8'b01000000;           
reg[7:0] cur_state,next_state,cur_state_1d;
`endif
wire ST_change;
assign ST_change = cur_state_1d !=  cur_state;
always @(posedge clk) cur_state_1d <= #DLY cur_state;
//第一段状态转移
always@(posedge clk or negedge rst)begin
    if(rst == 1'b1)begin
		cur_state <= #DLY IDLE;
    end else begin
		cur_state <= #DLY next_state;
	end
end
//第二段产生下一状态 数据流检测序列状态变化
always@(*)begin
    if(rst == 1'b1)begin
        next_state =  IDLE;
    end
    else begin
	case(cur_state)
	    IDLE:
            begin
                if(md0|md4|md6|md7|md8|md9)
                    next_state =SDCMD;
                else
                    next_state =  IDLE;
            end
	    SDCMD:
            begin
                if((md4|md7|md8|md9) &ST_wrvld)
                    next_state =  SDADDH;
                else if((md0&ST_wrvld)|mdstop)
                    next_state =  IDLE;
                else if(md6&ST_wrvld)
                    next_state =  RXDATA;
                else
                    next_state =  SDCMD;
            end
	    SDDATA:
            begin
                if(mdstop)
                    next_state =  IDLE;
                else
                    next_state =  SDDATA;
            end
	    SDADDH:
            begin
                if(ST_wrvld)
                    next_state =  SDADDM;
                else
                    next_state =  SDADDH;
            end
	    SDADDM:
            begin
                if(ST_wrvld)
                    next_state =SDADDL;
                else
                    next_state =  SDADDM;
            end
	    SDADDL:
            begin
                if(md8&ST_wrvld)
                    next_state =  SDDMY;
                else if(md7 &ST_wrvld)
                    next_state =  RXDATA;
                else if(md4 &ST_wrvld)
                    next_state =  SDDATA;
                else if(md9 &ST_wrvld)
                    next_state =  IDLE;
                else
                    next_state =  SDADDL;
            end
	    SDDMY:
            begin
                if(ST_wrvld)
                    next_state =  RXDATA;
                else
                    next_state =  SDDMY;
            end
	    RXDATA:
            begin
                if(mdstop)
                    next_state =  IDLE;
                else
                    next_state =  RXDATA;
            end
	    default:begin
	        next_state =  IDLE;
	    end
	endcase
    end
end
// rx_data0
always @(posedge clk or posedge rst)begin 
    if(rst) 
        rx_data0      <=#1 8'b0;
    else if( (cur_state==RXDATA) & ST_wrvld) 
        rx_data0      <=#1 ST_rdata;
end
// ST_wdata
always @(posedge clk or posedge rst)begin 
    if(rst) 
        ST_wdata      <=#1 8'b0;
    else if(  cur_state==SDCMD  ) 
        ST_wdata      <=#1 cmd;
    else if(  cur_state==SDADDH  ) 
        ST_wdata      <=#1 add_h;
    else if(  cur_state==SDADDM  ) 
        ST_wdata      <=#1 add_m;
    else if(  cur_state==SDADDL  ) 
        ST_wdata      <=#1 add_l;
    else if(  cur_state==SDDATA  ) 
        ST_wdata      <=#1 tx_data0;
end
// ST_cs
always @(posedge clk or posedge rst)begin 
    if(rst) 
        ST_cs      <=#1 1'b0;
    else if(  cur_state==IDLE  ) 
        ST_cs      <=#1 1'b0;
    else if(  ST_change  ) 
        ST_cs      <=#1 1'b1;
    else if(  (cur_state==SDDATA)&wr_data_1d   ) 
        ST_cs      <=#1 1'b1;
    else if(  (cur_state==RXDATA)&rd_data   ) 
        ST_cs      <=#1 1'b1;
    else if(  ST_cs==1'b1  ) 
        ST_cs      <=#1 1'b0;
end
// SB_wr
always @(posedge clk or posedge rst)begin 
    if(rst) 
        ST_wr      <=#1 1'b1;
    else if(  cur_state==RXDATA  ) 
        ST_wr      <=#1 1'b0;
    else  
        ST_wr      <=#1 1'b1;
end
// state
wire busy;
reg  SDEmy;//发送空
reg  RXFull;//接收有数据
reg  WaitData;//等待写入新数据

assign  busy = (cur_state==IDLE)?1'b0:1'b1;
// WaitData
always @(posedge clk or posedge rst)begin 
    if(rst) 
        WaitData      <=#1 1'b0;
    else if((cur_state==SDDATA)&ST_wrvld  ) 
        WaitData      <=#1 1'b1;
    else  if((cur_state!=SDDATA))  
        WaitData      <=#1 1'b0;
end
// SDEmy
always @(posedge clk or posedge rst)begin 
    if(rst) 
        SDEmy      <=#1 1'b0;
    else if((cur_state==SDDATA)&ST_wrvld  ) 
        SDEmy      <=#1 1'b1;
    else  if(wr_data)  
        SDEmy      <=#1 1'b0;
end
// RXFull
always @(posedge clk or posedge rst)begin 
    if(rst) 
        RXFull      <=#1 1'b0;
    else if((cur_state==RXDATA)&ST_wrvld  ) 
        RXFull      <=#1 1'b1;
    else  if(rd_data)  
        RXFull      <=#1 1'b0;
end


always @(posedge clk or posedge rst)begin 
    if(rst) 
        state      <=#1 8'b0;
    else  
        state      <=#1 {4'b0,WaitData,RXFull,SDEmy,busy};
end

assign spi_csn_int = (cur_state==IDLE)?1'b1:1'b0;
//通过写入命令触发FPGA发送时序操作FLASH





//--------------单Byte数据发送 Single Byte 简称SB--------------
// 外部输出是SPI
// S25FL128L mode0|3  此代码实现mode0 
// FLASH在上升沿采样FPGA发出的数据,--要求:FPGA下降沿改变数据。
// FPGA内部在上升沿采样FLASH发出的数据。
// 内部输入输出
wire      SB_cs         ;// input 高脉冲
wire      SB_wr         ;// input
wire[7:0] SB_wdata      ;// input
wire[7:0] SB_rdata      ;// output
wire      SB_wrvld      ;// output



reg [7:0] SB_wdata_int  ;// 锁存
reg [7:0] SB_rdata_int  ;// 锁存
reg [3:0] SB_cnt_bit    ;// 关键的计数器,bit计数器 后面全依赖此计数器
reg [7:0] SB_cnt_div    ;// 关键的计数器,分频计数器 后面全依赖此计数器
wire      SB_cnt_bit_up ;// 用于更新SB_cnt_bit

assign SB_cs         =   ST_cs     ;
assign SB_wr         =   ST_wr     ;
assign SB_wdata      =   ST_wdata  ;
assign ST_wrvld      =   SB_wrvld  ;
assign ST_rdata      =   SB_rdata  ;

assign SB_cnt_bit_up =  (SB_cnt_div==div_max);
assign SB_rdata_up   =  (SB_cnt_div==div_max/2);
assign SB_wrvld      =  (SB_cnt_div==div_max/2) && (SB_cnt_bit==4'ha);
// SB_wdata_int
always @(posedge clk or posedge rst)begin
    if(rst) 
        SB_wdata_int <=#1 8'b0;
    else if(SB_cs)
        SB_wdata_int <=#1 SB_wdata;
    else if(SB_cnt_bit==4'b1)
        SB_wdata_int <=#1 SB_wdata_int;
    else if(SB_cnt_bit_up)
        SB_wdata_int <=#1 {SB_wdata_int[6:0],1'b0};//MSB
end
// reg[3:0] SB_cnt_bit;// 关键的计数器,后面全依赖此计数器
always @(posedge clk or posedge rst)begin 
    if(rst) 
        SB_cnt_bit      <=#1 4'b0;
    else if(SB_cs==1'b1) 
        SB_cnt_bit      <=#1 4'b1;
    else if(SB_cnt_bit==4'b0)
        SB_cnt_bit      <=#1 4'b0;
    else  if(SB_cnt_bit==4'ha  && SB_cnt_bit_up)
        SB_cnt_bit      <=#1 4'b0;        
    else  if(SB_cnt_bit_up)
        SB_cnt_bit      <=#1 SB_cnt_bit+1'b1;
end
// reg [7:0] SB_cnt_div;// 关键的计数器,分频计数器 后面全依赖此计数器
always @(posedge clk or posedge rst)begin 
    if(rst) 
        SB_cnt_div      <=#1 8'h0;
    else if(SB_cs==1'b1) 
        SB_cnt_div      <=#1 8'b1;
    else if(SB_cnt_bit_up)
        SB_cnt_div      <=#1 8'b0;
    else if( (SB_cnt_bit<=4'd10) && (SB_cnt_bit!=4'd0) ) 
        SB_cnt_div      <=#1 SB_cnt_div+1'b1;
    else 
        SB_cnt_div      <=#1 8'b0;
end
// spi_clk_int
always @(posedge clk or posedge rst)begin 
    if(rst) 
        spi_clk_int      <=#1 1'b0;
    else if( (SB_cnt_bit==4'b1) ||(SB_cnt_bit==4'ha) ) 
        spi_clk_int      <=#1 1'b0;
    else if( (SB_cnt_div==(div_max/2)) | (SB_cnt_div==div_max) ) 
        spi_clk_int      <=#1 ~spi_clk_int;
end
// SB_rdata_int
always @(posedge clk or posedge rst)begin
    if(rst) 
        SB_rdata_int <=#1 8'b0;
    else if(SB_rdata_up)
        SB_rdata_int <=#1 {SB_rdata_int[6:0],spi_din_int};//MSB
end

assign spi_dout_int =  SB_wdata_int[7]  ;
assign SB_rdata     =  SB_rdata_int     ;

assign spi_clk      =  spi_clk_int      ;
assign spi_csn      =  spi_csn_int      ;
assign spi_din_int  =  spi_din          ;
assign spi_dout     =  spi_dout_int     ;

endmodule

tb.v

`timescale 1ns/100ps
module tb (  );

reg clk =0,rst =1;
always clk =#5  ~clk;
reg           m_cs      = 0 ; 
reg           m_wr      = 0 ;
reg  [2:0]    m_addr    = 0 ;
reg  [7:0]    m_din     = 0 ;
wire [7:0]    m_dout        ;

wire spi_clk        ;
wire spi_csn        ;
wire spi_din        ;
wire spi_dout       ;


parameter   NOP     = 8'hff;
parameter   WREN    = 8'h06;//md0
parameter   WRDI    = 8'h04;//md0
parameter   RDSR    = 8'h05;//md6
parameter   WRSR    = 8'h01;//md6
parameter   RDCMD   = 8'h03;//md7
parameter   F_RD    = 8'h0B;//md8
parameter   PP      = 8'h02;//md4
parameter   SE      = 8'hD8;//md9
parameter   BE      = 8'hC7;//md0
parameter   DP      = 8'hB9;//md0
parameter   RES     = 8'hAB;//md0
parameter   RDID    = 8'h9F;//md6

//-------------产生spi_din------------
reg [23:0]  rand1=0;
initial begin
    while(1) begin
        @(negedge spi_clk);
        rand1 = {$random} % 100;
    end
end
assign spi_din   = rand1[0];

reg[7:0] rdata_sim;
reg [7:0] WaitData=8'h80;
reg [31:0] index ;
integer i;

initial begin
rst =1;
#100;
@(posedge clk);#1;
rst =0;
#100;
@(posedge clk);#1;

m_interfasc(1,3'h0,8'haa,rdata_sim);//数据aa
m_interfasc(1,3'h2,8'h01,rdata_sim);//addh
m_interfasc(1,3'h3,8'h02,rdata_sim);//addm
m_interfasc(1,3'h4,8'h03,rdata_sim);//addl
m_interfasc(1,3'h1,PP,rdata_sim);//cmd

// #600;
//pp
for(i=0;i<7;i=1+i) begin
index = 1;
while(index)
    begin
        #100;
        @(posedge clk);#1;
        m_interfasc(0,3'h1,8'h00,rdata_sim);//读状态
        if(rdata_sim[3:0]==4'b1011) 
            begin
                m_interfasc(1,3'h0,WaitData,rdata_sim);//数据
                index = 0;
            end
    end
    WaitData= WaitData>>1;
end

#1000;
m_interfasc(1,3'h1,NOP,rdata_sim);//cmd


#1000;
// F_RD
m_interfasc(1,3'h1,F_RD,rdata_sim);//cmd
for(i=0;i<10;i=1+i) begin
index = 1;
while(index)
    begin
        #100;
        @(posedge clk);#1;
        m_interfasc(0,3'h1,8'h00,rdata_sim);//读状态
        if(rdata_sim[3:0]==4'b0111) 
            begin
                m_interfasc(0,3'h0,8'h00,rdata_sim);//数据
                index = 0;
            end
    end

end
#1000;
m_interfasc(1,3'h1,NOP,rdata_sim);//cmd




end
spi_ctrl  U_spi_ctrl(  
.clk        (clk              ),//input       clk         ,
.rst        (rst              ),//input       rst         ,

.spi_clk    (spi_clk          ),//output      spi_clk     ,
.spi_csn    (spi_csn          ),//output      spi_csn     ,
.spi_din    (spi_din          ),//input       spi_din     ,
.spi_dout   (spi_dout         ),//output      spi_dout   ,

.m_cs       (m_cs             ),//output      m_cs        ,
.m_wr       (m_wr             ),//output      m_wr        ,
.m_addr     (m_addr           ),//output      m_addr      ,
.m_din      (m_din            ),//output      m_din       ,
.m_dout     (m_dout           )//output      m_dout
);



task  m_interfasc;//注意分号;  在第一行“task”语句中不能列出端口名称; 
input wr;
input [3:0] addr;
input [7:0] wdata;
output reg[7:0] rdata;
#100;
@(posedge tb.clk);#1;
tb.m_cs      = 0 ;
tb.m_wr      = 0 ;
tb.m_addr    = 0 ;
tb.m_din     = 0 ;
@(posedge tb.clk);#1;
tb.m_cs      = 1 ;
tb.m_wr      = wr ;
tb.m_addr    = addr ;
tb.m_din     = wdata ;
if(wr==0)  begin
    @(posedge tb.clk);#3;
    rdata        = tb.m_dout ;
    $display("%h",rdata);
end else rdata   = tb.m_dout ;

@(posedge tb.clk);#1;
tb.m_cs      = 0 ;
tb.m_wr      = 0 ;
tb.m_addr    = 0 ;
tb.m_din     = 0 ;
endtask 
 
endmodule

runtb.do

quit -sim
cd D:/gaop/SIM/spi_ctrl
vlib work
vmap work work
vlog  -novopt -incr -sv -work  work "./tb/*.v"

vsim  -novopt -t 1ns  -L work  work.tb

log -r  /*
do wave.do

run 100us
#



onerror {resume}
quietly WaveActivateNextPane {} 0
add wave -noupdate /tb/U_spi_ctrl/clk
add wave -noupdate /tb/U_spi_ctrl/rst
add wave -noupdate /tb/U_spi_ctrl/spi_clk
add wave -noupdate /tb/U_spi_ctrl/spi_csn
add wave -noupdate /tb/U_spi_ctrl/spi_din
add wave -noupdate /tb/U_spi_ctrl/spi_dout
add wave -noupdate -expand -group m_x /tb/U_spi_ctrl/m_cs
add wave -noupdate -expand -group m_x /tb/U_spi_ctrl/m_wr
add wave -noupdate -expand -group m_x /tb/U_spi_ctrl/m_addr
add wave -noupdate -expand -group m_x /tb/U_spi_ctrl/m_din
add wave -noupdate -expand -group m_x /tb/U_spi_ctrl/m_dout
add wave -noupdate -expand -group ST_x /tb/U_spi_ctrl/ST_cs
add wave -noupdate -expand -group ST_x /tb/U_spi_ctrl/ST_wr
add wave -noupdate -expand -group ST_x /tb/U_spi_ctrl/ST_wdata
add wave -noupdate -expand -group ST_x /tb/U_spi_ctrl/ST_wrvld
add wave -noupdate -expand -group ST_x /tb/U_spi_ctrl/ST_rdata
add wave -noupdate /tb/U_spi_ctrl/tx_data0
add wave -noupdate /tb/U_spi_ctrl/rx_data0
add wave -noupdate /tb/U_spi_ctrl/cmd
add wave -noupdate /tb/U_spi_ctrl/add_h
add wave -noupdate /tb/U_spi_ctrl/add_m
add wave -noupdate /tb/U_spi_ctrl/add_l
add wave -noupdate -radix ascii /tb/U_spi_ctrl/cur_state
add wave -noupdate /tb/U_spi_ctrl/ST_change
add wave -noupdate /tb/rdata_sim
add wave -noupdate /tb/U_spi_ctrl/state
add wave -noupdate /tb/U_spi_ctrl/busy
add wave -noupdate /tb/U_spi_ctrl/SDEmy
add wave -noupdate /tb/U_spi_ctrl/RXFull
add wave -noupdate /tb/U_spi_ctrl/WaitData
add wave -noupdate /tb/U_spi_ctrl/rd_stata
TreeUpdate [SetDefaultTree]
WaveRestoreCursors {{Cursor 1} {4052 ns} 0}
quietly wave cursor active 1
configure wave -namecolwidth 176
configure wave -valuecolwidth 102
configure wave -justifyvalue left
configure wave -signalnamewidth 1
configure wave -snapdistance 10
configure wave -datasetprefix 0
configure wave -rowmargin 4
configure wave -childrowmargin 2
configure wave -gridoffset 0
configure wave -gridperiod 1
configure wave -griddelta 40
configure wave -timeline 0
configure wave -timelineunits ps
update
WaveRestoreZoom {0 ns} {14036 ns}

  • 7
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_1615549892

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值