ZYNQ:使用AXI Stream interface创建SPI发送IP核

        最近在学习zedboard,今天试着分析并理解一下Harald's Embedded Electronics 网站的的SPI发送ip核教学里的Verilog代码。链接如下:

ZYNQ: SPI Transmitter Using an AXI Stream Interface – Harald's Embedded Electronicshttp://www.harald-rosenfeldt.de/2017/12/28/zynq-spi-transmitter-using-an-axi-stream-interface/        原文中的创建步骤就不再赘述了,这里只分析代码,需要的话可以点击上面的链接

        原代码如下:

mySPI_Tx_AXIS_v1_0.v

`timescale 1 ns / 1 ps
 
    module mySPI_Tx_AXIS_v1_0 #
    (
        // Users to add parameters here
        parameter integer width = 8,
        parameter integer clkdiv= 4,
 
        // User parameters ends
        // Do not modify the parameters beyond this line
 
 
        // Parameters of Axi Slave Bus Interface S00_AXIS
        parameter integer C_S00_AXIS_TDATA_WIDTH    = 32
    )
    (
        // Users to add ports here
        output wire sclk,
        output wire mosi,
        output wire ss,
         
        // User ports ends
        // Do not modify the ports beyond this line
 
 
        // Ports of Axi Slave Bus Interface S00_AXIS
        input wire  s00_axis_aclk,
        input wire  s00_axis_aresetn,
        output wire  s00_axis_tready,
        input wire [C_S00_AXIS_TDATA_WIDTH-1 : 0] s00_axis_tdata,
        input wire [(C_S00_AXIS_TDATA_WIDTH/8)-1 : 0] s00_axis_tstrb,
        input wire  s00_axis_tlast,
        input wire  s00_axis_tvalid
    );
 
    // Instantiation of Axi Bus Interface S00_AXIS
    mySPI_Tx_AXIS_v1_0_S00_AXIS # (
        .width(width),
        .clkdiv(clkdiv),
        .C_S_AXIS_TDATA_WIDTH(C_S00_AXIS_TDATA_WIDTH)
    ) mySPI_Tx_AXIS_v1_0_S00_AXIS_inst (
        .sclk(sclk),
        .mosi(mosi),
        .ss(ss),
        .S_AXIS_ACLK(s00_axis_aclk),
        .S_AXIS_ARESETN(s00_axis_aresetn),
        .S_AXIS_TREADY(s00_axis_tready),
        .S_AXIS_TDATA(s00_axis_tdata),
        .S_AXIS_TSTRB(s00_axis_tstrb),
        .S_AXIS_TLAST(s00_axis_tlast),
        .S_AXIS_TVALID(s00_axis_tvalid)
    );
 
    // Add user logic here
 
    // User logic ends
 
    endmodule

mySPI_Tx_AXIS_v1_0_S00_AXIS.v

`timescale 1 ns / 1 ps
 
    module mySPI_Tx_AXIS_v1_0_S00_AXIS #
    (
        // Users to add parameters here
        parameter integer width = 8,
        parameter integer clkdiv= 4,
 
        // User parameters ends
        // Do not modify the parameters beyond this line
 
        // AXI4Stream sink: Data Width
        parameter integer C_S_AXIS_TDATA_WIDTH  = 32
    )
    (
        // Users to add ports here
        output wire sclk,
        output reg mosi = 0,
        output wire ss,
 
        // User ports ends
        // Do not modify the ports beyond this line
 
        // AXI4Stream sink: Clock
        input wire  S_AXIS_ACLK,
        // AXI4Stream sink: Reset
        input wire  S_AXIS_ARESETN,
        // Ready to accept data in
        output wire  S_AXIS_TREADY,
        // Data in
        input wire [C_S_AXIS_TDATA_WIDTH-1 : 0] S_AXIS_TDATA,
        // Byte qualifier
        input wire [(C_S_AXIS_TDATA_WIDTH/8)-1 : 0] S_AXIS_TSTRB,
        // Indicates boundary of last packet
        input wire  S_AXIS_TLAST,
        // Data is in valid
        input wire  S_AXIS_TVALID
    );
 
    // 八位的移位寄存器  This holds the shift register
    reg [width-1 : 0] buffer = 0;
    reg buffer_full = 0;
 
    // 六位bit计数 Counts the bits 
    reg [5:0] bitcounter = 0;
     
    // 四位的分频器 0-127 Makes things slower
    reg [clkdiv-1:0] prescaler = 0;
 
    // 状态机的四个状态 State machine states
    localparam IDLE = 0;
    localparam S1 = 1;
    localparam S2 = 2;
    localparam S3 = 3;
 
    // 状态机的默认状态为 IDLE Default state is IDLE
    reg [1:0] state = IDLE;
 
    // 等待信号接收状态 Signals we are ready to receive
    assign S_AXIS_TREADY = !buffer_full;
     
    // SPI的时钟信号(数据在上升沿有效) 在state为状态S2或S3时,为1,其他状态为0 SPI Clock (data is valid during Low/High transition)
    assign sclk = state==S2 || state==S3;
     
    // SPI的从机选择 只有state等于状态IDLE时才会置0  SPI Slave Select
    assign ss = state!=IDLE;
     
    // 下面的程序为主要的状态机 This is the main state machine
    always @(posedge S_AXIS_ACLK) begin
        // There is only one important rule for an AXI Stream interface:
        // If during the rising clock, S_AXIS_TVALID==1 and S_AXIS_TREADY==1, then we have to accept the data.  
        if (S_AXIS_TVALID==1 && S_AXIS_TREADY==1) begin
            buffer <= S_AXIS_TDATA[width-1 : 0]; //把数据存入buffer
            buffer_full = 1;    //buffer存满标志置1
        end else if (state==S3 && prescaler==1) begin
            buffer_full = 0;    //buffer存满标志清零
        end
     
        prescaler <= prescaler+1; //分频计数器prescaler加1  每128个S_AXIS_ACLK脉冲,prescaler溢出清零 
        if (prescaler==0) begin // The state transitions are synchronized to the SPI bit clock
            case(state)
                IDLE:   begin // ss=0, sclk=0, mosi=0
                            mosi <= 0;  //mosi清零
                            if (buffer_full==1) begin   //如果buffer存满
                                mosi <= buffer[width-1];    //buffer的最高位发送到mosi
                                bitcounter <= 1;       //bitcounter计数器置1
                                state <= S1;    //状态变为S1
                            end
                        end
                S1:     begin // ss=1, sclk=0
                            if ( bitcounter==width ) begin  //如果bitcounter计数器等于8,说明数据发送完
                                state <= S3;    //进入状态S3
                            end else begin
                                state <= S2;    //数据没有发送完,进入状态S2
                                buffer <= buffer<<1;    //buffer左移一位,最高位改变
                            end
                        end
                S2:     begin // ss=1, sclk=1   
                            state <= S1;    //进入状态S1
                            mosi <= buffer[width-1];    //buffer的最高位发送到mosi
                            bitcounter <= bitcounter+1;     //bitcounter计数器加1
                        end
                S3:     begin // ss=1, sclk=1 (last bit)
                            if (buffer_full==1) begin   //如果buffer存满
                                mosi <= buffer[width-1];    //buffer的最高位发送到mosi
                                bitcounter <= 1;    //bitcounter计数器置1
                                state <= S1;    //进入状态S1
                            end else begin
                                state <= IDLE;  //进入状态IDLE
                            end
                        end
                default:begin
                            state <= IDLE;  //进入状态IDLE
                        end
            endcase
        end
    end
 
    endmodule

        这两个文件都是根据AXI Stream Interface的基础上修改实现的,根据原始文件的提示在其中加入用户的参数、接口、逻辑就可以实现用户想要功能的IP核。

