Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

自定义AXI总线形式SPI接口IP核,点亮OLED

没落骑士 2019-02-05 11:58:00 阅读数:304 评论数:0 点赞数:0 收藏数:0

一、前言

最近花费很多精力在算法仿真和实现上,外设接口的调试略有生疏。本文以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 /1ps2 3 module spi_4wire/#(parameter DIV_CYC = 20)4 (5 //本地接口 6 input clk,//100MHZ 7 inputrst_n,8 input [2-1:0] com,//1发送控制信息,2发送数据 其他无效 9 input [8-1:0] din,10 outputbusy,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 integerbit_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 wireadd_div_cnt ;35 wireend_div_cnt ;36 reg [ (BIT_CNT_W-1):0] bit_cnt =0;37 wireadd_bit_cnt ;38 wireend_bit_cnt ;39 reg [2-1:0] state_c = IDLE,state_n =IDLE;40 wireidle2send,send2done,done2idle;41 reg [8+2-1:0] data_tmp = 0;42 wiredin_vld;43 wirestart_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 endmodulespi_4wire

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

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

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

1 写地址通道:

SAXIAWADDR:写地址

SAXIAWPORT:写地址保护类型

SAXIAWVALID:写地址有效

SAXIAWREADY:写地址准备

2 写数据通道:

SAXIWDATA:写数据

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

SAXIWVALID:写有效

SAXIWREADY:写数据准备

3 写响应通道:

SAXIBRESP:指示写传输状态

SAXIBVALID:写响应有效指示

SAXIBREADY:响应准备

4 读地址通道:

SAXIARADDR:读地址

SAXIARPROT:读地址保护类型

SAXIARVALID:读地址有效指示

SAXIARREADY:读地址准备

5 读数据通道:

SAXIRDATA:读数据

SAXIRVALID:读数据有效

SAXIRREADY:读数据准备

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

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

AXI Wrapper:

