AXI4协议详解(二)

本文深入解析AXI4协议中从机的写过程,包括端口声明、信号位宽选择及写地址、数据通道的实现。通过实例讲解如何设计AXI4从机的写操作,并提供了仿真验证的指导,帮助读者完成从机写过程的RTL设计与验证。
AI助手已提取文章相关产品:

写在前面:上一篇文章我们介绍了AXI4协议各通道的信号构成与握手机制,之后我们将在解析读写过程的同时,手把手教你实现一个从机的读写过程。由于今天的篇幅所限,本篇文章将实现一个从机的写过程,作为协议解析与实现系列的一个开始。回复AXI4可以得到本系列的RTL设计以及仿真环境,链接有实时更新

文章链接:https://mp.weixin.qq.com/s/b87ZkPyX68vYoiMXLjjB9g
在这里插入图片描述

先说下读完这篇文章的预期结果,也算是一个小作业——完成一个AXI4从机写过程的rtl设计,并使用VIP验证设计。

整体分为声明端口RTL设计以及仿真验证三部分,废话不多说,我们直接搞:

一、声明端口

首先,我们要进行输入输出端口的声明,由于我们今天设计的是一个AXI4从机,因此对照着上篇文章里的信号列表,标记S2M的设置为output,标记为M2S的设置为input。

当然,在写的时候不可避免要遇到一些位宽问题,在这里就一并再详细介绍下各信号的位宽是怎么选定的:

  • ID类信号(AWID、BID、ARID、RID)

    ID信号指示的是传输任务序号,主机可以设置传输任务的序号指示执行的顺序。其中,来自同一个主机,同一个序号的指令只能顺序执行;来自同一主机不同序号的或来自不同主机的传输任务没有执行顺序的要求。因此传输任务ID的作用是指定某些特殊传输任务的顺序,当存在明确的先后顺序时,可以用相同的任务ID标识。

    同时在这里还要特别指出,上篇文章当中有一些小错误,写通道的ID在AXI4协议中被取消了,这也就意味着写通道的写顺序必须与写地址通道保持一致。spec中讲取消这一信号的原因是AXI4取消了AXI3协议中对交织写数据的支持,AXI4只能进行连续写数据,因此WID信号变得冗余,为了节省端口数将WID取消掉了。

    四个ID信号的位宽可以使用一致的宽度,建议作为模块参数进行自定义,笔者定义位宽为4。

  • 地址类信号(AWADDR、ARADDR)

    地址类信号的位宽代表了本个从机的地址映射区的大小。这里再废话几句,主机对从机的控制其实是通过访问从机的寄存器实现的。如下图所示,两个从机的寄存器(寄存器大小均为32位)被映射在一个主机的访问地址中,其中0x44A0_0000是第一个从机的偏置地址,占用空间1KB,0x44A0_1000是第二个从机的偏置地址,占用空间4KB。如果想要访问第一个从机的第二个寄存器,只需要访问0x44A0_0004地址的寄存器即可。

在这里插入图片描述

AXI4协议规定主机上寄存器块的最小分块单元为4K Byte,每个从机的开始映射地址都是4KB的边界。这也影响了AXI4的一个读写规则,那就是一个burst传输不能跨越4KB的界限,否则就会引入将数据错误写入其他从机的风险。注意这个4KB的界限指的是MASTER的地址界限,例如一个burst中不能越过0xXXXX_X000的地址位。

