FPGA模块——SPI接口设计

SPI接口设计

  • SPI基础代码模版
  • 1. SPI协议与芯片交互接口
  • 2. SPI协议的控制器(状态机)

SPI基础代码模版

user输入: valid信号 , 要输出的值
输出 :一个周期读valid , 读到的值
在这里插入图片描述

module spi_drive#(parameter                           P_DATA_WIDTH        = 8 ,P_READ_DATA_WIDTH   = 8 , P_CPOL              = 0 ,P_CPHL              = 0 
)(                  input                               i_clk               ,input                               i_rst               ,output                              o_spi_clk           ,output                              o_spi_cs            ,output                              o_spi_mosi          ,input                               i_spi_miso          ,input   [P_DATA_WIDTH - 1 :0]       i_user_data         ,input                               i_user_valid        ,output                              o_user_ready        ,output  [P_READ_DATA_WIDTH - 1:0]   o_user_read_data    ,output                              o_user_read_valid   
);reg                                 ro_spi_clk          ;
reg                                 ro_spi_cs           ;
reg                                 ro_spi_mosi         ;
reg                                 ro_user_ready       ;
reg  [P_DATA_WIDTH - 1:0]           r_user_data         ;
reg                                 r_run               ;
reg  [15:0]                         r_cnt               ;
reg                                 r_spi_cnt           ;
reg  [P_READ_DATA_WIDTH - 1:0]      ro_user_read_data   ;
reg                                 ro_user_read_valid  ;
reg                                 r_run_1d            ;
/***************wire******************/
wire                                w_user_active       ;
wire                                w_run_negedge       ;/***************assign****************/
assign o_spi_clk            = ro_spi_clk            ;
assign o_spi_cs             = ro_spi_cs             ;
assign o_spi_mosi           = ro_spi_mosi           ;
assign o_user_ready         = ro_user_ready         ;
assign o_user_read_data     = ro_user_read_data     ;
assign o_user_read_valid    = ro_user_read_valid    ;
assign w_run_negedge        = !r_run & r_run_1d     ;/***************always****************/
assign w_user_active = i_user_valid & o_user_ready;always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_user_ready <='d1;else if(w_user_active)ro_user_ready <= 'd0;else if(w_run_negedge)ro_user_ready <= 'd1;else ro_user_ready <= ro_user_ready;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_user_data <= 'd0;else if(w_user_active)r_user_data <= i_user_data;else if(r_spi_cnt)r_user_data <= r_user_data << 1;else r_user_data <= r_user_data;    
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_run <= 'd0;else if(r_spi_cnt && r_cnt == 7)r_run <= 'd0;else if(w_user_active)r_run <= 'd1;else r_run <= r_run;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_run_1d <= 'd0;elser_run_1d <= r_run;
end
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_cnt <= 'd0;else if(r_spi_cnt && r_cnt == 7)r_cnt <= 'd0;else if(r_spi_cnt)r_cnt <= r_cnt + 1;else r_cnt <= r_cnt;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_spi_cnt <= 'd0;else if(r_run)r_spi_cnt <= r_spi_cnt + 1;else r_spi_cnt <= 'd0;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_spi_clk <= P_CPOL;else if(r_run)ro_spi_clk <= ~ro_spi_clk;else ro_spi_clk <= P_CPOL; 
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_spi_cs <= 'd1;else if(w_user_active)ro_spi_cs <= 'd0;else if(!r_run)ro_spi_cs <= 'd1;else ro_spi_cs <= ro_spi_cs;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_spi_mosi <= 'd0;else if(w_user_active)ro_spi_mosi <= i_user_data[P_DATA_WIDTH - 1];else if(r_spi_cnt)ro_spi_mosi <= r_user_data[P_DATA_WIDTH - 2];else ro_spi_mosi <= ro_spi_mosi;
end     always@(posedge ro_spi_clk,posedge i_rst)
beginif(i_rst)ro_user_read_data <= 'd0;elsero_user_read_data <= {ro_user_read_data[P_DATA_WIDTH - 2 : 0],i_spi_miso};endalways@(posedge i_clk,posedge i_rst) 
beginif(i_rst)ro_user_read_valid <= 'd0;else if(r_spi_cnt && r_cnt == 7)ro_user_read_valid <= 'd1;else ro_user_read_valid <= 'd0;
endendmodule

1. SPI协议与芯片交互接口

实际上就是加入了(芯片命令cmd + 寄存器地址) 的数据。读写操作的位数可以由spi控制器来控制,用于完成各种spi协议芯片的读写任务。

读数据
在这里插入图片描述

