存储器和控制器

懵懂时期作品之计算机组成原理

Posted by Birdie on May 20, 2022

一、实验准备

本次实验开始涉及 MIPS 架构 CPU 的设计,其中涵盖 CPU 在流水线设计中所分割的两个阶段,以下为实验概述:MIPS 架构 CPU 的传统流程可分为取指、译码、执行、访存、回写 (Instruction Fetch, Decode, Execution, Memory Request, Write Back) 五阶段。实验一完成了执行阶段的ALU 部分,并进行了简单的访存实验,本实验将实现取指、译码两个阶段的功能。在进行本次实验前,需要具备以下实验环境及基础能力:

1.1 了解 Xilinx Block Memory Generator IP 的使用

1.2 了解数据通路、控制器的概念

二、实验目的

2.1 了解随机存取存储器 RAM 的原理。

2.2 掌握调用 Xilinx 库 IP(Block Memory Generator)实例化 RAM 的方法;

2.3 掌握单周期 CPU 各个控制信号的作用和生成过程。

2.4 掌握单周期 CPU 控制器的工作原理及其设计方法。

2.5 理解单周期 CPU 执行指令的过程。

2.6 掌握取指、译码阶段数据通路、控制器的执行过程。

三、实验设备

3.1 ThinkPad E575 (操作系统:windows10)

3.2 Basys3开发板

3.3 Xilinx Vivado 2015.4

四、实验任务

图 1 为本次实验所需完成内容的原理图,依据取指、译码阶段的需求,分别需要实现以下模块:

1606875856(1)

4.1 PC:D 触发器结构,用于储存 PC(一个周期)。需实现 2 个输入,分别为 clk, rst, 分别连接时钟和复位信号;需实现 2 个输出,分别为 pc, inst_ce, 分别连接指令存储器的 addra, ena 端口。其中 addra 位数依据 coe 文件中指令数定义;

4.2 加法器:用于计算下一条指令地址,需实现2个输入,1个输出,输入值分别为当前指令地址 PC、32’h4;

4.3 Controller:其中包含两部分:

(a). main_decoder。负责判断指令类型,并生成相应的控制信号。需实现 1 个输入,为指令inst 的高 6 位 op,输出分为 2 部分,控制信号有多个,可作为多个输出,也作为一个多位输出,具体参照 4.3 进行设计;aluop 传输至 alu_decoder,使 alu_decoder 配合 inst 低 6 位funct,进行 ALU 模块控制信号的译码。

(b). alu_decoder。负责 ALU 模块控制信号的译码。需实现 2 个输入,1 个输出,输入分别为funct, aluop;输出位 alucontrol 信号。

(c). 除上述两个组件,需设计 controller 模块顶层文件调用两个 decoder,对应实现 op,funct输入信号,并传入调用模块;对应实现控制信号及 alucontrol,并连接至调用模块相应端口。

4.4 指令存储器。使用 Block Memory Generator IP 构造。

4.5 时钟分频器。将板载 100Mhz 频率降低为 1hz,连接 PC、指令存储器时钟信号 clk。

五、实验原理

5.1 取指阶段原理

1606910503

如图2如图 2 所示,PC为32bit(1 word)的寄存器,其存放指令地址,每条指令执行完毕后增加 4,即为下一条指令存放地址。指令地址传入指令存储器,即可取出相应地址存放的指令。需要注意的是,MIPS 架构中,采用字节读写,32bit word = 4 byte,故需要地址+4 来获取下一条指令。

5.2 指令译码原理

MIPS有三种指令:

1606910723(1)

各个域分别是:

op: 指令操作

rs, rt, rd: 源,目的寄存器序号

shamt: 移位量

funct: 对op类操作,确定详细的操作类型

address / immediate: 立即数或者地址

target address: 目标地址

32位MIPS 指令在不同类型指令中分别有不同结构。但[31:16]表示的opcode,以及[5:0] 表示的funct,为译码阶段明确指令控制信号的主要字段。

下表为opcode以及funct识别得到的信号:

1606911004(1)

5.3 控制器实现原理

1606911146(1)

由图 4 可知,控制器输出的控制信号,用于控制器件的使能和多路选择器的选择,因此,根据不同指令的功能分析其所需要的路径,即可得到信号所对应的值。在此之前,参照下对各个控制信号的含义进行理解。

