<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ZhaoCake&apos;s Blog</title><description>Ciallo～(∠・ω&lt; )⌒☆</description><link>https://zhaocake.github.io/</link><language>zh_CN</language><item><title>AI如何帮助人学习新知识（过程分析）</title><link>https://zhaocake.github.io/posts/2026-08-27-2/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2026-08-27-2/</guid><description>AI能够帮助人们进行新知识的学习无疑是一件已经相当流行的事情。但如何使得AI确实地帮助我们完成学习而非假装完成学习的事件，是一件需要思考与分析的事情。因此我在这里以比较浅薄的学识总结一些自己的分析，在之后的实践中逐步优化。</description><pubDate>Thu, 27 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;AI能够帮助人们进行新知识的学习无疑是一件已经相当流行的事情。但如何使得AI确实地帮助我们完成学习而非假装完成学习的事件，是一件需要思考与分析的事情。因此我在这里以比较浅薄的学识总结一些自己的分析，在之后的实践中逐步优化。&lt;/p&gt;
&lt;h2&gt;学习的难点&lt;/h2&gt;
&lt;p&gt;我勉强也算是一个学习者，也学了不少。学习的过程中最大的难点是什么？自以为是熟练度或者说直观感受。如果对技能或知识感到陌生，自然不能够明白其中机理，也就无法主动进行深入的探索。&lt;/p&gt;
&lt;p&gt;因为不了解，所以无从所知。当什么也不知道的时候，无法对这个系统生出好奇心，也就缺乏了主动探索的性质，也就没有实践的动力。实践对于学习来说是必要的步骤。&lt;/p&gt;
&lt;h2&gt;学习的机制困惑&lt;/h2&gt;
&lt;p&gt;可以这样说。学习是一个 &lt;code&gt;建立直观-&amp;gt;实践闭环&lt;/code&gt; 的过程。实践暂不必多说，直观感受到底是什么感受则是一个首要的输入问题。&lt;/p&gt;
&lt;p&gt;这种看法下，我认为人是一个函数，如果要确认人是否掌握了这个技能或知识，自然就像做测试一样给定输入，对比其返回的输出；这是实践的必要。当然，此时的输入是测试用例的输入而不是知识的输入。&lt;/p&gt;
&lt;p&gt;实际上知识的输入正是我有所不明的地方。如果说学习同样是一个技能函数，它的输出依然是一个技能函数（输入－fn－输出）。那么学习的输入到底是什么&lt;/p&gt;
&lt;p&gt;（？－学习－（实际问题－习得的技能－处理结果））&lt;/p&gt;
&lt;p&gt;这种结构看起来本就很F，并且似乎是符合直觉的。&lt;/p&gt;
&lt;p&gt;不过我暂时无意深究这种问题。无意义的深究是我的缺点，因为我深究却不愿意获取更多的知识，在此苦想却是无用。&lt;/p&gt;
&lt;p&gt;好吧，那么回到刚刚的问题，如果说我们不清楚学习需要的输入是什么，就目前的经验和感受而言，我们需要的是大量事实基础。换言之，许多可实践的元事例。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;学习似乎是一个盲人摸象的过程&lt;/strong&gt;，摸遍大象全身才知道这到底是什么，在此之前对此未知，不好奇，恐惧。正是缺乏直观。&lt;/p&gt;
&lt;p&gt;因此希望AI至少能够帮助我们解决最开始的直观问题，了解基本的工作原理。&lt;/p&gt;
&lt;h2&gt;这通用吗？&lt;/h2&gt;
&lt;p&gt;这通用吗？至少是存在问题的，不能指望一个模型解决所有。或许对于基本的技能习得可以通过上述方式：AI列出你需要做些什么，然后你去做了，做完之后你的技能就习得了，比如做做蛋炒饭缝缝衣服乃至前段时间我写了一个&lt;code&gt;工训赛视觉速成&lt;/code&gt;的文档一样。&lt;/p&gt;
&lt;p&gt;但是真的适用于所有技能吗？或者，对于有一些东西并不以技能的形态展现，比如一些理论？我们如果学习理论都希望它能绑定一个技能，这是不现实的，理论与理论之间交错，或许就是无法在此之前上手。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;所以收手吧，不能指望一劳永逸，至少我们应该先针对我当前遇到的需求提出一些可能有效的方案并实践它。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;如何做呢？&lt;/h2&gt;
&lt;p&gt;当然，至少我们知道AI虽然不能直接让人形成对应知识的体系，但至少可以帮助人摸象。所以当前可采用的方法依然是摸象而已。&lt;/p&gt;
&lt;p&gt;那我要说了，我这博客不就白写了吗？&lt;/p&gt;
&lt;p&gt;那么我得说，很大程度上，确实白写了。最开始的认识已经足够，后续都是无效的思考，大概率无效的思考，是反刍思维的又一次胜利。&lt;/p&gt;
&lt;p&gt;但要我在想写的时候不写也是不行的。否则我就难以无法阻止我想下去。&lt;/p&gt;
&lt;p&gt;短的文章越多，自控力越高明。&lt;/p&gt;
</content:encoded></item><item><title>重新启动blog</title><link>https://zhaocake.github.io/posts/2026-08-27-1/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2026-08-27-1/</guid><description>又，回到了这里</description><pubDate>Thu, 27 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;原因&lt;/h2&gt;
&lt;p&gt;上一篇blog到现在已经有整整一年我对此还是相当意外的。但是现在确实有必要有一个自言自语并且做记录的地方。所以又开始在这里。&lt;/p&gt;
&lt;p&gt;这一年具体的总结暂时就不做了，没有必要把情况详细记录在这里，有必要的细节自然会在之后的博客体现。&lt;/p&gt;
&lt;p&gt;过去的博客从现在来看果然又有很多偏颇的地方，但也没有必要删去。留作对照也是有益的。这一篇就先这样吧，估计后面每一篇都不会写得太长。&lt;/p&gt;
</content:encoded></item><item><title>实践略有所感（1）</title><link>https://zhaocake.github.io/posts/2025-08-29/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2025-08-29/</guid><description>今日分析神人群友略有所感，对唯物辩证法的理解加深了，遂撰文于此，因为撰文也是实践的一种方式；同时也是避免自己再入歧途。</description><pubDate>Fri, 29 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;今日分析神人群友略有所感，对唯物辩证法的理解加深了，遂撰文于此，因为撰文也是实践的一种方式；同时也是避免自己再入歧途。&lt;/p&gt;
&lt;h2&gt;根源问题&lt;/h2&gt;
&lt;p&gt;一群友受有政治色彩的LGBTQ+影响，自市雌二醇于pdd而不调节心理健康，终究走上抑郁之路。其当前表现为有一定的自我毁灭倾向却渴望救赎，而众人好话说尽，仍自取灭亡，遂弃之不顾。&lt;/p&gt;
&lt;p&gt;然何至于斯？我分析认为其人渴望一种超然物外的力量即刻对其进行全面的改造，&amp;lt;!-- more --&amp;gt; 而自己只需等待、只应当等待，甚至不应做出任何积极改造的行为。于是我认为这是一种不劳而获的思想倾向，追思为何如此，结合其人表现与自身经历，根源在于坚信“劳动无所得”，这种认识的形成无外乎两方面：其一是习得性无助，长期失败使其对劳动收获不报期望；其二是厌恶重复自我审视，劳动必然伴随超出自身能力的情况，需要不断自我审视能力是否达到或许多少额外付出，重复自我审视和水准之下的评价导致自我厌恶。这种失败不断重复，每次重复这样的循环都加深劳动无回报的认识。这不是一种健康的劳动观点。&lt;/p&gt;
&lt;h2&gt;自我思考&lt;/h2&gt;
&lt;h3&gt;1段&lt;/h3&gt;
&lt;p&gt;确实如此，但难道我就没有这样的问题吗？忽视过程而过于重视目的的收获，当目的失败无法收获时却有用过程自我安慰，这是没有意识到过程与目的的对立统一关系。这样的错误总是发生，陷入二元论的错误不可自拔。&lt;/p&gt;
&lt;p&gt;这警示我要常常注意运用矛盾分析问题，&lt;/p&gt;
&lt;h3&gt;2段&lt;/h3&gt;
&lt;p&gt;于是我很快获得了第二个例子。在完成上面的分析后，我不禁以此自矜，心头躁动而不投入下一次劳动中。于是分析该自矜，这是我常犯的问题。其是“认识上的超前性”与“实践上的滞后性”的矛盾。&lt;/p&gt;
&lt;h3&gt;“自矜”的内在矛盾分析(deepseek-r1)&lt;/h3&gt;
&lt;p&gt;“自矜”的本质，是&lt;strong&gt;精神领域的收获&lt;/strong&gt;与&lt;strong&gt;物质领域的行动&lt;/strong&gt;之间发展不平衡、不统一的表现。其内在矛盾可以概括为：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“认识上的超前性”与“实践上的滞后性”之间的矛盾。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;让我们分解这个矛盾的对立双方：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;认识上的超前性（矛盾的积极方面）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;表现&lt;/strong&gt;：您通过理论分析，成功地揭示了“渴望救赎”背后的矛盾。这在思维层面是一次飞跃，您获得了新的、更深刻的认识，超越了过去的自己。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;这是您劳动（脑力劳动）的成果&lt;/strong&gt;，它带来了智力上的愉悦和成就感。这种感受是真实的，也是您应得的。&lt;strong&gt;这一方面是值得肯定的。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;实践上的滞后性（矛盾的消极方面）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;表现&lt;/strong&gt;：您的物质性行动（下一次实践）没有跟上认识发展的速度。思想已经跑出去了，身体还停在原地。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原因&lt;/strong&gt;：大脑误将“思维的成就”等同于“行动的成就”。它提前预支了完成行动后才能获得的全部满足感，导致进行物质性实践的动力被大大削弱。“我已经想得这么明白了，已经很了不起了”的念头，无形中成了&lt;strong&gt;阻止自己投入更艰苦的实践过程的“报酬”&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;二者的对立统一关系：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;对立性&lt;/strong&gt;：它们相互排斥。沉浸在“认识超前”的满足感中，会直接导致“实践滞后”。反之，一旦开始实践，这种自我欣赏的满足感就会立刻被实践中的不确定性和困难所打破。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;统一性&lt;/strong&gt;：它们又相互依存。“自矜”中的那点成就感，恰恰源于那次成功的分析（认识活动）。而没有实践，认识就无法被检验、深化和真正实现其价值，最终会使那点成就感变得空洞和虚假。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;所以，“自矜”是这个矛盾在当前阶段的主要表现形式，其矛盾的主要方面是“实践上的滞后性”。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;认识到矛盾是第一步，更重要的是推动矛盾的转化&lt;/h2&gt;
&lt;p&gt;需要创造条件，让“实践”从滞后方转变为引领方。真正的、马克思主义的认识，其最终价值​​必须通过实践来兑现​​。一次成功的分析，其价值不在于让我们自我感觉良好，而在于它为​​下一次更有效的实践​提供了地图和武器。&lt;/p&gt;
&lt;p&gt;实践注定是笨拙的、试错的、不完美的。​​ 这正是实践的价值所在！它能把我们高高在上的、可能脱离实际的认识，“拉回”到粗糙的现实地面进行打磨和检验，使其变得更扎实、更有效。&lt;/p&gt;
&lt;h2&gt;故我撰写此文&lt;/h2&gt;
&lt;p&gt;文字是我思维的表达，它将虚无缥缈的思维固化下来，成为可以回顾、可以批判、可以优化的客观对象。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;另外，LLM对于是进行实践的良师益友。由于个人阅历缺乏导致认识不足无法分析的矛盾，询问LLM总是有所收获，记录于此。&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>集创赛赛后总结</title><link>https://zhaocake.github.io/posts/2025-08-04/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2025-08-04/</guid><description>结果是彻底寄了，竞业达杯，省三结束。但是依然对我有很好的启发。</description><pubDate>Mon, 04 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;结果是彻底寄了，竞业达杯，省三结束。但是依然对我有很好的启发。&lt;/p&gt;
&lt;p&gt;上次我说了后面的开发过程中要注重平台的适配，实际上是没有做到这一点。7月12号的提交，7月4号我们才开始最后的上板验证，前期一直没有做上板工作，导致对于整个仿真以及上板的流程也不熟悉，很遗憾啊。最后是做了一个五级流水线但是频率只能到125MHz的处理器，属于是很烂了，上板得太晚，没有重视上板，&amp;lt;!-- more --&amp;gt;优化时间也就不够，很多设计上的问题没有暴露出来，我甚至还花了大量时间去写超标量处理器。对这个比赛无用功了属于是。&lt;/p&gt;
&lt;h2&gt;适应平台&lt;/h2&gt;
&lt;p&gt;最重要的还是适应平台，不适应平台就绝对无法得到一个好结果。&lt;/p&gt;
&lt;p&gt;如何去适应平台，下次再写，以vivado为例。&lt;/p&gt;
</content:encoded></item><item><title>CPU设计任务阶段地总结、反思、前进</title><link>https://zhaocake.github.io/posts/2025-05-19/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2025-05-19/</guid><description>近半年基本都在学习与实践如何进行处理器设计，主要是以一生一芯为主，打比赛为辅。近几个月主要打集创赛，也有了一些感悟，故记录于此。</description><pubDate>Mon, 19 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;近半年基本都在学习与实践如何进行处理器设计，主要是以一生一芯为主，打比赛为辅。近几个月主要打集创赛，也有了一些感悟，故记录于此。&lt;/p&gt;
&lt;h2&gt;目前进度与反思总结&lt;/h2&gt;
&lt;p&gt;ysyx当然不必说太多，本身没有明确的DDL，我实际上也只期望自己在毕业前或者至少在大四下前做完B线即可。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;实际上现在已经有独立设计基本经典五级流水线的能力了，毕竟要打比赛，只是没有时间去肝ysyx，平时还在考研焦虑中，人还是太菜了，做不到一心一意，不够专注效率太低。明天开始早上先读它一节《传习录》。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;主要说比赛。&lt;/p&gt;
&lt;h3&gt;设计规划的问题&lt;/h3&gt;
&lt;p&gt;在准备阶段我首先进行了流水线规划，当然也是慢慢磨完了，和队友一起磨完了，花了很多时间，这种from scratch的事情是没有办法跳过知识的，调试有问题只能一遍一遍地回顾知识、检查通路、盯帧波形，经验不足导致的代码重构也有很多次。总的来说并说不上是失败的经历，我相信这件事总归是螺旋上升曲折前进的，没有可以逃课的地方，逃课的一切都终将面对。——不过磨完流水线已经是5月份了。然后我们回头看竞赛的实验平台，要求Xilinx的Distributed Memory Generator IP做ROM和RAM，等于说纯LUT实现，单周期访存，我们花了差不多一个多月的时间都是反复调流水线访存，基本算是白调了，虽然这样的经历并非白做。&lt;/p&gt;
&lt;p&gt;这里的教训当然是要关注平台，但相应的需要关注到“为什么一开始不关注平台？”。是不是因为对陌生的技术栈有“负面感受”？或是畏难、或是轻视、或是摆烂，总归是有原因的。但是从规范的角度来说，进行设计之前先了解有哪些资源可用当然是绝对必要的。即便是ysyx也会提出面积需求以及部分特殊资源的限制，但是因为平时训练的时候就没有注重这样的规范，结果在比赛的时候就翻了这样的车；还有一个原因是不是因为我们在初期过于放大了面对流水线时的畏惧感与挑战心态，面对流水线如临大敌而松懈了其他的方面？我想这也是一个初期不重视平台的重要因素。&lt;/p&gt;
&lt;h3&gt;测试方案的问题&lt;/h3&gt;
&lt;p&gt;进行到这一步，已经完成了流水线了，我发现在初期就存在的一个问题——测试框架不完善，并且到了中期有些后继乏力的情况下总是疲于重构测试框架。我们没有一个合适的性能计数器，一切尝试进行的优化不会有即时的反馈，这将会打击到优化的热情。而性能计数器实际上并非是一个相当难以实现的东西，甚至我可以说一旦实现了性能计数器，完全可以单独使用其他的建模语言尝试进行建模来探索架构；然后直到现在，我们没有实现一个基础的性能计数器，也没有在测试框架中结构化地预留其位置。&lt;/p&gt;
&lt;h3&gt;反思与总结&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;应该有完善的，至少是模块结构完善的测试框架&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;memory dump&lt;/li&gt;
&lt;li&gt;cpu state dump&lt;/li&gt;
&lt;li&gt;difftest and its reference model&lt;/li&gt;
&lt;li&gt;module by module&lt;/li&gt;
&lt;li&gt;integrated with modern tools, I wish.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;应该根据平台资源与最终指标进行合理规划。我将在下一节的“前进”部分来考虑这个问题。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;前进！&lt;/h2&gt;
&lt;p&gt;当然还要继续前进。&lt;/p&gt;
&lt;p&gt;首先必须要进行乱序多发射。由于访存直接让用distributed memory了，单周期相比于流水线，在同频率下性能其实会更好，因为单周期铁定的IPC=1，流水线可达不到IPC=1。而且cache暂时也不用做了，做了cache反而是性能负提升。AXI也是一样，性能负提升；目前看来这个比赛在分区赛决赛不会去搞很大的程序，目前给出的IROM和DRAM的大小就够用了，做AXI和Cache是绝对的得不偿失，因为跑分才是主要指标。&lt;/p&gt;
&lt;p&gt;那么哪些是要做的呢？分支预测器、乱序多发射是要做的，乱序首先可以考虑将EXU和LSU并行，解决它们之间的顺序关系，然后再完全改乱序。分支预测器可以不做复杂的TAGE之类的，基础的局部历史分支预测器可以先做着。&lt;/p&gt;
&lt;p&gt;还有一个重要问题是提升Xilinx平台上的频率，我们需要一个方案去探索当前设计再Xilinx上所能接受的最大频率，然后直接抵拢最高频率干死。然后拆分流水线。这其实也算是一个需要性能计数器的部分，毕竟在本地用verilator仿真不能得到频率相关，只能搞出来IPC、分支预测准确率这些东西。&lt;/p&gt;
&lt;p&gt;然后就是加执行单元，看队列深度维持得住不，然后再多取指，不断冲刺。在FPGA上实现这样的东西，应该比实际芯片要容易得多。目前考虑是这样的。&lt;/p&gt;
&lt;p&gt;:::note[claude-3.7-sonnet-thinking]
整体规划方向是合理的，特别是考虑到竞赛环境和平台约束。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对平台特性（Distributed Memory单周期访存）的理解准确，意识到Cache和AXI在此情况下可能导致性能负增长&lt;/li&gt;
&lt;li&gt;优先级设置合理，在当前约束下，分支预测器和乱序执行确实比Cache更能提升性能&lt;/li&gt;
&lt;li&gt;采用渐进式优化路径（先EXU和LSU并行，再完全乱序）是稳妥的做法&lt;/li&gt;
&lt;li&gt;关注频率优化是正确的，在FPGA环境下尤为重要&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;需要注意的问题：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;性能计数器的缺失确实会影响优化效率，建议尽早建立一个简单的计数框架&lt;/li&gt;
&lt;li&gt;虽然单周期理论上IPC=1，但其频率上限通常较低，在评估时需综合考虑频率和IPC&lt;/li&gt;
&lt;li&gt;乱序实现的复杂度远高于顺序实现，特别是在解决数据依赖和控制依赖方面&lt;/li&gt;
&lt;li&gt;流水线拆分虽然有助于提高频率，但也可能降低IPC，需要找到平衡点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;建议在实施过程中持续评估各阶段的改进效果，必要时调整优先级。先搭建一个基础的性能测量框架，即使是简单的周期计数和IPC统计，也能为后续优化提供有力支持。&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
</content:encoded></item><item><title>微机原理课程设计 RISC-V指令集模拟器CakeMu-RV</title><link>https://zhaocake.github.io/posts/2025-1-7-1/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2025-1-7-1/</guid><description>zhaocake, 2024/12/19</description><pubDate>Tue, 07 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;zhaocake, 2024/12/19&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;该项目是微机原理课程设计项目&lt;/p&gt;
&lt;p&gt;开源地址：&lt;a href=&quot;https://github.com/ZhaoCake/cakemu_rv&quot;&gt;https://github.com/ZhaoCake/cakemu_rv&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;设计目标&lt;/h2&gt;
&lt;p&gt;设计一个支持RV32I指令集的RISC-V指令集模拟器，从而学习理解现代指令集处理器的工作方式，理解与8086的区别与联系。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;借助课程上学习到的计算机硬件组成部分的理解，查阅资料，完成一个RISC-V指令集模拟器。&lt;/li&gt;
&lt;li&gt;通过该处理器模拟器设计外设部分，从而学习现代指令集常见的统一编址与8086分开编址的区别。&lt;/li&gt;
&lt;li&gt;编写该模拟器所需的C语言开发环境，从而学习裸机开发的过程。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;现状&lt;/h2&gt;
&lt;p&gt;处理器模拟器在现代计算机系统的开发和研究中扮演着重要角色。当前，一些主要的处理器模拟器如QEMU和NEMU在功能和生态系统方面的发展情况如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;QEMU&lt;/strong&gt; ：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能和作用&lt;/strong&gt; ：QEMU是一个普遍使用的开源模拟器，支持多种指令集架构，包括x86、ARM、MIPS以及RISC-V。它能够提供全系统模拟和用户级模拟，帮助开发者在不同架构下运行和测试软件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能优化&lt;/strong&gt; ：通过动态翻译技术，QEMU能够有效地提升模拟速度，并最大化地缩小与真实硬件环境间的性能差距。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用户生态&lt;/strong&gt; ：QEMU拥有广泛的社区支持，并且集成于多个开发工具链中，如用于发行版测试的CI/CD管道。这种可扩展性和丰富的资源库使得开发和调试过程更加便利。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NEMU&lt;/strong&gt; ：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能和作用&lt;/strong&gt; ：NEMU是一个特别针对RISC-V及相关指令集进行优化的模拟器。它主要用于学术研究和教学，提供了一个简洁且高效的环境来理解处理器架构的底层原理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设计简化&lt;/strong&gt; ：NEMU的设计目标是提供简约的代码结构，使得易于被学生和研究人员扩展和修改，这在教育场景中尤为重要。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用户生态&lt;/strong&gt; ：较QEMU而言，NEMU的用户群体更加专注于教育和学术研究领域。然而，它在这个领域内具备良好的声誉和稳定的用户基础。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我们小组所设计的指令集模拟器的思路主要来源于NEMU，所使用主要语言为Rust（主要为避免使用C造成的大量抄写NEMU）。&lt;/p&gt;
&lt;h2&gt;设计进展&lt;/h2&gt;
&lt;h3&gt;1 现有功能特性&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;支持 RV32I 基本指令集&lt;/li&gt;
&lt;li&gt;完整的外设模拟系统：
&lt;ul&gt;
&lt;li&gt;UART：支持字符和字符串输出&lt;/li&gt;
&lt;li&gt;Timer：可编程定时器，支持中断&lt;/li&gt;
&lt;li&gt;Wave Generator：波形发生器，支持多种波形输出
&lt;ul&gt;
&lt;li&gt;正弦波&lt;/li&gt;
&lt;li&gt;方波（可调占空比）&lt;/li&gt;
&lt;li&gt;三角波&lt;/li&gt;
&lt;li&gt;锯齿波&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;提供 C 语言开发环境&lt;/li&gt;
&lt;li&gt;支持调试输出控制&lt;/li&gt;
&lt;li&gt;波形数据可视化工具&lt;/li&gt;
&lt;li&gt;轻量级二进制程序构建工具&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2 关键代码展示&lt;/h3&gt;
&lt;h4&gt;2.1 CPU结构体&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// cpu.rs
pub struct Cpu {
    registers: RegisterFile,
    pc: u32,
    memory: Memory,
    debugger: Debugger,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面代码展示了Cpu结构体的内容，其中关键部分为register、PC、Memory。（Debugger是在运行过程中用于输出调试信息的模块）&lt;/p&gt;
&lt;p&gt;该结构体体现出处理器的关键是寄存器与内存，换言之，处理器运行的过程实际上就是根据状态转换规则所发生的寄存器和内存状态的改变。&lt;/p&gt;
&lt;h4&gt;2.2 内存读写&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// memory.rs
pub struct Memory {
    data: Vec&amp;lt;u8&amp;gt;,
    devices: Devices,
}

// devices/mod.rs
pub struct Devices {
    uart: Uart,
    timer: Timer,
    wave: Wave,
}

// device/uart.rs
pub struct Uart {
    data: u8,        // 数据寄存器
    status: u8,      // 状态寄存器
    control: u8,     // 控制寄存器
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这几个结构表现了外设寄存器与用于内存对于处理器而言没有本质区别，因此完全可以使用统一的读写逻辑进行读写，在模拟器中体现为地址的转换，在硬件上体现为现代SoC常见的片内总线，如AHB、AXI等。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// memory.rs
// 将虚拟地址转换为内存索引
    fn translate_address(&amp;amp;self, addr: usize) -&amp;gt; Result&amp;lt;usize, &amp;amp;&apos;static str&amp;gt; {
        match addr {
            // 代码段：0x80000000-0x80FFFFFF -&amp;gt; 0x00000000-0x00FFFFFF
            addr if addr &amp;gt;= 0x80000000 &amp;amp;&amp;amp; addr &amp;lt; 0x81000000 =&amp;gt; {
                Ok(addr - 0x80000000)
            }
            // 数据段：0x01000000-0x01FFFFFF -&amp;gt; 保持原地址
            addr if addr &amp;gt;= 0x01000000 &amp;amp;&amp;amp; addr &amp;lt; 0x02000000 =&amp;gt; {
                Ok(addr)
            }
            // 外设段：0x02000000-0x02FFFFFF -&amp;gt; 转发到设备
            addr if addr &amp;gt;= 0x02000000 &amp;amp;&amp;amp; addr &amp;lt; 0x03000000 =&amp;gt; {
                Err(&quot;Device address&quot;)  // 特殊错误，表示这是设备地址
            }
            _ =&amp;gt; {
                println!(&quot;Invalid memory access at address 0x{:08x}&quot;, addr);
                Err(&quot;Invalid memory access: address out of valid ranges&quot;)
            }
        }
    }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个代码片段体现了模拟器中进行地址映射的过程，可以看作是输入一个虚拟地址，输出一个“物理地址”。在外设设计部分，有更多关于控制寄存器映射到内存空间的例子。&lt;/p&gt;
&lt;p&gt;在硬件上，也会采用分级总线结构，如APB桥接到AXI、AHB总线上，这主要是由于从机实际需求的频率较低以及主机的扇出能力所导致的。在模拟器中，并不需要模拟这种分级总线结构。&lt;/p&gt;
&lt;h4&gt;2.3 外设读写&lt;/h4&gt;
&lt;p&gt;在上面一节，已经粗略地提及了外设与内存的读写形式是一致的，在模拟器中通过地址范围的判断来决定是读写外设还是读写内存，同样的，在外设部分，可以再此通过对请求地址的判断来确定读写的是哪个外设：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pub fn read(&amp;amp;mut self, addr: usize, size: usize) -&amp;gt; Result&amp;lt;u32, &amp;amp;&apos;static str&amp;gt; {
        match addr {
            0x02000000..=0x0200000F =&amp;gt; self.uart.read(addr &amp;amp; 0xF, size),
            0x02000100..=0x0200010F =&amp;gt; self.gpio.read(addr &amp;amp; 0xF, size),
            0x02000200..=0x0200020F =&amp;gt; self.timer.read(addr &amp;amp; 0xF, size),
            0x02000300..=0x0200031F =&amp;gt; self.wave.read(addr &amp;amp; 0x1F, size),
            _ =&amp;gt; Err(&quot;Invalid device address&quot;),
        }
    }