写数据
在这里插入图片描述

module spi_drive#(parameter                           P_DATA_WIDTH        = 8 ,P_OP_LEN            = 32,P_READ_DATA_WIDTH   = 8 , P_CPOL              = 0 ,P_CPHL              = 0 
)(                  input                               i_clk               ,//系统时钟input                               i_rst               ,//复位//spi驱动output                              o_spi_clk           ,//spi的clkoutput                              o_spi_cs            ,//spi的片选output                              o_spi_mosi          ,//spi的主机输出input                               i_spi_miso          ,//spi的从机输入//操作通道input   [P_OP_LEN - 1 :0]           i_user_op_data      ,//操作数据(命令8bit+地址24bit)input   [1 :0]                      i_user_op_type      ,//操作类型(读、写、指令)input   [15:0]                      i_user_op_len       ,//操作数据的长 32  或   8input   [15:0]                      i_user_clk_len      ,//时钟周期//握手信号input                               i_user_op_valid     ,//用户的有效信号output                              o_user_op_ready     ,//用户的准备信号//与上层交互通道input   [P_DATA_WIDTH - 1 :0]       i_user_write_data   ,//写的数据output                              o_user_write_req    ,//写数据请求//输出结果通道output  [P_READ_DATA_WIDTH - 1:0]   o_user_read_data    ,//读到的数据output                              o_user_read_valid    //读数据有效
);//3个状态:  命令/读/写
/***************parameter*************/
localparam                              P_OP_TYPE_INS   =   0,P_OP_READ       =   1,P_OP_WRITE      =   2;/***************reg*******************/
reg                                 ro_spi_clk          ;
reg                                 ro_spi_cs           ;
reg                                 ro_spi_mosi         ;
reg                                 ro_user_ready       ;
reg  [P_OP_LEN - 1:0]               r_user_op_data      ;
reg  [1 :0]                         r_user_op_type      ;
reg  [15:0]                         r_user_op_len       ;
reg  [15:0]                         r_user_clk_len      ;
reg  [P_DATA_WIDTH - 1:0]           r_user_data         ;
reg                                 r_run               ;
reg  [15:0]                         r_cnt               ;
reg                                 r_spi_cnt           ;
reg  [P_READ_DATA_WIDTH - 1:0]      ro_user_read_data   ;
reg                                 ro_user_read_valid  ;
reg                                 r_run_1d            ;
reg                                 ro_user_write_req   ;
reg                                 ro_user_write_req_1d;
reg  [15:0]                         r_write_cnt         ;
reg  [P_DATA_WIDTH - 1 :0]          r_user_write_data   ;
reg  [15:0]                          r_read_cnt          ;/***************wire******************/
wire                                w_user_active       ;
wire                                w_run_negedge       ;/***************component*************//***************assign****************/
assign o_spi_clk            = ro_spi_clk            ;
assign o_spi_cs             = ro_spi_cs             ;
assign o_spi_mosi           = ro_spi_mosi           ;
assign o_user_op_ready      = ro_user_ready         ;
assign o_user_read_data     = ro_user_read_data     ;
assign o_user_read_valid    = ro_user_read_valid    ;
assign w_run_negedge        = !r_run & r_run_1d     ;
assign o_user_write_req     = ro_user_write_req     ;/***************always****************/
assign w_user_active = i_user_op_valid & o_user_op_ready;//控制准备信号
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_user_ready <='d1;else if(w_user_active)ro_user_ready <= 'd0;else if(w_run_negedge)ro_user_ready <= 'd1;else ro_user_ready <= ro_user_ready;
end//操作总线,锁存USER的数据指令
always@(posedge i_clk,posedge i_rst)
beginif(i_rst) beginr_user_op_type <= 'd0;r_user_op_len  <= 'd0;r_user_clk_len <= 'd0;end else if(w_user_active) beginr_user_op_type <= i_user_op_type;r_user_op_len  <= i_user_op_len ;r_user_clk_len <= i_user_clk_len;end else begin r_user_op_type <= r_user_op_type;r_user_op_len  <= r_user_op_len ;r_user_clk_len <= r_user_clk_len;end   
end//激活后, 锁存操作数据
//下降沿, spi数据并转串
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_user_op_data <= 'd0;else if(w_user_active)r_user_op_data <= i_user_op_data;//指令8bit + 24bit地址else if(r_spi_cnt)//spi输出时,并转r_user_op_data <= r_user_op_data << 1;else r_user_op_data <= r_user_op_data;
end//run总线运行标志
//下降沿+spi的clk周期到达指定值 ,停止
//激活后,运行
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_run <= 'd0;else if(r_spi_cnt && r_cnt == r_user_clk_len - 1)r_run <= 'd0;else if(w_user_active)r_run <= 'd1;else r_run <= r_run;
end// run 打拍 获得下降沿
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_run_1d <= 'd0;elser_run_1d <= r_run;
end//spi时钟周期计数
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_cnt <= 'd0;else if(r_spi_cnt && r_cnt == r_user_clk_len - 1)r_cnt <= 'd0;else if(r_spi_cnt)r_cnt <= r_cnt + 1;else r_cnt <= r_cnt;
end//spi时钟计数,用于判断上升/下降沿
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_spi_cnt <= 'd0;else if(r_run)r_spi_cnt <= r_spi_cnt + 1;else r_spi_cnt <= 'd0;
end//spi时钟信号,run就开始翻转
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_spi_clk <= P_CPOL;else if(r_run)ro_spi_clk <= ~ro_spi_clk;else ro_spi_clk <= P_CPOL; 
end//spi片选信号  ,激活就片选
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_spi_cs <= 'd1;else if(w_user_active)ro_spi_cs <= 'd0;else if(!r_run)ro_spi_cs <= 'd1;else ro_spi_cs <= ro_spi_cs;
end//spi输出引脚
//1. 输出操作数据
//2. 输出要写出去的数据
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_spi_mosi <= 'd0;else if(w_user_active)//输出操作数据 最高位 指令+地址ro_spi_mosi <= i_user_op_data[P_OP_LEN - 1];//operationelse if(r_spi_cnt && r_cnt < r_user_op_len - 1)//依次输出操作数据次高位 ro_spi_mosi <= r_user_op_data[P_OP_LEN - 2];else if(r_user_op_type == P_OP_WRITE && r_spi_cnt)//串行输出写数据ro_spi_mosi <= r_user_write_data[7];else ro_spi_mosi <= ro_spi_mosi;
end     //
//2.(上升沿 + 周期计数器到P_OP_LEN -2  ||写数据计数==15  )   &&   写状态   
//    因为r_cnt是基于spi_clk(系统时钟/2)来计数的,!r_spi_cnt是作为i_clk时钟位置的判断   
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_user_write_req <= 'd0;else if(r_cnt >= r_user_clk_len - 5)ro_user_write_req <= 'd0;else if(((!r_spi_cnt && r_cnt == P_OP_LEN-2) || r_write_cnt == 15) &&  r_user_op_type == P_OP_WRITE )  ro_user_write_req <= 'd1;else ro_user_write_req <= 'd0;
end// 获得延时1个周期的写请求信号
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_user_write_req_1d <= 'd0;else ro_user_write_req_1d <= ro_user_write_req;
end// 1.用延时一个周期的写请求信号(此时外部数据已经更新),来锁存输入的要写的数据
// 2.spi_clk的下降沿 位移数据
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_user_write_data <= 'd0;else if(ro_user_write_req_1d)r_user_write_data <= i_user_write_data;else if(r_spi_cnt)r_user_write_data <= r_user_write_data << 1;else r_user_write_data <= r_user_write_data;
end//写请求后 r_write_cnt 写计数 0 ~ 15   用于下次产生写请求
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_write_cnt <= 'd0;else if(r_write_cnt == 15 || ro_spi_cs)r_write_cnt <= 'd0;else if(ro_user_write_req || r_write_cnt)r_write_cnt <= r_write_cnt + 1;else r_write_cnt <= r_write_cnt;
end
/*--------------------------读---------------------------------*/
//完成命令+地址的指令后,读数据
always@(posedge ro_spi_clk,posedge i_rst)
beginif(i_rst)ro_user_read_data <= 'd0;else if(r_cnt >= r_user_op_len )ro_user_read_data <= {ro_user_read_data[P_READ_DATA_WIDTH - 2 : 0],i_spi_miso};else ro_user_read_data <= ro_user_read_data;
end// 完成了命令+地址的指令后,读计数 0 ~ 8
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_read_cnt <= 'd0;else if(r_read_cnt == P_READ_DATA_WIDTH || ro_spi_cs)r_read_cnt <= 'd0;else if(r_spi_cnt && r_cnt >= r_user_op_len - 0 && r_user_op_type == P_OP_READ)r_read_cnt <= r_read_cnt + 1;else r_read_cnt <= r_read_cnt;
end//读数据有效信号
always@(posedge i_clk,posedge i_rst) 
beginif(i_rst)ro_user_read_valid <= 'd0;else if(r_spi_cnt && r_read_cnt == P_READ_DATA_WIDTH - 1 && r_user_op_type == P_OP_READ)ro_user_read_valid <= 'd1;else ro_user_read_valid <= 'd0;
endendmodule

