svt_apb_vip使用指南
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 信号发给各个从机。

相关新闻

终极Godot卡牌游戏框架:5分钟创建专业级卡牌游戏

终极Godot卡牌游戏框架:5分钟创建专业级卡牌游戏

终极Godot卡牌游戏框架:5分钟创建专业级卡牌游戏 【免费下载链接】godot-card-game-framework A framework which comes with prepared scenes and classes to kickstart your card game, as well as a powerful scripting engine to use to provide full rules enf…

2026/9/30 16:55:54 阅读更多 →
70元DIY桌面机械臂:从舵机控制到运动学实践

70元DIY桌面机械臂:从舵机控制到运动学实践

1. 这篇文章真正要解决的问题花几百上千元买一个桌面机械臂,对于很多想入门机器人、自动化或者嵌入式开发的朋友来说,是一笔不小的开销。更关键的是,买来的成品往往是一个“黑盒”,你只能用它提供的软件和接口,很难深入…

2026/10/9 23:54:02 阅读更多 →
范数:从向量长度到AI核心,理解数据科学的万能尺子

范数:从向量长度到AI核心,理解数据科学的万能尺子

1. 项目概述:为什么我们需要“范数”这把尺子? 在机器学习和数据分析的世界里,我们每天都在和一堆数字打交道。这些数字可能代表一张图片的像素值、一段文本的词向量、一个用户的特征画像,或者是一组传感器的读数。当这些数字被整…

2026/10/14 11:36:57 阅读更多 →

最新新闻

1050亿担保+380亿滞销债:AI算力资本链的四层业务架构拆解

1050亿担保+380亿滞销债:AI算力资本链的四层业务架构拆解

一、事件与数字 10月前两周的两组消息值得放在一起读: 英伟达监管文件(截至7月26日财季):担保总敞口上限1085亿美元,其中1050亿对应OpenAI关联方在俄亥俄Pike County的数据中心租约,另有15亿美元入股开发商…

2026/10/15 20:30:05 阅读更多 →
多变量时间序列预测为何需要岭回归?原理与实战解析

多变量时间序列预测为何需要岭回归?原理与实战解析

1. 为什么多变量时间序列预测里会用到岭回归1.1 时间序列预测的两种思路和回归方法的定位一提到时间序列预测,大家脑子里蹦出来的多半是 ARIMA、指数平滑、Prophet 这类专门处理时间依赖的模型。这个想法本身没错,但在实际项目里,尤其面对的是…

2026/10/15 20:30:05 阅读更多 →
tfsec 检查规则详解:aws-elastic-search-use-secure-tls-policy(Elasticsearch 域名端点 TLS 策略)

tfsec 检查规则详解:aws-elastic-search-use-secure-tls-policy(Elasticsearch 域名端点 TLS 策略)

应用安全SASTIaC 【免费下载链接】tfsec Tfsec is now part of Trivy 项目地址: https://gitcode.com/gh_mirrors/tf/tfsec 点击查看 免费下载 本文围绕 tfsec 的 AWS Elasticsearch 安全检查规则 aws-elastic-search-use-secure-tls-policy 展开,讲解该…

2026/10/15 20:30:05 阅读更多 →
C#实现Ymodem协议IAP升级的底层关键点解析

C#实现Ymodem协议IAP升级的底层关键点解析

简介:本资源是一套基于C#实现的Ymodem协议与嵌入式IAP在线升级融合方案,面向嵌入式开发工程师、物联网固件维护人员及学习串行通信与设备升级技术的中高级开发者,解决嵌入式设备在无网络或受限环境下通过串口安全可靠升级固件的核心问题。压缩…

2026/10/15 20:30:05 阅读更多 →
C# WinForm嵌入CefSharp:老项目浏览器内核升级实战指南

C# WinForm嵌入CefSharp:老项目浏览器内核升级实战指南

简介:在C# WinForm项目中嵌入Chromium内核浏览器时,CefSharp.Winform是最常用的.NET封装方案之一。这套示例面向使用VS2010和.NET Framework 4.0的开发者,基于CefSharp.Winform 49.0.1,细致展示了从NuGet安装引用、调用Cef.Initia…

2026/10/15 20:30:05 阅读更多 →
浏览器驱动自动下载全攻略:版本匹配、脚本实现与工具选型

浏览器驱动自动下载全攻略:版本匹配、脚本实现与工具选型

最近又有开发者问我,浏览器驱动总是和浏览器版本对不上,到底怎么用代码自动下载。这个问题其实挺有代表性。手动下载驱动的流程看着不复杂,但一旦机器多了、浏览器升级频繁了,版本就很容易漂移,测试结果也跟着不可信。…

2026/10/15 20:29:04 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →