RISC-V MCU中文社区

RISC-V学习笔记

分享于 2020-04-17 09:00:06
2
6021

最近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模块有了初步的认识,不过很多细节还没有深入研究,之后边研究边补充吧。

*免责声明:以上内容仅供交和流学习之用。如有任何疑问或异议,请留言与我们联系。
6021 2

你的回应
  • XQS0927

    2020-04-22 09:22:15 XQS0927

    大佬你好,想请教一下关于trigger module的问题。

    在追踪debug动作的时候,发现在读取通用寄存器组之后有读写trigger module register的操作,对tselect写入1后,读取tselect的值,读到的值也为1,而后就停止对trigger module的操作,开始执行对指令存储器的读写操作。

    然而我到E203的代码中查找,发现并没有trigger module的实现,而且也没有tselect、tdata1等寄存器的实现,那么对tselect写入1后读取到1是怎么实现的呢?这一点我不太明白。

    debug spec中关于trigger module的说明是这样:

    1, Write 0 to tselect.

    2, Read back tselect to confirm this trigger exists. If not, exit.

    3. Read tdata1, and possible tdata2 and tdata3 depending on the trigger type.
    4. If
    type in tdata1 was 0, then there are no more triggers.
    5. Repeat, incrementing the value in
    tselect.  

    这里也不太明白,回读tselect后,怎样判断该trigger是否存在呢?是读到0,还是读到写入的数据?

    还请大佬不吝赐教,谢谢!


  • 难忘初心

    2020-04-18 15:52:44 难忘初心

    原来调试器的世界还这么大,实现同样途径的方式还这么多,这种共享cpu并采用微码来实现的debug模块,真是出乎意料啊

tikitaka

tikitaka 实名认证

tikitaka

积分
问答
粉丝
关注
  • RV-STAR 开发板
  • RISC-V处理器设计系列课程
  • 培养RISC-V大学土壤 共建RISC-V教育生态
RV-STAR 开发板