8

cpu设计和实现(异常和中断)

 1 year ago
source link: https://feixiaoxing.blog.csdn.net/article/details/128122334
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

cpu设计和实现(异常和中断)

嵌入式-老费 已于 2022-12-01 08:13:39 修改 206

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

        异常和中断几乎是cpu最重要的特性。而异常和中断,本质上其实是一回事。很多熟悉mips的朋友,应该都听过这么一个词,那就是精确异常,那什么是精确异常呢?其实意思是说,cpu在某一个阶段发生了异常之后,并不急于马上处理,而是等到了mem访存阶段来统一处理,因为说不定在运行过程中还会出现其他异常。

        发生异常的阶段很多,但是wb写回阶段是肯定不会发生异常的。所以,在mem阶段统一处理中断和异常是比较合适的。一般来说,中断的优先级高一点。如果有中断,先处理中断;没有中断,有异常的话,就先处理异常;如果这些都没有,cpu就正常执行好了。

        那什么情况下会发生异常呢?其实除了wb,其他每一个阶段都有可能发生异常。取指失败、译码不正确、执行阶段发生除0、加载内存数据失败等等,这些都可能发生异常的。但是,发生异常之后,cpu不是立刻就处理的,而是跟着流水线一步一步往前走,到了访存阶段才统一处理。

        假设目前译码阶段发生了异常,那么这个异常只是记录下来。它会被先被送到执行阶段,再被送到访存阶段,在访存阶段的时候,异常才会得到真正的处理。有同学也许会问,如果有多个异常怎么办呢?那就看谁的异常先送到访存阶段,先送到访存的异常肯定是最新受到处理的,哪怕它不是第一时间出现的那个异常。

1、异常传递

1)译码阶段的异常传递

2)执行阶段的异常传递

3)mem阶段的异常输入和整理输出



newCodeMoreWhite.png

2、异常的统一处理,文件为ctrl.v



newCodeMoreWhite.png

        从软件的角度来说,异常处理和函数调用很像。都是pc跳到另外一个地址,开始执行新的操作。等处理完了,再返回来继续进行原来的操作。但是,和函数调用不同的地方,异常处理需要flush掉原来的流水线,这是从软件的角度所看不到的差异

3、cp0寄存器处理

        获得了excepttype_i之后,就可以在clock上升沿的时候记录返回地址、中断原因,同时关闭中断开关了。这里有一个小细节需要注意下,如果当前mem阶段中正在执行的指令是延迟槽里面的指令,那还需要对pc进行-4的操作,不然pc地址就飞掉了。

4、异常返回

        在mips下面,异常返回的地址是eret。按照道理,这个时候应该返回到之前被中断的程序继续执行。那用什么方法处理比较好呢?一个比较简单的方法,就是把eret看成是和syscall一样的异常指令,等指令运行到mem阶段的时候,flush掉原来的流水线,恢复地址,打开中断即可。

        大家细看一下ctrl.v这段代码,也能明白eret是如何处理的。

5、defines.v中需要修改的一处代码

`define InstMemNum 128

        之前测试的汇编文件都比较短,但是在异常测试的case中,需要pc地址跳转。这个时候,编译器就会出现很多数值0的插入动作,故代码长度比原来要长一点。

6、准备汇编文件



newCodeMoreWhite.png

        汇编代码中的地址有三处,分别是0x0、0x40、0x100,中间没有汇编的地方,编译器会用0进行补全操作。

7、翻译成二进制文件



newCodeMoreWhite.png

8、利用iverilog和gtkwave进行波形分析

b222bf9d9d6e42cf8781619ae30697c1.png

        测试的时候可以重点观察一下pc寄存器和flush信号。pc寄存器主要记录了取指的顺序,而flush表示了cpu当前正在发生异常,需要进行流水下清空,下一步pc就要跳转了。首先查看pc为0,接着跳到0x100,结合汇编来看,这一切都算正常。等到390ns的时候,发现出现了flush清空操作。

7df789256ad940829d6a3761c6a3d265.png

        因为异常只有mem阶段才会处理,而pc地址可能已经提前走了三步。当前pc是0x118,因为每条指令的长度是4,所以0x118 - 0x4*3 = 0x10C。这个时候看0x10c处的指令是什么即可。对着汇编文件看了下,原来是syscall,那么这个时候发生异常被执行也就不奇怪了。

        继续往后,可以观察下一次flush是什么时候被触发的。

2c13817adc76495786ecd92be30d3ba2.png

        看了一下 pc地址,数值为0x60。根据我们的经验,触发异常的指令地址是0x60-0x4 * 3 = 0x54。这个时候,对着汇编查看一下0x54对应的汇编指令是什么,原来是eret,也就是中断返回。所以这个时候,相当于再次借助于exception机制对流水线做了一次flush操作。

3c4b2ba0bdfc4be288b6ea8c58464a47.png

        并且,我们还惊奇的发现,中断后继续执行的pc地址是0x110,这就是之前0x10c后面一条指令的地址。而0x10c就是发生异常执行syscall的地址。这样一来,所有的汇编代码、波形图就全部对上了。

9、中断测试

1)准备中断测试的汇编代码



newCodeMoreWhite.png

2)翻译成二进制文件



newCodeMoreWhite.png

3)利用iverilog和gtkwave分析

37a0f76474bf41bc975c3d3f65ee39a4.png

        结合汇编代码,我们发现这是一个内部compare和count寄存器产生的中断。主程序中,初始化compare寄存器,打开中断。所有一切做完之后,_loop循环。如果发生中断,就会跳到0x20处理中断。中断处理程序中继续设置compare寄存器,方便下次继续产生中断。等到中断处理结束,eret返回原来的程序继续执行。周而复始,就是这么一个处理过程。

54c50111e3e143eaa26c088bb8f6c8bd.png

         对于数字电路分析来说,中断观察pc寄存器和flush的数值即可达到此目的。 

 注:后面的话

        中断和异常是cpu的一个重要组成部分,建议可以反复看看、反复思考。一旦掌握了,后续收益很大,对于debug和性能分析都有很大的好处。

        至此,关于cpu的分析就结束了,也许有同学会说,还有总线、gpio、uart、flash这些外设可以聊一聊。个人看来,这些外设都是作为单一功能模块独立存在的,他们都是为了配合cpu而形成一个完整的mcu或者soc而存在的。只要掌握了cpu设计的精髓,一般的外设ip编写,难度不大的。

5b4badb4b8dc45e489abcd26354bd649.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK