#计组实验#单周期CPU设计

本文介绍了单周期CPU的设计过程,包括InstructionMemory、ALU32、ControlUnit、DataMemory等模块,阐述了CPU的工作原理和操作步骤。通过实验,作者掌握了单周期CPU的实现方法和测试技巧,强调了通过波形图分析错误信号来定位问题的重要性。

一.实验目的

1.掌握单周期CPU数据通路图的构成、原理及其设计方法
2.掌握单周期CPU的实现方法,代码实现方法
3.认识和掌握指令与CPU的关系
4.掌握测试单周期CPU的方法

二.实验内容

设计一个单周期CPU,该CPU至少能实现以下指令功能操作。需设计的指令与格式如下:
这里写图片描述

这里写图片描述

三.实验原理

单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用一个时钟周期完成。电平从低到高变化的瞬间称为时钟上升沿,两个相邻时钟上升沿之间的时间间隔称为一个时钟周期。时钟周期一般也称振荡周期。
CPU在处理指令时,一般需要经过以下几个步骤:

(1) 取指令(IF):根据程序计数器PC中的指令地址,从存储器中取出一条指令,同时,PC根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令时,则控制器把“转移地址”送入PC,当然得到的“地址”需要做些变换才送入PC。
(2) 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。
(3) 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。
(4) 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。
(5) 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。
单周期CPU,是在一个时钟周期内完成这五个阶段的处理。

这里写图片描述
这里写图片描述
这里写图片描述
图2是一个简单的基本上能够在单周期CPU上完成所要求设计的指令功能的数据通路和必要的控制线路图。其中指令和数据各存储在不同存储器中,即有指令存储器和数据存储器。访问存储器时,先给出地址,然后由读或写信号控制操作。对于寄存器组,读操作时,先给出地址,输出端就直接输出相应数据;而在写操作时,在 WE使能信号为1时,在时钟边沿触发写入。图中控制信号作用如表1所示,表2是ALU运算功能表。
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

1.InstructionMemory

该模块实现了根据RW信号读Iaddr地址的指令的功能,写指令功能因为本实验没有用到,注释掉了。在初始化的时候,本模块先用系统调用$readmemb装载指令。

module InstructionMemory(
    input [31:0] Iaddr, // 指令存储器地址输入端口
    // input [31:0] IDataIn,  // 指令存储器数据输入端口(指令代码输入端口)
    input RW,      // 指令存储器读写控制信号,为1写,为0读
    output reg[31:0] IDataOut // 指令存储器数据输出端口(指令代码输出端口)
    );

    reg[7:0] storage [127:0];

     always @(RW or Iaddr ) begin
           if(RW == 1) begin //write
            /* 本次实验不需要用到写指令功能
               storage[Iaddr] <= IDataIn[7:0];
               storage[Iaddr + 1] <= IDataIn[15:8];
               storage[Iaddr + 2] <= IDataIn[23:16];
               storage[Iaddr + 3] <= IDataIn[31:24];
            */
               end
           else begin // read
               IDataOut[7:0] <= storage[Iaddr + 3];
               IDataOut[15:8] <= storage[Iaddr + 2];
               IDataOut[23:16] <= storage[Iaddr + 1];
               IDataOut[31:24] <= storage[Iaddr];
                end  
       end

       initial begin
            $readmemb("F:/ECOP_Experiment/CPU/CPU.srcs/sources_1/new/ins.txt",storage);
       end


endmodule

2.ALU32

ALU实现不复杂,根据表2,对每个opCode实现对应的运算,输出便可。

module ALU32(
    input [3:0] ALUopcode,
    input [31:0] rega,
    input [31:0] regb,
    output reg [31:0] result,
    output zero
    );

   assign zero = (result==0)?1:0;
   always @( ALUopcode or rega or regb ) begin
      case (ALUopcode)
         4'b0000 : result = rega + regb;
         4'b0001 : result = rega - regb;
         4'b0010 : result = (rega == regb)?1:0;
         4'b0011 : result = regb << rega;
         4'b0100 : result = rega & regb;
         4'b0101 : result = (!rega) & regb;
         4'b0110 : result = rega ^ regb; 
         4'b0111 : result = rega ^~ regb
         4'b1000 : result = rega | regb; 
         // 4'b1001 : result = (rega < regb)?1:0;
      default : begin
         result = 32'h00000000;
         $display (" no match");
        end
     endcase
   end
endmodule

3.CU

CU的设计较为复杂,实现的功能主要是根据输入的操作码opCode和ALU的zero信号来控制各个信号的输出。根据表1和表2,我们可以得出表3的ALU输出信号真值表。根据该表,实现每种输入对应的使出便可,如图6、图7、图8所示。
这里写图片描述

module CU(
    // input
    input [5:
### 计算机组成原理实验二:单周期CPU设计与实现 #### 单周期CPU的核心设计理念 单周期CPU设计目标是将理论知识转化为实践能力,通过具体实现加深对指令执行流程的理解。在这一过程中,模块化思维至关重要,即将整个系统划分为多个独立的功能单元,分别完成特定的任务后再集成在一起[^1]。 #### 指令执行与时序控制 为了确保系统的稳定性和可靠性,在单周期CPU中通常采用双沿触发机制来管理数据流动和状态更新。具体而言,寄存器和存储器中的数据会在时钟下降沿被保存,而程序计数器(PC)的状态则在时钟上升沿发生改变[^2]。这种分离式的时序安排有助于减少竞争条件的发生概率,从而提升整体性能表现。 #### 寄存器文件的实现方式 `registerMemory`组件负责处理寄存器的相关操作,其主要职责包括依据来自控制单元的选择信号决定输入端口接收何种类型的数据;此外还需支持初始配置阶段的操作需求——即设置默认值或者加载预定义参数至各个寄存器当中去。最后一点值得注意的是,考虑到效率因素以及避免重复写入可能引发的问题,该部件仅允许于单一指定时刻(此处特指时钟信号处于低电平期间)向内部存储位置实施有效赋值动作[^3]。 #### 并行进位链(ALU核心部分) 作为运算逻辑的关键构成要素之一,并行进位加法器采用了递归构建策略形成最终规模达到32比特宽度级别的版本(cla_32),它由更基础的小型构件逐级拼接而成,比如最原始形式下的半加器(add),进而发展成为具备完整功能属性的大尺寸实体对象。这种方法不仅能够显著提高计算速度而且还能保良好的可维护特性由于各层次间遵循统一架构模式使得后续扩展变得相对容易许多[^4]。 ```python def cla_recursive(width, a, b, cin): if width == 1: sum_bit = a ^ b ^ cin cout = (a & b) | ((a ^ b) & cin) return sum_bit, cout half_width = width // 2 lower_sum, lower_cout = cla_recursive(half_width, a[:half_width], b[:half_width], cin) upper_sum, _ = cla_recursive(half_width, a[half_width:], b[half_width:], lower_cout) return lower_sum + upper_sum, _ ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值