RISC-V入门必读:base ISA与ABI寄存器约定详解
1. 从零上手 RISC-V为什么 base ISA 和 ABI 寄存器约定是绕不开的第一道坎接触 RISC-V 的人十有八九是从一块开发板或者一个模拟器开始的。你可能已经跑通了第一个裸机程序或者用工具链编译了一个简单的 C 程序烧进去看着串口打印出 “Hello RISC-V” 心里挺美。但接下来想深入一点比如自己写个启动文件、对接一下中断、或者把 FreeRTOS 往上搬的时候大概率会卡在同一个地方寄存器到底该怎么用谁负责保存谁可以随便改。这个问题不搞清楚写出来的代码要么跑飞要么在某个不经意的函数调用后数据全乱套。这就是 base ISA 和 ABI 寄存器约定要解决的事。base ISA 定义了处理器能看懂哪些指令、有哪些寄存器、内存怎么访问它是硬件和软件之间的第一层契约。而 ABI 寄存器约定则是在此之上规定了函数调用时参数放哪、返回值放哪、哪些寄存器由调用者保存、哪些由被调用者保存。两者一个管“能不能做”一个管“怎么做才不乱”。对于做 RISC-V CPU 设计的人来说base ISA 是你要实现的指令集子集对于写底层软件的人来说ABI 约定是你写汇编和 C 混合代码时必须刻在脑子里的规则。这篇文章适合两类人看。一类是刚开始学 RISC-V、准备自己写启动代码或者移植操作系统的嵌入式开发者另一类是对 RISC-V 处理器设计感兴趣、想搞清楚指令集和调用规范之间关系的学生或工程师。我会从 base ISA 的寄存器结构讲起把 RV32I 和 RV64I 的差异、x0 到 x31 的用途、ABI 名称的由来、调用者保存与被调用者保存的划分逻辑以及实际写代码时怎么用这些规则全部拆开揉碎讲一遍。中间会穿插一些我在实际项目里踩过的坑比如栈对齐没做导致浮点运算崩溃、临时寄存器用错导致中断返回后数据错乱之类的希望能帮你少走点弯路。2. base ISA 到底定义了什么寄存器、指令格式与地址空间2.1 通用寄存器的物理布局与设计哲学RISC-V 的 base ISA 定义了一个非常规整的通用寄存器堆总共 32 个整数寄存器在 RV32I 里每个宽度是 32 位在 RV64I 里是 64 位。编号从 x0 到 x31这个编号是硬件层面的你在写汇编的时候可以直接用 x0、x1 这样的名字也可以用 ABI 名称如 zero、ra、sp 等。硬件只认编号ABI 名称是给人和编译器看的。x0 这个寄存器很特殊它被硬连线为常量 0。什么意思呢就是你读它永远得到 0写它会被直接丢弃不会报错也不会有任何效果。这个设计看起来有点浪费但实际上非常巧妙。它让很多指令可以简化比如addi x0, x0, 0就成了一条空操作指令 nop不需要单独设计 nop 指令。再比如mv x1, x2实际上就是addi x1, x2, 0的伪指令形式。有了 x0指令编码可以更规整译码逻辑也更简单。剩下的 31 个寄存器没有硬件强制规定的用途理论上你可以在汇编里随便用。但一旦涉及到函数调用、中断处理、操作系统就必须遵守 ABI 约定否则你写的代码没法跟编译器生成的代码对接也没法跟库函数配合。这就是为什么理解 ABI 寄存器约定比单纯记住寄存器编号更重要。2.2 RV32I 与 RV64I 的关键差异base ISA 目前最常用的是 RV32I 和 RV64I 两个版本I 表示整数指令集是必须实现的基础。RV32I 有 32 个 32 位寄存器地址空间是 32 位也就是 4GB。RV64I 把寄存器宽度和地址空间都扩展到 64 位但寄存器个数还是 32 个。这里有个容易混淆的点RV64I 的寄存器是 64 位宽但指令长度还是 32 位不考虑压缩指令集。这意味着指令里能直接编码的立即数范围变小了所以 RV64I 增加了一些新的指令来加载 64 位立即数比如lui和addiw的组合用法就跟 RV32I 不太一样。另外 RV64I 里所有涉及字操作的指令比如addw、subw、sllw都是对低 32 位做运算然后符号扩展到 64 位。这个细节在写汇编的时候特别容易出错我见过有人在 RV64 上做 32 位加法忘了用addw结果高位垃圾数据导致条件判断完全错误。还有一个实际项目里经常遇到的差异RV32I 的函数参数和返回值用 a0-a7 这 8 个寄存器传递RV64I 也是一样但因为寄存器变宽了传递 64 位指针或者 long long 类型时就不用像 RV32 那样拆成两个寄存器了。这个差异在移植代码的时候要特别注意尤其是涉及指针运算和结构体传参的地方。2.3 指令格式与寄存器编码的对应关系RISC-V 的指令格式有 R 型、I 型、S 型、B 型、U 型、J 型六种每种格式里寄存器的位置和位数都是固定的。比如 R 型指令里rs1、rs2、rd 各占 5 位正好能覆盖 32 个寄存器。这个 5 位的设计不是随便定的它直接决定了寄存器堆的大小就是 32 个不能再多了。为什么是 32 个而不是 16 个或者 64 个16 个寄存器对于编译器优化来说太少了很多临时变量放不下会导致频繁的栈读写。64 个寄存器又会让指令编码变长因为每个寄存器字段要 6 位三条寄存器操作数的指令就会多出 3 位整个指令长度可能要从 32 位变成 40 位甚至更多这对代码密度和译码复杂度都不利。32 个寄存器是一个经过权衡的甜点值既能满足大部分编译需求又能保持指令长度固定为 32 位。在实际写汇编或者看反汇编的时候你会发现指令里的寄存器编号就是 x0 到 x31 的二进制编码。比如add x1, x2, x3对应的机器码里rd 字段是 1rs1 字段是 2rs2 字段是 3。理解这个对应关系对调试很有帮助尤其是当你只有机器码没有反汇编器的时候可以手动译码看看寄存器用得对不对。3. ABI 寄存器约定函数调用背后的隐形规则3.1 调用者保存与被调用者保存的划分逻辑ABI 寄存器约定最核心的内容就是把 32 个寄存器分成两类调用者保存caller-saved和被调用者保存callee-saved。这个划分的逻辑其实很直观如果一个寄存器被调用者要用但调用者之后还要用它的值那被调用者就得先保存再恢复这就是被调用者保存。反过来如果调用者知道某个寄存器在函数调用后可能被改掉那调用者自己在调用前就得保存这就是调用者保存。具体到 RISC-V 的 ABI被调用者保存的寄存器是 s0-s11也就是 x8、x9、x18-x27还有 spx2。调用者保存的寄存器包括 t0-t6x5-x7、x28-x31、a0-a7x10-x17以及 rax1。ra 比较特殊它虽然是调用者保存但通常由被调用者在入口处保存到栈上因为被调用者自己还要调用别的函数需要把返回地址存起来。这个划分不是拍脑袋定的它直接影响了编译器的寄存器分配策略。编译器会尽量把生命周期长的变量放在 s 寄存器里因为它们在函数调用后还能保持把临时变量放在 t 寄存器里因为不需要额外保存。你在写汇编的时候如果乱用寄存器比如在一个循环里用了 t0 然后又调用了函数那 t0 的值很可能就被破坏了这种 bug 特别难查因为编译器生成的代码和你的汇编混在一起出问题的地方往往离现场很远。3.2 参数传递与返回值a0-a7 和栈的配合RISC-V 的 ABI 规定函数的前 8 个整型参数用 a0-a7 传递超出的部分通过栈传递。返回值放在 a0 和 a1 里a0 是主返回值a1 是辅助返回值比如返回一个结构体的时候会用到。浮点参数用 fa0-fa7 传递浮点返回值用 fa0。这里有个细节值得注意参数在栈上的布局是从右往左还是从左往右RISC-V 的约定是超出寄存器范围的参数从栈顶开始依次排列而且栈指针 sp 在函数入口处必须保持 16 字节对齐。这个 16 字节对齐的要求经常被忽略尤其是在手写汇编的时候。我见过一个案例有人在中断处理程序里手动调整了 sp 但没有保持对齐结果调用了浮点运算库函数库函数里用了对齐的向量加载指令直接触发异常。查了半天才发现是栈对齐的问题。另外结构体传参的规则也比较绕。小于等于两个 XLEN 的结构体会用寄存器传递大于两个 XLEN 的通过栈传递而且传递的是结构体的副本。这个规则在写 C 和汇编混合代码的时候要特别小心因为汇编里你得手动按照 ABI 的布局去取参数搞错了就会读到垃圾数据。3.3 栈帧布局与帧指针的取舍标准的 RISC-V 栈帧布局是这样的调用者在调用前把超出寄存器的参数压栈然后执行 call 指令call 会把返回地址放到 ra 里。被调用者在入口处先调整 sp 开辟自己的栈帧然后把需要保存的寄存器和 ra 压栈。如果用了帧指针 fp也就是 s0还要把旧的 fp 保存起来然后把当前 sp 赋给 fp。帧指针用不用在 RISC-V 社区里一直有争论。用了 fp 的好处是栈回溯方便调试器可以顺着 fp 链找到调用栈。不用 fp 的好处是省了一个寄存器而且少了两条指令保存旧 fp 和设置新 fp。在资源紧张的嵌入式场景里很多人选择不用 fp把 s0 当普通寄存器用。但这样一来如果程序崩溃了你只能靠 sp 和 ra 去猜调用栈调试难度会大不少。我的建议是在开发阶段打开 fp方便调试在发布版本里根据实际情况决定是否关掉。GCC 和 Clang 都有-fomit-frame-pointer选项来控制这个行为。如果你在写启动代码或者中断处理程序那 fp 基本用不上因为那些代码通常不遵循标准的函数调用约定。4. 实操手写汇编验证 ABI 寄存器约定的行为4.1 搭建一个最小的裸机测试环境要验证 ABI 寄存器约定最直接的办法就是写一段汇编调用一个 C 函数然后观察寄存器的变化。我用的是 QEMU 的 virt 机器配合 riscv64-unknown-elf-gcc 工具链。如果你手头有真实的开发板比如基于 GD32V 或者 ESP32-C3 的板子也可以但 QEMU 更方便观察寄存器状态。先准备一个链接脚本把代码段定位到 0x80000000这是 QEMU virt 机器的内存起始地址。然后写一个启动汇编设置好 sp然后调用 main。启动汇编里不需要遵守完整的 ABI因为这是最底层的入口但 sp 必须设置而且最好 16 字节对齐。.section .text.start .globl _start _start: la sp, _stack_top andi sp, sp, -16 call main j .这段代码里la是加载地址的伪指令andi sp, sp, -16确保 sp 低 4 位清零也就是 16 字节对齐。call main会跳转到 main 并把返回地址放到 ra 里。如果 main 正常返回就会执行j .进入死循环。4.2 编写测试函数观察寄存器保存行为接下来写一个 C 函数里面嵌入汇编来观察 t 寄存器和 s 寄存器的变化。思路是这样的在调用一个函数之前把 t0 和 s0 设置成特定的值调用之后再看它们有没有变。按照 ABIt0 应该被破坏s0 应该保持不变。register long t0_val asm(t0) 0x1111; register long s0_val asm(s0) 0x2222; void callee(void) { asm volatile(li t0, 0x3333); asm volatile(li s0, 0x4444); } int main(void) { asm volatile(li t0, 0x1111); asm volatile(li s0, 0x2222); callee(); asm volatile(mv %0, t0 : r(t0_val)); asm volatile(mv %0, s0 : r(s0_val)); // 此时 t0_val 应该是 0x3333s0_val 应该是 0x2222 return 0; }编译运行后如果你在调试器里看寄存器会发现 t0 变成了 0x3333而 s0 还是 0x2222。但这里有个陷阱callee 函数里直接改了 s0 却没有保存和恢复这违反了 ABI。编译器在优化的时候可能会假设 s0 在函数调用后不变从而生成错误的代码。正确的做法是如果 callee 要用 s0必须在入口处压栈保存在返回前恢复。这个实验说明了一个重要问题ABI 约定是编译器和你之间的契约你违反了它编译器不会报错但生成的代码行为就是未定义的。在实际项目里手写汇编函数一定要严格遵守 ABI否则出了问题很难定位。4.3 用反汇编验证编译器的寄存器分配写完 C 代码后用riscv64-unknown-elf-objdump -d反汇编看看编译器生成的汇编代码。你会发现编译器在函数入口处会有一组压栈指令把 ra 和用到的 s 寄存器保存起来。比如main: addi sp, sp, -32 sd ra, 24(sp) sd s0, 16(sp) ... call callee ... ld ra, 24(sp) ld s0, 16(sp) addi sp, sp, 32 ret这段代码里sp 减了 32保持了 16 字节对齐。ra 和 s0 被保存到栈上返回前恢复。如果你在写汇编的时候也遵循这个模式就能和编译器生成的代码无缝对接。反过来如果你自己写的汇编函数没有保存 s 寄存器而 C 代码里又假设 s 寄存器不变那就会出现数据错乱。我建议你在学习阶段多看看反汇编尤其是编译器优化级别从 -O0 到 -O2 的变化。你会发现 -O0 的时候编译器很老实每个变量都往栈上放-O2 的时候寄存器分配很激进t 寄存器被反复复用s 寄存器只在真正需要跨调用的时候才保存。理解这些行为对你写高效的汇编和调试优化后的代码都很有帮助。5. 常见问题与排查技巧实录5.1 栈对齐导致的异常排查栈对齐问题是我遇到最多的坑之一。RISC-V 的 ABI 要求 sp 在函数入口处 16 字节对齐但很多手写汇编或者不规范的代码会破坏这个规则。症状通常是调用某些库函数时触发异常比如浮点运算、向量操作、或者用了对齐加载指令的 memcpy。排查方法很简单在异常处理程序里打印出 mcause 和 mtval看看是不是 load/store 地址不对齐。如果是再检查 sp 的值是不是 16 的倍数。我通常会在启动代码里加一句andi sp, sp, -16确保初始 sp 是对齐的。然后在每个手写汇编函数的入口处检查 sp 有没有被意外修改。还有一个隐蔽的情况中断处理程序里如果用了浮点寄存器但没有保存和恢复也会导致浮点状态错乱。RISC-V 的浮点寄存器是独立的ABI 里把 fa0-fa7 和 ft0-ft11 都归为调用者保存但中断处理程序如果不保存这些寄存器被中断的浮点运算就会出错。这个问题的排查方法是在中断入口处把所有浮点寄存器都压栈返回前恢复看看问题是否消失。5.2 临时寄存器误用导致的数据错乱t 寄存器是调用者保存的意味着函数调用后它们的值可能被改掉。但有些人在写汇编的时候会忘记这一点在一个循环里用了 t0 保存循环计数然后循环体里调用了函数结果 t0 被改循环次数就乱了。这种 bug 的特点是单步调试的时候看起来正常因为单步不会触发函数调用全速运行就出错而且出错的位置和实际原因往往隔得很远。排查方法是在怀疑的代码段前后打印 t 寄存器的值看看调用前后有没有变化。如果变了那就说明有函数调用破坏了 t 寄存器你需要把循环计数放到 s 寄存器里或者在调用前手动保存 t 寄存器。我个人的习惯是在写汇编函数的时候先列出这个函数用到了哪些寄存器然后对照 ABI 表格看看哪些需要保存。t 寄存器默认不保存但如果函数内部有调用那调用前要把还需要用的 t 寄存器压栈。s 寄存器如果用了必须在入口处保存返回前恢复。这个习惯能避免大部分寄存器相关的 bug。5.3 参数传递错误的快速定位方法参数传递错误通常表现为函数收到的参数不对或者返回值不对。排查的时候首先确认参数个数有没有超过 8 个超过的话要看栈上的参数布局对不对。RISC-V 的栈上参数是从低地址到高地址依次排列的第一个栈参数在 sp0 的位置第二个在 sp8RV64或 sp4RV32。如果参数个数没超过 8 个那就检查 a 寄存器的值对不对。可以在调用前用调试器看 a0-a7 的值然后在被调用函数入口处再看一遍看看有没有变化。如果调用前是对的入口处变了那可能是 call 指令或者跳转指令出了问题。如果入口处是对的函数内部用错了那就是函数自己的问题。返回值错误的话检查 a0 和 a1 有没有被正确设置。有些函数会返回结构体这时候 a0 和 a1 一起用a0 是低地址部分a1 是高地址部分。如果只设置了 a0 没设置 a1返回值就会不对。这个细节在写返回结构体的汇编函数时特别容易忽略。5.4 常见问题速查表问题现象可能原因排查方法解决措施调用库函数时触发异常sp 未 16 字节对齐检查 sp 低 4 位是否为零入口处andi sp, sp, -16循环次数错乱t 寄存器被函数调用破坏调用前后打印 t 寄存器值改用 s 寄存器或调用前压栈浮点运算结果异常浮点寄存器未保存检查中断处理程序是否保存 fa/ft 寄存器中断入口压栈所有浮点寄存器函数参数错误栈上参数布局不对检查 sp0、sp8 处的值按 ABI 布局重新排列参数返回值错误a1 未设置检查 a0 和 a1 的值返回结构体时同时设置 a0 和 a1中断返回后数据错乱中断处理程序破坏了 s 寄存器检查中断入口是否保存 s0-s11中断入口保存所有用到的 s 寄存器这张表里的问题我都实际遇到过尤其是栈对齐和 t 寄存器误用这两个几乎每个刚接触 RISC-V 汇编的人都会踩一遍。我的建议是在写任何汇编代码之前先把 ABI 寄存器约定打印出来贴在显示器旁边写几行就对照一下养成习惯之后就不容易出错了。6. 从 base ISA 到 ABICPU 设计者需要关注什么6.1 硬件实现寄存器堆时的取舍如果你是在做 RISC-V CPU 设计那 base ISA 里的寄存器堆是你必须实现的第一个模块。32 个寄存器x0 硬连线为 0其余 31 个可读写。听起来简单但实际实现的时候有几个点要注意。首先是读端口和写端口的数量。RISC-V 的指令格式决定了大部分指令需要同时读两个寄存器、写一个寄存器所以寄存器堆至少要有两个读端口和一个写端口。如果你想做双发射或者乱序执行那端口数量还要增加。端口多了面积和功耗都会上去所以要在性能和成本之间做权衡。其次是 x0 的处理。有些实现会把 x0 也做成一个真实的寄存器只是在写的时候忽略写入读的时候返回 0。但更常见的做法是直接在读端口上做多路选择如果读的是 x0 就返回 0不实际访问寄存器堆。这样能省一点面积也能避免 x0 被意外写入。还有一个细节是寄存器的复位值。RISC-V 没有规定寄存器在上电后的初始值所以你不能假设它们是 0。启动代码必须显式地初始化 sp、gp 等寄存器否则程序行为不可预测。我在实际项目里见过因为忘了初始化 gp 导致全局变量访问出错的情况查了好久才发现是 gp 没设置。6.2 指令译码与寄存器索引的对应指令译码的时候rs1、rs2、rd 这三个字段的提取是固定的。R 型指令里rs1 在 bit 15-19rs2 在 bit 20-24rd 在 bit 7-11。I 型指令里rs1 在 bit 15-19rd 在 bit 7-11没有 rs2。S 型指令里rs1 在 bit 15-19rs2 在 bit 20-24没有 rd。B 型指令和 S 型类似但立即数的编码方式不同。这些字段的位置是硬件译码器必须硬编码的不能改。你在写 Verilog 或者 Chisel 的时候直接按位提取就行。但要注意提取出来的 5 位索引要直接送到寄存器堆的读地址端口中间不要加额外的逻辑否则会增加关键路径延迟。如果你在实现压缩指令集那译码会更复杂一些因为压缩指令的寄存器字段位置和 32 位指令不一样。但 base ISA 的译码相对简单先把这部分做稳再考虑扩展。6.3 异常与中断对寄存器约定的影响异常和中断会打断正常的指令流硬件需要保存一些状态软件需要保存寄存器。RISC-V 的硬件在进入异常时会自动把当前 pc 保存到 mepc把异常原因保存到 mcause但不会自动保存通用寄存器。这意味着异常处理程序必须自己保存和恢复用到的寄存器。这里有个设计上的选择是保存所有寄存器还是只保存用到的保存所有寄存器简单粗暴但性能差只保存用到的需要编译器或者程序员分析容易出错。常见的做法是在异常入口处保存 ra、sp、gp、tp、t0-t6、a0-a7 这些调用者保存的寄存器以及用到的 s 寄存器。浮点寄存器如果用了也要保存。中断返回的时候用 mret 指令它会从 mepc 恢复 pc从 mstatus 恢复中断使能状态。但通用寄存器需要软件自己恢复。如果保存和恢复的顺序不对或者漏了某个寄存器就会出现数据错乱。我的经验是写一个宏来统一保存和恢复避免手写出错。7. 实际项目中的经验与建议7.1 工具链选择与编译选项的影响RISC-V 的工具链主要有 GCC 和 Clang 两个选择。GCC 的支持更成熟尤其是对 RV32I 和 RV64I 的基础支持很稳定。Clang 的代码生成质量在某些场景下更好但工具链的完整性稍差一些。我一般用 GCC 做嵌入式开发用 Clang 做性能分析。编译选项里-mabi和-march是两个必须设置的。-marchrv32i表示目标架构是 RV32I-mabiilp32表示 ABI 是 32 位整数、长整数和指针都是 32 位。如果是 RV64I那就是-marchrv64i和-mabilp64。这两个选项必须匹配否则链接的时候会报错。还有一个选项是-mcmodel控制代码和数据的寻址范围。默认是medlow代码和数据都在 2GB 范围内。如果程序比较大可能需要改成medany让编译器用更灵活的寻址方式。这个选项在写大型项目的时候要注意选错了会导致链接错误或者运行时地址计算错误。7.2 调试技巧如何快速定位寄存器相关问题调试寄存器相关的问题最有效的工具是调试器和反汇编。我通常会在出问题的函数入口和出口处设置断点然后观察寄存器的变化。如果发现某个寄存器的值不符合预期就往前追溯看看是哪条指令改的。还有一个技巧是在代码里插入汇编断点比如ebreak指令。执行到 ebreak 的时候调试器会停下来你可以检查所有寄存器的状态。这个方法比打印调试更直接因为打印本身会用到寄存器和栈可能会掩盖问题。如果问题只在优化后的代码里出现那大概率是编译器假设了某些 ABI 规则而你的代码违反了。这时候可以试试降低优化级别看看问题是否消失。如果消失了那就说明是 ABI 违规导致的。然后对照 ABI 表格检查你的汇编代码有没有正确保存和恢复寄存器。7.3 给初学者的学习路径建议如果你是刚开始学 RISC-V我的建议是先不要碰复杂的 SoC 或者操作系统从最小的裸机程序开始。写一个启动汇编设置 sp调用一个 C 函数点亮一个 LED 或者打印一个字符。这个过程能让你理解 base ISA 的寄存器结构和最基本的 ABI 规则。然后写几个汇编函数练习参数传递和返回值。比如写一个加法函数参数用 a0 和 a1返回值用 a0。再写一个函数用 s 寄存器保存中间结果练习压栈和出栈。这些练习看起来简单但能帮你把 ABI 约定刻在脑子里。接下来可以尝试写一个简单的异常处理程序理解 mepc、mcause、mstatus 这些 CSR 的用法。然后试着在异常处理程序里保存和恢复寄存器确保被中断的程序能正确恢复执行。这个过程中你会遇到栈对齐、寄存器保存顺序等问题解决这些问题能让你对 ABI 的理解更上一层楼。最后再去看操作系统的移植或者 CPU 的 RTL 实现那时候你会发现base ISA 和 ABI 寄存器约定就像地基一样虽然不显眼但上面的所有东西都靠它撑着。地基没打好上面盖什么都会塌。我在实际项目里最大的体会是RISC-V 的文档虽然齐全但很多细节散落在不同的规范里。base ISA 在指令集手册里ABI 在调用规范文档里CSR 在特权架构手册里。你需要把这些文档对照着看才能拼出完整的图景。刚开始可能会觉得乱但看多了就会发现这些规范的设计其实很一致理解了 base ISA 的寄存器结构ABI 的约定就顺理成章了。

相关新闻

工业级嵌入式数据存储:MRAM与8位MCU的可靠组合

工业级嵌入式数据存储:MRAM与8位MCU的可靠组合

1. 项目概述:为什么在工业现场还要用MR25H40CDF配PIC18F85K22做数据存储?MR25H40CDF和PIC18F85K22这两个芯片组合,乍看像是从十年前的BOM清单里翻出来的老物件——一个4Mb串行磁阻RAM(MRAM),一个带硬件SPI模…

2026/10/4 13:49:02 阅读更多 →
Claude Code 101:Context Management 实战——把 settings 改到 TaoToken 的上下文瘦身清单

Claude Code 101:Context Management 实战——把 settings 改到 TaoToken 的上下文瘦身清单

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

2026/10/4 13:49:02 阅读更多 →
C++继承要点解析:构造析构顺序、虚函数与菱形继承

C++继承要点解析:构造析构顺序、虚函数与菱形继承

继承,一个几乎所有C教材都会讲、几乎所有C面试都会问的知识点。但说实话,我见过太多人把继承停留在"class B : public A"这个语法层面,等到真正上手做项目,要么在多重继承里绕晕,要么被构造析构顺序坑得欲哭…

2026/10/4 13:49:02 阅读更多 →

最新新闻

写罪犯心理矫正论文,别让 AI 替你“拍脑袋”:一份按环节挑工具的实用清单 [特殊字符]

写罪犯心理矫正论文,别让 AI 替你“拍脑袋”:一份按环节挑工具的实用清单 [特殊字符]

如果你读的是公安与司法大类 / 司法技术类 / 罪犯心理测量与矫正技术,大概率会遇到一类很典型的毕业任务: 围绕监所中的某类心理问题,完成一份“心理测评 矫正方案设计”论文。 例如:《短刑犯焦虑情绪测评及认知行为团体矫正方案…

2026/10/4 15:16:56 阅读更多 →
大语言模型学习之大模型技术基础和GPT、DeepSeek模型介绍:用TaoToken统一Key跑通三类模型调用

大语言模型学习之大模型技术基础和GPT、DeepSeek模型介绍:用TaoToken统一Key跑通三类模型调用

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

2026/10/4 15:16:55 阅读更多 →
【MLLM Agent】多模态理解Agent研究进展

【MLLM Agent】多模态理解Agent研究进展

note 方案选型: 做图片输入 function call 通用多模态 Agent → 优先看DeepSeek‑Harness MTA‑Agent;做图片多轮检索求证 → DR‑MMSearchAgent;做长文档图文问答 → MDocAgent;做成本优化、工具调用节流 → ToolGate&#xf…

2026/10/4 15:16:55 阅读更多 →
MR25H40CDF+STM32F302R8工业级非易失数据存储方案

MR25H40CDF+STM32F302R8工业级非易失数据存储方案

1. 项目概述:为什么选 MR25H40CDF STM32F302R8 这对组合做工业级数据存储?在工业现场和嵌入式设备里,数据存储从来不是“随便找个 Flash 芯片焊上去”就能了事的事。我做过十几个带数据记录功能的产线控制器、边缘采集盒和智能传感器节点&am…

2026/10/4 15:16:55 阅读更多 →
渲染管线与Shader实战:从漫反射到Blinn-Phong

渲染管线与Shader实战:从漫反射到Blinn-Phong

我最早接触“着色”这个概念的时候,完全是一头雾水。那时候拿到一个图形学作业,想给一个模型加个高光,翻来覆去调了半天材质参数,结果渲染出来该黑的地方还是黑,镜面反射亮得像是打了荧光。后来才明白,问题…

2026/10/4 15:16:55 阅读更多 →
ESP32-P4+C5双芯驱动:不堆模块,这块屏自己就是网关

ESP32-P4+C5双芯驱动:不堆模块,这块屏自己就是网关

1. 这块屏凭什么敢叫自己网关第一次看到“ESP32-P4ESP32-C5双芯驱动,不用堆模块,这块屏自己就是网关”这个说法,我脑子里蹦出来的第一个念头是:又来了,又是一个把“带Wi-Fi的屏幕”包装成“网关”的营销话术。毕竟这些…

2026/10/4 15:15:55 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/4 1:00:58 阅读更多 →

月新闻

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

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

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

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

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

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

2026/10/4 9:43:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

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

2026/10/3 9:42:36 阅读更多 →