【计算机系统设计】实践笔记(2)数据通路构建:第一类R型指令分析(1)

本文详细分析了MIPS R类指令中的算数和逻辑运算指令,包括add、sub等,阐述了指令格式、功能、器件需求以及控制器和ALU的设计。在译码阶段,需要寄存器堆和控制器,执行阶段则需要ALU。控制器根据op和func字段产生控制信号,ALU执行相应的运算操作。通过实例展示了如何设计控制信号和ALU操作,并提供了相关的Verilog代码实现。
摘要由CSDN通过智能技术生成

0 回顾

上一次实践笔记(0)我们实现了一个最简单的,能够每个上升沿+4的PC。

我们最需要关注的就是器件功能的独立性,避免内外功能混杂,同时一定要注意脑中有电路(RTL级描述的抽象电路而不是实际的门级电路,内个交给EDA工具就行)。

1 针对的指令

现在,我们分析一下R类指令,我们需要构建数据通路,而很多的R类指令是相似的,因此可以直接分析一类指令,就像所有指令都需要取指,我们就分析所有指令一样。

那么,我们要分析哪一类指令?这取决于我们准备实现的指令有什么,我们先看看。

add addu sub subu and or xor nor slt sltu sll srl sra sllv srlv srav jr

哦是的,我们需要实现以上R类指令,那么我们来分分类吧:

  1. 指令格式op rs rt rd 00000 func
    1. 算数运算指令:add addu sub subu and or xor nor
    2. 置位指令:slt sltu
    3. 移位指令:sllv srlv srav
  2. 指令格式op 00000 rt rd shamt func:移位指令:sll srl sra
  3. 指令格式op rs 00000 00000 00000 func:跳转指令:jr

我们需要注意的是,我们是按照指令格式的形式进行分类的,而不是功能,因为同样指令格式的指令,(各阶段的)执行过程是类似的,可以有类似的器件和信号。


下面,我们来分析一下第一类指令格式:

指令格式op rs rt rd 00000 func的分析如下:


2 功能分析

我们一直强调的是类似,而不是相同,因此我们先完成通用器件的分析,再针对个别特殊情况处理。

2.1 指令格式

指令oprsrtrdshamtfunce.g.explainoperation
add000000rsrtrd00000100000add $1,$2,$3$1=$2+S3(rd)←(rs)+(rt); rs=$2,rt=$3,rd=$1

我们就先看这一条add指令,它具备通用的特征,首先声明,我们不实现add指令的异常处理机制

我们依次分析一下各个字段

2.2 指令识别

CPU设计最重要的点,就是需要识别出当前执行的指令,对于该格式来说,需要识别是编码是 opfunc

  1. op = 6'b000_000,几乎所有R型指令都是这样,在我们要实现的R型指令中,全部都是op = 6'b000_000
  2. 对于func,是R型指令识别的关键,不同的R型指令(op = 000000),func字段一定不一样,每个func编号,对应了一个指令,对应了一种操作。

2.3 指令操作

这一类指令,shamt = 00000都是一样的,并且并不使用该字段,可以直接忽略。

而对于rs rt rd字段,这类指令都执行rd = rs 操作 rt

我们使用一种通用的表述方式:op $reg1,$reg2,$reg3,那么对应的是

  1. 字段:rs = reg2rt = reg3rd = reg1
  2. 操作:rd = rs op rt

我们目前分析的这一类,都是这样的!

它的完整功能(RTL)描述是:

  1. 取指:M[PC]; PC <- PC + 4
  2. 执行:R[rd] <- R[rs] op R[rd]

3 需要的器件

之前我们知道了指令的识别操作,现在,我们看看需要什么器件来完成操作

首先,毫无疑问是从ROM中取指(IF阶段),这一点,我们上一篇完成了PC,但是还没有设计ROM,后面再说。

现在,假设我们取到了指令,接下来该如何处理呢?

3.1 ID(译码)阶段

我们看看这个操作:R[rd] <- R[rs] op R[rd]

