APB协议可能是很多新手ICer入门接触过的最简单的总线协议。因为apb的最常用场景就是用来配寄存器但是正因为它简单导致很多人不够重视这个协议的细节。甚至大部分人连这个协议的官方文档都没有打开看过。对这个协议的了解基本就是在看波形debug过程中知道的一个最基本的协议规则。但其实apb的很多协议细节是值得深究的。特别是引入APB3有preday和pslverr之后以及SETUP和ACCESS阶段的各种行为我想真正能说明白的人并不多。也就是说越简单的东西往往难以引起人们的重视。在另一篇文章中记录了自己从头学习APB协议的总结svt_apb_if里的宏定义问题-CSDN博客APB协议学习总结_amba3 apb specification-CSDN博客本文将重点介绍svt_apb_vip的使用svt_apb_vip结构svt_apb_master_agentThe master agent is configured using the system configuration. The system configuration should be provided to the master agent in the build phase of the test.svt_apb_master_agent使用的是svt_apb_vif是顶层的apb接口使用的config是svt_apb_system_configuration。svt_apb_master_agent可以作为一个独立的agent和slave DUT进行连接。svt_apb_slave_agentThe slave agent is configured using slave configuration, which is available in the system configuration. The slave configuration should be provided to the slave agent in the build phase of the test.svt_apb_slave_agent使用的虚接口是svt_apb_slave_vif使用的config是svt_apb_slave_configuration这一点和svt_apb_master_agent完全不同。svt_apb_system_envsvt_apb_system_env内部封装了master_agent和slave_agent[]数组。通过以上svt_apb_vip的结构可以发现apb_vip的结构有点特别和axi_vip不一样svt_apb_system_env只有唯一一个master而且该master用的配置就是system_configuration。没有master_cfg。用的interface就是顶层的interface。svt_apb_ifslave_agent可以有多个用的configuration是slave_agent_configuration用的interface是svt_apb_slave_if。svt_apb_if结构apb是一主多从结构其顶层interface也是这样的对应结构svt_apb_if是和master_agent绑定的interface。在svt_apb_if中默认是将svt_apb_if和svt_apb_slave_if进行自动连接的。可以通过断开宏 SVT_APB_DISCONNECT_TOP_LEVEL_APB_IF_SIGNALS来断开内部的自动连接。验证环境拓扑结构Master DUT and Slave VIP可以选择使用一个单独的slave_agent来集成到环境中也可以选择使用整个apb_system_env集成到环境中。强烈建议使用apb_system_env进行集成。不同的集成方式连线方式和config配置方式是不同的。使用svt_apb_slave_agent进行连接这里只使用了svt_apb_slave_if没有使用svt_apb_ifassign tb_top.slave_if[0].psel dut.psel[0]; assign tb_top.slave_if[0].penable dut.penable; assign tb_top.slave_if[0].paddr dut.paddr; assign tb_top.slave_if[0].pwrite dut.pwrite; assign tb_top.slave_if[0].pwdata dut.pwdata; assign dut.pready[0] tb_top.slave_if[0].pready; assign dut.prdata[0] tb_top.slave_if[0].prdata; assign dut.pslverr[0] tb_top.slave_if[0].pslverr; assign tb_top.slave_if[1].psel dut.psel[1]; assign tb_top.slave_if[1].penable dut.penable; assign tb_top.slave_if[1].paddr dut.paddr; assign tb_top.slave_if[1].pwrite dut.pwrite; assign tb_top.slave_if[1].pwdata dut.pwdata; assign dut.pready[1] tb_top.slave_if[1].pready; assign dut.prdata[1] tb_top.slave_if[1].prdata; assign dut.pslverr[1] tb_top.slave_if[1].pslverr;slave_agent_cfg[0].is_active 1;使用svt_apb_system_env连接连接方式一defineSVT_APB_DISCONNECT_TOP_LEVEL_APB_IF_SIGNALS assign tb_top.apb_if.slave_if[0].psel dut.psel[0]; assign tb_top.apb_if.slave_if[0].penable dut.penable; assign tb_top.apb_if.slave_if[0].paddr dut.paddr; assign tb_top.apb_if.slave_if[0].pwrite dut.pwrite; assign tb_top.apb_if.slave_if[0].pwdata dut.pwdata; assign dut.pready[0] tb_top.apb_if.slave_if[0].pready; assign dut.prdata[0] tb_top.apb_if.slave_if[0].prdata; assign dut.pslverr[0] tb_top.apb_if.slave_if[0].pslverr; assign tb_top.apb_if.slave_if[1].psel dut.psel[1]; assign tb_top.apb_if.slave_if[1].penable dut.penable; assign tb_top.apb_if.slave_if[1].paddr dut.paddr; assign tb_top.apb_if.slave_if[1].pwrite dut.pwrite; assign tb_top.apb_if.slave_if[1].pwdata dut.pwdata; assign dut.pready[1] tb_top.apb_if.slave_if[1].pready; assign dut.prdata[1] tb_top.apb_if.slave_if[1].prdata; assign dut.pslverr[1] tb_top.apb_if.slave_if[1].pslverr;sys_cfg.is_active 0; sys_cfg.num_slaves 4; sys_cfg.slave_cfg[0].is_active 1; sys_cfg.slave_cfg[1].is_active 1; sys_cfg.slave_cfg[2].is_active 1; sys_cfg.slave_cfg[3].is_active 1; sys_cfg.slave_addr_allocation_enable 1; // NOTE sys_cfg.slave_addr_ranges new[4]; sys_cfg.slave_addr_ranges[0] new(); sys_cfg.slave_addr_ranges[0].start_addr h0_0000; sys_cfg.slave_addr_ranges[0].end_addr h0_FFFF; sys_cfg.slave_addr_ranges[0].slave_id 0; sys_cfg.slave_addr_ranges[1] new(); sys_cfg.slave_addr_ranges[1].start_addr h1_0000; sys_cfg.slave_addr_ranges[1].end_addr h1_FFFF; sys_cfg.slave_addr_ranges[1].slave_id 1; ……设置每个slave的地址范围如果slave错误的收到了不属于自己地址范围的数据system monitor将会报出错误。这种情况是很有可能发生的。如果将apb_if.slave_if直接和dut信号进行连接则需要使用断开连接的宏因为apb_if中默认会将apb_if顶层信号赋值给每一个slave_if这就会造成多驱动了。连接方式二 推荐这种连接方式assign tb_top.apb_if.psel[0] dut.psel[0]; assign tb_top.apb_if.penable dut.penable; assign tb_top.apb_if.paddr dut.paddr; assign tb_top.apb_if.pwrite dut.pwrite; assign tb_top.apb_if.pwdata dut.pwdata; assign dut.pready[0] tb_top.apb_if.pready[0]; assign dut.prdata[0] tb_top.apb_if.prdata[0]; assign dut.pslverr[0] tb_top.apb_if.pslverr[0]; assign tb_top.apb_if.psel[1] dut.psel[1]; //assign tb_top.apb_if.penable dut.penable; //assign tb_top.apb_if.paddr dut.paddr; //assign tb_top.apb_if.pwrite dut.pwrite; //assign tb_top.apb_if.pwdata dut.pwdata; assign dut.pready[1] tb_top.apb_if.pready[1]; assign dut.prdata[1] tb_top.apb_if.prdata[1]; assign dut.pslverr[1] tb_top.apb_if.pslverr[1];sys_cfg.is_active 0; sys_cfg.num_slaves 4; sys_cfg.slave_cfg[0].is_active 1; sys_cfg.slave_cfg[1].is_active 1; sys_cfg.slave_cfg[2].is_active 1; sys_cfg.slave_cfg[3].is_active 1;这种情况可以不用添加断开连接的宏因为属于dut信号驱动给apb_if顶层信号apb_if顶层信号又自动连接给了slave_if信号属于一路向下的连接结构。sys_cfg.is_active 0这个时候svt_apb_if充当了连接桥梁svt_apb_if不做任何驱动Slave DUT and Master VIP 常用场景 ⭐⭐⭐使用单独的svt_apb_master_agent作为master vip// Slave 0 连线已对调方向并移除 slave_if 级 assign dut.psel[0] tb_top.apb_if.psel[0]; assign dut.penable tb_top.apb_if.penable; assign dut.paddr tb_top.apb_if.paddr; assign dut.pwrite tb_top.apb_if.pwrite; assign dut.pwdata tb_top.apb_if.pwdata; assign tb_top.apb_if.pready[0] dut.pready[0]; assign tb_top.apb_if.prdata[0] dut.prdata[0]; assign tb_top.apb_if.pslverr[0] dut.pslverr[0]; // Slave 1 连线已对调方向并移除 slave_if 级 assign dut.psel[1] tb_top.apb_if.psel[1]; // 注penable, paddr, pwrite, pwdata 为公用总线上面已驱动此处无需重复 assign assign tb_top.apb_if.pready[1] dut.pready[1]; assign tb_top.apb_if.prdata[1] dut.prdata[1]; assign tb_top.apb_if.pslverr[1] dut.pslverr[1];sys_cfg.is_active 1; sys_cfg.num_slaves 0;使用svt_apb_system_env作为master VIP// Slave 0 连线已对调方向并移除 slave_if 级 assign dut.psel[0] tb_top.apb_if.slave_if[0].psel; assign dut.penable tb_top.apb_if.penable; assign dut.paddr tb_top.apb_if.paddr; assign dut.pwrite tb_top.apb_if.pwrite; assign dut.pwdata tb_top.apb_if.pwdata; assign tb_top.apb_if.slave_if[0].pready dut.pready[0]; assign tb_top.apb_if.slave_if[0].prdata dut.prdata[0]; assign tb_top.apb_if.slave_if[0].pslverr dut.pslverr[0]; // Slave 1 连线已对调方向并移除 slave_if 级 assign dut.psel[1] tb_top.apb_if.slave_if.psel; // 注penable, paddr, pwrite, pwdata 为公用总线上面已驱动此处无需重复 assign assign tb_top.apb_if.slave_if[1].pready dut.pready[1]; assign tb_top.apb_if.slave_if[1].prdata dut.prdata[1]; assign tb_top.apb_if.slave_if[1].pslverr dut.pslverr[1];sys_cfg.is_active 1; sys_cfg.num_slaves 4; sys_cfg.slave_cfg[0].is_active 0; sys_cfg.slave_cfg[1].is_active 0; sys_cfg.slave_cfg[2].is_active 0; sys_cfg.slave_cfg[3].is_active 0; sys_cfg.slave_addr_allocation_enable 0; // NOTE sys_cfg.slave_addr_ranges new[4]; sys_cfg.slave_addr_ranges[0] new(); sys_cfg.slave_addr_ranges[0].start_addr h0_0000; sys_cfg.slave_addr_ranges[0].end_addr h0_FFFF; sys_cfg.slave_addr_ranges[0].slave_id 0; sys_cfg.slave_addr_ranges[1] new(); sys_cfg.slave_addr_ranges[1].start_addr h1_0000; sys_cfg.slave_addr_ranges[1].end_addr h1_FFFF; sys_cfg.slave_addr_ranges[1].slave_id 1; ……slave_addr_allocation_enable 的真正作用是控制是否由 VIP 来“自动平均分配”从机地址空间。当svt_apb_vip作为master时一定要开启sys_cfg.is_active为1作为master时驱动都是通过svt_apb_if发出。这时将slave_agent设置为passive模式同时设置每个slave_agent的地址范围。slave_agent相当于一个中间桥梁连接了svt_apb_if和dut。slave_agent不做任何的信号驱动只是充当桥梁同时监测两边发送的信号。推荐使用system_env作为VIP进行集成。使用单独的agent很难验证出地址译码逻辑错误的情况建议使用system_env作为VIP进行集成。QA如果apb_vip作为master和dut的接口psel连错位了会怎么样仅使用svt_apb_master_agent也是不行的。要使用svt_apb_system_env进行集成配置sys_cfg.is_active为1位置sys_cfg.slave_cfg[x].is_active 0。将所有的slave_agent都设置成passive模式并且使用sys_cfg.slave_addr_rangdes正是这个地址段决定了master在发送地址时拉高哪个地址段对应的psel如果连线时slave_if和dut的接口连错了那么dut将会收到错误的地址它无法处理这个地址apb指令就无法下发。QA如果apb_vip作为slavedut的解码逻辑有误psel选中了不该去的地址会怎么样单独例化的 svt_apb_slave_agent 本身没有地址译码概念。APB slave 是靠自己那根 PSEL 被选中的agent 只关心我的 PSEL 拉起来之后PADDR/PWRITE/PWDATA 是什么它不会去判断这个地址是不是应该属于我。要检查 master DUT通常是 AHB2APB bridge 或 APB interconnect的译码逻辑正确做法是用 svt_apb_system_env svt_apb_system_configuration把地址映射表配进去并打开 system monitor。QA什么是APB Bridge有没有单独的apb master首先APB Bridge这种叫法是不规范的是很多人说的简称一个bridge一定是输入端一种协议输出端一种协议。比如ahb-apb bridgeaxi-apb bridge。不能单独的叫apb bridge其实APB Master 是独立存在的但在现实的 SoC 芯片架构和商业 IP 市场中很少看到有人把“APB Master”作为一个单独的硬核RTL IP拿出来卖或单独设计。这种“感觉没有单独 APB Master”的现象主要源于 APB 协议本身的定位以及芯片设计的工程考量1. 业务逻辑的归属Master 必须是“有主动权”的控制单元在芯片设计中一个总线 Master 必须具备“主动发起读写事务Transaction”的能力。Slave从机 被动响应即可如 UART、Timer、GPIO逻辑相对固定因此市场上有很多独立的 APB Slave IP。Master主机 必须有“大脑”或驱动源。单独一个光遵从 APB 时序的 Master 壳子是没有意义的它必须挂载在一个能产生读写指令的硬件实体上CPU / CPU Core执行指令发起读写。DMA Controller搬运数据。测试/调试接口如 JTAG to APB, USB to APB, PCIe to APB由外部激励发起读写。因此APB Master 通常是作为这些控制器如 CPU 或 JTAG Debugger的“前端总线接口Bus Interface”存在而不是一个独立存在的模块。2. 性能与架构矛盾低速 APB 不适合做主系统总线APB 协议的设计初衷就是极简、低功耗、低成本没有复杂的流水线、没有 Outstanding 传输、没有 Burst 突发模式。如果一个控制器比如 CPU 或高性能 DMA足够强强到可以独立作为一个 Master 操控全局那么它通常会采用 AXI 或 AHB 这种高性能总线接口而不是 APB。如果让 CPU 带着 APB Master 接口直接去挂载整颗芯片的资源系统的吞吐量会低得无法接受。所以系统的主干道必然是 AXI/AHB只在需要连接低速外设时通过一个 AXI/AHB-to-APB Bridge 把请求转接过去。3. 最常见的“单独 APB Master”形态APB Bridge在大部分 SoC 中最标准的“APB Master”其实就是 APB BridgeAPB 桥--------------- ----------------- | AXI/AHB | | APB Bridge | | Master || (AXI/AHB Slave)| | (CPU/DMA等) | AXI/AHB 高速总线 | | --------------- | (APB Master) |--- 实际上这里就是 APB 总线唯一的 Master ----------------- | | APB 总线 -------------------- | | v v -------------- -------------- | APB Slave | | APB Slave | | (UART/Timer) | | (GPIO/I2C) | -------------- --------------对于 APB 总线上的那些外设Slave来说APB Bridge 的 APB 侧接口就是一个标准的、单独的 APB Master。桥接收上游的 AXI/AHB 请求转换为 APB 信号发给各个从机。