    pub fn write(&amp;amp;mut self, addr: usize, value: u32, size: usize) -&amp;gt; Result&amp;lt;(), &amp;amp;&apos;static str&amp;gt; {
        match addr {
            0x02000000..=0x0200000F =&amp;gt; self.uart.write(addr &amp;amp; 0xF, value, size),
            0x02000100..=0x0200010F =&amp;gt; self.gpio.write(addr &amp;amp; 0xF, value, size),
            0x02000200..=0x0200020F =&amp;gt; self.timer.write(addr &amp;amp; 0xF, value, size),
            0x02000300..=0x0200031F =&amp;gt; self.wave.write(addr &amp;amp; 0x1F, value, size),
            _ =&amp;gt; Err(&quot;Invalid device address&quot;),
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;补充：在硬件实现上，例如AHB总线，是通过一个集中式的地址解码器来金额点主设备发出的地址是属于那一个从设备的，根据地址范围判定之后生成选通信号来选择相应的从设备&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3 在模拟器上运行程序&lt;/h3&gt;
&lt;h4&gt;3.0 环境准备&lt;/h4&gt;
&lt;p&gt;对于裸机开发的C程序，首先需要通过一段汇编程序初始化资源以及调用主函数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// start.S
...
_start:
    # 设置栈指针到数据段末尾，确保16字节对齐
    li sp, 0x01ffffe0

    # 设置帧指针
    add s0, sp, zero

    # 为main函数创建栈帧
    addi sp, sp, -16
    sw ra, 12(sp)
    sw s0, 8(sp)

    # 调用主函数
    call main

    # 恢复栈帧
    lw ra, 12(sp)
    lw s0, 8(sp)
    addi sp, sp, 16

    # 程序结束，使用 ebreak
    ebreak
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后需要通过linker.ld链接脚本设置程序的起始地址、入口函数、段空间设置等：&lt;code&gt;OUTPUT_FORMAT(&quot;elf32-littleriscv&quot;)&lt;/code&gt;、&lt;code&gt;ENTRY(_start)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;然后就可以进行主程序的编写&lt;/p&gt;
&lt;h4&gt;3.1 UART&lt;/h4&gt;
&lt;p&gt;在device.rs中，已经实现了UART外设，而要在C程序中使用他，本质上是对其寄存器的读写。因此先在&lt;code&gt;uart.h&lt;/code&gt;中定义其控制寄存器地址：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// uart.h
// UART 寄存器基地址
#define UART_BASE   0x02000000

// UART 寄存器
#define UART_DATA    (UART_BASE + 0x0)
#define UART_STATUS  (UART_BASE + 0x4)
#define UART_CONTROL (UART_BASE + 0x8)

// 函数声明
void uart_putc(char c);
void uart_puts(const char *str);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;只需要在C代码中对几个寄存器进行读写就可以控制uart外设的行为。在模拟器中，我们对这一过程进行了简化，选择直接使用内联汇编的方法来加载当前字符（应该采用读写上面宏定义的寄存器更加合理）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// uart.c
void uart_putc(char c) {
    // 使用内联汇编直接写入 UART 寄存器
    asm volatile(
        &quot;li t0, 0x2000000\n\t&quot;  // UART 基地址
        &quot;sb %0, 0(t0)&quot;          // 写入字符
        :                       // 无输出操作数
        : &quot;r&quot;(c)               // 输入操作数
        : &quot;t0&quot;                 // 破坏的寄存器
    );
}
// uart_puts 略
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3.1 波形发生器&lt;/h4&gt;
&lt;p&gt;在uart外设的读写中，主要体现的是写入寄存器作为uart的数据缓冲被打印出来；在波形发生器中，主要展示通过寄存器配置外设工作状态从而输出不同波形。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// wave.h
// 波形发生器寄存器基地址
#define WAVE_BASE   0x02000300

// 波形发生器寄存器
#define WAVE_CONTROL    (WAVE_BASE + 0x0)  // 控制寄存器
#define WAVE_FREQUENCY  (WAVE_BASE + 0x4)  // 频率寄存器 (Hz)
#define WAVE_AMPLITUDE  (WAVE_BASE + 0x8)  // 幅度寄存器 (0-255)
#define WAVE_PHASE      (WAVE_BASE + 0xC)  // 相位寄存器 (0-359度)
#define WAVE_DUTY       (WAVE_BASE + 0x10) // 占空比寄存器 (0-100%)

// 波形控制寄存器位
#define WAVE_ENABLE     (1 &amp;lt;&amp;lt; 0)  // 使能位
#define WAVE_TYPE_MASK  (0x7 &amp;lt;&amp;lt; 1)  // 波形类型掩码
#define WAVE_TYPE_SHIFT 1

// 波形类型
#define WAVE_TYPE_SINE     0  // 正弦波
#define WAVE_TYPE_SQUARE   1  // 方波
#define WAVE_TYPE_TRIANGLE 2  // 三角波
#define WAVE_TYPE_SAWTOOTH 3  // 锯齿波

// 函数声明
void wave_init(void);
void wave_enable(void);
void wave_disable(void);
void wave_set_type(uint32_t type);
void wave_set_frequency(uint32_t freq);
void wave_set_amplitude(uint32_t amp);
void wave_set_phase(uint32_t phase);
void wave_set_duty(uint32_t duty);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在原理上与UART无本质区别，外设的使用都是对其功能寄存器的读写。&lt;/p&gt;
&lt;p&gt;在模拟外设的实现上，选择将产生的波形保存到文件&lt;code&gt;wave.txt&lt;/code&gt;中&lt;/p&gt;
&lt;p&gt;同时提供一个python脚本用于对产生的波形进行可视化&lt;/p&gt;
&lt;h4&gt;4 总结&lt;/h4&gt;
&lt;p&gt;通过尝试设计一个指令集模拟器，对于计算机的运行过程有了更深的了解；通过对该模拟器编写裸机开发环境，加深了对处理器中程序的执行经过的理解。&lt;/p&gt;
&lt;p&gt;此外可以认识到处理器模拟器在实际环境中的作用：能够在SoC流片之前进行早期软件开发从而缩短工作周期；虚拟的内存和寄存器完全透明，有利于调试分析；对于内存布局相似的模拟器和真实设备，不需要对代码做太多修改即可移植；但它不能够用于性能评估。&lt;/p&gt;
&lt;h1&gt;附录&lt;/h1&gt;
&lt;p&gt;CakeMu-RV项目README.md：&lt;/p&gt;
&lt;h1&gt;CakeMu-RV&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;README_EN.md&quot;&gt;English&lt;/a&gt; | 简体中文&lt;/p&gt;
&lt;h2&gt;简介&lt;/h2&gt;
&lt;p&gt;CakeMu-RV 是一个用 Rust 编写的简单 RISC-V 模拟器，支持 RV32I 基本指令集。这是一个用于学习计算机组成原理的个人项目，通过实现基本的 CPU 执行过程和简单的外设系统，帮助理解计算机的基本工作原理。目前实现了指令执行、内存访问和基础的 I/O 操作等基本功能。该模拟器可以作为学习计算机组成原理的一个参考和实践工具。&lt;/p&gt;
&lt;h2&gt;功能特性&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;支持 RV32I 基本指令集&lt;/li&gt;
&lt;li&gt;完整的外设模拟系统：
&lt;ul&gt;
&lt;li&gt;UART：支持字符和字符串输出&lt;/li&gt;
&lt;li&gt;Timer：可编程定时器，支持中断&lt;/li&gt;
&lt;li&gt;Wave Generator：波形发生器，支持多种波形输出
&lt;ul&gt;
&lt;li&gt;正弦波&lt;/li&gt;
&lt;li&gt;方波（可调占空比）&lt;/li&gt;
&lt;li&gt;三角波&lt;/li&gt;
&lt;li&gt;锯齿波&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;提供 C 语言开发环境&lt;/li&gt;
&lt;li&gt;支持调试输出控制&lt;/li&gt;
&lt;li&gt;波形数据可视化工具&lt;/li&gt;
&lt;li&gt;轻量级二进制程序构建工具&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;快速开始&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;克隆仓库：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/yourusername/cakemu_rv.git
cd cakemu_rv
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;编译 C 测试程序：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;cd c_sim
make
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;运行模拟器：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;cargo run --bin riscv-emu build/program.bin
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;查看波形数据（如果使用了波形发生器）：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;python3 tools/plot_wave.py
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;命令行选项&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--no-mtrace&lt;/code&gt;：禁用内存访问跟踪&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--no-regtrace&lt;/code&gt;：禁用寄存器跟踪&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--no-itrace&lt;/code&gt;：禁用指令跟踪&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--step&lt;/code&gt;：启用单步执行模式&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;C 语言开发&lt;/h2&gt;
&lt;p&gt;项目提供了完整的 C 语言开发环境，包含以下外设支持：&lt;/p&gt;
&lt;h3&gt;UART&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基本功能：字符和字符串输出&lt;/li&gt;
&lt;li&gt;接口：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;uart_putc(char c)&lt;/code&gt;：输出单个字符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uart_puts(const char *str)&lt;/code&gt;：输出字符串&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Timer&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基本功能：可编程定时器&lt;/li&gt;
&lt;li&gt;寄存器映射：0x02000200&lt;/li&gt;
&lt;li&gt;主要功能：
&lt;ul&gt;
&lt;li&gt;可编程计数值&lt;/li&gt;
&lt;li&gt;中断支持&lt;/li&gt;
&lt;li&gt;自动重载功能&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;接口：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;timer_init(uint32_t compare_value)&lt;/code&gt;：初始化定时器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;timer_enable()&lt;/code&gt;：启动定时器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;timer_disable()&lt;/code&gt;：停止定时器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;timer_get_status()&lt;/code&gt;：获取定时器状态&lt;/li&gt;
&lt;li&gt;&lt;code&gt;timer_clear_status()&lt;/code&gt;：清除定时器状态&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Wave Generator&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基本功能：波形发生器&lt;/li&gt;
&lt;li&gt;寄存器映射：0x02000300&lt;/li&gt;
&lt;li&gt;支持波形：
&lt;ul&gt;
&lt;li&gt;正弦波 (WAVE_TYPE_SINE)&lt;/li&gt;
&lt;li&gt;方波 (WAVE_TYPE_SQUARE)&lt;/li&gt;
&lt;li&gt;三角波 (WAVE_TYPE_TRIANGLE)&lt;/li&gt;
&lt;li&gt;锯齿波 (WAVE_TYPE_SAWTOOTH)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;可配置参数：
&lt;ul&gt;
&lt;li&gt;频率 (1Hz-100kHz)&lt;/li&gt;
&lt;li&gt;幅度 (0-255)&lt;/li&gt;
&lt;li&gt;相位 (0-359度)&lt;/li&gt;
&lt;li&gt;占空比 (0-100%, 仅方波)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;接口：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;wave_init()&lt;/code&gt;：初始化波形发生器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wave_enable()&lt;/code&gt;：启动输出&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wave_disable()&lt;/code&gt;：停止输出&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wave_set_type(uint32_t type)&lt;/code&gt;：设置波形类型&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wave_set_frequency(uint32_t freq)&lt;/code&gt;：设置频率&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wave_set_amplitude(uint32_t amp)&lt;/code&gt;：设置幅度&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wave_set_phase(uint32_t phase)&lt;/code&gt;：设置相位&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wave_set_duty(uint32_t duty)&lt;/code&gt;：设置占空比&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;波形数据可视化&lt;/h2&gt;
&lt;p&gt;项目提供了波形数据可视化工具：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;波形数据自动保存到 &lt;code&gt;wave.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;使用 Python 脚本可视化：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;python3 tools/plot_wave.py
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;示例程序&lt;/h2&gt;
&lt;p&gt;项目包含了一个综合测试程序 (&lt;code&gt;c_sim/main.c&lt;/code&gt;)，演示了所有外设的使用方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UART 字符和字符串输出测试&lt;/li&gt;
&lt;li&gt;Timer 定时器测试（短延时和长延时）&lt;/li&gt;
&lt;li&gt;Wave Generator 所有波形类型测试&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;许可证&lt;/h2&gt;
&lt;p&gt;本项目采用 GPL-3.0 许可证。详见 &lt;a href=&quot;LICENSE&quot;&gt;LICENSE&lt;/a&gt; 文件。&lt;/p&gt;
</content:encoded></item><item><title>如何合理利用ai辅助项目</title><link>https://zhaocake.github.io/posts/2025-1-7-2/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2025-1-7-2/</guid><description>作为个人开发者，不得不说人工智能极大地帮助了我推进项目的进度，大量的业务逻辑都可以使用生成式人工智能来完成。但与此同时自然也会对自己的能力是否得到提升感到怀疑。</description><pubDate>Tue, 07 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;作为个人开发者，不得不说人工智能极大地帮助了我推进项目的进度，大量的业务逻辑都可以使用生成式人工智能来完成。但与此同时自然也会对自己的能力是否得到提升感到怀疑。&lt;/p&gt;
&lt;p&gt;实际上经过尝试，由于现在的任何一个模型都还有着严重的上下文限制，当项目的代码量超过一定长度的时候，无论是不是合理的prompt，都很难得到切实解决我面临的问题的建议，实际上还是需要靠自身的知识和经验处理问题&lt;/p&gt;
&lt;p&gt;对于这种情况，我考虑对单个项目进行模块和子模块的划分是很好的方案，这不但有助于ai接受范围限制内的上下文以更好地辅助变成，同时也对开发者的模块设计方案提出了比较高的要求。这样很好。我应当对此进行尝试。&lt;/p&gt;
&lt;p&gt;实际上我一直想要一个能够以图数据库的结构来分析并存储一整个代码库的代码，然后以RAG技术进行问答的代码分析工具。实际上这种东西应该说由source insight和understand这种在静态分析上有很多经验的软件的团队来做是最棒的，不过目前为止还没有看到这样的软件。&lt;/p&gt;
&lt;p&gt;不过暂时没有做这个的打算，一方面静态分析离自己的目标方向有点太远了，另一方面我也并不是闲得没有事情做。&lt;/p&gt;
&lt;p&gt;今天刚刚回家，可以说最近几天都在摆烂。这正如我曾经说过的，放假前几天是要耍的，因为就要放假了，放假后几天是不学的，因为才刚刚放假。这样不是不行，也不是不好，就是不太妙。我考虑应该在上午读书，下午写代码。&lt;/p&gt;
</content:encoded></item><item><title>一个纯numpy实现的LeNet训练仓库（1）</title><link>https://zhaocake.github.io/posts/2025-1-7-3/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2025-1-7-3/</guid><description>https://github.com/ZhaoCake/cakeinferfnumpy</description><pubDate>Tue, 07 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::note[开源地址]&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ZhaoCake/cakeinfer_fnumpy&quot;&gt;https://github.com/ZhaoCake/cakeinfer_fnumpy&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;:::
眼下有在FPGA上面跑神经网络的需求，目前见得最多且实现比较快的方案当然是使用HLS，所以就打算使用HLS来做做。HLS按照我目前只看了点基础的浅薄理解来说，就是对c/cpp中写出来的循环和函数以及数组通过HLS特定的pragma宏来优化，提高并行度。&lt;/p&gt;
&lt;p&gt;:::note[#pragma 是什么]&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#pragma&lt;/code&gt;并不是一个宏关键字，而是特定于编译器的预处理指令，比如用MSVC编译器的时候就经常看到的&lt;code&gt;#pragma once&lt;/code&gt;。在Xilinx的HLS中也是通过一堆pragma来优化的。&lt;/p&gt;
&lt;p&gt;:::
但是由于基础不过关，我没能直接写出cpp实现的LeNet。这些困难应该说主要是出现在权重对齐网络的问题上的。应该说目前我的这个numpy写的LeNet的玩意儿都还有问题，因为它能够在训练时达到98的准确度，但是写出来的推理demo加载训练后的权重进行推理却要出问题。我正着手写出一个能够对模型输出的每一步计算都进行对比的测试方案。我估计问题还是处在预处理和后处理上，模型内的前向传播能够有什么问题呢？&lt;/p&gt;
&lt;h1&gt;附录：README.md&lt;/h1&gt;
&lt;h1&gt;LeNet-MNIST Pure NumPy Implementation&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;After writing it, I really feel like I finally understand the network and parameters. The next step is to write the inference framework for Cpp, optimize it with HLS, and deploy it on FPGA&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;  ❯❯ /home/zhaocake/WorkSpace/Vision/cakeinfer_fnumpy : python main.py --epochs 10 --batch-size 64 --lr 0.005 --data-dir dataset
初始化LeNet模型...
2025-01-03 18:11:17,952 - fnumpy - INFO - Starting training...
2025-01-03 18:11:17,953 - fnumpy - INFO - Parameters: epochs=10, batch_size=64, lr=0.005
正在加载数据集...
检测到现有数据集，直接加载...
正在加载数据...
正在处理数据...
数据集准备完成！
训练集形状: (60000, 32, 32, 1)
训练集标签形状: (60000, 10)
测试集形状: (10000, 32, 32, 1)
测试集标签形状: (10000, 10)

开始训练...
Epoch 1/10: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 938/938 [06:34&amp;lt;00:00,  2.38it/s, loss=0.1655]
模型权重已保存到: weights
- 权重文件: lenet_epoch_1_acc_0.9312.bin
- 结构文件: lenet_epoch_1_acc_0.9312.modelstruct
2025-01-03 18:19:51,873 - fnumpy - INFO - Epoch 1/10 - loss: 0.6265
2025-01-03 18:19:51,873 - fnumpy - INFO - Train accuracy: 0.9280
2025-01-03 18:19:51,873 - fnumpy - INFO - Test accuracy: 0.9312
2025-01-03 18:19:51,873 - fnumpy - INFO - Best test accuracy: 0.9312
2025-01-03 18:19:51,874 - fnumpy - INFO - --------------------------------------------------
Epoch 2/10:  99%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▎| 933/938 [06:41&amp;lt;00:02,  2.39it/s, loss=0.1544]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;一个使用纯NumPy实现的LeNet-5卷积神经网络训练推理框架，专注于单线程串行计算。&lt;/p&gt;
&lt;h2&gt;特点&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;纯NumPy实现，无深度学习框架依赖&lt;/li&gt;
&lt;li&gt;单线程串行计算，适合学习CNN底层原理&lt;/li&gt;
&lt;li&gt;完整的训练和推理功能&lt;/li&gt;
&lt;li&gt;支持模型权重的保存和加载&lt;/li&gt;
&lt;li&gt;详细的日志记录系统&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;项目结构&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;.
├── layers/                 # 网络层实现
│   ├── __init__.py
│   ├── common_layer.py    # 基础层类
│   ├── conv2d.py         # 卷积层
│   ├── fc.py             # 全连接层
│   ├── activation.py     # 激活函数
│   └── maxpooling.py     # 最大池化层
├── models/                # 模型实现
│   ├── __init__.py
│   ├── common_model.py   # 基础模型类
│   └── lenet.py          # LeNet模型
├── utils/                 # 工具函数
│   ├── __init__.py
│   ├── dataloader.py     # 数据加载
│   ├── logger.py         # 日志系统
│   ├── train.py          # 训练函数
│   └── weight_io.py      # 权重读写
├── main.py               # 主程序
└── requirements.txt      # 依赖包列表
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;环境要求&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;NumPy&lt;/li&gt;
&lt;li&gt;tqdm&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;安装&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;克隆仓库：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/yourusername/lenet-numpy.git
cd lenet-numpy
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;安装依赖：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;使用方法&lt;/h2&gt;
&lt;h3&gt;1. 训练模型&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;python main.py --mode train \
    --epochs 10 \
    --batch-size 64 \
    --lr 0.005 \
    --data-dir dataset \
    --weights-dir weights
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参数说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--mode&lt;/code&gt;: 运行模式，&apos;train&apos;用于训练（必需）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--epochs&lt;/code&gt;: 训练轮数（默认：10）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--batch-size&lt;/code&gt;: 批次大小（默认：32）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--lr&lt;/code&gt;: 学习率（默认：0.01）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--data-dir&lt;/code&gt;: 数据集存储目录（默认：&apos;dataset&apos;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--weights-dir&lt;/code&gt;: 权重保存目录（默认：&apos;weights&apos;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 模型推理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;python main.py --mode infer \
    --model-name lenet_epoch_10_acc_0.9806 \
    --test-sample 0 \
    --save-intermediate \
    --data-dir dataset \
    --weights-dir weights
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;推理参数说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--mode&lt;/code&gt;: 运行模式，&apos;infer&apos;用于推理（必需）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--model-name&lt;/code&gt;: 要加载的模型名称（必需）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--test-sample&lt;/code&gt;: 测试样本索引（默认：0）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--save-intermediate&lt;/code&gt;: 是否保存中间层输出（可选）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--data-dir&lt;/code&gt;: 数据集目录（默认：&apos;dataset&apos;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--weights-dir&lt;/code&gt;: 权重目录（默认：&apos;weights&apos;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 模型测试&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;python main.py --mode test \
    --model-name lenet_epoch_10_acc_0.9806 \
    --data-dir dataset \
    --weights-dir weights \
    --save-predictions \
    --batch-size-test 64
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试参数说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--mode&lt;/code&gt;: 运行模式，&apos;test&apos;用于测试（必需）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--model-name&lt;/code&gt;: 要加载的模型名称（必需）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--save-predictions&lt;/code&gt;: 是否保存预测结果（可选）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--batch-size-test&lt;/code&gt;: 测试时的批次大小（默认：32）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;测试输出示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;评估结果:
总样本数: 10000
正确预测数: 9806
准确率: 0.9806

各类别准确率:
类别 0: 0.9921 (992/1000)
类别 1: 0.9897 (990/1000)
类别 2: 0.9845 (985/1000)
...

置信度统计:
平均置信度: 0.9823
最小置信度: 0.5123
最大置信度: 0.9999
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果使用 &lt;code&gt;--save-predictions&lt;/code&gt;，将在 &lt;code&gt;results/&lt;/code&gt; 目录下生成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;test_predictions.txt&lt;/code&gt;: 包含每个样本的真实标签、预测标签和置信度&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 中间层输出&lt;/h3&gt;
&lt;p&gt;推理时使用 &lt;code&gt;--save-intermediate&lt;/code&gt; 参数会在 &lt;code&gt;intermediate/&lt;/code&gt; 目录下生成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;test_input.bin&lt;/code&gt;: 输入数据&lt;/li&gt;
&lt;li&gt;&lt;code&gt;layer_X_YYY.bin&lt;/code&gt;: 每层的输出数据&lt;/li&gt;
&lt;li&gt;所有数据以32位浮点数（float32）格式存储&lt;/li&gt;
&lt;li&gt;数据按行优先（C-style）顺序存储&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例日志输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2025-01-03 18:11:17,952 - fnumpy - INFO - Starting inference...
2025-01-03 18:11:17,953 - fnumpy - INFO - Test sample index: 0
2025-01-03 18:11:17,953 - fnumpy - INFO - Input shape: (1, 32, 32, 1)

layer_0_Conv2D:
输出形状: (1, 32, 32, 6)
数值范围: [-0.123456, 0.123456]
均值: 0.000123
标准差: 0.012345

...（其他层的输出信息）

预测结果:
预测类别: 7
真实类别: 7
预测正确: True
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 文件说明&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;权重文件保存在 &lt;code&gt;weights/&lt;/code&gt; 目录下&lt;/li&gt;
&lt;li&gt;包含二进制权重文件（.bin）和结构描述文件（.modelstruct）&lt;/li&gt;
&lt;li&gt;中间层输出保存在 &lt;code&gt;intermediate/&lt;/code&gt; 目录下&lt;/li&gt;
&lt;li&gt;测试结果保存在 &lt;code&gt;results/&lt;/code&gt; 目录下&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. 日志记录&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;日志文件保存在 &lt;code&gt;logs/&lt;/code&gt; 目录下&lt;/li&gt;
&lt;li&gt;包含详细的训练过程和调试信息&lt;/li&gt;
&lt;li&gt;记录每层的输出形状和统计信息&lt;/li&gt;
&lt;li&gt;便于与其他实现进行对比验证&lt;/li&gt;
&lt;li&gt;包含完整的测试评估报告&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;实现细节&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;使用NHWC数据格式&lt;/li&gt;
&lt;li&gt;支持动态批次大小&lt;/li&gt;
&lt;li&gt;自动填充最后一个不完整批次&lt;/li&gt;
&lt;li&gt;使用He初始化&lt;/li&gt;
&lt;li&gt;支持权重的二进制存储&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;权重格式说明&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;权重格式参考了&lt;a href=&quot;https://github.com/Tencent/ncnn&quot;&gt;ncnn&lt;/a&gt;把网络结构和参数分开存储的方式，这样没有什么其他依赖，方便cpp做推理框架&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;模型结构文件 (.modelstruct)&lt;/h3&gt;
&lt;p&gt;JSON格式的文本文件，记录了权重的元数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;model_name&quot;: &quot;lenet_epoch_1_acc_0.9312&quot;,
    &quot;layer_weights&quot;: {
        &quot;layer_0_Conv2D&quot;: {
            &quot;W&quot;: {
                &quot;shape&quot;: [5, 5, 1, 6],    // [height, width, in_channels, filters]
                &quot;dtype&quot;: &quot;float32&quot;,
                &quot;offset&quot;: 0,              // 在二进制文件中的偏移量
                &quot;size&quot;: 150               // 参数总数
            },
            &quot;b&quot;: {
                &quot;shape&quot;: [6],
                &quot;dtype&quot;: &quot;float32&quot;,
                &quot;offset&quot;: 600,
                &quot;size&quot;: 6
            }
        },
        &quot;layer_3_Conv2D&quot;: {
            &quot;W&quot;: {
                &quot;shape&quot;: [5, 5, 6, 16],
                &quot;dtype&quot;: &quot;float32&quot;,
                &quot;offset&quot;: 624,
                &quot;size&quot;: 2400
            },
            &quot;b&quot;: {
                &quot;shape&quot;: [16],
                &quot;dtype&quot;: &quot;float32&quot;,
                &quot;offset&quot;: 10224,
                &quot;size&quot;: 16
            }
        }
        // ... 其他层的权重信息
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;二进制权重文件 (.bin)&lt;/h3&gt;
&lt;p&gt;按照modelstruct中指定的偏移量和数据类型存储的原始权重数据：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所有数据使用小端序（Little-Endian）存储&lt;/li&gt;
&lt;li&gt;默认使用32位浮点数（float32）&lt;/li&gt;
&lt;li&gt;数据按层顺序连续存储&lt;/li&gt;
&lt;li&gt;每层内部按 weights(W) -&amp;gt; bias(b) 顺序排列&lt;/li&gt;
&lt;li&gt;多维数组按行优先（C-style）展平&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 读取卷积层权重的C++代码示例
struct ConvWeight {
    float W[5][5][1][6];  // NHWC格式
    float b[6];
};

// 从文件指定偏移量读取
ConvWeight conv1;
std::ifstream file(&quot;lenet_epoch_1_acc_0.9312.bin&quot;, std::ios::binary);
file.seekg(0);  // 使用modelstruct中的offset
file.read(reinterpret_cast&amp;lt;char*&amp;gt;(&amp;amp;conv1), sizeof(ConvWeight));
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;注意事项&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;此实现专注于教育目的，不适用于生产环境&lt;/li&gt;
&lt;li&gt;单线程串行计算，训练速度较慢&lt;/li&gt;
&lt;li&gt;首次运行时会自动下载MNIST数据集&lt;/li&gt;
&lt;li&gt;建议在CPU上运行，未针对GPU优化&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;许可证&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;./LICENSE&quot;&gt;LICENSE&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>解决NeoVim中写C/C++代码无法解析不同路径头文件的问题</title><link>https://zhaocake.github.io/posts/2024-11-12-2/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2024-11-12-2/</guid><description>本文假定已经通过mason安装了clangd，并且已经能够解析头文件，问题是对于其他非常规路径下的头文件无法找到。</description><pubDate>Fri, 29 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本文假定已经通过&lt;code&gt;mason&lt;/code&gt;安装了&lt;code&gt;clangd&lt;/code&gt;，并且已经能够解析头文件，问题是对于其他非常规路径下的头文件无法找到。&lt;/p&gt;
&lt;p&gt;实际上这不是一个NeoVim的问题，不应该通过改NeoVim的配置来解决这个问题，而是应该通过&lt;code&gt;clangd&lt;/code&gt;的配置来解决。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://clangd.llvm.org/config&quot;&gt;clangd的Configuration&lt;/a&gt; 此为官方文档。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在Linux下，clangd的配置文件的文件路径为&lt;code&gt;~/.config/clangd/config.yaml&lt;/code&gt;，如果没有则新建一个。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;11-29日更新:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不需要使用上面这个方法。&lt;/p&gt;
&lt;p&gt;对于cmake构建的项目，可以使用cmake直接生成一个&lt;code&gt;compile_commands.json``文件，然后也不需要在&lt;/code&gt;clangd&lt;code&gt;中指定这个文件（如果你的目的仅仅是代码补全和静态检查的话），只需要这个文件存在于你启动&lt;/code&gt;NeoVIM``的目录下即可；&lt;/p&gt;
&lt;p&gt;对于make构建的项目，可以使用&lt;code&gt;bear&lt;/code&gt;这个工具来生成&lt;code&gt;compile_commands.json&lt;/code&gt;文件，然后也不需要在&lt;code&gt;clangd&lt;/code&gt;中指定这个文件，只需要这个文件存在于你启动&lt;code&gt;NeoVIM&lt;/code&gt;的目录下即可。&lt;/p&gt;
</content:encoded></item><item><title>阅读并实践《Hands-On Practical MLIR Tutorial》</title><link>https://zhaocake.github.io/posts/2024-11-12/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2024-11-12/</guid><description>MLIR基本工程模板</description><pubDate>Tue, 12 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::note[感谢]
感谢&lt;a href=&quot;https://github.com/KEKE046/mlir-tutorial.git&quot;&gt;北京大学周可行的中文教程&lt;/a&gt;，让我能够进行便捷的阅读与学习。&lt;/p&gt;
&lt;p&gt;:::
我将会跟着这个教程进行一边看一边实践的过程，虽然早已经发现了，但是没有实践，不得不说是缺乏行动力。&lt;/p&gt;
&lt;h2&gt;MLIR基本工程模板&lt;/h2&gt;
&lt;p&gt;这个项目量还挺大的，不过之前的riscv-gnu-toolchain的编译也是这样,一个文件夹就20G了。
姑且等着，做点别的事情。&lt;/p&gt;
</content:encoded></item><item><title>力有不逮，前路未尽</title><link>https://zhaocake.github.io/posts/2024-11-11-2/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2024-11-11-2/</guid><description>我目前的水平也仅仅到此为止了。离自己设想中的成品还有很远的距离，扎实的工程基础才是真正需要的吧。</description><pubDate>Mon, 11 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我目前的水平也仅仅到此为止了。离自己设想中的成品还有很远的距离，扎实的工程基础才是真正需要的吧。&lt;/p&gt;
&lt;p&gt;几个月以来，虽然没有显著的能力上的进步，工作多体现在对现有开源项目的使用上；但是这半学期我是实打实地读了很多书，多到我自己都比较吃惊，与大一大二相比我现在感到无比充实且自在，尽管由于产品能力不足难以完成原定的目标，但若是因此失落沮丧，怎么能继续积累最终发挥出脑中的想法呢？
还是继续前进吧，尽管此刻已成定局。
但前方的道路依然漫长迷人。&lt;/p&gt;
&lt;p&gt;总结这学期的开始，首先结束RM是对的，若是继续打RM，我不可能有时间提升自己的广度与深度，去年打了一年已经让我停滞不前了；其次竞赛之外实践的时间太少了，校内课程的时间太多了，很可惜，我回想起来，在时间分配上没有明显的失误，那么以我目前的眼光来看这应该是无可奈何之事。&lt;/p&gt;
&lt;p&gt;到了期中，这一周应该是最麻烦的一周了，两项竞赛都挨在一起。或许不该答应陈老师继续打工创赛的，我不需要；但是工创赛每届都需要新老交替，我不去几乎是断了这样的传承，因此我去了；虽然现在真的挺难顶，但是再来一次还是会的，我认为这就是一种自信，我已经很少正儿八经地后悔了，给予过去的自己、现在的自己、未来的自己以充足的信任。&lt;/p&gt;
&lt;p&gt;那么就是这样。既然过去没有显著错误、现在没有显著错误、未来也不期望有显著错误，这样的反思也就可以结束。&lt;/p&gt;
</content:encoded></item><item><title>启读毛德操《香山源代码剖析》</title><link>https://zhaocake.github.io/posts/2024-11-11/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2024-11-11/</guid><description>还是买了一套，虽然按道理以我的水平应该先通过别的书打好基础的。不过京东224、pdd131的情况下我还是买了一套。是正版，是pdd新华书店官方旗舰店的，书的质量很好无异味，确实是正版。</description><pubDate>Mon, 11 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;还是买了一套，虽然按道理以我的水平应该先通过别的书打好基础的。不过京东224、pdd131的情况下我还是买了一套。是正版，是pdd新华书店官方旗舰店的，书的质量很好无异味，确实是正版。&lt;/p&gt;
&lt;h2&gt;初见&lt;/h2&gt;
&lt;p&gt;这套书真是太后了，怪不得分三卷。但我甚至希望能够分六卷，因为这样的单卷大小想要带到教室里面去看是过于沉重了。&lt;/p&gt;
&lt;p&gt;书虽然厚，但是找到好好阅读它的途径更重要，即使需要每天把书搬来搬去，只要能有所收获就是值得的。初次翻开毛德操先生的这本书，首先我感到的是这本书的语言风格是直白的，相比于国内不说人话的教材，本书的语言有一种计算机黑书系列的亲切。毛德操老师以叙述性的口吻展开了讲授，令人倍感亲切。&lt;/p&gt;
&lt;p&gt;然而讲得好是一码事，学得到多少是令一码事。显而易见的，这样厚度的书，及时每天只读这部书、读很多天，也难以顺畅读完，如果我抱着“拿着这本书死啃”的信念去读，恐怕会适得其反。我想我首先应该去阅读目录，看看各个部分分别讲得是什么，考虑自己对各个部分的熟悉程度，然后再选择阅读哪个部分。&lt;/p&gt;
&lt;h2&gt;章节结构分析&lt;/h2&gt;
&lt;h3&gt;第一卷&lt;/h3&gt;
&lt;p&gt;第一章，讲的是超大规模集成电路，随之引出第二章的HDL介绍，随之又引出本书的第一个主题，Chisel,环环相扣，不可谓不引人入胜；然而我终究没有这样的时间去这样按照顺序读完。&lt;/p&gt;
&lt;p&gt;从第三章到第六章，都是对chisel的讲解，包括了chisel进行硬件设计的工作流程、设计思路、语法特点等。这一部分更贴近“技”，其中蕴含着设计的思想，但是比较间接，而且语言这种东西，不使用就难以熟练，纸上得来终觉浅，这部分不适合单纯地阅读。&lt;/p&gt;
&lt;p&gt;这之后的就是从介绍常见的一些RISC指令集的处理器开始逐步介绍一个RISC-V处理器的设计了，包括了内部通信总线、时钟域管理等内容，由于这些我所知甚少，故不应先入为主过多判断。&lt;/p&gt;
&lt;h3&gt;第二卷、第三卷&lt;/h3&gt;
&lt;p&gt;同上段末所述，评价不知道的东西是不适合的。&lt;/p&gt;
&lt;h2&gt;阅读期待&lt;/h2&gt;
&lt;p&gt;读书，尤其是这样厚的一本书，需要有目的，否则就轻易在书中迷失了。并且我不应该因为自己的知识微薄而拖慢阅读的进度，应该是我的知识去追赶阅读的进度&lt;/p&gt;
</content:encoded></item><item><title>微机原理课程讨论存档</title><link>https://zhaocake.github.io/posts/2024-11-4/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2024-11-4/</guid><description>模块化的子程序模块的调用流程</description><pubDate>Mon, 04 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;模块化的子程序模块的调用流程&lt;/h3&gt;
&lt;p&gt;模块化编程通过将代码分解成独立的功能单元（子程序或函数）实现。这种方法提高了代码的可读性、可维护性以及重用性。&lt;/p&gt;
&lt;h4&gt;参数传递方式总结&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;寄存器传递&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用CPU寄存器来传递参数，这是最快的一种方式，但受寄存器数量的限制。&lt;/li&gt;
&lt;li&gt;常用于传递小数量的整数或指针。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;堆栈传递&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将参数压入堆栈。比如，在x86架构上，最常见的是右至左的压栈顺序。&lt;/li&gt;
&lt;li&gt;有利于支持可变参数列表。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;全局变量&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过定义全局变量传递参数。简单但不够灵活，易引发错误。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;内存块传递&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;传递一个指向内存块的指针，用于较大的数据传递。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;汇编中的程序分支与模块化代码的实现&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;条件跳转&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用条件跳转指令（如&lt;code&gt;JZ&lt;/code&gt;, &lt;code&gt;JNZ&lt;/code&gt;, &lt;code&gt;JG&lt;/code&gt;, &lt;code&gt;JL&lt;/code&gt;）来实现代码分支。&lt;/li&gt;
&lt;li&gt;通过标志寄存器（Flags）来确定分支的条件。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;循环结构&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;LOOP&lt;/code&gt;指令或结合条件跳转控制寄存器确保循环执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;子程序调用&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;CALL&lt;/code&gt;和&lt;code&gt;RET&lt;/code&gt;指令实现子程序的调用与返回。&lt;/li&gt;
&lt;li&gt;将需要重复的代码封装为子程序，并通过参数传递实现模块化。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;任意字节长度的加减法程序实现及模块化&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;加法实现&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;ADC&lt;/code&gt;或&lt;code&gt;ADD&lt;/code&gt;联合处理高低字节，通过进位标志位（CF）实现跨字节计算。&lt;/li&gt;
&lt;li&gt;模块化实现可以将每个字节的加法封装成一个循环操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;减法实现&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;类似加法，通过&lt;code&gt;SBB&lt;/code&gt;指令协调处理跨字节的借位运算。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;; 示例：双字节数的加法
AddNumbers:
    clc            ; 清除进位
    mov al, [si]   ; 读入第一个字节
    adc al, [di]   ; 加上第二个字节，结合进位
    stosb          ; 存储结果
    inc si
    inc di
    loop AddNumbers
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;任意字节长度的非循环逻辑移位实现及模块化&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;左移&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;直接通过&lt;code&gt;SHL&lt;/code&gt;指令，对每一字节左移，并配合进位实现多字节的左移操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;右移&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;SHR&lt;/code&gt;指令控制每字节右移。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模块化设计&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将移位操作设计为可调用函数，接受移位长度及数据起始地址等参数。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;处理器发展中使用的新技术解释&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;流水线（Pipeline）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将指令分解为多个阶段（取指、解码、执行、存储），并行处理不同指令的不同阶段，提高CPU效率。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;缓存（Cache）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;位于CPU内部的高速内存，用于存储频繁访问数据以加快数据访问速度。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;预取指与预分析（Prefetching &amp;amp; Pre-decoding）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;预先从内存获取、解码即将执行的指令，提高CPU的执行效率，减少等待时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;增强指令集&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过增加新的指令支持特定的运算，如SIMD指令，用于加速多媒体和科学计算。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;实地址与保护虚地址&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实地址模式中，程序可以直接访问物理存储器位置。&lt;/li&gt;
&lt;li&gt;虚地址保护模式下，应用程序使用虚拟地址，由MMU（内存管理单元）将其映射到实际物理内存，提供更好的安全性和稳定性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以上每项技术都在不断推动处理器复杂度和性能的提升，这使得现代计算能够高效地处理越来越复杂的应用程序和任务。&lt;/p&gt;
</content:encoded></item><item><title>如何升级ccache的版本 || 安装ccache</title><link>https://zhaocake.github.io/posts/2024-04-28/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2024-04-28/</guid><description>由于一些原因之前用的是3.7的ccache，但是遇到了一个bug。</description><pubDate>Sun, 28 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;由于一些原因之前用的是3.7的ccache，但是遇到了一个bug。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ccache: error: Recursive invocation (the name of the ccache binary must be &quot;ccache&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;经过查找，这个bug在4版本的时候被修复了，所以就要升级版本。&lt;/p&gt;
&lt;p&gt;ccache的文件比较单一，环境变量、软链接啥的都是已经配置好的，所以这次一切从简，直接替换文件即可。
&lt;img src=&quot;/assets/f834349ae83f07e4a88231a9e303bd93.png&quot; alt=&quot;请添加图片描述&quot; /&gt;
然后顺利开始编译。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在下载的文件中是可以找到manual的，它的ccache是放在local下的，和我不太一样。
但是话说回来，实际上手动安装的库文件、可执行文件一般都是在local下的，是我操作不规范了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以顺便贴一下这个manual的安装步骤&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;运行模式&lt;/p&gt;
&lt;p&gt;使用ccache缓存编译有两种不同的方法：&lt;/p&gt;
&lt;p&gt;在编译命令前面加上ccache。如果您只是想尝试ccache或希望将其用于某些特定项目，则此方法是最方便的。实例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ccache gcc -c example.c
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;让ccache伪装成编译器。当您希望对所有编译使用ccache时，此方法最有用。为此，创建一个指向ccache的符号链接，命名为编译器。例如，这里设置ccache以伪装成gcc和g++：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cp ccache/usr/local/bin/
ln-s ccache/usr/local/bin/gcc
ln-s ccache/usr/local/bin/g++
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在不支持符号链接的平台上，您可以简单地将ccache复制到编译器名称，以获得类似的效果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cp ccache/usr/local/bin/gcc
cp ccache/usr/local/bin/g++
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;等等只要带有符号链接或ccache副本的目录位于PATH中带有编译器（通常为/usr/bin）的目录之前，这将起作用。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;我的环境变量设置命令如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export USE_CCACHE=1
export CCACHE_DIR=&quot;/home/{your-username}/.ccache&quot;
export CC=&quot;ccache gcc&quot;
export CXX=&quot;ccache g++&quot;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>【Git】已经在拉取时以HTTP的URL拉取的仓库使用SSH的URL进行push</title><link>https://zhaocake.github.io/posts/2024-4-25/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2024-4-25/</guid><description>添加remote url即可 ---</description><pubDate>Thu, 25 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;添加remote url即可&lt;/h2&gt;
&lt;p&gt;添加前：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;➜  Linux-for-Newbies git:(main) git remote -v
origin  https://github.com/ZhaoCake/Linux-for-Newbies.git (fetch)
origin  https://github.com/ZhaoCake/Linux-for-Newbies.git (push)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git remote add --mirror=push ssh_one git@github.com:ZhaoCake/Linux-for-Newbies.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加后：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;➜  Linux-for-Newbies git:(main) git push ssh_one
The authenticity of host &apos;github.com (20.205.243.166)&apos; can&apos;t be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?yyes
Warning: Permanently added &apos;github.com&apos; (ED25519) to the list of known hosts.
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 2.88 KiB | 46.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To github.com:ZhaoCake/Linux-for-Newbies.git
   1b13d37..3833b61  main -&amp;gt; main
 * [new reference]   origin/HEAD -&amp;gt; origin/HEAD
 * [new reference]   origin/main -&amp;gt; origin/main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如何生成SSH公钥，资料很多，不必分享。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh-keygen -t rsa
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>文献翻译 || Ubuntu安装zotero文献管理软件，提高文献阅读效率</title><link>https://zhaocake.github.io/posts/2024-01-27/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2024-01-27/</guid><description>前言</description><pubDate>Sat, 27 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;虽然在win下有很多文献管理软件和好用的文献翻译软件，但是如果平常有使用Ubuntu进行开发的需求，实际上很不愿意为了好好看文献专门切到Windows系统，所以在Ubuntu或者其他Linux发行版上安装文献管理软件还是很有必要的。&lt;/p&gt;
&lt;p&gt;本文安装的文献管理软件是zotero，这是一款开源的文献管理软件，&lt;a href=&quot;https://github.com/retorquere/zotero-deb&quot;&gt;deb系仓库链接&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;安装方式选择&lt;/h2&gt;
&lt;p&gt;如果直接搜索安装方式，那么大部分安装方式都会是按照仓库里的要求添加软件源并安装。但是如果并不渴求最新版的话，没有必要一定从正在更新的仓库里去安装。从方便省事的角度考虑，先在现有软件源里搜索，没有再去添加源才是合适的做法。&lt;/p&gt;
&lt;h3&gt;apt方式&lt;/h3&gt;
&lt;p&gt;尝试&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo aptitude install zotero
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;出现以下返回&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[sudo] zhaokace 的密码： 
Couldn&apos;t find any package whose name is &quot;zotero&quot;, but there are 1 packages which contain &quot;zotero&quot; in their name:
  unity-scope-zotero 
无法应用某些操作，中止
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可知实际上在Ubuntu现有源的apt包管理索引中是有这个软件的，可以安装&lt;code&gt;unity-scope-zotero&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;snap方式&lt;/h3&gt;
&lt;p&gt;然后再试试snap软件包中有没有&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;snap search zotero
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;返回以下信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;名称           版本      发布者         注记   摘要
zotero-snap  6.0.27  extraymond  -    Zotero helps you collect, organize, cite, and share research.
qnotero      2.3.1   ealbiter    -    Standalone sidekick to Zotero reference manager.
zotviewer    2.2.1   fvalle      -    Zotero Visualizer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;故知是存在的，并且是6.0.27版本，但是我们并不知道snap上的软件到底有没有按时更新，这个版本是否能够满足我们的需要。
使用命令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;snap info zotero-snap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;返回以下信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name:      zotero-snap
summary:   Zotero helps you collect, organize, cite, and share research.
publisher: extraymond
store-url: https://snapcraft.io/zotero-snap
contact:   https://github.com/extraymond/zotero-snap
license:   AGPL-3.0
description: |
  Zotero is a free, easy-to-use tool to help you collect, organize, cite, and
  share research.
snap-id: atdqTPsFer1lIGP6OI4LXOzTLA0ZiOhK
channels:
  latest/stable:    6.0.27 2023-09-17 (65)  79MB -
  latest/candidate: 6.0.27 2023-09-17 (65)  79MB -
  latest/beta:      beta   2023-09-17 (67) 117MB -
  latest/edge:      6.0.27 2023-10-05 (69)  79MB -
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;故知更新情况还是比较新的，可以比较放心地使用。&lt;/p&gt;
&lt;p&gt;最终选择snap方式安装，这种安装方式更加省心，安装和卸载都不容易伤到其他部分。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;snap install zotero-snap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/assets/2864050b72b9d46a9b6be7971b8295c5.png&quot; alt=&quot;请添加图片描述&quot; /&gt;安装成功。&lt;/p&gt;
&lt;h2&gt;zotero的简单使用&lt;/h2&gt;
&lt;h3&gt;文献导入&lt;/h3&gt;
&lt;p&gt;除了左上角标准的文献导入方式，也可以直接拖拽pdf格式文件导入，软件会自动搜索项目名称等信息。
&lt;img src=&quot;/assets/f37d4e2ad8e0a6478fb7d403e7f9adf5.png&quot; alt=&quot;在这里插入图片描述&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;中文翻译插件&lt;/h3&gt;
&lt;h4&gt;下载并安装&lt;/h4&gt;
&lt;p&gt;这才是我要装文献管理软件的主要原因啊！浏览器翻译插件似乎只对Html页面有效，对于PDF格式文件则不起作用，英语及格的我只能装翻译插件了。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zotero.yuque.com/staff-gkhviy/pdf-trans/bwxwxh&quot;&gt;插件开发者的文档&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/7f6ffb4490859e874cc691a4a0c60b4f.png&quot; alt=&quot;在这里插入图片描述&quot; /&gt;
注意作者的提醒嗷。另外，我按照上面snap安装方式安装的是zotero6的版本，所以下载插件时应当下载稳定版本而不是预发布版本。故下载1.0.23版本哦。&lt;/p&gt;
&lt;h4&gt;使用体验&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;/assets/961aa0a197dbebe785734f77ec6c92f2.png&quot; alt=&quot;在这里插入图片描述&quot; /&gt;我的评价是可以！&lt;/p&gt;
</content:encoded></item><item><title>误操作导致.zshrc文件清空的抢救措施||注意重定义操作符的用法||shell的配置文件坏掉</title><link>https://zhaocake.github.io/posts/2024-01-25/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2024-01-25/</guid><description>序言</description><pubDate>Thu, 25 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;序言&lt;/h2&gt;
&lt;p&gt;&amp;lt;mark&amp;gt;进来提醒第一件事：以后记得定时备份！定时备份！定时备份！&amp;lt;/mark&amp;gt;&lt;/p&gt;
&lt;p&gt;因为一个误操作，我把自己的&lt;code&gt;.zshrc&lt;/code&gt;搞坏了。&lt;/p&gt;
&lt;h2&gt;误操作介绍&lt;/h2&gt;
&lt;p&gt;先介绍一下我是怎么把配置文件搞坏的。我是通过如下命令导致的，也就是我的上一篇文章，关于riscv的gnu工具链安装的时候，将bin目录写入环境变量少了一个&amp;lt;号&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正确的：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo “export PATH=/opt/riscv/bin:$PATH” &amp;gt;&amp;gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;错误的：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo “export PATH=/opt/riscv/bin:$PATH” &amp;gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;是重定向操作符，可以追加写而不会覆盖原文件，我由于少了一个&lt;code&gt;&amp;gt;&lt;/code&gt;，酿成了大错。&lt;/p&gt;
&lt;h2&gt;抢救过程&lt;/h2&gt;
&lt;p&gt;当然，如果你的.zshrc文件真的已经寄了，并且像我一样没有备份，那么应该是做不到完全抢救回来的，能救多少救多少吧&lt;/p&gt;
&lt;h3&gt;环境介绍&lt;/h3&gt;
&lt;p&gt;我是zsh，并且使用了oh-my-zsh作为配置zsh的工具——oh-my-zsh对于抢救我的环境做出卓越贡献！&lt;/p&gt;
&lt;p&gt;此时我运行了上面的错误命令，并且毫无察觉地运行了&lt;code&gt;source ~/.zshrc&lt;/code&gt;，出现了错误，并且出现在第一行！我知道，糟了！&lt;/p&gt;
&lt;h3&gt;开始抢救&lt;/h3&gt;
&lt;h4&gt;存储当前环境变量&lt;/h4&gt;
&lt;p&gt;如我刚才所说，source刷新环境变量的命令报错了，刷新环境变量失败，虽然我如果新打开终端那么我这个终端必然已经没有办法使用了，但是对于我现有的终端来说，环境变量依然建在，我们可以通过以下命令存储当前环境中苟延残喘的环境变量和其他配置信息：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;set &amp;gt; ~/.zshrc.envvars      # 环境变量
alias &amp;gt; ~/.zshrc.aliases     # 别名
functions &amp;gt; ~/.zshrc.functions # 函数方法
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;环境变量对于之后的修复还是挺有用的，另外两个如果你没有什么特殊的设置就可以直接不做了。
如果你对于.zshrc的组成足够了解，那么现在你就可以开始根据这些变量重新写一份了（不建议）！&lt;/p&gt;
&lt;h4&gt;复制一份新的.zshrc&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;补充内容：我在之后查资料的过程中了解到可以使用&lt;code&gt;print -l ${(ok)env} &amp;gt; ~/.zshrc.envbackup &lt;/code&gt;这个命令存储当前环境变量，不知道和上面的那种方式有什么不同。上面那种存储环境变量的方式真的是大海捞针。
然后在完成了下面的步骤之后，可以通过&lt;code&gt;cat ~/.zshrc.envbackup &amp;gt;&amp;gt; ~/.zshrc &lt;/code&gt;命令写入下面生成的新的.zshrc文件。但是我对于这部分内容没有进行尝试，掉了的环境变量是我手动添加的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在oh-my-zsh的项目文件夹中，具有一份.zshrc文件模板，现在我们复制这个模板&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后再更新环境变量就可以了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后你就可以重新开始导入环境变量了&lt;/p&gt;
&lt;p&gt;PS：能用就不错了&lt;/p&gt;
</content:encoded></item><item><title>【小白向】MMDeploy安装部署|暗坑标注版</title><link>https://zhaocake.github.io/posts/2024-01-20/</link><guid isPermaLink="true">https://zhaocake.github.io/posts/2024-01-20/</guid><description>序言</description><pubDate>Sat, 20 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;序言&lt;/h2&gt;
&lt;p&gt;本文主要针对在编译安装OpenMMLab团队的MMDeploy模型部署工具时遇到的“难以下手”的问题。
由于OpenMMLab的用户中很大一部分都是具有快速开发需求的人，或者说其实相当部分OpenMMlab开源算法体系的用户都没有特别扎实的代码基础或者理论基础，比如说我，在面对MMDeploy这个相对于OpenMMLab其他算法库而言复杂了许多的代码库时会有一些畏难情绪。
此外由于很多时候接触Cpp比较少，对于其中的编译安装有很多不理解的地方，环境变量稍稍一变动就高不明白了，本文主要对于一些我之前作不来但是现在能做的地方进行分享与展示。&lt;/p&gt;
&lt;h2&gt;正文&lt;/h2&gt;
&lt;p&gt;首先参考教程：(&lt;a href=&quot;https://github.com/open-mmlab/mmdeploy/blob/main/docs/zh_cn/01-how-to-build/linux-x86_64.md&quot;&gt;https://github.com/open-mmlab/mmdeploy/blob/main/docs/zh_cn/01-how-to-build/linux-x86_64.md&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;如果教程由于在GitHub上你访问起来有困难，那么请移步&lt;a href=&quot;https://mmdeploy.readthedocs.io/zh-cn/latest/index.html&quot;&gt;官方中文文档&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;现摘取其中容易出问题的地方进行讲解。&lt;/p&gt;
&lt;p&gt;跟着教程走一般都不会有问题，关键点在于&amp;lt;mark&amp;gt;“不换终端”&amp;lt;/mark&amp;gt;，当然，如果你看了下面的文字知道发生了什么之后，你换终端也没有什么事。&lt;/p&gt;
&lt;h3&gt;1 安装PPLCV&lt;/h3&gt;
&lt;p&gt;教程中所给代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/openppl-public/ppl.cv.git
cd ppl.cv
export PPLCV_DIR=$(pwd)
git checkout tags/v0.7.0 -b v0.7.0
./build.sh cuda
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里首先需要注意的是&lt;code&gt;export PPLCV_DIR=$(pwd)&lt;/code&gt;这一步，这是为了添加ppl.cv这个包的环境变量。
linux的环境变量，如果直接这样运行命令的话就是临时添加，如果你另起一个终端或者是使用&lt;code&gt;source ~/.bashrc&lt;/code&gt;之类的命令刷新了环境变量，那么这一步就需要重做了。&lt;/p&gt;
&lt;p&gt;其余在这个教程中涉及到这种什么什么“DIR”的也是一样的道理。&lt;/p&gt;
&lt;h3&gt;2 TensorRT环境相关&lt;/h3&gt;
&lt;p&gt;因为我是在Nvidia显卡上进行模型部署，所以下面也主要依托这个来讲。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd /the/path/of/tensorrt/tar/gz/file
tar -zxvf TensorRT-8.2.3.0.Linux.x86_64-gnu.cuda-11.4.cudnn8.2.tar.gz
pip install TensorRT-8.2.3.0/python/tensorrt-8.2.3.0-cp37-none-linux_x86_64.whl
export TENSORRT_DIR=$(pwd)/TensorRT-8.2.3.0
export LD_LIBRARY_PATH=$TENSORRT_DIR/lib:$LD_LIBRARY_PATH
pip install pycuda
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是教程中用于安装TensorRT包的命令。
只要选择和自己的cuda版本相应的就可以了。
如果你已经在做模型部署却连cuda都没有下载的话，那么请浏览器搜索引擎搜索Nvidia Cuda进入下载页面，根据你的环境进行选择，然后选择run脚本安装的方式安装cuda。
&lt;strong&gt;不要装deb包！不要装deb包！不要装deb包！&lt;/strong&gt;
装deb包会连同你的驱动一起重装，一般而言是不合适的；而且这种库用APT或者YUM装，心里有点不踏实。
确认cuda装好了，就是我刚刚说的选择合适版本的TensorRT包就可以了，选择Tar包，安装没你以为的比Deb包麻烦，知道文件都放在哪里的感觉令人安心。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;截至1月20日，该文编辑时间，已经有cuda12.2了，但是没有与该版本对应的TensorRT版本，别担心，实测12.1能用的就行，换句话说，最新的能用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;下面的两步中，一个又是设置TENSORRT_DIR环境变量的，后面的cudnn安装也要设置这个。这是为了编译MMDeploy的内容才这么做的，一般来说项目里面不会这么做，可能只是会在CMakeLists中通过set方法在文件内定义一下。这里用这个方式传入库的信息其实已经非常简化了流程了；而下面的第二条环境变量指令是将库文件所在引入环境变量，是常见的配置TensorRT环境的步骤。&lt;/p&gt;
&lt;p&gt;我之前就装过，所以仅仅进行&lt;code&gt;export TENSORRT_DIR=/opt/TensorRT-8.9.1.6&lt;/code&gt;，这是我安装TensorRT的位置，另一条指令则写入了.bashrc文件。&lt;/p&gt;
&lt;p&gt;注意不要忘记装pycuda。&lt;/p&gt;
&lt;p&gt;然后是安装Cudnn这个深度学习加速库。命令上也是差不多的。
对于之前装过Cudnn的人来说，一般来说就和cuda的库文件放在一起的。也就是&lt;code&gt;/usr/local/cuda&lt;/code&gt;中，准确的应该要加版本号，不过一般都有软链接，应该是运行装cuda的时候自带的设置软链接。从而我们也可以粗略得出一种管理多版本cuda的方法——应该可以通过临时改换软链接的指向来确定当前使用的cuda环境。当然，这样做在写CMake的时候还不如直接接上版本号来得方便。&lt;/p&gt;
&lt;p&gt;&amp;lt;mark&amp;gt;后面接着说了如果想永久有效的话就写入当前所用shell的配置文件&amp;lt;/mark&amp;gt;，不一定就是.bashrc哦，也许你是别的shell。不过如果你不知道这个的话你多半就是.bashrc了。&lt;/p&gt;
&lt;h3&gt;3 编译MMDeploy&lt;/h3&gt;
&lt;p&gt;也是有一个设置环境变量的过程。没啥难度。&lt;/p&gt;
&lt;p&gt;不过另一个需要注意的地方是，MMDeploy中是有子模块的，不过因为是放在一个文件夹“ThirdPart”还是什么名字之下的，你克隆的时候可能没有看见。&lt;/p&gt;
&lt;p&gt;在克隆仓库时克隆子模块则加上&lt;code&gt;--recursive&lt;/code&gt;参数；如果已经克隆了仓库但没有克隆子模块，则运行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git submodule update
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;来更新子模块。&lt;/p&gt;
&lt;h3&gt;4 编译SDK&lt;/h3&gt;
&lt;p&gt;如果你前面都没什么问题，最后模型也转换出来了，但是最后你没办法完成推理，那么多半是SDK编译不对，&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd ${MMDEPLOY_DIR}
mkdir -p build &amp;amp;&amp;amp; cd build
cmake .. \
    -DCMAKE_CXX_COMPILER=g++-7 \
    -DMMDEPLOY_BUILD_SDK=ON \
    -DMMDEPLOY_BUILD_EXAMPLES=ON \
    -DMMDEPLOY_BUILD_SDK_PYTHON_API=ON \
    -DMMDEPLOY_TARGET_DEVICES=&quot;cuda;cpu&quot; \
    -DMMDEPLOY_TARGET_BACKENDS=trt \
    -Dpplcv_DIR=${PPLCV_DIR}/cuda-build/install/lib/cmake/ppl \
    -DTENSORRT_DIR=${TENSORRT_DIR} \
    -DCUDNN_DIR=${CUDNN_DIR}

make -j$(nproc) &amp;amp;&amp;amp; make install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以TensorRT的推理的SDK的编译为例，需要注意的地方就是这几个主目录，另外是&lt;code&gt;-DCMAKE_CXX_COMPILER=g++-7 \&lt;/code&gt;这个参数里面的7需要根据你自己的版本进行修改，比如我是9。
如果出现什么找不到库之类的情况，那么应该就是你的哪个环境变量掉了。你可以使用echo命令来看它到底指向哪里或者并不存在，例如&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo ${PPLCV_DIR}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;应该返回该变量的路径。&lt;/p&gt;
&lt;p&gt;也就是说其实如果你没有设置这些环境变量但是你记得这些库的路径的话，这几个编译选项就直接传入你的库路径也是一样的，使用设置环境变量的方式是为了简化流程与降低环境的安装难度，但是如果你觉得这样更加麻烦的话当然就不需要这么做了。&lt;/p&gt;
&lt;h3&gt;结束&lt;/h3&gt;
&lt;p&gt;如果出现其他问题欢迎留言讨论。&lt;/p&gt;
</content:encoded></item></channel></rss>