zoukankan      html  css  js  c++  java
  • 自定义AXI总线形式SPI接口IP核,点亮OLED

    一、前言

      最近花费很多精力在算法仿真和实现上,外设接口的调试略有生疏。本文以FPGA控制OLED中的SPI接口为例,重新夯实下基础。重点内容为SPI时序的RTL设计以及AXI-Lite总线分析。当然做些项目时可以直接调用Xilinx提供的SPI IP核,这里仅出于练习的目的考虑。

    二、接口时序分析

       本项目用的OLED型号为UG-2832HSWEG04,核心控制器是SSD1306。该芯片支持并口、I2C以及SPI接口,这里采用4线SPI作为数据总线。4线SPI接口包括:

    SCLK:串行时钟,SSD1306上升沿采集数据

    SDIN:串行数据输入,数据顺序为MSB

    D/C:数据命令控制,高电平为数据,低电平为控制命令

    CS:片选信号,低电平有效

    时序图如下:

       片选信号有效期间,每第8个时钟周期上升沿时刻,控制芯片会采样D/C并同时将进入的一字节数据写入到显示缓存GDDRAM或控制寄存器中。

      根据datasheet中的AC Characteristics中参数,选择SPI串行时钟周期为200ns,占空比为50%以保证足够的时序裕量。此时传输速率为:5MHZ/8 = 625KHZ。

    三、SPI接口模块设计

       根据上述分析,很容易可以设计出用于传输数据或命令的SPI时序接口模块。接口定义如下:

    用户侧:clk rst_n com din busy,依次是系统时钟,复位,指令信号(1为发送控制信息,2则发送数据),待传输字节以及忙等待指示。

    外设侧:SCLK SDIN CS D/C

      逻辑状态分为:IDLE SEND和DONE,具体时序如下:

      直接对照上图编写HDL:

      1 `timescale 1ns / 1ps
      2 
      3 module spi_4wire#(parameter DIV_CYC = 20)
      4 (
      5     //本地接口
      6     input clk,//100MHZ
      7     input rst_n,
      8     input [2-1:0] com,//1发送控制信息,2发送数据 其他无效
      9     input [8-1:0] din,
     10     output busy,
     11     //芯片侧接口
     12     output reg sclk = 0,
     13     output reg sdin = 0,
     14     output reg cs = 1'b1,
     15     output reg dc = 0//1是数据,0是控制命令
     16     );
     17 //**************************参数定义********************************************
     18     function integer clogb2 (input integer bit_depth);
     19       begin
     20         for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
     21           bit_depth = bit_depth >> 1;
     22       end
     23     endfunction
     24 
     25 localparam DIV_CNT_W = clogb2(DIV_CYC-1),
     26            BIT_CNT_W = clogb2(8-1);
     27 
     28 localparam  IDLE = 0 ,
     29             SEND = 1 ,
     30             DONE = 2 ;       
     31 
     32 //************************变量定义****************************************
     33 reg [ (DIV_CNT_W-1):0]  div_cnt =0    ;
     34 wire        add_div_cnt ;
     35 wire        end_div_cnt ;
     36 reg [ (BIT_CNT_W-1):0]  bit_cnt =0    ;
     37 wire        add_bit_cnt ;
     38 wire        end_bit_cnt ;
     39 reg [2-1:0] state_c = IDLE,state_n = IDLE;
     40 wire idle2send,send2done,done2idle;
     41 reg [8+2-1:0] data_tmp = 0;
     42 wire din_vld;
     43 wire start_send;
     44 reg busy_flag = 0;
     45 //************************逻辑****************************************
     46 //sclk时钟分频 T:10ns --> 200ns 20倍
     47 //分频计数器
     48 always @(posedge clk or negedge rst_n) begin 
     49     if (rst_n==0) begin
     50         div_cnt <= 0; 
     51     end
     52     else if(add_div_cnt) begin
     53         if(end_div_cnt)
     54             div_cnt <= 0; 
     55         else
     56             div_cnt <= div_cnt+1 ;
     57    end
     58 end
     59 assign add_div_cnt = (1);
     60 assign end_div_cnt = add_div_cnt  && div_cnt == (DIV_CYC)-1 ;
     61 
     62 //比特计数器
     63 always @(posedge clk or negedge rst_n) begin 
     64     if (rst_n==0) begin
     65         bit_cnt <= 0; 
     66     end
     67     else if(add_bit_cnt) begin
     68         if(end_bit_cnt)
     69             bit_cnt <= 0; 
     70         else
     71             bit_cnt <= bit_cnt+1 ;
     72    end
     73 end
     74 assign add_bit_cnt = (state_c == SEND && end_div_cnt);
     75 assign end_bit_cnt = add_bit_cnt  && bit_cnt == (8)-1 ;
     76 
     77 //控制状态机
     78 always @(posedge clk or negedge rst_n) begin 
     79     if (rst_n==0) begin
     80         state_c <= IDLE ;
     81     end
     82     else begin
     83         state_c <= state_n;
     84    end
     85 end
     86 
     87 always @(*) begin 
     88     case(state_c)  
     89         IDLE :begin
     90             if(idle2send ) 
     91                 state_n = SEND ;
     92             else 
     93                 state_n = state_c ;
     94         end
     95         SEND :begin
     96             if(send2done ) 
     97                 state_n = DONE ;
     98             else 
     99                 state_n = state_c ;
    100         end
    101         DONE :begin
    102             if(done2idle ) 
    103                 state_n = IDLE ;
    104             else 
    105                 state_n = state_c ;
    106         end
    107         default : state_n = IDLE ;
    108     endcase
    109 end
    110 
    111 assign idle2send  = state_c==IDLE && (end_div_cnt && data_tmp[10-1 -:2] != 0);
    112 assign send2done  = state_c==SEND && (end_bit_cnt);
    113 assign done2idle  = state_c==DONE && (end_div_cnt);
    114 
    115 
    116 //输入命令/数据寄存
    117 always  @(posedge clk or negedge rst_n)begin
    118     if(rst_n==1'b0)begin
    119         data_tmp <= 0;
    120     end
    121     else if(din_vld)begin
    122         data_tmp <= {com,din};
    123     end
    124     else if(done2idle)begin
    125         data_tmp <= 0;
    126     end
    127 end
    128 
    129 assign din_vld = busy_flag == 1'b0 && com != 2'd0;
    130 
    131 //SPI输出信号
    132 always  @(posedge clk or negedge rst_n)begin
    133     if(rst_n==1'b0)begin
    134         sdin <= 0;
    135     end
    136     else if(add_bit_cnt)begin
    137         sdin <= data_tmp[8-1-bit_cnt];
    138     end
    139 end
    140 
    141 always  @(posedge clk or negedge rst_n)begin
    142     if(rst_n==1'b0)begin
    143         cs <= 1'b1;
    144     end
    145     else if(start_send)begin
    146         cs <= 0;
    147     end
    148     else if(done2idle)begin
    149         cs <= 1'b1;
    150     end
    151 end
    152 
    153 assign start_send = add_bit_cnt && bit_cnt == 0;
    154 
    155 always  @(posedge clk or negedge rst_n)begin
    156     if(rst_n==1'b0)begin
    157         dc <= 1'b0;
    158     end
    159     else if(start_send)begin
    160         case(data_tmp[9:8])//1发送控制信息,2发送数据 其他无效
    161             2'd1:dc <= 1'b0;//1是数据,0是控制命令
    162             2'd2:dc <= 1'b1;
    163             default:dc <= 1'b0;
    164         endcase
    165     end
    166     else if(done2idle)begin
    167         dc <= 0;
    168     end
    169 end
    170 
    171 //SCLK
    172 always  @(posedge clk or negedge rst_n)begin
    173     if(rst_n==1'b0)begin
    174         sclk <= 0;
    175     end
    176     else if(add_div_cnt && div_cnt == DIV_CYC/2-1)begin
    177         sclk <= 1'b1;
    178     end
    179     else if(end_div_cnt)begin
    180         sclk <= 0;
    181     end
    182 end
    183 
    184 //本地侧输出
    185 always@(posedge clk or negedge rst_n)begin
    186     if(rst_n == 0)begin
    187         busy_flag <= 0;
    188     end
    189     else if(din_vld)begin
    190         busy_flag <= 1'b1;
    191     end
    192     else if(done2idle)begin
    193         busy_flag <= 0;
    194     end
    195 end
    196 
    197 assign busy = busy_flag | din_vld;
    198 
    199 
    200 endmodule
    spi_4wire

       逻辑非常清晰,分频计数器控制和比特计数器作为整个时序接口模块的跳变时刻。状态机决定SPI中的CS SCLK SDIN D/C信号变化。比较重要的是busy接口信号,该信号为后续衔接AXI总线作准备。

    四、AXI(AXI-Lite)总线详解及接口封装

       核心逻辑设计完成,最后是总线接口封装工作。由于SPI本地侧发送一个字节数据后需要很长一段时间才能将其转换成的串行数据发送完毕,因此使用AXI-Lite总线即可满足数据传输需求。利用VIVADO IP封装器自带的AXI总线模板可以简化设计,看下总线接口:

    1 写地址通道:

    S_AXI_AWADDR:写地址

    S_AXI_AWPORT:写地址保护类型

    S_AXI_AWVALID:写地址有效

    S_AXI_AWREADY:写地址准备

    2 写数据通道:

    S_AXI_WDATA:写数据

    S_AXI_WSTRB:指示对应字节是有效数据还是位置信息(1为有效数据)

    S_AXI_WVALID:写有效

    S_AXI_WREADY:写数据准备

    3 写响应通道:

    S_AXI_BRESP:指示写传输状态

    S_AXI_BVALID:写响应有效指示

    S_AXI_BREADY:响应准备

    4 读地址通道:

    S_AXI_ARADDR:读地址

    S_AXI_ARPROT:读地址保护类型

    S_AXI_ARVALID:读地址有效指示

    S_AXI_ARREADY:读地址准备

    5 读数据通道:

    S_AXI_RDATA:读数据

    S_AXI_RVALID:读数据有效

    S_AXI_RREADY:读数据准备

      可以看出,每个通道无论有多少信号,数据信息,有效指示以及准备就绪信号是必然存在的,这三个信号能够完成最基本的总线握手传输。

      这里将之前设计的SPI接口模块例化在AXI Wrapper(spi_4wire_w_v1_0)中,并添加与Slave接口模块(spi_4wire_w_v1_0_S00_AXI)的连接信号。其中Slave接口模块内ready信号默认是在ready为0且valid为1时拉高一个时钟周期,但应考虑SPI模块是否准备就绪或上一个数据传输完成,改动后AXI wrapper以及AXI-Lite Slave接口逻辑如下:

    AXI Wrapper:

      1 `timescale 1 ns / 1 ps
      2 
      3     module spi_4wire_w_v1_0 #
      4     (
      5         // Users to add parameters here
      6 
      7         // User parameters ends
      8         // Do not modify the parameters beyond this line
      9 
     10 
     11         // Parameters of Axi Slave Bus Interface S00_AXI
     12         parameter integer C_S00_AXI_DATA_WIDTH    = 32,
     13         parameter integer C_S00_AXI_ADDR_WIDTH    = 4
     14     )
     15     (
     16         // Users to add ports here
     17         //SPI signals
     18         output sclk,
     19         output sdin,
     20         output cs,
     21         output dc,
     22         // User ports ends
     23         // Do not modify the ports beyond this line
     24 
     25 
     26         // Ports of Axi Slave Bus Interface S00_AXI
     27         input wire  s00_axi_aclk,
     28         input wire  s00_axi_aresetn,
     29         input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_awaddr,
     30         input wire [2 : 0] s00_axi_awprot,
     31         input wire  s00_axi_awvalid,
     32         output wire  s00_axi_awready,
     33         input wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_wdata,
     34         input wire [(C_S00_AXI_DATA_WIDTH/8)-1 : 0] s00_axi_wstrb,
     35         input wire  s00_axi_wvalid,
     36         output wire  s00_axi_wready,
     37         output wire [1 : 0] s00_axi_bresp,
     38         output wire  s00_axi_bvalid,
     39         input wire  s00_axi_bready,
     40         input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_araddr,
     41         input wire [2 : 0] s00_axi_arprot,
     42         input wire  s00_axi_arvalid,
     43         output wire  s00_axi_arready,
     44         output wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_rdata,
     45         output wire [1 : 0] s00_axi_rresp,
     46         output wire  s00_axi_rvalid,
     47         input wire  s00_axi_rready    
     48     );
     49     
     50     reg rst_n = 1'b1;
     51     wire [8-1:0] din;
     52     wire [2-1:0] com;
     53     wire busy;
     54     wire [16-1:0] data;
     55     wire data_vld;
     56     
     57 // Instantiation of Axi Bus Interface S00_AXI
     58     spi_4wire_w_v1_0_S00_AXI # ( 
     59         .C_S_AXI_DATA_WIDTH(C_S00_AXI_DATA_WIDTH),
     60         .C_S_AXI_ADDR_WIDTH(C_S00_AXI_ADDR_WIDTH)
     61     ) spi_4wire_w_v1_0_S00_AXI_inst (
     62         .local_dout(data),
     63         .local_dout_vld(data_vld),
     64         .local_busy(busy),
     65         
     66         .S_AXI_ACLK(s00_axi_aclk),
     67         .S_AXI_ARESETN(s00_axi_aresetn),
     68         .S_AXI_AWADDR(s00_axi_awaddr),
     69         .S_AXI_AWPROT(s00_axi_awprot),
     70         .S_AXI_AWVALID(s00_axi_awvalid),
     71         .S_AXI_AWREADY(s00_axi_awready),
     72         .S_AXI_WDATA(s00_axi_wdata),
     73         .S_AXI_WSTRB(s00_axi_wstrb),
     74         .S_AXI_WVALID(s00_axi_wvalid),
     75         .S_AXI_WREADY(s00_axi_wready),
     76         .S_AXI_BRESP(s00_axi_bresp),
     77         .S_AXI_BVALID(s00_axi_bvalid),
     78         .S_AXI_BREADY(s00_axi_bready),
     79         .S_AXI_ARADDR(s00_axi_araddr),
     80         .S_AXI_ARPROT(s00_axi_arprot),
     81         .S_AXI_ARVALID(s00_axi_arvalid),
     82         .S_AXI_ARREADY(s00_axi_arready),
     83         .S_AXI_RDATA(s00_axi_rdata),
     84         .S_AXI_RRESP(s00_axi_rresp),
     85         .S_AXI_RVALID(s00_axi_rvalid),
     86         .S_AXI_RREADY(s00_axi_rready)
     87     );
     88 
     89     // Add user logic here
     90     spi_4wire#(.DIV_CYC(20))
     91     uut
     92     (
     93        .clk       (s00_axi_aclk)  ,//100MHZ
     94        .rst_n     (rst_n)  ,
     95        .com       (com)  ,//1发送控制信息,2发送数据 其他无效
     96        .din       (din)  ,
     97        .busy      (busy)  ,//rdy
     98        
     99        .sclk      (sclk)  ,
    100        .sdin      (sdin)  ,
    101        .cs        (cs)  ,
    102        .dc        (dc)  //1是数据,0是控制命令
    103         );
    104         
    105     always@(posedge s00_axi_aclk)begin
    106         rst_n <= s00_axi_aresetn;
    107     end     
    108     
    109     assign com = data_vld ? data[9:8] : 2'd0;
    110     assign din = data_vld ? data[8-1:0] : 8'd0;
    111     // User logic ends
    112 
    113     endmodule
    spi_4wire_w_v1_0

    Slave接口模块:

      1 `timescale 1 ns / 1 ps
      2 
      3     module spi_4wire_w_v1_0_S00_AXI #
      4     (
      5         // Users to add parameters here
      6 
      7         // User parameters ends
      8         // Do not modify the parameters beyond this line
      9 
     10         // Width of S_AXI data bus
     11         parameter integer C_S_AXI_DATA_WIDTH    = 32,
     12         // Width of S_AXI address bus
     13         parameter integer C_S_AXI_ADDR_WIDTH    = 4
     14     )
     15     (
     16         // Users to add ports here
     17         output [C_S_AXI_DATA_WIDTH-1:0] local_dout,
     18         output reg local_dout_vld = 0,
     19         input local_busy,
     20         // User ports ends
     21         // Do not modify the ports beyond this line
     22 
     23         // Global Clock Signal
     24         input wire  S_AXI_ACLK,
     25         // Global Reset Signal. This Signal is Active LOW
     26         input wire  S_AXI_ARESETN,
     27         // Write address (issued by master, acceped by Slave)
     28         input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
     29         // Write channel Protection type. This signal indicates the
     30             // privilege and security level of the transaction, and whether
     31             // the transaction is a data access or an instruction access.
     32         input wire [2 : 0] S_AXI_AWPROT,
     33         // Write address valid. This signal indicates that the master signaling
     34             // valid write address and control information.
     35         input wire  S_AXI_AWVALID,
     36         // Write address ready. This signal indicates that the slave is ready
     37             // to accept an address and associated control signals.
     38         output wire  S_AXI_AWREADY,
     39         // Write data (issued by master, acceped by Slave) 
     40         input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
     41         // Write strobes. This signal indicates which byte lanes hold
     42             // valid data. There is one write strobe bit for each eight
     43             // bits of the write data bus.    
     44         input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
     45         // Write valid. This signal indicates that valid write
     46             // data and strobes are available.
     47         input wire  S_AXI_WVALID,
     48         // Write ready. This signal indicates that the slave
     49             // can accept the write data.
     50         output wire  S_AXI_WREADY,
     51         // Write response. This signal indicates the status
     52             // of the write transaction.
     53         output wire [1 : 0] S_AXI_BRESP,
     54         // Write response valid. This signal indicates that the channel
     55             // is signaling a valid write response.
     56         output wire  S_AXI_BVALID,
     57         // Response ready. This signal indicates that the master
     58             // can accept a write response.
     59         input wire  S_AXI_BREADY,
     60         // Read address (issued by master, acceped by Slave)
     61         input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
     62         // Protection type. This signal indicates the privilege
     63             // and security level of the transaction, and whether the
     64             // transaction is a data access or an instruction access.
     65         input wire [2 : 0] S_AXI_ARPROT,
     66         // Read address valid. This signal indicates that the channel
     67             // is signaling valid read address and control information.
     68         input wire  S_AXI_ARVALID,
     69         // Read address ready. This signal indicates that the slave is
     70             // ready to accept an address and associated control signals.
     71         output wire  S_AXI_ARREADY,
     72         // Read data (issued by slave)
     73         output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
     74         // Read response. This signal indicates the status of the
     75             // read transfer.
     76         output wire [1 : 0] S_AXI_RRESP,
     77         // Read valid. This signal indicates that the channel is
     78             // signaling the required read data.
     79         output wire  S_AXI_RVALID,
     80         // Read ready. This signal indicates that the master can
     81             // accept the read data and response information.
     82         input wire  S_AXI_RREADY
     83     );
     84 
     85     // AXI4LITE signals
     86     reg [C_S_AXI_ADDR_WIDTH-1 : 0]     axi_awaddr;
     87     reg      axi_awready;
     88     reg      axi_wready;
     89     reg [1 : 0]     axi_bresp;
     90     reg      axi_bvalid;
     91     reg [C_S_AXI_ADDR_WIDTH-1 : 0]     axi_araddr;
     92     reg      axi_arready;
     93     reg [C_S_AXI_DATA_WIDTH-1 : 0]     axi_rdata;
     94     reg [1 : 0]     axi_rresp;
     95     reg      axi_rvalid;
     96 
     97     // Example-specific design signals
     98     // local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
     99     // ADDR_LSB is used for addressing 32/64 bit registers/memories
    100     // ADDR_LSB = 2 for 32 bits (n downto 2)
    101     // ADDR_LSB = 3 for 64 bits (n downto 3)
    102     localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
    103     localparam integer OPT_MEM_ADDR_BITS = 1;
    104     //----------------------------------------------
    105     //-- Signals for user logic register space example
    106     //------------------------------------------------
    107     //-- Number of Slave Registers 4
    108     reg [C_S_AXI_DATA_WIDTH-1:0]    slv_reg0;
    109     reg [C_S_AXI_DATA_WIDTH-1:0]    slv_reg1;
    110     reg [C_S_AXI_DATA_WIDTH-1:0]    slv_reg2;
    111     reg [C_S_AXI_DATA_WIDTH-1:0]    slv_reg3;
    112     wire     slv_reg_rden;
    113     wire     slv_reg_wren;
    114     reg [C_S_AXI_DATA_WIDTH-1:0]     reg_data_out;
    115     integer     byte_index;
    116 
    117     // I/O Connections assignments
    118 
    119     assign S_AXI_AWREADY    = axi_awready;
    120     assign S_AXI_WREADY    = axi_wready;
    121     assign S_AXI_BRESP    = axi_bresp;
    122     assign S_AXI_BVALID    = axi_bvalid;
    123     assign S_AXI_ARREADY    = axi_arready;
    124     assign S_AXI_RDATA    = axi_rdata;
    125     assign S_AXI_RRESP    = axi_rresp;
    126     assign S_AXI_RVALID    = axi_rvalid;
    127     
    128     
    129     assign local_dout = slv_reg0;
    130 
    131     always@(posedge S_AXI_ACLK)begin
    132         if( S_AXI_ARESETN == 1'b0)
    133             local_dout_vld <= 0;
    134         else
    135             local_dout_vld <= slv_reg_wren;
    136     end
    137     
    138     // Implement axi_awready generation
    139     // axi_awready is asserted for one S_AXI_ACLK clock cycle when both
    140     // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
    141     // de-asserted when reset is low.
    142 
    143     always @( posedge S_AXI_ACLK )
    144     begin
    145       if ( S_AXI_ARESETN == 1'b0 )
    146         begin
    147           axi_awready <= 1'b0;
    148         end 
    149       else
    150         begin    
    151           if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && ~local_busy)
    152             begin
    153               // slave is ready to accept write address when 
    154               // there is a valid write address and write data
    155               // on the write address and data bus. This design 
    156               // expects no outstanding transactions. 
    157               axi_awready <= 1'b1;
    158             end
    159           else           
    160             begin
    161               axi_awready <= 1'b0;
    162             end
    163         end 
    164     end       
    165 
    166     // Implement axi_awaddr latching
    167     // This process is used to latch the address when both 
    168     // S_AXI_AWVALID and S_AXI_WVALID are valid. 
    169 
    170     always @( posedge S_AXI_ACLK )
    171     begin
    172       if ( S_AXI_ARESETN == 1'b0 )
    173         begin
    174           axi_awaddr <= 0;
    175         end 
    176       else
    177         begin    
    178           if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && ~local_busy)
    179             begin
    180               // Write Address latching 
    181               axi_awaddr <= S_AXI_AWADDR;
    182             end
    183         end 
    184     end       
    185 
    186     // Implement axi_wready generation
    187     // axi_wready is asserted for one S_AXI_ACLK clock cycle when both
    188     // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is 
    189     // de-asserted when reset is low. 
    190 
    191     always @( posedge S_AXI_ACLK )
    192     begin
    193       if ( S_AXI_ARESETN == 1'b0 )
    194         begin
    195           axi_wready <= 1'b0;
    196         end 
    197       else
    198         begin    
    199           if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && ~local_busy)
    200             begin
    201               // slave is ready to accept write data when 
    202               // there is a valid write address and write data
    203               // on the write address and data bus. This design 
    204               // expects no outstanding transactions. 
    205               axi_wready <= 1'b1;
    206             end
    207           else
    208             begin
    209               axi_wready <= 1'b0;
    210             end
    211         end 
    212     end       
    213 
    214     // Implement memory mapped register select and write logic generation
    215     // The write data is accepted and written to memory mapped registers when
    216     // axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to
    217     // select byte enables of slave registers while writing.
    218     // These registers are cleared when reset (active low) is applied.
    219     // Slave register write enable is asserted when valid address and data are available
    220     // and the slave is ready to accept the write address and write data.
    221     assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;
    222 
    223     always @( posedge S_AXI_ACLK )
    224     begin
    225       if ( S_AXI_ARESETN == 1'b0 )
    226         begin
    227           slv_reg0 <= 0;
    228           slv_reg1 <= 0;
    229           slv_reg2 <= 0;
    230           slv_reg3 <= 0;
    231         end 
    232       else begin
    233         if (slv_reg_wren)
    234           begin
    235             case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
    236               2'h0:
    237                 for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
    238                   if ( S_AXI_WSTRB[byte_index] == 1 ) begin
    239                     // Respective byte enables are asserted as per write strobes 
    240                     // Slave register 0
    241                     slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
    242                   end  
    243               2'h1:
    244                 for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
    245                   if ( S_AXI_WSTRB[byte_index] == 1 ) begin
    246                     // Respective byte enables are asserted as per write strobes 
    247                     // Slave register 1
    248                     slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
    249                   end  
    250               2'h2:
    251                 for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
    252                   if ( S_AXI_WSTRB[byte_index] == 1 ) begin
    253                     // Respective byte enables are asserted as per write strobes 
    254                     // Slave register 2
    255                     slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
    256                   end  
    257               2'h3:
    258                 for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
    259                   if ( S_AXI_WSTRB[byte_index] == 1 ) begin
    260                     // Respective byte enables are asserted as per write strobes 
    261                     // Slave register 3
    262                     slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
    263                   end  
    264               default : begin
    265                           slv_reg0 <= slv_reg0;
    266                           slv_reg1 <= slv_reg1;
    267                           slv_reg2 <= slv_reg2;
    268                           slv_reg3 <= slv_reg3;
    269                         end
    270             endcase
    271           end
    272       end
    273     end    
    274 
    275     // Implement write response logic generation
    276     // The write response and response valid signals are asserted by the slave 
    277     // when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.  
    278     // This marks the acceptance of address and indicates the status of 
    279     // write transaction.
    280 
    281     always @( posedge S_AXI_ACLK )
    282     begin
    283       if ( S_AXI_ARESETN == 1'b0 )
    284         begin
    285           axi_bvalid  <= 0;
    286           axi_bresp   <= 2'b0;
    287         end 
    288       else
    289         begin    
    290           if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
    291             begin
    292               // indicates a valid write response is available
    293               axi_bvalid <= 1'b1;
    294               axi_bresp  <= 2'b0; // 'OKAY' response 
    295             end                   // work error responses in future
    296           else
    297             begin
    298               if (S_AXI_BREADY && axi_bvalid) 
    299                 //check if bready is asserted while bvalid is high) 
    300                 //(there is a possibility that bready is always asserted high)   
    301                 begin
    302                   axi_bvalid <= 1'b0; 
    303                 end  
    304             end
    305         end
    306     end   
    307 
    308     // Implement axi_arready generation
    309     // axi_arready is asserted for one S_AXI_ACLK clock cycle when
    310     // S_AXI_ARVALID is asserted. axi_awready is 
    311     // de-asserted when reset (active low) is asserted. 
    312     // The read address is also latched when S_AXI_ARVALID is 
    313     // asserted. axi_araddr is reset to zero on reset assertion.
    314 
    315     always @( posedge S_AXI_ACLK )
    316     begin
    317       if ( S_AXI_ARESETN == 1'b0 )
    318         begin
    319           axi_arready <= 1'b0;
    320           axi_araddr  <= 32'b0;
    321         end 
    322       else
    323         begin    
    324           if (~axi_arready && S_AXI_ARVALID)
    325             begin
    326               // indicates that the slave has acceped the valid read address
    327               axi_arready <= 1'b1;
    328               // Read address latching
    329               axi_araddr  <= S_AXI_ARADDR;
    330             end
    331           else
    332             begin
    333               axi_arready <= 1'b0;
    334             end
    335         end 
    336     end       
    337 
    338     // Implement axi_arvalid generation
    339     // axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both 
    340     // S_AXI_ARVALID and axi_arready are asserted. The slave registers 
    341     // data are available on the axi_rdata bus at this instance. The 
    342     // assertion of axi_rvalid marks the validity of read data on the 
    343     // bus and axi_rresp indicates the status of read transaction.axi_rvalid 
    344     // is deasserted on reset (active low). axi_rresp and axi_rdata are 
    345     // cleared to zero on reset (active low).  
    346     always @( posedge S_AXI_ACLK )
    347     begin
    348       if ( S_AXI_ARESETN == 1'b0 )
    349         begin
    350           axi_rvalid <= 0;
    351           axi_rresp  <= 0;
    352         end 
    353       else
    354         begin    
    355           if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
    356             begin
    357               // Valid read data is available at the read data bus
    358               axi_rvalid <= 1'b1;
    359               axi_rresp  <= 2'b0; // 'OKAY' response
    360             end   
    361           else if (axi_rvalid && S_AXI_RREADY)
    362             begin
    363               // Read data is accepted by the master
    364               axi_rvalid <= 1'b0;
    365             end                
    366         end
    367     end    
    368 
    369     // Implement memory mapped register select and read logic generation
    370     // Slave register read enable is asserted when valid address is available
    371     // and the slave is ready to accept the read address.
    372     assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
    373     always @(*)
    374     begin
    375           // Address decoding for reading registers
    376           case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
    377             2'h0   : reg_data_out <= slv_reg0;
    378             2'h1   : reg_data_out <= slv_reg1;
    379             2'h2   : reg_data_out <= slv_reg2;
    380             2'h3   : reg_data_out <= slv_reg3;
    381             default : reg_data_out <= 0;
    382           endcase
    383     end
    384 
    385     // Output register or memory read data
    386     always @( posedge S_AXI_ACLK )
    387     begin
    388       if ( S_AXI_ARESETN == 1'b0 )
    389         begin
    390           axi_rdata  <= 0;
    391         end 
    392       else
    393         begin    
    394           // When there is a valid read address (S_AXI_ARVALID) with 
    395           // acceptance of read address by the slave (axi_arready), 
    396           // output the read dada 
    397           if (slv_reg_rden)
    398             begin
    399               axi_rdata <= reg_data_out;     // register read data
    400             end   
    401         end
    402     end    
    403 
    404     // Add user logic here
    405 
    406     // User logic ends
    407 
    408     endmodule
    spi_4wire_w_v1_0_S00_AXI

    五、仿真测试

      我们写个简单的testbench测试接口和SPI时序逻辑正确性。只要准备就绪,便向顶层模块写入h02_4a。

    testbench代码:

      1 `timescale 1ns / 1ps
      2 
      3 
      4 module axi_spi_tb( );
      5 
      6 parameter CYC = 10,
      7           RST_TIM = 2;
      8 
      9 reg s00_axi_aclk;
     10 reg s00_axi_aresetn;
     11 reg [4-1:0] s00_axi_awaddr;
     12 wire [3-1:0] s00_axi_awprot;
     13 reg s00_axi_awvalid;
     14 wire s00_axi_awready;
     15 reg [32-1:0] s00_axi_wdata;
     16 reg [4-1:0] s00_axi_wstrb;
     17 reg s00_axi_wvalid;
     18 wire s00_axi_wready;
     19 wire [2-1:0] s00_axi_bresp;
     20 wire s00_axi_bvalid;
     21 wire s00_axi_bready;
     22 reg [4-1:0] s00_axi_araddr;
     23 wire [3-1:0] s00_axi_arprot;
     24 reg s00_axi_arvalid;
     25 wire s00_axi_arready;
     26 wire [32-1:0] s00_axi_rdata;
     27 wire [2-1:0] s00_axi_rresp;
     28 wire s00_axi_rvalid;
     29 wire s00_axi_rready;
     30 
     31     spi_4wire_w_v1_0 #
     32     (
     33         
     34         .C_S00_AXI_DATA_WIDTH(16),
     35         .C_S00_AXI_ADDR_WIDTH(4)
     36     )
     37     uut
     38     (
     39         // Users to add ports here
     40         //SPI signals
     41         . sclk                (sclk)  ,
     42         . sdin                (sdin)  ,
     43         . cs                  (cs)  ,
     44         . dc                  (dc)  ,
     45     
     46         . s00_axi_aclk        (s00_axi_aclk)  ,
     47         . s00_axi_aresetn     (s00_axi_aresetn)  ,
     48         . s00_axi_awaddr      (s00_axi_awaddr)  ,
     49         . s00_axi_awprot      (s00_axi_awprot)  ,
     50         . s00_axi_awvalid     (s00_axi_awvalid)  ,
     51         . s00_axi_awready     (s00_axi_awready)  ,
     52         . s00_axi_wdata       (s00_axi_wdata)  ,
     53         . s00_axi_wstrb       (s00_axi_wstrb)  ,
     54         . s00_axi_wvalid      (s00_axi_wvalid)  ,
     55         . s00_axi_wready      (s00_axi_wready)  ,
     56         . s00_axi_bresp       (s00_axi_bresp)  ,
     57         . s00_axi_bvalid      (s00_axi_bvalid)  ,
     58         . s00_axi_bready      (s00_axi_bready)  ,
     59         . s00_axi_araddr      (s00_axi_araddr)  ,
     60         . s00_axi_arprot      (s00_axi_arprot)  ,
     61         . s00_axi_arvalid     (s00_axi_arvalid)  ,
     62         . s00_axi_arready     (s00_axi_arready)  ,
     63         . s00_axi_rdata       (s00_axi_rdata)  ,
     64         . s00_axi_rresp       (s00_axi_rresp)  ,
     65         . s00_axi_rvalid      (s00_axi_rvalid)  ,
     66         . s00_axi_rready      (s00_axi_rready)      
     67     );
     68 
     69     initial begin
     70         s00_axi_aclk = 1;
     71         forever #(CYC/2.0) s00_axi_aclk = ~s00_axi_aclk;
     72     end
     73 
     74     initial begin
     75         s00_axi_aresetn = 1;
     76         #1;
     77         s00_axi_aresetn = 0;
     78         #(RST_TIM*CYC);
     79         s00_axi_aresetn = 1;
     80     end
     81 
     82     assign s00_axi_awprot = 3'd0;
     83     assign s00_axi_bready = 1'b1;
     84     assign s00_axi_arprot = 3'd0;
     85     assign s00_axi_rready = 1'b1;
     86 
     87     initial begin
     88         #1;
     89         s00_axi_awaddr = 0;
     90         s00_axi_awvalid = 0;
     91         s00_axi_wdata = 0;
     92         s00_axi_wstrb = 0;
     93         s00_axi_wvalid = 0;
     94         s00_axi_araddr = 0;
     95         s00_axi_arvalid = 0;
     96         #(RST_TIM*CYC);
     97         #(CYC*10);
     98         s00_axi_awaddr = 0;
     99         s00_axi_awvalid = 1'b1;
    100         s00_axi_wdata = 32'h02_4a;
    101         s00_axi_wstrb = 4'b1111;
    102         s00_axi_wvalid = 1'b1;
    103         s00_axi_araddr = 0;
    104         s00_axi_arvalid = 0;
    105         #5_000;
    106         $stop;
    107     end
    108     
    109 endmodule
    axi_spi_tb

    行为仿真波形:

        在CS为低时,串行输出为:0_1_0_0_1_0_1_0,正确完成SPI数据写功能。暂仅进行行为仿真,还没有上板验证,遇到问题后续改动更新。

  • 相关阅读:
    OpenResty 社区王院生:APISIX 的高性能实践
    【社群话题分享】有哪些程序员专属的“土味情话”?
    第一次亲密接触——二狗子初识 CDN
    又拍云叶靖:OpenResty 在又拍云存储中的应用
    【社群话题分享】程序员中年真这么难?
    Zabbix日常监控之lvs监控
    zabbix日常监控Apache2.4
    Centos7 apache2.4.29(httpd) 安装
    grafana的安装与设置(一)
    zabbix日常监控项java(四又分之一,通用性强)
  • 原文地址:https://www.cnblogs.com/moluoqishi/p/10339863.html
Copyright © 2011-2022 走看看