最近risc-v真是火的不要不要的,开源的cpu也出了不少,正好想学习soc,果断从risc-v开始搞起。
关于risc-v的介绍,网上到处都是了,简单说来,就是ucb搞的一套完全开源并且可以自由扩展的isa,目前有用户指令集和特权指令集两个,不过我的计划是先soc,再cpu,所以这些文档都只是粗略扫了一眼,了解了一下而已,后续随着学习的深入,估计会回头一遍又一遍的翻看。
截至目前,risc-v的开源cpu跟配套soc,不说上百,十几种反正是有了,本文及后续笔记(假如有空)都是基于蜂鸟e203开源cpu和部分修改后的soc,在此十分感谢蜂鸟作者胡振波,给我等菜鸟提供了这么好的学习机会。
本文为学习笔记第一篇,内容关于蜂鸟的debug模块的初步理解,由于都是基于自己的理解,难免有错误的地方,如果有错误,希望有好心人能帮忙指出来并纠正。
关于cpu的架构,只要是学过计算机体系结构都应该有所了解,一个基础版5级流水线mips处理器充其量也就是本科生大作业的水平。不过关于cpu的debug模块,说实话,我在本科和研究生的学习中都没有涉及,直接导致我之前完全不理解soc的debug机制。
蜂鸟开源soc实现的debug模块参考了sifive公司的0.11版本的debug spec,上图是总体结构,整个debug模块包含了dtm和dm两个大模块,其中dtm就是ieee 1149标准中所定义的jtag的边界扫描模块,而dm则是实现soc内部debug的核心模块。
dtm模块是依据ieee 1149标准实现的tap控制器,对边界寄存器进行扫描输入或输出,对应的是蜂鸟开源soc里面的sirv_jtag_dtm模块。之前我一直以为dtm模块应该去控制cpu周围的寄存器,从而就可以对cpu进行debug。但这里并不是这样,这里dtm仅仅是一个桥梁,将jtag端的信号转为内部寄存器,然后输入dm模块,或是将dm的响应结果转化成jtag信号返回上位机,dtm的设计则完全参照ieee标准即可。
dm模块是debug模块的核心,其包含控制逻辑、debug rom、debug ram跟硬件线程状态。debug模块包含一个slave总线端口,供cpu访问debug rom和debug ram。当控制逻辑拉起cpu中断后,cpu进入debug模式,此时pc应指向debug rom,dm模块将占据一个cpu核心。在debug rom内,cpu会根据当前状态执行一段固定程序,程序入口包括entry、resume或exception,entry是的作用是将s0、s1寄存器存入对相应的状态和控制寄存器(s1存入的地址为debug ram最后),之后进行状态寄存器的检查,并跳入debug ram。事实上,核心的调试均需要通过debug ram完成。例如想要对soc中的寄存器或内存进行写入操作,就将如下程序和数据通过dtm写入debug ram:
这也是为什么这个spec规定32位的risc-v至少需要28byte(7个word)的debug ram的原因,这里需要4条指令、2个word的数据,以及1个word保存着进入debug模式前s1的数据。
resume和exception执行的指令相同,区别在于exception会将0xffffffff写入debug ram最后一个word内,然后等待debug的中断重新拉起,再次将s1保存到debug ram,跳入debug ram执行里面的debug程序。我先前有一个疑惑,这里是如何通过最后一个word为全0或全1来判断是否有异常发生的?这里我的理解是,jtag debugger会在拉起新的中断前,先检查debug ram内的内容,如果发现出现了0xffffffff,则进行相应的处理,如果没有,再把新的debug程序写进去,再拉起中断,使得debug继续进行下去,直到debug结束,退出debug模式。
debug模块另一个关键之处是断点,断点是对cpu进行debug不可或缺的特性。spec0.11中对断点的介绍只有软件断点,就是在需要断点的地方插入一条断点指令,ebreak。查看蜂鸟的代码后发现,蜂鸟里面也是通过ebreak的方式实现软件断点,似乎都没有实现硬件断点。这里需要注意的是,在非debug模式下,如果debug模块里的dcsr寄存器中ebreakm没有置1,cpu执行ebreak指令只会直接跳入异常处理,只有当dcsr寄存器里ebreakm置1且执行到ebreak指令,cpu才会进入debug模式,然后进入异常处理,也就是实现断点调试。
spec中还介绍了trace模块,然而这个模块sifive也还没做,蜂鸟自然没有实现。另外,debug模块里面还可以增加一个master总线端口,这样的话某些读写soc中寄存器值的操作就不再需要占用cpu来进行了,比如xilinx的ip:jtag to axi master,不过这个功能蜂鸟里面也没有实现,这里就没啥可讨论的了。
看完spec0.11再结合蜂鸟的代码算是对debug模块有了初步的认识,不过很多细节还没有深入研究,之后边研究边补充吧。