2. SPI协议的控制器(状态机)

连续读出存在FIFO中的数据
握手信号

module flash_ctrl#(parameter                           P_DATA_WIDTH        = 8 ,//数据位宽P_OP_LEN            = 32,//指令长度P_READ_DATA_WIDTH   = 8 ,//读数据位宽P_CPOL              = 0 ,//空闲时时钟状态P_CPHL              = 0  //采集数据时钟沿
)(input                               i_clk                   ,//用户时钟input                               i_rst                   ,//用户复位/*--------用户接口--------*/    input  [1 :0]                       i_operation_type        ,//操作类型input  [23:0]                       i_operation_addr        ,//操作地址input  [8 :0]                       i_operation_num         ,//限制用户每次最多写256字节input                               i_operation_valid       ,//操作握手有效output                              o_operation_ready       ,//操作握手准备input  [P_DATA_WIDTH - 1 :0]        i_write_data            ,//写数据input                               i_write_sop             ,//写数据-开始信号input                               i_write_eop             ,//写数据-结束信号input                               i_write_valid           ,//写数据-有效信号output [P_DATA_WIDTH - 1 :0]        o_read_data             ,//读数据output                              o_read_sop              ,//读数据-开始信号output                              o_read_eop              ,//读数据-结束信号output                              o_read_valid            ,//读数据-有效信号/*--------驱动接口--------*/    output   [P_OP_LEN - 1 :0]          o_user_op_data          ,//操作数据(指令8bit+地址24bit)output   [1 :0]                     o_user_op_type          ,//操作类型(读、写、指令)output   [15:0]                     o_user_op_len           ,//操作数据的长度32、8output   [15:0]                     o_user_clk_len          ,//时钟周期output                              o_user_op_valid         ,//用户的有效信号input                               i_user_op_ready         ,//用户的准备信号output  [P_DATA_WIDTH - 1 :0]       o_user_write_data       ,//写数据input                               i_user_write_req        ,//写数据请求input   [P_READ_DATA_WIDTH - 1:0]   i_user_read_data        ,//读数据input                               i_user_read_valid        //读数据有效
);/***************function**************//***************parameter*************/
//用户接口操作类型
localparam                          P_TYPE_CLEAR    =   0   ,P_TYPE_WRITE    =   1   ,P_TYPE_READ     =   2   ;//SPI总线驱动器操作类型
localparam                          P_OP_TYPE_INS   =   0,P_OP_READ       =   1,P_OP_WRITE      =   2;//状态机状态
localparam                          P_IDLE          =   0   ,P_RUN           =   1   ,P_W_EN          =   2   ,P_W_INS         =   3   ,P_W_DATA        =   4   ,P_R_INS         =   5   ,P_R_DATA        =   6   ,P_CLEAR         =   7   ,P_BUSY          =   8   ,P_BUSY_CHECK    =   9   ,P_BUSY_WAIT     =   10  ;
/***************port******************/             /***************mechine***************/
//状态机
reg  [7 :0]                         r_st_current        ;
reg  [7 :0]                         r_st_next           ;
reg  [7 :0]                         r_st_cnt            ;/***************reg*******************/
reg  [1 :0]                         ri_operation_type   ;
reg  [23:0]                         ri_operation_addr   ;
reg  [8 :0]                         ri_operation_num    ;
reg  [P_DATA_WIDTH - 1 :0]          ri_write_data       ;
reg                                 ri_write_sop        ;
reg                                 ri_write_eop        ;
reg                                 ri_write_valid      ;
reg                                 r_user_ready_1d     ;
reg  [P_OP_LEN - 1 :0]              ro_user_op_data     ;
reg  [1 :0]                         ro_user_op_type     ;
reg  [15:0]                         ro_user_op_len      ;
reg  [15:0]                         ro_user_clk_len     ;
reg                                 ro_user_op_valid    ;
reg  [P_DATA_WIDTH - 1 :0]          ri_user_read_data   ;
reg                                 ri_user_read_valid  ;
reg                                 ro_operation_ready  ;
reg  [7 :0]                         ro_read_data        ;
reg                                 ro_read_sop         ;
reg                                 ro_read_eop         ;
reg                                 ro_read_valid       ;
reg                                 r_fifo_read_rden    ;
reg                                 r_fifo_read_rden_1d ;
reg                                 r_fifo_read_pos     ;
reg                                 r_fifo_read_emp_1d  ;
reg                                 r_fifo_read_wren    ;/***************wire******************/
wire                                w_operation_active  ;
wire                                w_user_ready_pos    ;
wire                                w_spi_drive_act     ;
wire                                w_fifo_read_empty   ;
wire [7 :0]                         w_read_data         ;/***************component*************///输入:用户写入想要 写进外设的数据
//输出:spi请求数据的时候,输出要写的数据FLASH_CTRL_FIFO_DATA FLASH_CTRL_FIFO_DATA_U0 (.clk      (i_clk              ),  .srst     (i_rst              ),  .din      (ri_write_data      ),  .wr_en    (ri_write_valid     ),  .rd_en    (i_user_write_req   ),  .dout     (o_user_write_data  ),  .full     (), .empty    ()  );//输入:spi读到的数据,写进去
//输出:用户要读取数据的时候,输出 FLASH_CTRL_FIFO_DATA FLASH_CTRL_FIFO_DATA_READ_U0 (.clk      (i_clk              ), .srst     (i_rst              ), .din      (ri_user_read_data  ), .wr_en    (r_fifo_read_wren   ), .rd_en    (r_fifo_read_rden   ), .dout     (w_read_data        ), .full     (),    .empty    (w_fifo_read_empty  )  );/***************assign****************/
assign w_operation_active   = i_operation_valid & o_operation_ready ;
assign w_user_ready_pos     = r_user_ready_1d & i_user_op_ready     ;
assign o_user_op_data       = ro_user_op_data                       ;
assign o_user_op_type       = ro_user_op_type                       ;
assign o_user_op_len        = ro_user_op_len                        ;
assign o_user_clk_len       = ro_user_clk_len                       ;
assign o_user_op_valid      = ro_user_op_valid                      ;
assign o_operation_ready    = ro_operation_ready                    ;
assign w_spi_drive_act      = o_user_op_valid & i_user_op_ready     ;
// assign o_read_data          = ro_read_data                          ; 
assign o_read_sop           = ro_read_sop                           ; 
assign o_read_eop           = ro_read_eop                           ; 
assign o_read_valid         = ro_read_valid                         ; 
assign o_read_data          = ro_read_data                          ;/***************always****************/
//状态机跳转
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_st_current <= P_IDLE;elser_st_current <= r_st_next;
end//跳转条件
always@(*)
begincase(r_st_current)P_IDLE          : r_st_next = w_operation_active    ? P_RUN    : P_IDLE    ;                //空闲状态,用户激活时跳转P_RUN           : r_st_next = ri_operation_type  == P_TYPE_READ ? P_R_INS  : P_W_EN     ;   //开始运行状态机,读/写P_W_EN          : r_st_next = w_spi_drive_act       ? ri_operation_type  == P_TYPE_WRITE ? P_W_INS : P_CLEAR         //判断是写数据还是擦除: P_W_EN    ;//写使能状态P_W_INS         : r_st_next = w_spi_drive_act       ? P_W_DATA : P_W_INS   ;                //写数据指令状态P_W_DATA        : r_st_next = i_user_op_ready       ? P_BUSY   : P_W_DATA  ;                //写数据P_R_INS         : r_st_next = w_spi_drive_act       ? P_R_DATA : P_R_INS   ;                //读数据指令状态P_R_DATA        : r_st_next = i_user_op_ready       ? P_BUSY   : P_R_DATA  ;                //读数据P_CLEAR         : r_st_next = w_spi_drive_act       ? P_BUSY   : P_CLEAR   ;                P_BUSY          : r_st_next = w_spi_drive_act       ? P_BUSY_CHECK : P_BUSY  ;              //读状态寄存器P_BUSY_CHECK    : r_st_next = ri_user_read_valid    ? i_user_read_data[0]   ? P_BUSY_WAIT : P_IDLE  : P_BUSY_CHECK        ;                                       //根据返回的状态值,判断是否繁忙P_BUSY_WAIT     : r_st_next = r_st_cnt == 255       ? P_BUSY       : P_BUSY_WAIT ;          //等待255个周期,重启读忙default         : r_st_next = P_W_EN; endcase
end  always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_st_cnt <= 'd0;else if(r_st_current != r_st_next)r_st_cnt <= 'd0;else r_st_cnt <= r_st_cnt + 1;
end
/*--------驱动逻辑--------*/
//第三段状态机
always@(posedge i_clk,posedge i_rst)
beginif(i_rst) beginro_user_op_data  <= 'd0;ro_user_op_type  <= 'd0;ro_user_op_len   <= 'd0;ro_user_clk_len  <= 'd0;ro_user_op_valid <= 'd0;end else if(r_st_current == P_W_EN) begin           //发送写使能指令ro_user_op_data  <= {8'h06,8'h00,8'h00,8'h00};ro_user_op_type  <= P_OP_TYPE_INS;ro_user_op_len   <= 8;ro_user_clk_len  <= 8;ro_user_op_valid <= 'd1;end else if(r_st_current == P_W_INS) begin          //发送写数据指令ro_user_op_data  <= {8'h02,ri_operation_addr};ro_user_op_type  <= P_OP_WRITE;ro_user_op_len   <= 32;ro_user_clk_len  <= 32 + 8 * ri_operation_num;ro_user_op_valid <= 'd1;end else if(r_st_current == P_R_INS) begin          //发送读数据指令ro_user_op_data  <= {8'h03,ri_operation_addr};ro_user_op_type  <= P_OP_READ;ro_user_op_len   <= 32;ro_user_clk_len  <= 32 + 8 * ri_operation_num;ro_user_op_valid <= 'd1;end else if(r_st_current == P_CLEAR) begin          //发送擦除指令ro_user_op_data  <= {8'h20,ri_operation_addr};ro_user_op_type  <= P_OP_TYPE_INS;ro_user_op_len   <= 32;ro_user_clk_len  <= 32;ro_user_op_valid <= 'd1;end else if(r_st_current == P_BUSY) begin           //发送读状态-BUSYro_user_op_data  <= {8'h05,24'd0};ro_user_op_type  <= P_OP_READ;ro_user_op_len   <= 8;ro_user_clk_len  <= 16;ro_user_op_valid <= 'd1;end else beginro_user_op_data  <= ro_user_op_data;ro_user_op_type  <= ro_user_op_type;ro_user_op_len   <= ro_user_op_len ;ro_user_clk_len  <= ro_user_clk_len;ro_user_op_valid <= 'd0;end
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_user_ready_1d <= 'd0;else r_user_ready_1d <= i_user_op_ready;
end// 锁存读到的数据和有效信号
always@(posedge i_clk,posedge i_rst)
beginif(i_rst) beginri_user_read_data  <= 'd0;ri_user_read_valid <= 'd0;end else beginri_user_read_data  <= i_user_read_data  ;ri_user_read_valid <= i_user_read_valid ;end
end/*--------用户逻辑--------*/
//握手激活,开始操作
always@(posedge i_clk,posedge i_rst)
beginif(i_rst) beginri_operation_type <= 'd0;ri_operation_addr <= 'd0;ri_operation_num  <= 'd0;end else if(w_operation_active) beginri_operation_type <= i_operation_type;ri_operation_addr <= i_operation_addr;ri_operation_num  <= i_operation_num ;end else  beginri_operation_type <= ri_operation_type;ri_operation_addr <= ri_operation_addr;ri_operation_num  <= ri_operation_num ;end
end//激活拉低准备信号
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_operation_ready <= 'd1;else if(r_st_next == P_IDLE)ro_operation_ready <= 'd1;else if(w_operation_active)   ro_operation_ready <= 'd0;else ro_operation_ready <= ro_operation_ready;
end//*------------------用户写入数据存入FIFO ------------------------*/
always@(posedge i_clk,posedge i_rst)
beginif(i_rst) beginri_write_data  <= 'd0;ri_write_sop   <= 'd0;ri_write_eop   <= 'd0;ri_write_valid <= 'd0;end else begin ri_write_data  <= i_write_data ;ri_write_sop   <= i_write_sop  ;ri_write_eop   <= i_write_eop  ;ri_write_valid <= i_write_valid;end
end/*-----------------------从FIFO中读数据---------------------------------*/
//从FIFO中读数据使能开启
always@(posedge i_clk,posedge i_rst)
beginif(i_rst) r_fifo_read_rden <= 'd0;else if(w_fifo_read_empty)r_fifo_read_rden <= 'd0;else if(r_st_current == P_R_DATA && r_st_next != P_R_DATA)r_fifo_read_rden <= 'd1;else r_fifo_read_rden <= r_fifo_read_rden;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst) r_fifo_read_rden_1d <= 'd0;else r_fifo_read_rden_1d <= r_fifo_read_rden;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_fifo_read_pos <= 'd0;else r_fifo_read_pos <= !r_fifo_read_rden_1d && r_fifo_read_rden;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_fifo_read_emp_1d <= 'd0;elser_fifo_read_emp_1d <= w_fifo_read_empty;
end//开始fifo数据输出
always@(posedge i_clk,posedge i_rst)
beginif(i_rst) ro_read_sop <= 'd0;else if(r_fifo_read_pos)ro_read_sop <= 'd1;elsero_read_sop <= 'd0;
end//结束fifo数据输出
always@(posedge i_clk,posedge i_rst)
beginif(i_rst) ro_read_eop <= 'd0;else if(w_fifo_read_empty && !r_fifo_read_emp_1d && ro_read_valid)ro_read_eop <= 'd1;else ro_read_eop <= 'd0;
end //fifo读有效信号指示        
always@(posedge i_clk,posedge i_rst)
beginif(i_rst) ro_read_valid <= 'd0;else if(ro_read_eop)ro_read_valid <= 'd0;else if(r_fifo_read_pos)ro_read_valid <= 'd1;else ro_read_valid <= ro_read_valid;
end
//从FIFO中读出的数据
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_read_data <= 'd0;else ro_read_data <= w_read_data;
end//---------------------------将读到的数据写入FIFO------------------------------*/
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_fifo_read_wren <= 'd0;else if(r_st_current == P_R_DATA)r_fifo_read_wren <= i_user_read_valid;else r_fifo_read_wren <= 'd0;
end
endmodule

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/681841.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue源码系列讲解——模板编译篇【三】(HTML解析器)