毫无疑问,我们需要寄存器,是的,就是MIPS的32个内部寄存器,然后我们需要控制器,因为寄存器需要控制的,至少写入操作需要控制,不是任何时候每条指令都能够写入寄存器的。

现在,我们知道我们需要2个器件了

  1. 控制器
  2. 寄存器

问题:为什么这两个器件在译码阶段?

  1. 经典的流水线就是这么设计的(好吧这不是什么分析…)
  2. 你可以想想,我们刚刚拿到了一串二进制信息,总得知道它是什么吧,控制器就是解析编码用的,根据opfunc的信息,来确定这是什么指令,需要什么控制信号。
  3. 我们又知道,译码是需要时间的,不过组合逻辑执行会非常快。我们知道硬件是并行工作的,控制器执行的同时,也可以干点别的事情,我们知道MIPS大部分指令都需要寄存器组的内容,所以,我们就用指令的其他字段完成**寄存器访问(读)**的工作。
  4. 另外,我们再探析一下,什么叫做译码,译码就是解析指令,它完成了2项内容
    1. 控制器解析opfunc字段,获取控制信号
    2. 寄存器使用rsrtrd字段,完成数据的访问,当然,第一阶段实际上只是读取数据,没有使用rd
    3. shamt字段并没有使用,我们当前分析的指令不需要管它,至于后面的,以后再说。
    4. 剧透(可以先不看):后面我们会看见,指令的解析不仅仅在译码阶段,也可以在其他阶段,shamt可以在EX执行阶段被使用,而func也可以在该阶段被解析,这完全取决于设计者的意志,我们最终能够达到正确执行指令的目标就好了。

3.1.1 寄存器堆(Register Files)的设计

在这里插入图片描述
我们看看这个寄存器堆的信号

  1. 指令字段相关:rs rt rd
  2. 通用功能:clk时钟上升沿\下降沿(取决于设计)触发写入操作,rst高电平复位
  3. 数据:R[rd]是待写入的数据,是计算结果,R[rs] R[rd]是根据指令字段读取的数据
  4. 控制相关:RegWrite代表是否允许写入数据,是来自控制器的控制信号,高电平有效

图中有一个错误:右边输出的下面的R[rd]应该是R[rt]

3.1.2 控制器的设计

我们的输入信号是opfunc字段,输出是什么?是控制信号。需要什么控制信号?我们之后再说。

我们能够得到输入输出,一个纯组合逻辑的电路不难设计,是的,控制器的硬件实现非常简单,困难的是如何设计控制信号,我们后面一步步展开。
在这里插入图片描述我们先建立一个空架子。

3.2 EX(执行)阶段

再这个操作:R[rd] <- R[rs] op R[rd],我们需要执行操作是op,那么

  1. 这个操作是什么?
  2. 如何识别这个操作?
  3. 识别操作后,如何执行?

我们依次解答一下。

首先,这个操作是什么,取决于opfunc字段,我们需要使用控制器识别操作,并且输出相应的控制信号。

给出了相应的控制信号,我们就知道要执行什么操作(加法,减法,还是乘法……),然后我们就需要ALU运算器完成运算。

因此,我们需要的新器件是ALU运算器

在这里插入图片描述这就是我们的ALU了

  1. 两个输入的数据来自于刚才从寄存器堆读取的值
  2. ALU的操作op来自于控制器输出的**运算操作ALUop**信号
  3. 输出的值R[rd]会被写回到寄存器堆中去

在这里插入图片描述现在,我们的数据通路是这样了,其中红色字是指令的字段。

4 控制信号

4.1 控制器设计

  • 输入信号:op func
  • 输出信号:RegWrite ALUop
instructionopfuncALUopRegWrite
add00000010000000001
addu00000010000100011
sub00000010001000101
subu00000010001100111
and00000010010001001
or00000010010101011
xor00000010011001101
nor00000010011101111
slt00000010101010001
sltu00000010101110011
sllv00000010101010101
srlv00000000011010111
srav00000000011111001

