• cpu设计和实现(协处理器cp0)


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

            除了通用计算器负责控制和计算之外,cpu如果需要正常有序地运行,还需要一定地协处理器来帮助完成对应地工作。在mips下面,这样地协处理器称之为cp0。协处理器的工作一般包括这几个方面,

            1)处理中断和异常;

            2)处理mmu和tlb;

            3)处理cache;

            4)处理其他cpu的相关属性。

            之前我们在谈到乘法和除法的时候,涉及到hi和lo这两个寄存器。其实,cp0的处理方法和他们是差不多的。如果是mf读操作,那么这个动作是在exe阶段完成的;如果是写操作,那么这个动作也是在wb阶段完成的。

    1、准备cp0_reg.v

    1. `include "defines.v"
    2. module cp0_reg(
    3. input wire clk,
    4. input wire rst,
    5. input wire we_i,
    6. input wire[4:0] waddr_i,
    7. input wire[4:0] raddr_i,
    8. input wire[`RegBus] data_i,
    9. // input wire[31:0] excepttype_i,
    10. input wire[5:0] int_i,
    11. // input wire[`RegBus] current_inst_addr_i,
    12. // input wire is_in_delayslot_i,
    13. output reg[`RegBus] data_o,
    14. output reg[`RegBus] count_o,
    15. output reg[`RegBus] compare_o,
    16. output reg[`RegBus] status_o,
    17. output reg[`RegBus] cause_o,
    18. output reg[`RegBus] epc_o,
    19. output reg[`RegBus] config_o,
    20. output reg[`RegBus] prid_o,
    21. output reg timer_int_o
    22. );
    23. always @ (posedge clk) begin
    24. if(rst == `RstEnable) begin
    25. count_o <= `ZeroWord;
    26. compare_o <= `ZeroWord;
    27. //status寄存器的CU为0001,表示协处理器CP0存在
    28. status_o <= 32'b00010000000000000000000000000000;
    29. cause_o <= `ZeroWord;
    30. epc_o <= `ZeroWord;
    31. //config寄存器的BE为1,表示Big-Endian;MT为00,表示没有MMU
    32. config_o <= 32'b00000000000000001000000000000000;
    33. //制作者是L,对应的是0x48,类型是0x1,基本类型,版本号是1.0
    34. prid_o <= 32'b00000000010011000000000100000010;
    35. timer_int_o <= `InterruptNotAssert;
    36. end else begin
    37. count_o <= count_o + 1 ;
    38. cause_o[15:10] <= int_i;
    39. if(compare_o != `ZeroWord && count_o == compare_o) begin
    40. timer_int_o <= `InterruptAssert;
    41. end
    42. if(we_i == `WriteEnable) begin
    43. case (waddr_i)
    44. `CP0_REG_COUNT: begin
    45. count_o <= data_i;
    46. end
    47. `CP0_REG_COMPARE: begin
    48. compare_o <= data_i;
    49. //count_o <= `ZeroWord;
    50. timer_int_o <= `InterruptNotAssert;
    51. end
    52. `CP0_REG_STATUS: begin
    53. status_o <= data_i;
    54. end
    55. `CP0_REG_EPC: begin
    56. epc_o <= data_i;
    57. end
    58. `CP0_REG_CAUSE: begin
    59. //cause寄存器只有IP[1:0]、IV、WP字段是可写的
    60. cause_o[9:8] <= data_i[9:8];
    61. cause_o[23] <= data_i[23];
    62. cause_o[22] <= data_i[22];
    63. end
    64. endcase //case addr_i
    65. end
    66. end //if
    67. end //always
    68. always @ (*) begin
    69. if(rst == `RstEnable) begin
    70. data_o <= `ZeroWord;
    71. end else begin
    72. case (raddr_i)
    73. `CP0_REG_COUNT: begin
    74. data_o <= count_o ;
    75. end
    76. `CP0_REG_COMPARE: begin
    77. data_o <= compare_o ;
    78. end
    79. `CP0_REG_STATUS: begin
    80. data_o <= status_o ;
    81. end
    82. `CP0_REG_CAUSE: begin
    83. data_o <= cause_o ;
    84. end
    85. `CP0_REG_EPC: begin
    86. data_o <= epc_o ;
    87. end
    88. `CP0_REG_PrId: begin
    89. data_o <= prid_o ;
    90. end
    91. `CP0_REG_CONFIG: begin
    92. data_o <= config_o ;
    93. end
    94. default: begin
    95. end
    96. endcase //case addr_i
    97. end //if
    98. end //always
    99. endmodule

    2、id添加译码

    1. if(inst_i[31:21] == 11'b01000000000 &&
    2. inst_i[10:0] == 11'b00000000000) begin
    3. aluop_o <= `EXE_MFC0_OP;
    4. alusel_o <= `EXE_RES_MOVE;
    5. wd_o <= inst_i[20:16];
    6. wreg_o <= `WriteEnable;
    7. instvalid <= `InstValid;
    8. reg1_read_o <= 1'b0;
    9. reg2_read_o <= 1'b0;
    10. end else if(inst_i[31:21] == 11'b01000000100 &&
    11. inst_i[10:0] == 11'b00000000000) begin
    12. aluop_o <= `EXE_MTC0_OP;
    13. alusel_o <= `EXE_RES_NOP;
    14. wreg_o <= `WriteDisable;
    15. instvalid <= `InstValid;
    16. reg1_read_o <= 1'b1;
    17. reg1_addr_o <= inst_i[20:16];
    18. reg2_read_o <= 1'b0;
    19. end

    3、ex阶段

    1)增加数据读取操作

    1. `EXE_MFC0_OP: begin
    2. cp0_reg_read_addr_o <= inst_i[15:11];
    3. moveres <= cp0_reg_data_i;
    4. if( mem_cp0_reg_we == `WriteEnable &&
    5. mem_cp0_reg_write_addr == inst_i[15:11] ) begin
    6. moveres <= mem_cp0_reg_data;
    7. end else if( wb_cp0_reg_we == `WriteEnable &&
    8. wb_cp0_reg_write_addr == inst_i[15:11] ) begin
    9. moveres <= wb_cp0_reg_data;
    10. end
    11. end

            看到这里,大家应该对这个代码不陌生了。之前谈到过,所有的寄存器都是在wb之后,才会真正写到寄存器里面的。但是,mfc0的动作是在exe阶段进行的,那么这个时候势必会出现数据读取错误的情况的。所以,解决这个问题最好的办法就是数据预取。id中寄存器预取、ex阶段hi&lo以及mfc预取、mem阶段llbit预取,本质上都是一回事。

    2)增加写操作

    1. always @ (*) begin
    2. if(rst == `RstEnable) begin
    3. cp0_reg_write_addr_o <= 5'b00000;
    4. cp0_reg_we_o <= `WriteDisable;
    5. cp0_reg_data_o <= `ZeroWord;
    6. end else if(aluop_i == `EXE_MTC0_OP) begin
    7. cp0_reg_write_addr_o <= inst_i[15:11];
    8. cp0_reg_we_o <= `WriteEnable;
    9. cp0_reg_data_o <= reg1_i;
    10. end else begin
    11. cp0_reg_write_addr_o <= 5'b00000;
    12. cp0_reg_we_o <= `WriteDisable;
    13. cp0_reg_data_o <= `ZeroWord;
    14. end
    15. end

            cp0寄存器的写操作是和通用寄存器分开来的。所以这部分代码需要单独用逻辑快来表达。

    4、mem阶段

            mem阶段对cp0没有什么影响,主要工作就是把之前ex阶段的数据透传下去即可。

    1. cp0_reg_we_o <= cp0_reg_we_i;
    2. cp0_reg_write_addr_o <= cp0_reg_write_addr_i;
    3. cp0_reg_data_o <= cp0_reg_data_i;

    5、准备汇编代码测试

    1. .org 0x0
    2. .set noat
    3. .set noreorder
    4. .set nomacro
    5. .global _start
    6. _start:
    7. ori $1,$0,0xf
    8. mtc0 $1,$11,0x0 #写compare寄存器,开始计时
    9. lui $1,0x1000
    10. ori $1,$1,0x401
    11. mtc0 $1,$12,0x0 #将0x401写如status寄存器
    12. mfc0 $2,$12,0x0 #读status寄存器,$2=0x401
    13. _loop:
    14. j _loop
    15. nop

    6、将对应的汇编代码翻译成二进制文件

    1. 3401000f
    2. 40815800
    3. 3c011000
    4. 34210401
    5. 40816000
    6. 40026000
    7. 08000006
    8. 00000000

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

             除了通用的pc、inst这些寄存器、wire之外,还可以把cp0_reg0里面的寄存器拉出来看看。重点看看we_i什么时候为高、写入的waddr_i对不对、和之前给出来的汇编代码能不能对的上。最后就是死循环了,因为mips延迟槽的原因,循环肯定是两个pc地址交替进行的。

  • 相关阅读:
    C语言--结构体(内容超级详细)
    Linux安装Tomcat最新版
    Python源码剖析2-字符串对象PyStringObject
    【Spring篇】IOC/DI配置管理第三方bean
    云原生微服务架构及实现技术
    从零开始的Hadoop学习(四)| SSH无密登录配置、集群配置
    基于Win11、CentOS7、VMWare15pro搭建Hadoop2.7.7
    Python:函数进阶
    python中实现定时任务的几种方案
    shell_40.Linux特殊参数变量
  • 原文地址:https://blog.csdn.net/feixiaoxing/article/details/128108394