目录 1. 前言 2. HTML解析器内部运行流程 3. 如何解析不同的内容 3.1 解析HTML注释 3.2 解析条件注释 3.3 解析DOCTYPE 3.4 解析开始标签 3.5 解析结束标签 3.6 解析文本 4. 如何保证AST节点层级关系 5. 回归源码 5.1 HTML解析器源码 5.2 parseEndTag函数源码 6. …

前端(二十七)——封装指南:Axios接口、常用功能、Vue和React中的封装技术

&#x1f60a;博主&#xff1a;小猫娃来啦 &#x1f60a;文章核心&#xff1a;前端封装指南&#xff1a;Axios接口、常用功能、Vue和React中的封装技术 本文目录 小引前端封装以真实项目举个例子 Axios接口封装常用功能封装封装 Vue中的封装技术React中的封装技术Vue和React封装…

Istio复习总结:xDS协议、Istio Pilot源码、Istio落地问题总结

1、xDS协议 1&#xff09;、xDS是什么 xDS是一类发现服务的总称&#xff0c;包含LDS、RDS、CDS、EDS以及SDS。Envoy通过xDS API可以动态获取Listener&#xff08;监听器&#xff09;、Route&#xff08;路由&#xff09;、Cluster&#xff08;集群&#xff09;、Endpoint&…

