本文于自己动手写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实现多周期处理器之——(三)数据相关问题及其解决相关推荐

  1. verilog实现多周期处理器之——(五)移动操作(通用数据传送)指令的实现

    本文参考作者 自己动手写CPU之第六阶段(1)--移动操作指令说明 自己动手写CPU之第六阶段(2)--移动操作指令实现思路 本文会添加笔者自己的思路以及理解在其中. 指令说明 这6条指令都是R类型指 ...

  2. verilog实现多周期处理器之——目录及总述

    本系列博文将使用verilog语言,实现兼容MIPS32指令集架构的处理器--OpenMIPS,MIPS是典型的RSIC处理器.主要依据雷思磊老师的<自己动手写CPU>,袁春风老师主编的& ...

  3. verilog实现多周期处理器之——(六)简单算数操作指令的实现

    实现的指令说明 这里解释有符号扩展与无符号扩展,有符号数扩展符号位.也就是1,无符号数扩展0.也就是在前面补满零或1 R-型指令 加减比较指令 add.addu.sub.sub.slt.sltu 这6 ...

  4. verilog实现多周期处理器之——(四)逻辑,移位操作与空指令的添加

    逻辑,移位操作与空指令的添加 综述 ID模块的修改 EX模块的修改 仿真验证 I-型指令 lui ori andi xori xor&nor R-型指令 or and 移位类指令 sll sr ...

  5. verilog实现多周期处理器之——(二)第一条指令ori的实现

    本博文希望对于OpenMIPS第一条指令ori加以实现并总结.会加入一些基本的理论以及博主的学习记录. 流水与五级流水 什么是流水:拆分,并行.将多条指令的执行相互重叠起来.就构成了流水,这样充分利用 ...

  6. verilog实现多周期处理器之——(一)基本概念以及总体框架

    本系列博文将使用verilog语言,实现兼容MIPS32指令集架构的处理器--OpenMIPS,MIPS是典型的RSIC处理器.本系列博文参考雷思磊老师的<自己动手写CPU>,袁春风老师主 ...

  7. verilog实现多周期处理器之——(零)GUN工具链的安装

    参考雷思磊老师得<自己动手写CPU> 这里不需要下载书中说的虚拟机,这里笔者用的是VMware.不需要破解,直接安装点击选择仅用于非商业即可使用!! 安装Ubuntu这里笔者不给出步骤. ...

  8. 自己动手写CPU之第五阶段(1)——流水线数据相关问题

    将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第15篇,我尽量每周四篇 上一章建立了原始的OpenMIPS五级流水线结构,可是仅仅实现了一条ori指令,从本章開始,将逐步完 ...

  9. 数据相关,资源相关,控制相关的解决方法

    解决结构相关问题: 解决方法:1.后续指令停顿2.指令存储器和数据存储器分开,多部件3.指令预存技术(适用于访存周期短的情况) 解决数据相关问题: 解决方法:旁路技术 解决控制相关问题: 解决方法:分 ...

最新文章

  1. 竞赛大杀器xgboost,波士顿房价预测
  2. 28本实体书包邮免费送!年前替我领回家!
  3. python编程界面怎么打开-为Python程序添加图形化界面的教程
  4. android studio使用nodejs本地服务器json数据_使用Node.js的Alexa技巧
  5. 离线轻量级大数据平台Spark之单机部署及Java开发
  6. python中format和int_python函数之format()
  7. 《React 学习之道》The Road to learn React (简体中文版)
  8. python文件传输模块_[宜配屋]听图阁 - python 使用poster模块进行http方式的文件传输到服务器的方法...
  9. 24. yii2 表单赋值 model-load(), model-attributes 方法
  10. 如何在云端编译APIcloud代码
  11. Delphi2007卸载
  12. aspcms转php,aspcms转phpcms方法
  13. 移动应用开发课堂总结
  14. 对XPO对象数据库访问数据库连接可控
  15. Python使用天气网api接口获取天气数据
  16. 黑马全新推出《2022年最实用的PS全套教程》
  17. file.exists(),file.isFile()和file.isDirectory()的区别
  18. EMV规范(七)——持卡人验证(CVM)一
  19. 7种抓包工具详细介绍
  20. 如何让思路更清晰?simplemind for Mac思维导图来帮忙

热门文章

  1. 【java】 java 高并发解决方案和高负载优化方法
  2. homebrew安装失败的解决方案
  3. BeyondCompare3 提示许可证密钥已被撤销解决方法
  4. ASP.NET文件上传大小的限制解决方案
  5. openstack上创建vm实例后,状态为ERROR问题解决
  6. 如何在Swift中发出HTTP请求?
  7. Git不断询问我ssh密钥密码
  8. AssemblyVersion,AssemblyFileVersion和AssemblyInformationalVersion之间有什么区别?
  9. 每个叶子节点(nil)是黑色。_填充每个节点的下一个右侧节点指针
  10. easypoi list中的map导出_EasyPOI简单用例,简单有效