<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Hyacimond’s Blog</title>
        <link>http://hyacimond.top/</link>
        <description>个人技术博客 &amp; 知识库</description>
        <lastBuildDate>Thu, 08 Oct 2026 05:23:26 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <copyright>All rights reserved 2026, Hyacimond</copyright>
        <item>
            <title><![CDATA[“一生一芯”项目学习记录（二）性能优化与流水线（v23.06）]]></title>
            <link>http://hyacimond.top/arch/ysyx-record2</link>
            <guid>http://hyacimond.top/arch/ysyx-record2</guid>
            <pubDate>Fri, 26 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[包含部分源码详解与踩坑记录]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-38bc0e14283f802fbd5fee24bf2d63b5"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-38cc0e14283f8037b3ecd6e18819e0cf" data-id="38cc0e14283f8037b3ecd6e18819e0cf"><span><div id="38cc0e14283f8037b3ecd6e18819e0cf" class="notion-header-anchor"></div><a class="notion-hash-link" href="#38cc0e14283f8037b3ecd6e18819e0cf" title="B 阶段"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">B 阶段</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-38bc0e14283f806fa483d78091bb7c20" data-id="38bc0e14283f806fa483d78091bb7c20"><span><div id="38bc0e14283f806fa483d78091bb7c20" class="notion-header-anchor"></div><a class="notion-hash-link" href="#38bc0e14283f806fa483d78091bb7c20" title="性能优化"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">性能优化</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-38bc0e14283f80338a79ce8527a700ad" data-id="38bc0e14283f80338a79ce8527a700ad"><span><div id="38bc0e14283f80338a79ce8527a700ad" class="notion-header-anchor"></div><a class="notion-hash-link" href="#38bc0e14283f80338a79ce8527a700ad" title="IPC"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">IPC</span></span></h4><div class="notion-text notion-block-38bc0e14283f8056a8b2f92bac8d5e99">IPC 的统计最直接的做法是在 NPC 执行一次指令的循环中递增 <code class="notion-inline-code">cycles</code> 和 <code class="notion-inline-code">insts</code>，但实际跑的时候发现越大的程序 IPC 越低，<b>很显然 </b>拉低 IPC 的原因是 FSBL 和 SSBL ，尤其是 FSBL 大量从 Flash 中取指令和取数据。个人觉得更严谨的统计是只统计 main 函数开始到结束的 IPC。经过查询得知，处理器性能监控一般使用两个 CSR 来统计 IPC，mcycle 和 minstret，好处是可以自由选择要统计的范围，只需在开始和结束点读取这两个 CSR 即可计算。</div><div class="notion-text notion-block-38bc0e14283f8066a5b9c696cb4c5e74">但是如果采用这种方式，要求 AM 环境必须支持软浮点运算，更完善的 printf 打印。移植一些开源的嵌入式 printf.c 的实现仍然需要更多的软浮点相关符号链接。因此还是选择在仿真环境中统计 IPC。后期等待处理器设计完善后再考虑解决软浮点问题。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-38cc0e14283f8059bd17d88bd46bfa9a" data-id="38cc0e14283f8059bd17d88bd46bfa9a"><span><div id="38cc0e14283f8059bd17d88bd46bfa9a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#38cc0e14283f8059bd17d88bd46bfa9a" title="性能计数器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">性能计数器</span></span></h4><div class="notion-text notion-block-38cc0e14283f801693a5d13a9a63179b">之前重构 NPC 时改成了基于分布消息控制的架构，但是目前 CPU 的多周期本质上是单周期 + 访存延迟等待，一方面，这种控制流导致模块间握手传输不清晰，ready 有时候充当了阻塞标志，但实际前级模块已经将消息发送给后级（不符合 <code class="notion-inline-code">valid &amp; ready</code> 握手协议）。因此，性能计数器要求的访存延迟几乎等效于访存类指令的执行时间，IFU 取指令阻塞的原因也非常单一，主要是取指请求发出后等待下游存储系统返回指令数据，之后则是等待指令彻底执行结束后才会开始取下一条指令。</div><div class="notion-callout notion-yellow_background_co notion-block-38cc0e14283f8091bcc5c8007053c282"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="⚠️">⚠️</span></div><div class="notion-callout-text"><div class="notion-text notion-block-38cc0e14283f809ab98dff352a557708">此处统计排除了 FSBL/SSBL 阶段，指令执行周期计数不统计取指延迟。</div></div></div><div class="notion-text notion-block-38cc0e14283f80baa8b4d61ebcf6a0ff">对于一个 microbench test 规模的 PMC 结果：</div><div class="notion-text notion-block-38cc0e14283f808bae95e201c4b14ff9">Amdahl 定律如下：</div><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span><div class="notion-text notion-block-38cc0e14283f80eb8910dcba4e01d196">其中，<span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span> 表示当前总时间中可优化部分的占比，<span role="button" tabindex="0" class="notion-equation notion-equation-inline"><span></span></span> 表示该部分被优化的倍数。</div><ol start="1" class="notion-list notion-list-numbered notion-block-38cc0e14283f800887fcde5e46b37d8d" style="list-style-type:decimal"><li>IFU 取指等待优化</li></ol><div class="notion-text notion-block-38cc0e14283f80368e63f8f4d98236ba">取指相关等待周期为：</div><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span><div class="notion-text notion-block-38cc0e14283f80f49098d9cc6e0ba328">占总周期比例为：</div><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span><div class="notion-text notion-block-38cc0e14283f80028f7fdd32d4d80fc8">若 IFU 取指等待优化 2 倍：</div><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span><div class="notion-text notion-block-38cc0e14283f80e2b508f4636205d48c">若 IFU 取指等待优化 4 倍：</div><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span><div class="notion-text notion-block-38cc0e14283f803280cecd84b23fa89b">若完全消除 IFU 取指等待：</div><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span><ol start="1" class="notion-list notion-list-numbered notion-block-38cc0e14283f80139f56e4fa7286e417" style="list-style-type:decimal"><li>LSU 总执行周期优化</li></ol><div class="notion-text notion-block-38cc0e14283f80218eccc9f3a2de2b67">LSU 总执行周期为：</div><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span><div class="notion-text notion-block-38cc0e14283f80ceb5bbdfbd5dafe449">占总周期比例为：</div><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span><div class="notion-text notion-block-38cc0e14283f805ebd1dd50ff3ed5c3d">若 LSU 总执行周期优化 2 倍：</div><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span><div class="notion-text notion-block-38cc0e14283f80faafeccf811916deaf">若 LSU 总执行周期优化 4 倍：</div><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span><div class="notion-text notion-block-38cc0e14283f80fc8b38fb9ece99aa53">若完全消除 LSU 总执行周期：</div><span role="button" tabindex="0" class="notion-equation notion-equation-block"><span></span></span><div class="notion-text notion-block-38cc0e14283f800ba4d8dab5e5b69003">根据 Amdahl 定律，当前 MicroBench 下最主要的性能瓶颈是 IFU 取指路径。取指相关
等待约占总周期的 79.80%，完全消除后的理论加速上限约为 4.95 倍。相比之下，LSU
额外访存等待占总周期约 13.71%，即使完全消除，理论加速上限也只有约 1.16 倍。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-38dc0e14283f801697eec10267375662" data-id="38dc0e14283f801697eec10267375662"><span><div id="38dc0e14283f801697eec10267375662" class="notion-header-anchor"></div><a class="notion-hash-link" href="#38dc0e14283f801697eec10267375662" title="校准访存延迟"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">校准访存延迟</span></span></h4><div class="notion-text notion-block-38dc0e14283f804bb65de2fd8cecd519">F_CPU ~= 380.918 MHz
r = F_CPU / 100 MHz ~= 3.80918</div><div class="notion-text notion-block-38dc0e14283f805fafcdf2eb1134989f">即外设每一个周期的时间等于 CPU 运行 3.80918 个周期，乘以 2 的 N 次方量化成定点数。其中需要注意的是 <code class="notion-inline-code">delay_counter</code> 从 <code class="notion-inline-code">delay_max</code> 获取延迟计数时需要四舍五入。</div><div class="notion-text notion-block-38fc0e14283f803a8149d18823abdc2c">最高综合频率情况：</div><div class="notion-text notion-block-390c0e14283f80f1a2e3f5683166a980">ALU / 加法器相关上限：约 822.698 MHz</div><div class="notion-text notion-block-390c0e14283f80509735f841015d113c">regfile 读路径上限：约 1968.682 MHz</div><div class="notion-text notion-block-390c0e14283f80a58b49c91db59083d4">添加访存延迟后，主要的性能瓶颈依然是访存。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-391c0e14283f80608f9cdb8c5a6c93a6" data-id="391c0e14283f80608f9cdb8c5a6c93a6"><span><div id="391c0e14283f80608f9cdb8c5a6c93a6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#391c0e14283f80608f9cdb8c5a6c93a6" title="提升功能测试效率"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">提升功能测试效率</span></span></h4><div class="notion-text notion-block-393c0e14283f8018813efd022b535232">通过配置项切换三种运行模式：Interpreter、NPC Standalone 和 NPC with SoC，配套修改覆盖了 AM、NEMU 和 NPC 三层。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-393c0e14283f80d3b528f4dd7aaabf8e" data-id="393c0e14283f80d3b528f4dd7aaabf8e"><span><div id="393c0e14283f80d3b528f4dd7aaabf8e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#393c0e14283f80d3b528f4dd7aaabf8e" title="程序的局部性"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">程序的局部性</span></span></h4><div class="notion-text notion-block-394c0e14283f80578459f28f573ce11f">使用 codex 编写的脚本根据 mtrace 日志生成了三张图（测试环境：ysyxSoC，Microbench: test 规模）：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-394c0e14283f80eb9868fd21e0ee4e09"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A6a1ee72c-5736-478f-9c1c-4ec70c02e8a4%3Aaccess_scatter.png?table=block&amp;id=394c0e14-283f-80eb-9868-fd21e0ee4e09&amp;t=394c0e14-283f-80eb-9868-fd21e0ee4e09" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-394c0e14283f803dae5ed1200b2b04f1">有明显分段的横线，同一地址附近被反复多次访问。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-394c0e14283f806b99ddef6d8bb75394"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ae3609833-f830-43bd-8a7d-2d07ae9ac9ca%3Aaddress_heatmap.png?table=block&amp;id=394c0e14-283f-806b-99dd-ef6d8bb75394&amp;t=394c0e14-283f-806b-99dd-ef6d8bb75394" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-394c0e14283f80089072ea292b0e862d">整个时间窗口内也有明显的局部热点。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-394c0e14283f803caa06ceca6d8727ab"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Af2456de5-16a6-48c9-b191-01d54688160f%3Areuse_hist.png?table=block&amp;id=394c0e14-283f-803c-aa06-ceca6d8727ab&amp;t=394c0e14-283f-803c-aa06-ceca6d8727ab" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-394c0e14283f80399b8cce04ece031e0">按地址块分组统计，发现具有很强的时间局部性。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-394c0e14283f805cb138ed98b1e84f6c" data-id="394c0e14283f805cb138ed98b1e84f6c"><span><div id="394c0e14283f805cb138ed98b1e84f6c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#394c0e14283f805cb138ed98b1e84f6c" title="简易 Cache"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">简易 Cache</span></span></h4><table class="notion-simple-table notion-block-42f7a12e36a140a8a8f0a0cd8e7fd4b4"><tbody><tr class="notion-simple-table-row notion-simple-table-header-row notion-block-30eaac8b3c6a43f7a7ed4c9788946dea"><td class="" style="width:120px"><div class="notion-simple-table-cell">LINE_NUM</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Total Cycles</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">IPC</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Misses</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Hit Rate</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">相对 16 行加速</div></td></tr><tr class="notion-simple-table-row notion-block-6d6cb1ef27144548a57d0a0cab670dc4"><td class="" style="width:120px"><div class="notion-simple-table-cell">16</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">2,308,401,555</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0.0845</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">51,164,876</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">73.78%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1.00x</div></td></tr><tr class="notion-simple-table-row notion-block-2a4f2fc140314d7fa6ad6ccec0184ed1"><td class="" style="width:120px"><div class="notion-simple-table-cell">32</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1,550,991,219</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0.1258</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">25,189,043</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">87.09%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1.49x</div></td></tr><tr class="notion-simple-table-row notion-block-05766342f5b14c74b68d9b39d6c29da1"><td class="" style="width:120px"><div class="notion-simple-table-cell">64</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1,234,119,897</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0.1581</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">14,365,021</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">92.64%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1.87x</div></td></tr><tr class="notion-simple-table-row notion-block-6312d4c1c7ec451381580fba1d081108"><td class="" style="width:120px"><div class="notion-simple-table-cell">128</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1,004,123,641</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0.1944</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">7,075,902</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">96.37%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">2.30x</div></td></tr></tbody></table><table class="notion-simple-table notion-block-b2e669c857cc4c16a12cc8b4f311cbb4"><tbody><tr class="notion-simple-table-row notion-simple-table-header-row notion-block-a613067b7a164ee0b72faf9345c7dfba"><td class="" style="width:120px"><div class="notion-simple-table-cell">Cache lines</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Area</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Fmax</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">ALDFF_PP</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">ICG</div></td></tr><tr class="notion-simple-table-row notion-block-f33d8ada66ee4f3498e22e0202705fa1"><td class="" style="width:120px"><div class="notion-simple-table-cell">16</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">26648.44</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">331.679 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">928</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">92</div></td></tr><tr class="notion-simple-table-row notion-block-dfebae02c37841e58f284c1ad45a1ba5"><td class="" style="width:120px"><div class="notion-simple-table-cell">32</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">28597.80</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">333.644 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1824</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">108</div></td></tr><tr class="notion-simple-table-row notion-block-c6bf8ac9c6e9444ea0b425395647a64a"><td class="" style="width:120px"><div class="notion-simple-table-cell">64</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">31890.32</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">337.019 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">3584</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">140</div></td></tr><tr class="notion-simple-table-row notion-block-b9314feaa1c743d2aafe83df493f4f8c"><td class="" style="width:120px"><div class="notion-simple-table-cell">128</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">40909.40</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">306.537 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">7040</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">204</div></td></tr></tbody></table><div class="notion-text notion-block-397c0e14283f806cb2d7cea49f04df72">上述是运行 Microbench Train 规模的评估结果。</div><div class="notion-text notion-block-398c0e14283f80f392a9e56233e94ce3">实现 cachesim 之后发现数据与 ysyxSoC 运行时评估不完全相同，后续发现原因是 interpreter 和 SoC 环境的动态运行指令本来就不一样，导致 pctrace 的日志输出不一样。</div><div class="notion-text notion-block-398c0e14283f8062b1ebd8421b0ab1ab"><b>具体是 uart 代码轮询。</b>interpreter 不模拟总线行为，每次读 LSR 寄存器都可以读到 1，而 SoC 就不一定了，可能存在等待的情况，这会增加轮询次数。因此，对于有串口输出的程序，cachesim 只要使用的是 interpreter 模式生成的 .bin 日志，必然评估结果不会精确一致。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-39fc0e14283f809f86c8c9bf444dbb4e" data-id="39fc0e14283f809f86c8c9bf444dbb4e"><span><div id="39fc0e14283f809f86c8c9bf444dbb4e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#39fc0e14283f809f86c8c9bf444dbb4e" title="在 iCache 侧支持 AXI 的突发传输"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">在 iCache 侧支持 AXI 的突发传输</span></span></h4><div class="notion-text notion-block-39fc0e14283f80498a07e2d19b9c5f1f">由于先前针对 core 内部模块的简单接口不支持突发，所以接口层次需要做修改：</div><ul class="notion-list notion-list-disc notion-block-39fc0e14283f806ab89bd3cca6668059"><li><code class="notion-inline-code">axi4_master.sv</code> 更名为 <code class="notion-inline-code">core_axi4_wrapper.sv</code> ，针对每个模块需求的 AXI 协议部分做单独适配，不再追求一个文件实现全量 AXI 协议，这样对于面积优化可能也有帮助。</li></ul><ul class="notion-list notion-list-disc notion-block-39fc0e14283f808985bdf88a5dc0dfda"><li>AXI 仲裁器需要支持突发事务的响应，突发事务完成标志：<code class="notion-inline-code">xlast &amp;&amp; valid &amp;&amp; ready</code> </li></ul><ul class="notion-list notion-list-disc notion-block-39fc0e14283f80fc9e82d5bf65dd8b3e"><li>接入 AXI 接口的 SDRAM 之后，总线位宽部分需要修改以支持 32 位宽。</li></ul><div class="notion-text notion-block-3a0c0e14283f806096e2f78801b4c47b">iCache 内部逻辑也需要修改：</div><ul class="notion-list notion-list-disc notion-block-3a0c0e14283f80ad94e2c86e62b44b30"><li>iCache 顶层 master 接口直接输出 AXI</li></ul><ul class="notion-list notion-list-disc notion-block-3a0c0e14283f80bc9ec8fd790a9d7378"><li>ifu 发起请求后，iCache 判断是否命中。命中则直接返回数据，不命中则发起一笔填充 Cache line 的突发请求，按照 WRAP 模式优先返回 ifu 需要的地址下的数据。</li></ul><ul class="notion-list notion-list-disc notion-block-3a0c0e14283f809a94b0f705f7125c07"><li>iCache 可以处理 secondary miss 的事件。例如，ifu 请求下一条地址时，上一次突发还未完成，则判断是否为 secondary miss（命中数据在传输途中），否则视为 MISS，同上</li></ul><h5 class="notion-h notion-h4 notion-h-indent-3 notion-block-39fc0e14283f80ed8f1be7f02ceb1088" data-id="39fc0e14283f80ed8f1be7f02ceb1088"><span><div id="39fc0e14283f80ed8f1be7f02ceb1088" class="notion-header-anchor"></div><a class="notion-hash-link" href="#39fc0e14283f80ed8f1be7f02ceb1088" title="修改到 AXI 接口的 SDRAM 之后的 bug 解决："><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">修改到 AXI 接口的 SDRAM 之后的 bug 解决：</span></span></h5><div class="notion-text notion-block-39fc0e14283f80f89b37ea6ad7b939e9">但接入之后触发取指令异常错误在第一条主程序指令（0xa0000000）：</div><div class="notion-text notion-block-39fc0e14283f805bbcbae1629c4b79a5">在 CPU 顶层总线上可以发现，R 通道提前握手成功了，传回非法值</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-39fc0e14283f8050a203f1922840ab80"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ab4b07fb6-e9e4-4ae1-9020-64e87a6a9510%3Aimage.png?table=block&amp;id=39fc0e14-283f-8050-a203-f1922840ab80&amp;t=39fc0e14-283f-8050-a203-f1922840ab80" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-39fc0e14283f80578a75cdb439de298f">一路追溯至：</div><div class="notion-text notion-block-39fc0e14283f8071b303c7684c5b7611">其中<code class="notion-inline-code">resp_valid_w</code> 信号指示响应队列非空，该信号断言时说明此时有一笔待读取的事务，是 <code class="notion-inline-code">rvalid</code> 拉高的必要条件之一。再看 <code class="notion-inline-code">resp_is_read_w</code>：</div><div class="notion-text notion-block-39fc0e14283f8029a5a9d38780fb84f0">它拉高需要 <code class="notion-inline-code">req_out_valid_w &amp;&amp; req_out_w[5]</code> ，其中 req_out_valid_w 连接到了另一个 FIFO (请求)，指示请求队列非空。请求队列非空则获取 FIFO 输出的 MSB。</div><div class="notion-text notion-block-39fc0e14283f80d596ddef84ba103f14">对于请求 FIFO 保存的数据格式可见：</div><div class="notion-text notion-block-39fc0e14283f80afb3c0fcb6fa008d98"><b>最高位保存的是是否为读请求的标志位</b></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-39fc0e14283f80779346e7dd9ab0b221"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Abcec9b3f-7ce8-45c1-a4e9-682f94ff8ef5%3Aimage.png?table=block&amp;id=39fc0e14-283f-8077-9346-e7dd9ab0b221&amp;t=39fc0e14-283f-8077-9346-e7dd9ab0b221" alt="sdram_axi_pmem.v" loading="lazy" decoding="async"/><figcaption class="notion-asset-caption">sdram_axi_pmem.v</figcaption></div></figure><div class="notion-text notion-block-39fc0e14283f8096ab38f564af428368">因此，<code class="notion-inline-code">rvalid</code> 被拉高的条件就是：当前请求了一笔读事务，且读数据的 FIFO 非空。初步推断在于错误读取 FIFO 的数据。</div><div class="notion-text notion-block-39fc0e14283f80a3aa85e588ae9cedff">进一步查看两个 FIFO 发现，响应 FIFO 有一笔数据后紧跟着被消费，但是请求 FIFO 的 <code class="notion-inline-code">count</code> 长期在 3-4 摆动，也就是一直有三笔没被消费的请求</div><div class="notion-text notion-block-39fc0e14283f803f9beef06f34324b4b">于是打算查看写入请求 FIFO 的来源：<code class="notion-inline-code">push_i</code> → <code class="notion-inline-code">req_push_w</code> → <code class="notion-inline-code">ram_accept_w</code> → <code class="notion-inline-code">state == STATE_WRITE0</code></div><div class="notion-text notion-block-39fc0e14283f8013ac31d10a71ea58ab">在波形图查看，STATE_WRITE0 持续了两个周期，同时导致 ack_q 拉高两次，进一步引发 FIFO 保存了两次一模一样的请求！</div><div class="notion-text notion-block-39fc0e14283f8057b0f8ec908135131e">而自己之前为了完成 SDRAM 扩展做了如下改动：</div><div class="notion-text notion-block-39fc0e14283f80e9b0b9f294b59051ee">这个开源控制器原本的实现是，STATE_WRITE0 会无条件向 STATE_WRITE1 转移，STATE_WRITE1 会判断当前是否连续写，最多呈现 0 → 1 → 0 → 1…… 的循环写入。因此这里可以强制让 STATE_WRITE0 只持续一周期，删除掉那一串判断（目前也暂时不用支持连续写入）</div><div class="notion-text notion-block-39fc0e14283f80b89c1cebaf4b74f1a4"><span class="notion-teal">通过测试：</span></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-39fc0e14283f807a95a4dd72cf858eb2"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A9656c248-d01c-4867-8b10-14d1bc984611%3Aimage.png?table=block&amp;id=39fc0e14-283f-807a-95a4-dd72cf858eb2&amp;t=39fc0e14-283f-807a-95a4-dd72cf858eb2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h5 class="notion-h notion-h4 notion-h-indent-3 notion-block-39fc0e14283f80c9b49ce140c4a091b3" data-id="39fc0e14283f80c9b49ce140c4a091b3"><span><div id="39fc0e14283f80c9b49ce140c4a091b3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#39fc0e14283f80c9b49ce140c4a091b3" title="Cachesim 评估说明"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Cachesim 评估说明</span></span></h5><div class="notion-text notion-block-3b4c0e14283f8018b78fc35d0900b8c0">该 icache 设计加入了 secondary miss，这部分属于总线行为，在 cachesim 侧不太好模拟，但是在总体命中率上仍然有参考价值。只是对于现在的情况，cachesim 不再是对于 icache 的精确差分模拟。</div><div class="notion-text notion-block-3b4c0e14283f80bc8193ec3577a76dcf">实际上 NPC 仿真得出的 <b>HIT + SEC_MISS 也约等于 Cachesim 评估的命中率</b>。因此使用 Cachesim 进行快速评估还是有一定参考价值的。</div><h5 class="notion-h notion-h4 notion-h-indent-3 notion-block-3b4c0e14283f803ca98dfa013579b6bf" data-id="3b4c0e14283f803ca98dfa013579b6bf"><span><div id="3b4c0e14283f803ca98dfa013579b6bf" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3b4c0e14283f803ca98dfa013579b6bf" title="初步评测"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">初步评测</span></span></h5><div class="notion-text notion-block-3b4c0e14283f807690dcdedeae0ce9f3">使用 microbench test 初步对比 4 blocks x 16 bytes 和 16 blocks x 4bytes 两种布局：</div><div class="notion-row notion-block-3b4c0e14283f80089a68db8a7715c3f6"><div class="notion-column notion-block-3b4c0e14283f803bba11f8ac27fbf511" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><div class="notion-blank notion-block-3b4c0e14283f8042afc0e29857a6ed7e"> </div></div><div class="notion-spacer"></div><div class="notion-column notion-block-3b4c0e14283f80149226e3354be94940" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><div class="notion-blank notion-block-3b4c0e14283f80b48a7bcf5a0758cb3e"> </div></div><div class="notion-spacer"></div></div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-3b4c0e14283f80f09e7edb87f9156c00" data-id="3b4c0e14283f80f09e7edb87f9156c00"><span><div id="3b4c0e14283f80f09e7edb87f9156c00" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3b4c0e14283f80f09e7edb87f9156c00" title="Cache 最佳设计空间探索"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Cache 最佳设计空间探索</span></span></h4><div class="notion-text notion-block-245231b85c544020bbfc70975764e1c3">前面的实验已经说明，单纯增加 Cache 行数虽然能够提高命中率，但面积会随着数据阵列、Tag 和 Valid 位快速增长。进一步的问题是：<b>在面积预算下，Cache 行数和块大小应该怎样组合？</b></div><div class="notion-text notion-block-12e7df0cf1d8478cb58dfa69d5eb7abf">这次使用 NEMU 运行 MicroBench test 规模生成的 PC 游程编码元数据进行回放。CacheSim 模拟直接映射 ICache，一共扫描了 20 种 <code class="notion-inline-code">BLOCK_NUM × BLOCK_BYTES</code> 组合；随后选取其中 7 个具有代表性的布局，分别使用 Nangate45 和 ICSprout55 进行 Yosys 综合与 iSTA 静态时序分析。</div><div class="notion-text notion-block-fc3f32130c3b422a9c7f75a684b971d2">综合环境统一采用当前 RV32E 的 16 个 GPR 配置和 1000 MHz 目标约束。表中的 Fmax 取自 iSTA 报告中 <code class="notion-inline-code">core_clock/max</code> 路径的 <code class="notion-inline-code">Freq(MHz)</code>，用于比较不同布局的前端综合时序。</div><h5 class="notion-h notion-h4 notion-h-indent-3 notion-block-5449b4f9e40e4b6e9c2f00f172cc1849" data-id="5449b4f9e40e4b6e9c2f00f172cc1849"><span><div id="5449b4f9e40e4b6e9c2f00f172cc1849" class="notion-header-anchor"></div><a class="notion-hash-link" href="#5449b4f9e40e4b6e9c2f00f172cc1849" title="评估结果（Cachesim + Yosys）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">评估结果（Cachesim + Yosys）</span></span></h5><table class="notion-simple-table notion-block-ea4e4db95b6b41d288be78cb7adeea19"><tbody><tr class="notion-simple-table-row notion-simple-table-header-row notion-block-74a2b62e46134f66b29970ee96a2a4a9"><td class="" style="width:120px"><div class="notion-simple-table-cell">Cache 布局</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">数据容量</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">突发拍数</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">命中率</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Nangate45 面积（μm²）</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Nangate45 Fmax</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">ICS55 面积（μm²）</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">ICS55 Fmax</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">满足 Nangate 23000</div></td></tr><tr class="notion-simple-table-row notion-block-2dd6ce73cb4c4007b8db573cb1e40ed1"><td class="" style="width:120px"><div class="notion-simple-table-cell">2 × 16B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">32B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">4</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">86.21%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">16267.496</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">516.533 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">22251.880</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">452.192 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">是</div></td></tr><tr class="notion-simple-table-row notion-teal_background notion-block-88c81f8f946e43b69bd82cb394035afe"><td class="" style="width:120px"><div class="notion-simple-table-cell">4 × 16B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">64B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">4</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">90.80%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">18845.568</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">519.441 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">25571.560</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">426.834 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">是</div></td></tr><tr class="notion-simple-table-row notion-block-c7f5b3c4adce4e86a056c85ca0eac05b"><td class="" style="width:120px"><div class="notion-simple-table-cell">8 × 16B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">128B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">4</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">95.17%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">23791.572</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">495.557 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">32276.720</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">448.303 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">否</div></td></tr><tr class="notion-simple-table-row notion-teal_background notion-block-3da1d80f0a0f4eb6ab89feb2a390e900"><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>2 × 32B</b></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>64B</b></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>8</b></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>93.47%</b></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>18617.606</b></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>506.641 MHz</b></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>25573.800</b></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>457.220 MHz</b></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>是</b></div></td></tr><tr class="notion-simple-table-row notion-block-7ecc8c8a090a481b887194013b6ad518"><td class="" style="width:120px"><div class="notion-simple-table-cell">4 × 32B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">128B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">8</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">95.81%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">23622.130</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">557.191 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">31780.840</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">423.490 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">否</div></td></tr><tr class="notion-simple-table-row notion-block-fa76a8c608ec448d93b5d725d4c0fe86"><td class="" style="width:120px"><div class="notion-simple-table-cell">8 × 8B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">64B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">2</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">84.62%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">19152.000</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">530.904 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">26516.000</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">456.183 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">是</div></td></tr><tr class="notion-simple-table-row notion-block-b73a7fbc97bd48c595404fb32c177d00"><td class="" style="width:120px"><div class="notion-simple-table-cell">16 × 4B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">64B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">73.78%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">19731.880</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">545.951 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">26874.120</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">449.357 MHz</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">是</div></td></tr></tbody></table><div class="notion-text notion-block-5b9d66dea5a34518bbc64901b7437c3e">所有 14 份综合结果的 <code class="notion-inline-code">synth_check.txt</code> 均报告 <code class="notion-inline-code">Found and reported 0 problems</code>。</div><div class="notion-callout notion-blue_background_co notion-block-a24d5b1db47042c2b038bde2bc08aaed"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="🔎">🔎</span></div><div class="notion-callout-text"><div class="notion-text notion-block-3b4c0e14283f8016bcc9da4a9828fff2">2×32B 与 4×16B 的数据容量均为64B且综合面积接近：前者通过8拍突发充分利用空间局部性，命中率更高，但 refill 和 MSHR 占用时间更长，Secondary Miss 等待代价更大；后者命中率较低、Primary Miss更多，但4拍 refill 能更快释放总线和 MSHR，降低缺失代价。因此仅凭命中率无法确定最优布局，还需在完整SoC中比较AMAT、Secondary Miss、总周期和IPC。</div></div></div><h5 class="notion-h notion-h4 notion-h-indent-3 notion-block-46d895581b12435aabf857ce9c6ed24a" data-id="46d895581b12435aabf857ce9c6ed24a"><span><div id="46d895581b12435aabf857ce9c6ed24a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#46d895581b12435aabf857ce9c6ed24a" title="完整 SoC 最终评估"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">完整 SoC 最终评估</span></span></h5><table class="notion-simple-table notion-block-d0e6d01d8eaa48cb86b45d5dfd099262"><tbody><tr class="notion-simple-table-row notion-simple-table-header-row notion-block-853125e10e0f45f3bb1102a03f406db0"><td class="" style="width:120px"><div class="notion-simple-table-cell">布局</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">测试规模</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">总周期</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">动态指令数</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">IPC</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Hit Rate</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Primary Miss</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Primary Rate</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Secondary Miss</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Secondary Rate</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">平均缺失延迟</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">AMAT</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">最终评价</div></td></tr><tr class="notion-simple-table-row notion-block-61592cfa981b43c8ade7fe02bb78b5a0"><td class="" style="width:120px"><div class="notion-simple-table-cell">2 × 16B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">train</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">2,321,923,102</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">195,161,085</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0.084051</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">79.15%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">26,904,106</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">13.79%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">13,794,245</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">7.07%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">31.83 cycles</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">7.43</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">容量较小，缺失次数多，总周期最长</div></td></tr><tr class="notion-simple-table-row notion-teal_background notion-block-3929c4f5ba6342e5871852f07ff57977"><td class="" style="width:120px"><div class="notion-simple-table-cell">4 × 16B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">train</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1,820,098,664</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">195,160,354</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0.107225</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">85.69%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">17,947,819</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">9.20%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">9,972,220</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">5.11%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">31.38 cycles</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">5.35</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">总周期最少、IPC最高且AMAT最低，当前最优</div></td></tr><tr class="notion-simple-table-row notion-block-bf3132426dd24a0ba48fcf57cb44463e"><td class="" style="width:120px"><div class="notion-simple-table-cell">2 × 32B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">train</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1,956,200,058</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">195,158,942</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0.099764</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">89.64%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">12,752,948</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">6.53%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">7,469,722</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">3.83%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">45.46 cycles</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">5.61</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">命中率最高，但长突发使平均缺失延迟明显增加</div></td></tr><tr class="notion-simple-table-row notion-block-92bf3613f6a14d11b58538483efcad62"><td class="" style="width:120px"><div class="notion-simple-table-cell">8 × 8B</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">train</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1,953,808,091</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">195,164,756</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0.099889</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">77.21%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">30,013,416</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">15.38%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">14,458,343</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">7.41%</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">24.67 cycles</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">6.39</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">平均缺失延迟较低，但缺失次数过多</div></td></tr></tbody></table><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-3b4c0e14283f807c8318fddeba393037" data-id="3b4c0e14283f807c8318fddeba393037"><span><div id="3b4c0e14283f807c8318fddeba393037" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3b4c0e14283f807c8318fddeba393037" title="流水线处理器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">流水线处理器</span></span></h3><div class="notion-text notion-block-99ddc91741e54370ab36e9da9a36f8f2">在 B 阶段开始时已经做过了 NPC 的重构：按照分布消息控制方式拆分成五级流水的模型，所以直接加入流水线寄存器以及处理三大冒险。</div><div class="notion-text notion-block-52b8559b3f174815816dab26bdd5a534">冒险处理也先采用比较简单的方案：数据冒险从 LSU、WBU 向 EXU 前递，离 EXU 更近的 LSU 优先；Load 的数据在 LSU 阶段还没有返回时就阻塞相关指令。结构冒险主要来自 IFU 和 LSU 共用总线，目前不加入 DCache，由仲裁和流水线反压解决。控制冒险使用静态不跳转预测，分支在 EXU 得出结果后重定向 PC，并冲刷 IF/ID、ID/EX 中的错误路径指令。WBU 作为唯一提交点，Difftest、指令计数和停机等信息都从同一条提交指令中取得，避免软件侧看到尚未退休的流水线状态。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-8278358bb2e7424f8369cd43f9d49f59" data-id="8278358bb2e7424f8369cd43f9d49f59"><span><div id="8278358bb2e7424f8369cd43f9d49f59" class="notion-header-anchor"></div><a class="notion-hash-link" href="#8278358bb2e7424f8369cd43f9d49f59" title="前递数据不能只看当前周期"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">前递数据不能只看当前周期</span></span></h4><div class="notion-text notion-block-232c950d1f494b8e96d527c1dc5e82f7">实现时遇到过一个比较隐蔽的问题。下面三条指令执行到分支时，<code class="notion-inline-code">a4</code> 可以从 LSU 获取，<code class="notion-inline-code">a5</code> 可以从 WBU 获取，所以两个源操作数能在同一周期凑齐：</div><div class="notion-text notion-block-31ca01fc6f694f02a5e3effb0b40c9af">但换成连续 Load 后情况不同：</div><div class="notion-text notion-block-ac6e735c59034f5b9d97782693d98851">第二条 Load 进入 LSU 后需要等待内存返回，分支也会因此停在 EXU。等待期间第一条 Load 已经在 WBU 退休，<code class="notion-inline-code">a5</code> 的前递值只在当时的组合逻辑中出现过；下一周期 WBU 内容更新，这个值就丢失了，而分支保存在级间寄存器里的仍然是 IDU 之前读出的旧值。</div><div class="notion-text notion-block-bf20cd31522f48f99f5417b3e803f619">因此，EXU 在被阻塞时需要分别记录两个源操作数是否已经就绪，并把已经前递到的值暂存下来。之后即使原来的前递来源离开流水线，也可以用暂存的 <code class="notion-inline-code">a5</code> 配合刚返回的 <code class="notion-inline-code">a4</code> 继续执行。这里不能只做一个整体的“操作数有效”标志，因为两个操作数到达的时间可能不同。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-3cfc0e14283f808ea928fb83758a19b1" data-id="3cfc0e14283f808ea928fb83758a19b1"><span><div id="3cfc0e14283f808ea928fb83758a19b1" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3cfc0e14283f808ea928fb83758a19b1" title="使用 Konata 流水线可视化工具"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">使用 Konata 流水线可视化工具</span></span></h4><ul class="notion-list notion-list-disc notion-block-3d0c0e14283f80f0938fd58f6adf8fa4"><li>I：创建一条动态指令。</li></ul><ul class="notion-list notion-list-disc notion-block-3d0c0e14283f80d59531e22acedf5419"><li>S：进入一个流水级。</li></ul><ul class="notion-list notion-list-disc notion-block-3d0c0e14283f80daa112d1a09315bd35"><li>L：设置指令显示文本。</li></ul><ul class="notion-list notion-list-disc notion-block-3d0c0e14283f80c1b988f995c1445bbd"><li>R：这条动态指令结束，可能是正常退休，也可能被冲刷</li></ul><ul class="notion-list notion-list-disc notion-block-3d0c0e14283f800c8f1fc73bff90eda2"><li>C：仿真时间向前推进若干周期。</li></ul><div class="notion-text notion-block-3d3c0e14283f80c8bcb7eb8fb1a49062">使用这个工具时候遇到一个 bug：在 NPC STANDALONE 模式下，一些测试用例会触发 disassemble 反汇编失败的断言。</div><div class="notion-row notion-block-3d4c0e14283f80e2b1eede6b84defb06"><div class="notion-column notion-block-3d4c0e14283f803995e5ff0538882164" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3d4c0e14283f80f5afd4f33e460758c3"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A26616d45-0b08-45fb-9828-c8bf9d9a4266%3Aimage.png?table=block&amp;id=3d4c0e14-283f-80f5-afd4-f33e460758c3&amp;t=3d4c0e14-283f-80f5-afd4-f33e460758c3" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-3d4c0e14283f806cb3f4d7ce7631162a" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3d4c0e14283f809a8ac5cf2db1a79019"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ac2119923-761e-46a2-8176-21aa1f97349a%3Aimage.png?table=block&amp;id=3d4c0e14-283f-809a-8ac5-cf2db1a79019&amp;t=3d4c0e14-283f-809a-8ac5-cf2db1a79019" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3d4c0e14283f80cbac4bf39b40e615ed"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A4de46f1a-5611-4e1b-8704-79f4dc9f1ce3%3Aimage.png?table=block&amp;id=3d4c0e14-283f-80cb-ac4b-f39b40e615ed&amp;t=3d4c0e14-283f-80cb-ac4b-f39b40e615ed" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div></div><div class="notion-row notion-block-3d4c0e14283f80c2b623f611eb858992"><div class="notion-column notion-block-3d4c0e14283f800c9796e1942f06af74" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3d4c0e14283f802eb394df2a2b8e7d69"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A8b62bb51-4191-41fa-8312-f35435282fe1%3Aimage.png?table=block&amp;id=3d4c0e14-283f-802e-b394-df2a2b8e7d69&amp;t=3d4c0e14-283f-802e-b394-df2a2b8e7d69" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-3d4c0e14283f80ee8ae4db0e80f3cd53" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3d4c0e14283f807b8906c020ac6b7fef"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A2ad7bff7-b463-421b-967e-86023e3a7c62%3Aimage.png?table=block&amp;id=3d4c0e14-283f-807b-8906-c020ac6b7fef&amp;t=3d4c0e14-283f-807b-8906-c020ac6b7fef" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div></div><div class="notion-text notion-block-3d4c0e14283f80d9b19fc5016a4c2e4d">调查发现是由于生成 konata.log 时候调用 NEMU 中的反汇编工具，IF 取到非法指令但来不及冲刷时调用了反汇编函数导致仿真触发断言中断程序。</div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[“一生一芯”项目学习记录（一）SoC 计算机系统（v23.06）]]></title>
            <link>http://hyacimond.top/arch/ysyx-record1</link>
            <guid>http://hyacimond.top/arch/ysyx-record1</guid>
            <pubDate>Thu, 01 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[包含部分源码详解与踩坑记录]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-2dbc0e14283f805381c6f0a650171f58"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-red_background_co notion-block-2dbc0e14283f802c9409f39173d2a8d0"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="❗">❗</span></div><div class="notion-callout-text"><span class="notion-red_background"><b>声明</b></span>