地址位的位宽也可以自定义设置,建议定义为模块参数,笔者设置为了8位,也就是说设计的从机共有64个寄存器,每个寄存器含有一个32位数据。

  • 数据类信号(WDATA、RDATA、WSTRB、WLAST、RLAST)

    其中WDATA与RDATA为总线的数据位宽,也是每个地址下的寄存器大小。位宽可以根据总线需要设定,本设计中使用较为常用的32位。

    WSTRB为写选通信号,指示当前传输的有效数据段。有效数据段单位是8bit,也就是32bit数据位宽的总线的WSTRB信号会有4位,WSTRB[3]指示WDATA[31:24]的有效性。

    1. WSTRB可以用在AXI4总线的“Narrow Transfer”特性中:当burst传输的数据位(burst size)小于总线位宽时,可以通过选通信号进行部分写入。如图所示,当burst size表示传输数据位为8且传输类型为递增(INCR)时,32位数据总线位宽的写入情况(此时的写入选通信号依次为:4’b0001,4’b0010,4’b0100,4’b1000,4’b0001):

    1. WSTRB信号也可以用在AXI4总线的“Unaligned Transfer”中:当传输的目标地址与寄存器的边界未对齐时,可以使用非对齐传输,如下图所示,在寄存器位宽32位时,起始传输地址0x07时的传输情况:

    WLAST信号指示最后一个写数据,与最后一个写数据的WVALID信号同步拉高。只有当WLAST由高变为低,一个传输任务才完成。在此之前无法中断传输过程。RLAST信号也类似。

  • 传输配置信号(AWLEN、ARLEN、AWSIZE、ARSIZE、AWBURST、ARBURST、AWLOCK、ARLOCK、AWCACHE、ARCACHE、AWPROT、ARPROT、AWQOS、ARQOS、AWREGION、ARREGION)

    1. AWLEN/ARLEN:传输任务中的传输数据包个数,在INCR传输类型下可以传输1至256个数据包,在其他传输类型下只能传输1-16个数据包,特别地,WRAP传输类型下只能传输2,4,8或16个固定的数据包个数。因此AWLEN与ARLEN的位宽为8位,其中LEN=0代表传输一个数据包。

    2. AWSIZE/ARSIZE:传输数据包大小,位宽为3位,具体值的意义如下表所示(Bytes=2^Burst_size):

      AxSIZE传输字节数AxSIZE传输字节数
      3’b00013’b10016
      3’b00123’b10132
      3’b01043’b11064
      3’b01183’b111128
    3. AWBURST/ARBURST:传输类型,位宽为2位,具体含义如下:

      AxBURST传输类型
      2’b00固定(FIXED)
      2’b01递增(INCR)
      2’b10回环(WRAP)
      2’b11未定义

      固定传输类型是对一个地址重复写入数据,递增传输类型是地址自动增长,不断写入数据流。需要特别说明的是回环传输类型,其实故名思义,就是地址递增到一定边界后从初始位置继续递增,如下图所示,传输长度为8,传输数据包大小为2 byte,起始传输地址为0x02:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8r4Hleba-1597583309001)(wrap.png)]

      也许这样简单地说不能解决大家的一些疑问,因此将手册中的计算公式也一并给出:

      起始地址(Start_Addr): AxADDR
      数据包字节数(Number_Bytes):2^(AxSIZE)
      传输长度(Burst_Len):AxLEN + 1
      对齐地址(Aligned_Addr):(INT(Start_Addr / Number_Bytes))*Number_Bytes
      回环界(Wrap_Boundary):(INT(Start_Addr / (Number_Bytes * Burst_Len))) * (Number_Bytes * Burst_Len)
      当地址递增至Wrap_Boundary + (Number_Bytes * Burst_Len)时:
      当前传输地址:Address = Wrap_boundary
      下一个传输地址:Address = Start_Addr + ((N-1)*Number_Bytes) - (Number_Bytes * Burst_Len)
      其中N表示数据包在整个Burst传输长度中的序号
      
  1. AWLOCK/ARLOCK:这个信号在AXI3协议中用于锁定从机,占用总线,但在AXI4中取消了相关支持,仅 留下一位信号用作指示传输为正常传输(1’b0)还是独有传输(1’b1)。

  2. AWCACHE/ARCACHE:该信号指明了总线中的存储类型,信号位宽为4位。

    ARCACHEAWCACHEMemory Type
    00000000Device Non-bufferable
    00010001Device Bufferable
    00100010Normal Non-cacheable Non-bufferable
    00110011Normal Non-cacheable Bufferable
    10100110Write-through No-allocate
    1110(0110)0110Write-through Read-allocate
    10101110(1010)Write-through Write-allocate
    11101110Write-through Read and Write-allocate
    10110111Write-back No-allocate
    1111(0111)0111Write-back Read-allocate
    10111111(1011)Write-back Write-allocate
    11111111Write-back Read and Write-allocate

    关于这些配置类型,甚至都可以再写一篇推送了,之后在用到的时候我会再详细讲这一部分,大家有兴趣的可以查看手册的A4.4.1部分。

  3. AWPROT/ARPROT:指明访问是否被允许的信号,位宽为3位,具体可见下表:

    AxPROT功能
    AxPROT[0]0非优先访问
    1优先访问
    AxPROT[1]0安全访问
    1非安全访问
    AxPROT[2]0数据访问
    1指令访问
  4. AWQoS/ARQoS:Quality of Service, 位宽为4位,手册中并没有固定该信号的确切用途,但是建议将该信号用于优先级声明信号,值越高代表优先级越高。

  5. AWREGION/ARREGION:区域标记,位宽为4位。4位的区域标记信号可以标记16个区域。当从机有很多分离的地址空间时,使用这个信号可以仅仅使用一个物理总线就能实现多个逻辑总线的功能。经典的应用是一个外设控制寄存器与数据寄存器分离的时候,使用该信号可以在不对地址解码的情况下访问到两块地址空间。例如,当一个外设控制寄存器的地址空间为0x0000 - 0x00FF,数据寄存器的地址空间为0x1000 - 0x1FFF,就可以将地址位设置为12位(0x000-0xFFF),控制寄存器的区域标记为4’b0000,数据寄存器的区域标记为4’b0001,实际寻址的地址为区域标记+地址位,其中区域标记位作为高位加在地址位前就可以了。

    需要注意的是,每4KB的地址空间上仅能有一种REGION标记,也就是区域分割的最小单元为4KB

