自定义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数据写功能。暂仅进行行为仿真,还没有上板验证,遇到问题后续改动更新。

原文地址:https://www.cnblogs.com/moluoqishi/p/10339863.html

时间: 2024-11-10 11:54:36

自定义AXI总线形式SPI接口IP核,点亮OLED的相关文章

利用ZYNQ SOC快速打开算法验证通路(6)——利用AXI总线实时配置sysGen子系统

利用ZYNQ验证算法的一大优势在于,可以在上位机发送指令借助CPU的控制能力和C语言易开发特点,实时配置算法模块的工作模式.参数等对来对其算法模块性能进行全面的评估.最重要的是无需重新综合硬件模块. 接着上篇该系列博文,在sysGen中设计模块功能为:根据模式选择输入,来完成乘2或除2两种运算,0乘1除. 测试激励选用From Workspace模块,从MATLAB工作空间导入数据.利用MALTAB脚本可以非常容易地生成任意数据集,极大体现了sysGen开发的优势. 设计完成调用xilinx w

Quartus II 与ModelSim-Altera联合仿真FFT IP核之FFT IP核分析

FFT IP Core Features 参数设置 FFT IP核有4种模式,分别为: 突发模式(Burst): 缓存突发模式(Buffered Burst): 流模式(Streaming): 可变流模式(Variable Streaming): 前3中模式运算速度依次增大,占用资源也依次增加,第4种模式(Variable Streaming)可以用于在线改变FFT的大小.速度和流模式差不多,资源占用更多. 分为基本配置和高级配置两种,基本配置包括配置FFT ip核的变换长度,FFT还是IFFT

AXI-Lite总线及其自定义IP核使用分析总结

ZYNQ的优势在于通过高效的接口总线组成了ARM+FPGA的架构.我认为两者是互为底层的,当进行算法验证时,ARM端现有的硬件控制器和库函数可以很方便地连接外设,而不像FPGA设计那样完全写出接口时序和控制状态机.这样ARM会被PL端抽象成"接口资源":当进行多任务处理时,各个PL端IP核又作为ARM的底层被调用,此时CPU仅作为"决策者",为各个IP核分配任务:当实现复杂算法时,底层算法结构规整可并行,数据量大,实时性要求高,而上层算法则完全相反,并且控制流程复杂

一步一步学ZedBoard &amp; Zynq(四):基于AXI Lite 总线的从设备IP设计 转载

文章来源 http://www.cnblogs.com/surpassal/archive/2012/10/09/Zynq_Lab4.html 本小节通过使用XPS中的定制IP向导(ipwiz),为已经存在的ARM PS 系统添加用户自定IP(Custom IP ),了解AXI Lite IP基本结构,并掌握AXI Lite IP的定制方法,为后续编写复杂AXI IP打下基础.同时本小节IP定制方法同样适用于MicroBlaze处理系统. 本小节定制的是简单LED的IP,只有一个数据寄存器,向其

SPI、I2C、UART三种串行总线协议的区别和SPI接口介绍(转)

SPI.I2C.UART三种串行总线协议的区别 第一个区别当然是名字: SPI(Serial Peripheral Interface:串行外设接口); I2C(INTER IC BUS) UART(Universal Asynchronous Receiver Transmitter:通用异步收发器) 第二,区别在电气信号线上: SPI总线由三条信号线组成:串行时钟(SCLK).串行数据输出(SDO).串行数据输入(SDI).SPI总线可以实现多个SPI设备互相连接.提供SPI串行时钟的SPI

将自己写的HDL代码封装成带AXI总线的IP

1.Tools->create and package IP 2.create AXI4总线的IP 3.新建block design 4.点击右键,选择edit in ip packager 此时生成了一个新的工程: 5.对生成的.v文件进行编辑 6.先修改顶层文件 添加一个LED输出端口 其实在文件的下面还可以添加自己的逻辑: 7.修改源文件 先添加端口,下面还有提示,编辑端口不要超过这条线.下面都是AXI总线的端口,不要去动它. 8.下一步操作 选择->Merge changes from

第7讲 SPI和RAM IP核

学习目的: (1) 熟悉SPI接口和它的读写时序: (2) 复习Verilog仿真语句中的$readmemb命令和$display命令: (3) 掌握SPI接口写时序操作的硬件语言描述流程(本例仅以写时序为例),为以后描述更复杂的时序逻辑电路奠定基础. 学习过程: [SPI的相关知识] ① SPI的速度比串口的快,采用源同步传输的方式,且为串行传输,应用场景不同则时序和接口名称会有不同: ② 串行flash的读写擦除命令可通过SPI接口进行通信,CPU芯片与FPGA可通过SPI接口进行通信,某些

【OpenHW12参赛手记】ZedBoard-自定义IP核实现+PS成功调用【详细步骤+流程介绍+源码】 转载

文章来源 图片无法复制,请看原文 http://www.eefocus.com/jefby1990/blog/13-03/291975_490bc.html [OpenHW12参赛手记]ZedBoard-自定义IP核实现+PS成功调用[详细步骤+流程介绍+源码] 2013-03-07 17:56:30 分享: (图片请点击查看原图) 软件环境:WIN7_64 + ISE 14.4 (system_edition) 硬件:Zedboard.USB-Cable线 搭建图: 经过前几天的学习,查看数据

zedboard如何从PL端控制DDR读写(二)——AXI总线

 虽然Xilinx已经将和AXI时序有关的细节都封装起来,给出了官方IP和向导生成自定义IP,用户只需要关注自己的逻辑实现,但是还是有必要简单了解一下AXI的时序,毕竟咱是做硬件设计的. AXI(Advanced eXtensible Interface)是一种总线协议,该协议是ARM公司提出的AMBA(Advanced Microcontroller Bus Architecture)3.0协议中最重要的部分,是一种面向高性能.高带宽.低延迟的片内总线.它的地址/控制和数据相位是分离的,支持不