计算机组成原理上机实验5 单周期CPU
2018-05-04

实验目的

  • 综合前几次实验的结果,实现一个单周期MIPS指令集的CPU, 然后运行一个计算斐波那契数列的程序。

实验平台

  • ISE 14.7

实验过程(分析)

  1. 模块化设计,主要有以下几个模块

    a) alu模块——算术逻辑单元

    b) regfile模块——寄存器文件

    c) mux模块——选择器

    d) IP核生成的IMem模块和DMem模块——存放数据段和代码段

    e) nextpclogic模块——计算下一个PC

    f) control模块——控制regfile、IMem、DMem、nextpclogic和alu

    g) top模块——实例化前几个模块,连接各个信号

  2. alu模块使用case语句判断8种操作类型。

  3. regfile模块用组合逻辑读,时序逻辑写。

  4. IMem和DMem是异步读,同步写,且由指定coe文件初始化,coe文件的内容是16进制文本,由Mars编译一个汇编代码生成。

  5. nextpclogic模块通过组合逻辑计算出nextPC的值。

  6. control模块根据输入的操作符Op对各控制变量进行赋值,根据ALUOp对ALUControl进行赋值。

  7. top模块实例化前几个模块,连接各个信号。

  8. bgtz的实现:bgtz是伪指令(类似地有la和li),不是MIPS指令集的指令,编译时会进行处理,转换为几条指令实现。

这里我用以下三条指令

 slt $t6,$zero,$t1 # if $t1 > 0, that $t6 = 1, else $t6 = 0

 addi $t7,$zero,1 # set $t7 = 1

 beq $t6,$t7,loop # jump if $t6 = 1, that is to say, $t1 > 0

实现

bgtz $t1, loop # repeat if not finished yet.
  1. 完整的数据通路

10、分析结果

.data

fibs: .word 0 : 20 # "array" of 20 words to contain fib values

size: .word 20 # size of "array"

temp: .word 3  3

.text

   la $t0, fibs # load address of array

   la $t5, size # load address of size variable

   lw $t5, 0($t5) # load array size

   la $t3, temp # load

   lw $t3, 0($t3)

   la $t4, temp

   lw $t4, 4($t4)

   sw $t3, 0($t0) # F[0] = $t3

   sw $t4, 4($t0) # F[1] = $t4

   addi $t1, $t5,  -2 # Counter for loop, will execute (size-2) times

loop: lw $t3, 0($t0) # Get value from array F[n]

   lw $t4, 4($t0) # Get value from array F[n+1]

   add $t2, $t3, $t4 # $t2 = F[n] + F[n+1]

   sw $t2, 8($t0) # Store F[n+2] = F[n] + F[n+1] in array

   addi $t0, $t0,  4 # increment address of Fib. number source

   addi $t1, $t1,  -1 # decrement loop counter

   slt $t6,$zero,$t1 # if $t1 > 0, that $t6 = 1, else $t6 = 0

   addi $t7,$zero,1 # set $t7 = 1

   beq $t6,$t7,loop # jump if $t6 = 1, that is to say, $t1 > 0

out: 

   j out
  • 完整汇编代码如上,可知程序先向内存数据段读取数组首地址和大小(循环次数),读取并写入f[0]=3, f[1]=3,然后循环18次,执行f[i]=f[i-1]+f[i-2]计算斐波那契数列。

  • 如果CPU编写正确,DMem [0-19]内容 将是3、3、6、9、15、24、39、63、102、165、267、432、699、1131、1830、2961、4791、7752、12543、20295

实验结果

  • 仿真结果

    IP核ram设置界面中Load In设置初始化coe文件的路径,

    其中IMem内容为

    MEMORY_INITIALIZATION_RADIX=16;
    
    MEMORY_INITIALIZATION_VECTOR=
    
    20080000,
    
    200d0050,
    
    8dad0000,
    
    200b0054,
    
    8d6b0000,
    
    200c0054,
    
    8d8c0004,
    
    ad0b0000,
    
    ad0c0004,
    
    21a9fffe,
    
    8d0b0000,
    
    8d0c0004,
    
    016c5020,
    
    ad0a0008,
    
    21080004,
    
    2129ffff,
    
    1d20fff9,
    
    08000011,

