精华内容
下载资源
问答
  • apb协议

    2020-03-03 23:32:40
    一、apb协议英文原版下载地址 https://developer.arm.com/docs/ihi0024/c 二、apb简介 APB(Advanced Peripheral Bus),外围总线。APB属于AMBA 3 协议系列,它提供了一个低功耗的接口, 并降低了接口的复杂性。 APB...

    一、apb协议英文原版下载地址
    https://developer.arm.com/docs/ihi0024/c
    二、apb简介
    APB(Advanced Peripheral Bus),外围总线。APB属于AMBA 3 协议系列,它提供了一个低功耗的接口, 并降低了接口的复杂性。 APB接口用在低带宽和不需要高性能总线的外围设备上。 APB是非流水线结构,所有的信号仅与时钟上升沿相关,这样就可以简化APB外围设备的设计流程,每个传输至少耗用两个时钟周期。APB可以与AMBA高级高性能总线(AHB-Lite) 和AMBA 高级可扩展接口 (AXI)连接
    ​APB主要用于低带宽的周边外设之间的连接,例如UART、1284等,它的总线架构不像AHB支持多个主模块,**在APB里面唯一的主模块就是APB 桥。**其特性包括:两个时钟周期传输;无需等待周期和回应信号;控制逻辑简单,只有四个控制信号。APB上的传输可以用状态图来说明。
    在这里插入图片描述
    a) 系统初始化为IDLE状态,此时没有传输操作,也没有选中任何从模块。
    b) 当有传输要进行时,PSELx=1,PENABLE=0,系统进入SETUP状态,并只会在SETUP 状态停留一个周期。当PCLK的下一个上升沿时到来时,系统进入ENABLE 状态。
    c) 系统进入ENABLE状态时,维持之前在SETUP 状态的PADDR、PSEL、PWRITE不变,并将PENABLE置为1。传输也只会在ENABLE状态维持一个周期,在经过SETUP与ENABLE状态之后就已完成。之后如果没有传输要进行,就进入IDLE状态等待;如果有连续的传输,则进入SETUP状态。
    三、信号简介
    下表给出了APB的信号。APB的支持最大32-bit的数据位宽。APB协议有两个独立的数据通道,读通道和写通道,由于APB的两个通道没有自己的handshake信号(vaild/ ready),因此两个通道不会同时使用。
    在这里插入图片描述
    除了上表的信号外,APB还有两个信号接口:
    PPROT: 保护类型,分为 normal, privileged, secure, data/instruction 访问。
    PSTRRB: 写选通信号,指示哪个字节是有效的数据,PSTRB与PWDATA之间的关系为PSTRB[n] <–> PWDATA[(8n+7):(8n)]​
    四、写读传输
    4.1 无等待状态写传输
    下图显示了一个基本的无等待状态的写传输。
    在这里插入图片描述
    地址、写入数据、写入信号和选择信号都在时钟上升沿后改变。第一个时钟周期叫做Setup phase。 下一个时钟沿后使能信号PENABLE被置位,表示Access phase就位。地址、数据和控制信号在Access phase期间有效。传输在该周期后结束。使能信号PENABLE, 在传输结束后清空。 选择信号PSELx同样被置低,除非紧接着下一传输开始。
    4.2 有等待状态写传输
    下图展示了 PREADY 信号是如何扩展了从器件的传输。
    在这里插入图片描述
    在Access phase期间,当PENABLE为高,传输可以通过拉低PREADY来扩展传输。下述信号仍旧不变:PADDR、PWRITE、PSEL、 PENABLE、PWDATA。
    4.3 无等待状态读传输
    下图显示了一个读传输的无等待状态的时序图。从器件必须在读传输结束前提供数据。
    在这里插入图片描述
    4.4 有等待状态读传输
    图显示了信号是如何扩展传输的。如果在Access phase期间PREADY信号拉低,则传输被扩展。但下述信号不变:PADDR、PWRITE、 PSEL、 PENABLE.
    图中显示了如何使用PREADY信号来添加两个周期,你也可以添加数个周期。
    在这里插入图片描述
    4.5 错误响应
    使用PSLVERR来指示APB传输错误。当PSEL, PENABLE以及PREADY 都为高时, PSLVERR才在最后一个周期进行判断。 当任何一个PSEL, PENABLE或者PREADY为低时,你可以将PSLVERR拉低,这是推荐,并不是强制要求。 收到一个错误后,可能或不可能改变外围器件的状态。 APB外围设备不要求必须支持PSLVERR引脚,当不使用该引脚时,应被置低。
    写传输失败的例子。
    在这里插入图片描述
    读传输同样可以使用错误响应,下图便是一个错误响应的例子。
    在这里插入图片描述
    PSLVERR映射:
    AXI桥接到APB: AXI的RRESP/BRESP = APB的SLVERR(read: PSLVERR -> RRESP[1], write: PSLVERR -> BRESP[1])
    AHB桥接到APB: PSLVERR被映射到HRESP = ERROR(PSLVERR -> HRESP[0])

    下图给出了 APB的操作流程
    在这里插入图片描述
    状态机按照下面的状态执行:

    IDLE 这是默认的APB状态
    SETUP 当传输被请求时,总线进入SETUP状态,选择信号 PSELx,被置位。总线仅在SETUP 状态停留一个时钟周期,并在下一个时钟周期进入ACCESS状态
    ACCESS 使能信号PENABLE, 在ACCESS状态中置位。在传输从SETUP状态到ACCESS状态转变的过程中address, write, select和write data信号必须保持不变。从ACCESS状态退出,由从器件的PREADY 信号控制:a) 如果PREADY 为低,保持ACCESS状态。b)如果PREADY 为高,则退出ACCESS状态,如果此时没有其它传输请求,总线返回IDLE状态,否则进入SETUP状态。

    展开全文
  • APB协议

    2020-12-21 16:02:09
    Addr是层层映射的关系,AHB将一段地址划分给APB Bridge,APB bridge再将这段地址细分给多个不同的APB设备。 PCLK,PADDR,PWRITE,PENABLE,PWDATA都是广播的信号,PSEL信号是单独的片选。PRDATA通过MUX选择...

    Address decoding stage

    Addr是层层映射的关系,AHB将一段地址划分给APB Bridge,APB bridge再将这段地址细分给多个不同的APB设备。

     

    PCLK,PADDR,PWRITE,PENABLE,PWDATA都是广播的信号,PSEL信号是单独的片选。PRDATA通过MUX选择作为输出。

     

    PENABLE为高传输有效,APB传输分为两个cycle,setup和enable,先setup,后拉高ENABLE,完成传输,读时序和写时序类似。

    传输是有两拍的,在不同的状态(SETUP,ENABLE)之间转换

     

    AHB读取APB slave的数据,首先HADDR将读地址写出,slave拉低HREADY准备数据,PENABLE之后,bridge会将PRDATA通过组合逻辑传递到HRDATA,AHB读到数据

    不同的Master之间需要争夺总线,获得仲裁的grant之后才能访问slave

    CPU只有少数的interrupt接口,通过ISR来根据优先级将需要中断进行处理,最终选择一个中断送给CPU,CPU进入中断模式,会跳转到相应的中断服务程序地址来执行相应的中断服务程序。

     

     

     

    展开全文
  • AMBA-APB协议 芯片设计 总线协议 ASIC 硬件开发
  • APB协议详解

    千次阅读 多人点赞 2020-11-12 10:47:02
    APB协议详解背景介绍APB2读操作写操作APB3APB4 背景介绍 这里我先放上APB协议的官方文档,需要自取,放心食用~ APB协议是ARM公司中AMBA协议的一种。最早的APB协议现在叫做APB2,后来又有APB3和APB4。APB协议是向下...

    背景介绍

    这里我先放上APB协议的官方文档,需要自取,放心食用~
    链接:APB协议
    提取码:rtxz

    APB协议是ARM公司中AMBA协议的一种。最早的APB协议现在叫做APB2,后来又有APB3和APB4。APB协议是向下兼容的,随着时间的推移,根据实际需求,APB3在APB2的基础上添加一些功能,APB4在APB3的基础上再添加了一些功能。后面我会先从最基本的APB2讲起,逐步到APB4。
    什么是APB协议?在APB3官方文档里有这么一句话:
    The APB is part of the AMBA 3 protocol family. It provides a low-cost interface that is optimized for minimal power consumption and reduced interface complexity.
    The APB interfaces to any peripherals that are low-bandwidth and do not require the high performance of a pipelined bus interface. The APB has unpipelined protocol.
    翻译:
    APB是AMBA 3协议系列的一部分。 它提供了一种低成本接口,该接口经过了优化,可最大程度降低功耗并降低接口复杂性。 APB可以连接到任何低带宽不需要流水线总线接口高性能的外围设备。 APB是非流水线协议。
    紧接着还有这么两句话:
    All signal transitions are only related to the rising edge of the clock to enable the integration of APB peripherals easily into any design flow. Every transfer takes at least two cycles.
    所有信号跳变仅与时钟的上升沿相关,从而能够将APB外设轻松集成到任何设计流程中。 每次传输至少需要两个周期。
    虽然还没有看到具体的协议内容,但是我们已经知道了APB协议的特点

    • 低成本
    • 低功耗
    • 低带宽
    • 无流水线
    • 所有信号都是时钟上升沿有效
    • 进行一次数据传输至少需要两个周期

    APB2

    APB协议里面就是说的怎么进行数据传输,首先协议里面定义了很多端口,我们先来看看APB2协议里这些端口的定义

    Signal Description
    PCLK 时钟。APB协议里所有的数据传输都在PCLK上升沿进行
    PRESETn 复位。低电平有效
    PADDR APB地址总线。最大宽度为32位
    PSELx 选通。APB master会将此信号生成给每个slave。它指示已选择的slave,并且需要进行数据传输。 每个slave都有一个PSELx信号。
    PENABLE 使能。当它为高时,表示数据有效
    PWRITE 读写控制。为高时表示写操作,为低时表示读操作
    PWDATA 写数据。master通过PWDATA将数据写到slave,该总线最大宽度为32位
    PRDATA 读数据。master通过PRDATA将数据从slave读取回来,该总线最大宽度为32位

    看了这个端口定义,大家可能对master和slave还不太明白,解释一下,可以把APB看成一种“线”,这种“线”是来连接两个设备的,一个就是master,一个就是slave,所有的数据传输都是master来控制的,slave来回应。

    读操作

    接下来我们进入主题,研究下APB是具体怎么规定读操作的:master从slave读取数据
    在这里插入图片描述
    从这张图我们看出几点:

    • 所有的数据都是在PCLK上升沿跳变的
    • T0-T1:初始状态,准备开始数据传输
    • T1时刻:master向总线上发送了地址,这个地址是slave的地址;并且把PWRITE拉低,说明这次数据传输是一次读数据。PSEL拉高,APB协议里可能是有一个master,多个slave,当PADDR将地址发到总线上后,PSEL选择哪一个slave也就定了。
    • T1-T2时刻:保持现状不变,这个时候slave收到了mater的地址,读写控制,salve得知自己要讲这个地址对应的数据发送到master,所以它会做好准备。
    • T3时刻:PENABLE也就是使能信号拉高,这个时候也就是master通知slave进行PRDATA的传输

    总结一下:一开始我们就说到,APB数据传输至少需要两个周期,也就是T1-T3。其实很简单,第一个周期做准备工作(把PADDR,PWRITE,PSEL发送到总线),第二个周期进行传输读或写的data(PENABLE拉高,表面当前时刻,数据有效,是master想要的数据!)

    写操作

    我们看看写操作,和读操作很类似
    在这里插入图片描述

    • T0-T1:初始状态
    • T1-T2:master把PADDR和PWRITE放在总线上,通过PSEL选择一个slave,slave得知mater将要进行一次写操作,并且master把需要写进slave的data也放到总线上。
    • T2:PENABLE为高,表示当前数据有效,并且master将data写入slave
    • T3:数据传输结束,再次回到初始状态

    通过读写操作的时序图,我们可以看到,无论是读还是写,都是两个周期。在第一个周期,PSEL为高,PENABLE为低,这个时候为data的传输做准备工作;第二个周期里,PSEL和PENABLE同时为高,进行data的传输。

    APB3

    在APB2诞生后,随着行业发展,对于APB协议有了新的需求,ARM公司针对这些需求,在APB2的基础上添加了两个端口,一个是PREADY和PSLVERR。PREADY是一个对于slave的准备信号,用于扩展APB的传输。PSLVERR是一个错误反馈信号,表示当前传输的数据有误。把这种协议叫做APB3。

    只要你理解了APB2,APB3就会很简单,我们直接看时序图

    读操作,无等待

    这种情况和APB2的读操作没有区别。
    在这里插入图片描述
    相对于APB2无非就是多了个PREADY,也就是说,当PSEL为高,PENABLE为高时,总线会看PREADY是否为高,如果为高,则进行数据传输,如果为低,那么等待其变为高。
    我们讨论的这种情况,无等待,无错误,和APB2的没有区别。

    读操作,有等待

    在这里插入图片描述
    我们看到这张时序图,当PSEL和PENABLE都为高的时候,PREADY为低,说明slave没有准备好,再给他一点时间,在T4时刻这条虚线,发现PSEL和PENABLE为高,但是PREADY还是为低,继续等待。在T5时刻这条虚线,发现PSEL,PENABLE,PREADY都为高,说明这个时候采样的Data是我们想要读取的data。

    写操作,无等待

    直接放时序图,和APB2一样,应该不需要再解释了
    在这里插入图片描述

    写操作,有等待

    在这里插入图片描述
    和之前说的一样,也是先判断PSEL和PENABLE,都为高时再判断PREADY,如果为低,就保持现状,为高这表明数据有效,进行传输。

    错误反馈

    我们可以通过使用PSLVERR来指示APB传输上的错误情况。读取和写入事务都可能发生错误。当PSEL,PENABLE和PREADY均为高电平时,仅在APB传输的最后一个周期内才认为PSLVERR有效,其他时间不考虑PSLVERR。

    写操作

    在这里插入图片描述
    在前面的有等待的写操作的基础上,添加PSLVERR,也就是在T4时刻采样,发现PSEL,PENABLE和PREADY均为高电平的前提下,PSLVERR为高,说明这次数据传输有错误。

    读操作

    在这里插入图片描述
    读操作也是一样,不再赘述。

    APB4

    APB4在APB3的基础上添加了两个端口,一个是PPROT,一个是PSTRB。 (其实在平时工作中几乎用不到这两个,主要还是APB3)
    PPROT:一种保护信号,可支持APB上的非安全传输和安全传输。
    PSTRB:一个写选通信号,用于在写数据总线上进行稀疏数据传输。

    我们直接来看看官方文档里对这两个端口的解释

    PPROT

    为了支持复杂的系统设计,通常需要互连和系统中的其他设备提供针对非法交易的保护。 对于APB接口,此保护由PPROT [2:0]信号提供。
    这个信号位宽为3,每一位代表不同的作用。
    PPROT [0]:普通或特权

    • 低代表正常
    • 高代表特权

    一些master使用它来指示其处理模式。特权处理模式通常在系统内具有更高级别的访问权限。

    PPROT [1]:安全还是非安全

    • 低代表安全
    • 高代表非安全
      这用于需要更大程度区分处理模式的系统中。

    PPROT [2]:数据还是指令

    • 低代表数据
    • 高代表指令
      该位指示事务是数据访问还是指令访问。此指示仅作为提示,并非在所有情况下都是准确的。 例如,传输包含指令和数据项的混合。 建议默认情况下,除非明确将其称为指令访问,否则将访问标记为数据访问。

    总结一张表就是这样:
    在这里插入图片描述

    PSTRB

    写选通信号PSTRB使写数据总线上的稀疏数据传输成为可能。 每个写选通信号对应于写数据总线的一个字节。 当置为高电平时,写选通脉冲指示写数据总线的相应字节通道包含有效信息。 写数据总线的每八位有一个写选通脉冲,因此PSTRB [n]对应于PWDATA [(8n + 7): ( 8n)]。 下图显示了32位数据总线上的这种关系。
    在这里插入图片描述

    总结

    APB协议中的2,3,4版本,都是在前一个版本上增加了一些功能,但是APB4的PPROT和PSTRB用的特别少,大家可以不用太在意,如果需要用到的时候再去查阅即可。只要能理解APB3中几种类型的数据传输就可以了(掌握时序图)

    最后:若有错误的地方,望能指出~

    展开全文
  • AHB与APB协议小解.docx

    2019-11-27 11:32:53
    简单说明了AHB与APB协议,对比了AHB与APB的区别。说明了接口的一些设计趣点,供大家查看参阅。
  • APB协议UVM验证环境的搭建 一、编译文件 只需编译这两个文件即可 apb_pkg.sv 里面包含了"apb.svh",即编译apb_pkg.sv这个文件的同时,也会编译所需要的所有的头文件。 `ifndef APB_PKG_SV `define APB_PKG_SV ...

    APB协议UVM验证环境的搭建

    一、编译文件

    只需编译这两个文件即可
    在这里插入图片描述

    apb_pkg.sv

    里面包含了"apb.svh",即编译apb_pkg.sv这个文件的同时,也会编译所需要的所有的头文件。

    `ifndef APB_PKG_SV
    `define APB_PKG_SV
    
    package apb_pkg;
    
    import uvm_pkg::*;
    `include "uvm_macros.svh"
    
    `include "apb.svh"
    
    endpackage : apb_pkg
    
       
    `endif //  `ifndef APB_PKG_SV
    
    

    apb.svh

    `ifndef APB_SVH
    `define APB_SVH
    
    
    `include "apb_transfer.sv"
    `include "apb_config.sv"
    
    //master所有的头文件
    `include "apb_master_driver.svh"
    `include "apb_master_monitor.svh"
    `include "apb_master_sequencer.svh"
    `include "apb_master_agent.svh"
    
    //slave所有的头文件
    `include "apb_slave_driver.svh"
    `include "apb_slave_monitor.svh"
    `include "apb_slave_sequencer.svh"
    `include "apb_slave_agent.svh"
    
    //master头文件里面具体的实现方法
    `include "apb_master_driver.sv"       
    `include "apb_master_monitor.sv"
    `include "apb_master_sequencer.sv"
    `include "apb_master_agent.sv"
    `include "apb_master_seq_lib.sv"
    
    //slave头文件里面具体的实现方法
    `include "apb_slave_driver.sv"       
    `include "apb_slave_monitor.sv"
    `include "apb_slave_sequencer.sv"
    `include "apb_slave_agent.sv"
    `include "apb_slave_seq_lib.sv"
    
    
    
       
    `endif //  `ifndef APB_SVH
    

    再来编译apb_tb.sv文件

    编译的同时,也会编译"apb_tests.svh"、"apb_if.sv"这两个文件。例化协议接口,配置顶层环境的master和slave,默认执行“apb_single_transaction_test”这个测试用例。

    `timescale 1ps/1ps
    import uvm_pkg::*;
    `include "uvm_macros.svh"
    `include "apb_tests.svh"
    `include "apb_if.sv"
    module apb_tb;
      bit clk, rstn;
      initial begin
        fork
          begin 
            forever #5ns clk = !clk;
          end
          begin
            #100ns;
            rstn <= 1'b1;
            #100ns;
            rstn <= 1'b0;
            #100ns;
            rstn <= 1'b1;
          end
        join_none
      end
    
      apb_if intf(clk, rstn);
    
      initial begin
        uvm_config_db#(virtual apb_if)::set(uvm_root::get(), "uvm_test_top.env.mst", "vif", intf);
        uvm_config_db#(virtual apb_if)::set(uvm_root::get(), "uvm_test_top.env.slv", "vif", intf);
        run_test("apb_single_transaction_test");
      end
    
    endmodule
    
    

    apb_tests.svh

    `ifndef APB_TESTS_SV
    `define APB_TESTS_SV
    
    import apb_pkg::*;
    
    class apb_env extends uvm_env;
      apb_master_agent mst;
      apb_slave_agent slv;
      `uvm_component_utils(apb_env)
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
      function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        mst = apb_master_agent::type_id::create("mst", this);
        slv = apb_slave_agent::type_id::create("slv", this);
      endfunction
    endclass
    
    class apb_base_test extends uvm_test;
      apb_env env;
      `uvm_component_utils(apb_base_test)
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
      function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        env = apb_env::type_id::create("env", this);
      endfunction
    endclass
    
    class apb_base_test_sequence extends uvm_sequence #(apb_transfer);
      bit[31:0] mem[bit[31:0]];		//关联数组mem,用来master和slave之间的数据比对,test和slave中都有一个mem
      `uvm_object_utils(apb_base_test_sequence)
      function new(string name=""); 
        super.new(name);
      endfunction : new
      function bit check_mem_data(bit[31:0] addr, bit[31:0] data);
        if(mem.exists(addr)) begin
          if(data != mem[addr]) begin
            `uvm_error("CMPDATA", $sformatf("addr 32'h%8x, READ DATA expected 32'h%8x != actual 32'h%8x", addr, mem[addr], data))
            return 0;
          end
          else begin
            `uvm_info("CMPDATA", $sformatf("addr 32'h%8x, READ DATA 32'h%8x comparing success!", addr, data), UVM_LOW)
            return 1;
          end
        end
        else begin
          if(data != 0) begin
            `uvm_error("CMPDATA", $sformatf("addr 32'h%8x, READ DATA expected 32'h00000000 != actual 32'h%8x", addr, data))
            return 0;
          end
          else begin
            `uvm_info("CMPDATA", $sformatf("addr 32'h%8x, READ DATA 32'h%8x comparing success!", addr, data), UVM_LOW)
            return 1;
          end
        end
      endfunction: check_mem_data
    
      task wait_reset_release();
        @(negedge apb_tb.rstn);
        @(posedge apb_tb.rstn);
      endtask
    
      task wait_cycles(int n);
        repeat(n) @(posedge apb_tb.clk);
      endtask
    
      function bit[31:0] get_rand_addr();
        bit[31:0] addr;
        void'(std::randomize(addr) with {addr[31:12] == 0; addr[1:0] == 0;});
        return addr;
      endfunction
    endclass
    
    class apb_single_transaction_sequence extends apb_base_test_sequence;
      apb_master_single_write_sequence single_write_seq;
      apb_master_single_read_sequence single_read_seq;
      apb_master_write_read_sequence write_read_seq;
      rand int test_num = 100;
      constraint cstr{
        soft test_num == 100;
      }
      `uvm_object_utils(apb_single_transaction_sequence)    
      function new(string name=""); 
        super.new(name);
      endfunction : new
      task body();
        bit[31:0] addr;
        this.wait_reset_release();
        this.wait_cycles(10);
    
        // TEST continous write transaction
        `uvm_info(get_type_name(), "TEST continous write transaction...", UVM_LOW)
        repeat(test_num) begin
          addr = this.get_rand_addr();
          `uvm_do_with(single_write_seq, {addr == local::addr; data == local::addr;})
          mem[addr] = addr;
        end
    
        // TEST continous read transaction
        `uvm_info(get_type_name(), "TEST continous read transaction...", UVM_LOW)
        repeat(test_num) begin
          addr = this.get_rand_addr();
          `uvm_do_with(single_read_seq, {addr == local::addr;})
          void'(this.check_mem_data(addr, single_read_seq.data));
        end
    
        // TEST read transaction after write transaction
        `uvm_info(get_type_name(), "TEST read transaction after write transaction...", UVM_LOW)
        repeat(test_num) begin
          addr = this.get_rand_addr();
          `uvm_do_with(single_write_seq, {addr == local::addr; data == local::addr;})
          mem[addr] = addr;
          `uvm_do_with(single_read_seq, {addr == local::addr;})
          void'(this.check_mem_data(addr, single_read_seq.data));
        end
    
    
        // TEST read transaction immediately after write transaction
        `uvm_info(get_type_name(), "TEST read transaction immediately after write transaction", UVM_LOW)
        repeat(test_num) begin
          addr = this.get_rand_addr();
          `uvm_do_with(write_read_seq, {addr == local::addr; data == local::addr;})
          mem[addr] = addr;
          void'(this.check_mem_data(addr, write_read_seq.data));
        end
    
        this.wait_cycles(10);
      endtask
    endclass: apb_single_transaction_sequence
    
    class apb_single_transaction_test extends apb_base_test;
      `uvm_component_utils(apb_single_transaction_test)
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
      task run_phase(uvm_phase phase);
        apb_single_transaction_sequence seq = new();
        phase.raise_objection(this);
        super.run_phase(phase);
        seq.start(env.mst.sequencer);
        phase.drop_objection(this);
      endtask
    endclass: apb_single_transaction_test
    
    class apb_burst_transaction_sequence extends apb_base_test_sequence;
      apb_master_burst_write_sequence burst_write_seq;
      apb_master_burst_read_sequence burst_read_seq;
      rand int test_num = 100;
      constraint cstr{
        soft test_num == 100;
      }
      `uvm_object_utils(apb_burst_transaction_sequence)
      function new(string name=""); 
        super.new(name);
      endfunction : new
      task body();
        bit[31:0] addr;
        this.wait_reset_release();
        this.wait_cycles(10);
    
        // TEST continous write transaction
        repeat(test_num) begin
          addr = this.get_rand_addr();
          `uvm_do_with(burst_write_seq, {addr == local::addr;})
          foreach(burst_write_seq.data[i]) begin
            mem[addr+(i<<2)] = burst_write_seq.data[i];
          end
          `uvm_do_with(burst_read_seq, {addr == local::addr; data.size() == burst_write_seq.data.size();})
          foreach(burst_read_seq.data[i]) begin
            void'(this.check_mem_data(addr+(i<<2), burst_write_seq.data[i]));
          end
        end
    
        this.wait_cycles(10);
      endtask
    endclass: apb_burst_transaction_sequence
    
    class apb_burst_transaction_test extends apb_base_test;
      `uvm_component_utils(apb_burst_transaction_test)
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
      task run_phase(uvm_phase phase);
        apb_burst_transaction_sequence seq = new();
        phase.raise_objection(this);
        super.run_phase(phase);
        seq.start(env.mst.sequencer);
        phase.drop_objection(this);
      endtask
    endclass: apb_burst_transaction_test
    
    
    
    `endif // APB_TESTS_SV
    

    apb_if.sv

    
    `ifndef APB_IF_SV
    `define APB_IF_SV
    
    interface apb_if (input clk, input rstn);
    
      logic [31:0] paddr;
      logic        pwrite;
      logic        psel;
      logic        penable;
      logic [31:0] pwdata;
      logic [31:0] prdata;
    
      // Control flags
      bit                has_checks = 1;
      bit                has_coverage = 1;
    
      // Actual Signals 
      // USER: Add interface signals
    
      clocking cb_mst @(posedge clk);
        // USER: Add clocking block detail
        default input #1ps output #1ps;
        output paddr, pwrite, psel, penable, pwdata;
        input prdata;
      endclocking : cb_mst
    
      clocking cb_slv @(posedge clk);
       // USER: Add clocking block detail
        default input #1ps output #1ps;
        input paddr, pwrite, psel, penable, pwdata;
        output prdata;
      endclocking : cb_slv
    
      clocking cb_mon @(posedge clk);
       // USER: Add clocking block detail
        default input #1ps output #1ps;
        input paddr, pwrite, psel, penable, pwdata, prdata;
      endclocking : cb_mon
    
      // Coverage and assertions to be implemented here.
      // USER: Add assertions/coverage here
    
      // APB command covergroup
      covergroup cg_apb_command @(posedge clk iff rstn);
        pwrite: coverpoint pwrite{
          type_option.weight = 0;
          bins write = {1};
          bins read  = {0};
    
        }
        psel : coverpoint psel{
          type_option.weight = 0;
          bins sel   = {1};
          bins unsel = {0};
        }
        cmd  : cross pwrite, psel{
          bins cmd_write = binsof(psel.sel) && binsof(pwrite.write);
          bins cmd_read  = binsof(psel.sel) && binsof(pwrite.read);
          bins cmd_idle  = binsof(psel.unsel);
        }
      endgroup
    
      // APB transaction timing group
      covergroup cg_apb_trans_timing_group @(posedge clk iff rstn);
        psel: coverpoint psel{
          bins single   = (0 => 1 => 1  => 0); 
          bins burst_2  = (0 => 1 [*4]  => 0); 
          bins burst_4  = (0 => 1 [*8]  => 0); 
          bins burst_8  = (0 => 1 [*16] => 0); 
          bins burst_16 = (0 => 1 [*32] => 0); 
          bins burst_32 = (0 => 1 [*64] => 0); 
        }
        penable: coverpoint penable {
          bins single = (0 => 1 => 0 [*2:10] => 1);
          bins burst  = (0 => 1 => 0         => 1);
        }
      endgroup
    
      // APB write & read order group
      covergroup cg_apb_write_read_order_group @(posedge clk iff (rstn && penable));
        write_read_order: coverpoint pwrite{
          bins write_write = (1 => 1);
          bins write_read  = (1 => 0);
          bins read_write  = (0 => 1);
          bins read_read   = (0 => 0);
        } 
      endgroup
    
      initial begin
        automatic cg_apb_command cg0 = new();
        automatic cg_apb_trans_timing_group cg1 = new();
        automatic cg_apb_write_read_order_group cg2 = new();
      end
    
    endinterface : apb_if
    
    `endif // APB_IF_SV
    
    

    二、apb_tests.sv代码分析

    apb_base_test_sequence

    check_mem_data()方法原理结构框图:

    关联数组mem,用来master和slave之间的数据比对,test和slave中都有一个mem,master通过接口发送数据给slave,slave中的mem和test中的mem都会存储这个数据,等从slave读回数据时,就可以和test中mem里面的数据进行比较。
    在这里插入图片描述

    apb_single_transaction_sequence

    随机化addr,测试连续写操作

        // TEST continous write transaction
        `uvm_info(get_type_name(), "TEST continous write transaction...", UVM_LOW)
        repeat(test_num) begin
          addr = this.get_rand_addr();
          `uvm_do_with(single_write_seq, {addr == local::addr; data == local::addr;})
          mem[addr] = addr;
        end
    

    随机化addr,测试连续读操作,并比较数据是否一致

        // TEST continous read transaction
        `uvm_info(get_type_name(), "TEST continous read transaction...", UVM_LOW)
        repeat(test_num) begin
          addr = this.get_rand_addr();
          `uvm_do_with(single_read_seq, {addr == local::addr;})
          void'(this.check_mem_data(addr, single_read_seq.data));
        end
    

    随机化addr,先进行写操作,再进行读操作,并比较读取的数据是否一致

        // TEST read transaction after write transaction
        `uvm_info(get_type_name(), "TEST read transaction after write transaction...", UVM_LOW)
        repeat(test_num) begin
          addr = this.get_rand_addr();
          `uvm_do_with(single_write_seq, {addr == local::addr; data == local::addr;})
          mem[addr] = addr;
          `uvm_do_with(single_read_seq, {addr == local::addr;})
          void'(this.check_mem_data(addr, single_read_seq.data));
        end
    

    随机化addr,写完立即读,中间没有idle空闲,并检查读取数据是否一致

        // TEST read transaction immediately after write transaction
        `uvm_info(get_type_name(), "TEST read transaction immediately after write transaction", UVM_LOW)
        repeat(test_num) begin
          addr = this.get_rand_addr();
          `uvm_do_with(write_read_seq, {addr == local::addr; data == local::addr;})
          mem[addr] = addr;
          void'(this.check_mem_data(addr, write_read_seq.data));
        end
    

    例化并挂载

    class apb_single_transaction_test extends apb_base_test;
      `uvm_component_utils(apb_single_transaction_test)
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
      task run_phase(uvm_phase phase);
        apb_single_transaction_sequence seq = new();
        phase.raise_objection(this);
        super.run_phase(phase);
        seq.start(env.mst.sequencer);
        phase.drop_objection(this);
      endtask
    endclass: apb_single_transaction_test
    

    apb_burst_transaction_sequence

    先全部写操作完毕,在完全读出来,地址是连续增长的

        // TEST continous write transaction
        repeat(test_num) begin
          addr = this.get_rand_addr();
          `uvm_do_with(burst_write_seq, {addr == local::addr;})
          foreach(burst_write_seq.data[i]) begin
            mem[addr+(i<<2)] = burst_write_seq.data[i];
          end
          `uvm_do_with(burst_read_seq, {addr == local::addr; data.size() == burst_write_seq.data.size();})
          foreach(burst_read_seq.data[i]) begin
            void'(this.check_mem_data(addr+(i<<2), burst_write_seq.data[i]));
          end
        end
    

    例化并挂载

    class apb_burst_transaction_test extends apb_base_test;
      `uvm_component_utils(apb_burst_transaction_test)
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
      task run_phase(uvm_phase phase);
        apb_burst_transaction_sequence seq = new();
        phase.raise_objection(this);
        super.run_phase(phase);
        seq.start(env.mst.sequencer);
        phase.drop_objection(this);
      endtask
    endclass: apb_burst_transaction_test
    

    三、apb_master_agent.sv代码分析

    agent包括三个组件driversequencermonitor,以及configinterface

    例化monitor,根据配置决定是否例化driversequencer

    function void apb_master_agent::build();
      super.build();
      // get config
      if( !uvm_config_db#(apb_config)::get(this,"","cfg", cfg)) begin
        `uvm_warning("GETCFG","cannot get config object from config DB")
         cfg = apb_config::type_id::create("cfg");
      end
      // get virtual interface
      if( !uvm_config_db#(virtual apb_if)::get(this,"","vif", vif)) begin
        `uvm_fatal("GETVIF","cannot get vif handle from config DB")
      end
      monitor = apb_master_monitor::type_id::create("monitor",this);
      monitor.cfg = cfg;
      if(cfg.is_active == UVM_ACTIVE) begin
        sequencer = apb_master_sequencer::type_id::create("sequencer",this);
        sequencer.cfg = cfg;
        driver = apb_master_driver::type_id::create("driver",this);
        driver.cfg = cfg;
      end
    endfunction : build
    

    根据配置决定是否连接driversequencer

    function void apb_master_agent::connect();
      assign_vi(vif);
    
      if(is_active == UVM_ACTIVE) begin
        driver.seq_item_port.connect(sequencer.seq_item_export);       
      end
    
    endfunction : connect
    

    根据配置决定是否vif和driver、sequencer之间的连接

    function void apb_master_agent::assign_vi(virtual apb_if vif);
       monitor.vif = vif;
       if (is_active == UVM_ACTIVE) begin
          sequencer.vif = vif; 
          driver.vif = vif; 
        end
    endfunction : assign_vi
    

    四、apb_master_driver.sv代码分析

    并行触发get_and_drive()reset_listener()

    task apb_master_driver::run();
       fork
         get_and_drive();
         reset_listener();
       join_none
    endtask : run
    

    捕捉到复位信号以后,所以信号清零

    task apb_master_driver::reset_listener();
      `uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)
      fork
        forever begin
          @(negedge vif.rstn); // ASYNC reset
          vif.paddr <= 0;
          vif.pwrite <= 0;
          vif.psel <= 0;
          vif.penable <= 0;
          vif.pwdata <= 0;
        end
      join_none
    endtask
    

    sequencesequencer需要握手,获取transaction以后调用driver_transfer()发送。发送成功以后克隆request生成新的response,作为响应发送回去。

    task apb_master_driver::get_and_drive();
      forever begin
        seq_item_port.get_next_item(req);
        `uvm_info(get_type_name(), "sequencer got next item", UVM_HIGH)
        drive_transfer(req);
        void'($cast(rsp, req.clone()));
        rsp.set_sequence_id(req.get_sequence_id());
        seq_item_port.item_done(rsp);
        `uvm_info(get_type_name(), "sequencer item_done_triggered", UVM_HIGH)
      end
    endtask : get_and_drive
    
    task apb_master_driver::drive_transfer (apb_transfer t);
      `uvm_info(get_type_name(), "drive_transfer", UVM_HIGH)
      case(t.trans_kind)
        IDLE    : this.do_idle();
        WRITE   : this.do_write(t);
        READ    : this.do_read(t);
        default : `uvm_error("ERRTYPE", "unrecognized transaction type")
      endcase
    endtask : drive_transfer
    

    根据trans_kind判断操作命令,分别调用相对应的方法。

    task apb_master_driver::do_write(apb_transfer t);
      `uvm_info(get_type_name(), "do_write ...", UVM_HIGH)
      //写操作一共分为两个周期,根据协议第一个周期setup准备阶段需要如下操作
      @(vif.cb_mst);
      vif.cb_mst.paddr <= t.addr;
      vif.cb_mst.pwrite <= 1;
      vif.cb_mst.psel <= 1;
      vif.cb_mst.penable <= 0;
      vif.cb_mst.pwdata <= t.data;
      //第二个阶段拉高penable信号,发送数据
      @(vif.cb_mst);
      vif.cb_mst.penable <= 1;
      repeat(t.idle_cycles) this.do_idle();		//取决于transaction里面的idle
    endtask: do_write
    
    task apb_master_driver::do_read(apb_transfer t);
      `uvm_info(get_type_name(), "do_write ...", UVM_HIGH)
      //第一个阶段
      @(vif.cb_mst);
      vif.cb_mst.paddr <= t.addr;
      vif.cb_mst.pwrite <= 0;
      vif.cb_mst.psel <= 1;
      vif.cb_mst.penable <= 0;
      //第二个阶段
      @(vif.cb_mst);
      vif.cb_mst.penable <= 1;
      #100ps;	//需要采样数据,人为添加100ps的delay,是为了避免delta-cycle
      t.data = vif.prdata;		//采样数据
      repeat(t.idle_cycles) this.do_idle();
    endtask: do_read
    
    task apb_master_driver::do_idle();
      `uvm_info(get_type_name(), "do_idle ...", UVM_HIGH)
      @(vif.cb_mst);
      //根据协议,paddr、pwrite可以保持不变,等待下一次的传输,这是为了省电
      //vif.cb_mst.paddr <= 0;
      //vif.cb_mst.pwrite <= 0;
      vif.cb_mst.psel <= 0;
      vif.cb_mst.penable <= 0;
      vif.cb_mst.pwdata <= 0;
    endtask:do_idle
    

    五、apb_master_monitor.sv代码分析

    collect_transfer()方法

    在时钟上升沿,同时psel=1penabl=0的时候,判断当前情况下pwrite信号,在第二个周期进行读或者写操作。

    task apb_master_monitor::collect_transfer();
      apb_transfer t;
      // Advance clock
      @(vif.cb_mon);
      if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
        t = apb_transfer::type_id::create("t");
        case(vif.cb_slv.pwrite)
          1'b1    : begin
                      @(vif.cb_mon);
                      t.addr = vif.cb_mon.paddr;
                      t.data = vif.cb_mon.pwdata;
                      t.trans_kind = WRITE;
                    end 
          1'b0    : begin
                      @(vif.cb_mon);
                      t.addr = vif.cb_mon.paddr;
                      t.data = vif.cb_mon.prdata;
                      t.trans_kind = READ;
                    end
          default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
        endcase
        item_collected_port.write(t);
      end
    endtask: collect_transfer
    

    六、apb_master_seq_lib.sv代码分析

    apb_master_single_write_sequence

    使用宏'uvm_do_with发送数据。

    class apb_master_single_write_sequence extends apb_master_base_sequence;
      rand bit [31:0]      addr;
      rand bit [31:0]      data;
    
      `uvm_object_utils(apb_master_single_write_sequence)    
      function new(string name=""); 
        super.new(name);
      endfunction : new
    
      virtual task body();
        `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
    	  `uvm_do_with(req, {trans_kind == WRITE; addr == local::addr; data == local::data;})
        get_response(rsp);
        `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
      endtask: body
    
    endclass: apb_master_single_write_sequence
    

    apb_master_single_read_sequence

    读操作,拿到返回的rsp的数据后存储在成员变量data里。

    class apb_master_single_read_sequence extends apb_master_base_sequence;
      rand bit [31:0]      addr;
      rand bit [31:0]      data;
    
      `uvm_object_utils(apb_master_single_read_sequence)    
      function new(string name=""); 
        super.new(name);
      endfunction : new
    
      virtual task body();
        `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
    	  `uvm_do_with(req, {trans_kind == READ; addr == local::addr;})
        get_response(rsp);
        data = rsp.data;
        `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
      endtask: body
    
    endclass: apb_master_single_read_sequence
    

    apb_master_write_read_sequence

    写操作后进行读操作,所以idle_cycles == 0

    class apb_master_write_read_sequence extends apb_master_base_sequence;
      rand bit [31:0]    addr;
      rand bit [31:0]    data;
      rand int           idle_cycles; 
      constraint cstr{
        idle_cycles == 0;
      }
    
      `uvm_object_utils(apb_master_write_read_sequence)    
      function new(string name=""); 
        super.new(name);
      endfunction : new
    
      virtual task body();
        `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
    	  `uvm_do_with(req,  {trans_kind == WRITE; 
                            addr == local::addr; 
                            data == local::data;
                            idle_cycles == local::idle_cycles;
                           })
        get_response(rsp);
        `uvm_do_with(req, {trans_kind == READ; addr == local::addr;})
        get_response(rsp);
        data = rsp.data;
        `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
      endtask: body
    
    endclass: apb_master_write_read_sequence
    

    apb_master_burst_write_sequence

    连续的写操作,按照地址增长的顺序,把所有的数据写到data数组中。因为是连续写操作,所以idle_cycles == 0,即数据之间没有空闲周期。

    class apb_master_burst_write_sequence extends apb_master_base_sequence;
      rand bit [31:0]      addr;
      rand bit [31:0]      data[];
      constraint cstr{
        soft data.size() inside {4, 8, 16, 32};
        foreach(data[i]) soft data[i] == addr + (i << 2);
      }
    
      `uvm_object_utils(apb_master_burst_write_sequence)    
      function new(string name=""); 
        super.new(name);
      endfunction : new
    
      virtual task body();
        `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
        foreach(data[i]) begin
    	    `uvm_do_with(req, {trans_kind == WRITE; 
                             addr == local::addr + (i<<2); 
                             data == local::data[i];
                             idle_cycles == 0;
                            })
          get_response(rsp);
        end
        `uvm_do_with(req, {trans_kind == IDLE;})
        get_response(rsp);
        `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
      endtask: body
    endclass: apb_master_burst_write_sequence
    

    apb_master_burst_read_sequence

    连续的读操作,每次读取回来的数据,从rsp中拿出来放到data数组中。全部读取完成之后,将总线置为IDLE

    class apb_master_burst_read_sequence extends apb_master_base_sequence;
      rand bit [31:0]      addr;
      rand bit [31:0]      data[];
      constraint cstr{
        soft data.size() inside {4, 8, 16, 32};
      }
      `uvm_object_utils(apb_master_burst_read_sequence)
      function new(string name=""); 
        super.new(name);
      endfunction : new
    
      virtual task body();
        `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
        foreach(data[i]) begin
    	    `uvm_do_with(req, {trans_kind == READ; 
                             addr == local::addr + (i<<2); 
                             idle_cycles == 0;
                            })
          get_response(rsp);
          data[i] = rsp.data;
        end
        `uvm_do_with(req, {trans_kind == IDLE;})
        get_response(rsp);
        `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
      endtask: body
    endclass: apb_master_burst_read_sequence
    

    七、apb_slave_driver.sv代码分析

    slave要接收master发送过来的数据,所以要模拟一个存储功能,即关联数组mem

      bit[31:0] mem [bit[31:0]];
    

    run()方法

    三个方法并行执行

    task apb_slave_driver::run();
       fork
         get_and_drive();
         reset_listener();
         drive_response();
       join_none
    endtask : run
    

    get_and_drive()方法

    task apb_slave_driver::get_and_drive();
      forever begin
        seq_item_port.get_next_item(req);
        `uvm_info(get_type_name(), "sequencer got next item", UVM_HIGH)
        void'($cast(rsp, req.clone()));
        rsp.set_sequence_id(req.get_sequence_id());
        seq_item_port.item_done(rsp);
        `uvm_info(get_type_name(), "sequencer item_done_triggered", UVM_HIGH)
      end
    endtask : get_and_drive
    

    reset_listener()方法

    等待复位信号,将prdata <= 0,同时清空mem里面的数据。

    task apb_slave_driver::reset_listener();
      `uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)
      fork
        forever begin
          @(negedge vif.rstn); // ASYNC reset
          vif.prdata <= 0;
          this.mem.delete(); // reset internal memory
        end
      join_none
    endtask: reset_listener
    

    drive_response()方法

    如果当前这一周期是SETUP阶段,即psel = 1 && penable = 0,进而判断是写操作还是读操作,然后调用相对应的方法。

    task apb_slave_driver::drive_response();
      `uvm_info(get_type_name(), "drive_response", UVM_HIGH)
      forever begin
        @(vif.cb_slv);
        if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
          case(vif.cb_slv.pwrite)
            1'b1    : this.do_write();
            1'b0    : this.do_read();
            default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
          endcase
        end
        else begin
          this.do_idle();
        end
      end
    endtask : drive_response
    

    do_write()方法

    如果是写操作,那么等待时钟下一拍,拿到addrdata并放到mem中。

    task apb_slave_driver::do_write();
      bit[31:0] addr;
      bit[31:0] data;
      `uvm_info(get_type_name(), "do_write", UVM_HIGH)
      @(vif.cb_slv);
      addr = vif.cb_slv.paddr;
      data = vif.cb_slv.pwdata;
      mem[addr] = data;
    endtask: do_write
    

    do_read()方法

    如果是读操作,等待penable=1,并且判断mem中是否写过该addr,如果有则写入data,没有则将data置为0,即还是初始化的数据。等待一个延迟后,将data驱动到总线上面。

    task apb_slave_driver::do_read();
      bit[31:0] addr;
      bit[31:0] data;
      `uvm_info(get_type_name(), "do_read", UVM_HIGH)
      wait(vif.penable === 1'b1);
      addr = vif.cb_slv.paddr;
      if(mem.exists(addr))
        data = mem[addr];
      else
        data = 0;
      #1ps;
      vif.prdata <= data;
      @(vif.cb_slv);
    endtask: do_read
    

    八、运行仿真

    执行命令

    run -all
    

    验证环境结构
    在这里插入图片描述

    写操作:写入地址和写入数据相同,只有penable拉高才会写入,数据之间有一个空闲。
    在这里插入图片描述
    读操作:只有penable拉高才会读数据,没有写入过数据的地址,读出来的值为0。
    在这里插入图片描述
    先写后读:
    在这里插入图片描述
    写完立即读操作:
    在这里插入图片描述

    仿真结果:

    在这里插入图片描述
    覆盖率:

      // APB command covergroup
      covergroup cg_apb_command @(posedge clk iff rstn);
        pwrite: coverpoint pwrite{
          type_option.weight = 0;
          bins write = {1};
          bins read  = {0};
    
        }
        psel : coverpoint psel{
          type_option.weight = 0;
          bins sel   = {1};
          bins unsel = {0};
        }
        cmd  : cross pwrite, psel{
          bins cmd_write = binsof(psel.sel) && binsof(pwrite.write);
          bins cmd_read  = binsof(psel.sel) && binsof(pwrite.read);
          bins cmd_idle  = binsof(psel.unsel);
        }
      endgroup
    
      // APB transaction timing group
      covergroup cg_apb_trans_timing_group @(posedge clk iff rstn);
        psel: coverpoint psel{
          bins single   = (0 => 1 => 1  => 0); 
          bins burst_2  = (0 => 1 [*4]  => 0); 
          bins burst_4  = (0 => 1 [*8]  => 0); 
          bins burst_8  = (0 => 1 [*16] => 0); 
          bins burst_16 = (0 => 1 [*32] => 0); 
          bins burst_32 = (0 => 1 [*64] => 0); 
        }
        penable: coverpoint penable {
          bins single = (0 => 1 => 0 [*2:10] => 1);
          bins burst  = (0 => 1 => 0         => 1);
        }
      endgroup
    
      // APB write & read order group
      covergroup cg_apb_write_read_order_group @(posedge clk iff (rstn && penable));
        write_read_order: coverpoint pwrite{
          bins write_write = (1 => 1);
          bins write_read  = (1 => 0);
          bins read_write  = (0 => 1);
          bins read_read   = (0 => 0);
        } 
      endgroup
    
    
    

    在这里插入图片描述

    展开全文
  • apb协议的一些特点

    2020-10-11 20:43:00
    协议简单:无复杂的时序; ③ 同步总线:总线上所有的transaction(读写操作)都依赖于时钟的上升沿; ④ 一主多从:一般情况下,APB挂在AHB总线系统下,通过AHB-APB Bridge将事务在AHB总线系统之间进行转化,...
  • AMBA 系列之 APB 协议

    千次阅读 2019-12-27 15:07:59
    APB主要用于低带宽的周边外设之间的连接,例如UART、1284等,它的总线架构不像AHB支持多个主模块,在APB里面唯一的主模块就是APB 桥。其特性包括:两个时钟周期传输;无需等待周期和回应信号;控制逻辑简单,只有四...

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 222
精华内容 88
关键字:

apb协议