最近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:

lw s1,data
lw s0,address
sw s1,0(s0)
j resume
address:.word address
data:.word data

这也是为什么这个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模式,然后进入异常处理,也就是实现断点调试。

// The ebreak instruction will generated regular exception when the ebreakm
// bit of DCSR reg is not set
wire alu_excp_i_ebreak4excp = (alu_excp_i_ebreak & ((~dbg_ebreakm_r) | dbg_mode))
;
// The ebreak instruction will enter into the debug-mode when the ebreakm
// bit of DCSR reg is set
wire alu_excp_i_ebreak4dbg = alu_excp_i_ebreak
& (~alu_need_flush)// override by other alu exceptions
& dbg_ebreakm_r
& (~dbg_mode);//Not in debug mode

spec中还介绍了trace模块,然而这个模块sifive也还没做,蜂鸟自然没有实现。另外,debug模块里面还可以增加一个master汇流排埠,这样的话某些读写soc中寄存器值的操作就不再需要占用cpu来进行了,比如xilinx的ip:jtag to axi master,不过这个功能蜂鸟里面也没有实现,这里就没啥可讨论的了。

看完spec0.11再结合蜂鸟的代码算是对debug模块有了初步的认识,不过很多细节还没有深入研究,之后边研究边补充吧。

推荐阅读:

相关文章