cpu设计和实现(协处理器hi和lo)

news2025/7/13 18:47:35

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

        很多同学可能不了解mips处理器,如果个人想补充一点mips cpu的知识,可以找些书籍资料来读一下,比如《See Mips Run》。

        上一期说到了,在cpu设计的过程当中,使用了数据预取的技术。它解决的问题就是,因为流水线的关系,后续数据在计算的过程当中,如果仅仅从寄存器获取数据,就有可能发生数据读取错误的情况。因为这个时候,相关寄存器的数据可能在执行、访存、写回的阶段就已经被改写了,只不过数据暂时还没有写到寄存器里面而已。所以,这个时候就需要从执行、访存、写回模块把数据提前读回来,这样就不会发生数据读取错误的情况。

1、添加更多mips 命令  

      有了数据预取的操作,这个时候cpu就可以添加更多汇编指令来执行了。比如说,简单的逻辑指令and、andi、or、ori、xor、xori、nor、lui,简单的移位指令sll、sllv、sra、srav、srl、srlv等等。完成这些指令的解析之后,可以准备一段汇编代码,


  .org 0x0 
  .global _start 
  .set noat 
_start: 
  lui  $1,0x0101          
  ori  $1,$1,0x0101       
  ori  $2,$1,0x1100      
  or   $1,$1,$2          
  andi $3,$1,0x00fe      
  and  $1,$3,$1          
  xori $4,$1,0xff00     
  xor  $1,$4,$1           
  nor  $1,$4,$1           

        生成对应的二进制代码inst_rom.data,

3c010101
34210101
34221100
00220825
302300fe
00610824
3824ff00
00810826
00810827

        有了这些二进制代码,加上在译码、执行阶段添加必要的verilog代码。这样就可以通过波形图来验证我们的设计是否正确了。

         因为对应的汇编代码比较简单,所以可以直接通过一些寄存器的阅读就可以判断汇编代码是否执行正确。被引入的波形主要是三部分,第一部分是pc_reg0;第二部分是mem_wb0;第三部分是regfile1。pc_reg0主要看pc地址有没有层层递进;mem_wb0则查看exe-wb阶段要写回的寄存器地址、数值是什么,对不对;regfile1则确认一下写回的寄存器数值对不对。

        比如说从wb_wd寄存器地址为1开始,也就是汇编第一条指令的访存结束阶段,寄存器地址为1,数值为0x01010000,可以结合汇编指令看下是否正确。下一条写回的寄存器地址还是1,数值为0x01010101,继续结合汇编看下对不对。以此类推,不断检测要写的寄存器地址、寄存器数值,就可以判断之前添加的译码、执行、数据预取有没有做正确。这就是怎么读波形的一个方法。

        需要注意的是,regfile1会比mem_wb0晚一个上升沿,这个留意一下即可。

2、hi和lo寄存器

        hi和lo属于协处理器,不在通用寄存器的范围内。这两个寄存器主要是在用来处理乘法和除法。以乘法作为示例,如果两个整数相乘,那么乘法的结果低位保存在lo寄存器,高位保存在hi寄存器。当然,这两个寄存器也可以独立进行读取和写入。读的时候,使用mfhi、mflo;写入的时候,用mthi、mtlo。

        和通用寄存器不同,mfhi、mflo是在执行阶段才开始从hi、lo寄存器获取数值的。写入则和通用寄存器一样,也是在写回的时候完成的。

        也许这个时候,就有同学发现了一个问题。如果mfhi、mflo发现在访存和写回阶段,hi和lo的数值已经发生了变更怎么办?其实方法很简单,就是继续使用数据预取的方法即可。在数值还没有写回到hi和lo的时候,就提前把这些数据读回来。