该文章作为一生一芯官方学习记录的补充，部分代码实现思路以及思考仅代表个人观点。
本文不立足于撰写“详细教程”、“通关秘籍”等类型的文章，也不包含考核答辩面经，仅仅作为个人记录留存。
<span class="notion-red_background"><b><span class="notion-inline-underscore">如果您正参加该项目，且尚未完成相应任务点，请自觉退出！该行为可能与“一生一芯”项目官方所推崇的独立解决能力培养理念相违背！</span></b></span></div></div><div class="notion-callout notion-teal_background_co notion-block-2dbc0e14283f80d39ad2ff8baa9c1c45"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="📖">📖</span></div><div class="notion-callout-text"><div class="notion-text notion-block-2dbc0e14283f80ec879ccf8141dc107f">本文内容包括但不限于：</div><ol start="1" class="notion-list notion-list-numbered notion-block-2dbc0e14283f8061b358f5d24b8320a4" style="list-style-type:decimal"><li>部分代码设计实现思路</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-2dbc0e14283f801caaafe530217c56e7" style="list-style-type:decimal"><li>踩坑记录</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-2dbc0e14283f804c83a6e7b7f2523164" style="list-style-type:decimal"><li>部分必做题、思考题观点整理</li></ol><div class="notion-text notion-block-2dbc0e14283f80bf95edda6d2f560e14">……</div></div></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-2dbc0e14283f801c8292ea7bda982591" data-id="2dbc0e14283f801c8292ea7bda982591"><span><div id="2dbc0e14283f801c8292ea7bda982591" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2dbc0e14283f801c8292ea7bda982591" title="📝 预学习阶段"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📝 预学习阶段</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2dbc0e14283f80c3ba90ee8acc80690e" data-id="2dbc0e14283f80c3ba90ee8acc80690e"><span><div id="2dbc0e14283f80c3ba90ee8acc80690e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2dbc0e14283f80c3ba90ee8acc80690e" title="NVBoard"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">NVBoard</span></span></h3><ol start="1" class="notion-list notion-list-numbered notion-block-2dbc0e14283f806c951bcecf29b54e7a" style="list-style-type:decimal"><li><code class="notion-inline-code">sim</code>目标通过定义<code class="notion-inline-code">FST</code>宏可自由生成不同格式的波形文件</li></ol><blockquote class="notion-quote notion-block-2dbc0e14283f8043a077f0fbe4e89b90"><div>一生一芯官方推荐版本为<code class="notion-inline-code">Verilator 5.008 2023-03-04 rev v5.008</code>，该版本下<code class="notion-inline-code">—trace-vcd</code>和<code class="notion-inline-code">—trace-fst</code>不可用
新版本中已经弃用<code class="notion-inline-code">—trace</code>参数，作者此处使用版本为<code class="notion-inline-code">Verilator 5.040 2025-08-30 rev v5.040-50-g0a9d9db5a</code>
具体请参考官方手册：</div><div class="notion-row"><a class="notion-bookmark notion-block-2dbc0e14283f8095b5c7fb92de94366c" href="https://veripool.org/guide/latest/exe_verilator.html#cmdoption-trace" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">verilator Arguments — Verilator Devel 5.043 documentation</div><div class="notion-bookmark-description">The following arguments may be passed to the “verilator” executable.</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fveripool.org%2Fguide%2Flatest%2F_static%2Fverilator_32x32_min.png?table=block&amp;id=2dbc0e14-283f-8095-b5c7-fb92de94366c&amp;t=2dbc0e14-283f-8095-b5c7-fb92de94366c" alt="verilator Arguments — Verilator Devel 5.043 documentation" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://veripool.org/guide/latest/exe_verilator.html#cmdoption-trace</div></div></div></a></div></blockquote><div class="notion-blank notion-block-327c0e14283f807db644dcb656b0670b"> </div><ol start="2" class="notion-list notion-list-numbered notion-block-2dbc0e14283f80ceacf0f2d86d97dc94" style="list-style-type:decimal"><li>键盘检测实验中作者觉得讲义中所写状态机不好理解，遂自行实现，依照 HDLBits 网站中的状态机描述风格：</li></ol><hr class="notion-hr notion-block-2e3c0e14283f8001af06c2c1865d0686"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2dbc0e14283f8079aef3eca7874ca72e" data-id="2dbc0e14283f8079aef3eca7874ca72e"><span><div id="2dbc0e14283f8079aef3eca7874ca72e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2dbc0e14283f8079aef3eca7874ca72e" title="NEMU（PA1）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">NEMU（PA1）</span></span></h3><div class="notion-text notion-block-2dbc0e14283f80ceb817cc945182e71b">主要内容是实现一个简易调试器</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2dbc0e14283f80eb9f66d139b78a2a12"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Afc963d40-505a-40ba-9c8a-2b5ecb22ce56%3Aimage.png?table=block&amp;id=2dbc0e14-283f-80eb-9f66-d139b78a2a12&amp;t=2dbc0e14-283f-80eb-9f66-d139b78a2a12" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-2dbc0e14283f80f9abfcc09c96e8b923" data-id="2dbc0e14283f80f9abfcc09c96e8b923"><span><div id="2dbc0e14283f80f9abfcc09c96e8b923" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2dbc0e14283f80f9abfcc09c96e8b923" title="sdb_mainloop()解析"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><code class="notion-inline-code">sdb_mainloop()</code>解析</span></span></h4><div class="notion-text notion-block-2dbc0e14283f808fb7eee1755af9e5a2"><code class="notion-inline-code">rl_gets()</code>函数将整行命令读取到<code class="notion-inline-code">str</code>中，<code class="notion-inline-code">strtok</code>函数按空格分割出目标命令，在查找表中逐个对比，一旦匹配则进入相应的<code class="notion-inline-code">cmd_*</code>逻辑中继续执行。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-2dbc0e14283f80f69663e41e04a8f840" data-id="2dbc0e14283f80f69663e41e04a8f840"><span><div id="2dbc0e14283f80f69663e41e04a8f840" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2dbc0e14283f80f69663e41e04a8f840" title="优雅的退出"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">优雅的退出</span></span></h4><div class="notion-text notion-block-2dbc0e14283f80dfba24fa271a9fa2dd">主函数退出前会执行一个判断状态的函数：</div><div class="notion-text notion-block-2dbc0e14283f80b289cbd28a8620687b">故只需要在<code class="notion-inline-code">cmd_q</code>中设置 NEMU 状态为退出即可：</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-2dbc0e14283f804ea304ec3159f3ec5b" data-id="2dbc0e14283f804ea304ec3159f3ec5b"><span><div id="2dbc0e14283f804ea304ec3159f3ec5b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2dbc0e14283f804ea304ec3159f3ec5b" title="表达式求值"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">表达式求值</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-2dbc0e14283f80018f10e765703fd714" style="list-style-type:decimal"><li>隐式类型转换</li></ol><div class="notion-text notion-block-2dbc0e14283f80159243c4a26b4ff2f2">完成表达式求值的核心任务就是不断处理各种非法表达式，确保计算结果格式与预期相符。</div><div class="notion-text notion-block-2dbc0e14283f8096898ec62c4300448e">讲义中统一规定使用无符号格式进行计算，而C语言中执行字面量除法默认是有符号数除法，为了与测试脚本运行结果一致，将<code class="notion-inline-code">val1</code>和<code class="notion-inline-code">val2</code>强转为有符号类型</div><ol start="2" class="notion-list notion-list-numbered notion-block-2dbc0e14283f80a2a4f3ed600d1c7d58" style="list-style-type:decimal"><li>表达式生成器</li></ol><div class="notion-text notion-block-2dbc0e14283f8015a388f258e55daa8b">讲义中建议为在主函数中加入测试，而作者在这里单独新增一个test指令，在 NEMU 中执行 test 命令将自动生成 500 条表达式（自动丢弃掉非法表达式，例如包含除以 0 的表达式）。</div><div class="notion-text notion-block-2dbc0e14283f80d98d50c495d675b698">同时为了避免生成过长的表达式，对递归深度和生成表达式长度进行限制，否则有可能除法段错误：</div><div class="notion-text notion-block-2dbc0e14283f80a7809adae88793d5fd">测试结果：</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-2dbc0e14283f8024a9e9cbbd693df943" data-id="2dbc0e14283f8024a9e9cbbd693df943"><span><div id="2dbc0e14283f8024a9e9cbbd693df943" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2dbc0e14283f8024a9e9cbbd693df943" title="断点Bug"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">断点Bug</span></span></h4><div class="notion-text notion-block-2dbc0e14283f8063a286d0edb45e8975">完成监视点内容之后，作者使用<code class="notion-inline-code">w $pc==0x8000000c</code>设置断点到最后一条 ebreak 指令，结果如下：</div><div class="notion-text notion-block-2dbc0e14283f80a09208ecd007332e64">发现非法指令被执行。经过 gdb 调试发现，执行结束 ebreak 指令后，NEMU 状态会被设置为END后安全退出，而扫描监视点时，命中监视点会将 NEMU 状态设置为STOP，导致后续读取到非法指令触发错误，解决方案也很简单，加一层状态判断即可。
</div><div class="notion-text notion-block-2dbc0e14283f80e0a535c49e7e812d06">另外，此处是用监视点模拟断点，因此程序代码运行时产生了两次中断（旧值不等于新值）。</div><hr class="notion-hr notion-block-2e3c0e14283f808e805ffcf7638693ea"/><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-2dbc0e14283f8025bd63d98566775e18" data-id="2dbc0e14283f8025bd63d98566775e18"><span><div id="2dbc0e14283f8025bd63d98566775e18" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2dbc0e14283f8025bd63d98566775e18" title="C阶段"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">C阶段</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2dbc0e14283f8099a1ddc69c5caee864" data-id="2dbc0e14283f8099a1ddc69c5caee864"><span><div id="2dbc0e14283f8099a1ddc69c5caee864" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2dbc0e14283f8099a1ddc69c5caee864" title="理解一条指令在 NEMU 中的执行过程"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">理解一条指令在 NEMU 中的执行过程</span></span></h3><ul class="notion-list notion-list-disc notion-block-2dbc0e14283f803393a8f922b6f15055"><li><code class="notion-inline-code">exec_once(&amp;s, cpu.pc);</code> 包含指令的取指、译码、执行、pc 更新，cpu 结构体保存全局状态，<code class="notion-inline-code">Decode s;</code>为运行时上下文结构体，保存静态 pc、动态 pc、指令等相关元素。</li></ul><ul class="notion-list notion-list-disc notion-block-2dbc0e14283f800096eaefc5e002550c"><li>exec_once 前四行：</li></ul><ul class="notion-list notion-list-disc notion-block-2dbc0e14283f80ba8583e1a7a19b3de4"><li>取指过程：</li></ul><div class="notion-text notion-block-2dbc0e14283f8092a391dd46168ec905"><code class="notion-inline-code">vaddr_ifetch</code>函数到对应地址下读取32位长度的指令，然后将<code class="notion-inline-code">snpc+4</code>，返回读取的指令。</div><ul class="notion-list notion-list-disc notion-block-2dbc0e14283f80f5bb4ef602c5e1dea1"><li>译码与执行过程：</li></ul><div class="notion-text notion-block-2dbc0e14283f8078bb15f3aacbefa3a7">这一长串宏直接使用 VS Code 插件自带的智能感知（Intellisence）展开为：（下文只保留一个INSTPAT）</div><blockquote class="notion-quote notion-block-2dbc0e14283f806282a6d8e7204f49b7"><div>有关于<code class="notion-inline-code">do {} while(0)</code>的用法参考</div><div class="notion-row"><a class="notion-bookmark notion-block-2dbc0e14283f80c087a5ff207a6ffb29" href="https://stackoverflow.com/questions/154136/why-use-apparently-meaningless-do-while-and-if-else-statements-in-macros" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">Why use apparently meaningless do-while and if-else statements in macros?</div><div class="notion-bookmark-description">In many C/C++ macros I&#x27;m seeing the code of the macro wrapped in what seems like a meaningless do while loop.  Here are examples.
#define FOO(X) do { f(X); g(X); } while (0)
#define FOO(X) if (1) {...</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fstackoverflow.com%2FContent%2FSites%2Fstackoverflow%2FImg%2Fapple-touch-icon.png%3Fv%3Dc78bd457575a?table=block&amp;id=2dbc0e14-283f-80c0-87a5-ff207a6ffb29&amp;t=2dbc0e14-283f-80c0-87a5-ff207a6ffb29" alt="Why use apparently meaningless do-while and if-else statements in macros?" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://stackoverflow.com/questions/154136/why-use-apparently-meaningless-do-while-and-if-else-statements-in-macros</div></div></div><div class="notion-bookmark-image"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fstackoverflow.com%2FContent%2FSites%2Fstackoverflow%2FImg%2Fapple-touch-icon%402.png%3Fv%3D73d79a89bded?table=block&amp;id=2dbc0e14-283f-80c0-87a5-ff207a6ffb29&amp;t=2dbc0e14-283f-80c0-87a5-ff207a6ffb29" alt="Why use apparently meaningless do-while and if-else statements in macros?" loading="lazy" decoding="async"/></div></a></div></blockquote><blockquote class="notion-quote notion-block-2dbc0e14283f80e78dfecdbcfaf941e0"><div>有关于符号标记用法参考。从功能上来说，这种方式给出了一种高效快捷的代码跳转方式，一旦匹配则提前结束该代码块。</div><div class="notion-row"><a class="notion-bookmark notion-block-2dbc0e14283f8078ba8ef1791e4c6b68" href="https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">Labels as Values (Using the GNU Compiler Collection (GCC))</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fgcc.gnu.org%2Ffavicon.ico?table=block&amp;id=2dbc0e14-283f-8078-ba8e-f1791e4c6b68&amp;t=2dbc0e14-283f-8078-ba8e-f1791e4c6b68" alt="Labels as Values (Using the GNU Compiler Collection (GCC))" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html</div></div></div></a></div></blockquote><ul class="notion-list notion-list-disc notion-block-2dbc0e14283f80d09c83e3798ae0aebf"><li><code class="notion-inline-code">pattern_decode</code>函数详解</li></ul><div class="notion-text notion-block-2dbc0e14283f8064b9c9e878280c1339">这里<code class="notion-inline-code">i</code>表示处理指令字符串的下标。空格被自动忽略，除0、1、?之外的字符会抛出异常，下面以“??????? ????? ????? ??? ????? 00101 11”为例子：</div><div class="notion-text notion-block-2dbc0e14283f80c3a972e1194ff800ac"><code class="notion-inline-code">__key</code>用于识别指令操作码，只对0和1感兴趣，最终值为0010111;</div><div class="notion-text notion-block-2dbc0e14283f80ad8d32fd5cbc10d884"><code class="notion-inline-code">__mask</code>用于生成掩码，?对应位置设置为0，其他对应设置为1，表示?区域不应参与匹配。</div><div class="notion-text notion-block-2dbc0e14283f80088ab9e97f745589e8"><code class="notion-inline-code">__shift</code>对于一些不关心低位匹配的操作码，如110 ????，最终它的值为4，右移四位匹配。</div><div class="notion-text notion-block-2dbc0e14283f805180ede091c801af3a"><code class="notion-inline-code">micro64(0);</code> 展开为<code class="notion-inline-code">macro32(0);macro32(32);</code>，继续展开为<code class="notion-inline-code">macro16(0);macro16(16);macro16(32);macro16(48);</code> ，最终变成<code class="notion-inline-code">macro(0);macro(1);macro(2);……;marcro(64);</code> ，这种宏展开可以在编译阶段直接优化，减少for循环方式造成的运行时开销。</div><div class="notion-callout notion-yellow_background_co notion-block-2dbc0e14283f80c49164c741cdbbd9b2"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text"><div class="notion-text notion-block-2dbc0e14283f80b3865af67eb7cc03f2">这种思路的可实现性在于指令是提前就加载到代码中的，即编译阶段指令代码的内容就是确定的。而对于等待用户输入是程序运行时才能解决的问题。</div></div></div><div class="notion-text notion-block-2dbc0e14283f80afbc8be19e3c5fab48">接下来这行代码<code class="notion-inline-code">(((uint64_t)((s)-&gt;isa.inst) &gt;&gt; shift) &amp; mask) == key</code> 对指令进行匹配，匹配成功则提取操作码和操作数。</div><ul class="notion-list notion-list-disc notion-block-2dbc0e14283f8000b052f278e3dd7e95"><li><code class="notion-inline-code">decode_operand</code>函数详解</li></ul><div class="notion-text notion-block-2dbc0e14283f801da861d6316a50c408">这里<code class="notion-inline-code">BITS</code>宏进行位切片，提取出对应的寄存器。</div><div class="notion-text notion-block-2dbc0e14283f80808b99d55270d2eedd"><code class="notion-inline-code">src1R()</code>宏展开之后长这样，很显然是对相应寄存器的赋值操作：</div><div class="notion-text notion-block-2dbc0e14283f80f197fac3c7d1c268bd"><code class="notion-inline-code">immI()</code>宏展开之后长这样，提取I型指令的立即数，并做符号扩展：</div><div class="notion-text notion-block-2dbc0e14283f8091bb28c73232eaf3ea">之后便可以完成代码的执行，结束后返回0。</div><hr class="notion-hr notion-block-2e3c0e14283f808bb970e6ab240d404b"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2dbc0e14283f8032b0fbec57cde7012b" data-id="2dbc0e14283f8032b0fbec57cde7012b"><span><div id="2dbc0e14283f8032b0fbec57cde7012b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2dbc0e14283f8032b0fbec57cde7012b" title="实现更多的指令"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">实现更多的指令</span></span></h3><div class="notion-text notion-block-2dcc0e14283f80e7a7f0d877cd9f7b2f">运行测试用例 mul-longlong.c 时出现 <span class="notion-red">HIT BAD TRAP </span>的提示：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2dcc0e14283f80ee9177e37f16aad2d9"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A13ed9265-2b50-4891-8f0b-273d709bcf97%3Aimage.png?table=block&amp;id=2dcc0e14-283f-80ee-9177-e37f16aad2d9&amp;t=2dcc0e14-283f-80ee-9177-e37f16aad2d9" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2dcc0e14283f80328ca4d5759fae3a56">使用 gdb 调试，调用栈如下：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2dcc0e14283f80149fffc0da3cd4c80d"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Acebc01ee-02f3-4dee-ab76-2854ef93a102%3Aimage.png?table=block&amp;id=2dcc0e14-283f-8014-9fff-c0da3cd4c80d&amp;t=2dcc0e14-283f-8014-9fff-c0da3cd4c80d" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2dcc0e14283f802881e9c6e631aa462b">该语句导致<code class="notion-inline-code">halt_ret</code>被置1：</div><div class="notion-text notion-block-2dcc0e14283f8095be7cf2143678f9f1">这说明，执行 ebreak 时，$a0 寄存器的值为 1。打印当前<code class="notion-inline-code">s→pc</code>的值为 0x80000140 </div><div class="notion-text notion-block-2dcc0e14283f80859419eccff7e1caa5">接下来利用 sdb 在 NEMU 中设置对 $a0 的监视点：</div><div class="notion-text notion-block-2dcc0e14283f8077a788ce9251dcbf86">查看反汇编，发现 check 函数执行导致 a0 被设置为 1</div><div class="notion-text notion-block-2dcc0e14283f806daee0de6ac8ef5140">结合C代码和反汇编分析容易知道，<code class="notion-inline-code">check</code>函数负责检查乘法计算结果和预期结果是否一致，说明我们<span class="notion-red_background"><b><span class="notion-inline-underscore">乘法指令的实现存在问题</span></b></span></div><blockquote class="notion-quote notion-gray_background notion-block-2dcc0e14283f80f4863ccf626c483285"><div>C11 标准（N1570）中指出：
<b>“When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.”
当一个整数类型的值被转换为除 _Bool 以外的另一种整数类型时，如果该值能够被新类型表示，则该值保持不变。
</b>32位无符号数任意取值都能够被 64 位有符号类型的整数所表示，所以转换时值不变，高位作零扩展。如果作符号扩展，它的数值就被改变了。</div><div class="notion-row"><a class="notion-bookmark notion-block-2dcc0e14283f803b9304c9c77e2b2449" href="https://www.iso-9899.info/n1570.html#6.3.1" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">www.iso-9899.info</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fwww.iso-9899.info%2Ffavicon.ico?table=block&amp;id=2dcc0e14-283f-803b-9304-c9c77e2b2449&amp;t=2dcc0e14-283f-803b-9304-c9c77e2b2449" alt="www.iso-9899.info" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://www.iso-9899.info/n1570.html#6.3.1</div></div></div></a></div></blockquote><div class="notion-text notion-block-2dcc0e14283f8021a361e6b677729a34">此处实现方法的确有问题，对于<code class="notion-inline-code">mulh</code>这种指令，<code class="notion-inline-code">src1</code>和<code class="notion-inline-code">src2</code>如果是负数，类型转换后变成了正数，计算结果自然与实际不相符。于是对<code class="notion-inline-code">src1</code>和<code class="notion-inline-code">src2</code>先使用宏函数<code class="notion-inline-code">SEXT()</code>进行符号扩展，最后进行运算。最终修改如下：</div><div class="notion-text notion-block-2dcc0e14283f8068ada6fe8ace248329">再次运行成功通过：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2dcc0e14283f80ee9271edcf2272e2bd"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Abf231651-b499-48b6-9315-374722d2699d%3Aimage.png?table=block&amp;id=2dcc0e14-283f-80ee-9271-edcf2272e2bd&amp;t=2dcc0e14-283f-80ee-9271-edcf2272e2bd" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2dcc0e14283f8059be2cef818200e161">完整测试如下：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2dcc0e14283f8019a08bd6416a6fa57b"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:320px"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A66a0f0f5-78a8-46a4-91ac-957892dbb162%3Afb466413-55ba-4463-b360-0f4a84b746b6.png?table=block&amp;id=2dcc0e14-283f-8019-a08b-d6416a6fa57b&amp;t=2dcc0e14-283f-8019-a08b-d6416a6fa57b" alt="notion image" loading="lazy" decoding="async"/></div></figure><hr class="notion-hr notion-block-2e3c0e14283f8059b5fdc795b906b146"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2e3c0e14283f80c58d36cb9c68d8eb5d" data-id="2e3c0e14283f80c58d36cb9c68d8eb5d"><span><div id="2e3c0e14283f80c58d36cb9c68d8eb5d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2e3c0e14283f80c58d36cb9c68d8eb5d" title="使用 DPI-C 机制验证 RV32I 单周期 CPU"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">使用 DPI-C 机制验证 RV32I 单周期 CPU</span></span></h3><div class="notion-text notion-block-2e3c0e14283f8062ac24d2c3e20aa8bb">DPI-C 机制允许在 System Verilog 中直接调用 C 函数，或者导出 function 和 task 供 C 语言调用。</div><div class="notion-text notion-block-2e3c0e14283f8097857efb6c75b26e8e"><code class="notion-inline-code">npc_trap();</code>的实现：</div><div class="notion-text notion-block-2e3c0e14283f809d9fefeaf9ee093429">使用 <b>verilator </b>进行仿真</div><div class="notion-text notion-block-2e3c0e14283f8054b024fc672ad4a4bb"><code class="notion-inline-code">while</code>内至少<code class="notion-inline-code">top→eval();</code>两次，第一次计算由时钟沿触发产生的时序逻辑更新（<code class="notion-inline-code">clk</code> 0→1，<code class="notion-inline-code">eval()</code>触发 pc_o 的更新），第二次计算触发寄存器更新导致的组合逻辑更新（由新的<code class="notion-inline-code">pc_o</code>取出下一条指令）。</div><div class="notion-text notion-block-2e3c0e14283f807c8250e32a1619dfdb">这样波形上才能和<b><span class="notion-inline-underscore">事件驱动型仿真器</span></b>一致。</div><div class="notion-text notion-block-2e3c0e14283f8099b94df1cbb5c19a38"><code class="notion-inline-code">top→eval();</code>是根据输入激励计算输出</div><div class="notion-text notion-block-2e3c0e14283f8072b487ca2d48bdfa5d"><code class="notion-inline-code">tfp-&gt;dump(contextp-&gt;time());</code>是按当前时间轴记录一次波形
<code class="notion-inline-code">contextp-&gt;timeInc(1);</code>是当前记录时间加一，三者必须共同配合才能仿真出完整波形</div><div class="notion-text notion-block-2e3c0e14283f8075b0e0fcd7543d4b47">测试结果：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2e3c0e14283f806d997fee573931c47c"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A1f28672e-8c7a-4f82-a10f-5d5f6c6e9b08%3Aimage.png?table=block&amp;id=2e3c0e14-283f-806d-997f-ee573931c47c&amp;t=2e3c0e14-283f-806d-997f-ee573931c47c" alt="notion image" loading="lazy" decoding="async"/></div></figure><hr class="notion-hr notion-block-2e3c0e14283f800db445cde358e44cf4"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2e3c0e14283f80399d10c0d9a38dcbdc" data-id="2e3c0e14283f80399d10c0d9a38dcbdc"><span><div id="2e3c0e14283f80399d10c0d9a38dcbdc" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2e3c0e14283f80399d10c0d9a38dcbdc" title="对 AM 的 Makefile 默认启动批处理模式NEMU"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">对 AM 的 Makefile 默认启动批处理模式NEMU</span></span></h3><div class="notion-text notion-block-2e3c0e14283f80c3b5dbf403219da385">通过RTFSC得知，要在运行NEMU时传入参数<code class="notion-inline-code">-b</code>   </div><div class="notion-text notion-block-2e3c0e14283f807b97fbd9bf866da608">作者此处直接使用 <a class="notion-link" href="https://remake.readthedocs.io/en/remake-4-4/" target="_blank" rel="noopener noreferrer">remake</a> 工具单步调试，发现最终<code class="notion-inline-code">ARGS</code>变量的来源：</div><div class="notion-text notion-block-2e3c0e14283f80a3afb6d0b84ffbad62">对应 Makefile 源代码位置修改如下：</div><div class="notion-text notion-block-2e5c0e14283f8011a63acd95aae5aa58">考虑到对测试代码使用 sdb 的需求，作如下调整：</div><hr class="notion-hr notion-block-2e3c0e14283f8079a0d7c642a2fafc4e"/><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-2e3c0e14283f80a6b5fac823a6ee5fa2" data-id="2e3c0e14283f80a6b5fac823a6ee5fa2"><span><div id="2e3c0e14283f80a6b5fac823a6ee5fa2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2e3c0e14283f80a6b5fac823a6ee5fa2" title="添加递归 remake 支持"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">添加递归 remake 支持</span></span></h4><div class="notion-text notion-block-2e3c0e14283f804cbe3dfcceb4679c42">起因是由于使用 remake 单步调试时遇到子项目的 make 构建就会直接跳过，如果想要调试子目标的 Makefile 就要中途退出一次，然后使用 remake 调试 Makefile.xxx，不太方便。后来发现了 nemu.mk 文件使用了 <code class="notion-inline-code">$(MAKE)</code> 这个内置变量，查询了该变量相关用法，于是做如下修改：</div><div class="notion-row notion-block-2e8c0e14283f80b29d04dbad1be45662"><div class="notion-column notion-block-2e8c0e14283f80649166dd7d5cbf1acd" style="width:calc((100% - (1 * min(32px, 4vw))) * 1)"><div class="notion-text notion-block-2e3c0e14283f805e91a8d888d990b755">修改前单步调试：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2e3c0e14283f809a9241c4d532eee6a5"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:288px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ad268eacb-22d9-4692-8052-8ca173432514%3Aimage.png?table=block&amp;id=2e3c0e14-283f-809a-9241-c4d532eee6a5&amp;t=2e3c0e14-283f-809a-9241-c4d532eee6a5" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-2e8c0e14283f80539086fea911fa5d3e" style="width:calc((100% - (1 * min(32px, 4vw))) * 1)"><div class="notion-text notion-block-2e3c0e14283f80c8bd11fe8111a66170">修改后单步调试：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2e3c0e14283f8097b5edd8f9358a4dce"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ae4e516c3-9033-4db3-b555-26f07272f8e0%3Aimage.png?table=block&amp;id=2e3c0e14-283f-8097-b5ed-d8f9358a4dce&amp;t=2e3c0e14-283f-8097-b5ed-d8f9358a4dce" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div></div><div class="notion-text notion-block-2e3c0e14283f80569934f0893ef31eff">内部的 Make 也会开启 Trace 模式，子目录的详细构建过程也被打印出来了。</div><blockquote class="notion-quote notion-gray_background notion-block-2e3c0e14283f80beb84cfbc4e3cb29d1"><div><b>参考文档内容</b></div><div class="notion-row"><a class="notion-bookmark notion-block-2e3c0e14283f80488b8ed9768188b942" href="https://www.gnu.org/software/make/manual/make.html#Recursion" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">GNU make</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fwww.gnu.org%2Ffavicon.ico?table=block&amp;id=2e3c0e14-283f-8048-8b8e-d9768188b942&amp;t=2e3c0e14-283f-8048-8b8e-d9768188b942" alt="GNU make" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://www.gnu.org/software/make/manual/make.html#Recursion</div></div></div></a></div></blockquote><hr class="notion-hr notion-block-2e6c0e14283f80439cd0f5150b3aaa97"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2e5c0e14283f801a8650ce7da7d10850" data-id="2e5c0e14283f801a8650ce7da7d10850"><span><div id="2e5c0e14283f801a8650ce7da7d10850" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2e5c0e14283f801a8650ce7da7d10850" title="使用 C 语言解析 ELF 文件并实现 ftrace"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">使用 C 语言解析 ELF 文件并实现 ftrace</span></span></h3><div class="notion-text notion-block-2e6c0e14283f8091962bc6de60dae831">主要完成：</div><ul class="notion-list notion-list-disc notion-block-2e6c0e14283f80338840ddfbf9e3599a"><li>使用 elf.h 解析 ELF 文件的符号表，建立针对 FUNC 类型的查找表</li></ul><ul class="notion-list notion-list-disc notion-block-2e6c0e14283f8031870de302941f84fc"><li>判断 call 指令和 ret 指令并打印跟踪日志</li></ul><ul class="notion-list notion-list-disc notion-block-2e6c0e14283f8072b043ff78bb652eee"><li>修改 Makefile 使得运行 tests 时可以自动传入<code class="notion-inline-code">elf</code>参数</li></ul><div class="notion-text notion-block-2e6c0e14283f8042955de31d4b8659d4">ftrace 效果如下：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2e9c0e14283f80da96abc3fb68c6aeaa"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Abaa115b3-c77c-4e62-a513-6265b62ae891%3A%E6%88%AA%E5%9B%BE_2026-01-25_03-47-23.png?table=block&amp;id=2e9c0e14-283f-80da-96ab-c3fb68c6aeaa&amp;t=2e9c0e14-283f-80da-96ab-c3fb68c6aeaa" alt="notion image" loading="lazy" decoding="async"/></div></figure><hr class="notion-hr notion-block-2e6c0e14283f804d817cea543b5d7ba3"/><div class="notion-text notion-block-2e6c0e14283f8072a408fe16d55438f9">这里发现两个个问题：</div><ul class="notion-list notion-list-disc notion-block-2e6c0e14283f80ac8f07d423facd1a88"><li>C 代码最开始是 f0 调用了 f3，但是 ftrace 显示是 f0 调用了 f2</li></ul><ul class="notion-list notion-list-disc notion-block-2e6c0e14283f80c29842ee9e8b706752"><li>讲义中提到的返回不对应问题</li></ul><div class="notion-callout notion-purple_background_co notion-block-2e6c0e14283f8069b0dce7bd62a5a788"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="❓">❓</span></div><div class="notion-callout-text"><div class="notion-text notion-block-2e6c0e14283f8089be9ffd2a1e0cc54e">这里反汇编是 jalr x0, 0(a5)，作为调用指令目的寄存器却不是 ra。</div><div class="notion-text notion-block-2e6c0e14283f80ed89b8ef026d3a28c0">经过 STFW，该现象可能与尾调用有关。如果函数最后一步的操作是调用一个函数，无需把返回地址压入栈中，直接跳转到那个函数，由它直接返回。</div><div class="notion-row"><a class="notion-bookmark notion-block-2e6c0e14283f80e7b243ed6a752673ec" href="https://www.ruanyifeng.com/blog/2015/04/tail-call.html" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">尾调用优化 - 阮一峰的网络日志</div><div class="notion-bookmark-description">上面代码中，情况一是调用函数g之后，还有别的操作，所以不属于尾调用，即使语义完全一样。情况二也属于调用后还有操作，即使写在一行内。</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fwww.ruanyifeng.com%2Ffavicon.ico?table=block&amp;id=2e6c0e14-283f-80e7-b243-ed6a752673ec&amp;t=2e6c0e14-283f-80e7-b243-ed6a752673ec" alt="尾调用优化 - 阮一峰的网络日志" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://www.ruanyifeng.com/blog/2015/04/tail-call.html</div></div></div></a></div><div class="notion-row"><a class="notion-bookmark notion-block-2e6c0e14283f8069ab90da448e2b0558" href="https://zh.wikipedia.org/wiki/%E5%B0%BE%E8%B0%83%E7%94%A8" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">尾调用</div><div class="notion-bookmark-description">在计算机学裡，尾调用是指一个函数里的最后一个动作是返回一个函数的调用结果的情形，即最后一步新调用的返回值直接作為当前函数的返回结果。&amp;#91;1&amp;#93;此时，该尾部调用位置被称为尾位置。尾调用中有一种重要而特殊的情形叫做尾递归。经过适当处理，尾递归形式的函数的运行效率可以被极大地优化。&amp;#91;1&amp;#93;尾调用原则上都可以通过简化函数调用栈的结构而获得性能优化（称为“尾调用消除”），但是优化尾调用是否方便可行取决于运行环境对此类优化的支持程度如何。</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fzh.wikipedia.org%2Fstatic%2Fapple-touch%2Fwikipedia.png?table=block&amp;id=2e6c0e14-283f-8069-ab90-da448e2b0558&amp;t=2e6c0e14-283f-8069-ab90-da448e2b0558" alt="尾调用" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://zh.wikipedia.org/wiki/%E5%B0%BE%E8%B0%83%E7%94%A8</div></div></div></a></div></div></div><blockquote class="notion-quote notion-gray_background notion-block-2e5c0e14283f809e83b8e26938525289"><div><b>参考资料</b></div><div class="notion-row"><a class="notion-bookmark notion-block-2e5c0e14283f806e8d6cd0584dffd21e" href="https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_layout" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">Executable and Linkable Format</div><div class="notion-bookmark-description">In computing, the Executable and Linkable Format is a common standard file format for executable files, object code, shared libraries, device drivers, and core dumps. First published in the specification for the application binary interface (ABI) of the Unix operating system version named System V Release 4 (SVR4), and later in the Tool Interface Standard, it was quickly accepted among different vendors of Unix systems. In 1999, it was chosen as the standard binary file format for Unix and Unix-like systems on x86 processors by the 86open project.</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fen.wikipedia.org%2Fstatic%2Fapple-touch%2Fwikipedia.png?table=block&amp;id=2e5c0e14-283f-806e-8d6c-d0584dffd21e&amp;t=2e5c0e14-283f-806e-8d6c-d0584dffd21e" alt="Executable and Linkable Format" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_layout</div></div></div><div class="notion-bookmark-image"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2F7%2F77%2FElf-layout--en.svg%2F1083px-Elf-layout--en.svg.png?table=block&amp;id=2e5c0e14-283f-806e-8d6c-d0584dffd21e&amp;t=2e5c0e14-283f-806e-8d6c-d0584dffd21e" alt="Executable and Linkable Format" loading="lazy" decoding="async"/></div></a></div><div class="notion-row"><a class="notion-bookmark notion-block-2e5c0e14283f80e78f6ae1b5c764e6ec" href="https://zhuanlan.zhihu.com/p/1927510429430703942" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">zhuanlan.zhihu.com</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-text">https://zhuanlan.zhihu.com/p/1927510429430703942</div></div></div></a></div><div class="notion-text notion-block-2e5c0e14283f80909ca1cc478912ad31">The RISC-V Instruction Set Manual Volume I | © RISC-V</div><div class="notion-text notion-block-2e5c0e14283f80eebeeed235c34a89c7">2.5.1. Unconditional Jumps</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2e5c0e14283f80bd82b7cd4b4ec2f25b"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A41363ff7-3569-4e7b-95e0-dde247b0d9f8%3Aimage.png?table=block&amp;id=2e5c0e14-283f-80bd-82b7-cd4b4ec2f25b&amp;t=2e5c0e14-283f-80bd-82b7-cd4b4ec2f25b" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2e5c0e14283f80e58c4afa6ec1a2941e">另外，根据手册和查阅反汇编代码，函数调用指令<b><span class="notion-inline-underscore">一般情况</span></b>目标寄存器为 ra 寄存器，ret 指令在 RV32I 和 RV64I 中扩展为 <code class="notion-inline-code">jalr x0, 0(x1)</code></div></blockquote><hr class="notion-hr notion-block-2e6c0e14283f80829f38e0beaa2f31ee"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2e7c0e14283f805ba7c5df256e5a46d5" data-id="2e7c0e14283f805ba7c5df256e5a46d5"><span><div id="2e7c0e14283f805ba7c5df256e5a46d5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2e7c0e14283f805ba7c5df256e5a46d5" title="Difftest 实现"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Difftest 实现</span></span></h3><div class="notion-text notion-block-2e7c0e14283f80449343dd18833373f1">RISCV32 寄存器成员顺序定义：先是 32 位通用寄存器，然后是 PC 寄存器。</div><div class="notion-text notion-block-2e7c0e14283f80e1987fe739d9074686">对比每个寄存器值和下一条指令地址是否一致即可，不过多赘述。</div><div class="notion-text notion-block-2e7c0e14283f80b18160fefba5762b50">验证效果：尝试修改 inst.c 中几条指令的实现，比如先后修改 add、sub、sw、jal、bne，执行</div><div class="notion-text notion-block-2e7c0e14283f808e873edc4c2d1d9665">add：</div><div class="notion-text notion-block-2e7c0e14283f80098deff45f30f76629">sub：</div><div class="notion-text notion-block-2e7c0e14283f805b9d0de6702663dfd0">sw：</div><div class="notion-text notion-block-2e7c0e14283f80ebac48de3bf7a00101">jal：</div><div class="notion-text notion-block-2e7c0e14283f80bb8cdae4988a5d6e2d">bne：</div><hr class="notion-hr notion-block-2e7c0e14283f806384cefdd81accb217"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2e7c0e14283f8098b8acc4a95f96e78e" data-id="2e7c0e14283f8098b8acc4a95f96e78e"><span><div id="2e7c0e14283f8098b8acc4a95f96e78e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2e7c0e14283f8098b8acc4a95f96e78e" title="构建 NPC-Infra"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">构建 NPC-Infra</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-2f0c0e14283f809f8376dbe5e44948ac" data-id="2f0c0e14283f809f8376dbe5e44948ac"><span><div id="2f0c0e14283f809f8376dbe5e44948ac" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2f0c0e14283f809f8376dbe5e44948ac" title="总体思路"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">总体思路</span></span></h4><div class="notion-text notion-block-2f3c0e14283f80ccbe8ff65cf481751a">总代码量（整个 C.4 任务）：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2f3c0e14283f80378de5f278447cd082"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A165834e3-c028-4915-a676-e6ca83ff4e4b%3Aimage.png?table=block&amp;id=2f3c0e14-283f-8037-8de5-f278447cd082&amp;t=2f3c0e14-283f-8037-8de5-f278447cd082" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-306c0e14283f80d1bd72f11f869ce9d0">本文将介绍一种设计思路：<span class="notion-blue_background"><b>将 NPC 编译为动态链接库 (.so)，然后嵌入到 NEMU 中运行。让 NPC 负责执行指令，而让 NEMU 负责提供内存和外设服务。</b></span></div><div class="notion-text notion-block-306c0e14283f80e4bc48c2f980f33c56">核心思想是将 <b>NPC</b> 视为 NEMU 中的一个“执行引擎”。NEMU 原本使用的是解释器引擎，现在我们扩展它，使其能加载 NPC 硬件模型。同时为了最大程度实现解耦合，NEMU 文件夹下仅新增<code class="notion-inline-code">src/engine/npc</code>文件夹，相关修改只涉及 Kconfig、Makefile 中将包含 NPC 目录下的构建脚本，其他修改均位于 NPC 目录下。NEMU 中可以通过 menuconfig 便捷切换后端执行引擎，以达到共用同一套前端设施（SDB, Trace, Device）的目的。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-2ebc0e14283f80d39351f8960c24a813" data-id="2ebc0e14283f80d39351f8960c24a813"><span><div id="2ebc0e14283f80d39351f8960c24a813" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2ebc0e14283f80d39351f8960c24a813" title="将 NPC 编译成动态链接库"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">将 NPC 编译成动态链接库</span></span></h4><div class="notion-text notion-block-2f0c0e14283f80b79f9dd032b8919998">最开始徒手链接源文件瞎折腾了几个小时，后来才发现 verilator 原生支持编译成一个动态链接库（—lib-create）<span class="notion-purple_background"><span class="notion-inline-underscore">（没好好 RTFM 的后果……）</span></span>，几行命令就搞定：</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-2f1c0e14283f80abb623ec119149f462" data-id="2f1c0e14283f80abb623ec119149f462"><span><div id="2f1c0e14283f80abb623ec119149f462" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2f1c0e14283f80abb623ec119149f462" title="Difftest 支持"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Difftest 支持</span></span></h4><div class="notion-text notion-block-2f1c0e14283f8089abdec524ce746e02">我们可以在 Kconfig 选项中增加以 NEMU 作为 REF 的选项，阅读源码发现此处有 <code class="notion-inline-code">CONFIG_DIFFTEST_REF_NEMU</code> 的宏定义包裹，<s>我们只需要在别的地方定义这个宏然后重新定义加载 .so 的加载路径即可</s>。（更好的方式直接在 Kconfig 定义路径即可）</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-2f2c0e14283f8002b033dc81dc2c2a35" data-id="2f2c0e14283f8002b033dc81dc2c2a35"><span><div id="2f2c0e14283f8002b033dc81dc2c2a35" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2f2c0e14283f8002b033dc81dc2c2a35" title="组合逻辑调用 DPI-C 接口的问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">组合逻辑调用 DPI-C 接口的问题</span></span></h4><div class="notion-text notion-block-2f2c0e14283f803b9f3cd9975484a5d4">运行示例程序时始终会在 Load 类指令发生内存访问越界错误：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2f2c0e14283f80dea654cbb7da1b951e"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A9149ac4f-5c03-425b-aa76-3cbe4b676c2b%3Aimage.png?table=block&amp;id=2f2c0e14-283f-80de-a654-cbb7da1b951e&amp;t=2f2c0e14-283f-80de-a654-cbb7da1b951e" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2f2c0e14283f80d09b96e0069a72a941">使用 gdb 调试发现地址异常出现总是在上升沿时钟调用 <code class="notion-inline-code">eval()</code> 之后出现（期间会出现多次调用的情况）。猜测有可能是组合逻辑毛刺问题。</div><div class="notion-row notion-block-2f2c0e14283f8057b058c6cf96aaa43d"><div class="notion-column notion-block-2f2c0e14283f80808b8fc42bc6aa854b" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2f2c0e14283f8092bad8cc27db846aca"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Aad2ee103-71a6-4497-9607-3e3d4c0a825a%3Aimage.png?table=block&amp;id=2f2c0e14-283f-8092-bad8-cc27db846aca&amp;t=2f2c0e14-283f-8092-bad8-cc27db846aca" alt="bit.c" loading="lazy" decoding="async"/><figcaption class="notion-asset-caption">bit.c</figcaption></div></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-2f2c0e14283f8096914ccfa4deac6bd2" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2f2c0e14283f804fb604ffae8554db44"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ac6c62fa0-23cd-4af5-94a3-4c690a18e26c%3Aimage.png?table=block&amp;id=2f2c0e14-283f-804f-b604-ffae8554db44&amp;t=2f2c0e14-283f-804f-b604-ffae8554db44" alt="recursion.c" loading="lazy" decoding="async"/><figcaption class="notion-asset-caption">recursion.c</figcaption></div></figure></div><div class="notion-spacer"></div></div><div class="notion-text notion-block-2f2c0e14283f80899a13e4b5973723b4"><span class="notion-blue">另外由于代码是在 itrace 日志写入成功后加入环形缓冲区，此处出发错误的位置还在 </span><span class="notion-blue"><code class="notion-inline-code">isa_exec_once</code></span><span class="notion-blue"> 函数内部，所以环形缓冲区指向的最后一条指令应该是问题现场指令的前一条。</span></div><div class="notion-text notion-block-2f2c0e14283f80239773ea0f933a7bad">最后拉起时钟后结算一次信号是为了实时更新指令结束后的寄存器状态，然而这也会导致 DPI-C 函数再次调用：</div><div class="notion-text notion-block-2f2c0e14283f80169476f479a604cad5">当前指令会将 a0 所指向的地址的值写入 a0，也就是说，<code class="notion-inline-code">eval()</code> 执行之后 a0 这个寄存器的值会发生变化。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2f2c0e14283f805cbd64e7f9935d24c8"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A9a5e9d70-7ed7-417a-abd3-03a1f9ed8720%3Aimage.png?table=block&amp;id=2f2c0e14-283f-805c-bd64-e7f9935d24c8&amp;t=2f2c0e14-283f-805c-bd64-e7f9935d24c8" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2f2c0e14283f80acb60ffed7b2ff71d2">结果很显然，上升沿更新了寄存器状态，但是指令状态没有更新，这时继续调用 DPI-C 函数，从而出现内存异常访问（此处是将 NEMU 的 <code class="notion-inline-code">vaddr_read()</code> 复用，这样就省去了后续再添加 mtrace 的任务）。<span class="notion-purple_background">解决这个问题需要想办法在上升沿之后停止对其继续调用。</span></div><div class="notion-text notion-block-2f2c0e14283f806b8faeed53b27350cc">那么第一次 <code class="notion-inline-code">eval()</code> 时候也多次调用了该函数，它有无可能意外触发内存访问越界呢？多次试验貌似未发生这种情况，或许和 verilator 调用 C++ 的机制有关，具体原因暂时不得而知。</div><div class="notion-callout notion-red_background_co notion-block-2f2c0e14283f80e2965dc1a22472fe1f"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="❓">❓</span></div><div class="notion-callout-text"><div class="notion-text notion-block-2f2c0e14283f8002a733d6444bf0260a">对于其他指令，add sub 等在第二个<code class="notion-inline-code">eval()</code> 的执行下确实会进行组合路径传播，但是在下一次进入该函数又立即被新指令冲刷掉，而 <code class="notion-inline-code">npc_pmem_write()</code> 本身被时序逻辑调用，显然不会受到影响。</div></div></div><div class="notion-callout notion-orange_background_co notion-block-313c0e14283f8050a593fa22205a35a1"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="⚠️">⚠️</span></div><div class="notion-callout-text"><div class="notion-text notion-block-313c0e14283f80c3b57eef3130bbf60f"><b><span class="notion-inline-underscore">后续补充：</span></b></div><div class="notion-text notion-block-313c0e14283f80ce888ac2a4007f2e2a">在对 NPC 做键盘设备支持时出现了新问题，<code class="notion-inline-code">vaddr_read</code>函数会产生副作用（键盘数据队列移动），这时 DPI-C 函数必须只能调用一次，否则键盘永远读取不到有效值。为了暂时向单周期架构的 NPC 妥协，将 DPI-C 读取函数放在下降沿时钟触发。</div></div></div><blockquote class="notion-quote notion-gray_background notion-block-2ebc0e14283f804ebe90c72f6ff25c9a"><div>参考资料</div><div class="notion-row"><a class="notion-bookmark notion-block-2ebc0e14283f80a096ecd5cc89d8c317" href="https://juejin.cn/post/7024079752801550350" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">juejin.cn</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-text">https://juejin.cn/post/7024079752801550350</div></div></div></a></div><div class="notion-row"><a class="notion-bookmark notion-block-2f0c0e14283f80568681f7c09d0d9069" href="https://veripool.org/guide/latest/exe_verilator.html" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">verilator Arguments — Verilator Devel 5.045 documentation</div><div class="notion-bookmark-description">The following arguments may be passed to the “verilator” executable.</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fveripool.org%2Fguide%2Flatest%2F_static%2Fverilator_32x32_min.png?table=block&amp;id=2f0c0e14-283f-8056-8681-f7c09d0d9069&amp;t=2f0c0e14-283f-8056-8681-f7c09d0d9069" alt="verilator Arguments — Verilator Devel 5.045 documentation" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://veripool.org/guide/latest/exe_verilator.html</div></div></div></a></div></blockquote><hr class="notion-hr notion-block-2ebc0e14283f805f9655dc9e74a025e7"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2fdc0e14283f805ea9eff0df709a2e28" data-id="2fdc0e14283f805ea9eff0df709a2e28"><span><div id="2fdc0e14283f805ea9eff0df709a2e28" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2fdc0e14283f805ea9eff0df709a2e28" title="设备相关"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">设备相关</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-2fdc0e14283f8008a00cddd1925669ee" data-id="2fdc0e14283f8008a00cddd1925669ee"><span><div id="2fdc0e14283f8008a00cddd1925669ee" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2fdc0e14283f8008a00cddd1925669ee" title="设备的输入与输出"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">设备的输入与输出</span></span></h4><div class="notion-text notion-block-2fdc0e14283f808da024f9956ea2711e">首先主循环中 <code class="notion-inline-code">execute()</code> 函数负责 VGA 显示和键盘状态的更新：</div><div class="notion-text notion-block-2fdc0e14283f802cb31dfe54aad3dde9">对于其他设备，NEMU 在初始化时会为所有设备注册一个地址空间，设备相关信息（名称、地址范围、回调函数）会占有一个<code class="notion-inline-code">IOMap</code>结构体：</div><div class="notion-text notion-block-2fdc0e14283f80e0b289f5f7f92fe690">后续凡是对设备地址空间的读写，都会对执行相应设备的回调函数以 “唤醒” 设备运行。
以定时器为例子，若指令出现 Load 类，则会调用<code class="notion-inline-code">paddr_read</code>函数，在判断地址属于设备空间后，依次进入<code class="notion-inline-code">mmio_read</code> → <code class="notion-inline-code">map_read</code> → <code class="notion-inline-code">invoke_callback</code>。<code class="notion-inline-code">rtc_io_handler</code>负责向设备私有寄存器写入当前时间值，实际写入和读取的地址则是通过<code class="notion-inline-code">IOMap</code>中的<code class="notion-inline-code">space</code>成员变量进行管理</div><div class="notion-text notion-block-2fdc0e14283f80d8957ffd762994a430">以上是从模拟器层面理解，接下来从 AM 层面：
<code class="notion-inline-code">io_read</code> 完成了几件事：定义设备结构体 → 对应地址读取 → 更新该结构体的值。IO 地址访问产生了 “副作用” （NEMU 中体现为设备回调函数的执行）。</div><div class="notion-callout notion-yellow_background_co notion-block-2fdc0e14283f808eba26e11c74164c00"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text"><div class="notion-text notion-block-2fdc0e14283f8053a26edb69da8838fe">这里是 gcc 的 Statement expression 扩展特性，允许在表达式中使用循环、switch、和局部变量。上述代码最后的结果就相当于<code class="notion-inline-code">__io_param.us</code></div><div class="notion-row"><a class="notion-bookmark notion-block-2fdc0e14283f807b91c5d09d102021fe" href="https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">Statement Exprs (Using the GNU Compiler Collection (GCC))</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fgcc.gnu.org%2Ffavicon.ico?table=block&amp;id=2fdc0e14-283f-807b-91c5-d09d102021fe&amp;t=2fdc0e14-283f-807b-91c5-d09d102021fe" alt="Statement Exprs (Using the GNU Compiler Collection (GCC))" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html</div></div></div></a></div></div></div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-2fdc0e14283f805ea2ddea34646123f0" data-id="2fdc0e14283f805ea2ddea34646123f0"><span><div id="2fdc0e14283f805ea2ddea34646123f0" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2fdc0e14283f805ea2ddea34646123f0" title="读取定时器寄存器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">读取定时器寄存器</span></span></h4><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2fec0e14283f8028bf41c329637fa232"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ac24bed99-83a7-4ade-aee4-bc4d7ac2b16f%3Aimage.png?table=block&amp;id=2fec0e14-283f-8028-bf41-c329637fa232&amp;t=2fec0e14-283f-8028-bf41-c329637fa232" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2fec0e14283f80acaa55e28867511405">官方手册明确指定了读取定时器寄存器的顺序，先读高位 → 读低位 → 循环校验，防止读取过程中发生了进位。</div><div class="notion-text notion-block-2fec0e14283f80b8b0bfdb0851b88215">如果先读低位（有可能是 <code class="notion-inline-code">0x00000000_FFFFFFFF</code>），读高位就容易读到<code class="notion-inline-code">0x00000001</code> 。</div><div class="notion-callout notion-yellow_background_co notion-block-388c0e14283f801cbe7be8b826c0faa5"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="⚠️">⚠️</span></div><div class="notion-callout-text"><div class="notion-text notion-block-388c0e14283f807dbac0e7b4ecbf0d03">勘误：这里的伪指令是 RISC-V 中实际用于读取一个周期计数 CSR 寄存器的，常用于统计 IPC，和这里的 timer.c 的读取语境上完全不同，只不过本质功能都是计数。</div></div></div><div class="notion-text notion-block-2fec0e14283f803d89f5f553d6cda767">在 <code class="notion-inline-code">device/timer.c</code>中，只有读取高位时才触发一次更新，这意味着如果先读取低位，再读取高位，结果是前一时刻的低位 + 当前时刻的高位。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2fec0e14283f8053a72edcad45c0dcc5"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Aeb3ce398-064a-47fe-9d49-26ce2269a8ad%3Aimage.png?table=block&amp;id=2fec0e14-283f-8053-a72e-dcad45c0dcc5&amp;t=2fec0e14-283f-8053-a72e-dcad45c0dcc5" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2fec0e14283f80b48d40de3c2248e427"><span class="notion-inline-underscore">猜测讲义所说的坑有可能就是上述内容。</span></div><div class="notion-text notion-block-2ffc0e14283f80ada020ef06bdd670ae">而讲义中的设备是模拟出来的，只有读取寄存器高位才会触发一次更新，因此不必读二遍校验。</div><div class="notion-text notion-block-2fec0e14283f80b592e3ead182b0fa9a">microbench 跑分结果：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2fec0e14283f80d9acf3cfad7f075425"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Aa21cca4b-6c20-41b7-a071-b915302c5948%3Aimage.png?table=block&amp;id=2fec0e14-283f-80d9-acf3-cfad7f075425&amp;t=2fec0e14-283f-80d9-acf3-cfad7f075425" alt="notion image" loading="lazy" decoding="async"/></div></figure><hr class="notion-hr notion-block-30dc0e14283f80459acadbea57ffe893"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-309c0e14283f8056bbccf1ee58e6063f" data-id="309c0e14283f8056bbccf1ee58e6063f"><span><div id="309c0e14283f8056bbccf1ee58e6063f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#309c0e14283f8056bbccf1ee58e6063f" title="异常处理机制"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">异常处理机制</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-30dc0e14283f8068904dc7f0c2d5aed7" data-id="30dc0e14283f8068904dc7f0c2d5aed7"><span><div id="30dc0e14283f8068904dc7f0c2d5aed7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#30dc0e14283f8068904dc7f0c2d5aed7" title="实现流程"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">实现流程</span></span></h4><div class="notion-text notion-block-30dc0e14283f80148006dd312d82a67b">NEMU 运行至<code class="notion-inline-code">ecall</code> 指令时，进入<code class="notion-inline-code">isa_raise_intr</code> 函数调用，传入的参数依据手册内容，同时需要对 <b>mstatus</b> 寄存器状态更新以通过 difftest。而后该函数将返回<code class="notion-inline-code">cte_init</code>中传给 <b>mtvec</b> 寄存器的异常处理代码的地址，使得 NEMU 跳转到异常处理区域。</div><div class="notion-callout notion-gray_background_co notion-block-30dc0e14283f80e8b071c04d6d846dfa"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="🤖">🤖</span></div><div class="notion-callout-text"><div class="notion-text notion-block-30dc0e14283f802dae3dcf4d8525fbfd">以下内容由 NotebookLM 根据特权级指令手册内容总结：</div><div class="notion-text notion-block-30dc0e14283f802fab6dccdd812d6486">触发 <code class="notion-inline-code">ecall</code>（环境调用）指令时，<b>mcause 的值不从任何通用寄存器（GPR）中取得</b>。</div><div class="notion-text notion-block-30dc0e14283f80699aa3d26b82ca18f5">根据来源文档的详细解释：</div><ul class="notion-list notion-list-disc notion-block-30dc0e14283f80e49373f56466bbd99e"><li><b>硬件自动设置</b>：当发生陷阱（Trap）进入机器模式时，<code class="notion-inline-code">mcause</code> 寄存器是由<b>处理器硬件自动写入</b>一个代码，该代码用于标识导致陷阱的具体事件。</li></ul><ul class="notion-list notion-list-disc notion-block-30dc0e14283f804a9305f96af81cf6f5"><li><b>异常代码的来源</b>：硬件根据 <code class="notion-inline-code">ecall</code> 指令执行时处理器所处的<b>特权模式</b>，在 <code class="notion-inline-code">mcause</code> 的 <code class="notion-inline-code">Exception Code</code> 字段中填入由标准预设的固定数值：</li></ul><div class="notion-text notion-block-30dc0e14283f804f9d43f2b8e5b529b9">◦ 如果 <code class="notion-inline-code">ecall</code> 来自 <b>U 模式</b>（用户模式），<code class="notion-inline-code">mcause</code> 被设置为 <b>8</b>。</div><div class="notion-text notion-block-30dc0e14283f8083b44ccdc7760f071e">◦ 如果 <code class="notion-inline-code">ecall</code> 来自 <b>S 模式</b>（监督员模式），<code class="notion-inline-code">mcause</code> 被设置为 <b>9</b>。</div><div class="notion-text notion-block-30dc0e14283f800b9357e57fa29d5620">◦ 如果 <code class="notion-inline-code">ecall</code> 来自 <b>M 模式</b>（机器模式），<code class="notion-inline-code">mcause</code> 被设置为 <b>11</b>。</div><ul class="notion-list notion-list-disc notion-block-30dc0e14283f80c19b51d444bee3ebd5"><li><b>通用寄存器的角色</b>：虽然硬件不从通用寄存器读取 <code class="notion-inline-code">mcause</code> 的值，但在软件层面，执行环境接口（EEI）通常会规定使用<b>通用寄存器</b>（如 ABI 约定的 <code class="notion-inline-code">a0-a7</code>）来传递具体的服务请求参数或系统调用号。这属于软件处理程序的逻辑，而非硬件填充 <code class="notion-inline-code">mcause</code> 的机制。</li></ul><div class="notion-text notion-block-30dc0e14283f8024ac96e88d949ada99"><b>总结</b>：<code class="notion-inline-code">mcause</code> 的值反映的是“发生了环境调用异常”这一事件类型，其数值由硬件根据当前的<b>特权级状态</b>直接生成。</div></div></div><div class="notion-text notion-block-30dc0e14283f80f29c41cbd6fb9c64be">异常处理代码对应 trap.S 中的 __am_asm_trap，它主要执行下面几件事：</div><ul class="notion-list notion-list-disc notion-block-30dc0e14283f80d0bfafd77ad8171893"><li>入栈顺序：gpr, mcause, mstatus, mepc。</li></ul><ul class="notion-list notion-list-disc notion-block-30dc0e14283f803cadcef540156d114e"><li>其中传入给<code class="notion-inline-code">__am_irq_handle</code> 的参数是通过将栈指针送入 a0 寄存器实现的，栈指针指向整个上下文结构体的首地址。</li></ul><ul class="notion-list notion-list-disc notion-block-30dc0e14283f8016b486fe9de151fad2"><li>在回调函数内部，根据 mcause 来确定事件类型（11 属于机器模式下的系统调用），进而根据 a7 寄存器来确定所属事件类型（NEMU 中根据 a7 == -1 确定属于 <code class="notion-inline-code">EVENT_YIELD</code>）。接着事件结构体和上下文结构体被传入用户（AM层）设置的句柄中。</li></ul><ul class="notion-list notion-list-disc notion-block-30dc0e14283f8084a496c741aa21f695"><li>另外需要注意，mepc 位置如果是 <code class="notion-inline-code">ecall</code> 需要<b>由软件进行手动更新</b> mepc 的操作。</li></ul><ul class="notion-list notion-list-disc notion-block-30dc0e14283f80d0a668c7dbced253c5"><li><code class="notion-inline-code">mret</code>之后，如果是 <code class="notion-inline-code">ecall</code> 引起的异常则运行后续指令。</li></ul><ul class="notion-list notion-list-disc notion-block-30ec0e14283f80eb9484f50689e622f3"><li><span class="notion-red"><b>注意到</b></span>，恢复上下文时并没有加载 sp 寄存器，原因是 sp 寄存器已经通过<code class="notion-inline-code">mv sp, a0</code>完成了上下文切换，它的值正是当前进程的私有栈指针。</li></ul><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-30dc0e14283f80aaac6fc7e89125e17c" data-id="30dc0e14283f80aaac6fc7e89125e17c"><span><div id="30dc0e14283f80aaac6fc7e89125e17c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#30dc0e14283f80aaac6fc7e89125e17c" title="让 Difftest 支持异常相应机制"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">让 Difftest 支持异常相应机制</span></span></h4><div class="notion-text notion-block-30dc0e14283f805c8442c9c0c5a16e2c">首先需要在 difftest.cc 中对寄存器复制添加相应的支持，在<code class="notion-inline-code">cte_init</code>中实现对 mstatus 的初始化，同时需要对 NEMU 中 dut.c 的寄存器比较函数做出额外的针对 CSR 寄存器的检查。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-30dc0e14283f80e3ab2ec8b524150d87" data-id="30dc0e14283f80e3ab2ec8b524150d87"><span><div id="30dc0e14283f80e3ab2ec8b524150d87" class="notion-header-anchor"></div><a class="notion-hash-link" href="#30dc0e14283f80e3ab2ec8b524150d87" title="内核线程的切换（yield-os）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">内核线程的切换（yield-os）</span></span></h4><ul class="notion-list notion-list-disc notion-block-30ec0e14283f8072ba11cc6309fea2f4"><li>创建上下文给进程管理单元，需要指定 mepc, mstatus, a0 (ABI 规范) 寄存器。</li></ul><ul class="notion-list notion-list-disc notion-block-30ec0e14283f80e3bc96db327d3f36ba"><li><b>内核进程执行前会从栈中弹出上下文，执行结束（触发自陷）后会将上下文压栈。</b></li></ul><ul class="notion-list notion-list-disc notion-block-30ec0e14283f80e0a4bce76740d1bbe7"><li>对于一个还未执行的进程，gpr 中的值不重要，不需要一开始初始化，进程不会尝试调用一个未经初始化的通用寄存器。内核进程自陷后会保存当前上下文。</li></ul><ul class="notion-list notion-list-disc notion-block-30fc0e14283f80499a98e7cc26d9c734"><li>切换的上下文是否和原来相同（也即是否要进行切换）由<code class="notion-inline-code">user_handle</code>决定，他接受一个<code class="notion-inline-code">Event</code>和一个<code class="notion-inline-code">Context*</code>参数并返回一个<code class="notion-inline-code">Context*</code> 。在 yield-os 中，<code class="notion-inline-code">user_handle</code>始终将进程切换到另一个。</li></ul><ul class="notion-list notion-list-disc notion-block-30fc0e14283f80d6ac0efed3b0197210"><li><span class="notion-purple">关于为什么</span><span class="notion-purple"><code class="notion-inline-code">kcontext()</code></span><span class="notion-purple">要求不能从入口函数返回</span>：普通函数的返回依靠 <b>ra</b> 寄存器，而入口函数依靠 <b>mret</b> 跳转，<b>mret</b> 会根据创建上下文时给出的 <b>mepc</b> 找到入口的地址。如果此时调用 <b>ret</b> 会返回到一个非法的 <b>ra</b> 寄存器指向的地址。</li></ul><hr class="notion-hr notion-block-310c0e14283f80b68f4efdaf62a6144b"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-310c0e14283f80be9726ebbcbb3999f5" data-id="310c0e14283f80be9726ebbcbb3999f5"><span><div id="310c0e14283f80be9726ebbcbb3999f5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#310c0e14283f80be9726ebbcbb3999f5" title="支线"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">支线</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-310c0e14283f80c99fadd9ae4356b40f" data-id="310c0e14283f80c99fadd9ae4356b40f"><span><div id="310c0e14283f80c99fadd9ae4356b40f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#310c0e14283f80c99fadd9ae4356b40f" title="给 NEMU/NPC 添加串口输入支持"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">给 NEMU/NPC 添加串口输入支持</span></span></h4><div class="notion-text notion-block-314c0e14283f8098a090e8cefdd3787b">核心代码框架基本参考 native 的串口读取、键盘设备的队列，以下分为几个部分介绍：</div><ol start="1" class="notion-list notion-list-numbered notion-block-314c0e14283f80ceaeb6e6b1fb6de7f7" style="list-style-type:decimal"><li>NEMU 端</li></ol><div class="notion-text notion-block-314c0e14283f803dac58e9210dabaaf0">串口的输入/输出缓冲区寄存器已经实现，接下来只需要实现 <b>LSR</b> 寄存器的第一位，即该位指示队列中是否有数据。需要在回调函数添加对应的逻辑：</div><ul class="notion-list notion-list-disc notion-block-314c0e14283f807bb6dddf427eb12690"><li>根据头尾指针是否相等设置 LSR 寄存器值。</li></ul><ul class="notion-list notion-list-disc notion-block-314c0e14283f8030a3bef6cf480a2bef"><li>实现类似 keyboard.c 中读取队列的逻辑。</li></ul><ul class="notion-list notion-list-disc notion-block-315c0e14283f80558c95d374947dcdfc"><li>在初始化串口设备的位置修改终端标准输入流的文件描述符，需要设置为<code class="notion-inline-code">O_NONBLOCK</code>（非阻塞式读取，这部分参考的 native 实现），然后在<code class="notion-inline-code">device_update</code>函数中调用<code class="notion-inline-code">fgetc()</code>函数即可实现无阻塞地读取终端输入字符。</li></ul><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-314c0e14283f80f4bdf2c44d7a39ff0c"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A2a6217d1-af14-4570-a7da-abb3a17f87e8%3Aimage.png?table=block&amp;id=314c0e14-283f-80f4-bdf2-c44d7a39ff0c&amp;t=314c0e14-283f-80f4-bdf2-c44d7a39ff0c" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="2" class="notion-list notion-list-numbered notion-block-314c0e14283f80b08cfded068f389bb2" style="list-style-type:decimal"><li>AM 端</li></ol><div class="notion-text notion-block-314c0e14283f80618f76ddda0b2e80eb">根据 amdev.h 中定义的三个串口寄存器，大致需要实现以下逻辑：</div><ul class="notion-list notion-list-disc notion-block-315c0e14283f80178d81deb90e456479"><li>新增<code class="notion-inline-code">__am_uart_rx</code>和<code class="notion-inline-code">__am_uart_tx</code>，并在 ioe.c 中补充声明与查找表内容。</li></ul><ul class="notion-list notion-list-disc notion-block-315c0e14283f804dbf78f08286aa7792"><li>修改对应的 Makefile 脚本添加源文件。</li></ul><ul class="notion-list notion-list-disc notion-block-315c0e14283f80249212c007ba070b1c"><li>为了保持一致性，在 trm.c 中新增<code class="notion-inline-code">getch()</code>函数。</li></ul><ol start="3" class="notion-list notion-list-numbered notion-block-314c0e14283f806f88fdf9e8d3fd21a7" style="list-style-type:decimal"><li>RTT BSP 端</li></ol><div class="notion-text notion-block-314c0e14283f80178031ef48f2ba48aa"><code class="notion-inline-code">_uart_getc</code> 函数直接依赖 AM 层的 <code class="notion-inline-code">io_read</code> 即可，一行代码。</div><div class="notion-text notion-block-315c0e14283f8026b071d2eda4bc8a6d"><code class="notion-inline-code">io_read</code>会实例化一个串口接收寄存器结构体然后通过回调函数从 NEMU 中的串口缓冲区获取一个字符，并填充该结构体。</div><div class="notion-text notion-block-315c0e14283f80d2a101c465e1203f7f">NPC 侧逻辑大致差不多，不过多赘述。</div><div class="notion-callout notion-gray_background_co notion-block-315c0e14283f8074a72adfaae0954c16"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="🛠️">🛠️</span></div><div class="notion-callout-text"><div class="notion-text notion-block-315c0e14283f807fb0b4c69209892872">NEMU/NPC 下运行 RT-Thread 输入命令回车后终端会回显一次该命令本身，该现象对于功能暂无影响，后期再进行进一步处理。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-315c0e14283f8026abaee54f43e8399e"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A9826c107-b683-42c5-b223-a7efbdfe91f2%3Aimage.png?table=block&amp;id=315c0e14-283f-8026-abae-e54f43e8399e&amp;t=315c0e14-283f-8026-abae-e54f43e8399e" alt="notion image" loading="lazy" decoding="async"/></div></figure></div></div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-310c0e14283f80bb9f5bd7f26cfb41cc" data-id="310c0e14283f80bb9f5bd7f26cfb41cc"><span><div id="310c0e14283f80bb9f5bd7f26cfb41cc" class="notion-header-anchor"></div><a class="notion-hash-link" href="#310c0e14283f80bb9f5bd7f26cfb41cc" title="简单优化 NEMU 性能（使用 perf 工具）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">简单优化 NEMU 性能（使用 perf 工具）</span></span></h4><div class="notion-text notion-block-30fc0e14283f809b8b23d1593cef3778"><b>perf</b> 是一个系统级性能分析工具，可用于评估代码性能，执行下列命令记录红白机模拟器运行时性能数据，退出后会在当前目录生成 perf.data 文件：</div><div class="notion-text notion-block-315c0e14283f800aa2b2eb408853b7ff"><span class="notion-inline-underscore">在此之前</span>需要运行以下命令临时开放内核权限：</div><div class="notion-text notion-block-315c0e14283f80758844d5a4dee3dc1c">然后会进入一个图形化交互界面，选择第二项 CPU 性能核（采样数最多）</div><div class="notion-text notion-block-315c0e14283f808ca742e0336a5ab50a">第二个界面可以看到性能热点列表，排名第一的占用性能 31.07%。</div><div class="notion-text notion-block-315c0e14283f80c7b920e83d2b530cae">第三个界面有一些常用选项：</div><ul class="notion-list notion-list-disc notion-block-315c0e14283f8081aa90d62e2eafce84"><li><b>Annotate </b>：如果你选中具体的函数按这个，<code class="notion-inline-code">perf</code> 会尝试反汇编该函数，展示哪一条具体的汇编指令最耗时。</li></ul><ul class="notion-list notion-list-disc notion-block-315c0e14283f80069f42d7e53f614ab6"><li><b>Zoom into ... thread</b>：只看当前线程的性能开销，屏蔽其他线程的干扰。</li></ul><ul class="notion-list notion-list-disc notion-block-315c0e14283f8048bed2d73cd094e1a9"><li><b>Zoom into [vdso] DSO</b>：放大看共享库。选择这个后，列表里就只剩下属于 <code class="notion-inline-code">[vdso]</code> 的事件了。</li></ul><div class="notion-row notion-block-315c0e14283f80f49da4c2967f5679f1"><div class="notion-column notion-block-315c0e14283f808cac1ecc463e639089" style="width:calc((100% - (2 * min(32px, 4vw))) * 0.3333333333333333)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-315c0e14283f805dac9ee65040ef2cd9"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ad170a2ca-7484-4df0-a7fe-730154bcd2fb%3Aimage.png?table=block&amp;id=315c0e14-283f-805d-ac9e-e65040ef2cd9&amp;t=315c0e14-283f-805d-ac9e-e65040ef2cd9" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-315c0e14283f80f0918fd69744af6cc7" style="width:calc((100% - (2 * min(32px, 4vw))) * 0.3333333333333333)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-315c0e14283f80be94c7d97043485a15"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Abb7b6133-24c2-4ff0-a5ac-fb970b384022%3Aimage.png?table=block&amp;id=315c0e14-283f-80be-94c7-d97043485a15&amp;t=315c0e14-283f-80be-94c7-d97043485a15" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-315c0e14283f80f0af60fc62c2fc4144" style="width:calc((100% - (2 * min(32px, 4vw))) * 0.3333333333333333)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-315c0e14283f802f985bdde14507319a"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ac27fcabe-576d-48bf-bd65-b13476ecfd0c%3Aimage.png?table=block&amp;id=315c0e14-283f-802f-985b-dde14507319a&amp;t=315c0e14-283f-802f-985b-dde14507319a" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div></div><div class="notion-text notion-block-315c0e14283f80c38998da0a57ed5ab6">反汇编可以发现耗时严重部分位于<code class="notion-inline-code">0000000000000869 &lt;__vdso_gettimeofday@@LINUX_2.6-0x597&gt;</code> ，这部分正是 NEMU 中的获取系统时间的底层函数，位于<code class="notion-inline-code">device_update</code>函数内部。</div><div class="notion-text notion-block-315c0e14283f802db5aed1b07dea2f3a">CPU 每执行一次指令都会调用一次 <code class="notion-inline-code">device_update</code> 。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-315c0e14283f80518845fd3ab3cf70a9"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A5907b7c5-7c55-4103-be3b-72192e2c06b2%3Aimage.png?table=block&amp;id=315c0e14-283f-8051-8845-fd3ab3cf70a9&amp;t=315c0e14-283f-8051-8845-fd3ab3cf70a9" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-315c0e14283f8003a43bfbeb24ffa5ad">于是我们可以降低<code class="notion-inline-code">device_update</code> 函数的调用频率，最简化的实现如下：</div><div class="notion-text notion-block-315c0e14283f80ff9790dbf43ec36865">实际效果有较大提升，优化前操作跟手性差，声音输出较为杂乱。优化后更加流畅，声音更加连贯：（实际测试发现至少每隔 3 条指令执行一次<code class="notion-inline-code">get_time()</code>可以使得该部分性能消耗排名降至第二）</div><div class="notion-row notion-block-315c0e14283f80d69cbbead15f9a6709"><div class="notion-column notion-block-315c0e14283f80a19906d798ea556e82" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-video notion-block-315c0e14283f80da84e2e49b501e71b9"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:708px;max-width:100%;flex-direction:column;height:320px"><video playsinline="" controls="" preload="metadata" src="https://file.notion.com/f/f/0de3bb29-0660-43ab-a47e-86b619f7871c/25cbf2aa-8bb2-4299-9e8b-cb739ba5205a/%E5%BD%95%E5%B1%8F_2026%E5%B9%B402%E6%9C%8828%E6%97%A5_23%E6%97%B607%E5%88%8600%E7%A7%92.webm?table=block&amp;id=315c0e14-283f-80da-84e2-e49b501e71b9&amp;spaceId=0de3bb29-0660-43ab-a47e-86b619f7871c&amp;expirationTimestamp=1791460800000&amp;signature=cOIY2Z-bE9u7h2dhq3Ld2ZK5IYA_z2Da2URzFbkTfRM" title="video"></video></div><figcaption class="notion-asset-caption">优化前</figcaption></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-315c0e14283f8059a63ed8840ff84da0" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-video notion-block-315c0e14283f807c95ecfd19b939285a"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:708px;max-width:100%;flex-direction:column;height:320px"><video playsinline="" controls="" preload="metadata" src="https://file.notion.com/f/f/0de3bb29-0660-43ab-a47e-86b619f7871c/10977070-a725-4869-b6fc-573b7c60041b/%E5%BD%95%E5%B1%8F_2026%E5%B9%B402%E6%9C%8828%E6%97%A5_23%E6%97%B631%E5%88%8621%E7%A7%92.webm?table=block&amp;id=315c0e14-283f-807c-95ec-fd19b939285a&amp;spaceId=0de3bb29-0660-43ab-a47e-86b619f7871c&amp;expirationTimestamp=1791460800000&amp;signature=zjP4hrwOa-OpodlZHsx7siP47YHFU5ZQi3nZwSLmQCw" title="video"></video></div><figcaption class="notion-asset-caption">间隔 4 条指令</figcaption></figure></div><div class="notion-spacer"></div></div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-321c0e14283f80b68121f0ffa41c26ee" data-id="321c0e14283f80b68121f0ffa41c26ee"><span><div id="321c0e14283f80b68121f0ffa41c26ee" class="notion-header-anchor"></div><a class="notion-hash-link" href="#321c0e14283f80b68121f0ffa41c26ee" title="NPC 性能优化（ Verilator ）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">NPC 性能优化（ Verilator ）</span></span></h4><ul class="notion-list notion-list-disc notion-block-321c0e14283f80c09e58dc9b4ccf281b"><li>Kconfig 中配置波形生成开关，并在软件驱动侧使用宏包裹波形相关逻辑。关闭波形生成提速大约 3 倍左右。使用 <code class="notion-inline-code">—trace-threads 1</code>开启独立线程波形追踪，速度提升大约 80%。</li></ul><ul class="notion-list notion-list-disc notion-block-324c0e14283f8055a921cd70e45782e5"><li>开启链接时优化 <code class="notion-inline-code">-flto</code>。</li></ul><ul class="notion-list notion-list-disc notion-block-338c0e14283f804a88edff0af1261c39"><li>关闭断言 <code class="notion-inline-code">—no-assert</code>。</li></ul><ul class="notion-list notion-list-disc notion-block-321c0e14283f804e8dd0e4aadb01b084"><li>开启 <code class="notion-inline-code">-O3</code> 优化，速度提升较明显。参考手册部分：</li></ul><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-324c0e14283f805ab5bdfe693fac3aad"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A51a63a29-192b-495b-9274-53d9008d8a59%3Aimage.png?table=block&amp;id=324c0e14-283f-805a-b5bd-fe693fac3aad&amp;t=324c0e14-283f-805a-b5bd-fe693fac3aad" alt="notion image" loading="lazy" decoding="async"/></div></figure><ul class="notion-list notion-list-disc notion-block-321c0e14283f80a79cb7fe32a083c442"><li>多线程：开启多线程速度反而下降。</li></ul><div class="notion-callout notion-gray_background_co notion-block-324c0e14283f80e9a8c0c58d315064f9"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="🤖">🤖</span></div><div class="notion-callout-text"><div class="notion-text notion-block-324c0e14283f806481aee310f78ff0c2">Gemini-3.1-pro总结内容：</div><div class="notion-text notion-block-324c0e14283f805dbb2dcd6a853c276d">1. 如何开启</div><ul class="notion-list notion-list-disc notion-block-324c0e14283f8053b42fe4a2707269e3"><li><b>编译参数</b>：在调用 Verilator 时添加 <code class="notion-inline-code">-threads &lt;N&gt;</code> （N 为期望的物理线程数，通常不超过 CPU 核心数）。</li></ul><ul class="notion-list notion-list-disc notion-block-324c0e14283f8056ad3bf1cbe20d54ae"><li><b>代码修改</b>：<b>零修改</b>。Verilator 会自动在底层的 C++ 代码中插入多线程调度和同步锁，无需修改 Testbench（<code class="notion-inline-code">main.cpp</code>）。</li></ul><div class="notion-text notion-block-324c0e14283f80a9b27ef763f2e8026f">2. 底层原理</div><ul class="notion-list notion-list-disc notion-block-324c0e14283f80eba017cbf832f25bdc"><li><b>依赖图分析</b>：Verilator 会在编译阶段分析 Verilog 代码的信号依赖。</li></ul><ul class="notion-list notion-list-disc notion-block-324c0e14283f80568729fb4913d069e4"><li><b>任务拆分 (mtasks)</b>：将<b>互不依赖</b>的逻辑块拆分给不同的线程并行计算。</li></ul><ul class="notion-list notion-list-disc notion-block-324c0e14283f80279e9cffaa200d6a2f"><li><b>同步屏障 (Barrier)</b>：在每个时钟边沿，跑得快的线程必须停下来等待跑得慢的线程，全员算完再进入下一周期。</li></ul><div class="notion-text notion-block-324c0e14283f807ea875cd5fa9194e83">3. ✅ 适合开多线程的场景（正收益）</div><ul class="notion-list notion-list-disc notion-block-324c0e14283f80cb9265fbf06b8dde27"><li><b>大规模设计</b>：复杂的 SoC（包含 CPU、GPU、DDR 控制器等独立大模块）。</li></ul><ul class="notion-list notion-list-disc notion-block-324c0e14283f801fb48dfc0dc4b9e8fa"><li><b>多核处理器</b>：各个核心之间大部分时间独立运行，交互极少。</li></ul><ul class="notion-list notion-list-disc notion-block-324c0e14283f806f8173fbedb646027a"><li><b>高度并行的算法</b>：如多路独立运行的图像/信号处理流水线。</li></ul><div class="notion-text notion-block-324c0e14283f80bd8fcfdbcb4e9d30f8">4. ❌ 绝对不要开多线程的场景（负收益/反向优化）</div><ul class="notion-list notion-list-disc notion-block-324c0e14283f80c09d86f46043df30e0"><li><b>单核小规模 CPU（如一生一芯 NPC）</b>：内部 ALU、译码、取指之间组合逻辑<b>强耦合</b>，无法拆分出独立的大块逻辑。</li></ul><ul class="notion-list notion-list-disc notion-block-324c0e14283f80038217c3a7b8616873"><li><b>长依赖链</b>：信号 A-&gt;B-&gt;C 串行依赖的纯组合逻辑。</li></ul><ul class="notion-list notion-list-disc notion-block-324c0e14283f807b9b98d7aef895efe2"><li><b>重度使用 DPI-C</b>：默认情况下，遇到 DPI-C 调用时多线程会退化为单线程排队执行，且跨语言同步开销极大。</li></ul><ul class="notion-list notion-list-disc notion-block-324c0e14283f803cb864e37b4b10217f"><li><em>后果</em>：线程花在“互相唤醒和等待锁”上的时间，远超并行计算省下的时间，导致<b>比单线程还慢</b>。</li></ul></div></div><hr class="notion-hr notion-block-315c0e14283f80d7971af5dbec93c645"/><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-315c0e14283f80c982cfc2e1d391d140" data-id="315c0e14283f80c982cfc2e1d391d140"><span><div id="315c0e14283f80c982cfc2e1d391d140" class="notion-header-anchor"></div><a class="notion-hash-link" href="#315c0e14283f80c982cfc2e1d391d140" title="B 阶段"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">B 阶段</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-320c0e14283f8003a66bcf67c1433418" data-id="320c0e14283f8003a66bcf67c1433418"><span><div id="320c0e14283f8003a66bcf67c1433418" class="notion-header-anchor"></div><a class="notion-hash-link" href="#320c0e14283f8003a66bcf67c1433418" title="评估单周期NPC的主频和程序性能"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">评估单周期NPC的主频和程序性能</span></span></h3><div class="notion-text notion-block-324c0e14283f8049a219ca94e53b9eaf">运行 microbench 的 train 规模测试，获得关键信息<code class="notion-inline-code">total guest instruction</code> 为 195 142 896，对于单周期 CPU 来说这就是运行 microbench 的时钟周期。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-324c0e14283f80908b14f676db885c32"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A2082eae1-3e52-43d0-9775-0c1abd9bd797%3Aimage.png?table=block&amp;id=324c0e14-283f-8090-8b14-f676db885c32&amp;t=324c0e14-283f-8090-8b14-f676db885c32" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-327c0e14283f80079a43d063a24ef0da">在进一步评估之前，先使用 <b>sv2v</b> 工具把 sv 全部转化到一个大的 verilog 文件中，因为 yosys 对 sv 一些高级语法特性支持不太好。</div><div class="notion-text notion-block-327c0e14283f80509bfae2272f2c7463"><b>yosys</b> 综合后估算最大主频 2.36 GHz，如果固定这个主频下运行 microbench 大约只需要 80 ms，实际肯定不会是这么多，讲义中也提到了综合器优化等等相关的一系列问题。</div><hr class="notion-hr notion-block-330c0e14283f802bb866fb2152fbb6d2"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-327c0e14283f80f1b983d423b4449f49" data-id="327c0e14283f80f1b983d423b4449f49"><span><div id="327c0e14283f80f1b983d423b4449f49" class="notion-header-anchor"></div><a class="notion-hash-link" href="#327c0e14283f80f1b983d423b4449f49" title="重构 NPC"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">重构 NPC</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-335c0e14283f80369a5dd348389272b5" data-id="335c0e14283f80369a5dd348389272b5"><span><div id="335c0e14283f80369a5dd348389272b5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#335c0e14283f80369a5dd348389272b5" title="整体结构"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">整体结构</span></span></h4><div class="notion-text notion-block-335c0e14283f803c8dd1e263f7c24891">按照经典五级单元重构基于分布式消息控制的处理器，并注入 SVA 语法，<code class="notion-inline-code">bind</code> 分离可综合代码与验证代码，实现功能解耦。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-327c0e14283f80758a92f35ac4bfd337" data-id="327c0e14283f80758a92f35ac4bfd337"><span><div id="327c0e14283f80758a92f35ac4bfd337" class="notion-header-anchor"></div><a class="notion-hash-link" href="#327c0e14283f80758a92f35ac4bfd337" title="问题记录"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">问题记录</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-330c0e14283f80abb5d1c62292be0af5" style="list-style-type:decimal"><li><code class="notion-inline-code">5.038</code> 之后的版本断言是默认开启的，要禁用需要显式的参数 <code class="notion-inline-code">—no-assert</code></li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-330c0e14283f809c8c6ad9cce4ad955b"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ae7ad1205-8e39-41c8-821b-e5c4c984cbd7%3Aimage.png?table=block&amp;id=330c0e14-283f-809c-8c6a-d9cce4ad955b&amp;t=330c0e14-283f-809c-8c6a-d9cce4ad955b" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="2" class="notion-list notion-list-numbered notion-block-330c0e14283f800eac28c403194f58db" style="list-style-type:decimal"><li>报错 <code class="notion-inline-code">%Error: unknown:0: Testbench C called &#x27;dpi_get_pc&#x27; but scope wasn&#x27;t set, perhaps due to dpi import call without &#x27;context&#x27;, or missing svSetScope. See IEEE 1800-2023 35.5.3.</code>
查询 Verilator 手册得知需要在模块实例化之后，函数调用之前设置作用域：</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-335c0e14283f8045b46deb11cd0c043d"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A16c2a101-b146-46f3-87a3-4face1cdce0c%3Aimage.png?table=block&amp;id=335c0e14-283f-8045-b46d-eb11cd0c043d&amp;t=335c0e14-283f-8045-b46d-eb11cd0c043d" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="3" class="notion-list notion-list-numbered notion-block-330c0e14283f8070b0d2c91325885942" style="list-style-type:decimal"><li>指令译码在 default 分支使用的立即断言会在复位时误触发，于是添加断言门控并定义成宏函数，顶层引出 <code class="notion-inline-code">inst_valid</code> 信号由 C++ 控制：</li></ol><hr class="notion-hr notion-block-335c0e14283f8004b347d57446f589fa"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-336c0e14283f802f94ffcd5a5bd2d433" data-id="336c0e14283f802f94ffcd5a5bd2d433"><span><div id="336c0e14283f802f94ffcd5a5bd2d433" class="notion-header-anchor"></div><a class="notion-hash-link" href="#336c0e14283f802f94ffcd5a5bd2d433" title="多周期 NPC"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">多周期 NPC</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-336c0e14283f80ca90b9f3b5d9efa65a" data-id="336c0e14283f80ca90b9f3b5d9efa65a"><span><div id="336c0e14283f80ca90b9f3b5d9efa65a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#336c0e14283f80ca90b9f3b5d9efa65a" title="核心要点"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">核心要点</span></span></h4><div class="notion-text notion-block-336c0e14283f80b98e35dc8a8fe100d3">从单周期迈向多周期处理器之后，C++ 侧的 <code class="notion-inline-code">npc_exec_once</code> 的任务由翻转一次时钟变成了正确执行并提交一条指令，原本单周期做出的一些约定与妥协不能再使用。为最大程度解耦合，需要对于 NEMU 和 NPC 的交互做出一些约定：</div><ol start="1" class="notion-list notion-list-numbered notion-block-336c0e14283f8025bd79cb5b246b07d0" style="list-style-type:decimal"><li>NEMU 的 <code class="notion-inline-code">isa_exec_once</code> 外部理应认为 NPC 的指令是<b>顺序提交</b>的，这一点也符合 gdb 视角看真实运行的 CPU。NEMU 需要拿到正确的 <code class="notion-inline-code">dnpc</code> 以及执行完一条指令后的所有新的架构状态。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-336c0e14283f80d9a849e51f50f924ab" style="list-style-type:decimal"><li><code class="notion-inline-code">npc_exec_once</code> 依然是提交一条指令，get_regs_from_npc 负责更新 NEMU 中架构状态（gpr 和 csr）。即退出该函数后，NEMU 需要看到 NPC 执行完成指令后所有的提交的新状态。</li></ol><div class="notion-text notion-block-337c0e14283f80d682a1eac4706dcd69">因此只要严格遵守运行完 <code class="notion-inline-code">isa_exec_once</code> 之后，等效<span class="notion-purple"><b>顺序执行一条指令并完成所有状态更新</b></span>，即可适配 NEMU 的所有基础设施。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-337c0e14283f80ffb431f4db205d847b" data-id="337c0e14283f80ffb431f4db205d847b"><span><div id="337c0e14283f80ffb431f4db205d847b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#337c0e14283f80ffb431f4db205d847b" title="问题记录"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">问题记录</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-337c0e14283f8097bf91e8db8476db26" style="list-style-type:decimal"><li>波形记录：</li></ol><div class="notion-text notion-block-337c0e14283f80828b65ef7a16a52a95">在 <code class="notion-inline-code">ebreak</code> 指令之前一刻记录的波形是 <code class="notion-inline-code">clk = 1</code>，时刻是 4879 ps，但是实际打开波形图发现 4878 ps 低电平之后就没有记录了。查询得知原因不是仿真没执行到，而是最后一个采样点正好落在 trace文件结尾，例如最后一条记录在 4879 ps。波形查看器展示的是状态在<b>一个时间区间内的持续情况</b>，也就是 <b>[当前采样时刻, 下一采样时刻)</b>。如果 4879 ps 已经是最后一个采样点，那么这个最终状态虽然被记录了，但它后面没有“下一时刻”来形成可见宽度，所以在 Surfer 里通常看起来像“没有画出来”。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-337c0e14283f80c099d6e43160e23a34"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A708fa368-acc6-46cc-94cb-2f7735dded10%3Aimage.png?table=block&amp;id=337c0e14-283f-80c0-99d6-e43160e23a34&amp;t=337c0e14-283f-80c0-99d6-e43160e23a34" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="2" class="notion-list notion-list-numbered notion-block-337c0e14283f80e29dbde1d7f6df48f3" style="list-style-type:decimal"><li>指令提交与 Difftest：</li></ol><div class="notion-text notion-block-337c0e14283f808a897affc94b2f61ed">之前由于是手动送指令，可在指令周期结束后手动拉高一次时钟更新架构状态。多周期则将更多主动权交还给 NPC。最开始是在第一条红线处作为指令提交点（状态采样点均在右侧），该处对于非 load 类指令属于已经拿到指令译码，准备写回但还没写回成功；对于 load 类指令属于已经拿到了 load 数据，准备写回。因此如果在该处提交，对于 snpc 和 dnpc 的值是有效的，但是架构状态仍然没有更新，仍是上一条指令执行完毕的状态，不利于 SDB 还有 Difftest。故考虑将提交处放在第三根红线处，此时架构状态已经更新，但是 pc 也随之更新，因此在 clk=0 还有 clk =1 之间提交指令（第二根红线处）。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-337c0e14283f80958508cb2b987a4153"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A168cb5bf-9843-4166-b09e-7ef2ff53329e%3Aimage.png?table=block&amp;id=337c0e14-283f-8095-8508-cb2b987a4153&amp;t=337c0e14-283f-8095-8508-cb2b987a4153" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-337c0e14283f8059b5ebdfae674eef91"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A4f6d55b8-4718-4370-b457-80050be0e15a%3Aimage.png?table=block&amp;id=337c0e14-283f-8059-b5eb-dfae674eef91&amp;t=337c0e14-283f-8059-b5eb-dfae674eef91" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="3" class="notion-list notion-list-numbered notion-block-337c0e14283f801ba324f9e9879b1530" style="list-style-type:decimal"><li>内存读写副作用</li></ol><div class="notion-text notion-block-338c0e14283f80b2a8e5d985afce1cb8">由于 load 类型指令需要<b>一个额外周期</b>，此期间如果继续保持读使能拉高，就会重复触发 DPI-C 函数，会触发一些对于设备访问的副作用（例如重复触发出队列缓冲区，这个问题此前已经提到过）。在数据已经读出后（<code class="notion-inline-code">state == LS_GET</code>），需要拉低读使能。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-338c0e14283f80df9953ee1336c497ba"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Adf3415e3-c05b-4041-85af-5f40423a3c92%3Aimage.png?table=block&amp;id=338c0e14-283f-80df-9953-ee1336c497ba&amp;t=338c0e14-283f-80df-9953-ee1336c497ba" alt="notion image" loading="lazy" decoding="async"/></div></figure><hr class="notion-hr notion-block-338c0e14283f8013b243ca82e4616e2b"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-338c0e14283f800c8932dfc741744e62" data-id="338c0e14283f800c8932dfc741744e62"><span><div id="338c0e14283f800c8932dfc741744e62" class="notion-header-anchor"></div><a class="notion-hash-link" href="#338c0e14283f800c8932dfc741744e62" title="AXI 总线接入"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">AXI 总线接入</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-339c0e14283f80bbb152f5e1c65ccfc7" data-id="339c0e14283f80bbb152f5e1c65ccfc7"><span><div id="339c0e14283f80bbb152f5e1c65ccfc7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#339c0e14283f80bbb152f5e1c65ccfc7" title="避免握手的活锁和死锁"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">避免握手的活锁和死锁</span></span></h4><div class="notion-text notion-block-33ac0e14283f804eac82c1d800cdbec4">相关内容主要在 <b>Chapter A2 AXI transport</b> 章节，重点关注 <b>A2.3 Valid-Ready transport</b>，包含传输规范以及不同通道的握手依赖规范。以下内容由 <b>NotebookLM</b> 根据手册内容总结：</div><h5 class="notion-h notion-h4 notion-default_background notion-h-indent-3 notion-block-33ac0e14283f8091a511e369d88e7bff" data-id="33ac0e14283f8091a511e369d88e7bff"><span><div id="33ac0e14283f8091a511e369d88e7bff" class="notion-header-anchor"></div><a class="notion-hash-link" href="#33ac0e14283f8091a511e369d88e7bff" title="单通道握手信号的基本约束"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><span class="notion-default_background"><b>单通道握手信号的基本约束</b></span></span></span></h5><ol start="1" class="notion-list notion-list-numbered notion-block-33ac0e14283f80dc88a8df6628ef0f10" style="list-style-type:decimal"><li><b>VALID 不得依赖 READY</b>：发送端在产生 <code class="notion-inline-code"><b>VALID</b></code> 信号时，<b>绝对不允许</b>等待接收端的 <code class="notion-inline-code"><b>READY</b></code> 信号响应后再断定是否有效。如果双方都在等待对方的信号，就会发生死锁。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-33ac0e14283f8090842cff844a7bfc54" style="list-style-type:decimal"><li><b>READY 可以依赖 VALID</b>：接收端可以等待检测到 <code class="notion-inline-code"><b>VALID</b></code> 信号后再断定是否产生 <code class="notion-inline-code"><b>READY</b></code> 信号。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-33ac0e14283f80828002e8e60ade4c4e" style="list-style-type:decimal"><li><b>推荐 READY 默认置高</b>：协议建议 <code class="notion-inline-code"><b>READY</b></code> 信号的默认状态为 HIGH，这样传输可以在单周期内完成，提高效率。</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-33ac0e14283f801ba053cac931cc057e" style="list-style-type:decimal"><li><b>信号稳定性要求</b>：一旦 <code class="notion-inline-code"><b>VALID</b></code> 信号被置为 HIGH，它必须保持稳定，直到握手成功（即 <code class="notion-inline-code"><b>VALID</b></code> 和 <code class="notion-inline-code"><b>READY</b></code> 同时为 HIGH 的时钟上升沿）。在此期间，地址、数据或控制信息也必须保持不变。</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-33ac0e14283f80a9bf5bf17842c3a7aa" style="list-style-type:decimal"><li><b>READY 信号的灵活性</b>：即使 <code class="notion-inline-code">READY</code> 已置高，如果在 <code class="notion-inline-code">VALID</code> 变为高电平之前，接收方有理由取消准备就绪状态，则允许将 <code class="notion-inline-code">READY</code> 拉低。</li></ol><h5 class="notion-h notion-h4 notion-default_background notion-h-indent-3 notion-block-33ac0e14283f8025ab1bc6469409daa4" data-id="33ac0e14283f8025ab1bc6469409daa4"><span><div id="33ac0e14283f8025ab1bc6469409daa4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#33ac0e14283f8025ab1bc6469409daa4" title="通道间依赖关系约束"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><span class="notion-default_background"><b>通道间依赖关系约束</b></span></span></span></h5><div class="notion-text notion-block-33ac0e14283f803ea80bca6a5e9fbdfe"><b>1. 读事务依赖关系</b></div><ul class="notion-list notion-list-disc notion-block-33ac0e14283f802fbc6cfa6f2ce70a36"><li><b>必须遵守</b>：从机必须等到 <code class="notion-inline-code"><b>ARVALID</b></code> 和 <code class="notion-inline-code"><b>ARREADY</b></code> 同时置高（即地址握手完成）后，才能置高 <code class="notion-inline-code"><b>RVALID</b></code> 开始返回数据。</li></ul><ul class="notion-list notion-list-disc notion-block-33ac0e14283f80c49733e310a3c707ad"><li><b>严禁依赖</b>：</li><ul class="notion-list notion-list-disc notion-block-33ac0e14283f80c49733e310a3c707ad"><li>主机不得等待 <code class="notion-inline-code"><b>ARREADY</b></code> 置高后再置高 <code class="notion-inline-code"><b>ARVALID</b></code>。</li><li>从机不得等待主机置高 <code class="notion-inline-code"><b>RREADY</b></code> 后再置高 <code class="notion-inline-code"><b>RVALID</b></code>。</li></ul></ul><div class="notion-text notion-block-33ac0e14283f803c82b2e0c2b63bbbdf"><b>2. 写事务依赖关系</b></div><ul class="notion-list notion-list-disc notion-block-33ac0e14283f80b8a32aec641cb2c06c"><li><b>主机端独立性</b>：主机置高 <code class="notion-inline-code"><b>AWVALID</b></code> 或 <code class="notion-inline-code"><b>WVALID</b></code> 时，不得依赖于从机端的 <code class="notion-inline-code"><b>AWREADY</b></code> 或 <code class="notion-inline-code"><b>WREADY</b></code>。</li></ul><ul class="notion-list notion-list-disc notion-block-33ac0e14283f80a697bdf14645fc4709"><li><b>从机端响应约束</b>：</li><ul class="notion-list notion-list-disc notion-block-33ac0e14283f80a697bdf14645fc4709"><li>从机可以等待 <code class="notion-inline-code"><b>AWVALID</b></code> 或 <code class="notion-inline-code"><b>WVALID</b></code>（或两者）之后再置高 <code class="notion-inline-code"><b>AWREADY</b></code> 或 <code class="notion-inline-code"><b>WREADY</b></code>。</li><li><b>从机必须等到写地址和所有写数据都握手完成</b>（且 <code class="notion-inline-code"><b>WLAST</b></code> 为高）后，才能置高 <code class="notion-inline-code"><b>BVALID</b></code> 发送写响应。这是为了确保写响应仅在事务彻底完成后给出。</li></ul></ul><ul class="notion-list notion-list-disc notion-block-33ac0e14283f8067bca4df8870862b65"><li><b>严禁依赖</b>：从机不得等待主机置高 <code class="notion-inline-code"><b>BREADY</b></code> 后再置高 <code class="notion-inline-code"><b>BVALID</b></code>。</li></ul><div class="notion-blank notion-block-33ac0e14283f80a9aaf5fcdaa6131ab9"> </div><ul class="notion-list notion-list-disc notion-block-33ac0e14283f80a8bac1f21a733f91fe"><li>Single-headed arrows point to signals that can be asserted <span class="notion-red"><b>before or after</b></span> the signal at the start of the arrow.</li></ul><ul class="notion-list notion-list-disc notion-block-33ac0e14283f80d98324d8161f3967d4"><li>Double-headed arrows point to signals that must be asserted <span class="notion-red"><b>only after</b></span> assertion of the signal at the start of the arrow.</li></ul><div class="notion-row notion-block-33ac0e14283f8043b975f4a162a44678"><div class="notion-column notion-block-33ac0e14283f8035992cf674a0f4f40c" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-33ac0e14283f80b1b0d3de2f3b444968"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Aae7d4686-cda2-4f50-ab7b-32f731961a9d%3Aimage.png?table=block&amp;id=33ac0e14-283f-80b1-b0d3-de2f3b444968&amp;t=33ac0e14-283f-80b1-b0d3-de2f3b444968" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-33ac0e14283f806ea6e1c70dc6881b95" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-33ac0e14283f801d8d20c9d4c940115a"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ac4e8cecc-c380-4bc7-9290-2fb904377dc3%3Aimage.png?table=block&amp;id=33ac0e14-283f-801d-8d20-c9d4c940115a&amp;t=33ac0e14-283f-801d-8d20-c9d4c940115a" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div></div><blockquote class="notion-quote notion-block-33ac0e14283f80d285c1dd38e9123f1b"><div>参考手册</div><div class="notion-file notion-block-33ac0e14283f808a9b32e7214b623c1e"><a class="notion-file-link" href="https://file.notion.com/f/f/0de3bb29-0660-43ab-a47e-86b619f7871c/b21db214-0e66-4cb0-959f-0a1d50e8c20d/IHI0022L_amba_axi_protocol_spec.pdf?table=block&amp;id=33ac0e14-283f-808a-9b32-e7214b623c1e&amp;spaceId=0de3bb29-0660-43ab-a47e-86b619f7871c&amp;expirationTimestamp=1791460800000&amp;signature=PMBs5Ju8Kt2HkQ8NL9IvzsDxUtFAv-7Z_4MJ40Oxf7o" target="_blank" rel="noopener noreferrer"><svg class="notion-file-icon" viewBox="0 0 30 30"><path d="M22,8v12c0,3.866-3.134,7-7,7s-7-3.134-7-7V8c0-2.762,2.238-5,5-5s5,2.238,5,5v12c0,1.657-1.343,3-3,3s-3-1.343-3-3V8h-2v12c0,2.762,2.238,5,5,5s5-2.238,5-5V8c0-3.866-3.134-7-7-7S6,4.134,6,8v12c0,4.971,4.029,9,9,9s9-4.029,9-9V8H22z"></path></svg><div class="notion-file-info"><div class="notion-file-title">IHI0022L_amba_axi_protocol_spec.pdf</div><div class="notion-file-size">2 MiB</div></div></a></div><div class="notion-file notion-block-33ac0e14283f80719b70ed2ad2ed9fd6"><a class="notion-file-link" href="https://file.notion.com/f/f/0de3bb29-0660-43ab-a47e-86b619f7871c/818a0120-264a-4342-bc46-a08479a02ac5/IHI0022H_amba_axi_protocol_spec.pdf?table=block&amp;id=33ac0e14-283f-8071-9b70-ed2ad2ed9fd6&amp;spaceId=0de3bb29-0660-43ab-a47e-86b619f7871c&amp;expirationTimestamp=1791460800000&amp;signature=O7N1kk_q9V0CzILuB7P-g0B1j6fLndu2ExAp80GtH3I" target="_blank" rel="noopener noreferrer"><svg class="notion-file-icon" viewBox="0 0 30 30"><path d="M22,8v12c0,3.866-3.134,7-7,7s-7-3.134-7-7V8c0-2.762,2.238-5,5-5s5,2.238,5,5v12c0,1.657-1.343,3-3,3s-3-1.343-3-3V8h-2v12c0,2.762,2.238,5,5,5s5-2.238,5-5V8c0-3.866-3.134-7-7-7S6,4.134,6,8v12c0,4.971,4.029,9,9,9s9-4.029,9-9V8H22z"></path></svg><div class="notion-file-info"><div class="notion-file-title">IHI0022H_amba_axi_protocol_spec.pdf</div><div class="notion-file-size">2.2 MiB</div></div></a></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-33ac0e14283f8036a86fdd815c77f595"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A1efbf099-7d74-4f3e-aa9f-19f731c1ad76%3Aimage.png?table=block&amp;id=33ac0e14-283f-8036-a86f-dd815c77f595&amp;t=33ac0e14-283f-8036-a86f-dd815c77f595" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-33ac0e14283f80598038c863f2805692">值得注意的是，某一个手册版本之后移除了 AXI4-Lite 相关内容，只能去更旧版手册找。</div></blockquote><h5 class="notion-h notion-h4 notion-h-indent-3 notion-block-343c0e14283f809bbb41d6e3cd93dab7" data-id="343c0e14283f809bbb41d6e3cd93dab7"><span><div id="343c0e14283f809bbb41d6e3cd93dab7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#343c0e14283f809bbb41d6e3cd93dab7" title="单次阻塞式事务实现"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">单次阻塞式事务实现</span></span></h5><div class="notion-text notion-block-343c0e14283f800ab246e8e624ceca29">这次主要做的事情，是把 IFU 和 LSU 都接到 AXI4-Lite 总线上，并分别连到指令侧和数据侧的 DPI 从机模型。设计选择了最简单的单次事务阻塞，目的是快速跑通整个流程，学习意义大于实用意义。</div><ol start="1" class="notion-list notion-list-numbered notion-block-343c0e14283f8033a5c3f67f0926f80f" style="list-style-type:decimal"><li>总体结构</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-343c0e14283f80609857ecda8aaf3f12" style="list-style-type:decimal"><li>接口解耦合 &amp; 协议转换</li></ol><div class="notion-text notion-block-343c0e14283f80b0a1ccf7fa5575f144">起初是觉得将巨大的 AXI 状态机硬塞进 CPU 的模块中显得非常臃肿，遂想实现一个接口转换，面向内部实现一个精简化接口，并在外部实现一个 AXI 协议转换的模块，例如 <a class="notion-link" href="https://shili2017.github.io/posts/TL1/" target="_blank" rel="noopener noreferrer">Tilelink</a>。</div><div class="notion-text notion-block-343c0e14283f800aa104c0fb6f08476b">至于从机，当前就直接和 DPI 函数调用地点放在同一个位置。</div><div class="notion-text notion-block-343c0e14283f80eb8509ff86d367cd38">它的限制也很明确：</div><ul class="notion-list notion-list-disc notion-block-343c0e14283f80938a9fda46f8e03b76"><li>每个桥只支持 one outstanding</li></ul><ul class="notion-list notion-list-disc notion-block-343c0e14283f80e6ad87f09fc8d197de"><li>IFU 是 blocking fetch</li></ul><ul class="notion-list notion-list-disc notion-block-343c0e14283f801880fcde1d170f52b8"><li>LSU 是 blocking LSU</li></ul><ul class="notion-list notion-list-disc notion-block-343c0e14283f80d19508c7a60d25ecf1"><li>没有 request queue / store buffer / ID / out-of-order completion</li></ul><div class="notion-callout notion-blue_background_co notion-block-343c0e14283f8066a758f8e2136fce66"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="📝">📝</span></div><div class="notion-callout-text"><div class="notion-text notion-block-343c0e14283f80f2a6c2e3d186141c41">自己写的随机延迟测试用模板</div></div></div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-343c0e14283f80be9394dd571ea7193e" data-id="343c0e14283f80be9394dd571ea7193e"><span><div id="343c0e14283f80be9394dd571ea7193e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#343c0e14283f80be9394dd571ea7193e" title="仲裁器实现"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">仲裁器实现</span></span></h4><div class="notion-text notion-block-347c0e14283f80ee8d9bc16b8d2d17d6">这里直接对 AXI4-Lite 主机接口进行仲裁，由于目前 IFU 和 LSU 目前不会同时请求访问，所以按照固定优先级模式分配总线，谁的请求来了就把总线仲裁给谁。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-347c0e14283f80a28e00c6ec47b359c5" data-id="347c0e14283f80a28e00c6ec47b359c5"><span><div id="347c0e14283f80a28e00c6ec47b359c5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#347c0e14283f80a28e00c6ec47b359c5" title="Yosys 评估时序"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Yosys 评估时序</span></span></h4><div class="notion-text notion-block-347c0e14283f80bea089c0cfb586b5af">评估最大主频为 <b>397.867MHz</b>，之前做第一次评估到 GHz 多半有点问题，可能是 sv2v 没转换完全。。。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-347c0e14283f80bdac4ae30429b81da6" data-id="347c0e14283f80bdac4ae30429b81da6"><span><div id="347c0e14283f80bdac4ae30429b81da6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#347c0e14283f80bdac4ae30429b81da6" title="总线互联矩阵"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">总线互联矩阵</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-349c0e14283f804183a5d9ff4b6763bb" style="list-style-type:decimal"><li>总体架构</li></ol><div class="notion-text notion-block-349c0e14283f806d81f4ed3134d00d17">xbar 的实现逻辑比较简单，直接根据上游的请求信号地址决定路由到对应的从机。如果地址译码不匹配任何一个从机，将会路由到 xbar 内部一个<b>虚拟从机</b>，直接返回 DECERR 的错误信号。目前 AXI 主机的实现是接受到错误信号直接触发立即断言终止仿真进程。</div><ul class="notion-list notion-list-disc notion-block-349c0e14283f80dcaf57e256f0270975"><li>UART 和 CLINT 作为独立从设备挂在 xbar 下</li></ul><ul class="notion-list notion-list-disc notion-block-349c0e14283f80c2b1bacaffa034bd10"><li>其他暂时没有单独建模的 MMIO 地址，先路由到 dpi_sim_sram，由内部的 DPI 函数处理其他设备的访问</li></ul><ul class="notion-list notion-list-disc notion-block-349c0e14283f80e09eb7e97a37e9420b"><li>对于 MTIME，讲义提到需要根据仿真速率修改 AM 侧的代码，使得 <code class="notion-inline-code">io_read</code> 读出的 us 数更接近真实的时间流逝。实际运行发现 mtime 读秒没有远远大于物理时间流逝，且随着各种调试开关仿真速率会时刻变化，故不对 AM 端侧代码进行更改。</li></ul><ol start="2" class="notion-list notion-list-numbered notion-block-349c0e14283f80f0a7daf441c443fbc5" style="list-style-type:decimal"><li>Difftest bug</li></ol><div class="notion-text notion-block-349c0e14283f80378f94e59a7846b8be">在开启 difftest 的情况，由于之前访问设备的时候
通过 dpi 接口 <code class="notion-inline-code">paddr_write</code>，而内部如果是设备访问会设置 <code class="notion-inline-code">is_skip_ref</code>，从而跳过指令对
比。但是现在 sim_uart.sv 将 npc 的串口访问路由到 rtl 内部，副作用由<code class="notion-inline-code">$write()</code>完成，因
此 difftest 会对设备访问指令进行检查，又由于参考模型 ref 不支持设备，导致 <code class="notion-inline-code">out_of_bound</code> 触发物理地址越界。<b><span class="notion-inline-underscore">解决方案通过 dpi 接口设置一个跳过标志位即可。</span></b></div><hr class="notion-hr notion-block-34cc0e14283f801d95dee8b6b3ab13c0"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-34cc0e14283f8044906dd3196efd5b85" data-id="34cc0e14283f8044906dd3196efd5b85"><span><div id="34cc0e14283f8044906dd3196efd5b85" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34cc0e14283f8044906dd3196efd5b85" title="ysyxSoC"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">ysyxSoC</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-34cc0e14283f80abb434ff99a014df4b" data-id="34cc0e14283f80abb434ff99a014df4b"><span><div id="34cc0e14283f80abb434ff99a014df4b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34cc0e14283f80abb434ff99a014df4b" title="输出自动刷新"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">输出自动刷新</span></span></h4><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-34cc0e14283f80a2b67ede21f928f122"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A40b294f4-1450-4de2-87e7-a78909ac05d4%3Aimage.png?table=block&amp;id=34cc0e14-283f-80a2-b67e-de21f928f122&amp;t=34cc0e14-283f-80a2-b67e-de21f928f122" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-34cc0e14283f80a5ae6afd14f415938f">UART 设备通过 <code class="notion-inline-code">$write()</code> 系统任务输出单个字符，这时需要启用 <code class="notion-inline-code"><a class="notion-link" href="https://verilator.org/guide/latest/exe_verilator.html#cmdoption-autoflush" target="_blank" rel="noopener noreferrer">--autoflush</a></code> 选项，但官方手册仍然建议在 C++ 主循环中偶尔调用 <code class="notion-inline-code">fflush(stdout)</code>，启用该参数可能降低运行性能。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-34cc0e14283f80d4a475f12b6fcaa389" data-id="34cc0e14283f80d4a475f12b6fcaa389"><span><div id="34cc0e14283f80d4a475f12b6fcaa389" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34cc0e14283f80d4a475f12b6fcaa389" title="添加 AM 运行时环境"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">添加 AM 运行时环境</span></span></h4><h5 class="notion-h notion-h4 notion-default_background notion-h-indent-3 notion-block-34cc0e14283f80e7b92bc9722cf55af2" data-id="34cc0e14283f80e7b92bc9722cf55af2"><span><div id="34cc0e14283f80e7b92bc9722cf55af2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34cc0e14283f80e7b92bc9722cf55af2" title="重新梳理 abstract-machine 文件夹下的 script 调用"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>重新梳理 abstract-machine 文件夹下的 script 调用</b></span></span></h5><div class="notion-text notion-block-34cc0e14283f809ab6f1e5d664dae617">终端输入 make ARCH=riscv32e-npc run 之后：</div><ol start="1" class="notion-list notion-list-numbered notion-block-34cc0e14283f804589d0c2cefa3cfed6" style="list-style-type:decimal"><li>生成一个 <code class="notion-inline-code">Makefile.xxx</code>，<code class="notion-inline-code">$(ALL)</code>用于选择参与编译的源文件对象</li></ol><div class="notion-text notion-block-34cc0e14283f801e8417cd8deb77642e">核心入口是 abstract-machine/Makefile</div><ol start="2" class="notion-list notion-list-numbered notion-block-34cc0e14283f800ebe52c773f4070d9b" style="list-style-type:decimal"><li><code class="notion-inline-code">ARCH</code> 必须等于 abstract-machine/scripts/ 下某个 <code class="notion-inline-code">.mk</code> 文件的文件名，否则会引发报错。之后内部会对 <code class="notion-inline-code">ARCH</code> 的名称进行拆解，例如：</li><ol class="notion-list notion-list-numbered notion-block-34cc0e14283f800ebe52c773f4070d9b" style="list-style-type:lower-alpha"><ul class="notion-list notion-list-disc notion-block-34cc0e14283f80d99e9dc91429150b12"><li><code class="notion-inline-code">ARCH=riscv32e-npc</code> -&gt; <code class="notion-inline-code">ISA=riscv32e, PLATFORM=npc</code></li></ul><ul class="notion-list notion-list-disc notion-block-34cc0e14283f8040ab2cfa0ac61549de"><li><code class="notion-inline-code">ARCH=riscv32-nemu</code> -&gt; <code class="notion-inline-code">ISA=riscv32, PLATFORM=nemu</code></li></ul><div class="notion-text notion-block-34cc0e14283f809f8080de84b641007b">依据解析出的 <code class="notion-inline-code">ARCH</code>，包含对应的 <code class="notion-inline-code">.mk</code> 脚本：</div><ul class="notion-list notion-list-disc notion-block-34cc0e14283f804e824fd8f4fdf3f661"><li><code class="notion-inline-code">ARCH=riscv32e-npc</code> 时，自动包含 <code class="notion-inline-code">scripts/riscv32e-npc.mk</code></li></ul><ul class="notion-list notion-list-disc notion-block-34cc0e14283f80618dd7c80df8f05528"><li><code class="notion-inline-code">ARCH=riscv32-nemu</code> 时，自动包含 <code class="notion-inline-code">scripts/riscv32-nemu.mk</code></li></ul></ol></ol><div class="notion-text notion-block-34cc0e14283f804593fdf7d3406df8de">而这些 <code class="notion-inline-code">script/$(ARCH).mk</code> 包含了 <code class="notion-inline-code">$(ISA).mk</code> 和 <code class="notion-inline-code">$(PLATFORM).mk</code> 以及私有的源码和编译参数。</div><h5 class="notion-h notion-h4 notion-default_background notion-h-indent-3 notion-block-34dc0e14283f80af8bc6c3a073db9075" data-id="34dc0e14283f80af8bc6c3a073db9075"><span><div id="34dc0e14283f80af8bc6c3a073db9075" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34dc0e14283f80af8bc6c3a073db9075" title="文件结构组织"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>文件结构组织</b></span></span></h5><div class="notion-text notion-block-34dc0e14283f80269e81f9d82d1c7f00">有了上面的分析，如何添加 AM 环境相关的文件就比较清晰了，这里先按照最小化可以运行的配置添加，后续再逐步新增设备/功能的支持。</div><ol start="1" class="notion-list notion-list-numbered notion-block-34dc0e14283f80f6a18dcdaddf7b8e41" style="list-style-type:decimal"><li>在 <code class="notion-inline-code">am/src/platform/</code> 和 <code class="notion-inline-code">am/src/riscv/</code> 下添加两个 <code class="notion-inline-code">ysyxsoc</code> 的文件夹：</li><ol class="notion-list notion-list-numbered notion-block-34dc0e14283f80f6a18dcdaddf7b8e41" style="list-style-type:lower-alpha"></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-34dc0e14283f80608beefa0bdf0890e7" style="list-style-type:decimal"><li>新增 <code class="notion-inline-code">am/scripts/platform/ysyxsoc.mk</code> 和 <code class="notion-inline-code">am/scripts/riscv32e-ysyxsoc.mk</code> 两个 Makefile 脚本，内容大致参考 NPC 和 NEMU 的，只修改源文件列表和链接脚本路径。</li></ol><h5 class="notion-h notion-h4 notion-default_background notion-h-indent-3 notion-block-34dc0e14283f80609189ef27824ee31c" data-id="34dc0e14283f80609189ef27824ee31c"><span><div id="34dc0e14283f80609189ef27824ee31c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34dc0e14283f80609189ef27824ee31c" title="链接脚本和启动代码详解"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>链接脚本和启动代码详解</b></span></span></h5><div class="notion-text notion-block-34dc0e14283f80e79124d391a8371d3e"><b>启动代码：</b></div><ul class="notion-list notion-list-disc notion-block-34dc0e14283f8056b696d023abbc1ae8"><li>第一行定义 section name 为 entry，”ax” = alloc + executable 运行时需要分配/加载到内存 + 可执行。</li></ul><ul class="notion-list notion-list-disc notion-block-34dc0e14283f80799c1dcb6c5c923e9c"><li>定义 <code class="notion-inline-code">_start</code> 为全局符号，这样链接脚本中的 <code class="notion-inline-code">ENTRY(_start)</code> 还有 <code class="notion-inline-code">LDFLAGS</code> 中的 <code class="notion-inline-code">-e _start</code> 才能找到该符号。</li></ul><ul class="notion-list notion-list-disc notion-block-34dc0e14283f806fb92bfed05fb2033f"><li>第三行，指定 _start 的类型是 FUNC</li><ul class="notion-list notion-list-disc notion-block-34dc0e14283f806fb92bfed05fb2033f"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-34dc0e14283f8066b87bc3b6f12e8ecf"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A5068166b-7477-40d3-856c-9a795312f82f%3Aimage.png?table=block&amp;id=34dc0e14-283f-8066-b87b-c3b6f12e8ecf&amp;t=34dc0e14-283f-8066-b87b-c3b6f12e8ecf" alt="notion image" loading="lazy" decoding="async"/></div></figure></ul></ul><ul class="notion-list notion-list-disc notion-block-34dc0e14283f806c83bbde27041f0209"><li><code class="notion-inline-code">_start</code>：主要就是分配栈空间，然后进入 <code class="notion-inline-code">trm_init</code> 函数，执行 <code class="notion-inline-code">main</code> 后根据 <code class="notion-inline-code">ret</code> 触发 <code class="notion-inline-code">trap</code>。</li></ul><div class="notion-text notion-block-34dc0e14283f80ee8682c4e44cf6302b"><b>链接脚本</b></div><ul class="notion-list notion-list-disc notion-block-34dc0e14283f806d951bf9f303ea0a40"><li>定义入口为 <b>_start</b></li></ul><ul class="notion-list notion-list-disc notion-block-34dc0e14283f802ab2d6e4f61e99443d"><li>MEMORY 字段划分物理内存空间，后面 <code class="notion-inline-code">&gt; mrom</code> 代表将该 section 划分到对应的物理空间</li></ul><ul class="notion-list notion-list-disc notion-block-34dc0e14283f802394d8d4b30303dfb3"><li>相比于原本的 <code class="notion-inline-code">linker.ld</code> 主要修改了栈指针和堆地址，<code class="notion-inline-code">_stack_top</code> 从 sram 基地址开始连续划分 4 KB 的空间，剩余的空间作为 heap</li></ul><ul class="notion-list notion-list-disc notion-block-34dc0e14283f80e4a075e3549f9fc98a"><li>内存布局如下：</li><ul class="notion-list notion-list-disc notion-block-34dc0e14283f80e4a075e3549f9fc98a"></ul></ul><div class="notion-text notion-block-34dc0e14283f8036b153c4cc9eae2b03">值得注意的是，因为此时 .data 和 .bss 段被分配到 mrom，所以 <b>全局变量不支持写入</b>。</div><div class="notion-text notion-block-34dc0e14283f805aa90ce479cb727222">Program header 定义了程序可加载段，loader 解析 elf 文件的时候会用到（操作系统执行 elf 可执行文件、OpenOCD 烧录程序），对于当前阶段来说也并不是必须的，详见：</div><div class="notion-row"><a class="notion-bookmark notion-block-34dc0e14283f80bd8c85d077b88e877c" href="https://www.cnblogs.com/jiqingwu/p/elf_format_research_01.html" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">ELF文件解析（一）：Segment和Section - JollyWing - 博客园</div><div class="notion-bookmark-description">ELF 是Executable and Linking Format的缩写，即可执行和可链接的格式，是Unix/Linux系统ABI (Application Binary Interface)规范的一部分。 Unix/Linux下的可执行二进制文件、目标代码文件、共享库文件和core dump文件</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fassets.cnblogs.com%2Ffavicon_v3_3.ico?table=block&amp;id=34dc0e14-283f-80bd-8c85-d077b88e877c&amp;t=34dc0e14-283f-80bd-8c85-d077b88e877c" alt="ELF文件解析（一）：Segment和Section - JollyWing - 博客园" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://www.cnblogs.com/jiqingwu/p/elf_format_research_01.html</div></div></div><div class="notion-bookmark-image"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fimg2018.cnblogs.com%2Fblog%2F417313%2F201810%2F417313-20181012154909093-954664315.png?table=block&amp;id=34dc0e14-283f-80bd-8c85-d077b88e877c&amp;t=34dc0e14-283f-80bd-8c85-d077b88e877c" alt="ELF文件解析（一）：Segment和Section - JollyWing - 博客园" loading="lazy" decoding="async"/></div></a></div><h5 class="notion-h notion-h4 notion-default_background notion-h-indent-3 notion-block-34dc0e14283f802982d1f09dfc59aee1" data-id="34dc0e14283f802982d1f09dfc59aee1"><span><div id="34dc0e14283f802982d1f09dfc59aee1" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34dc0e14283f802982d1f09dfc59aee1" title="支持全局变量的写入操作"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>支持全局变量的写入操作</b></span></span></h5><div class="notion-text notion-block-34ec0e14283f806a9be3c523d2394484"><code class="notion-inline-code">fib</code> 测试中使用并写入了 <b>全局变量</b>，而在上一个 <code class="notion-inline-code">linker.ld</code> 中，<code class="notion-inline-code">.data</code> 被分配到了 mrom，因此触发断言 mrom 拒绝写入。为了支持全局变量的写入，很显然应该将 <code class="notion-inline-code">.data</code> 放入 sram，但是修改完之后又遇到了一个问题：</div><div class="notion-text notion-block-34ec0e14283f80d396edcc4ddb0f6d21"><code class="notion-inline-code">.bin</code> 镜像达到了 200 多 MB，最终应该导致加载时 <code class="notion-inline-code">pmem</code> 溢出了。</div><div class="notion-text notion-block-34ec0e14283f802db24be7c891359ad5">关于这个问题，下面的网页都有提到：</div><div class="notion-row"><a class="notion-bookmark notion-block-34ec0e14283f8074acd2d405baa20d77" href="https://mcu.eetrend.com/blog/2021/100555717.html" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">关于STM32CubeIDE链接脚本的小问题 | MCU加油站</div><div class="notion-bookmark-description">越来越多的客户在使用STM32CubeIDE作为集成开发工具。STM32CubeIDE在编译代码的时候，用到了链接脚本。通常情况下，STM32CubeIDE会自动生成默认的链接脚本。但是有些情况下，例如，用户程序需要定义一些特别的段来放置代码或者数据的时候，我们就需要修改链接脚本文件。</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-text">https://mcu.eetrend.com/blog/2021/100555717.html</div></div></div></a></div><div class="notion-row"><a class="notion-bookmark notion-block-34ec0e14283f80b08b13cf189d0679f7" href="https://stackoverflow.com/questions/19019199/raw-binary-file-generated-by-objcopy-is-too-big" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">Raw binary file generated by objcopy is too big</div><div class="notion-bookmark-description">Already read this but cannot solve my problem.