1606911253(1)

分析数据通路图,判断指令是否需要写寄存器、访存等等操作,以产生相应的控制信号。下面给出参考信号表:

1606911305(1)

表四里面没有定于pcsrc,这里pcsrc只用于决定beq的PC选择,因此可以定义为:pcsrc = branch & zero。

1606912348(1)

六、实验步骤

6.1 创建clk_div模块

由于需要通过LED灯的闪烁情况来确定指令的信号,则LED灯的变化情况需要被肉眼观察到。一个clk周期100MHz的时间是1ns,为此,需要将clk信号分频。此处,将时钟信号分频至1Hz左右,即延长2^28倍的时间,大概在2.68s左右,在人眼识别范围内。

6.2 创建PC模块

PC+4操作的目的是使指令指针转移到下一条指令,但在ROM中,1即代表了一个32bit。因此在具体实现中,PC+4操作更改为PC+1。

6.3 连接IP核中的ROM模块

使用 Block Memory,并导入coe文件,在coe文件中写入指令集。

6.4 创建controller模块

6.4.1 创建maincontroller模块

通过指令的[31:26]五位op信号,确定memtoreg, memwrite, pcsrc, alusrc, regdst, regwrite, jump, branch, aluop[1:0]信号;

6.4.2 创建alucontroller模块

通过指令的[5:0]六位funct信号以及maincontroller输出的两位aluop信号,确定alucontroller[2:0]。

6.5 创建display模块

由于数码管只能显示16位,因此该模块连接指令的低16位,并在七段数码管中显示。

6.6 创建top文件

top文件连接上述五个模块。

6.7 配置引脚文件

所需配置的引脚有:led,clk,rst,seg。

七、实验代码

7.1 top.v

`timescale 1ns / 1ps

module top(
    input clk,
    input rst,
    output [3:0] ans, //select for seg
    output [6:0] seg, //segment digital
    output memwrite,
    output memtoreg,
    output regwrite,
    output regdst,
    output branch,
    output jump,
    output alusrc,
    output [2:0] alucontrol
    );
    wire newclk;
    clk_div U2(.clk(clk),.reset(rst),.newclk(newclk));
    
    wire [31:0] pc;
    pcadd U3(.pcin(pc),.reset(rst),.pcout(pc),.clk(newclk));
    
    wire [31:0] inst;
    wire [7:0] low_pc;
    assign low_pc=pc[7:0];
    rom (
      .clka(clk),    // input wire clka
      .addra(low_pc),     // input wire [7 : 0] addra
      .douta(inst)  // output wire [31 : 0] douta
    );
    
    controller U4(
        .inst(inst),
        .memwrite(memwrite),
        .memtoreg(memtoreg),
        .regwrite(regwrite),
        .regdst(regdst),
        .branch(branch),
        .jump(jump),
        .alusrc(alusrc),
        .alucontrol(alucontrol)
     );
    
    wire [15:0] inst1;
    assign inst1=inst[15:0];
    display U1(.clk(clk),.reset(rst),.s(inst1),.ans(ans),.seg(seg));
endmodule

7.2 clk_div.v

`timescale 1ns / 1ps

module clk_div(
    input clk,
    input reset,
    output reg newclk
    );
    
    reg [27:0] count;
    always@(posedge clk,posedge reset)
        if(reset==1)  
            begin
                count <= 0;
                newclk <= 0;
            end
        else 
            begin
                if (count[27])
                    begin
                        count <= 0;
                        newclk <= ~newclk;
                    end
                else
                    begin
                        count <= count+1;
                    end
            end
endmodule

7.3 pcadd.v

`timescale 1ns / 1ps

module pcadd(
    input [31:0] pcin,
    input clk,
    output reg [31:0] pcout,
    input reset
    );
    always @(posedge clk)
        pcout <= pcin+1;
endmodule

7.4 controller.v

`timescale 1ns / 1ps

module controller(
    input [31:0] inst,
    output memwrite,
    output memtoreg,
    output regwrite,
    output regdst,
    output branch,
    output jump,
    output alusrc,
    output [2:0] alucontrol
    );
    
    wire [5:0] op;
    assign op = inst[31:26];
    wire pcsrc;
    wire [1:0] aluop;
    maincontroller D1(
        .op(op),
        .memtoreg(memtoreg),
        .memwrite(memwrite),
        .pcsrc(pcsrc),
        .alusrc(alusrc),
        .regdst(regdst),
        .regwrite(regwrite),
        .branch(branch),
        .jump(jump),
        .aluop(aluop)
    );
    
    wire [5:0] funct;
    assign funct = inst [5:0];
    ALUcontroller D2(
        .funct(funct),
        .aluop(aluop),
        .alucontrol(alucontrol)
    );
