verilog实现多周期处理器之——(三)数据相关问题及其解决
本文于自己动手写CPU之第五阶段——流水线数据相关问题
“相关”问题
流水线中常常有一些被称为“相关”的情况发生,它使得指令序列中下一条指令无法依照设计的时钟周期运行,这些“相关”会减少流水线的性能。
流水线中的相关分为三种类型。
相关类型 | 描述 |
---|---|
结构相关 | 指的是在指令运行的过程中,因为硬件资源满足不了指令运行的要求,发生硬件资源冲突而产生的相关。比方:指令和数据都共享一个存储器。在某个时钟周期,流水线既要完毕某条指令对存储器中数据的訪问操作,又要完毕兴许的取指令操作。这样就会发生存储器訪问冲突。产生结构相关。 |
数据相关 | 指在流水线中运行的几条指令中。一条指令依赖于前面指令的运行结果。 |
控制相关 | 指流水线中的分支指令或者其它须要改写PC的指令造成的相关。 |
结构相关、控制相关将在兴许指令分析中讨论,本节重点讨论数据相关的问题。流水线数据相关又分为三种情况:RAW、WAR、WAW。
类型 | 描述 |
---|---|
RAW | Read After Write,如果指令j是在指令i后面运行的指令。RAW表示指令i将数据写入寄存器后,指令j才干从这个寄存器读取数据。 |
WAR | Write After Read。如果指令j是在指令i后面运行的指令,WAR表示指令i读出数据后,指令j才干写这个寄存器。如果指令j在指令i读出数据前就写该寄存器,将使得指令i读出的数据不对。 |
WAW | Write After Write,如果指令j是在指令i后面运行的指令,WAW表示指令i将数据写入寄存器后,指令j才干将数据写入这个寄存器。如果指令j在指令i之前写该寄存器,将使得该寄存器的值不是最新值。 |
解决方案
使用的解决方法
这里使用的是数据前推的方式。
对于数据相关,相隔指令为一条,相隔两条指令数据相关,相隔指令为三条,解决方式分别为将执行的结果直接送至译码,将访存数据直接送至译码,或者将回写数据送入译码,即译码本身进行写入地址与读取地址的比对。若是要要读取寄存器的地址与要写入寄存器地址一样,那么直接将数据送入进行计算,不从寄存器相应的位置进行数据读取,避免出现相关的问题。
这里最大的原因是读写寄存器都是在寄存器堆完成的,因此这里只需要在译码阶段,将访存与执行的结果与需要读取的寄存器地址进行对比。然后决定数据读取的是从执行的结果还是具体的寄存器中。避免了流水线的暂停。
代码修改
首先解决相隔三条指令的相关问题。即为在寄存器堆中添加修改成如下代码。即可在读取时进行判断需要读取的数据是不是要写入的数据。若是,直接将写入的数据作为读出的数据即可。
/*********************************************************************************
*************************** 第三阶段:读端口1的读操作 ***************************
*********************************************************************************/always @ (*)
beginif(rst == `RstEnable)rdata1 <= `ZeroWord;else if(raddr1 == `RegNumLog2'h0 )rdata1 <= `ZeroWord;else if( (raddr1 == waddr) && (we == `WriteEnable) && (re1 == `ReadEnable))rdata1 <= wdata;else if(re1 == `ReadEnable)rdata1 <= regs[raddr1];else rdata1 <= `ZeroWord;
end/*********************************************************************************
*************************** 第四阶段:读端口2的读操作 ***************************
*********************************************************************************/always @ (*)
beginif(rst == `RstEnable)rdata2 <= `ZeroWord;else if(raddr2 == `RegNumLog2'h0 )rdata2 <= `ZeroWord;else if((raddr2 == waddr) && (we == `WriteEnable) && (re2 == `ReadEnable))rdata2 <= wdata;else if(re2 == `ReadEnable)rdata2 <= regs[raddr2];else rdata2 <= `ZeroWord;
end
接下来对于相隔两条指令以及一条指令进行修改。这里的做法是将访存执行阶段的数据引入译码阶段。
同时修改对应的端口即可。
/*********************************************************************************
*************************** 第二阶段:确定进行运算的源操作数1***************************
*********************************************************************************/
//regfile读端口1的输出值
always @ (*)
beginif(rst == `RstEnable)reg1_o <= `ZeroWord;else // 对于源操作数,若是目前端口的读取得寄存器数据地址是 执行阶段的要写入的目的寄存器 ,那么直接将执行的结果作为reg1_o的值。//这个数相当于是要写入的数据if((reg1_read_o == 1'b1) && (ex_wreg_i == 1'b1) && (ex_wd_i == reg1_addr_o) )reg1_o <= ex_wdata_i; else//对于要是目的寄存器,我们要读取的寄存器其实是最终访存要写入的寄存器,那么访存的数据就直接作为源操作数进行处理if((reg1_read_o == 1'b1) && (mem_wreg_i == 1'b1) && (mem_wd_i == reg1_addr_o) )reg1_o <= mem_wdata_i ;else if(reg1_read_o == 1'b1)reg1_o <= reg1_data_i;else if(reg1_read_o == 1'b0)reg1_o <= imm; //立即数else reg1_o <= `ZeroWord;
end/*********************************************************************************
*************************** 第三阶段:确定进行运算的源操作数2***************************
*********************************************************************************/
//regfile读端口2的输出值
always @ (*)
beginif(rst == `RstEnable)reg2_o <= `ZeroWord;else // 对于源操作数,若是目前端口的读取得寄存器数据地址是 执行阶段的要写入的目的寄存器 ,那么直接将执行的结果作为reg2_o的值。//这个数相当于是要写入的数据if((reg2_read_o == 1'b1) && (ex_wreg_i == 1'b1) && (ex_wd_i == reg2_addr_o) )reg2_o <= ex_wdata_i; else//对于要是目的寄存器,我们要读取的寄存器其实是最终访存要写入的寄存器,那么访存的数据就直接作为源操作数进行处理if((reg2_read_o == 1'b1) && (mem_wreg_i == 1'b1) && (mem_wd_i == reg2_addr_o) )reg2_o <= mem_wdata_i ;else if(reg2_read_o == 1'b1)reg2_o <= reg2_data_i;else if(reg2_read_o == 1'b0)reg2_o <= imm; //立即数else reg2_o <= `ZeroWord;
end
仿真验证
这里将验证相隔分别为一条两条和三条指令的情况。
最终结果与预期一致。说明相关问题处理结束。
verilog实现多周期处理器之——(三)数据相关问题及其解决相关推荐
- verilog实现多周期处理器之——(五)移动操作(通用数据传送)指令的实现
本文参考作者 自己动手写CPU之第六阶段(1)--移动操作指令说明 自己动手写CPU之第六阶段(2)--移动操作指令实现思路 本文会添加笔者自己的思路以及理解在其中. 指令说明 这6条指令都是R类型指 ...
- verilog实现多周期处理器之——目录及总述
本系列博文将使用verilog语言,实现兼容MIPS32指令集架构的处理器--OpenMIPS,MIPS是典型的RSIC处理器.主要依据雷思磊老师的<自己动手写CPU>,袁春风老师主编的& ...
- verilog实现多周期处理器之——(六)简单算数操作指令的实现
实现的指令说明 这里解释有符号扩展与无符号扩展,有符号数扩展符号位.也就是1,无符号数扩展0.也就是在前面补满零或1 R-型指令 加减比较指令 add.addu.sub.sub.slt.sltu 这6 ...
- verilog实现多周期处理器之——(四)逻辑,移位操作与空指令的添加
逻辑,移位操作与空指令的添加 综述 ID模块的修改 EX模块的修改 仿真验证 I-型指令 lui ori andi xori xor&nor R-型指令 or and 移位类指令 sll sr ...
- verilog实现多周期处理器之——(二)第一条指令ori的实现
本博文希望对于OpenMIPS第一条指令ori加以实现并总结.会加入一些基本的理论以及博主的学习记录. 流水与五级流水 什么是流水:拆分,并行.将多条指令的执行相互重叠起来.就构成了流水,这样充分利用 ...
- verilog实现多周期处理器之——(一)基本概念以及总体框架
本系列博文将使用verilog语言,实现兼容MIPS32指令集架构的处理器--OpenMIPS,MIPS是典型的RSIC处理器.本系列博文参考雷思磊老师的<自己动手写CPU>,袁春风老师主 ...
- verilog实现多周期处理器之——(零)GUN工具链的安装
参考雷思磊老师得<自己动手写CPU> 这里不需要下载书中说的虚拟机,这里笔者用的是VMware.不需要破解,直接安装点击选择仅用于非商业即可使用!! 安装Ubuntu这里笔者不给出步骤. ...
- 自己动手写CPU之第五阶段(1)——流水线数据相关问题
将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第15篇,我尽量每周四篇 上一章建立了原始的OpenMIPS五级流水线结构,可是仅仅实现了一条ori指令,从本章開始,将逐步完 ...
- 数据相关,资源相关,控制相关的解决方法
解决结构相关问题: 解决方法:1.后续指令停顿2.指令存储器和数据存储器分开,多部件3.指令预存技术(适用于访存周期短的情况) 解决数据相关问题: 解决方法:旁路技术 解决控制相关问题: 解决方法:分 ...
最新文章
- 竞赛大杀器xgboost,波士顿房价预测
- 28本实体书包邮免费送!年前替我领回家!
- python编程界面怎么打开-为Python程序添加图形化界面的教程
- android studio使用nodejs本地服务器json数据_使用Node.js的Alexa技巧
- 离线轻量级大数据平台Spark之单机部署及Java开发
- python中format和int_python函数之format()
- 《React 学习之道》The Road to learn React (简体中文版)
- python文件传输模块_[宜配屋]听图阁 - python 使用poster模块进行http方式的文件传输到服务器的方法...
- 24. yii2 表单赋值 model-load(), model-attributes 方法
- 如何在云端编译APIcloud代码
- Delphi2007卸载
- aspcms转php,aspcms转phpcms方法
- 移动应用开发课堂总结
- 对XPO对象数据库访问数据库连接可控
- Python使用天气网api接口获取天气数据
- 黑马全新推出《2022年最实用的PS全套教程》
- file.exists(),file.isFile()和file.isDirectory()的区别
- EMV规范(七)——持卡人验证(CVM)一
- 7种抓包工具详细介绍
- 如何让思路更清晰?simplemind for Mac思维导图来帮忙
热门文章
- 【java】 java 高并发解决方案和高负载优化方法
- homebrew安装失败的解决方案
- BeyondCompare3 提示许可证密钥已被撤销解决方法
- ASP.NET文件上传大小的限制解决方案
- openstack上创建vm实例后,状态为ERROR问题解决
- 如何在Swift中发出HTTP请求?
- Git不断询问我ssh密钥密码
- AssemblyVersion,AssemblyFileVersion和AssemblyInformationalVersion之间有什么区别?
- 每个叶子节点(nil)是黑色。_填充每个节点的下一个右侧节点指针
- easypoi list中的map导出_EasyPOI简单用例,简单有效