将陆续上传本人写的新书《自己动手写CPU》。今天是第30篇。我尽量每周四篇

亚马逊的销售地址例如以下。欢迎大家围观呵!

http://www.amazon.cn/dp/b00mqkrlg8/ref=cm_sw_r_si_dp_5kq8tb1gyhja4

China-pub的销售地址例如以下:

http://product.china-pub.com/3804025

北发的销售地址例如以下:

http://book.beifabook.com/Product/BookDetail.aspx?Plucode=712123950&extra=0_s25960657

7.8 改动OpenMIPS以实现乘累加、乘累减指令

7.8.1 改动译码阶段的ID模块

译码阶段的ID模块要加入对乘累加、乘累减指令的分析。根据图7-11给出的指令格式可知,这4条指令都是SPECIAL2类指令,能够根据功能码确定是哪一种指令,确定指令的过程如图7-13所看到的。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGVpc2hhbmd3ZW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

当中涉及的宏定义例如以下,正是图7-13中各个指令的功能码。在本书附带光盘Code\Chapter7_2文件夹下的defines.v文件里能够找到这些定义。

  1. `define EXE_MADD 6'b000000
  2. `define EXE_MADDU 6'b000001
  3. `define EXE_MSUB 6'b000100
  4. `define EXE_MSUBU 6'b000101

译码阶段的ID模块主要改动内容例如以下。完整代码请參考本书光盘Code\Chapter7_2文件夹下的id.v文件。

  1. module id(
  2. ......
  3. );
  4.  
  5. ......
  6. assign stallreq = `NoStop;
  7.  
  8. always @ (*) begin
  9. if (rst == `RstEnable) begin
  10. ......
  11. end else begin
  12. aluop_o <= `EXE_NOP_OP;
  13. alusel_o <= `EXE_RES_NOP;
  14. wd_o <= inst_i[15:11]; // 默认目的寄存器地址wd_o
  15. wreg_o <= `WriteDisable;
  16. instvalid <= `InstInvalid;
  17. reg1_read_o <= 1'b0;
  18. reg2_read_o <= 1'b0;
  19. reg1_addr_o <= inst_i[25:21]; // 默认的reg1_addr_o
  20. reg2_addr_o <= inst_i[20:16]; // 默认的reg2_addr_o
  21. imm <= `ZeroWord;
  22. case (op)
  23. ......
  24. `EXE_SPECIAL2_INST: begin // SPECIAL2类指令
  25. case ( op3 )
  26. ......
  27. `EXE_MADD: begin // madd指令
  28. wreg_o <= `WriteDisable;
  29. aluop_o <= `EXE_MADD_OP;
  30. alusel_o <= `EXE_RES_MUL;
  31. reg1_read_o <= 1'b1;
  32. reg2_read_o <= 1'b1;
  33. instvalid <= `InstValid;
  34. end
  35. `EXE_MADDU: begin // maddu指令
  36. wreg_o <= `WriteDisable;
  37. aluop_o <= `EXE_MADDU_OP;
  38. alusel_o <= `EXE_RES_MUL;
  39. reg1_read_o <= 1'b1;
  40. reg2_read_o <= 1'b1;
  41. instvalid <= `InstValid;
  42. end
  43. `EXE_MSUB: begin // msub指令
  44. wreg_o <= `WriteDisable;
  45. aluop_o <= `EXE_MSUB_OP;
  46. alusel_o <= `EXE_RES_MUL;
  47. reg1_read_o <= 1'b1;
  48. reg2_read_o <= 1'b1;
  49. instvalid <= `InstValid;
  50. end
  51. `EXE_MSUBU: begin // msubu指令
  52. wreg_o <= `WriteDisable;
  53. aluop_o <= `EXE_MSUBU_OP;
  54. alusel_o <= `EXE_RES_MUL;
  55. reg1_read_o <= 1'b1;
  56. reg2_read_o <= 1'b1;
  57. instvalid <= `InstValid;
  58. end
  59. default: begin
  60. end
  61. endcase //EXE_SPECIAL_INST2 case
  62. ......
  63.  
  64. endmodule

这4条指令的译码过程都是相似的。简单说明例如以下。

(1)由于终于结果都是写入HI、LO寄存器,而不是写入通用寄存器。所以设置wreg_o为WriteDisable。

