《RISC-V 开放架构设计之道》- RISC-V开放架构设计简读
<div class='showpostmsg'><article data-content="[{"type":"block","id":"3060-1621846615933","name":"heading","data":{"level":"h1"},"nodes":[{"type":"text","id":"p5PQ-1621846617594","leaves":[{"text":"一、前言","marks":[]}]}],"state":{}},{"type":"block","id":"PTQy-1718377266935","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"MexK-1718377266936","leaves":[{"text":"感谢eeworld电子工程世界提供《","marks":[{"type":"fontSize","value":18}]},{"text":"RISC-V开放架构设计之道","marks":[{"type":"bold"},{"type":"color","value":"#333333"},{"type":"backgroundColor","value":"rgb(255, 255, 255)"},{"type":"fontFamily","value":"SimHei"},{"type":"fontSize","value":18}]},{"text":"》书籍测评,书中并没有过多繁琐介绍riscv的概念,也没有过多强调riscv的好处,更多介绍的是riscv的简洁、免费、开放的思想。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"7UDb-1718438509089","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"ixVc-1718438509081","leaves":[{"text":"如同书中一位大佬推荐所说的:这本方便的小书轻松地总结了RISC-V指令集架构所有的基本要素,是学生和从业者的完美参考指南。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"Bsnd-1718438627592","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"tOZo-1718438627590","leaves":[{"text":"RISC-V,是比较大体、宽泛的概念,","marks":[{"type":"fontSize","value":18}]},{"text":"但是","marks":[{"type":"color","value":"#4d4d4d"},{"type":"backgroundColor","value":"rgb(255, 255, 255)"},{"type":"fontFamily","value":"SimHei"},{"type":"fontSize","value":18}]},{"text":"书中内容很简短,内容插入了不少插图以及RISCV的参考卡,说是参考指南一点也不过分;加之目录介绍很清晰,从riscv32的基础整数指令集RV32I、乘法和除法指令集M扩展、原子指令集A扩展、单双精度浮点数指令集RV32F/RV32D、压缩指令RV32C、以及汇编语言和向量RV32V。以及后续的RV64介绍。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"fpOb-1718438974333","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"gysQ-1718438974331","leaves":[{"text":"而刚好先楫的处理器作为典型标准的RISCV处理器,当然也支持上述的指令集,并且还有所扩充。以实际的硬件进行书籍分享是最好不过的思路了。比如先楫的HPM5300系列。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"Mqc6-1718439097476","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/19912/WEBRESOURCE60168c14b311dbcac1fef06a2db3a29e","width":411,"height":431},"nodes":[],"state":{}},{"type":"block","id":"rGHx-1718439065289","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"YcWF-1718439065287","leaves":[{"text":"本系列文章更多是讲一些基础只知识,利用HPM5300EVK配合SEGGER Embedded Studio这个IDE通过查看机器码和汇编来分析RISCV的一些指令集,以此能对riscv有一定的了解兴趣。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"OMyi-1718439224951","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"9Vmi-1718439224949","leaves":[{"text":"本书内容目录比较多,系列文章定期分享RISCV的每个指令集基础。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"v7u7-1718439522867","name":"heading","data":{"level":"h1"},"nodes":[{"type":"text","id":"daIb-1718439522865","leaves":[{"text":"二、导读","marks":[]}]}],"state":{}},{"type":"block","id":"5CAH-1718440591487","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"7T0m-1718440591488","leaves":[{"text":"如果想要接触riscv,那么典型的reference card(参考卡)不可或缺,你会发现相比其他的指令集架构,riscv真的相对简洁了不少(基础的就只有两页),而且操作码7位,相当可自由扩充127个扩展指令,这也是riscv的开放自由思想。这里截图一小部分,需要了解的可以搜下riscv cheat sheet。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"1vsq-1718440895646","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"Wd70-1718440895644","leaves":[{"text":"链接","marks":[{"type":"fontSize","value":18}]},{"text":":","marks":[]}]},{"type":"inline","id":"rzRT-1718440910890","name":"link","data":{"href":"https://www.cl.cam.ac.uk/teaching/1617/ECAD+Arch/files/docs/RISCVGreenCardv8-20151013.pdf"},"nodes":[{"type":"text","id":"e69W-1718440910889","leaves":[{"text":"https://www.cl.cam.ac.uk/teaching/1617/ECAD+Arch/files/docs/RISCVGreenCardv8-20151013.pdf","marks":[]}]}]},{"type":"text","id":"0eq9-1718440910891","leaves":[{"text":"","marks":[]}]}],"state":{}},{"type":"block","id":"5aEw-1718440937539","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/19948/WEBRESOURCE0bec8e26312056cce5b07a21a3220366","width":480,"height":51},"nodes":[],"state":{}},{"type":"block","id":"FS0X-1718440549731","name":"heading","data":{"level":"h2"},"nodes":[{"type":"text","id":"E8uI-1718440549732","leaves":[{"text":"(一)RV32I指令格式","marks":[]}]}],"state":{}},{"type":"block","id":"zS2X-1718441935107","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/19957/WEBRESOURCE16a5d490faada320169719e3f18836f7","width":379,"height":258},"nodes":[],"state":{}},{"type":"block","id":"UYuI-1718441635763","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"7LXY-1718441635764","leaves":[{"text":"如书中描述,RV32I 基础指令集的一页图形表示。 对于每幅图, 将有下划线的字母从左到右连接起来,即可组成完整的 RV32I 指令集。对于每一个图,集合标志{}内列举了指令的所有变体,变体用加下划线的字母或下划线字符_表示。特别的,下划线字符_表示对于此指令变体不需用字符表示。 例如,下图表示了这四个 RV32I 指令: slt, slti, sltu, sltiu:","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"vfHv-1718441977117","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/19960/WEBRESOURCE92f493d7aa5a33183cb52cae46b72f20","width":479,"height":51},"nodes":[],"state":{}},{"type":"block","id":"qP2S-1718441977121","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"hQul-1718441977120","leaves":[{"text":"书中展示了6种基本指令格式,分别是用于寄存器-寄存器操作的 R 类型指令,用于短立即数和访存 load 操作的 I 型指令,用于访存 store 操作的 S 型指令,用于条件跳转操作的 B 类型指令,用于长立即数的 U 型指令和用于无条件跳转的 J 型指令。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"YGKX-1718444720004","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/19964/WEBRESOURCE9fbb95a310a5ec6e8b01b5440e703585","width":720,"height":137},"nodes":[],"state":{}},{"type":"block","id":"ynmr-1718444720008","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"reql-1718444720007","leaves":[{"text":"这里使用最简单的U类型种的lui指令,配合利用HPM5300EVK配合SEGGER Embedded Studio来进行说明上述所说的指令格式是什么意思。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"ZLbt-1718445848742","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"RmYi-1718445848740","leaves":[{"text":"随便打开hpm_sdk一个例子,从左边的汇编窗口找到lui的汇编,比如以下:","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"gBDS-1718446169796","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"SkW5-1718446169795","leaves":[{"text":"可以看到,ses这个IDE对于汇编和机器码呈现还是是否直观的,对于不熟悉gdb调试又想查看汇编的开发者是十分友好的。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"roBR-1718446220175","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"6Y8P-1718446220173","leaves":[{"text":"对于左边的8000452a指的是存储到flash的机器码的flash地址,中间的E40007B7就是机器码,最右边就是汇编部分。","marks":[{"type":"color","value":"#F33232"},{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"0lkK-1718447084813","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"ASRc-1718447084811","leaves":[{"text":"hpm单片机支持压缩指令集RV32C,所以机器码有些会是16位,这对于固件空间的缩小利用是有一定帮助的。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"o2OV-1718446164273","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/19974/WEBRESOURCE234f99bb64aabe81fa1fd2ee3b5f136b","width":637,"height":170},"nodes":[],"state":{}},{"type":"block","id":"tlpo-1718446137362","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"tiTY-1718446137361","leaves":[{"text":"可以分析下E40007B7这个机器码对应的Lui指令格式,如下图可看到","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"aV9z-1718447304056","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"6x3W-1718447304054","leaves":[{"text":"E40007B7的低7位代表操作码opcode,也就是lui的操作码(0b0110111),7到11bit是5位目的寄存器(0b01111),这很奇妙,5bit宽度刚好是32,对应的就是寄存器的长度(32bit),换成十六进制就是15,对应的通用寄存器就是X15,也就是别名a5寄存器。","marks":[{"type":"color","value":"#F33232"},{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"HN78-1718451250423","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"PS8R-1718451250421","leaves":[{"text":"高11位就是立即数,对应的十六进制就是0xE4000,最终根据该机器码可以反汇编得到:","marks":[{"type":"color","value":"#F33232"},{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"MAto-1718451349772","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"opAR-1718451349771","leaves":[{"text":"把一个20位的立即数0xE4000加载到a5寄存器,对应的汇编指令就是:lui a5, 0xE4000,刚好可以对上ses IDE的汇编显示。","marks":[{"type":"color","value":"#F33232"},{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"JNII-1718447304344","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/19988/WEBRESOURCEd9401fe4faa080ca216cd8a3e5e2d1be","width":638,"height":111},"nodes":[],"state":{}},{"type":"block","id":"8tZ7-1718447304348","name":"heading","data":{"level":"h2"},"nodes":[{"type":"text","id":"N3t9-1718447304347","leaves":[{"text":"(二)RISCV通用寄存器","marks":[]}]}],"state":{}},{"type":"block","id":"pZwD-1718457345576","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"HmXB-1718457345574","leaves":[{"text":"无论是RISCV32还是RISCV64,他的通用寄存器的数量都是32个,分别是x0~x31,当然为了方便汇编编写,每个寄存器也有别名,在参考卡中如下:","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"ClLg-1718454934631","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/20024/WEBRESOURCE27ebea292a4b668da9a2fcab8b20836f","width":435,"height":412},"nodes":[],"state":{"renderSource":"https://note.youdao.com/yws/res/20024/WEBRESOURCE27ebea292a4b668da9a2fcab8b20836f","initialSize":{"width":435,"height":776},"loading":false}},{"type":"block","id":"v1cq-1718454298537","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"CfN9-1718454298536","leaves":[{"text":"RISC-V 有足够多的寄存器来达到两全其美的结果:既能将操作数存放在寄存器中,同时也能减少保存和恢复寄存器的次数。其中的关键在于,在函数调用的过程中不保留部分寄存器存储的值,称它们为临时寄存器;另一些寄存器则对应地称为保存寄存器。不再调用其它函数的函数称为叶函数。当一个叶函数只有少量的参数和局部变量时,它们可以都被存储在寄存器中,而不会“溢出(spilling)”到内存中。但如果函数参数和局部变量很多,程序还是需要把寄存器的值保存在内存中,不过这种情况并不多见。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"LusV-1718457355234","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"yq6d-1718457355233","leaves":[{"text":"这里说明下caller是调用者,调用(或执行)一个函数的代码段或者函数,是主动发起函数调用的一方,可以理解是\"甲方\"。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"SLp5-1718455086344","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"RhkJ-1718455086343","leaves":[{"text":"callee是被调用者,被调用的函数本身,是被动接收函数调用并且执行对应操作的一方,可以理解是\"乙方\"。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"wEBp-1718455142858","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"nxOU-1718455142856","leaves":[{"text":"从上面的表格知道,从通用寄存器上看,X0到X31,调用者需要保存的通用寄存器16个,剩下的是被调用者需要保存的寄存器。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"YMa6-1718455390449","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"AuIm-1718455390448","leaves":[{"text":"这里可以从函数调用阶段来进行说明通用寄存器的作用,配合利用HPM5300EVK配合SEGGER Embedded Studio,使用helloworld例子来说明。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"wo60-1718455627348","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"0MFm-1718455627346","leaves":[{"text":"函数调用基本分为以下几个阶段,在书中也提到:","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"HZiF-1718457257934","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/a/WEBRESOURCE2801bdef730fc2ac065a9d4df690323a","width":404,"height":274},"nodes":[],"state":{"loading":false,"renderSource":"https://note.youdao.com/yws/res/a/WEBRESOURCE2801bdef730fc2ac065a9d4df690323a","initialSize":{"width":749,"height":519}}},{"type":"block","id":"vBHY-1718457247560","name":"paragraph","data":{},"nodes":[{"type":"text","id":"Hfvb-1718457247561","leaves":[{"text":"","marks":[]}]}],"state":{}},{"type":"block","id":"DkWN-1718457240150","name":"heading","data":{"level":"h3"},"nodes":[{"type":"text","id":"SaLU-1718457240149","leaves":[{"text":"1、进入到被调用者函数的开始位置(caller需要保存的)","marks":[]}]}],"state":{}},{"type":"block","id":"NHYn-1718455868666","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/20051/WEBRESOURCE761fa450d86cb2ba8487b616f9c244e0","width":858,"height":127},"nodes":[],"state":{"renderSource":"https://note.youdao.com/yws/res/20051/WEBRESOURCE761fa450d86cb2ba8487b616f9c244e0","initialSize":{"width":858,"height":402},"loading":false}},{"type":"block","id":"qca0-1718455600925","name":"heading","data":{"level":"h3"},"nodes":[{"type":"text","id":"RMo3-1718455600924","leaves":[{"text":"2、将函数传参存储到指定寄存器上","marks":[]}]}],"state":{}},{"type":"block","id":"WA3h-1718456096833","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"AtUl-1718456096831","leaves":[{"text":"这里就是上图的x10~x17,相比其他指令集架构参数依赖于内存,也就是需要保存在栈中,riscv反而可以通过寄存器保存参数,最多可以存储8个参数,当然超过的也会存储到内存中。一般应用还是推荐不要多余8个参数传参。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"aAra-1718456158214","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"mtel-1718456158212","leaves":[{"text":"那么我们进入到board_timer_create API中,传参是300和board_led_toggle函数指针。那么将会存储在x10和x11这两个寄存器上。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"OwAj-1718456297923","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/20067/WEBRESOURCEce0236aa64bf92748b7db8bf757d8b75","width":718,"height":233},"nodes":[],"state":{"renderSource":"https://note.youdao.com/yws/res/20067/WEBRESOURCEce0236aa64bf92748b7db8bf757d8b75","initialSize":{"width":718,"height":233},"loading":false}},{"type":"block","id":"sIPn-1718456391285","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/20071/WEBRESOURCEfa33c688a22d80af62e803851accfdfe","width":716,"height":232},"nodes":[],"state":{"renderSource":"https://note.youdao.com/yws/res/20071/WEBRESOURCEfa33c688a22d80af62e803851accfdfe","initialSize":{"width":716,"height":518},"loading":false}},{"type":"block","id":"EDrL-1718456391290","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"6gwS-1718456391288","leaves":[{"text":"可以看到,x10现在是300,是符合预期的,对于x11是0x8000669e,我们可以在SES IDE中的汇编窗口查到该函数地址。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"YMic-1718456501128","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"281T-1718456501126","leaves":[{"text":"0x8000669e对应的就是board_led_toggle,这也是符合预期的。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"FXwT-1718456493323","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/20077/WEBRESOURCEeb6e39962cf754234fccd81343c36afe","width":566,"height":347},"nodes":[],"state":{"renderSource":"https://note.youdao.com/yws/res/20077/WEBRESOURCEeb6e39962cf754234fccd81343c36afe","initialSize":{"width":566,"height":347},"loading":false}},{"type":"block","id":"UFLY-1718456462380","name":"heading","data":{"level":"h3"},"nodes":[{"type":"text","id":"YJWK-1718456462379","leaves":[{"text":"3、获取函数局部资源变量保存在寄存器,并执行函数中的指令,可以看到寄存器窗口会发生不同的赋值变化","marks":[]}]}],"state":{}},{"type":"block","id":"96r0-1718456714396","name":"heading","data":{"level":"h3"},"nodes":[{"type":"text","id":"k5SJ-1718456714394","leaves":[{"text":"4、执行完毕之后将返回值存储到调用者能够访问的位置(X10寄存器),恢复寄存器,出栈释放局部资源","marks":[]}]}],"state":{}},{"type":"block","id":"hbhQ-1718456846725","name":"heading","data":{"level":"h3"},"nodes":[{"type":"text","id":"JOYo-1718456846723","leaves":[{"text":"5、返回进行下一个调用函数位置。","marks":[]}]}],"state":{}},{"type":"block","id":"LBo5-1718457081072","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"BMyp-1718457081073","leaves":[{"text":"在ses IDE中也展示了main调用者主函数调用被调用者函数的一些汇编代码过程。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"YEkg-1718457113578","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"41Ma-1718457113576","leaves":[{"text":"","marks":[]}]}],"state":{}},{"type":"block","id":"gC46-1718457127808","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/c/WEBRESOURCEbfee27898b7f1a7dcf76e4ac6282307c","width":348,"height":500},"nodes":[],"state":{"loading":false,"renderSource":"https://note.youdao.com/yws/res/c/WEBRESOURCEbfee27898b7f1a7dcf76e4ac6282307c","initialSize":{"width":583,"height":761}}},{"type":"block","id":"iElv-1718457127813","name":"heading","data":{"level":"h1","style":{}},"nodes":[{"type":"text","id":"HvaT-1718457127811","leaves":[{"text":"三、总结","marks":[]}]}],"state":{}},{"type":"block","id":"FWve-1718457383409","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"rUMd-1718457383408","leaves":[{"text":"1、riscv的书籍众多,RISC-V开放架构设计之道这本书籍值得阅读,书中内容简洁但又不缺完整,而且丰富的参考卡可作为riscv的日常参考指南所用","marks":[{"type":"fontSize","value":18}]}]}],"state":{}},{"type":"block","id":"vwat-1718457507356","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"KXLP-1718457507354","leaves":[{"text":"2、对于技术书籍,需要有一个实操机会可以加深印象,先楫的处理器内核遵守risv规范,具有多种扩展指令集,而其配套的SEGGER Embedded Studio这个IDE通过查看机器码和汇编,让一些不熟悉gdb调试的开发者,是一个值得推荐的实操平台。","marks":[{"type":"fontSize","value":18}]}]}],"state":{}}]"><p>一、前言</p>
<p>感谢eeworld电子工程世界提供《RISC-V开放架构设计之道》书籍测评,书中并没有过多繁琐介绍riscv的概念,也没有过多强调riscv的好处,更多介绍的是riscv的简洁、免费、开放的思想。</p>
<p>如同书中一位大佬推荐所说的:这本方便的小书轻松地总结了RISC-V指令集架构所有的基本要素,是学生和从业者的完美参考指南。</p>
<p>RISC-V,是比较大体、宽泛的概念,但是书中内容很简短,内容插入了不少插图以及RISCV的参考卡,说是参考指南一点也不过分;加之目录介绍很清晰,从riscv32的基础整数指令集RV32I、乘法和除法指令集M扩展、原子指令集A扩展、单双精度浮点数指令集RV32F/RV32D、压缩指令RV32C、以及汇编语言和向量RV32V。以及后续的RV64介绍。</p>
<p>而刚好先楫的处理器作为典型标准的RISCV处理器,当然也支持上述的指令集,并且还有所扩充。以实际的硬件进行书籍分享是最好不过的思路了。比如先楫的HPM5300系列。</p>
<p> </p>
<p>本系列文章更多是讲一些基础只知识,利用HPM5300EVK配合SEGGER Embedded Studio这个IDE通过查看机器码和汇编来分析RISCV的一些指令集,以此能对riscv有一定的了解兴趣。</p>
<p>本书内容目录比较多,系列文章定期分享RISCV的每个指令集基础。</p>
<p>二、导读</p>
<p>如果想要接触riscv,那么典型的reference card(参考卡)不可或缺,你会发现相比其他的指令集架构,riscv真的相对简洁了不少(基础的就只有两页),而且操作码7位,相当可自由扩充127个扩展指令,这也是riscv的开放自由思想。这里截图一小部分,需要了解的可以搜下riscv cheat sheet。</p>
<p>链接:<a href="https://www.cl.cam.ac.uk/teaching/1617/ECAD+Arch/files/docs/RISCVGreenCardv8-20151013.pdf">https://www.cl.cam.ac.uk/teaching/1617/ECAD+Arch/files/docs/RISCVGreenCardv8-20151013.pdf</a></p>
<p> </p>
<p>(一)RV32I指令格式</p>
<p> </p>
<p>如书中描述,RV32I 基础指令集的一页图形表示。 对于每幅图, 将有下划线的字母从左到右连接起来,即可组成完整的 RV32I 指令集。对于每一个图,集合标志{}内列举了指令的所有变体,变体用加下划线的字母或下划线字符_表示。特别的,下划线字符_表示对于此指令变体不需用字符表示。 例如,下图表示了这四个 RV32I 指令: slt, slti, sltu, sltiu:</p>
<p> </p>
<p>书中展示了6种基本指令格式,分别是用于寄存器-寄存器操作的 R 类型指令,用于短立即数和访存 load 操作的 I 型指令,用于访存 store 操作的 S 型指令,用于条件跳转操作的 B 类型指令,用于长立即数的 U 型指令和用于无条件跳转的 J 型指令。</p>
<p> </p>
<p>这里使用最简单的U类型种的lui指令,配合利用HPM5300EVK配合SEGGER Embedded Studio来进行说明上述所说的指令格式是什么意思。</p>
<p>随便打开hpm_sdk一个例子,从左边的汇编窗口找到lui的汇编,比如以下:</p>
<p>可以看到,ses这个IDE对于汇编和机器码呈现还是是否直观的,对于不熟悉gdb调试又想查看汇编的开发者是十分友好的。</p>
<p>对于左边的8000452a指的是存储到flash的机器码的flash地址,中间的E40007B7就是机器码,最右边就是汇编部分。</p>
<p>hpm单片机支持压缩指令集RV32C,所以机器码有些会是16位,这对于固件空间的缩小利用是有一定帮助的。</p>
<p> </p>
<p>可以分析下E40007B7这个机器码对应的Lui指令格式,如下图可看到</p>
<p>E40007B7的低7位代表操作码opcode,也就是lui的操作码(0b0110111),7到11bit是5位目的寄存器(0b01111),这很奇妙,5bit宽度刚好是32,对应的就是寄存器的长度(32bit),换成十六进制就是15,对应的通用寄存器就是X15,也就是别名a5寄存器。</p>
<p>高11位就是立即数,对应的十六进制就是0xE4000,最终根据该机器码可以反汇编得到:</p>
<p>把一个20位的立即数0xE4000加载到a5寄存器,对应的汇编指令就是:lui a5, 0xE4000,刚好可以对上ses IDE的汇编显示。</p>
<p> </p>
<p>(二)RISCV通用寄存器</p>
<p>无论是RISCV32还是RISCV64,他的通用寄存器的数量都是32个,分别是x0~x31,当然为了方便汇编编写,每个寄存器也有别名,在参考卡中如下:</p>
<p> </p>
<p>RISC-V 有足够多的寄存器来达到两全其美的结果:既能将操作数存放在寄存器中,同时也能减少保存和恢复寄存器的次数。其中的关键在于,在函数调用的过程中不保留部分寄存器存储的值,称它们为临时寄存器;另一些寄存器则对应地称为保存寄存器。不再调用其它函数的函数称为叶函数。当一个叶函数只有少量的参数和局部变量时,它们可以都被存储在寄存器中,而不会“溢出(spilling)”到内存中。但如果函数参数和局部变量很多,程序还是需要把寄存器的值保存在内存中,不过这种情况并不多见。</p>
<p>这里说明下caller是调用者,调用(或执行)一个函数的代码段或者函数,是主动发起函数调用的一方,可以理解是"甲方"。</p>
<p>callee是被调用者,被调用的函数本身,是被动接收函数调用并且执行对应操作的一方,可以理解是"乙方"。</p>
<p>从上面的表格知道,从通用寄存器上看,X0到X31,调用者需要保存的通用寄存器16个,剩下的是被调用者需要保存的寄存器。</p>
<p>这里可以从函数调用阶段来进行说明通用寄存器的作用,配合利用HPM5300EVK配合SEGGER Embedded Studio,使用helloworld例子来说明。</p>
<p>函数调用基本分为以下几个阶段,在书中也提到:</p>
<p> </p>
<p>1、进入到被调用者函数的开始位置(caller需要保存的)</p>
<p> </p>
<p>2、将函数传参存储到指定寄存器上</p>
<p>这里就是上图的x10~x17,相比其他指令集架构参数依赖于内存,也就是需要保存在栈中,riscv反而可以通过寄存器保存参数,最多可以存储8个参数,当然超过的也会存储到内存中。一般应用还是推荐不要多余8个参数传参。</p>
<p>那么我们进入到board_timer_create API中,传参是300和board_led_toggle函数指针。那么将会存储在x10和x11这两个寄存器上。</p>
<p> </p>
<p> </p>
<p>可以看到,x10现在是300,是符合预期的,对于x11是0x8000669e,我们可以在SES IDE中的汇编窗口查到该函数地址。</p>
<p>0x8000669e对应的就是board_led_toggle,这也是符合预期的。</p>
<p> </p>
<p>3、获取函数局部资源变量保存在寄存器,并执行函数中的指令,可以看到寄存器窗口会发生不同的赋值变化</p>
<p>4、执行完毕之后将返回值存储到调用者能够访问的位置(X10寄存器),恢复寄存器,出栈释放局部资源</p>
<p>5、返回进行下一个调用函数位置。</p>
<p>在ses IDE中也展示了main调用者主函数调用被调用者函数的一些汇编代码过程。</p>
<p> </p>
<p>三、总结</p>
<p>1、riscv的书籍众多,RISC-V开放架构设计之道这本书籍值得阅读,书中内容简洁但又不缺完整,而且丰富的参考卡可作为riscv的日常参考指南所用</p>
<p>2、对于技术书籍,需要有一个实操机会可以加深印象,先楫的处理器内核遵守risv规范,具有多种扩展指令集,而其配套的SEGGER Embedded Studio这个IDE通过查看机器码和汇编,让一些不熟悉gdb调试的开发者,是一个值得推荐的实操平台。</p>
</article>
</div><script> var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
(function($){
var postHeight = getTextHeight(400);
$(".showpostmsg").html($(".showpostmsg").html());
$(".showpostmsg").after(loginstr);
$(".showpostmsg").css({height:postHeight,overflow:"hidden"});
})(jQuery);
} </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script> <p>是初学者学习掌握RISC-V指令集架构所有的基本要素的好书</p>
<p>这个和arm有啥区别?除了不要钱。</p>
freebsder 发表于 2024-6-26 14:43
这个和arm有啥区别?除了不要钱。
<p>指令集更加精简了,代码的执行效率可以更高,书中有详细RISC-V和x86,arm指令集的性能对比</p>
rtyu789 发表于 2024-9-21 22:19
指令集更加精简了,代码的执行效率可以更高,书中有详细RISC-V和x86,arm指令集的性能对比
<p>主要是现在硬件过剩,软件跟不上<img height="63" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/victory.gif" width="61" /></p>
页:
[1]