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

news2024/11/28 13:53:15

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

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

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

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

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

1、异常传递

1)译码阶段的异常传递

  //exceptiontype的低8bit留给外部中断,第9bit表示是否是syscall指令
  //第10bit表示是否是无效指令,第11bit表示是否是trap指令
  assign excepttype_o = {19'b0,excepttype_is_eret,2'b0,
  												instvalid, excepttype_is_syscall,8'b0};
  //assign excepttye_is_trapinst = 1'b0;
  
	assign current_inst_address_o = pc_i;

2)执行阶段的异常传递

  assign excepttype_o = {excepttype_i[31:12],ovassert,trapassert,excepttype_i[9:8],8'h00};
  
	assign is_in_delayslot_o = is_in_delayslot_i;
	assign current_inst_address_o = current_inst_address_i;

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

	always @ (*) begin
		if(rst == `RstEnable) begin
			excepttype_o <= `ZeroWord;
		end else begin
			excepttype_o <= `ZeroWord;
			
			if(current_inst_address_i != `ZeroWord) begin
				if(((cp0_cause[15:8] & (cp0_status[15:8])) != 8'h00) && (cp0_status[1] == 1'b0) && 
							(cp0_status[0] == 1'b1)) begin
					excepttype_o <= 32'h00000001;        //interrupt
				end else if(excepttype_i[8] == 1'b1) begin
			  	excepttype_o <= 32'h00000008;        //syscall
				end else if(excepttype_i[9] == 1'b1) begin
					excepttype_o <= 32'h0000000a;        //inst_invalid
				end else if(excepttype_i[10] ==1'b1) begin
					excepttype_o <= 32'h0000000d;        //trap
				end else if(excepttype_i[11] == 1'b1) begin  //ov
					excepttype_o <= 32'h0000000c;
				end else if(excepttype_i[12] == 1'b1) begin  //返回指令
					excepttype_o <= 32'h0000000e;
				end
			end
				
		end
	end	

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

`include "defines.v"

module ctrl(

	input wire										rst,

	input wire[31:0]             excepttype_i,
	input wire[`RegBus]          cp0_epc_i,

	input wire                   stallreq_from_id,

  //来自执行阶段的暂停请求
	input wire                   stallreq_from_ex,

	output reg[`RegBus]          new_pc,
	output reg                   flush,	
	output reg[5:0]              stall       
	
);


	always @ (*) begin
		if(rst == `RstEnable) begin
			stall <= 6'b000000;
			flush <= 1'b0;
			new_pc <= `ZeroWord;
		end else if(excepttype_i != `ZeroWord) begin
		  flush <= 1'b1;
		  stall <= 6'b000000;
			case (excepttype_i)
				32'h00000001:		begin   //interrupt
					new_pc <= 32'h00000020;
				end
				32'h00000008:		begin   //syscall
					new_pc <= 32'h00000040;
				end
				32'h0000000a:		begin   //inst_invalid
					new_pc <= 32'h00000040;
				end
				32'h0000000d:		begin   //trap
					new_pc <= 32'h00000040;
				end
				32'h0000000c:		begin   //ov
					new_pc <= 32'h00000040;
				end
				32'h0000000e:		begin   //eret
					new_pc <= cp0_epc_i;
				end
				default	: begin
				end
			endcase 						
		end else if(stallreq_from_ex == `Stop) begin
			stall <= 6'b001111;
			flush <= 1'b0;		
		end else if(stallreq_from_id == `Stop) begin
			stall <= 6'b000111;	
			flush <= 1'b0;		
		end else begin
			stall <= 6'b000000;
			flush <= 1'b0;
			new_pc <= `ZeroWord;		
		end    //if
	end      //always
			

endmodule

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

3、cp0寄存器处理

			case (excepttype_i)
				32'h00000001:		begin
					if(is_in_delayslot_i == `InDelaySlot ) begin
						epc_o <= current_inst_addr_i - 4 ;
						cause_o[31] <= 1'b1;
					end else begin
					  epc_o <= current_inst_addr_i;
					  cause_o[31] <= 1'b0;
					end
					status_o[1] <= 1'b1;
					cause_o[6:2] <= 5'b00000;
					
				end

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

4、异常返回

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

				32'h0000000e:		begin   //eret
					new_pc <= cp0_epc_i;
				end

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

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

`define InstMemNum 128

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

6、准备汇编文件

   .org 0x0
   .set noat
   .set noreorder
   .set nomacro
   .global _start
_start:
   ori $1,$0,0x100     # $1 = 0x100
   jr $1
   nop

   .org 0x40
   ori $1,$0,0x8000    # $1 = 0x00008000
   ori $1,$0,0x9000    # $1 = 0x00009000
   mfc0 $1,$14,0x0     # $1 = 0x0000010c
   addi $1,$1,0x4      # $1 = 0x00000110
   mtc0 $1,$14,0x0
   eret
   nop

   .org 0x100
   ori $1,$0,0x1000    # $1 = 0x1000
   sw  $1, 0x0100($0)  # [0x100] = 0x00001000
   mthi $1             # HI = 0x00001000
   syscall
   lw  $1, 0x0100($0)  # $1 = 0x00001000
   mfhi $2             # $2 = 0x00001000             
_loop:
   j _loop
   nop
   
   

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

7、翻译成二进制文件

34010100
00200008
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
34018000
34019000
40017000
20210004
40817000
42000018
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
34011000
ac010100
00200011
0000000c
8c010100
00001010
08000046
00000000

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

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

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

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

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

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

 9、后面的话

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

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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/49483.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法竞赛入门【码蹄集进阶塔335题】(MT2291-2295)

算法竞赛入门【码蹄集进阶塔335题】(MT2291-2295&#xff09; 文章目录算法竞赛入门【码蹄集进阶塔335题】(MT2291-2295&#xff09;前言为什么突然想学算法了&#xff1f;为什么选择码蹄集作为刷题软件&#xff1f;目录1. MT2291 饿饿!饭饭!2. MT2292 甜甜花的研究3. MT2293 赌…

【2013NOIP普及组】T4. 车站分级 试题解析

【2013NOIP普及组】T4. 车站分级 试题解析 时间限制: 1000 ms 内存限制: 131072 KB 【题目描述】 一条单向的铁路线上,依次有编号为 1,2,…,n 的 n 个火车站。每个火车站都有一个级别,最低为 1 级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟…

护眼灯真的有用吗?2022双十二选哪个牌子的护眼台灯好

护眼灯对保护眼睛是真的有用&#xff0c;它不是那种如医学奇迹般的治疗眼睛疾病&#xff0c;或者降低近视度数等等&#xff0c;这样的伪科学只会让人觉得是智商税。护眼灯的作用原理很简单也很有效&#xff0c;即通过各种方法提高光线的舒适度&#xff0c;使人眼在晚上长时间工…

厦门市会展局携手美创:以数据为核心的安全建设守护“云上会展”

新冠疫情影响下&#xff0c;会展业与云计算、大数据、物联网等数字技术加速融合&#xff0c;“云上会展”成为新趋势。然而风口之下&#xff0c;高价值的展会敏感数据无时不面临着被窃取、攻击的风险。因此&#xff0c;成熟配套的数据安全能力体系建设&#xff0c;也是会展业创…

Monaco Editor教程(二十):在编辑器的某个特定位置插入自定义的dom内容,图片,表单,表格,视频

前言 哇咔咔&#xff0c;这是我的第20篇Monaco教程&#xff0c;写完这一篇会暂时休息一段时间&#xff0c;练练字&#xff0c;存存稿&#xff0c;读读书&#xff0c;顺便修修文章。 目前全网成系统的monaco中文专栏应该只有我这一个&#xff0c;欢迎评论区打脸。自结束了GitLa…

面试题------线程池的拒绝策略

面试题------线程池的拒绝策略 线程池有7个核心参数 1.核心线程数 2.最大线程数 3.非核心线程存活时间 4.存活时间的单位 5.工作队列 6.线程自定义的一些配置 7.拒绝策略&#xff08;当达到最大线程数、且工作队列也满了会执行拒绝策略&#xff09; public ThreadPoolExecutor…

马上2023年了,学一下gradle(Gradle)安装及配置

Gradle学习 例如&#xff1a;相信已经很多公司在用了&#xff0c;但是小伙伴对此还是很模糊 文章目录Gradle学习Gradle一、Gradle介绍&#xff1f;二、常见的项目构建工具gradle安装1.下载2. 配置&#xff08;环境变量&#xff09;2.1打开环境变量2.2**新建环境变量**2.3在Pat…

【C++】STL—vector的常用接口

文章目录前言一、vector介绍二、vector的使用1. vector的定义2. vector的遍历2.1.operator[ ]2.2.迭代器2.3.范围for3. vector的空间增长问题3.1.size和capacity3.2.max_size和empty3.3.reserve3.4.resize3.5.Shrink to fit4. vector的增删查改4.1.push_back和pop_backinsert和…

Vue 3的高颜值UI组件库

Vue 3.0 已经发布两年多的时间&#xff0c;今年 2 月 Vue 3.0 也正式成为新的默认版本。今天就来分享 7 个适用于 Vue 3 的高颜值 UI 组件库&#xff01; Element Plus Element Plus 是一套由饿了么开源出品的为开发者、设计师和产品经理准备的基于 Vue 3.0 的组件库。Elemen…

【能源管理】制造行业中汽车厂房综合能效管理平台应用分析

安科瑞 李亚俊 平台概述 壹捌柒贰壹零玖捌柒伍柒AcrelEMS-EV汽车厂房能效管理平台集变电站综合自动化、电力监控、电气安全、电能质量分析及治理、能耗管理、能效分析、照明控制、充电桩运营管理、设备运维于一体&#xff0c;为建立可靠、安全的工厂能源管理体系提供数据支持…

【Flink】处理迟到元素(续)、自定义水位线和多流的合并与合流

文章目录一 处理迟到元素1 处理策略&#xff08;3&#xff09;使用迟到元素更新窗口计算结果a 代码编写b 测试二 自定义水位线1 产生水位线的接口2 自定义水位线的产生逻辑三 多流的合流与分流1 union算子2 水位线传递规则&#xff08;1&#xff09; 分流a 代码编写b 测试&…

virtio-net 实现机制【二】(图文并茂)

4. virio-net前端驱动分析 4.1 重要数据结构 4.1.1 send_queue结构 send_queue结构是对virtqueue的封装&#xff0c;是virtio-net的发送队列&#xff0c;即数据流向从前端驱动&#xff08;Guest&#xff09;到后端驱动&#xff08;Host&#xff09; 4.1.2 receive_queue结构…

【PlasticSCM Could Edition】新版本云托管浅试2

【PlasticSCM Could Edition】新版本云托管浅试2首先修复更改托管提一嘴首先 建议还是使用 PlasticHub&#xff0c;不要去用 PlasticSCM Cloud 原因&#xff1a; 由于比较新&#xff0c;伴随着的就是 —— 太多 bigs 了&#xff01;而且不知道怎么去改。 当时我创建了一个…

数据结构复习

期末的一套模拟题&#xff0c;仅供参考&#xff0c;切莫作为期末考试依据&#xff01;&#xff01;&#xff01; 选择题 数组A[1..5,1..6]每个元素占5个单元&#xff0c;将其按行优先次序存储在起始地址为1000的连续的内存单元中&#xff0c;则元素A[5,5]的地址为&#xff1a;…

轻量应用服务器部署vue项目

首先我已经拥有一个轻量云服务器了.windows2012的版本&#xff1a; 1.搭建 FTP 服务 为了将我们打包好的vue项目传到服务器&#xff0c;我们要先在服务器搭建FTP服务。 具体步骤可以参考官方文档&#xff0c;官方文档的教程十分清楚详细&#xff0c;按照步骤来就不会出错&am…

传奇服务端服务端运行7个窗口的各窗口功能讲解

大家都知道打开传奇版本里的游戏引擎后&#xff0c;就会弹出7各窗口&#xff0c;下面给大家讲解下各窗口的功能定义 GameCenter是游戏服务器启动程序&#xff0c;负责将DBServer.exe处转来的客户 端转发到相应的M2Server.exe。 DBServer.exe是用户数据库服务器&#xff0c;负责…

Allure与Jenkins持续集成

目标&#xff1a;每次提交代码到代码托管平台&#xff08;gitee&#xff09;&#xff0c;自动触发jenkins项目构建&#xff0c;生成allure测试报告&#xff0c;并发送邮件通知。 1、部署jenkins Jenkins — 快速入门 2、配置Gitee &#xff08;1&#xff09;Plugin Manager&…

01_openstack概述

一、openstack起源 Openstack是一个由NASA(美国国家航空航天局)和Rackspace合作研发并发起的项目Openstack是一套IaaS解决方案Openstack是一个开源的云计算管理平台以Apache许可证为授权二、Openstack七大组件 1、Horizaon组件 Horizon为Openstack服务的Web控制面板&#xff…

leecode#用Read4读取n个字符#相交链表

题目描述&#xff1a; 给你一个文件&#xff0c;并且该文件只能通过给定的 read4 方法来读取&#xff0c;请实现一个方法使其能够读取 n 个字符。 分析&#xff1a; read4 方法&#xff1a;API read4 可以从文件中读取 4 个连续的字符&#xff0c;并且将它们写入缓存数组 buf…

为什么电脑运行越来越慢?解决方法又是什么呢?

文章目录为什么电脑运行越来越慢&#xff1f;解决方法又是什么呢&#xff1f;一&#xff0c;电脑运行慢的原因二&#xff0c;提高电脑运行速度的方法1&#xff0c;重启电脑2&#xff0c;还原&#xff08;重置&#xff09;操作系统3&#xff0c;关闭不使用的应用程序4&#xff0…