DMem内容为

    MEMORY_INITIALIZATION_RADIX=16;
    
    MEMORY_INITIALIZATION_VECTOR=
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000000
    
    00000014
    
    00000003
    
    00000003

仿真得DMem和reg数据为

DMem

Regfile

可见运行结果符合分析。

附录:

  • 编写过程中遇到问题和一些发现

    1. 位拼接作为左值,单个变量也要花括号,如

       `{{RegDst},{ALUSrc},{MemtoReg},{RegWrite},{MemWrite},{Branch},{ALUOp[1:0]},{Jump}}=9'b100100100;`
      
    2. “==”会严格匹配每一位,比如module内5位数字比较(一个常量,一个input),但外部传入的是3位的,高位自动拓展好像是x,就导致“==”不成立,这是一个不好发现的问题。

    3. 仿真遇到高阻态,多半是这个信号不存在,经常是拼写错了,有时候ise检查不出来。

    4. 发现如果Mars的memory configuration设置为默认,则编译后导出代码里面的所有la(给某个寄存器赋值一个32位地址)被翻译为连续两句lui和ori;若memory configuration设置为text at address 0则编译后导出代码里面的所有la被翻译为一句addi。我觉得原因是text地址从0 开始的话,如果text部分较短,地址非0部分未超过16位,那么la只需要给低16位赋值就行了,就可以直接用addi实现(addi指令末16位为立即数)。如果地址是实打实的32位那addi就无能为力了,这时候需要lui给高16位赋值,低16位用ori(ori的rs取$0,作用就和addi一样)。

  • 模块源代码

    • top.v
        module top(
            input clk,
            input rst_n,    
            );
            
            wire ALUSrc;
            wire [31:0] ALUSrcA;
            wire [31:0] ALUSrcB;
            wire [2:0] ALUControl;
            wire [31:0] ALUResult;
            wire Zero;
            
            wire [31:0] SignExtented;
            
            wire [5:0] RegRdaddr1;
            wire [31:0] RegRdout1;
            wire [5:0] RegRdaddr2;
            wire [31:0] RegRdout2;
            wire [5:0] RegWdaddr;   
            wire [31:0] RegWdin;
            wire RegWrite;
            wire RegDst;
        
            wire [31:0] DMemaddr;
            wire [31:0] DMemout;
            wire [31:0] DMemin;
            wire DMemWrite;
            wire [31:0] IMemRdaddr;
            wire [31:0] IMemRdout;
            wire MemtoReg;
            
            wire [31:0] Instr;
            
            reg [31:0] PC=0;
            wire [31:0] nextPC;
            
            wire [5:0] Funct;
            wire [15:0] IMM16;
            wire [4:0] Rd;
            wire [4:0] Rt;
            wire [4:0] Rs;
            wire [5:0] Op;
        
            wire [25:0] JumpIMM;
            wire Jump;
            
            wire Branch;
            integer first;
            
            //=======================PC========================
            always @(posedge clk or negedge rst_n)
                if(~rst_n) begin first <= 1; PC <= 0; end 
                else if(first == 1) begin first <= 0; PC <= PC; end
                else 
                    PC <= nextPC;       
                
            nextpclogic nextpclogic(PC,Jump,JumpIMM,Branch,Zero,SignExtented,nextPC);   
            
            //=====================Instruction Memory======================
            assign IMemRdaddr = PC >> 2;//>>2是因为这里IMem是每个地址存储4字节,和实际上的(一地址一字节)不一样
            IMem IMem(0,0,IMemRdaddr,0,0,IMemRdout);
            assign Instr = IMemRdout;
            
            assign JumpIMM = Instr[25:0];
            assign Funct = Instr[5:0];
            assign IMM16 = Instr[15:0];
            assign Rd = Instr[15:11];
            assign Rt = Instr[20:16];
            assign Rs = Instr[25:21];
            assign Op = Instr[31:26];
        
            //=======================Regfile========================
            assign RegRdaddr1 = Rs;
            assign RegRdaddr2 = Rt;
            mux #(6) MUXRegWdaddr(RegDst,{1'b0,Rt},{1'b0,Rd},RegWdaddr);
            regfile regfile(~clk,rst_n,RegRdaddr1,RegRdout1,RegRdaddr2,RegRdout2,RegWdaddr,RegWdin,RegWrite);
            
            //=========================ALU==========================
            assign SignExtented = {{16{IMM16[15]}},IMM16};
            assign ALUSrcA = RegRdout1;
            mux MUXALUSrc(ALUSrc,RegRdout2,SignExtented,ALUSrcB);
            alu alu(ALUSrcA,ALUSrcB,{2'b00,{ALUControl}},ALUResult,Zero);
            
            //=======================Control========================
            control control(clk,Op,Funct,RegDst,ALUSrc,MemtoReg,RegWrite,DMemWrite,Branch,ALUControl,Jump); 
            
            //=======================Data Memory========================
            assign DMemaddr = ALUResult >> 2;//>>2理由同上
            assign DMemin = RegRdout2;
            DMem DMem(DMemaddr,DMemin,~clk,DMemWrite,DMemout);  
            mux MUXtoReg(MemtoReg,ALUResult,DMemout,RegWdin);
            
        endmodule
      
    • alu.v
        parameter A_NOP =5'h00; //nop
        parameter A_ADD =5'h01; //sign_add
        parameter A_SUB =5'h02; //sign_sub
        parameter A_AND =5'h03; //and
        parameter A_OR  =5'h04; //or
        parameter A_XOR =5'h05; //xor
        parameter A_NOR =5'h06; //nor
        parameter A_SLT =5'h07; //slt
        
        module alu(
            input [31:0] alu_a,
            input [31:0] alu_b,
            input [4:0] alu_op,
            output reg [31:0] alu_out,
             output zero
            );
            assign zero = (alu_out == 32'b0)?1:0;
            always@(*)
                case (alu_op)
                    A_NOP: alu_out = 0;
                    A_ADD: alu_out = alu_a + alu_b;
                    A_SUB: alu_out = alu_a - alu_b;
                    A_AND: alu_out = alu_a & alu_b;
                    A_OR : alu_out = alu_a | alu_b;
                    A_XOR: alu_out = alu_a ^ alu_b;
                    A_NOR: alu_out = ~(alu_a | alu_b);
                    A_SLT: //a<b(signed) return 1 else return 0;
                        begin
                            if(alu_a[31] == alu_b[31]) alu_out = (alu_a < alu_b) ? 32'b1 : 32'b0;
                            //同号情况,后面的小于是视为无符号的比较
                            else alu_out = (alu_a[31] < alu_b[31]) ? 32'b0 : 32'b1;
        //有符号比较符号
                        end         
                    default: ;
                endcase
        endmodule
      
    • regfile.v
        module regfile(
            input   clk,
            input rst_n,
            input [5:0] rAddr1,//读地址1
            output [31:0] rDout1,//读数据1
            input [5:0] rAddr2,//读地址2
            output [31:0] rDout2,//读数据2
            input [5:0] wAddr,//写地址
            input [31:0] wDin,//写数据
            input wEna//写使能
            
        );
            reg [31:0] data [0:63];
            integer i;
            assign rDout1=data[rAddr1];//读1
            assign rDout2=data[rAddr2];//读2
            always@(posedge clk or negedge rst_n)//写和复位
                if(~rst_n)
                begin
                    for(i=0; i<64; i=i+1) data[i]<=0;
                end
                else
                begin
                    if(wEna)
                        data[wAddr]<=wDin;
                end
        endmodule
      
    • nextpclogic.v
        module nextpclogic(
           input [31:0] PC,
            input Jump,
            input [25:0] JumpIMM,
           input Branch,
           input Zero,
            input [31:0] SignExtented,
            output [31:0] nextPC
           );
            
            wire [31:0] ShiftLeft2;
            wire PCSrc;
            wire [31:0] PCPlus4;
            wire [31:0] PCBeq;
            wire [31:0] tmpPC;
            wire tmpzero1,tmpzero2;//no used
            
            assign ShiftLeft2 = SignExtented << 2;
            alu ALUPCPlus4(PC,4,A_ADD,PCPlus4,tmpzero1);
            alu ALUPCOffset(ShiftLeft2,PCPlus4,A_ADD,PCBeq,tmpzero2);
            and(PCSrc,Branch,Zero);
            mux MUXPC(PCSrc,PCPlus4,PCBeq,tmpPC);
            mux MUXPC2(Jump,tmpPC,{{PCPlus4[31:28]},{{2'b00,JumpIMM}<<2}},nextPC);
        endmodule
      
    • control.v
        module control(
            input clk,
            input [5:0] Op, //instr[31:26]
            input [5:0] Funct,//instr[5:0]
            output reg RegDst,
            output reg ALUSrc,
            output reg MemtoReg,
            output reg RegWrite,
            output reg MemWrite,
            output reg Branch,
            output reg [2:0] ALUContol,
            output reg Jump
            );
            reg [1:0] ALUOp;
            
            always @(*)
            begin
                case(Op)
                    6'b000000://R type
                        {{RegDst},{ALUSrc},{MemtoReg},{RegWrite},{MemWrite},{Branch},{ALUOp[1:0]},{Jump}}=9'b100100100;
                    6'b100011://lw
                        {{RegDst},{ALUSrc},{MemtoReg},{RegWrite},{MemWrite},{Branch},{ALUOp[1:0]},{Jump}}=9'b011100000;
                    6'b101011://sw  
                        {{RegDst},{ALUSrc},{MemtoReg},{RegWrite},{MemWrite},{Branch},{ALUOp[1:0]},{Jump}}=9'bx1x010000;
                    6'b000100://beq
                        {{RegDst},{ALUSrc},{MemtoReg},{RegWrite},{MemWrite},{Branch},{ALUOp[1:0]},{Jump}}=9'bx0x001010;
                    6'b000010://jump
                        {{RegDst},{ALUSrc},{MemtoReg},{RegWrite},{MemWrite},{Branch},{ALUOp[1:0]},{Jump}}=9'bxxxxxxxx1;
                    6'b001000://addi
                        {{RegDst},{ALUSrc},{MemtoReg},{RegWrite},{MemWrite},{Branch},{ALUOp[1:0]},{Jump}}=9'b010100000; 
                    default: ;
                endcase             
            end
            
            always @(*)
            begin
                case(ALUOp)
                    2'b00://lw,sw,addi
                        ALUContol = 5'h01;//add
                    2'b01://beq
                        ALUContol = 5'h02;//sub
                    2'b10://R type
                        case(Funct)
                            6'b100000: ALUContol = 5'h01;//add
                            6'b100010: ALUContol = 5'h02;//sub
                            6'b100100: ALUContol = 5'h03;//and
                            6'b100101: ALUContol = 5'h04;//or
                            6'b100110: ALUContol = 5'h05;//xor
                            6'b100111: ALUContol = 5'h06;//nor      
                            6'b101010: ALUContol = 5'h07;//slt
                            default: ;
                        endcase
                    2'b11:
                        ALUContol = 5'h00;//nop
                    default: ;
                endcase
            end
            
        endmodule
      
    • mux.v
        module mux #(parameter WIDTH = 32)(
            input sel,
            input [WIDTH-1:0] d0,
            input [WIDTH-1:0] d1,
            output [WIDTH-1:0] out
            );
            assign out = (sel == 1'b1 ? d1 : d0);
        endmodule
      
    • test.v
        module test;
        
            // Inputs
            reg clk;
            reg rst_n;
        
            // Instantiate the Unit Under Test (UUT)
            top uut (
                .clk(clk), 
                .rst_n(rst_n),
            );
            always #10 clk=~clk;
            initial begin
                // Initialize Inputs
                clk = 0;
                rst_n = 0;
        
                // Wait 100 ns for global reset to finish
                #100;
                rst_n = 1;
                 
                // Add stimulus here
         
            end
              
        endmodule
      
搜索
背景设置