至此,大部分信号都已经简单介绍过了,剩下的还有ready、valid信号在上一篇文章介绍过,各种USER信号手册中其实是不建议使用的,在本设计中就全部使用1位位宽,无特别含义。经过了上面的铺垫,让我们来实现一个简单的总线从机写过程:

二、实现AXI总线从机的写过程

看到现在有没有一种感觉,明明对每个信号都熟悉了,可为什么还是下不去手呢?这么多的信号要从哪里开始实现??在这里也给大家提供一个思路,当你写完输入输出端口声明后,从output信号入手,在实现output信号的同时肯定会有一些中间信号需要实现,当你完成了这些信号的实现后,不知不觉就会把整个设计都完成了~

因此我们先从写地址通道的输出信号——awready入手。这时不得不再搬出上一篇文章的握手时序图了:

可以看到,在写回应通道握手前将写地址通道和写数据通道握手完毕就可以了。握手嘛!我会!

always@(posedge aclk or negedge aresetn)begin
    if(!aresetn)
        awready <= 1'b0;
    else if(awvalid && ~awready)
        awready <= 1'b1;
    else
        awready <= 1'b0;
end

当awvalid为高时,拉高一个周期的ready信号,就可以在这个周期进行写地址通道的握手。**但是,有没有感觉哪里不对劲呢?**好像这样写,每个awvalid有效时都会被从机响应,那从机有没有这个能力呢?这就不得不提一下outstanding机制了,当从机的outstanding为N时,从机可以缓冲N个指令,也就是说可以在执行一个指令时(读或写),继续与读地址或写地址通道握手N-1次。

在我们本次的小设计中outstanding设置为1,即在完成整个传输任务前不接收新的写指令。之后会在介绍outstanding的特性时在本设计基础上进行改进。因此,需要引入一个指示写过程正在占用总线的信号,暂设为hand_aw,该信号从写地址通道握手成功开始拉高,至写通道握手成功后开始恢复低电平,以等待下一次的写地址通道握手。