其中,RegWrite高电平有效,代表能够写入到寄存器,再配合时钟触发(暂定上升沿触发)即可向寄存器堆写入数据。

现在有13种操作,后面还有一些操作,因此暂时设置ALUop为4位信号(最多识别16种操作),后续如果需要再更改。

4.2 ALU设计

  • 数据输入:R[rs] R[rd]
  • 控制输入:ALUop
  • 数据输出:R[rd]
ALUop操作
  • 操作:对应4.1中的instruction
  • ALUop:对应4.1中的ALUop

这里就不写了。

5 数据线

输入的指令就是最重要的数据。

6 地址线

7 控制线

clk时钟信号和rst复位信号。

8 实现

8.1 控制器

control_1.v

`timescale 1ns / 1ps

module control_1(
           input [5:0] op,
           input [5:0] func,

           output reg RegWrite,
           output reg [3:0] ALUop
       );

always @(*)
begin
    if(op == 0)
    begin
        case (func)
            6'b100000:  // add
            begin
                RegWrite <= 1;
                ALUop <= 4'b0000;
            end
            6'b100001:  // addu
            begin
                RegWrite <= 1;
                ALUop <= 4'b0001;
            end
            6'b100010:  // sub
            begin
                RegWrite <= 1;
                ALUop <= 4'b0010;
            end
            6'b100011:  // subu
            begin
                RegWrite <= 1;
                ALUop <= 4'b0011;
            end
            6'b100100:  // and
            begin
                RegWrite <= 1;
                ALUop <= 4'b0100;
            end
            6'b100101:  // or
            begin
                RegWrite <= 1;
                ALUop <= 4'b0101;
            end
            6'b100110:  // xor
            begin
                RegWrite <= 1;
                ALUop <= 4'b0110;
            end
            6'b100111:  // nor
            begin
                RegWrite <= 1;
                ALUop <= 4'b0111;
            end
            6'b101010:  // slt
            begin
                RegWrite <= 1;
                ALUop <= 4'b1000;
            end
            6'b101011:  // sltu
            begin
                RegWrite <= 1;
                ALUop <= 4'b1001;
            end
            6'b000100:  // sllv
            begin
                RegWrite <= 1;
                ALUop <= 4'b1010;
            end
            6'b000110:  // srlv
            begin
                RegWrite <= 1;
                ALUop <= 4'b1011;
            end
            6'b000111:  // srav
            begin
                RegWrite <= 1;
                ALUop <= 4'b1100;
            end
            default:
            begin
                RegWrite <= 0;
                ALUop <= 4'b1111;
            end
        endcase
    end
    else
    begin
        RegWrite <= 0;
        ALUop <= 4'b1111;
    end
end

endmodule

注意默认情况下的值。

RTL优化
在这里插入图片描述

功能仿真测试

tb_control_1.v

`timescale 1ns / 1ps


module tb_control_1(

       );
// control_1 Parameters
parameter PERIOD  = 10;

// control_1 Inputs
reg   [5:0]  op                            = 0 ;
reg   [5:0]  func                          = 0 ;

// control_1 Outputs
wire  RegWrite                             ;
wire  [3:0]  ALUop                         ;


initial
begin
    op = 1 ;
    func = 0 ;
    #10
     op = 0 ;
    func = 6'b100000;
    #10
     op = 0 ;
    func = 6'b100001;
    #10
     op = 0 ;
    func = 6'b100010;
    #10
     op = 0 ;
    func = 6'b000111;
    #10
     op = 0 ;
    func = 6'b111111;
end

control_1  u_control_1 (
               .op                      ( op        [5:0] ),
               .func                    ( func      [5:0] ),

               .RegWrite                ( RegWrite        ),
               .ALUop                   ( ALUop     [3:0] )
           );

endmodule

是的,控制器非常简单,2个输入,2个输出,真值表都有了,非常容易不是吗?


8.2以及之后的内容,在下一篇文章。

【计算机系统设计】实践笔记(2)数据通路构建:第一类R型指令分析(2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XV_

感谢您的认可,我会继续努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值