Map和Set(哈希表)

目录 map&#xff1a; map说明&#xff1a; Map.Entry的说明&#xff1a;,v> Map 的常用方法: 演示&#xff1a; 注意&#xff1a; TreeMap和HashMap的区别 Set&#xff1a; 常见方法说明&#xff1a; 注意&#xff1a; TreeSet和HashSet的区别 哈希表: 冲突&a…

19 删除链表的倒数第 N 个结点

19. 删除链表的倒数第 N 个结点 中等 相关标签 相关企业 提示 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 这段代码使用了双指针的方法&#xff0c;其中一个指针先走 n 步&#xff0c;然后两个指针一起走&#xff0c;直到第一…

一文看懂春晚刘谦魔术

魔术步骤 step1: 准备4张牌,跟随魔术步骤,见证奇迹 step2: 将4张牌平均斯成两份,并叠在一起 step3: 将牌堆顶数量为名字字数的牌移到牌堆底 step4: 将前三张牌放在牌堆中间并取出牌堆顶的一张牌放到屁股下 step5: 南方人、北方人、不确定分别取顶上的1/2/3张牌插入牌堆…

17 ABCD数码管显示与动态扫描原理

1. 驱动八位数码管循环点亮 1.1 数码管结构图 数码管有两种结构&#xff0c;共阴极和共阳极&#xff0c;ACX720板上的是共阳极数码管&#xff0c;低电平点亮。 1.2 三位数码管等效电路图 为了节约I/O接口&#xff0c;各个数码管的各段发光管被连在一起&#xff0c;通过sel端…