此时,上面的Verilog代码的第4行就变成了

else if(awvalid && ~awready && ~hand_aw)

再实现一下hand_aw信号:

always@(posedge aclk or negedge aresetn)begin
    if(!aresetn)
        hand_aw <= 1'b0;
    else if(awvalid && awready)
        hand_aw <= 1'b1;
    else if(wlast && wready)
        hand_aw <= 1'b0;
end

到此刻,我们已经完成了写地址通道的输出信号——awready信号的实现,此外,关于写地址通道,我们还需要用寄存器保存一下写通道的其他信号,在之后的时钟周期里就可以使用这些信号了~涉及到的信号主要有AWID、AWLEN、AWSIZE、AWBURST、AWLOCK、AWCACHE、AWPROT、AWQoS、AWREGION、AWUSER。

always@(posedge aclk or negedge aresetn)begin
    if(!aresetn)begin
        id		 <= 4'h0;
        awlen_r   <= 8'h00;
        awsize_r  <= 3'b000;// 1 Byte 
        awburst_r <= 2'b00;// FIXED burst type
        awlock_r  <= 1'b0; // Normal Transfer
        awcache_r <= 4'h0;
        awprot_r  <= 3'b000; 
        awqos_r   <= 4'h0;
        awregion_r<= 4'h0;
        awuser_r  <= 1'b0;
    end
    else if(awvalid && ~awready && ~hand_aw)begin
        id 		 <= awid;
        awlen_r   <= awlen;
        awsize_r  <= awsize;
        awburst_r <= awburst;
        awlock_r  <= awlock;
        awcache_r <= awcache;
        awprot_r  <= awprot; 
        awqos_r   <= awqos;
        awregion_r<= awregion; 
        awuser_r  <= awuser;
    end
end

由于在写地址期间,只能写入一个首地址,接下来的地址是通过从机自己计算的。因此我们不仅要完成首地址的存储,还要产生一个可以自增的地址信号。这就与burst type、burst size、burst_len这三个信号有紧密的联系,关系如下:

  • FIXED模式,地址信号不变;
  • INCR模式,地址一直增加,递增的值为(2^Burst_size);
  • WRAP模式,在地址在回环边界+(Number_Bytes * Burst_Len)前会一直递增,递增到回环边界时地址置位到回环起始的地方。

首先来实现2^Burst_size,我们可以直接将结果存储下来:

always@(*)begin
    case(awsize_r)
        3'b000:Num_Bytes = 8'd1;
        3'b001:Num_Bytes = 8'd2;
        3'b010:Num_Bytes = 8'd4;
        3'b011:Num_Bytes = 8'd8;        
        3'b100:Num_Bytes = 8'd16;
        3'b101:Num_Bytes = 8'd32;        
        3'b110:Num_Bytes = 8'd64;
        3'b111:Num_Bytes = 8'd128;
        default:Num_Bytes = 8'dx;
    endcase
end

继续来,产生一个在WRAP模式下,地址递增到(Number_Bytes * Burst_Len)的整数倍时的指示信号:

