名称:五级流水线CPU设计Verilog代码VIVADO仿真(文末获取)
软件:VIVADO
语言:Verilog
代码功能:
五级流水线CPU设计
- 基于FPGA的简单的五级流水线CPU,支持基本的运算,跳转,数据存储与传送等基本指令,解决流水线cpu中的控制相关和数据相关的问题。
- 根据五级流水的五个阶段(取指,译码,执行,执行,访存,写回),将整个cpu分为五个子模块分别编码实现,然后封装在顶层模块里。
- 在五个子模块中又分别对alu(运算模块),cu(控制模块),bru(跳转处理模块)等更小的子模块进行封装。
- 由于流水执行,跳转指令在译码阶段被识别时,下一条指令已经取指开始执行,如不处理,会导致寄存器值被更改,即控制相关问题。采用在跳转指令后插入空指令的方法处理控制相关。
- 下一条指令读寄存器时,前三条指令还未进行写回,如不处理,所读取的内容将不是最新的,即数据相关问题。采用数据前推的方式解决。
- 由于LW指令改写寄存器的写入数据在访存阶段才能确定,因此单纯的数据回推无法解决相关问题,需要在指令后插入一条空指令。为了简化设计,对于所有LW指令,无论是否出现数据相关,均在其后插入空指令。
- 通过汇编工具将指令翻译成二进制数字,然后输入cpu进行仿真。
工程文件
设计原理及代码实现
总体结构
CPU根据五级流水分成五个模块,分别为取指、译码、执行、访存、写回,每条指令执行时,数据依次流经这些模块。五级流水线各阶段的主要工作如下:
●取指阶段:从指令存储器读出指令,同时确定下一条指令地址。
●译码阶段:对指令进行译码,从通用寄存器中读出要使用的寄存器的值,如果指令中含有立即数,则将立即数进行符号扩展或无符号扩展。如果是转移指令且满足转移条件,那么给出转移目标,作为新的指令地址。
●执行阶段:按照译码阶段给出的操作数、运算类型,进行运算,给出运算结果。如果是Load/Store指令,则另需计算出Load/Store的目标地址。
●访存阶段:如果是Load/Store指令,那么在此阶段会访问数据存储器,反之,只是将执行阶段的结果向下传递到回写阶段。同时,此阶段还要判断是否有异常需要处理,如果有,那么会清除流水线,然后转移到异常处理例程入口地址处继续执行。
●
回写阶段:将运算结果保存到目标寄存器。
图1 五级流水线CPU模块示意图
单周期CPU中,当指令位于某一个特定模块(如MES模块)时,其他模块为空闲状态,会造成浪费。因此引入流水线CPU,对某一个特定模块,当正在执行的某一条特定指令位于其他模块时,该模块转而执行其他指令,而不是闲置。
图2 五级流水线CPU实现简图
以下为顶层模块例化代码,目的是对上述五个阶段进行例化。
图3 五级流水线CPU总体结构代码实现
主要功能
跳转指令
所有的跳转操作都封装在跳转控制模块bru,下一条指令的地址根据控制单元cu生成的控制信号生成。
图4 下一条指令地址示意图
bru模块具体代码实现如下:
图5 bru模块代码实现
控制相关
由于流水执行,跳转指令在译码阶段被识别时,下一条指令已经取指开始执行,如不处理,会导致寄存器值被更改。这一问题被称为控制相关。
可以采用跳转指令后插入空指令的方法处理控制相关。
数据相关
下一条指令读寄存器时,前三条指令还进行写回,如不处理,所读取的内容将不是最新的,这一问题被称为数据相关。采用数据前推的方式解决。
图8 数据前推示意
特殊地,对于LW指令,由于LW指令改写寄存器的写入数据在mem阶段才能确定,因此单纯的数据回推无法解决相关问题,需要在指令后插入一条空指令。为了简化设计,对于所有LW指令,无论是否出现数据相关,均在其后插入空指令(图10)。由于下一条指令仍需执行,所以此时pc的值要保持不变。
(nextpc = pc if(is_lw))
图9 LW指令的数据相关解决示意 图10 LW指令后插入空指令
以下为IDS模块结构,目的是用于处理数据相关问题。
图11 IDS模块结构
仿真测试
本次课程设计的CPU仿真共实现31条指令,其中22条属于计算类型,4条属于数据存取与传送类型,5条跳转类型,为了简化对测试结果的分析,只对pc地址与寄存器数据(及存储器数据)进行验证。
(一)计算、访存、运算指令
每一个指令的执行共花费5个CPU周期,图示第5个周期结束后写回Lui指令的运算结果,置T1值为2^16=65536。其他指令以此类推。运算到LW指令时,插入一个空指令,同时保证PC值不变,以便于其后Beq等一系列指令的顺利执行。执行到Beq指令时,判断T1=T2=65536,跳转到label指令集xor、and、……
图12 计算、访存、运算指令及其仿真结果
(二)跳转指令
执行到Jr 指令时,其后插一条空指令nop,此时对应$t1 = bfc00000,即跳转到目标地址(程序的开始位置),开始循环执行。
图13 跳转指令仿真结果
(三)传送指令
执行到MOVN指令时,判定此时T2=65537!=0,则在5个周期后将T1赋值给T3。
图14 传送指令仿真结果
程序文件
Testbench
部分代码展示:
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/09/11 12:10:21 // Design Name: // Module Name: one_cycle_cpu // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // module pipe_cpu( input clk, input rst ); wire c_is_br; wire c_is_lw_id_ex; wire c_is_lw_ex_id; wire pc_temp; wire [ 1:0] c_alu_src; wire reg_we; wire c_reg_we_id_ex; wire c_reg_we_ex_me; wire c_reg_we_me_wb; wire c_mem_we_id_ex; wire c_mem_we_ex_me; wire c_reg_data_id_ex; wire c_reg_data_ex_me; wire c_reg_data_me_wb; wire c_reg_addr_id_ex; wire c_reg_addr_ex_me; wire c_reg_addr_me_wb; wire [31:0] mem_data; wire [31:0] target; wire [13:0] ca; wire [ 4:0] reg_addr_ex_b; wire [31:0] reg_data_ex_b; wire [ 4:0] reg_addr_me_b; wire [31:0] reg_data_me_b; wire [ 4:0] reg_addr_wb_b; wire [31:0] reg_data_wb_b; wire [31:0] inst_if_id; wire [31:0] inst_id_ex; wire [31:0] inst_ex_me; wire [31:0] inst_me_wb; wire [31:0] rs_value; wire [31:0] rt_value_id_ex; wire [31:0] rt_value_ex_me; wire [31:0] alu_result_ex_me; wire [31:0] alu_result_me_wb; wire [31:0] reg_waddr; wire [31:0] reg_wdata; wire [31:0] nextpc; reg [31:0] pc; reg reset; always @(posedge clk) reset <= ~rst; assign nextpc = target; always @(posedge clk) begin if (reset) begin pc <= 32'hbfc00000; end else pc <= nextpc; end ifs _ifs( .clk (clk), .rst (rst), .c_is_br (c_is_br), .c_is_lw (c_is_lw_id_ex), .pc (pc), .final_inst (inst_if_id) ); ids _ids( .clk (clk), .rst (rst), .c_is_lw_in (c_is_lw_ex_id), .inst_in (inst_if_id), .reg_we (reg_we), .reg_we_ex (c_reg_we_ex_me), .reg_we_me (c_reg_we_me_wb), .reg_waddr (reg_waddr), .reg_wdata (reg_wdata), .reg_addr_ex_b (reg_addr_ex_b), .reg_data_ex_b (reg_data_ex_b), .reg_addr_me_b (reg_addr_me_b), .reg_data_me_b (reg_data_me_b), .reg_addr_wb_b (reg_addr_wb_b), .reg_data_wb_b (reg_data_wb_b), .mem_data_b (mem_data), .pc
源代码
点击下方的公众号卡片获取