《数字图像处理-OpenCV/Python》连载:形态学图像处理

《数字图像处理-OpenCV/Python》连载&#xff1a;形态学图像处理 本书京东 优惠购书链接 https://item.jd.com/14098452.html 本书CSDN 独家连载专栏 https://blog.csdn.net/youcans/category_12418787.html 第 12 章 形态学图像处理 形态学图像处理是基于形状的图像处理&…

Android的常用Drawable讲解

今天来讲讲Android开发中水都绕不开的东西----drawable。最常使用的莫过于通过XML所声明的Drawable作为View背景&#xff0c;通过代码创建的应用场景则较少。其有着使用简单&#xff0c;比自定义view的成本要低的特点。同时&#xff0c;非图片类型的drawable占用空间较小&#…

【教程】Kotlin语言学习笔记(一)——认识Kotlin(持续更新)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【Kotlin语言学习】系列文章 第一章 《认识Kotlin》 文章目录 【Kotlin语言学习】系列文章一、Kotlin介绍二、学习路径 一、…

Leetcode 1035 不相交的线

题意理解&#xff1a; 在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 现在&#xff0c;可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线&#xff0c;这些直线需要同时满足满足&#xff1a; nums1[i] nums2[j]且绘制的直线不与任何其他连线&#xff…

面向对象2:继承