assign aw_wrap_size = {3'b000,awlen_r} << awsize_r;// << awsize = * 2^(burst_size) = * Number_Byetes
assign aw_wrap_en = ((awaddr_r & aw_wrap_size)  == aw_wrap_size) ? 1'b1 : 1'b0;

还是有必要详细解释一下上面为什么要这么写。在RTL设计时总会想方设法地规避乘法、除法等非常耗费资源的操作,因此在计算wrap size时运用了移位的方式。

当地址与aw_wrap_size相与后,与aw_wrap_size作比较就可以产生使能信号aw_wrap_en,重置地址。原因大家可以思考下,留个小作业~另外这里的写法会有两个数据位宽不一致导致截断的现象,后续可以再进行优化。

接下来完成地址的变化:

always@(posedge aclk)begin
    if(~awready && awvalid && ~hand_aw)
        awaddr_r <= awaddr;
    else if((awlen_cnt_r <= awlen_r) && wready && wvalid)begin// write data
        case(awburst_r)
        BURST_FIXED:
        	awaddr_r <= awaddr_r;// no increment
        BURST_INCR:
            awaddr_r <= awaddr_r + Num_Bytes;
        BURST_WRAP:
            if(aw_wrap_en)
                awaddr_r <= awaddr_r - aw_wrap_size;
            else
                awaddr_r <= awaddr_r + Num_Bytes;
       	default:awaddr_r <= 'dx;//reserved
        endcase
    end
end

这里就又引入了一个未知的信号: awlen_cnt_r,这个信号是计数写数据个数的信号,每个wvalid与wready握手的时刻计数值将会加一。多说一句,下面将组合逻辑与时序逻辑分开写的方式是比较推荐的。

always@(posedge aclk or negedge aresetn)begin
    if(!aresetn)
        awlen_cnt_r <= 'd0;
    else
        awlen_cnt_r <= awlen_cnt;
end

assign awlen_cnt = ((awlen_cnt_r <= awlen_r) && wready && wvalid) ? awlen_cnt_r + 1 : awlen_cnt_r;

接下来再完成一下写通道的wready信号的实现,本设计中暂时还没有写入延时,因此在写入时就将信号一直拉高了。整体实现其实与awready信号比较相似:

always@(posedge aclk or negedge aresetn)begin
    if(!aresetn)
        wready <= 1'b0;
    else if (~wready && wvalid)
        wready <= 1'b1;
    else if(wlast && wready)
        wready <= 1'b0;
end

最后是写回应通道的一些信号,bid、buser、bresp、bvalid等。由于本设计没有outstanding的特性,因此直接将id赋给bid输出即可,bresp也复位为OKAY,如果从机有对其他情形的回应,可以在这部分加内容。

// bid
assign bid = id;
// buser
assign buser = 1'b0;
// bresp
always@(posedge aclk or negedge aresetn)begin
    if(!aresetn)
        bresp <= RESP_OKAY;
    // add other response
end
//bvalid
always@(posedge aclk or negedge aresetn)begin
    if(!aresetn)
        bvalid <= 1'b0;
    else if(wlast && wready)
        bvalid <= 1'b1;
    else if(bvalid && bready)
        bvalid <= 1'b0;
end

目前,我们还剩下WDATA、WSTRB信号没有处理,好在没有做处理也能够做仿真,设计部分就肝到这里,剩下的随着下篇的读设计一起呈现给大家吧~

三、仿真

又到了激动人心的时刻了,是骡子是马得拿到仿真平台上去溜溜嘛!

仿真激励是使用的Xilinx的AXI4 VIP MASTER激励,testbench有过简单的修改,环境已经在链接中提供,大家可以直接在上面调试仿真。

首先是起始地址0x12,数据包大小2个byte(awsize = 1),传输长度12(awlen = 11),传输类型递增(awburst = 1)的验证

写地址握手过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XkTJaK6a-1597583309003)(sim_handshake.PNG)]

再看下地址递增情况,并观察下WSTRB信号与写通道握手信号:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t3KSQCNX-1597583309004)(sim_addr1.PNG)]

最后看下写回应通道的握手信号:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zwugZW9J-1597583309005)(sim_bchannel.PNG)]

将master发送设置为WRAP类型(awburst = 2),传输长度设为8(awlen=7),传输数据包大小4个byte(awsize=2),起始地址设为0x12。图中可以看到地址回环的情形,注意观察下aw_wrap_en的拉高时间。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kcaIef2h-1597583309008)(sim_wrap.PNG)]

至此,AXI4从机的写过程就基本完成了~之后我们将完成从机的读过程,欢迎关注后续的分享!
在这里插入图片描述

您可能感兴趣的与本文相关内容

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值