`include "defines.v"

module hilo_reg(

	input	wire										clk,
	input wire										rst,
	
	
	input wire										we,
	input wire[`RegBus]				    hi_i,
	input wire[`RegBus]						lo_i,
	
	
	output reg[`RegBus]           hi_o,
	output reg[`RegBus]           lo_o
	
);

	always @ (posedge clk) begin
		if (rst == `RstEnable) begin
					hi_o <= `ZeroWord;
					lo_o <= `ZeroWord;
		end else if((we == `WriteEnable)) begin
					hi_o <= hi_i;
					lo_o <= lo_i;
		end
	end

endmodule

        这是hi、lo寄存器的访问文件hilo_reg.v。内容还是比较简单的。复位的时候,直接置为0;如果有写入的操作,则对hi_o、lo_o进行赋值操作。

        解决了hi、lo的写入、读取问题,下面就要在ex.v中解决数据依赖的问题,

	always @ (*) begin
		if(rst == `RstEnable) begin
			{HI,LO} <= {`ZeroWord,`ZeroWord};
		end else if(mem_whilo_i == `WriteEnable) begin
			{HI,LO} <= {mem_hi_i,mem_lo_i};
		end else if(wb_whilo_i == `WriteEnable) begin
			{HI,LO} <= {wb_hi_i,wb_lo_i};
		end else begin
			{HI,LO} <= {hi_i,lo_i};			
		end
	end	

        这段代码是组合逻辑。如果复位,则HI、LO为0;如果访存做了修改,那么直接从访存获取数值;如果写回做了修改,那么直接从写回获取数值;否则就是正常从hilo_reg.v那里获取数值。有了这段代码,那么对访存阶段的hi、lo输出就有了基本的依据,

	always @ (*) begin
		if(rst == `RstEnable) begin
			whilo_o <= `WriteDisable;
			hi_o <= `ZeroWord;
			lo_o <= `ZeroWord;		
		end else if(aluop_i == `EXE_MTHI_OP) begin
			whilo_o <= `WriteEnable;
			hi_o <= reg1_i;
			lo_o <= LO;
		end else if(aluop_i == `EXE_MTLO_OP) begin
			whilo_o <= `WriteEnable;
			hi_o <= HI;
			lo_o <= reg1_i;
		end else begin
			whilo_o <= `WriteDisable;
			hi_o <= `ZeroWord;
			lo_o <= `ZeroWord;
		end				
	end	

        注意,这段代码同样是一段组合逻辑。复位之后,没有操作;如果是mthi,那么将reg1_i和LO进行输出,这里的LO来自刚刚介绍的组合逻辑;如果是mtlo,那么将HI和reg1_i进行输出,同样HI也是来自之前的组合逻辑;当然如果上面的情况都不对,直接返回0。

        上面只是说了hi、lo操作的方法和原理。真正要实施起来,还需要对访存阶段的代码修改一下。至少需要把hi、lo必要的寄存器透传下去。

`include "defines.v"

module ex_mem(

	input	wire										clk,
	input wire										rst,
	
	
	
	input wire[`RegAddrBus]       ex_wd,
	input wire                    ex_wreg,
	input wire[`RegBus]					 ex_wdata, 	
	input wire[`RegBus]           ex_hi,
	input wire[`RegBus]           ex_lo,
	input wire                    ex_whilo, 	
	

	output reg[`RegAddrBus]      mem_wd,
	output reg                   mem_wreg,
	output reg[`RegBus]					 mem_wdata,
	output reg[`RegBus]          mem_hi,
	output reg[`RegBus]          mem_lo,
	output reg                   mem_whilo	
	
	
);


	always @ (posedge clk) begin
		if(rst == `RstEnable) begin
			mem_wd <= `NOPRegAddr;
			mem_wreg <= `WriteDisable;
		  mem_wdata <= `ZeroWord;	
		  mem_hi <= `ZeroWord;
		  mem_lo <= `ZeroWord;
		  mem_whilo <= `WriteDisable;		  
		end else begin
			mem_wd <= ex_wd;
			mem_wreg <= ex_wreg;
			mem_wdata <= ex_wdata;	
			mem_hi <= ex_hi;
			mem_lo <= ex_lo;
			mem_whilo <= ex_whilo;					
		end   
	end     
			

endmodule

        接下来就可以准备一段汇编代码测试下,

   .org 0x0
   .set noat
   .global _start
_start:
   lui $1,0x0000          
   lui $2,0xffff         
   lui $3,0x0505         
   lui $4,0x0000         

   movz $4,$2,$1         
   movn $4,$3,$1          
   movn $4,$3,$2          
   movz $4,$2,$3         

   mthi $0               
   mthi $2                
   mthi $3               
   mfhi $4                

   mtlo $3                
   mtlo $2                
   mtlo $1                
   mflo $4                

        转成必要的指令数据,16行有效汇编代码对应16条32位数据,

3c010000
3c02ffff
3c030505
3c040000
0041200a
0061200b
0062200b
0043200a
00000011
00400011
00600011
00002010
00600013
00400013
00200013
00002012

        做好了这些准备之后,就可以开始进行仿真测试了。同样,这里使用的都是《自己动手写cpu》里面的参考代码,是Chapter6里面的代码。借鉴使用别人的代码问题不大,关键是能够看懂、学会,这样就可以在自己的项目中不断实践了。当然,之前说过,需要对代码进行修改一下。

         简单分析下,先找到pc第一次取指操作,然后找到第一次wb数据。从图形上看,第一次写回数据的时候是290ns。写回的地址是0x1,数值是0x0,这个时候就可以结合汇编第一条代码确认下是不是这样的。没有问题的话,可以再确认下一个周期里面,是不是regs1真的发生了改变。这就是第一条汇编指令的分析过程。

        继续分析,直到450ns的时候,开始执行第一个协处理器指令mthi。

         这里分析下mthi是否正确,可以直接把两组信号拖出来。一组信号来自于mem_wb0;另一组信号来自于hilo_reg0。从450ns开始,第一次写入的数据hi是0x0、lo是0x0;第二次写入的数据hi是0xffff0000、lo是0x0,以此类推。这两个数据写的对不对,可以结合汇编代码一起来看下。对应的汇编代码应该是mthi $0,mthi $2这样的。

         从530ns开始,执行的命令变成了mtlo,分析方法和刚才mthi还是一样的。

        从上面可以看出,cpu的实现主要还是要静下心来慢慢做,耐得烦。要相信自己,问题总是可以解决的。

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

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

相关文章

Springboot中集成mongodb,mysql(密码从密码服务中获取并且动态更新)

一.密码服务&#xff1a;公司统一进行数据库密码管理&#xff0c;为了防止密码泄露&#xff0c;会不定时更换密码&#xff0c;服务端就需要获取密码&#xff0c;类似key,value账号类型&#xff0c;首先根据数据库名去密码服务注册一个账号&#xff0c;后面通过这个注册的这个账…

【python】-详解进程与线程

文章目录进程1、多任务2、进程介绍3、多进程1 进程的创建步骤2 通过进程类创建进程对象3 进程的创建与启动代码4、进程执行带有参数的任务1 进程执行带有参数的任务2 args 参数的使用3 kwargs 参数的使用4 代码实现5 获取进程编号1 os.getpid()的使用2 os.getppid()的使用3 代码…

PLC中ST编程的定时器

定义通电延时功能块TON的变量&#xff0c;掉电延时功能块TOF的变量&#xff1b; 通过实例名来使用定时器&#xff1b; IN: 和 PT: 是输入引脚&#xff0c;Q> 和 ET> 是输出引脚&#xff1b; 定时器的通过IN输入引脚来触发的&#xff1b;定时器尽量不要在IF内调用&#…

ceph集群的搭建

ceph集群部署&#xff08;准备阶段&#xff09; 1. 配置静态网络&#xff08;自选&#xff09; 配置静态IP 2. 配置主机名&#xff08;必做&#xff09; ceph01&#xff1a; hostnamectl set-hostname ceph01ceph02&#xff1a; hostnamectl set-hostname ceph02ceph03&a…

[C++]打开新世界的大门之C++入门

&#x1f941;作者&#xff1a;华丞臧 &#x1f4d5;​​​​专栏&#xff1a;【C】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449;LeetCode 目录 一、C关键字…

iOS适配Unity-2019

iOS适配Unity-2019 背景 由于2019起&#xff0c;Unity的Xcode工程&#xff0c;更改了项目结构。 Unity 2018的结构&#xff1a; 可以看Targets只有一个Unity-iPhone&#xff0c;Unity-iPhone直接依赖管理三方库。 Unity2019以后&#xff1a; Targets多了一个UnityFramework…

什么是地理信息系统(GIS)?

什么是地理信息系统&#xff08;GIS&#xff09;&#xff1f; 什么是地理信息系统&#xff08;GIS&#xff09;&#xff1f;GIS是一个收集、显示、管理和分析地理信息的系统。让我们进一步探讨地理信息系统的所有方面。 地理信息系统&#xff08;GIS&#xff09;将地理与数据连…

读《大话数据结构》溢彩加强版

源代码&#xff1a; C:\迅雷下载\2021072816023491335\59e95a4689eeb92f380f4ab2\202107\29976aaa-ef7a-11eb-aba5-00163e0a088c PPT: C:\迅雷下载\2021072816023491335\59e95a4689eeb92f380f4ab2\202009\942a5ce8-fe34-11ea-a6a1-00163e0396a1 参考文献&#xff1a; C:\迅雷下…

SpringBoot整合JSR-303表单校验

JSR-303表单校验 思考一个问题&#xff0c;引出JSR-303 为什么前端做了参数校验&#xff0c;后端还要进行参数校验&#xff1f; 普通用户通过页面操作&#xff0c;前端可以校验住参数的正确性。但如果有人获取到接口&#xff0c;利用接口调用工具比如&#xff1a;postman对后…

Python 基础测试题(含答案)

一、 选择题&#xff1a;每小题 2 分&#xff0c;共 40 分。 1、 下列标识符命名中&#xff0c; 符合规范的是&#xff08; &#xff09;。 A、 1_a B、 for C、 年龄 D、 a#b 2、 下列标识符中&#xff0c;不是 Python 支持的数据类型的是 &#xff08; &#xff09;。 A、…

深度学习之Python,OpenCV中的卷积

这篇博客将介绍图像内核和卷积。如果将图像视为一个大矩阵&#xff0c;那么图像内核只是一个位于图像顶部的微小矩阵。从左到右和从上到下滑动内核&#xff0c;计算输入图像和内核之间的元素乘法总和——称这个值为内核输出。内核输出存储在与输入图像相同 &#xff08;x&#…

数据结构(高阶)—— 红黑树

目录 一、红黑树的概念 二、红黑树的性质 三、红黑树的结点定义 四、红黑树的插入 五、红黑树的验证 六、红黑树与AVL树的比较 一、红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加了一个存储位表示结点的颜色&#xff0c;可以使Red或Bl…

升级打怪拿offer,10w+字总结的Java面试题(附答案)够你刷

升级打怪拿offer&#xff0c;献上熬夜整理最新“10w字总结的Java面试题&#xff08;附答案&#xff09;”够你刷&#xff01; 其包含的内容模块有&#xff1a;基础、JVM、多线程与高并发、Spring、MyBatis、SpringBoot、MYSQL、SpringCloud、Dubbo、Nginx、MQ、数据结构与算法…

CF104064 E. Exchange Students(NWERC2021)

题目分析 首先需要观察到一个性质&#xff1a;在最优方案下的操作一定是首先交换距离最近能交换的两个点来达到交换的效果&#xff0c;这个很好理解&#xff1a;题目要求如果要交换两个人的位置&#xff0c;中间的人的身高必须严格小于这两个人&#xff0c;因此合法的交换操作仅…

生成对抗网络(GAN)

GAN简介 GAN思想是一种二人的零和博弈思想&#xff0c;GAN中有两个博弈者&#xff0c;一个生成器&#xff08;G&#xff09;&#xff0c;一个判别器&#xff08;D&#xff09;&#xff0c;这两个模型都有各自的输入和输出。具体功能如下&#xff1a; 生成器&#xff08;G&…

声门脉冲语音处理

对于 0<t<tpeak&#xff0c;gattack(t) 攻击部分&#xff0c;即上升分支的时间&#xff0c;时间 t 的范围从 0 秒到最大峰值时间 tpeak &#xff0c;图示例中选择为大约总长度的 35%&#xff0c;即 tpeak35%⋅T0&#xff0c;或者在样本 Lattack⌊35%⋅Lg⌉ 中&#xff0c…

2023年系统规划与设计管理师-第三章信息技术服务知识

一. 思维导图 二.IT 服务管理 (ITSM) 1. 什么是 IT 服务管理 (ITSM)&#xff1f; IT 服务管理 (ITSM) 包含一组策略和实践&#xff0c;这些策略和实践可用于为最终用户实施、交付和管理 IT 服务&#xff0c;以满足最终用户的既定需求和企业的既定目标。 在此定义中&#xff0…

otn 709帧结构

otn架构说明: 基于G.709接口,包括波分侧和客户侧,客户侧通常用于互联互通。 光通路净荷单元:OPU0/OPU1/OPU2/OPU3/OPU4/flex,主要用于完成业务同步或异步映射; 光通路数据单元:ODU0/ODU1/ODU2/ODU3/ODU4/ODU-flex,完成通道连接性能监测和子速率复用、 光通路传送单元…

POJ1008:玛雅日历

一、Description During his last sabbatical, professor M. A. Ya made a surprising discovery about the old Maya calendar. From an old knotted message, professor discovered that the Maya civilization used a 365 day long year, called Haab, which had 19 months.…

Netty学习笔记

文章目录二、Netty 入门2.1、概述2.1.1、Netty 是什么&#xff1f;2.1.2、Netty 的作者2.1.3、Netty 的地位2.1.4、Netty 的优势2.2、Hello World2.2.1、目标2.2.2、服务器端2.2.3、客户端2.2.4、流程梳理&#x1f4a1; 提示2.3、组件2.3.1、EventLoop&#x1f4a1; 优雅关闭演…