huge binary files with objcopy

For some testing need.
I have to add following link script when link.

.section_test 0x11323000: {  *(.section_test) ...</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fstackoverflow.com%2FContent%2FSites%2Fstackoverflow%2FImg%2Fapple-touch-icon.png%3Fv%3D9168b8ec82a5?table=block&amp;id=34ec0e14-283f-80b0-8b13-cf189d0679f7&amp;t=34ec0e14-283f-80b0-8b13-cf189d0679f7" alt="Raw binary file generated by objcopy is too big" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://stackoverflow.com/questions/19019199/raw-binary-file-generated-by-objcopy-is-too-big</div></div></div><div class="notion-bookmark-image"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fstackoverflow.com%2FContent%2FSites%2Fstackoverflow%2FImg%2Fapple-touch-icon%402.png%3Fv%3D0f0cab681579?table=block&amp;id=34ec0e14-283f-80b0-8b13-cf189d0679f7&amp;t=34ec0e14-283f-80b0-8b13-cf189d0679f7" alt="Raw binary file generated by objcopy is too big" loading="lazy" decoding="async"/></div></a></div><div class="notion-text notion-block-34ec0e14283f80ff9f8cd6b294226700">大概意思就是，<code class="notion-inline-code">.text</code> 段被分配在 mrom 中，<code class="notion-inline-code">.data</code> 被分配在了 sram 中，表面上看没有什么问题，实际上：</div><blockquote class="notion-quote notion-block-350c0e14283f8034bb6bf2dc02b5ce0d"><div>“GNU Objcopy 工具生成 BIN 文件的过程，实际上就是把 ELF 文件中各个定义了 LOAD 属性的 SECTION 按照 LMA 的先后顺序提取出来，写入 BIN 文件中。SECTION 的地址如果不是连续的，间隔部分则会填充 DUMMY 或指定的数据。所以 BIN 文件的大小为最大 LMA 加上对应的 SECTION 的 Size。”</div></blockquote><div class="notion-text notion-block-34ec0e14283f8003a25df8c53a299b64">查看 elf 文件的 section 地址，由于 <code class="notion-inline-code">.rodata</code> 和 <code class="notion-inline-code">.data</code> 之间的间隔都被 DUMMY 数据填充了，所以会生成巨大的 BIN 文件。BIN 文件不自带地址属性，这样才能保证实际的数据加载到对应的物理地址中去。</div><div class="notion-text notion-block-351c0e14283f804da936c18eb1ef1f85">调整内存布局如下：</div><div class="notion-text notion-block-351c0e14283f80bb9d9ef6cd3092928b">.data 段的初始化数据保存至 mrom 区域（LMA），运行时进入主函数前需要由 bootloader 加载进 sram 区域（VMA）。.bss 段不需要加载进入 .bin 文件中，直接由 bootloader 在其对应的 VMA 位置清零即可。堆和栈的大致布局还是和之前一样。这样 .text 段和 .data 的数据存放是连续的，不会造成 .bin 大小异常的问题。</div><div class="notion-text notion-block-351c0e14283f8090871fedd169085093"><b>bootloader</b> 的实现很简单，在 trm.c 创建一个 C 函数分别调用 klib 的 <code class="notion-inline-code">memcpy</code> 和 <code class="notion-inline-code">memset</code> 加载 <code class="notion-inline-code">.data</code> 和 <code class="notion-inline-code">.bss</code> 段即可，在 <code class="notion-inline-code">_start</code> 中添加 <code class="notion-inline-code">call __boot_loader</code> 即可。</div><div class="notion-text notion-block-351c0e14283f807e808dc0bf3f9bcdcf">大部分 cpu-test 测试都通过了，但是有极个别出现报错（wanshu and string）。打开 difftest 发现，报错指令均为 <code class="notion-inline-code">lbu</code> 。后来意外发现一个现象，每次触发 difftest 断言的时候，日志中的 Expect 值是随机值：</div><div class="notion-text notion-block-351c0e14283f805c893dd3696ede1947"><span class="notion-yellow"><b>NEMU 上电时会随机初始化物理内存区域的数据</b></span>，由此可以推断，这一块内存区域自运行后一直没有被写入过。简单测试中不涉及内存的分配与释放，所以不可能存在访问非法内存的问题，大概率是 bootloader 没有正确从 LMA 加载到 VMA。</div><div class="notion-text notion-block-351c0e14283f8093b725cd342ef51057">查看两个报错 elf 的节头表：</div><div class="notion-text notion-block-351c0e14283f800382bafa31f42a6c15">断言指令 <code class="notion-inline-code">lbu</code> 访存地址恰好分别是 <code class="notion-inline-code">0x0f000000</code> 和 <code class="notion-inline-code">0x0f000018</code> ，访问 section 为 .sdata.*，而 linker.ld 中 .sdata.* 并未作为 <b>Input Section</b>，因此最终加载内存时没有将这部分数据内容正确搬运，最终的 <b>Output Section</b> 中不包含该数据段。</div><div class="notion-text notion-block-352c0e14283f80c2b1b4d4ab91c6a7bc"><b>.sdata section</b> 核心目的：把较小的全局/静态对象集中放在可由一个专用基址寄存器加短位移访问的区域，从而减少取地址指令数量，缩短代码，有时也提高访问速度。</div><div class="notion-text notion-block-352c0e14283f8022a986ed0039152ecd">在 ysyxsoc.mk 中 <code class="notion-inline-code">LDFLAGS</code> 追加 <code class="notion-inline-code">--print-map</code> 参数查看链接器的链接过程：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-352c0e14283f803c9625ebbd98264f14"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Afe18eed7-cc53-483a-ac16-4efa347d9012%3Aimage.png?table=block&amp;id=352c0e14-283f-803c-9625-ebbd98264f14&amp;t=352c0e14-283f-803c-9625-ebbd98264f14" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-352c0e14283f808fb8b9e9df1561e57d">主要关注舍弃的输入节，在 ysyxsoc.mk 中有以下参数：</div><div class="notion-text notion-block-352c0e14283f80cfa0abfc7cc8166cbc">两个编译参数为每个全局/静态数据对象/函数分配独立的 section，这样才能配合后面的链接参数精准做 section 级别的垃圾回收，剔除未使用的函数/变量代码。</div><hr class="notion-hr notion-block-34ec0e14283f80c1bd67ede9a5404f54"/><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-350c0e14283f8000b212cce5e2a9ed57" data-id="350c0e14283f8000b212cce5e2a9ed57"><span><div id="350c0e14283f8000b212cce5e2a9ed57" class="notion-header-anchor"></div><a class="notion-hash-link" href="#350c0e14283f8000b212cce5e2a9ed57" title="symbol lookup error 问题解决记录"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">symbol lookup error 问题解决记录</span></span></h4><div class="notion-text notion-block-350c0e14283f80028131d8539ea8c0c8">在一次关闭设备选项运行 NPC 时，遇到报错：</div><div class="notion-text notion-block-350c0e14283f80909180f88596af4629">使用 LD_DEBUG=symbols 来查看 <code class="notion-inline-code">compressBound</code> 的查找过程：</div><div class="notion-text notion-block-350c0e14283f8040b02de5942808d76f">可知，关闭 Device 选项导致 <code class="notion-inline-code">libz.so.1</code> 库依赖缺失，更具体的，相关符号声明位于一个叫 <code class="notion-inline-code">zlib.h</code> 的头文件中。</div><div class="notion-text notion-block-350c0e14283f80faa07ad63874ddd887">后面查询得知，verilator 在执行 <code class="notion-inline-code">--lib-create</code> 参数时不会自动添加 <code class="notion-inline-code">$(LDLIBS)</code> 的链接参数，其中包含 <code class="notion-inline-code">-lz</code> 以及多线程和原子操作的支持库（也就是说多线程在此前可能也没有真正起作用？）。只有在添加 <code class="notion-inline-code">--exe</code> 参数生成最终可执行文件的时候，verilator 才会链接所有可用的第三方库。</div><div class="notion-text notion-block-350c0e14283f80fea2f1dde8de1ce71c">临时的解决方案是在 V{top_module_name}.mk文件构建 .so 目标中末尾追加 <code class="notion-inline-code">$(LDLIBS)</code>：</div><div class="notion-text notion-block-350c0e14283f800bb54ddfd0f943efe9">使用 nm 命令查看 libnpc.so 中的符号表：</div><div class="notion-text notion-block-350c0e14283f80ad9d6ed8d00011013e"><code class="notion-inline-code">U compressBound@ZLIB_1.2.0</code> U 表示这个库调用了该函数/变量，但是 <b>在这个库自己的代码里没有它的实现</b>。它期望在运行时，由加载它的主程序或者其他的动态库提供。</div><hr class="notion-hr notion-block-350c0e14283f8032a472e39f92847766"/><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-356c0e14283f8035b157ced0527895eb" data-id="356c0e14283f8035b157ced0527895eb"><span><div id="356c0e14283f8035b157ced0527895eb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#356c0e14283f8035b157ced0527895eb" title="Flash XIP"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Flash XIP</span></span></h4><div class="notion-text notion-block-356c0e14283f8047b770fc471c94cbea"><b>状态机设计：</b><code class="notion-inline-code">is_xip</code> 标志拉高表明 CPU 通过 APB 总线访问 Flash 的地址区域，状态机启动，在 SETUP ↔ ACCESS 状态之间流转依次向 5 个 SPI 内部寄存器发出写入请求。等待 SPI 中断信号拉高之后发起对 SPI Master RX0 寄存器的读请求，取得应答信号后拉高 APB 的 <code class="notion-inline-code">pready</code> 信号，此时 APB 总线上的读数据有效。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-357c0e14283f802d804bc1b92afd0024"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A48c7ce18-cbb4-43ee-a100-b0cc11f1e255%3Aimage.png?table=block&amp;id=357c0e14-283f-802d-804b-c1b92afd0024&amp;t=357c0e14-283f-802d-804b-c1b92afd0024" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-callout notion-blue_background_co notion-block-357c0e14283f8026bb3af022af990f22"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="📝">📝</span></div><div class="notion-callout-text"><div class="notion-text notion-block-357c0e14283f8088a39ad4870a4a7544">这里其实明显还可以优化一下，第一次 <code class="notion-inline-code">state == SETUP &amp;&amp; reg_cnt == 5</code> 之后设置一个 cfg_done 的标志位，reg_cnt 复位值将被设置为 3，后续有 flash 请求时直接写两个寄存器 TX1 和 CTRL.GO 即可。</div></div></div><div class="notion-text notion-block-357c0e14283f801395f9d26b24383b9b">附上非 XIP 的软件流程：</div><div class="notion-text notion-block-357c0e14283f8091abb3f00e4f573498">在完成 XIP 内容的时候有两点容易忽视：</div><ol start="1" class="notion-list notion-list-numbered notion-block-357c0e14283f808a86bbf336ea35d47d" style="list-style-type:decimal"><li>flash.v 中会将读出数据的字节序翻转一次，符合小端内存按照地址递增读出字节的效果。例如仿真环境中内存一个字的数据是 <code class="notion-inline-code">0x11223344</code> ，SPI Master 的 RX0 中最后将会是 <code class="notion-inline-code">0x44332211</code>。<span class="notion-purple"><b>XIP 还需要做一次字节序调整</b></span>。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-357c0e14283f802db92bce28b2c2b60c" style="list-style-type:decimal"><li>当 SPI Master 的 ASS 控制位为 1 时，控制器会在传输期间自动驱动已选中的 SS 信号：传输开始后拉低对应片选，传输结束后释放片选。但 ASS 只负责自动控制片选时序，并不负责选择哪个从设备。因此<b><span class="notion-inline-underscore">软件仍需要先写 SS 寄存器</span></b>，指定本次传输要选中的 SPI 设备。</li></ol><hr class="notion-hr notion-block-357c0e14283f80458258cdb919226ea3"/><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-35ac0e14283f80948698db6a9fdc4640" data-id="35ac0e14283f80948698db6a9fdc4640"><span><div id="35ac0e14283f80948698db6a9fdc4640" class="notion-header-anchor"></div><a class="notion-hash-link" href="#35ac0e14283f80948698db6a9fdc4640" title="PSRAM"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">PSRAM</span></span></h4><div class="notion-text notion-block-362c0e14283f801a869af5e7ce8a6f29">状态机设计如下：</div><div class="notion-text notion-block-362c0e14283f801882cae7145e8c7018">状态转移主要来源于一个全局 <code class="notion-inline-code">sck</code> 周期计数器 <code class="notion-inline-code">cyc_cnt</code></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-362c0e14283f809c9456c7d8328d2861"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Aeb3875f4-de96-43e4-8037-9bd67254bd10%3Aimage.png?table=block&amp;id=362c0e14-283f-809c-9456-c7d8328d2861&amp;t=362c0e14-283f-809c-9456-c7d8328d2861" alt="notion image" loading="lazy" decoding="async"/></div></figure><hr class="notion-hr notion-block-35ac0e14283f80989cced382c45121cd"/><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-35ac0e14283f80d79ca6e02da2abca5d" data-id="35ac0e14283f80d79ca6e02da2abca5d"><span><div id="35ac0e14283f80d79ca6e02da2abca5d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#35ac0e14283f80d79ca6e02da2abca5d" title="从 Flash 加载程序到 RAM"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">从 Flash 加载程序到 RAM</span></span></h4><div class="notion-text notion-block-35ac0e14283f8037b991c441cdc5c400">按照加载数据段的方法，对 linker.ld 作如下修改：</div><div class="notion-text notion-block-35ac0e14283f80bd95d0cdcd8e14e2b2">很显然这有<span class="notion-red"><b>一个致命的问题</b></span>，一开始从 Flash 取指令加载代码段到 SRAM，然后跳到 SRAM 取指令，又来一次 bootloader …… 循环往复。期望的效果应该是，bootloader 之前的程序仍然存放在 Flash，用户代码将会被 bootloader 搬运到 SRAM 中执行，因此需要新增一个数据段，更正 linker.ld 和 start.S 启动代码。</div><div class="notion-text notion-block-35ac0e14283f80a5a8a9ff34d1072a82">入口放在 <code class="notion-inline-code">.boot</code> 段，分配临时启动栈，调用 <code class="notion-inline-code">_boot_loader</code> 将用户代码段和数据都放入 SRAM 中，然后直接跳转到 <code class="notion-inline-code">_main</code>（原来的 <code class="notion-inline-code">_start</code>），后丢弃初始栈。</div><div class="notion-text notion-block-35bc0e14283f803485a9d1922c07fb48">在 linker.ld 的 Output Section 添加 .boot 段：</div><div class="notion-text notion-block-35bc0e14283f80b385dbe9c7dcce235d">运行发现取到了非法指令：</div><div class="notion-text notion-block-35bc0e14283f80b3b6e8d595b1b0ac49">查看反汇编发现 <code class="notion-inline-code">_boot_loader</code> 还有<code class="notion-inline-code">memcpy</code> 和 <code class="notion-inline-code">memset</code> 这类 klib 函数也是被分配到了 <code class="notion-inline-code">.text</code> 段，于是使用 <code class="notion-inline-code">__attribute__</code> 显式声明，并为 boot 程序准备专用的两个内存操作函数。</div><div class="notion-text notion-block-35bc0e14283f80798f0debfb6ddc7942">但事实上仍然有部分程序运行不成功。查看反汇编文件发现一些程序的入口函数 <code class="notion-inline-code">_start</code> 并没有被排在最前面，SDB 调试也可以发现第一条指令是从 <code class="notion-inline-code">boot_memcpy</code> 开始执行的：</div><div class="notion-row notion-block-35bc0e14283f80d2b940ef8531cb8493"><div class="notion-column notion-block-35bc0e14283f801fab49eff01e9c7ba3" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"></div><div class="notion-spacer"></div><div class="notion-column notion-block-35bc0e14283f80809401c7f990ef8dcd" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"></div><div class="notion-spacer"></div></div><div class="notion-text notion-block-35bc0e14283f8081b078f0b1b6eb0bdd">搜索相关资料后发现，入口函数虽然已经明确了为 <code class="notion-inline-code">_start</code> ，分别在两处（linker.ld: <code class="notion-inline-code">ENTRY(_start)</code> 以及 链接参数 <code class="notion-inline-code">-e</code>），但并不保证生成的二进制文件一定被排在前面，只是指定了 elf header 的一个值。</div><blockquote class="notion-quote notion-block-35bc0e14283f804199caf45eb1cfd09f"><div>objcopy can be used to generate a raw binary file by using an output target of ‘binary’ (e.g., use -O binary). When objcopy generates a raw binary file, it will essentially produce a memory dump of the contents of the input object file. All symbols and relocation information will be discarded. The memory dump will start at the load address of the lowest section copied into the output file.
<code class="notion-inline-code">objcopy</code> 可通过使用输出目标“ binary ”来生成原始二进制文件（例如，使用 -O binary ）。当 <code class="notion-inline-code">objcopy</code> 生成原始二进制文件时，它本质上会生成输入对象文件内容的内存转储。所有符号和重定位信息都会被丢弃。该内存转储将从复制到输出文件中的<span class="notion-red"><b>最低段的加载地址开始</b></span>。
—— 源于 objcopy 官方手册</div></blockquote><div class="notion-row notion-block-35bc0e14283f80f6be9af3bb4b847c78"><div class="notion-column notion-block-35bc0e14283f80aaabdcf31a923a3caa" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"></div><div class="notion-spacer"></div><div class="notion-column notion-block-35bc0e14283f80d1b361e37e0236ce3b" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"></div><div class="notion-spacer"></div></div><div class="notion-text notion-block-35bc0e14283f80a29d82d07aa34d6d6b">在一些嵌入式设备中，硬件复位时会读取中断向量表，正确找到程序的入口地址，因此入口地址不必是指令存储器的最开头。这里要注意区分 <span class="notion-purple">“硬件复位地址” 与 “程序入口地址”</span>。</div><div class="notion-row"><a class="notion-bookmark notion-block-35bc0e14283f80afbaead54041bf3b3f" href="https://unix.com.hr/2022/06/05/effect-of-entry-directive-in-linker-script-on-cortex-m4-binary/" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">Effect of ENTRY directive in Linker Script on Cortex-M4 binary</div><div class="notion-bookmark-description">My initial understanding of ARM Cortex-M4 bootstraping sequence was that when MCU was started, it loaded pointer from Reset_Handler IVT and then jumped to that address to start configuring the processor.Later on when I started doing some stuff on Linker Script, I noted something called ENTRY keyword which by GNU Linker documentation specified that it defined starting point of binary. So which one is it, IVR or ENTRY? That’s what we are going to tackle in this post.</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Funix.com.hr%2Ffavicon.ico?table=block&amp;id=35bc0e14-283f-80af-baea-d54041bf3b3f&amp;t=35bc0e14-283f-80af-baea-d54041bf3b3f" alt="Effect of ENTRY directive in Linker Script on Cortex-M4 binary" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://unix.com.hr/2022/06/05/effect-of-entry-directive-in-linker-script-on-cortex-m4-binary/</div></div></div></a></div><div class="notion-text notion-block-35bc0e14283f80ceb019edc75a5b1c6a">而对于目前的裸机程序就要求<b>硬件复位地址等于程序入口地址。</b></div><div class="notion-text notion-block-35bc0e14283f8005a7afc11e35a21ebf">具体的解决方法是：给 .boot 段的入口单独分配一个 section，然后在 linker.ld 的 .boot output section 里把 <code class="notion-inline-code">.boot.entry</code> 明确排在其他 .boot 段之前。</div><div class="notion-text notion-block-35bc0e14283f80c3bcb3e6d28750fcf7">二级加载思路比较简单，在 _start 调用 <code class="notion-inline-code">_fsbl</code> 把 <code class="notion-inline-code">_ssbl</code> 相关的逻辑全部搬运到 SRAM，再直接跳转到 <code class="notion-inline-code">_ssbl_entry</code> ，最后跳转到 <code class="notion-inline-code">_main</code>。每次跳转都可以丢弃当前栈，因为加载阶段完成后不再需要返回了。</div><div class="notion-text notion-block-35dc0e14283f80c4a1b3dc628be71d42">linker.ld 将 fsbl 和 ssbl 单独划分 section：</div><div class="notion-text notion-block-371c0e14283f804c9cf9e8abe7df14a8">运行 RTT 加载时间很长，查看符号表：</div><div class="notion-text notion-block-371c0e14283f80d599b4d96b40e553c4">假设按照每秒 10000 条指令估算，大概需要 20 分钟。于是想到之前在 RTT 中通过命令运行了一些 AM 程序，这部分程序很可能占用了较大空间。</div><div class="notion-callout notion-red_background_co notion-block-374c0e14283f8086b055fc3b265449dc"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="🚨">🚨</span></div><div class="notion-callout-text"><div class="notion-text notion-block-374c0e14283f80a394b7f80f9d1d6719">此外，上面表明 <code class="notion-inline-code">.srodata.*</code> 没有被放入统一的 output section 中，这会导致 bootloader 不加载这些数据。</div></div></div><hr class="notion-hr notion-block-35dc0e14283f808287c2c39d45962a5e"/><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-368c0e14283f80d68758eac0b1605e6d" data-id="368c0e14283f80d68758eac0b1605e6d"><span><div id="368c0e14283f80d68758eac0b1605e6d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#368c0e14283f80d68758eac0b1605e6d" title="SDRAM"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">SDRAM</span></span></h4><div class="notion-text notion-block-368c0e14283f80f09715e8e785127bfc">SDRAM 的逻辑相较于之前其他的存储器要复杂一些，所以单开一小节对控制器代码和仿真模型实现做一些记录。</div><div class="notion-text notion-blue_background notion-block-36dc0e14283f80e5af37c8a076fe1f6a"><b>控制器部分</b></div><div class="notion-text notion-block-36dc0e14283f80b394a7f4b62899298e">模式寄存器值</div><ul class="notion-list notion-list-disc notion-block-36dc0e14283f8094a334f5abf015843a"><li>A12 ~ A10：保留段，设置为 0</li></ul><ul class="notion-list notion-list-disc notion-block-36dc0e14283f808c9b8bc2a19de76320"><li>A9：可编程突发长度模式，只决定写入是否突发，读出仍然为突发</li></ul><ul class="notion-list notion-list-disc notion-block-36dc0e14283f80debff8fb0341717063"><li>A8 ~ A7：Standard Operation</li></ul><ul class="notion-list notion-list-disc notion-block-36dc0e14283f80fe9c45f3d687967e48"><li>A6 ~ A4：列选通延迟，注册一个 READ 命令到输出数据可用之间的时钟周期延迟为 <b>2</b>。</li></ul><ul class="notion-list notion-list-disc notion-block-36dc0e14283f8084b2d0f0f890888db6"><li>A3：顺序突发模式</li></ul><ul class="notion-list notion-list-disc notion-block-36dc0e14283f806793fcd6da69a19595"><li>A2 ~ A0：一次突发两个 16 bits，即 4 bytes</li></ul><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-36dc0e14283f801f894ce45db04cd2b4"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A14e290d6-b84a-41cd-b7c7-42b629946b1c%3Aimage.png?table=block&amp;id=36dc0e14-283f-801f-894c-e45db04cd2b4&amp;t=36dc0e14-283f-801f-894c-e45db04cd2b4" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-36dc0e14283f80aeb1f8f482e09804e1">APB 地址解码</div><div class="notion-text notion-block-36dc0e14283f80bf8ed3e63baa897a57">上面三行等效为：</div><ul class="notion-list notion-list-disc notion-block-36dc0e14283f800baee6d94721a861e8"><li>列地址取总线上地址的九位，寻址范围 512</li></ul><ul class="notion-list notion-list-disc notion-block-36dc0e14283f80029d3bfa93307274a4"><li>行地址取总线上 <code class="notion-inline-code">[24:12]</code>，寻址范围 8192</li></ul><ul class="notion-list notion-list-disc notion-block-36dc0e14283f80448104c0eb6139af89"><li>块地址取总线上 <code class="notion-inline-code">[11:10]</code>，寻址范围 4</li></ul><div class="notion-callout notion-yellow_background_co notion-block-373c0e14283f807195f8d8bdc2ad35b7"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="⚠️">⚠️</span></div><div class="notion-callout-text"><div class="notion-text notion-block-373c0e14283f806f91d2c0ceaedddb11">此处需要明确 CPU 总线地址到 SDRAM 内部地址的转换。通常内存按照字节寻址，这款型号的 SDRAM 内部则是按照<b>半字寻址</b>。</div></div></div><div class="notion-text notion-block-36dc0e14283f80fe85bde5affeef11f1">如果把地址进行线性映射（DPI-C 访问数组），则是 <code class="notion-inline-code">{row, bank, col}</code> ，索引走完一行之后跳到下一个 bank，走完所有 bank 后回到第一个 bank 的下一行，周而复始。</div><div class="notion-text notion-block-375c0e14283f80dab78dc2c15451d84e">对于位扩展，控制器的地址切分要改成这样：</div><div class="notion-text notion-block-375c0e14283f802e9851e62b45df1372">因为位扩展后 SDRAM 相当于按字寻址，所以总线地址清零低两位后再除以4。相应的，DPI 函数也要乘以 4 映射至按字节寻址的地址。NEMU 中仍然把扩展后 32MB * 2 的空间线性映射成数组，便于 SDB 访问。</div><div class="notion-text notion-block-375c0e14283f8064bd17da826ddedb1a">字扩展就更容易了，直接取总线地址上的第 26 位作为片选信号。<b>此外还需要修改控制器行缓存的数量。</b>但是考虑到做字扩展直接修改 verilog 代码比较复杂，必然还会在 Makefile 中添加大量的 sed 命令，所以还是直接选择修改 chisel 里面的端口定义。之前有点被讲义误导了，其实就算只写 verilog 也是可以修改端口定义部分的。</div><div class="notion-text notion-blue_background notion-block-36dc0e14283f80b9b12ad6646899e91a"><b>仿真模型</b></div><div class="notion-text notion-block-36dc0e14283f804da1adfa05b43613a6">跨行访问测试中出现错误，波形中发现问题。</div><div class="notion-text notion-block-374c0e14283f80c2884cd47e655060dd">首先对 bank3 row0 写入一个数据：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-374c0e14283f809e97d5e3b4a098eef7"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A2553ea4a-7820-42bd-9983-a08e9673c9b9%3Aimage.png?table=block&amp;id=374c0e14-283f-809e-97d5-e3b4a098eef7&amp;t=374c0e14-283f-809e-97d5-e3b4a098eef7" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-374c0e14283f8009a9dac3a11f11b949">然后对 bank0 row1 写入一个数据：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-374c0e14283f805c9a86d4ed74ccbce1"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Adae26c44-dfd5-4514-b5c6-ca9b82e1cee5%3Aimage.png?table=block&amp;id=374c0e14-283f-805c-9a86-d4ed74ccbce1&amp;t=374c0e14-283f-805c-9a86-d4ed74ccbce1" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-374c0e14283f80f68f60eb6cf3c8e5a5">由于是跨行访问，两次均发送 ACTIVE 命令。</div><div class="notion-text notion-block-374c0e14283f802487d3f0efeb380289">然后紧接着读取第一个地址，控制器并未发送 ACTIVE 命令，而是直接发送了 READ。而仿真模型中 <code class="notion-inline-code">addr_q</code> 仍然是前一个行地址，读出无效数据。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-374c0e14283f80f89f93e0019700fe25"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A9ad85843-839c-47d9-b2b2-211f1f2a9c88%3Aimage.png?table=block&amp;id=374c0e14-283f-80f8-9f93-e0019700fe25&amp;t=374c0e14-283f-80f8-9f93-e0019700fe25" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-374c0e14283f8067aaece8131f055f38">RTFSC 控制器，想查看一下发出 ACTIVE 命令的条件：</div><div class="notion-text notion-block-374c0e14283f80c79b34ca10f12663d8">从代码可以看到，当前行地址如果已经在 active_row_q 被记录视为行命中，不会再次发送 ACTIVE 命令。控制器 <b>每个 bank 都有独立的 row buffer</b>，而自己的仿真模型仅仅实现了一个 row buffer。</div><div class="notion-text notion-block-374c0e14283f80d29b3bd756854f183b">bug 原因找到。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-374c0e14283f800e9c4bcec325332466"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ad6df9c24-e24c-4fa6-9c9a-d5e8ae4c823e%3Aimage.png?table=block&amp;id=374c0e14-283f-800e-9c4b-cec325332466&amp;t=374c0e14-283f-800e-9c4b-cec325332466" alt="notion image" loading="lazy" decoding="async"/></div></figure><hr class="notion-hr notion-block-378c0e14283f800b80b8cf2b3acca69c"/><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-378c0e14283f8025b2b2ee2224810ecc" data-id="378c0e14283f8025b2b2ee2224810ecc"><span><div id="378c0e14283f8025b2b2ee2224810ecc" class="notion-header-anchor"></div><a class="notion-hash-link" href="#378c0e14283f8025b2b2ee2224810ecc" title="接入 NVBoard"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">接入 NVBoard</span></span></h4><div class="notion-text notion-block-378c0e14283f80d8b699e814ff17762d">首先需要添加 NVBoard 的构建规则，主要参考 <code class="notion-inline-code">nvboard/example</code> 项目。</div><div class="notion-text notion-block-379c0e14283f80d18207c0213e239ed1">基本构建流程是将 NVBoard 源文件编译成静态库 <code class="notion-inline-code">nvboard.a</code> ，作为 verilator 编译的依赖之一。由于我这里是需要将 NPC 编译成动态链接库，所以需要在 nvboard 的 <code class="notion-inline-code">CXXFLAGS</code> 加一条 <code class="notion-inline-code">-fPIC</code> 。最后在 npc_api.cpp 中添加 nvboard 初始化、引脚绑定、状态更新、退出。</div><div class="notion-text notion-block-379c0e14283f80dfbc0dd68f7ced4a7d">python 脚本会读取约束文件，生成一个封装了所有引脚绑定的 cpp 文件，这个文件也需要添加到 verilator 的依赖项。</div><div class="notion-text notion-block-379c0e14283f80a59657ca20b0e51aeb">测试过程中遇到的一些符号/文件未找到、未定义等问题基本都是源文件或者编译参数、链接参数不全导致的。</div><div class="notion-text notion-block-379c0e14283f8040b805e24a8841d537">Uart 手册中提到：</div><blockquote class="notion-quote notion-block-37bc0e14283f80aaa86afbdb7244c864"><div>The value set should be equal to (system clock speed) / (16 x desired baud rate)</div></blockquote><div class="notion-text notion-block-37bc0e14283f80cfab5cfb4f844d513d">实际波特率等于时钟频率除以 16 再除以用户设置的 divisor，而 nvboard 中实际波特率就是时钟频率除以 divisor。因此 divisor (nv) = 16 * divisor (rtl)。此外还需要注意，<code class="notion-inline-code">nvboard_update()</code> 应该时钟每翻转一次就调用，而不是提交一条指令才调用。</div><div class="notion-text notion-block-37bc0e14283f80c4bf25dc268c716620">串口输入大部分内容其实已经在 C 阶段最后的支线任务完成了，当时是通过 NEMU 底层设置终端文件描述符，非阻塞从终端获取字符输入的。这里则是通过 nvboard SDL 监测键盘输入事件，获取到字符按照 uart 标准协议发送到 ysyxSoC 顶层的 uart_rx 中。软件再读取 uart 的寄存器来获取到键盘输入到内存中。</div><div class="notion-text notion-block-37bc0e14283f80228ba2e9d3655142b0"><b>最后终于完成 SoC 计算机系统：</b><s>完结撒花！</s></div><div class="notion-text notion-block-37fc0e14283f80b182c6d2134699132c">性能：大约 48000 insts/s，microbecn test 规模</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-37fc0e14283f80a4b415d8baf860d318"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A42b72548-7a19-4f1d-b108-0708bded5895%3Aimage.png?table=block&amp;id=37fc0e14-283f-80a4-b415-d8baf860d318&amp;t=37fc0e14-283f-80a4-b415-d8baf860d318" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-37fc0e14283f80599c79fdfa13d1813e"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ae9a136a0-4bf0-4b71-90d1-b9cbb2d37c62%3Aimage.png?table=block&amp;id=37fc0e14-283f-8059-9c79-fdfa13d1813e&amp;t=37fc0e14-283f-8059-9c79-fdfa13d1813e" alt="notion image" loading="lazy" decoding="async"/></div></figure><hr class="notion-hr notion-block-37fc0e14283f8066b742caa3d8698099"/><div class="notion-blank notion-block-390c0e14283f80679bfddf00db4d08a0"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[使用 remake 工具分析复杂 Makefile]]></title>
            <link>http://hyacimond.top/tools/remake</link>
            <guid>http://hyacimond.top/tools/remake</guid>
            <pubDate>Fri, 09 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[包含 remake 教程，KCachegrind 可视化，Callgrind 性能分析]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-2e3c0e14283f8016ba6efac7c73cf01a"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-2e3c0e14283f8052b901ef971597d574" data-id="2e3c0e14283f8052b901ef971597d574"><span><div id="2e3c0e14283f8052b901ef971597d574" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2e3c0e14283f8052b901ef971597d574" title="使用 Remake"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">使用 Remake</span></span></h2><div class="notion-text notion-block-2e3c0e14283f8077a762ff5c01d0daab">ysyx-workbench 里面的 Makefile 通常都十分巨大，直接 RTFSC 十分痛苦。后来通过 AI 搜索到一个叫 Remake 的工具，可以单步运行 Makefile 文件，跟踪 Makefile 的内部层层调用。</div><div class="notion-row"><a class="notion-bookmark notion-block-2e3c0e14283f80688922cd0adf04b1f0" href="https://bashdb.sourceforge.net/remake/" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">Remake - GNU Make with comprehensible tracing and a debugger</div><div class="notion-bookmark-description">remake is an enahanced version of GNU Make that adds
improved error reporting, better tracing, profiling and a debugger.</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-text">https://bashdb.sourceforge.net/remake/</div></div></div></a></div><div class="notion-text notion-block-2e3c0e14283f8055bddee6a1b5e94fd8">以一个简单的 Makefile 为例子：</div><div class="notion-text notion-block-2e3c0e14283f8009b9e3f86916b5e74d">终端执行<code class="notion-inline-code">remake -x</code>或者<code class="notion-inline-code">remake --trace</code> 可展示 Makefile 的详细执行流程：</div><div class="notion-text notion-block-2e3c0e14283f8059a658d6538adfd5b9">如果执行指令时报错，会打印出更详细的错误信息：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2e3c0e14283f80e88565e5462c158478"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ab297c38b-118e-4856-9144-5aaf46a7ebb9%3Aimage.png?table=block&amp;id=2e3c0e14-283f-80e8-8565-e5462c158478&amp;t=2e3c0e14-283f-80e8-8565-e5462c158478" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-31bc0e14283f807394f3c130af1d539a">如果需要更加精简的日志输出，执行：</div><div class="notion-text notion-block-31bc0e14283f80259333edf4568f9c2c">这将会使用 make 基础调试模式，并静默 make 时执行的 shell 命令。</div><hr class="notion-hr notion-block-31bc0e14283f80a89300eefd1679c561"/><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-2e3c0e14283f80b283b4d86a7b2b2dcd" data-id="2e3c0e14283f80b283b4d86a7b2b2dcd"><span><div id="2e3c0e14283f80b283b4d86a7b2b2dcd" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2e3c0e14283f80b283b4d86a7b2b2dcd" title="单步调试"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">单步调试</span></span></h2><div class="notion-text notion-block-2e3c0e14283f800eab1dccaa14f099ba">remake 可以像 gdb 工具调试 C 语言程序一样对 Makefile 执行单步调试，输入：</div><div class="notion-text notion-block-2e3c0e14283f8094aeadf903411c576d">即可进入单步调试状态，支持打印变量、查看调用栈、设置断点等，大部分指令和 gdb 是一样的，不过多赘述，可查看常用指令帮助。以下有几个除了<code class="notion-inline-code">step</code>和<code class="notion-inline-code">next</code>的常用指令：</div><ul class="notion-list notion-list-disc notion-block-31bc0e14283f80ed8e2ce7acd70f5c8a"><li><code class="notion-inline-code">expand</code> (<code class="notion-inline-code">x</code>) ——原地展开变量</li></ul><ul class="notion-list notion-list-disc notion-block-31bc0e14283f801b91aaf4647c080877"><li><code class="notion-inline-code">target [ Target-NAME ] [ info1 [ info2 … ] ]</code>——查询目标，可以查看自动变量等信息</li></ul><ul class="notion-list notion-list-disc notion-block-31bc0e14283f80dc9b1ad3d0d3ff62a3"><li><code class="notion-inline-code">where, backtrace, bt, T</code> ——查看当前调用栈</li></ul><hr class="notion-hr notion-block-31bc0e14283f80558a6be54879523bcd"/><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-2e3c0e14283f80aebf11cd0ede54fe09" data-id="2e3c0e14283f80aebf11cd0ede54fe09"><span><div id="2e3c0e14283f80aebf11cd0ede54fe09" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2e3c0e14283f80aebf11cd0ede54fe09" title="可视化"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">可视化</span></span></h2><div class="notion-text notion-block-2e3c0e14283f801580dbeed740c43766">首先清理旧的构建，然后执行上述命令打开报告，可以看见树状图展现了清晰的构建依赖关系：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2e3c0e14283f80d1b313d2ab89589a50"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Af6ff8ca8-10c4-4265-85d9-12cbe39797a1%3Aimage.png?table=block&amp;id=2e3c0e14-283f-80d1-b313-d2ab89589a50&amp;t=2e3c0e14-283f-80d1-b313-d2ab89589a50" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2e3c0e14283f80889372feb51685e10c"><span class="notion-inline-underscore">顺带</span>，Kcachegrind 也可以查看 Valgrind 平台的性能分析工具 Callgrind 生成的报告：</div><ul class="notion-list notion-list-disc notion-block-2e3c0e14283f80869f7cf209bacba6a4"><li>记录程序运行时的函数调用层次关系</li></ul><ul class="notion-list notion-list-disc notion-block-2e3c0e14283f80919161d8435ddcafc8"><li>执行指令的个数、指令与源码行的对应关系</li></ul><ul class="notion-list notion-list-disc notion-block-2e3c0e14283f8066823ff1f0c279e810"><li>时间占用的性能分析，执行哪个函数任务花费的相对时间最多</li></ul><div class="notion-text notion-block-2e3c0e14283f807ba0bbd293c008e54e">……</div><hr class="notion-hr notion-block-2e3c0e14283f80e99518ed41f4ea721f"/><div class="notion-text notion-block-2e3c0e14283f80c08709d333f6132071">以 ysyx-workbench 的 NEMU 运行分析为例子：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2e3c0e14283f80eb80bef79e33484722"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A936f73b4-630b-4cee-ac1e-c22c124fd3c3%3Aimage.png?table=block&amp;id=2e3c0e14-283f-80eb-80be-f79e33484722&amp;t=2e3c0e14-283f-80eb-80be-f79e33484722" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2e3c0e14283f800fae3eed88aa4a5752"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ac0bd629a-f7fc-4feb-9525-388e7fb3de00%3Aimage.png?table=block&amp;id=2e3c0e14-283f-800f-ae3e-ed88aa4a5752&amp;t=2e3c0e14-283f-800f-ae3e-ed88aa4a5752" alt="notion image" loading="lazy" decoding="async"/></div></figure><hr class="notion-hr notion-block-31bc0e14283f80dfa01ee1bd0b108825"/><h2 class="notion-h notion-h1 notion-gray_background notion-h-indent-0 notion-block-2e3c0e14283f80819901fcb648b31fa8" data-id="2e3c0e14283f80819901fcb648b31fa8"><span><div id="2e3c0e14283f80819901fcb648b31fa8" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2e3c0e14283f80819901fcb648b31fa8" title="参考资料"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">参考资料</span></span></h2><div class="notion-row"><a class="notion-bookmark notion-block-2e3c0e14283f80dc86f0d72932a6f6ee" href="https://lazybing.github.io/blog/2019/04/15/profiler/" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">性能优化之vallgrind之callgrind分析瓶颈 - 懒人李冰</div><div class="notion-bookmark-description">Callgrind 概述 主要功能 基本使用方法 高级使用方法 生成多个 profile 文件 限制收集事件的范围 Callgrind 命令行选项 dump 生成选项 activity 选项 data collection 选项 cost entity separations 选项 …</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Flazybing.github.io%2Ffavicon.png?table=block&amp;id=2e3c0e14-283f-80dc-86f0-d72932a6f6ee&amp;t=2e3c0e14-283f-80dc-86f0-d72932a6f6ee" alt="性能优化之vallgrind之callgrind分析瓶颈 - 懒人李冰" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://lazybing.github.io/blog/2019/04/15/profiler/</div></div></div></a></div><div class="notion-row"><a class="notion-bookmark notion-block-2e3c0e14283f8076b38cc5c84ea2588a" href="https://kcachegrind.github.io/html/Home.html" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">KCachegrind</div><div class="notion-bookmark-description">Callgrind uses runtime instrumentation via the Valgrind
framework for its cache simulation and call-graph generation.
This way, even shared libraries and
dynamically opened plugins can be profiled.
The data files generated by Callgrind can be loaded into KCachegrind for
browsing the performance results. But there is also a command line tool
in the package to get ASCII reports from data files
without the need to use KCachegrind.</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-text">https://kcachegrind.github.io/html/Home.html</div></div></div></a></div><div class="notion-row"><a class="notion-bookmark notion-block-2e3c0e14283f8021acf1f2b34abd6abb" href="https://bashdb.sourceforge.net/remake/" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">Remake - GNU Make with comprehensible tracing and a debugger</div><div class="notion-bookmark-description">remake is an enahanced version of GNU Make that adds
improved error reporting, better tracing, profiling and a debugger.</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-text">https://bashdb.sourceforge.net/remake/</div></div></div></a></div><div class="notion-blank notion-block-2e3c0e14283f8061b0afd7f1e4898947"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[仿真过程中的竞态条件]]></title>
            <link>http://hyacimond.top/digital/simulation</link>
            <guid>http://hyacimond.top/digital/simulation</guid>
            <pubDate>Fri, 10 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Verilog 仿真中的事件调度、阻塞/非阻塞赋值与竞态条件]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-288c0e14283f806db915f83570aef901"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-orange_background_co notion-block-8be6c6b6b4dd45fb8658d5e9a88c11ef"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="🤔">🤔</span></div><div class="notion-callout-text"><div class="notion-text notion-block-65bc898497154315b2d37e370769e17b">本文将讨论一个很容易困惑 Verilog 新手的问题：在行为级仿真中，如果 testbench 恰好在时钟边沿所在的同一仿真时刻修改输入，时序逻辑究竟会采到修改前的值，还是修改后的值？</div></div></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-288c0e14283f8021af7cc57389c377f0" data-id="288c0e14283f8021af7cc57389c377f0"><span><div id="288c0e14283f8021af7cc57389c377f0" class="notion-header-anchor"></div><a class="notion-hash-link" href="#288c0e14283f8021af7cc57389c377f0" title="📝 问题导入"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📝 问题导入</span></span></h2><div class="notion-text notion-block-288c0e14283f8076ab8ceb7a3022b951">以下面模块为例。该模块实现奇偶校验功能：每当时钟上升沿采样到 <code class="notion-inline-code">in</code> 为高电平时，<code class="notion-inline-code">odd</code> 翻转一次：</div><div class="notion-text notion-block-288c0e14283f8016bbdfecc4e7191fa3">testbench 关键逻辑如下：</div><div class="notion-text notion-block-288c0e14283f80439c77ff849d437d48">使用 Icarus Verilog 仿真后，可以得到如下波形：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-288c0e14283f80788fb9e267d6ebe885"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Afaad26a0-9d5d-4014-baa8-952d0d34b1a4%3Aimage.png?table=block&amp;id=288c0e14-283f-8078-8fb9-e267d6ebe885&amp;t=288c0e14-283f-8078-8fb9-e267d6ebe885" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-288c0e14283f80deaf50c1dda90c7473">从波形上看，<code class="notion-inline-code">in</code> 和 <code class="notion-inline-code">odd</code> 似乎在同一个时刻发生变化：<code class="notion-inline-code">in</code> 被置为高电平后，<code class="notion-inline-code">odd</code> 也在该时钟沿完成了翻转。</div><div class="notion-text notion-block-3d4da4970f09426ba18386e93b68df09">但这里需要注意，时序逻辑并不是固定采样“前一个周期的值”，而是在当前有效时钟沿采样该时刻所能观察到的输入值。现在的问题是：testbench 对 <code class="notion-inline-code">in</code> 的阻塞赋值与 DUT 的 <code class="notion-inline-code">always @(posedge clk)</code> 在同一仿真时刻被触发，二者究竟谁先执行？</div><div class="notion-callout notion-gray_background_co notion-block-288c0e14283f800d85f2c40209e1a715"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="⚠️">⚠️</span></div><div class="notion-callout-text"><div class="notion-text notion-block-288c0e14283f805d94abdba7ee208cb8">在不添加显式延迟或回标时序信息的 RTL 行为级仿真中，通常不会体现门传播延迟、布线延迟以及触发器的 clock-to-Q 延迟。因此，不同信号可能在波形中显示为同一个仿真时刻发生变化。</div><div class="notion-text notion-block-c10d9e75764d43bd9ddcd0218654c82c">但这种“同一时刻”并不代表它们在仿真器内部同时完成更新：同一个仿真时刻还会被划分为不同的事件调度区域。delta cycle 只是仿真器用于组织零时间事件的调度概念，不对应任何真实的物理时间，也不能用于表示建立时间或保持时间。</div><div class="notion-text notion-block-b7cafa282b124612a8990878632b4a48">在真实同步电路中，输入数据应在时钟有效沿前后分别满足建立时间和保持时间要求，而不是在时钟沿附近任意跳变。<div class="notion-text-children"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-288c0e14283f8025a977c0a4368b6aef"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:644.5667114257812px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A2521f00c-265e-4793-9870-b06257a59599%3Aimage.png?table=block&amp;id=288c0e14-283f-8025-a977-c0a4368b6aef&amp;t=288c0e14-283f-8025-a977-c0a4368b6aef" alt="notion image" loading="lazy" decoding="async"/></div></figure></div></div></div></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-288c0e14283f8096a0b7e7ccbdb99474" data-id="288c0e14283f8096a0b7e7ccbdb99474"><span><div id="288c0e14283f8096a0b7e7ccbdb99474" class="notion-header-anchor"></div><a class="notion-hash-link" href="#288c0e14283f8096a0b7e7ccbdb99474" title="🤗 阻塞/非阻塞赋值"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">🤗 阻塞/非阻塞赋值</span></span></h2><blockquote class="notion-quote notion-block-288c0e14283f80cbbd8ddfd438c1e944"><div>A blocking procedural assignment statement shall be executed before the execution of the statements that follow it in a sequential block (see 9.8.1). A blocking procedural assignment statement shall not prevent the execution of statements that follow it in a parallel block (see 9.8.2)</div><div class="notion-text notion-block-288c0e14283f80ea987dfba749096163">The nonblocking procedural assignment allows assignment scheduling without blocking the procedural flow. The nonblocking procedural assignment statement can be used whenever several variable assignments within the same time step can be made without regard to order or dependence upon each other.</div><div class="notion-text notion-block-288c0e14283f80d58621fef28aa66ca5">—— IEEE Std 1364-2005，procedural assignment 相关章节</div></blockquote><div class="notion-text notion-block-288c0e14283f805ea4bae87fa9b4fe3e">总结一下：</div><ul class="notion-list notion-list-disc notion-block-288c0e14283f80ecb140de166d1d6044"><li><code class="notion-inline-code">=</code> 是阻塞赋值。在同一个顺序过程块中，当前赋值完成后，才会继续执行该语句之后的过程语句；因此，后续语句读取到的是已经更新的左值。</li></ul><div class="notion-text notion-block-288c0e14283f80af9d0cc06054eeeb08">需要注意，“阻塞”描述的是仿真过程中的程序执行语义，并不表示综合后的实际硬件会像软件一样逐条等待执行。综合工具最终生成什么电路，取决于赋值所在的过程块、敏感条件以及整个 RTL 结构。</div><div class="notion-text notion-block-9fae1d303b2b4a2c8318886b87a21a85">例如，级联加法器形成的进位链需要经过一定的物理传播时间才能稳定，但这是组合逻辑传播延迟造成的，而不是阻塞赋值语义本身造成的。在 <code class="notion-inline-code">fork...join</code> 创建的多个并行进程之间，一个进程中的阻塞赋值不会阻塞其他并行进程。</div><ul class="notion-list notion-list-disc notion-block-1e088def25f64c259612f75b86a31b29"><li><code class="notion-inline-code">&lt;=</code> 是非阻塞赋值。语句执行时会先计算右值，然后将左值的更新安排到非阻塞赋值更新区域（NBA region）。如果没有显式延迟，这次更新仍发生在当前仿真时刻，只是晚于当前 Active 区域中的事件，而不是进入下一个时钟周期，也不一定进入下一个仿真时间点。</li></ul><div class="notion-text notion-block-74e354f73de2481ca607d1d425529bf1">非阻塞赋值被广泛用于时钟驱动的时序逻辑中，可以让同一个时钟沿触发的多个寄存器先读取旧值，再统一完成状态更新，从而降低代码执行顺序带来的影响。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-288c0e14283f8019a38ac3ee2302e216" data-id="288c0e14283f8019a38ac3ee2302e216"><span><div id="288c0e14283f8019a38ac3ee2302e216" class="notion-header-anchor"></div><a class="notion-hash-link" href="#288c0e14283f8019a38ac3ee2302e216" title="⏱️ 事件调度模型"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">⏱️ 事件调度模型</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-288c0e14283f8074b9e6d2ae2dd9d763" data-id="288c0e14283f8074b9e6d2ae2dd9d763"><span><div id="288c0e14283f8074b9e6d2ae2dd9d763" class="notion-header-anchor"></div><a class="notion-hash-link" href="#288c0e14283f8074b9e6d2ae2dd9d763" title="🍀 time slot 和 delta cycle"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">🍀 time slot 和 delta cycle</span></span></h3><ul class="notion-list notion-list-disc notion-block-9d0d7921be494c45971b46d6954a2931"><li><b>仿真时间单位与精度</b>： <code class="notion-inline-code">timescale </code>用于规定延迟数值的时间单位和舍入精度。</li></ul><ul class="notion-list notion-list-disc notion-block-3a0c0e14283f80aebfc4d3203b65a411"><li><b>time slot</b>：指某一个确定仿真时间点上需要处理的全部事件。例如仿真时间为 10 ns 时，该时刻触发和派生的事件都属于 10 ns 这个 time slot。众所周知，硬件电路是全并行的，而仿真器作为软件只能通过串行执行指令模拟硬件行为，由此诞生了仿真器的事件调度模型。每一个时间片内有一系列 <span class="notion-pink">按照一定顺序执行的关键区域（Region）</span>，在每一个时间片的出口将完成所有事件的更新。</li></ul><ul class="notion-list notion-list-disc notion-block-288c0e14283f80698945ee95e517d582"><li>delta cycle（零时间调度过程）：它不是一个比仿真精度更小的物理时间单位，而是仿真时间不推进时，对零延迟事件进行排序和处理的调度过程，通常无法在仿真波形中直接显示。在同一个 time-slot 内可以有无穷个 delta-cycle，delta-cycle 会跟随事件迭代而增加。例如在时间节点 100 ns，有 ABC 三个信号需要驱动，它们所处的有序关键区域的执行顺序为为 A → B → C，并且 ABC 各自的更新将<b><span class="notion-inline-underscore">不会再触发其他任何敏感事件</span></b>，则 ABC 完成更新的时间节点为 100ns + 0，100ns + 1，100 ns + 2。</li></ul><div class="notion-text notion-block-288c0e14283f80c288efdc9bbc6973a7">更详细的关于事件调度可参考：</div><div class="notion-text notion-block-289c0e14283f80cb9e4adc9ea5d7046a"><a class="notion-link" href="https://mp.weixin.qq.com/s?__biz=MzUxNDM1MTAxNg==&amp;mid=2247483863&amp;idx=1&amp;sn=8f202743ecd2223bec454dd2e2d74692&amp;chksm=f84e0db5ce5550dcc27c06c7ec39d9e71b7406d3c4104afd2223f356dfb209111fc59b009a4e&amp;app_lang=zh-CN#rd" target="_blank" rel="noopener noreferrer">详解SystemVerilog中time slot的调度</a></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-288c0e14283f80c2be3ae9b95e5c43b7"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:572.9888916015625px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Aae8b7c47-698e-4275-a754-3f8e3b703baf%3Aimage.png?table=block&amp;id=288c0e14-283f-80c2-be3a-e9b95e5c43b7&amp;t=288c0e14-283f-80c2-be3a-e9b95e5c43b7" alt="notion image" loading="lazy" decoding="async"/></div></figure><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-288c0e14283f807d9493f7bb38c94e6b" data-id="288c0e14283f807d9493f7bb38c94e6b"><span><div id="288c0e14283f807d9493f7bb38c94e6b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#288c0e14283f807d9493f7bb38c94e6b" title="🔍 仿真分析"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">🔍 仿真分析</span></span></h3><div class="notion-text notion-block-288c0e14283f80259ddae024c8f5f827">为便于理解，可以认为普通过程中的阻塞赋值事件通常在 Active 区域执行，而非阻塞赋值对左值的更新被安排到 NBA 区域，NBA 更新在同一 time slot 中晚于 Active 区域事件。如果 NBA 更新又触发了新的组合逻辑事件，仿真器仍可能在当前 time slot 内继续处理后续事件。对于先前的 testbench：</div><div class="notion-text notion-block-288c0e14283f80be9aa0e09add22f004">我们可以推测，以<code class="notion-inline-code">clk</code>信号时间节点为基准，<code class="notion-inline-code">in</code>首先完成更新，然后对<code class="notion-inline-code">odd</code>进行更新，此时条件满足，<code class="notion-inline-code">odd</code>完成一次翻转。由于波形查看器通常不会展开同一 time slot 内的各个调度区域，在波形上表现出<code class="notion-inline-code">in</code>和<code class="notion-inline-code">odd</code>同步变化的现象。这与 iverilog 的仿真波形相符。</div><div class="notion-text notion-block-288c0e14283f807ab636f8e693d65ba1">因此，改为<code class="notion-inline-code">in &lt;= seq[i];</code> 则会把 <code class="notion-inline-code">in</code> 的左值更新安排到当前 time slot 的 NBA 区域，因此 DUT 在 Active 区域读取到的通常是 <code class="notion-inline-code">in</code> 更新前的旧值，此时条件不满足，<code class="notion-inline-code">odd</code>要直到下一个时钟上升沿才会更新。</div><div class="notion-callout notion-yellow_background_co notion-block-288c0e14283f80808f1cd2ed54d56741"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text"><div class="notion-text notion-block-288c0e14283f800aab77d3035e067384">这样的调度非常符合真实硬件电路。对于组合逻辑，理想情况下是立即响应电路的变化，以至于上述的行为看起来像是在时钟沿到来前一刻就已经完成了更新。而时序逻辑通常考虑到寄存器 D 到 Q 有比组合逻辑更大的延迟，因此上述行为看起来像是在时钟沿到来之后才更新。对于一个同步信号，后者更符合现实情况。</div></div></div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-288c0e14283f801c95a1d47b21dbaa82" data-id="288c0e14283f801c95a1d47b21dbaa82"><span><div id="288c0e14283f801c95a1d47b21dbaa82" class="notion-header-anchor"></div><a class="notion-hash-link" href="#288c0e14283f801c95a1d47b21dbaa82" title="🔍 行为级仿真验证"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">🔍 行为级仿真验证</span></span></h3><div class="notion-text notion-block-288c0e14283f80948e0fecbd2b984414">Modelsim仿真结果：</div><div class="notion-text notion-block-288c0e14283f80d99b1acfd59776fd38">时间轴置于时钟上升沿，点击上方工具栏 Expanded Time Deltas Mode → Expand Time At Active Cursor。这样可以清晰查看 delta-cycle 的情况，实际开发中无需刻意关心仿真器复杂的底层事件调度。</div><ol start="1" class="notion-list notion-list-numbered notion-block-288c0e14283f809a809ecd1c955e7883" style="list-style-type:decimal"><li>“=” 赋值。</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-289c0e14283f804e8e86fc3278a93415"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:567.95556640625px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A8c9c51df-ba37-4126-abad-6d22c5dd794d%3Aimage.png?table=block&amp;id=289c0e14-283f-804e-8e86-fc3278a93415&amp;t=289c0e14-283f-804e-8e86-fc3278a93415" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="1" class="notion-list notion-list-numbered notion-block-289c0e14283f80859399ee370b2eec98" style="list-style-type:decimal"><li>“ &lt;=” 赋值。</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-289c0e14283f805cba5bd356b29df8ec"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:567.95556640625px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A96e07acd-36b4-4069-81a0-4dc6beb96102%3Aimage.png?table=block&amp;id=289c0e14-283f-805c-ba5b-d356b29df8ec&amp;t=289c0e14-283f-805c-ba5b-d356b29df8ec" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-callout notion-yellow_background_co notion-block-289c0e14283f80dfb9fbfbf4b91ba318"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text"><div class="notion-text notion-block-289c0e14283f80249de6de90979c6cb4">对代码进行如下修正也可以产生 delta-cycle：</div></div></div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-288c0e14283f8049a195f88c9314b2c2" data-id="288c0e14283f8049a195f88c9314b2c2"><span><div id="288c0e14283f8049a195f88c9314b2c2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#288c0e14283f8049a195f88c9314b2c2" title="💡 综合后仿真"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">💡 综合后仿真</span></span></h3><div class="notion-text notion-block-288c0e14283f800d8506e74400e65bc9">综合后仿真分为功能性和时序性仿真，前者只是将综合后生成的网表信号添加至仿真中，后者继续引入了软件时序评估，更贴近真实情况。同时，我们可以看到，在引入物理延迟后，两种写法在波形上实际没有差别。</div><div class="notion-callout notion-red_background_co notion-block-288c0e14283f802eacc3f5a4b50b927b"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="❗">❗</span></div><div class="notion-callout-text"><div class="notion-text notion-block-288c0e14283f803dadb6e0ae69edb0cc">注意，综合后仿真引入会默认添加 glbl.v 文件，内含一个持续 100 ns 的全局复位信号 GSR，建议将有效输入信号延迟相应时间输入。参考 AMD 官方手册：</div><div class="notion-row"><a class="notion-bookmark notion-block-289c0e14283f80c682f7db33564cabf4" href="https://docs.amd.com/r/zh-CN/ug900-vivado-logic-simulation/%E4%BD%BF%E7%94%A8%E5%85%A8%E5%B1%80%E4%B8%89%E6%80%81%E5%8F%8A%E5%85%A8%E5%B1%80%E7%BD%AE%E4%BD%8D%E5%92%8C%E5%A4%8D%E4%BD%8D%E4%BF%A1%E5%8F%B7" target="_blank" rel="noopener noreferrer"><div><div class="notion-bookmark-title">AMD Technical Information Portal</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fdocs.amd.com%2Ffavicon.ico?table=block&amp;id=289c0e14-283f-80c6-82f7-db33564cabf4&amp;t=289c0e14-283f-80c6-82f7-db33564cabf4" alt="AMD Technical Information Portal" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://docs.amd.com/r/zh-CN/ug900-vivado-logic-simulation/%E4%BD%BF%E7%94%A8%E5%85%A8%E5%B1%80%E4%B8%89%E6%80%81%E5%8F%8A%E5%85%A8%E5%B1%80%E7%BD%AE%E4%BD%8D%E5%92%8C%E5%A4%8D%E4%BD%8D%E4%BF%A1%E5%8F%B7</div></div></div></a></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-289c0e14283f801d8f50e9648a5621af"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:624px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A9541033f-6cdb-4acd-8d5b-51238d2a94fb%3Axvd1565607197023.svg?table=block&amp;id=289c0e14-283f-801d-8f50-e9648a5621af&amp;t=289c0e14-283f-801d-8f50-e9648a5621af" alt="notion image" loading="lazy" decoding="async"/></div></figure></div></div><ol start="1" class="notion-list notion-list-numbered notion-block-289c0e14283f802ab1e3cb883a6d2361" style="list-style-type:decimal"><li>“=” 赋值。</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-289c0e14283f803290c5c31719f0d1ec"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A30360d1e-bcb8-4cd2-8c16-236ad7ccc781%3Aimage.png?table=block&amp;id=289c0e14-283f-8032-90c5-c31719f0d1ec&amp;t=289c0e14-283f-8032-90c5-c31719f0d1ec" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="2" class="notion-list notion-list-numbered notion-block-289c0e14283f8051a412e73fb2e49f08" style="list-style-type:decimal"><li>“&lt;=” 赋值。</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-289c0e14283f80678a7af85b13f795aa"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ac2039799-c375-48e3-9f42-6f6d20ec6f74%3Aimage.png?table=block&amp;id=289c0e14-283f-8067-8a7a-f85b13f795aa&amp;t=289c0e14-283f-8067-8a7a-f85b13f795aa" alt="notion image" loading="lazy" decoding="async"/></div></figure><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-289c0e14283f80b389b6fd0db5c0699e" data-id="289c0e14283f80b389b6fd0db5c0699e"><span><div id="289c0e14283f80b389b6fd0db5c0699e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#289c0e14283f80b389b6fd0db5c0699e" title="🔗 参考文章"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">🔗 参考文章</span></span></h3><ul class="notion-list notion-list-disc notion-block-289c0e14283f80608e37dc62e231d0d5"><li><a class="notion-link" href="https://fpga.eetrend.com/content/2018/100014656.html#:~:text=%E5%9C%A8%E4%BB%BF%E7%9C%9F%E6%97%B6%E9%97%B4%E7%9A%840%E6%97%B6%E5%88%BB%EF%BC%8C%E5%B0%86%E6%89%80%E6%9C%89%E7%9A%84%E8%AE%BE%E8%AE%A1%E8%BE%93%E5%85%A5%E5%88%9D%E5%A7%8B%E5%8C%96%E4%BD%8D%E4%B8%BA%E4%B8%80%E4%B8%AA%E7%A1%AE%E5%AE%9A%E7%9A%84%E5%80%BC%EF%BC%9B,%E5%9C%A8%E7%BB%BC%E5%90%88%E5%90%8E%E5%92%8C%E5%AE%9E%E7%8E%B0%E5%90%8E%E7%9A%84%E6%97%B6%E5%BA%8F%E4%BB%BF%E7%9C%9F%E4%B8%AD%EF%BC%8C%E4%BC%9A%E8%87%AA%E5%8A%A8%E8%A7%A6%E5%8F%91%E5%85%A8%E5%B1%80%E7%BD%AE%E4%BD%8D%2F%E5%A4%8D%E4%BD%8D%E8%84%89%E5%86%B2%EF%BC%88GSR%EF%BC%89%EF%BC%8C%E8%BF%99%E4%BC%9A%E8%AE%A9%E6%89%80%E6%9C%89%E7%9A%84%E5%AF%84%E5%AD%98%E5%99%A8%E5%9C%A8%E4%BB%BF%E7%9C%9F%E7%9A%84%E5%89%8D100ns%E5%86%85%E9%94%81%E5%AE%9A%E5%85%B6%E5%80%BC%E3%80%82%20%E5%9B%A0%E6%AD%A4%E5%9C%A8100ns%E4%B9%8B%E5%90%8E%E5%86%8D%E8%B5%8B%E5%80%BC%E6%BF%80%E5%8A%B1%E6%95%B0%E6%8D%AE%EF%BC%9B" target="_blank" rel="noopener noreferrer">Vivado使用技巧（18）——仿真功能概述</a></li></ul><ul class="notion-list notion-list-disc notion-block-289c0e14283f80e18df4d948ddd66af9"><li><a class="notion-link" href="https://zhuanlan.zhihu.com/p/482158995" target="_blank" rel="noopener noreferrer">https://zhuanlan.zhihu.com/p/482158995</a></li></ul><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-bc0d8a7843294a6587141a360951bd38" data-id="bc0d8a7843294a6587141a360951bd38"><span><div id="bc0d8a7843294a6587141a360951bd38" class="notion-header-anchor"></div><a class="notion-hash-link" href="#bc0d8a7843294a6587141a360951bd38" title="🛠️ 如何避免 testbench 竞态"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">🛠️ 如何避免 testbench 竞态</span></span></h2><div class="notion-text notion-block-1938927f0f9b42bfaceaaae092c3d12f">仅仅将 testbench 中的所有赋值都替换为 <code class="notion-inline-code">&lt;=</code>，并不是解决竞态问题的通用方法。更直接的原则是：<b>testbench 不要在 DUT 的采样边沿所在的同一个调度区域中修改输入。</b></div><div class="notion-text notion-block-a93b35e121b140afbbb68eaf8ca81c5a">对于这个简单例子，可以让 testbench 在时钟下降沿修改输入，使输入在下一个上升沿到来前已经稳定：</div><div class="notion-text notion-block-49b58ee684004999820bd599a4295e2e">这里仍然可以使用阻塞赋值，因为 testbench 在下降沿驱动输入，而 DUT 在上升沿采样输入，二者在时间上已经分离。</div><div class="notion-text notion-block-7f1bf37705bc41478373e73e7dc61f3c">同样需要注意，复位信号也不应恰好在 DUT 的有效采样边沿使用阻塞赋值撤销。对于更复杂的 SystemVerilog 验证环境，还可以使用 clocking block 明确 testbench 的采样时刻和驱动时刻，从语言层面减少 testbench 与 DUT 之间的竞态。</div><div class="notion-callout notion-blue_background_co notion-block-c0cde5b09ad242e3a50b70b87300ea0b"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="📌">📌</span></div><div class="notion-callout-text"><div class="notion-text notion-block-0ecf7dc1b72043b78fde7356e65a4db4"><b>本文结论</b></div><ol start="1" class="notion-list notion-list-numbered notion-block-d2a48b1362d14c01b4605b2dc646bd86" style="list-style-type:decimal"><li>同一个仿真时间点不代表事件真正“同时”完成，time slot 内部仍存在事件调度顺序。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-a7cb9d967bd64067b80765b6cdd352db" style="list-style-type:decimal"><li>无显式延迟的非阻塞赋值通常在当前 time slot 的 NBA 区域更新，不等于延迟一个时钟周期。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-afe3dee823dc4628ba89650641bd7620" style="list-style-type:decimal"><li>testbench 与 DUT 在同一个采样边沿修改、读取同一信号时，可能形成竞态，结果不应依赖仿真器的进程执行顺序。</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-3f3adfc79d6d4915a4d612f758185a96" style="list-style-type:decimal"><li>简单 testbench 可以在非采样边沿驱动输入；复杂 SystemVerilog 验证环境可以使用 clocking block。</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-2efe6b0dfb0041799caf33259f0d43a6" style="list-style-type:decimal"><li>delta cycle 是仿真器的零时间调度概念，不能与真实电路的传播延迟、建立时间和保持时间混为一谈。</li></ol></div></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Linux 环境下 Vivado 与 Modelsim 联合仿真无法自动弹出界面解决方案]]></title>
            <link>http://hyacimond.top/tools/vivadomodelsim</link>
            <guid>http://hyacimond.top/tools/vivadomodelsim</guid>
            <pubDate>Fri, 10 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Linux环境下Vivado与Modelsim联合仿真无法自动弹出界面解决方案]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-288c0e14283f8010b502f42966fc113d"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-yellow_background_co notion-block-288c0e14283f806e8b34e3aec4db77ad"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text"><div class="notion-text notion-block-288c0e14283f801db764e315ffcee4af">本机环境：</div><ul class="notion-list notion-list-disc notion-block-288c0e14283f80e59ea2fe4ba965671c"><li>Ubuntu 22.04 LTS</li></ul><ul class="notion-list notion-list-disc notion-block-288c0e14283f80c7b4bec7e0a088e493"><li>Vivado 2023.1</li></ul><ul class="notion-list notion-list-disc notion-block-288c0e14283f80058667eaf376ce195b"><li>Modelsim 2020.4</li></ul></div></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-288c0e14283f807e9313d6c3b37e4b82" data-id="288c0e14283f807e9313d6c3b37e4b82"><span><div id="288c0e14283f807e9313d6c3b37e4b82" class="notion-header-anchor"></div><a class="notion-hash-link" href="#288c0e14283f807e9313d6c3b37e4b82" title="配置路径"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">配置路径</span></span></h2><ol start="1" class="notion-list notion-list-numbered notion-block-288c0e14283f807d8a4fc035029f474a" style="list-style-type:decimal"><li>进入 Settings → 3rd Party Simulators，填写 Modelsim 安装路径。</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-288c0e14283f8079b417e5bf561f66e9"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:576px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A20c25402-18d5-4411-b550-c33033d49c84%3Aimage.png?table=block&amp;id=288c0e14-283f-8079-b417-e5bf561f66e9&amp;t=288c0e14-283f-8079-b417-e5bf561f66e9" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="2" class="notion-list notion-list-numbered notion-block-288c0e14283f8009bb86c168d885b675" style="list-style-type:decimal"><li>Tools → Compile Simulation Libraries…</li><ol class="notion-list notion-list-numbered notion-block-288c0e14283f8009bb86c168d885b675" style="list-style-type:lower-alpha"><ul class="notion-list notion-list-disc notion-block-288c0e14283f80c6b133d09262400a17"><li>选择需要的器件（否则编译时间会很长）</li></ul><ul class="notion-list notion-list-disc notion-block-288c0e14283f80bca9afcbaef2f3438e"><li>设置三条路径</li></ul><ul class="notion-list notion-list-disc notion-block-288c0e14283f802ba25fcf891d42b479"><li>勾选 Compile Xilinx IP</li></ul></ol></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-288c0e14283f809f9117d55987cf7fdf"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:576px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A390acaa8-5264-4703-a0bb-fa0b5f0e1496%3Aimage.png?table=block&amp;id=288c0e14-283f-809f-9117-d55987cf7fdf&amp;t=288c0e14-283f-809f-9117-d55987cf7fdf" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="3" class="notion-list notion-list-numbered notion-block-288c0e14283f80449f35cc69d55a3001" style="list-style-type:decimal"><li>编译完成后设置编译库路径，并在 Simulation 选项设置 Modelsim 为仿真器。</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-288c0e14283f8051a123e5191298ba12"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:528px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A71f31e96-b901-4d57-a1cb-9004d5c5c1b0%3Aimage.png?table=block&amp;id=288c0e14-283f-8051-a123-e5191298ba12&amp;t=288c0e14-283f-8051-a123-e5191298ba12" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-288c0e14283f806b8f92fa3e39ac3f30"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:528px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A2d90e8e5-16c3-4347-9e29-96ea4977d9fc%3Aimage.png?table=block&amp;id=288c0e14-283f-806b-8f92-fa3e39ac3f30&amp;t=288c0e14-283f-806b-8f92-fa3e39ac3f30" alt="notion image" loading="lazy" decoding="async"/></div></figure><hr class="notion-hr notion-block-288c0e14283f80378aaccde0cff982f6"/><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-288c0e14283f80fd83f0c12acca2fe0a" data-id="288c0e14283f80fd83f0c12acca2fe0a"><span><div id="288c0e14283f80fd83f0c12acca2fe0a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#288c0e14283f80fd83f0c12acca2fe0a" title="问题分析与解决"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">问题分析与解决</span></span></h2><div class="notion-text notion-block-288c0e14283f80289280c4db9b545fdf">启动仿真后 GUI 界面迟迟未弹出，TCL 命令界面如下：</div><div class="notion-text notion-block-288c0e14283f805881f0fdb0238fab15">Vivado 日志显示仿真流程已正常启动，所有设计文件编译无错误，但 ModelSim 程序（vsim）<b>迟迟未弹出界面或进入仿真</b>。在终端输入：</div><div class="notion-text notion-block-288c0e14283f805787f1fb60b0ae18bf">发现进程标识符 141630 的 vsim 进程实际上已经启动，但是无法启动 GUI 界面查看波形。</div><div class="notion-text notion-block-28ac0e14283f80ce8c2de6227348a12c">排查过程中修改 vivado.desktop 文件，改为<code class="notion-inline-code">terminal=true</code>后启动，再次运行仿真后，终端打印出一条错误信息：</div><div class="notion-text notion-block-28ac0e14283f80e5824ed7c690934104">显示缺少许可证的环境变量，于是尝试将 .bashrc 中的 Modelsim 相关的环境变量添加至 Vivado 的启动脚本中：</div><div class="notion-text notion-block-28ac0e14283f8062898ded5fb2a361f3">将下面三个环境变量添加到启动脚本：（修改为自己的路径）</div><div class="notion-text notion-block-28ac0e14283f80e88ccac2fbdff9e498">运行仿真后成功启动 Modelsim GUI 界面。</div><hr class="notion-hr notion-block-2e8c0e14283f80429022c3f501e8980b"/><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-288c0e14283f80cfbd3dc75b08dec794" data-id="288c0e14283f80cfbd3dc75b08dec794"><span><div id="288c0e14283f80cfbd3dc75b08dec794" class="notion-header-anchor"></div><a class="notion-hash-link" href="#288c0e14283f80cfbd3dc75b08dec794" title="其他解决方案"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">其他解决方案</span></span></h2><div class="notion-text notion-block-288c0e14283f80ac942ac066b0111003">Vivado 启动 ModelSim 进程时，实际上执行了一个 TCL 脚本，解决方法有两种，但核心都是使其<span class="notion-red"><b><span class="notion-inline-underscore">完整继承系统终端的环境。</span></b></span></div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-288c0e14283f803a977ff45f30064961" data-id="288c0e14283f803a977ff45f30064961"><span><div id="288c0e14283f803a977ff45f30064961" class="notion-header-anchor"></div><a class="notion-hash-link" href="#288c0e14283f803a977ff45f30064961" title="方法一：手动执行 simulate.sh"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">方法一：手动执行 simulate.sh</span></span></h3><div class="notion-text notion-block-288c0e14283f807ca973d400c06e8175">运行仿真后在 <code class="notion-inline-code">{PRJ_NAME}/{PRJ_NAME}.sim/sim_1/behav/</code> 目录下会生成 modelsim 文件夹，终端中运行以下命令即可启动 GUI 界面，届时会在该目录下生成 vsim.wlf 文件。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-288c0e14283f80a29093e3bde49c24ef"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A57d3951d-c825-4119-b285-80a3b42bbc16%3Aimage.png?table=block&amp;id=288c0e14-283f-80a2-9093-e3bde49c24ef&amp;t=288c0e14-283f-80a2-9093-e3bde49c24ef" alt="notion image" loading="lazy" decoding="async"/></div></figure><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-288c0e14283f801b8df4f78f6021a14e" data-id="288c0e14283f801b8df4f78f6021a14e"><span><div id="288c0e14283f801b8df4f78f6021a14e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#288c0e14283f801b8df4f78f6021a14e" title="方法二：终端界面启动 Vivado"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">方法二：终端界面启动 Vivado</span></span></h3><div class="notion-text notion-block-288c0e14283f80fdaa1cfbedb5981d1d">再次正常运行仿真即可启动 Modelsim 的 GUI 界面。</div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Vitis 2023.1 常见问题记录]]></title>
            <link>http://hyacimond.top/tools/vitis</link>
            <guid>http://hyacimond.top/tools/vitis</guid>
            <pubDate>Sat, 08 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Vitis 2023.1 常见问题记录]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-281c0e14283f804a9fd3fc76c8745c1e"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-281c0e14283f80c0b189c4b7b6286952" data-id="281c0e14283f80c0b189c4b7b6286952"><span><div id="281c0e14283f80c0b189c4b7b6286952" class="notion-header-anchor"></div><a class="notion-hash-link" href="#281c0e14283f80c0b189c4b7b6286952" title="运行到某处进入 DataAbort 向量"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">运行到某处进入 DataAbort 向量</span></span></h2><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-281c0e14283f80519587ca33e56a9ad1"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A10c170f6-3f49-4ac9-a51e-9010b37fcab4%3Acd6dadcafb55c656dcea70feb1ed6fe5.png?table=block&amp;id=281c0e14-283f-8051-9587-ca33e56a9ad1&amp;t=281c0e14-283f-8051-9587-ca33e56a9ad1" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-281c0e14283f80d5bb3cc011fdc43bc1">查看程序中是否定义了占用空间较大的变量，这通常会导致堆栈溢出，发生内存访问错误。</div><div class="notion-text notion-block-281c0e14283f8084b5b3d3931053eb5f">为该类变量在 DDR 独自划分一块合适大小的区间（<b>注意不要和其他地址空间重叠</b>）</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-281c0e14283f801c8129f4fc99de6631" data-id="281c0e14283f801c8129f4fc99de6631"><span><div id="281c0e14283f801c8129f4fc99de6631" class="notion-header-anchor"></div><a class="notion-hash-link" href="#281c0e14283f801c8129f4fc99de6631" title="Debug 启动超时报错，但仍能成功调试"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Debug 启动超时报错，但仍能成功调试</span></span></h2><div class="notion-text notion-block-281c0e14283f80ad9b0dd722d39f0e83"><a class="notion-link" href="https://adaptivesupport.amd.com/s/question/0D54U00005ZMjFaSAL/baremetal-vitis-project-error-xrtserverexe-error-illegal-option-?language=en_US" target="_blank" rel="noopener noreferrer">Baremetal Vitis project: ERROR xrt_server.exe: error: illegal option &#x27;-&#x27; (amd.com)</a></div><div class="notion-text notion-block-281c0e14283f80898308fb75e7b0b3c1">参考以上的 AMD Community 回答：</div><div class="notion-text notion-block-281c0e14283f801f848ce7a76f7386e6">这通常是启用系统级调试出现的问题：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-281c0e14283f8090a3c4d10da55fac7b"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A3816d5aa-52c6-46f4-8374-fc1f1edfe3e6%3Aimage.png?table=block&amp;id=281c0e14-283f-8090-a3c4-d10da55fac7b&amp;t=281c0e14-283f-8090-a3c4-d10da55fac7b" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-281c0e14283f80ecacc2df49ccba3bf8">解决方案是选择层次结构中想要调试的项目，使用 Single application debug</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-281c0e14283f809bb3f3e8f3ec6e8402"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Abb84bacc-c784-4bf1-8dc6-165da696644b%3Aimage.png?table=block&amp;id=281c0e14-283f-809b-b3f3-e8f3ec6e8402&amp;t=281c0e14-283f-809b-b3f3-e8f3ec6e8402" alt="notion image" loading="lazy" decoding="async"/></div></figure><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-281c0e14283f8071a99ec6272c360c31" data-id="281c0e14283f8071a99ec6272c360c31"><span><div id="281c0e14283f8071a99ec6272c360c31" class="notion-header-anchor"></div><a class="notion-hash-link" href="#281c0e14283f8071a99ec6272c360c31" title="找不到 BSP 文件"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">找不到 BSP 文件</span></span></h2><div class="notion-text notion-block-3cdc0e14283f8004801bfc02b73c20e1">Vitis 2018 版本会在 Application project 创建时自动生成一个 bsp 文件（Board Support Package）</div><div class="notion-callout notion-gray_background_co notion-block-3cdc0e14283f801fa61ef4fc7ce58dc5"><div class="notion-page-icon-inline notion-page-icon-span"><img class="notion-page-icon" src="https://www.notion.so/icons/light-bulb_green.svg?mode=light" alt="page icon" loading="lazy" decoding="async"/></div><div class="notion-callout-text"><div class="notion-text notion-block-3cdc0e14283f805c966fd5c767c701ef">BSP（Board Support Package）是一个软件包，它包含了特定硬件平台所需的基本驱动程序、库文件和工具。它的主要功能包括：</div><ul class="notion-list notion-list-disc notion-block-3cdc0e14283f805a97dec04e11bfd0f8"><li>为特定的硬件平台提供基础软件支持</li></ul><ul class="notion-list notion-list-disc notion-block-3cdc0e14283f803f82d6e16e5428a22d"><li>包含底层驱动程序（如 CPU、内存、外设等）</li></ul><ul class="notion-list notion-list-disc notion-block-3cdc0e14283f80bfb0c7d552fbaa01cf"><li>提供硬件初始化代码</li></ul><ul class="notion-list notion-list-disc notion-block-3cdc0e14283f80a49fc8ffbe5de53edc"><li>集成开发所需的库文件和API</li></ul><ul class="notion-list notion-list-disc notion-block-3cdc0e14283f80f2baf0cab4c6226800"><li>提供调试和开发工具支持</li></ul><div class="notion-text notion-block-3cdc0e14283f80058e22f4011519dc89">在 Xilinx Vitis 开发环境中，BSP 是连接硬件平台和应用程序的重要桥梁，它使开发者能够更容易地访问和控制硬件资源。</div></div></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3cdc0e14283f80eaab48f1546fa4dbea"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A76961fef-44fe-4ec9-b69d-bbbdaee6c01f%3A%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE_2025-01-12_003517.png?table=block&amp;id=3cdc0e14-283f-80ea-ab48-f1546fa4dbea&amp;t=3cdc0e14-283f-80ea-ab48-f1546fa4dbea" alt="来自 www.hellofpga.com" loading="lazy" decoding="async"/><figcaption class="notion-asset-caption">来自 www.hellofpga.com</figcaption></div></figure><div class="notion-text notion-block-3cdc0e14283f8082b048ec2ce1dea416">Vitis 2023.1 创建 Application 项目后文件树如下，操作流程为：</div><ol start="1" class="notion-list notion-list-numbered notion-block-3cdc0e14283f80b7b47ae4c8f088f7ab" style="list-style-type:decimal"><li>找到 platform.spr 文件（系统项目文件）并双击</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-3cdc0e14283f80f492d5e0eb0f223bd6" style="list-style-type:decimal"><li>右侧窗口单击 Board Support Package</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-3cdc0e14283f80d389cad678fff1304e" style="list-style-type:decimal"><li>Driver Libraries 中可以找到相关驱动文档和例程</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3cdc0e14283f8009b459e14fda87fc5a"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Add40f8aa-b9ee-4ca7-8ddc-58d62b9ace1b%3Aimage.png?table=block&amp;id=3cdc0e14-283f-8009-b459-e14fda87fc5a&amp;t=3cdc0e14-283f-8009-b459-e14fda87fc5a" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-3cdc0e14283f8064bc24e8ae723d23c4">此时可以选择导入例程，文件树中会增加一个对应资源文件夹</div><div class="notion-row notion-block-3cdc0e14283f80fe92aedb454243092f"><div class="notion-column notion-block-3cdc0e14283f8076a39ef80dca7400da" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3cdc0e14283f80d2a15fda5bd9ae7eb2"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A67a99202-756e-4232-8bbe-a56813bacf88%3Aimage.png?table=block&amp;id=3cdc0e14-283f-80d2-a15f-da5bd9ae7eb2&amp;t=3cdc0e14-283f-80d2-a15f-da5bd9ae7eb2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-3cdc0e14283f805b8aeee75dc5c7e8e7"> </div></div><div class="notion-spacer"></div><div class="notion-column notion-block-3cdc0e14283f8067bb75deaab455801a" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3cdc0e14283f8006bb33f2a6f0298192"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:715px"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Aa4d85c90-5c87-4297-8dd4-7e0e1a988a52%3Aimage.png?table=block&amp;id=3cdc0e14-283f-8006-bb33-f2a6f0298192&amp;t=3cdc0e14-283f-8006-bb33-f2a6f0298192" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-3cdc0e14283f807699a1fadba3c049ee" data-id="3cdc0e14283f807699a1fadba3c049ee"><span><div id="3cdc0e14283f807699a1fadba3c049ee" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3cdc0e14283f807699a1fadba3c049ee" title="导入自制 IP 问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">导入自制 IP 问题</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-3cdc0e14283f80e982bdccbd595d3209" data-id="3cdc0e14283f80e982bdccbd595d3209"><span><div id="3cdc0e14283f80e982bdccbd595d3209" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3cdc0e14283f80e982bdccbd595d3209" title="现象分析"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">现象分析</span></span></h3><div class="notion-text notion-block-3cdc0e14283f804b9034f477ebb52a53">使用 Vivado 时加入自定义 IP 核，使用编译生成的 xsa 文件构建 application 项目会有makefile报错</div><div class="notion-text notion-block-3cdc0e14283f80bf9d84ea4a62cacd25">系自定义IP核目录下  .\ip_repo\&lt;ip_name&gt;_1_0\drivers\&lt;ip_name&gt;_v1_0\src 下Makefile 文件导致编译报错问题，原因可能为官方希望希望用户为 IP 开发一套完整驱动后在编译入工程。直接在 Vitis 工程中修改 Makefile 有时并不能解决问题，笔者经尝试给出以下可行的解决方案</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-3cdc0e14283f807fb126edfa6c28c95d" data-id="3cdc0e14283f807fb126edfa6c28c95d"><span><div id="3cdc0e14283f807fb126edfa6c28c95d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3cdc0e14283f807fb126edfa6c28c95d" title="解决方案一"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">解决方案一</span></span></h3><div class="notion-text notion-block-3cdc0e14283f8084914fc57a912bb5b2"><a class="notion-link" href="https://adaptivesupport.amd.com/s/question/0D52E00006hppXKSAY/%E4%BD%BF%E7%94%A8%E8%87%AA%E5%AE%9A%E4%B9%89axi4lite%E5%A4%96%E8%AE%BEipvitis%E7%9A%84patform-build%E6%97%B6%E6%8A%A5%E9%94%99?language=zh_CN" target="_blank" rel="noopener noreferrer">使用自定义AXI4LITE外设IP，vitis的patform build时报错 (amd.com)</a></div><ol start="1" class="notion-list notion-list-numbered notion-block-3cdc0e14283f80ac9f3ec5b1857f8f17" style="list-style-type:decimal"><li>需要找到 BSP 文件修改 driver 的设置，进入 Modify BSP Settings</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3cdc0e14283f80efa9b4e91ca40d51c4"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A2325e2ff-4808-483f-8b94-21964e1ea8df%3Aimage.png?table=block&amp;id=3cdc0e14-283f-80ef-a9b4-e91ca40d51c4&amp;t=3cdc0e14-283f-80ef-a9b4-e91ca40d51c4" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="2" class="notion-list notion-list-numbered notion-block-3cdc0e14283f809e8d9ad24a726b693e" style="list-style-type:decimal"><li>将当前的 conponent 驱动设置为 none </li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3cdc0e14283f80929ef0ea8001360268"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A65e42035-30b7-44b8-810b-ef3c28ec5fdf%3Aimage.png?table=block&amp;id=3cdc0e14-283f-8092-9ef0-ea8001360268&amp;t=3cdc0e14-283f-8092-9ef0-ea8001360268" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="3" class="notion-list notion-list-numbered notion-block-3cdc0e14283f806db342f2e790800355" style="list-style-type:decimal"><li>重新编译整个项目。</li></ol><div class="notion-callout notion-gray_background_co notion-block-3cdc0e14283f805cb74af496782ee2a0"><div class="notion-page-icon-inline notion-page-icon-span"><img class="notion-page-icon" src="https://www.notion.so/icons/light-bulb_lightgray.svg?mode=light" alt="page icon" loading="lazy" decoding="async"/></div><div class="notion-callout-text"><div class="notion-text notion-block-3cdc0e14283f80dcb5ffc918573d5edc">使用 IP 创建向导创建的带 AXI4 通信接口的 IP 通常会自带一些 driver demo（包含与通信相关的宏函数，本质上是调用 <code class="notion-inline-code">Xil_In32()</code> 和 <code class="notion-inline-code">Xil_Out32()</code> 以及基地址和偏移量的宏定义），用户根据需要进行修改</div></div></div><hr class="notion-hr notion-block-3cdc0e14283f80deb956e1788ea0dfe7"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-3cdc0e14283f80dbbdcee0c03d8e33c5" data-id="3cdc0e14283f80dbbdcee0c03d8e33c5"><span><div id="3cdc0e14283f80dbbdcee0c03d8e33c5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3cdc0e14283f80dbbdcee0c03d8e33c5" title="解决方案二"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">解决方案二</span></span></h3><div class="notion-text notion-block-3cdc0e14283f80429e79cdcb0f937820"><a class="notion-link" href="https://adaptivesupport.amd.com/s/question/0D52E00006jpcvVSAQ/fatal-error-xparametersh-no-such-file-or-directoryxilinx-platform-definition-filexpfm-is-removed-after-building-the-project?language=en_US" target="_blank" rel="noopener noreferrer">https://adaptivesupport.amd.com/s/question/0D52E00006jpcvVSAQ/fatal-error-xparametersh-no-such-file-or-directoryxilinx-platform-definition-filexpfm-is-removed-after-building-the-project?language=en_US</a></div><div class="notion-text notion-block-3cdc0e14283f80b48c40ca03749f1b2e">参考 xilinx 官方论坛的解决方案</div><ol start="1" class="notion-list notion-list-numbered notion-block-3cdc0e14283f804bacbdc32a1f7a9ad1" style="list-style-type:decimal"><li>直接修改 .\ip_repo\&lt;ip_name&gt;_1_0\drivers\&lt;ip_name&gt;_v1_0\src 下的 Makefile 文件</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-3cdc0e14283f8035a064c8e686ce02e3" style="list-style-type:decimal"><li>修改 Makefile 文件</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-3cdc0e14283f80afafb2f474e2b4ce33" style="list-style-type:decimal"><li>进入 Vivado 工程 Refresh IP Catalog，更新 IP 目录</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3cdc0e14283f80a6974fcd1176370d14"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:44px"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A03a29c8d-af22-4758-b4e7-d8e439efaa10%3Aimage.png?table=block&amp;id=3cdc0e14-283f-80a6-974f-cd1176370d14&amp;t=3cdc0e14-283f-80a6-974f-cd1176370d14" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3cdc0e14283f806eb027e8ad7f68acce"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A6f85d9cc-1e2a-4962-8e33-85f6d78e1225%3Aimage.png?table=block&amp;id=3cdc0e14-283f-806e-b027-e8ad7f68acce&amp;t=3cdc0e14-283f-806e-b027-e8ad7f68acce" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="4" class="notion-list notion-list-numbered notion-block-3cdc0e14283f80278e3dd3229fc2adba" style="list-style-type:decimal"><li>重新输出并编译整个项目</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3cdc0e14283f8037b488e720df670a6d"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:593.9888916015625px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ac7539fa1-4e87-4a8d-8212-96e0a7a243af%3Aimage.png?table=block&amp;id=3cdc0e14-283f-8037-b488-e720df670a6d&amp;t=3cdc0e14-283f-8037-b488-e720df670a6d" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="5" class="notion-list notion-list-numbered notion-block-3cdc0e14283f806cb993d0373e83b16a" style="list-style-type:decimal"><li>完成后导出 xsa 文件并更新 Platform 项目</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3cdc0e14283f80cd8bececdf3b58771e"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A8bad6ff9-47b9-4b43-9be0-42febb7bbfd5%3Aimage.png?table=block&amp;id=3cdc0e14-283f-80cd-8bec-ecdf3b58771e&amp;t=3cdc0e14-283f-80cd-8bec-ecdf3b58771e" alt="notion image" loading="lazy" decoding="async"/></div></figure><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-3cdc0e14283f80d3ae96d87ac0e73b4a" data-id="3cdc0e14283f80d3ae96d87ac0e73b4a"><span><div id="3cdc0e14283f80d3ae96d87ac0e73b4a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3cdc0e14283f80d3ae96d87ac0e73b4a" title="解决方案三"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><em><b>解决方案三</b></em></span></span></h3><div class="notion-text notion-block-3cdc0e14283f80bbb4ddc134cde5bea8">若任何已有方法均不凑效，<span class="notion-red"><span class="notion-orange_background"><b><span class="notion-inline-underscore">极有可能是文件夹名称或者工程名称过长！！！请修改名称后重新尝试以上解决方案！</span></b></span></span></div></main></div>]]></content:encoded>
        </item>
    </channel>
</rss>