目录 2.1继承 2.2 继承的好处 2.3 权限修饰符 2.4 单继承、Object 2.5 方法重写 2.6 子类中访问成员的特点 2.7 子类中访问构造器的特点 面向对象1&#xff1a;静态 2.1继承 向对象编程之所以能够能够被广大开发者认可&#xff0c;有一个非常重要的原因&#xff0c;是…

1921:【02NOIP普及组】过河卒

1921&#xff1a;【02NOIP普及组】过河卒 【题目描述】 如图&#xff0c;A点有一个过河卒&#xff0c;需要走到目标B点。卒行走的规则&#xff1a;可以向下、或者向右。 同时在棋盘上的任一点有一个对方的马&#xff08;如上图的C点&#xff09;&#xff0c;该马所在的点和所有…

联合体与枚举

联合体与枚举 联合体枚举问题 联合体 联合体也是由一个或多个成员构成的数据类型,它最大的特点是只为最大的一个成员开辟空间,其他成员共用这个空间,这个东西也叫共用体!!! union Un {char c;int i; };int main() {union Un un { 0 };un.c 0x01;//先为最大的成员开辟空间un.…

开源免费的Linux服务器管理面板分享

开源免费的Linux服务器管理面板分享 一、1Panel1.1 1Panel 简介1.2 1Panel特点1.3 1Panel面板首页1.4 1Panel使用体验 二、webmin2.1 webmin简介2.2 webmin特点2.3 webmin首页2.4 webmin使用体验 三、Cockpit3.1 Cockpit简介3.2 Cockpit特点3.3 Cockpit首页3.4 Cockpit使用体验…

C++ //练习 6.10 编写一个函数,使用指针形参交换两个整数的值。在代码中调用该函数并输出交换后的结果,以此验证函数的正确性。

C Primer&#xff08;第5版&#xff09; 练习 6.10 练习 6.10 编写一个函数&#xff0c;使用指针形参交换两个整数的值。在代码中调用该函数并输出交换后的结果&#xff0c;以此验证函数的正确性。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&…

酒店押金预授权怎么开通?微信酒店押金+房态+门锁关联 +电子押金单 解决方案

一、酒店押金管理有哪些&#xff1f; 1.渠道有银行预授权 2.微信押金支付 3.酒店押金系统 4.支付押金管理 二、银行预授权模式 酒店押金预授权通常是在客人办理入住时进行的&#xff0c;酒店会要求客人提供信用卡或借记卡的卡号、有效期、持卡人姓名等信息&#xff0c;然后…

第3讲 小程序TabBar搭建

tabBar&#xff0c;底部三个tab&#xff0c;对应三个页面&#xff0c;创建投票&#xff0c;关于锋哥&#xff0c;我的。 新建三个页面 pages.json 页面定义 "pages": [ //pages数组中第一项表示应用启动页&#xff0c;参考&#xff1a;https://uniapp.dcloud.io/col…

蓝桥杯嵌入式第11届真题(完成) STM32G431

蓝桥杯嵌入式第11届真题(完成) STM32G431 题目 代码 程序和之前的大同小异&#xff0c;不过多解释 main.c /* USER CODE BEGIN Header */ /********************************************************************************* file : main.c* brief :…

AtCoder Beginner Contest 340 C - Divide and Divide【打表推公式】

原题链接&#xff1a;https://atcoder.jp/contests/abc340/tasks/abc340_c Time Limit: 2 sec / Memory Limit: 1024 MB Score: 300 points 问题陈述 黑板上写着一个整数 N。 高桥将重复下面的一系列操作&#xff0c;直到所有不小于2的整数都从黑板上移除&#xff1a; 选择…