(2)由于都要读取两个寄存器的值。所以设置reg1_read_o、reg2_read_o为1'b1。默认通过Regfile模块读port1读取的寄存器地址reg1_addr_o的值是指令的21-25bit,正是指令中的rs,默认通过Regfile模块读port2读取的寄存器地址reg2_addr_o的值是指令的16-20bit,正是指令中的rt。所以终于译码阶段的输出reg1_o就是地址为rs的寄存器的值。reg2_o就是地址为rt的寄存器的值。

(3)运算类型alusel_o的值都设置为EXE_RES_MUL。只是因为没有要写的通用寄存器,所以此处的alusel_o的值并没有作用。也能够设置为EXE_RES_NOP。

(4)运算子类型aluop_o的值设置为与详细指令相应。

7.8.2 改动运行阶段的EX模块

參考图7-12可知,EX模块要添加4个接口。含义如表7-2所看到的。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGVpc2hhbmd3ZW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

EX模块的代码主要改动例如以下。完整代码请參考本书附带光盘Code\Chapter7_2文件夹下的ex.v文件。

  1. module ex(
  2.  
  3. ......
  4.  
  5. // 添加的输入接口
  6. input wire[`DoubleRegBus] hilo_temp_i,
  7. input wire[1:0] cnt_i,
  8.  
  9. ......
  10.  
  11. // 添加的输出接口
  12. output reg[`DoubleRegBus] hilo_temp_o,
  13. output reg[1:0] cnt_o,
  14.  
  15. output reg stallreq
  16.  
  17. );
  18.  
  19. ......
  20. wire[`RegBus] opdata1_mult;
  21. wire[`RegBus] opdata2_mult;
  22. wire[`DoubleRegBus] hilo_temp;
  23. reg[`DoubleRegBus] hilo_temp1;
  24. reg stallreq_for_madd_msub;
  25.  
  26. ......
  27.  
  28. /****************************************************************
  29. *********** 第一段:计算乘法结果 *********
  30. *****************************************************************/
  31.  
  32. //(1)取得乘法操作的被乘数,指令madd、msub都是有符号乘法,假设第一个
  33. // 操作数reg1_i是负数,那么取reg1_i的补码作为被乘数,反之。直接
  34. // 使用reg1_i作为被乘数
  35. assign opdata1_mult = (((aluop_i == `EXE_MUL_OP) ||
  36. (aluop_i == `EXE_MULT_OP) ||
  37. (aluop_i == `EXE_MADD_OP) ||
  38. (aluop_i == `EXE_MSUB_OP))&&
  39. (reg1_i[31] == 1'b1)) ? (~reg1_i + 1) : reg1_i;
  40.  
  41. //(2)取得乘法操作的乘数,指令madd、msub是有符号乘法。假设第二个
  42. // 操作数reg2_i是负数,那么取reg2_i的补码作为乘数。反之。直接
  43. // 使用reg2_i作为乘数
  44. assign opdata2_mult = (((aluop_i == `EXE_MUL_OP) ||
  45. (aluop_i == `EXE_MULT_OP) ||
  46. (aluop_i == `EXE_MADD_OP) ||
  47. (aluop_i == `EXE_MSUB_OP))&&
  48. (reg2_i[31] == 1'b1)) ? (~reg2_i + 1) : reg2_i;
  49.  
  50. //(3)得到暂时乘法结果,保存在变量hilo_temp中
  51. assign hilo_temp = opdata1_mult * opdata2_mult;
  52.  
  53. //(4)对暂时乘法结果进行修正,终于的乘法结果保存在变量mulres中,有两种情况:
  54. // A、假设是有符号乘法运算madd、msub。那么须要修正暂时乘法结果,例如以下:
  55. // A1、假设被乘数与乘数,两者一正一负,那么须要对暂时乘法结果
  56. // hilo_temp求补码,作为终于的乘法结果。赋给变量mulres。
  57. // A2、假设被乘数与乘数同号。那么hilo_temp的值就作为mulres
  58. // 的值。
  59. // B、假设是无符号乘法运算maddu、msubu。那么hilo_temp的值就作为
  60. // 终于的乘法结果,赋给变量mulres。
  61.  
  62. always @ (*) begin
  63. if(rst == `RstEnable) begin
  64. mulres <= {`ZeroWord,`ZeroWord};
  65. end else if ((aluop_i == `EXE_MULT_OP) || (aluop_i == `EXE_MUL_OP) ||
  66. (aluop_i == `EXE_MADD_OP) || (aluop_i == `EXE_MSUB_OP)) begin
  67. if(reg1_i[31] ^ reg2_i[31] == 1'b1) begin
  68. mulres <= ~hilo_temp + 1;
  69. end else begin
  70. mulres <= hilo_temp;
  71. end
  72. end else begin
  73. mulres <= hilo_temp;
  74. end
  75. end
  76.  
  77. /****************************************************************
  78. *********** 第二段:乘累加、乘累减 *********
  79. *****************************************************************/
  80.  
  81. // MADD、MADDU、MSUB、MSUBU指令
  82. always @ (*) begin
  83. if(rst == `RstEnable) begin
  84. hilo_temp_o <= {`ZeroWord,`ZeroWord};
  85. cnt_o <= 2'b00;
  86. stallreq_for_madd_msub <= `NoStop;
  87. end else begin
  88. case (aluop_i)
  89. `EXE_MADD_OP, `EXE_MADDU_OP: begin // madd、maddu指令
  90. if(cnt_i == 2'b00) begin // 运行阶段第一个时钟周期
  91. hilo_temp_o <= mulres;
  92. cnt_o <= 2'b01;
  93. hilo_temp1 <= {`ZeroWord,`ZeroWord};
  94. stallreq_for_madd_msub <= `Stop;
  95. end else if(cnt_i == 2'b01) begin // 运行阶段第二个时钟周期
  96. hilo_temp_o <= {`ZeroWord,`ZeroWord};
  97. cnt_o <= 2'b10;
  98. hilo_temp1 <= hilo_temp_i + {HI,LO};
  99. stallreq_for_madd_msub <= `NoStop;
  100. end
  101. end
  102. `EXE_MSUB_OP, `EXE_MSUBU_OP: begin // msub、msubu指令
  103. if(cnt_i == 2'b00) begin // 运行阶段第一个时钟周期
  104. hilo_temp_o <= ~mulres + 1 ;
  105. cnt_o <= 2'b01;
  106. stallreq_for_madd_msub <= `Stop;
  107. end else if(cnt_i == 2'b01)begin // 运行阶段第二个时钟周期
  108. hilo_temp_o <= {`ZeroWord,`ZeroWord};
  109. cnt_o <= 2'b10;
  110. hilo_temp1 <= hilo_temp_i + {HI,LO};
  111. stallreq_for_madd_msub <= `NoStop;
  112. end
  113. end
  114. default: begin
  115. hilo_temp_o <= {`ZeroWord,`ZeroWord};
  116. cnt_o <= 2'b00;
  117. stallreq_for_madd_msub <= `NoStop;
  118. end
  119. endcase
  120. end
  121. end
  122.  
  123. /****************************************************************
  124. *********** 第三段:暂停流水线 *********
  125. *****************************************************************/
  126.  
  127. // 眼下仅仅有乘累加、乘累减指令会导致流水线暂停,所以stallreq就直接等于
  128. // stallreq_for_madd_msub的值
  129. always @ (*) begin
  130. stallreq = stallreq_for_madd_msub;
  131. end
  132.  
  133. ......
  134.  
  135. /****************************************************************
  136. *********** 第四段:改动HI、LO寄存器的写信息 ********
  137. *****************************************************************/
  138.  
  139. always @ (*) begin
  140. if(rst == `RstEnable) begin
  141. whilo_o <= `WriteDisable;
  142. hi_o <= `ZeroWord;
  143. lo_o <= `ZeroWord;
  144. end else if((aluop_i == `EXE_MSUB_OP) || (aluop_i == `EXE_MSUBU_OP)) begin
  145. whilo_o <= `WriteEnable;
  146. hi_o <= hilo_temp1[63:32];
  147. lo_o <= hilo_temp1[31:0];
  148. end else if((aluop_i == `EXE_MADD_OP) ||
  149. (aluop_i == `EXE_MADDU_OP)) begin
  150. whilo_o <= `WriteEnable;
  151. hi_o <= hilo_temp1[63:32];
  152. lo_o <= hilo_temp1[31:0];
  153. ......
  154.  
  155. endmodule

上述代码能够分为四段理解。

(1)第一段:计算从通用寄存器中读出的两个寄存器的乘法结果,保存在mulres中。

(2)第二段:以乘累加指令为例进行解说。

乘累减指令与此类似。

  • 假设cnt_i为2'b00,表示是乘累加指令的第一个运行周期。此时将乘法结果mulres通过接口hilo_temp_o输出到EX/MEM模块,以便在下一个时钟周期使用。

    同一时候,设置变量stallreq_for_madd_msub为Stop,表示乘累加指令请求流水线暂停。

  • 假设cnt_i为2'b01。表示是乘累加指令的第二个运行周期。此时EX模块的输入hilo_temp_i就是上一个时钟周期得到的乘法结果。所以将hilo_temp_i与HI、LO寄存器的值相加。得到终于运算结果,保存到变量hilo_temp1中。同一时候。设置变量stallreq_for_madd_msub为NoStop。表示乘累加指令运行结束,不再请求流水线暂停。最后,设置cnt_o为2'b10,而不是直接设置为2'b00,目的是:假设因其他原因导致流水线保持暂停,那么因为cnt_o为2'b10,所以EX阶段不再计算,从而防止乘累加指令反复运行。

(3)第三段:给出信号stallreq的值,眼下仅仅有乘累加、乘累减指令会导致流水线暂停,所以stallreq就直接等于变量stallreq_for_madd_msub的值。

(4)第四段:因为乘累加、乘累减指令要将终于结果写入HI、LO寄存器。所以在第四段给出了对HI、LO寄存器的写信息。

7.8.3 改动EX/MEM模块

參考图7-12可知,EX/MEM模块要添加4个接口,含义如表7-3所看到的。

EX/MEM模块的代码主要改动例如以下。完整代码位于本书附带光盘Code\Chapter7_2文件夹下的ex_mem.v文件。

  1. module ex_mem(
  2.  
  3. ......
  4.  
  5. // 来自控制模块的信息
  6. input wire[5:0] stall,
  7.  
  8. ......
  9.  
  10. // 添加的输入接口
  11. input wire[`DoubleRegBus] hilo_i,
  12. input wire[1:0] cnt_i,
  13.  
  14. ......
  15.  
  16. // 添加的输出接口
  17. output reg[`DoubleRegBus] hilo_o,
  18. output reg[1:0] cnt_o
  19.  
  20. );
  21.  
  22. // 在流水线运行阶段暂停的时候,将输入信号hilo_i通过输出接口hilo_o送出、
  23. // 输入信号cnt_i通过输出接口cnt_o送出。其余时刻,hilo_o为0。cnt_o
  24. // 也为0。
  25.  
  26. always @ (posedge clk) begin
  27. if(rst == `RstEnable) begin
  28. ......
  29. hilo_o <= {`ZeroWord, `ZeroWord};
  30. cnt_o <= 2'b00;
  31. end else if(stall[3] == `Stop && stall[4] == `NoStop) begin
  32. ......
  33. hilo_o <= hilo_i;
  34. cnt_o <= cnt_i;
  35. end else if(stall[3] == `NoStop) begin
  36. ......
  37. hilo_o <= {`ZeroWord, `ZeroWord};
  38. cnt_o <= 2'b00;
  39. end else begin
  40. hilo_o <= hilo_i;
  41. cnt_o <= cnt_i;
  42. end
  43. end
  44.  
  45. endmodule

7.8.4 改动OpenMIPS模块

由于上面为EX、EX/MEM模块加入了接口。所以须要改动OpenMIPS模块,以将这些接口连接起来。连接关系如图7-12所看到的,详细代码不在书中列出。读者能够參考本书附带光盘Code\Chapter7_2文件夹下的openmips.v文件。

代码下载地址http://download.csdn.net/detail/leishangwen/7858701

自己动手写CPU之第七阶段(7)——乘累加指令的实现的更多相关文章

  1. 自己动手写CPU之第四阶段(3)——MIPS编译环境的建立

    将陆续上传本人写的新书<自己动手写CPU>(尚未出版).今天是第13篇.我尽量每周四篇 4.4 MIPS编译环境的建立 OpenMIPS处理器在设计的时候就计划与MIPS32指令集架构兼容 ...

  2. 自己动手写CPU之第九阶段(8)——MIPS32中的LL、SC指令说明

    将陆续上传新书<自己动手写CPU>,今天是第47篇. 9.7 ll.sc指令实现思路 9.7.1 实现思路 这2条指令都涉及到訪问链接状态位LLbit,能够将LLbit当做寄存器处理,ll ...

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

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

  4. 自己动手写CPU之第五阶段(3)——MIPS指令集中的逻辑、移位与空指令

    将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第17篇.我尽量每周四篇 5.4 逻辑.移位操作与空指令说明 MIPS32指令集架构中定义的逻辑操作指令有8条:and.and ...

  5. 自己动手写CPU之第六阶段(2)——移动操作指令实现思路

    将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第21篇,我尽量每周四篇 6.2 移动操作指令实现思路 6.2.1 实现思路 这6条移动操作指令能够分为两类:一类是不涉及特殊 ...

  6. 自己动手写CPU之第八阶段(4)——转移指令实现过程2

    将陆续上传本人写的新书<自己动手写CPU>,今天是第36篇,我尽量每周四篇 开展晒书评送书活动,在亚马逊.京东.当当三大图书站点上,发表<自己动手写CPU>书评的前十名读者,均 ...

  7. 自己动手写CPU 笔记

    自己动手写CPU 跳转至: 导航. 搜索 文件夹 1 处理器与MIPS 2 可编程逻辑器件与Verilog HDL 3 教学版OpenMIPS处理器蓝图 4 第一条指令ori 5 逻辑.移位与nop ...

  8. 《自己动手写CPU》写书评获赠书活动结果

    <自己动手写CPU>写书评获赠图书的读者有: 京东:8***2.16号哨兵.magicyu.kk6803.jddickyd.杰出的胡兵 亚马逊:徐贺.马先童.jaychen.farmfar ...

  9. 自己动手写CPU之第九阶段(2)——载入存储指令说明2(lwl、lwr)

    将陆续上传新书<自己动手写CPU>.今天是第38篇,我尽量每周四篇,可是近期已经非常久没有实现这个目标了.一直都有事,不好意思哈. 开展晒书评送书活动,在q=%E4%BA%9A%E9%A9 ...

随机推荐

  1. USB (Universal Serial Bus)

    USB歷史簡介 USB規格演變 標準 USB 2.0 介面 實體層 訊號傳輸 傳輸速率 網路層 USB 通訊模型 Endpoints 傳輸型態 USB 資料連結 Transaction Frame P ...

  2. ruby Mixin用法

    module My NA="China" attr:name attr:age def set_name(name) @name=name end def get_name ret ...

  3. JNI编程(二) —— 让C++和Java相互调用(1)

    自己在外面偷偷的算了下,又有将近两个月没更新过blog了.趁着今天有兴致,来更新JNI编程的第二篇文章.在第一篇里,大概介绍了JNI的特点.用途和优劣.并且做一个最简单的JNI的例子,不过说实话那个例 ...

  4. Java之sleep和wait的区别

    这个问题在面试线程方面的知识时,基本上属于必问的问题.因此这里有必要做一个较为详细的总结. 区别一 首先需要明白的是这两个方法根本来自不同的类,sleep来自Thread,wait来自Object类. ...

  5. OpenSSH ‘mm_newkeys_from_blob’函数权限许可和访问控制漏洞

    漏洞名称: OpenSSH ‘mm_newkeys_from_blob’函数权限许可和访问控制漏洞 CNNVD编号: CNNVD-201311-117 发布时间: 2013-11-12 更新时间: 2 ...

  6. Ubuntu Builder —— 一个制作自己的发行版的工具

    Ubuntu Builder 是一个使用起来很简单的用来构建基于 Ubunut 的自己的发行版的工具. 你可以下载最新的 Ubuntu Builder 的 DEB 安装包.下载和安装请前往:http: ...

  7. ☀【CSS3】box-sizing

    <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8& ...

  8. 【转】【iOS知识学习】_视图控制对象生命周期-init、viewDidLoad、viewWillAppear、viewDidAppear、viewWillDisappear等的区别及用途

    原文网址:http://blog.csdn.net/weasleyqi/article/details/8090373 iOS视图控制对象生命周期-init.viewDidLoad.viewWillA ...

  9. 【转】Ubuntu更改语言环境设置

    原文网址:http://studiogang.blog.51cto.com/505887/385199 上午装了下Ubuntu 10.4,默认安装时选择的语言是english的,结果由于英语水平太次, ...

  10. SharePoint 2010在win7 x64 安装

    转:http://kaneboy.blog.51cto.com/1308893/328000 关于<SharePoint 2010应用程序开发指南>,我和杜伟同学正在撰写中,希望下半年早点 ...