mySPI_Tx_AXIS_v1_0.v代码分析

        代码第4到15行为参数定义。第6、7行为添加的两个参数。一个width = 8,一个clkdiv = 4。

        代码第18到20行定义了三个输出接口 sclk,mosi,ss,这三个接口都是SPI通讯的接口。

        sclk、mosi、ss分别为SPI通讯的 时钟,数据发送接口,和从机选择。对于SPI通信协议不了解的朋友可以去看其他帖子。但在原贴中,ss这个接口的写法跟平时常见的不太一样,这里为高电平有效,其他地方常见的用法还是低电平有效。

mySPI_Tx_AXIS_v1_0_S00_AXIS.v代码分析

        6、7行的两个参数,width为传输数据的位数这里为8。另一个clkdiv为分频器的位数,这里是4位,用来将S_AXIS_ACLK的频率降低128倍给SPI通讯的时钟sclk。

       40-57行定义了一些寄存器和状态机的状态参数,我在代码里都给了注释。

                buffer是一个八位寄存器,用来接收来自PS的数据,并在后续发送给SPI通讯的mosi接口

                四个状态IDLE,S1,S2,S3,IDLE为初始状态,S3为结束状态,S1和S2为发送状态。

               最关键的还是ss和sclk。ss为从机选择,高电平有效,这里在状态S1,S2,S3时都是高电平,在IDLE状态为低电平。sclk为时钟信号,在S2或S3时置1,IDLE和S1状态置0。

后面的代码里都给了注释,大家可以自己阅读。下面为工作的时序图。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值