endmodule

7.5 maincontroller.v

`timescale 1ns / 1ps

module maincontroller(
    input [5:0] op,
    output wire memtoreg,
    output wire memwrite,
    output wire pcsrc,
    output wire alusrc,
    output wire regdst,
    output wire regwrite,
    output wire branch,
    output wire jump,
    output wire [1:0] aluop
    );
    
    reg [8:0] controls;
    assign {regwrite,regdst,alusrc,branch,memwrite,memtoreg,jump,aluop} = controls;
    always@ (op)
    begin
        case (op)
            6'b000000:controls <= 9'b110000010;
            6'b100011:controls <= 9'b101001000;
            6'b101011:controls <= 9'b0X101X000;
            6'b000100:controls <= 9'b0X010X001;
            6'b001000:controls <= 9'b101000000;
            6'b000010:controls <= 9'b0XXX0X1XX;
6'b001101:controls <= 9'b101000000;
        endcase
    end
    assign pcsrc = branch & 0;
endmodule

7.6 ALUcontroller.v

`timescale 1ns / 1ps

module ALUcontroller(
    input [5:0] funct,
    input [1:0] aluop,
    output reg [2:0] alucontrol
    );
    
    always@ (aluop,funct)
        begin
            case ({aluop,funct})
                8'b00XXXXXX:alucontrol <= 3'b010;
                8'b01XXXXXX:alucontrol <= 3'b110;
                8'b10100000:alucontrol <= 3'b010;
                8'b10100010:alucontrol <= 3'b110;
                8'b10100100:alucontrol <= 3'b000;
                8'b10100101:alucontrol <= 3'b001;
                8'b10101010:alucontrol <= 3'b111;                
            endcase
        end
endmodule

7.7 display.v

`timescale 1ns / 1ps

module display(
    input wire clk,reset,
    input wire [31:0]s,
    output wire [6:0]seg,
    output reg [3:0]ans
    );
    reg [20:0]count;
    reg [4:0]digit; 
    always@(posedge clk,posedge reset)
    if(reset)  
        count = 0;
    else 
        count = count + 1;
       
    always @(posedge clk)
    case(count[20:19])
        0:begin
            ans = 4'b1110;
            digit = s[3:0];
        end
        
        1:begin
            ans = 4'b1101;
            digit = s[7:4];
        end

        2:begin
            ans = 4'b1011;
            digit =s[11:8];
        end
        
        3:begin
            ans = 4'b0111;
            digit = s[15:12];
        end
    endcase
    
    seg7 U4(.din(digit),.dout(seg));
endmodule

7.8 seg7.v

`timescale 1ns / 1ps

module seg7(
    input wire [3:0]din,
    output reg [6:0]dout
    );
 
    always@(*)
    case(din)
        5'h0:dout = 7'b000_0001;
        5'h1:dout = 7'b100_1111;
        5'h2:dout = 7'b001_0010;
        5'h3:dout = 7'b000_0110;
        5'h4:dout = 7'b100_1100;
        5'h5:dout = 7'b010_0100;
        5'h6:dout = 7'b010_0000;
        5'h7:dout = 7'b000_1111;
        5'h8:dout = 7'b000_0000;
        5'h9:dout = 7'b000_0100;
        5'ha:dout = 7'b000_1000;
        5'hb:dout = 7'b110_0000;
        5'hc:dout = 7'b011_0001;
        5'hd:dout = 7'b100_0010;
        5'he:dout = 7'b011_0000;
        5'hf:dout = 7'b011_1000;
        default:dout = 7'b111_1111;        
    endcase
endmodule

7.9 prgmip32.coe

memory_initialization_radix = 16;
memory_initialization_vector =
00000000,
8d410000,
ad410000,
112a004c,
21490002,
0810004f,
014b4822,
3549000c,
00000000

7.10 引脚文件

八、实验结果

image-20220520204738322

293b4c9491712bb3d1142edd816069f