1 `timescale 1 ns / 1ps2 3 modulespi_4wire_w_v1_0 /#4 (5 //Users to add parameters here6 7 //User parameters ends8 //Do not modify the parameters beyond this line9 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 here17 //SPI signals 18 outputsclk,19 outputsdin,20 outputcs,21 outputdc,22 //User ports ends23 //Do not modify the ports beyond this line24 25 26 //Ports of Axi Slave Bus Interface S00_AXI 27 input wires00_axi_aclk,28 input wires00_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 wires00_axi_awvalid,32 output wires00_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 wires00_axi_wvalid,36 output wires00_axi_wready,37 output wire [1 : 0] s00_axi_bresp,38 output wires00_axi_bvalid,39 input wires00_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 wires00_axi_arvalid,43 output wires00_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 wires00_axi_rvalid,47 input wires00_axi_rready48 );49 50 reg rst_n = 1'b1; 51 wire [8-1:0] din;52 wire [2-1:0] com;53 wirebusy;54 wire [16-1:0] data;55 wiredata_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 uut92 (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 endmodulespi4wirewv10

Slave接口模块:

1 `timescale 1 ns / 1ps2 3 modulespi_4wire_w_v1_0_S00_AXI /#4 (5 //Users to add parameters here6 7 //User parameters ends8 //Do not modify the parameters beyond this line9 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 inputlocal_busy,20 //User ports ends21 //Do not modify the ports beyond this line22 23 //Global Clock Signal 24 input wireS_AXI_ACLK,25 //Global Reset Signal. This Signal is Active LOW 26 input wireS_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 the30 //privilege and security level of the transaction, and whether31 //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 signaling34 //valid write address and control information. 35 input wireS_AXI_AWVALID,36 //Write address ready. This signal indicates that the slave is ready37 //to accept an address and associated control signals. 38 output wireS_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 hold42 //valid data. There is one write strobe bit for each eight43 //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 write46 //data and strobes are available. 47 input wireS_AXI_WVALID,48 //Write ready. This signal indicates that the slave49 //can accept the write data. 50 output wireS_AXI_WREADY,51 //Write response. This signal indicates the status52 //of the write transaction. 53 output wire [1 : 0] S_AXI_BRESP,54 //Write response valid. This signal indicates that the channel55 //is signaling a valid write response. 56 output wireS_AXI_BVALID,57 //Response ready. This signal indicates that the master58 //can accept a write response. 59 input wireS_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 privilege63 //and security level of the transaction, and whether the64 //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 channel67 //is signaling valid read address and control information. 68 input wireS_AXI_ARVALID,69 //Read address ready. This signal indicates that the slave is70 //ready to accept an address and associated control signals. 71 output wireS_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 the75 //read transfer. 76 output wire [1 : 0] S_AXI_RRESP,77 //Read valid. This signal indicates that the channel is78 //signaling the required read data. 79 output wireS_AXI_RVALID,80 //Read ready. This signal indicates that the master can81 //accept the read data and response information. 82 input wireS_AXI_RREADY83 );84 85 //AXI4LITE signals 86 reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;87 regaxi_awready;88 regaxi_wready;89 reg [1 : 0] axi_bresp;90 regaxi_bvalid;91 reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr;92 regaxi_arready;93 reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;94 reg [1 : 0] axi_rresp;95 regaxi_rvalid;96 97 //Example-specific design signals98 //local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH99 //ADDR_LSB is used for addressing 32/64 bit registers/memories100 //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 example106 //------------------------------------------------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 wireslv_reg_rden;113 wireslv_reg_wren;114 reg [C_S_AXI_DATA_WIDTH-1:0] reg_data_out;115 integerbyte_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 generation139 //axi_awready is asserted for one S_AXI_ACLK clock cycle when both140 //S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is141 //de-asserted when reset is low. 142 143 always @( posedgeS_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 when154 //there is a valid write address and write data155 //on the write address and data bus. This design156 //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 latching167 //This process is used to latch the address when both168 //S_AXI_AWVALID and S_AXI_WVALID are valid. 169 170 always @( posedgeS_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 generation187 //axi_wready is asserted for one S_AXI_ACLK clock cycle when both188 //S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is189 //de-asserted when reset is low. 190 191 always @( posedgeS_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 when202 //there is a valid write address and write data203 //on the write address and data bus. This design204 //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 generation215 //The write data is accepted and written to memory mapped registers when216 //axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to217 //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 available220 //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 @( posedgeS_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 strobes240 //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 strobes247 //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 strobes254 //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 strobes261 //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 generation276 //The write response and response valid signals are asserted by the slave277 //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 of279 //write transaction. 280 281 always @( posedgeS_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 generation309 //axi_arready is asserted for one S_AXI_ACLK clock cycle when310 //S_AXI_ARVALID is asserted. axi_awready is311 //de-asserted when reset (active low) is asserted.312 //The read address is also latched when S_AXI_ARVALID is313 //asserted. axi_araddr is reset to zero on reset assertion. 314 315 always @( posedgeS_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 generation339 //axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both340 //S_AXI_ARVALID and axi_arready are asserted. The slave registers341 //data are available on the axi_rdata bus at this instance. The342 //assertion of axi_rvalid marks the validity of read data on the343 //bus and axi_rresp indicates the status of read transaction.axi_rvalid344 //is deasserted on reset (active low). axi_rresp and axi_rdata are345 //cleared to zero on reset (active low). 346 always @( posedgeS_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 generation370 //Slave register read enable is asserted when valid address is available371 //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 @( posedgeS_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) with395 //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 here405 406 //User logic ends 407 408 endmodulespi4wirewv10S00AXI

五、仿真测试

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

testbench代码:

1 `timescale 1ns /1ps2 3 4 moduleaxi_spi_tb( );5 6 parameter CYC = 10,7 RST_TIM = 2;8 9 regs00_axi_aclk;10 regs00_axi_aresetn;11 reg [4-1:0] s00_axi_awaddr;12 wire [3-1:0] s00_axi_awprot;13 regs00_axi_awvalid;14 wires00_axi_awready;15 reg [32-1:0] s00_axi_wdata;16 reg [4-1:0] s00_axi_wstrb;17 regs00_axi_wvalid;18 wires00_axi_wready;19 wire [2-1:0] s00_axi_bresp;20 wires00_axi_bvalid;21 wires00_axi_bready;22 reg [4-1:0] s00_axi_araddr;23 wire [3-1:0] s00_axi_arprot;24 regs00_axi_arvalid;25 wires00_axi_arready;26 wire [32-1:0] s00_axi_rdata;27 wire [2-1:0] s00_axi_rresp;28 wires00_axi_rvalid;29 wires00_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 uut38 (39 //Users to add ports here40 //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 endmoduleaxispitb

行为仿真波形:

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

版权声明
本文为[没落骑士]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/moluoqishi/p/10339863.html

编程之旅,人生之路,不止于编程,还有诗和远方。
阅代码原理,看框架知识,学企业实践;
赏诗词,读日记,踏人生之路,观世界之行;

支付宝红包,每日可领