Verlog——单周期8指令CPU实现

Verlog——单周期8指令CPU实现

1、综述:CPU模块分为:设计代码,测试代码

1.1、设计代码包括:
1.1.1、顶层模块代码 CPU.v
1.1.2、指令存储器
1.1.3、寄存器组
1.1.4、运算器
1.1.5、数据存储器
设计通路可以参考华中科技老师谭志虎老师布置的单周期CPU作业的网络答案,代码中保留了许多系统函数作为查看运行状况,如在使用或烧写必须删去,(因为开发版资源有限建议使用IP盒的方式作为存储器)
在写CPU顶层模块时候:每个调用的模块我都定义了wire型变量其作为输入输出的,这样设计主要便以我连接模块,对于模块的输入用xx_in_xx表示,输出用xx_out_xx表示;如果logism画的图出现了分线器,就会定义一个reg变量作为分线器的输出,我指令存储器初始化的指令是谭志虎老师的sort.hex中的数据,所有输出检测都是用于检测该sort程序在cpu的运行状况

2、指令存储器

对其有初始化,用于设置指令存储器存储的指令的,可以看到代码有readmemh()地址请自己修改指令用的是

module Instruct_Memory(
input rst_l,
input [9:0]ir_in_a,
output [31:0] ir_out_d
    );
//指令存储器,缩写IR
//功能:输入地址即可输出对应地址的数据
//输入:指令地址 10b,ir_in_a;
//输出:指令 32b,ir_out_d;
//存储器每个单位32位,单元个数是:dm_len

   parameter dm_len=38;
   reg [31:0] D_M[dm_len:0];

  always @(*)
  if(!rst_l)//复位用于初始化指令存储器 
  begin
  $readmemh("D:/Documents/Hardware_verlog/MIPS_CPU/Complete_Routine_Test_Data/sort.txt",D_M);//保存指令
  //$readmemh("D:/Documents/Hardware_verlog/MIPS_CPU/Complete_Routine_Test_Data/sort.txt",D_M);//保存指令
  //?
  $monitor("D_M[ 0]=%h",D_M[ 0]); 
  end
  
  //用于根据地址取出指令
   assign ir_out_d=D_M[ir_in_a];
   
endmodule

3、寄存器组

对其也有初始化请注意readmemh自己修改新地址,(自己写一个32个0000_0000作为寄存器组的初值即可)

module Reg_File(
input clk,
input rst_l,
input [4:0] rf_in_ra1,//读出数据的地址
input [4:0] rf_in_ra2,//

input rf_in_wre,//写使能信号

input [4:0]rf_in_wa,// 写入数据的地址
input [31:0]rf_in_wd,//写入数据

output reg[31:0]rf_out_rd1,//读出寄存器组的数据
output reg[31:0]rf_out_rd2
    );
    
    reg[31:0] RF_Mem[31:0];//寄存器主体
    reg [4:0] i=0;
   
    always @(posedge clk ,negedge rst_l)//写入寄存器组
    begin
         if(!rst_l)//clk
         ;
        else    
             begin
                if(rf_in_wre==1'b1)//当是写入数据时
                     begin
                        RF_Mem[rf_in_wa]=rf_in_wd;
                        $display($time,"输出 写数据时:写读使能信号:rf_in_wre=%h,写数据:rf_in_wd=%h,寄存器组对应单元被写后单元的值:RF_Mem[%h]=%h",rf_in_wre,rf_in_wd,rf_in_wa, RF_Mem[rf_in_wa]);
                      end                         
             end
      end
      
      
      always @(*)//读入寄存器组
           if(!rst_l)//复位
                begin
                    $readmemh("D:/Documents/Hardware_verlog/MIPS_CPU/RF_data.txt",RF_Mem);//保存指令 
                    rf_out_rd1<=32'h0000_0000;
                    rf_out_rd2<=32'h0000_0000;
                    //删除
                     $display($time,,"初始化后各寄存器的值");
                     repeat(32)
                         begin
                                $display($time,,"RF_Mem[%h]=%h",i,RF_Mem[i]);
                                 i=i+5'h1;
                        end
                     //删除
                end
            else
                begin//读数据
                       rf_out_rd1 <=RF_Mem[rf_in_ra1];//读取R1用<=也是错的,得用=
                       rf_out_rd2 <= RF_Mem[rf_in_ra2];//读取R2
                       $display($time,"输出 读数据1时:读输出的数据:rf_out_rd1=%h,对应被读单元内保存的值:RF_Mem[%h]=%h",rf_out_rd1,rf_in_ra1,RF_Mem[rf_in_ra1]);
                       $display($time,"输出 读数据2时:读输出的数据:rf_out_rd2=%h,对应被读单元内保存的值:RF_Mem[%h]=%h",rf_out_rd2,rf_in_ra2,RF_Mem[rf_in_ra2]);
                       i=5'h0;
                       repeat(32)
                            begin
                                $display($time,,"RF_Mem[%h]=%h",i,RF_Mem[i]);
                                i=i+1;
                            end 
                  end    
                         
endmodule

4、运算器

循环移位有编写另外模块

module Arith_Logic_Unit
(
input rst_l,

input [31:0]alu_in_a,//参加运算的数A
input [31:0]alu_in_b,//参加运算的数B
input [3:0]alu_in_contr,//运算功能控制

output reg alu_out_equ,
output reg [31:0] alu_out_rl,//低字节
output reg [31:0] alu_out_rh//高字节

);
//缩写ALU
//输入32位的参加运算的A,B,4位运算种类的控制 alu_in_contr
//输出运算结果64位,分为2个32位的输出口

reg LR;
wire [31:0]Temp_A;
always @(alu_in_a,alu_in_b,alu_in_contr,Temp_A,rst_l)
if(!rst_l)
begin
alu_out_equ=1'b0;
alu_out_rh=32'h0;
alu_out_rl=32'h0;
end
else
begin
alu_out_equ<=(alu_in_a==alu_in_b)? 1'b1 :1'b0;
case(alu_in_contr)
4'b0000:alu_out_rl<=alu_in_a<<alu_in_b[3:0];//0左移
4'b0001:
        begin
        LR=1;
       alu_out_rl<=Temp_A;
end//1循环右移
4'b0010:alu_out_rl<=alu_in_a>>alu_in_b[3:0];//2右移
4'b0011:{alu_out_rh,alu_out_rl}<=alu_in_a*alu_in_b;//3乘法
4'b0100:{alu_out_rh,alu_out_rl}<=alu_in_a/alu_in_b;//4除法
4'b0101:{alu_out_rh,alu_out_rl}<=alu_in_a+alu_in_b;//5加法
4'b0110:alu_out_rl<=alu_in_a-alu_in_b;//6减法
4'b0111:alu_out_rl<=alu_in_a&alu_in_b;//7and
4'b1000:alu_out_rl<=alu_in_a|alu_in_b;//8OR
4'b1001:alu_out_rl<=alu_in_a^alu_in_b;//9XOR
4'b1010:alu_out_rl<=alu_in_a^~alu_in_b;//aNOR
4'b1011:
       if(alu_in_a>alu_in_b)//c不相等
            alu_out_rl<=32'h0;
        else
            alu_out_rl<=32'h1;
4'b1100:
        if(alu_in_a>alu_in_b)//c不相等
            alu_out_rl<=32'h0;
        else
            alu_out_rl<=32'h1;
4'b1101:;//d
4'b1110:;//e 
4'b1111:;//f
endcase
end
Ring_Shift_LR T1(alu_in_a,alu_in_b[3:0],LR,Temp_A);
Ring_Shift_LR T2(alu_in_a,alu_in_b[3:0],LR,Temp_A);




endmodule


循环移位模块


module Ring_Shift_LR(
input [31:0]I, 
input [3:0]n,
input r_or_l,
output reg[31:0]R
    );
    parameter MAX=31;
    parameter MIN=0;
    always @(I,n,r_or_l)
    if(r_or_l)
    case(n)
    4'h0:R<=I;
    4'h1:R<={I[0],I[31:1]};
    4'h2:R<={I[1:0],I[31:2]};
    4'h3:R<={I[2:0],I[31:3]};
    4'h4:R<={I[3:0],I[31:4]};
    4'h5:R<={I[4:0],I[31:5]};
    4'h6:R<={I[5:0],I[31:6]};
    4'h7:R<={I[6:0],I[31:7]};
    4'h8:R<={I[7:0],I[31:8]};
    4'h9:R<={I[8:0],I[31:9]};
    4'ha:R<={I[9:0],I[31:10]};
    4'hb:R<={I[10:0],I[31:11]};
    4'hc:R<={I[11:0],I[31:12]};
    4'hd:R<={I[12:0],I[31:13]};
    4'he:R<={I[13:0],I[31:14]};
    4'hf:R<={I[14:0],I[31:15]};
        endcase
    else
    case(n)
    4'h0:R<=I;
    4'h1:R<={I[30:0],I[31]};
    4'h2:R<={I[29:0],I[31:30]};
    4'h3:R<={I[28:0],I[31:29]};
    4'h4:R<={I[27:0],I[31:28]};
    4'h5:R<={I[26:0],I[31:27]};
    4'h6:R<={I[25:0],I[31:26]};
    4'h7:R<={I[24:0],I[31:25]};
    4'h8:R<={I[23:0],I[31:24]};
    4'h9:R<={I[22:0],I[31:23]};
    4'ha:R<={I[21:0],I[31:22]};
    4'hb:R<={I[20:0],I[31:21]};
    4'hc:R<={I[19:0],I[31:20]};
    4'hd:R<={I[18:0],I[31:19]};
    4'he:R<={I[17:0],I[31:18]};
    4'hf:R<={I[16:0],I[31:17]};
        endcase
endmodule


5、数据存储器

module Data_Memory(
input clk,
input rst_l,

input dm_in_wre, //在前面用1有效,后的0有效
input wire [9:0] dm_in_rwa,
input wire [31:0] dm_in_wd,


output reg[31:0] dm_out_rd
);
//单周期的数据存储器 
parameter DM_len=150;
reg [31:0]DM_Mem[ DM_len:0];//定义存储器主体

always @(posedge clk,negedge rst_l )//写入数据存储
    begin
        if(!rst_l)//复位信号有效时
        ; 
        else   
            begin 
                if(dm_in_wre)//复位信号无效时,发出写指令
                    DM_Mem[dm_in_rwa]<= dm_in_wd;
             end
    end
    
    always@(*)//读出数据
            begin
                 if(!rst_l)//复位信号有效时
                        dm_out_rd=32'h0000_0000; 
                 else//发出写指令
                         dm_out_rd <= DM_Mem[dm_in_rwa];
                 $display("输出:数据存储器 在读写后 存储单元的值DM[80]=%h,DM[81]=%h,DM[82]=%h,DM[83]=%h,DM[84]=%h,DM[85]=%h,DM[86]=%h,DM_Mem[87]=%h"
                 ,DM_Mem[8'h80],DM_Mem[8'h81],DM_Mem[8'h82],DM_Mem[8'h83],DM_Mem[8'h84],DM_Mem[8'h85],DM_Mem[8'h86],DM_Mem[8'h87]);
             end
endmodule

符号扩展器

module Sign_Extender(
     input  se_in_sign,
     output reg [31:0] se_out_data
    );
    //组合电路,简写调用模块名: SE
    //功能输入最高位,输入接口名:se_in
    //输出对应最高位的32位扩展,输出接口名:se_out
    //符合设计
    
    always@(*)
    if(se_in_sign==1'b1)
    se_out_data<=32'hffff_ffff;
    else
    se_out_data<=32'h0000_0000;
endmodule

7、控制器

module Controller_Uint(
input  [5:0]cu_Op,
input [5:0]cu_Func,

output wire cu_Halt,
output wire cu_MemtoReg,
output wire cu_MemWrite,
output wire cu_Beq,
output wire cu_Bne,
output reg[3:0] cu_AluOP,
output wire cu_AluSrcB,
output wire cu_RegWrite,
output wire cu_RegDst
    );
    
    reg LW=1'b0,SW=1'b0,BEQ=1'b0,BNE=1'b0,ADDI=1'b0,R=1'b0;
    reg ADD=1'b0,SLT=1'b0,SysCall=1'b0;
    wire R_TYPE;
    
    //识别L指令和其他指令指令类型
    always @(*)
    case(cu_Op)
    6'h23:
        begin 
            LW<=1'b1;
             SW<=1'b0;BEQ<=1'b0;BNE<=1'b0;ADDI<=1'b0;R<=1'b0;
             ADD<=1'b0;SLT<=1'b0;SysCall<=1'b0;
        end
    6'h2b: 
             begin 
                    SW<=1'b1;
                    LW<=1'b0;BEQ<=1'b0;BNE<=1'b0;ADDI<=1'b0;R<=1'b0;
                    ADD<=1'b0;SLT<=1'b0;SysCall<=1'b0;
              end
    6'h04:
             begin 
                    BEQ<=1'b1;
                    SW<=1'b0;LW<=1'b0;BNE<=1'b0;ADDI<=1'b0;R<=1'b0;
                    ADD<=1'b0;SLT<=1'b0;SysCall<=1'b0;
              end
    6'h05: 
            begin 
                    BNE<=1'b1;
                    BEQ<=1'b0;SW<=1'b0;LW<=1'b0;ADDI<=1'b0;R<=1'b0;
                    ADD<=1'b0;SLT<=1'b0;SysCall<=1'b0;
              end
    6'h08: 
             begin 
                    ADDI<=1'b1;
                    BNE<=1'b0;BEQ<=1'b0;SW<=1'b0;LW<=1'b0;R<=1'b0;
                    ADD<=1'b0;SLT<=1'b0;SysCall<=1'b0;
              end
            
    6'h00:
            begin 
                    R<=1'b1;
                    ADDI<=1'b0;BNE<=1'b0;BEQ<=1'b0;SW<=1'b0;LW<=1'b0;
                    ADD<=1'b0;SLT<=1'b0;SysCall<=1'b0;
              end
    
    endcase
    
    //识别其他指令中:R型,J型,Syscall
     always @(*)
     if(R==1'b1)
    case(cu_Func)
    6'h20:
             begin 
                    R<=1'b1; ADDI<=1'b0;BNE<=1'b0;BEQ<=1'b0;SW<=1'b0;LW<=1'b0;
                    ADD<=1'b1;SLT<=1'b0;SysCall<=1'b0;
              end
    6'h2a:
            begin 
                    R<=1'b1; ADDI<=1'b0;BNE<=1'b0;BEQ<=1'b0;SW<=1'b0;LW<=1'b0;
                    ADD<=1'b0;SLT<=1'b1;SysCall<=1'b0;
              end
    8'h0c:
            begin 
                    R<=1'b0; ADDI<=1'b0;BNE<=1'b0;BEQ<=1'b0;SW<=1'b0;LW<=1'b0;
                    ADD<=1'b0;SLT<=1'b0;SysCall<=1'b1;
              end
    endcase
    
  
    
    //AluOP产生
    always@(*)
    case((R)&(cu_Func==6'h2a))
    1'b0:cu_AluOP<=4'h5;
    1'b1:cu_AluOP<=4'hc;
    endcase
    
    //控制信号产生
    assign cu_RegDst=ADD|SLT;
    assign cu_RegWrite=LW|ADDI|(ADD|SLT);
    assign cu_MemtoReg=LW;
    assign cu_MemWrite=SW;
    assign cu_AluSrcB=SW|LW|ADDI;
    assign cu_Beq=BEQ;
    assign cu_Bne=BNE;
    assign cu_Halt=SysCall;
    
endmodule

8、顶层CPU

module CPU(
    input sys_clk_in,
    input sys_rst_n
    //output PC
);
//所有多路选择器都带有一个寄存器
    //Define Inner Variable
    reg [31:0]PC;
    reg [4:0] RF_IN_WA;
    reg [31:0]RF_IN_WD;
    reg [31:0]ALU_IN_B;
    wire [31:0]instruct;
    
    //指令存储器
     wire [9:0]ir_in_a;
     wire [31:0]ir_out_d;
    //控制器 
    wire [5:0]Op,Func; //控制器输入
    wire  Halt,MemtoReg,MemWrite,Beq,Bne,AluSrcB,RegWrite,RegDst; //控制器输出
    wire [3:0]AluOP;
    //寄存器组
    wire rf_in_wre;//寄存器组读写使能信号,1w,0r

    wire [4:0] rf_in_ra1,rf_in_ra2; //寄存器组的读地址
    wire [31:0]rf_out_rd1,rf_out_rd2;//寄存器组读出数据
   
    wire[4:0]rf_in_wa;//寄存器组写入地址
    wire[31:0]rf_in_wd;//寄存器组写入数据
    
    //运算器 
   wire [31:0]alu_in_a,alu_in_b;//运算器参与运算的A,B
   wire [3:0]alu_in_contr;//运算器的控制信号
   wire alu_out_equ;//等于输出信号
   wire [31:0]alu_out_rl,alu_out_rh;//运算结果输出

    //数据存储器 
   wire dm_in_wre;//数据存储器 的写读使能信号,1w,0r
   wire [9:0] dm_in_rwa;//数据存储器 的读写地址
   wire[31:0]dm_in_wd,dm_out_rd;//数据存储器 的写数据,读数据
    
    //符号扩展器
   wire  se_in_sign;
   wire [31:0]se_out_data;

    //loginc Implementation 
    //PC产生器
    
    always @(posedge sys_clk_in,negedge sys_rst_n)
    begin
     $display($time,,"上次instruct=%h,PC=%h",instruct,PC);
    if(! sys_rst_n)//复位
        PC<=32'h0000_0000;
    else    if( Halt==1'b0)
                begin
                    if(( Beq&alu_out_equ)||(Bne&&!alu_out_equ)) //跳转时PC变化
                        begin
                             PC<=PC+32'h4+{se_out_data[13:0],instruct[15:0],2'b00};
                         end
                     else //正常PC变化
                        PC<=PC+32'h4;
                   end
                else
                PC<=PC;
    
        end
        always@(*)
        $monitor($time,,"本周期PC=%h",PC);
     

    //从 指令存储器 取出指令
    assign ir_in_a=PC[11:2];//获取 指令存储器 地址
    Instruct_Memory IR(sys_rst_n,ir_in_a, instruct);//取指令
    //assign instruct=ir_out_d;//传指令总线

    //寄存器组
    always @(*)
        $display($time,,"本周期的指令:instruct=%h",instruct);
    assign rf_in_ra1=instruct[25:21];//获取 寄存器组的 读入地址1
    assign rf_in_ra2=instruct[20:16];//获取 寄存器组的 读入地址2
    assign rf_in_wre=RegWrite;//获取 寄存器组的 读写使能信号
    
    always @(*)//获取写入地址
    	if(RegDst)
    		RF_IN_WA<=instruct[15:11];
    	else
		RF_IN_WA<=instruct[20:16];
    assign rf_in_wa=RF_IN_WA;

    always @(*)//获取写入数据
    	if(MemtoReg)
    		RF_IN_WD<=dm_out_rd;
    	else
		RF_IN_WD<=alu_out_rl;
    assign rf_in_wd=RF_IN_WD;//获取写入数据
    /*always @(*)
       $display($time,,"输出:进入寄存器前 的各项数据RF_IN_WD=%h,rf_in_ra1=%h,rf_in_ra2=%h,rf_in_wre=%h,rf_in_wa=%h,rf_in_wd=%h,rf_out_rd1=%h,rf_out_rd2=%h",
 RF_IN_WD,rf_in_ra1,rf_in_ra2,rf_in_wre,rf_in_wa,rf_in_wd,rf_out_rd1,rf_out_rd2);*/
    Reg_File RF(
sys_clk_in,sys_rst_n,
rf_in_ra1,rf_in_ra2,
rf_in_wre,
rf_in_wa,rf_in_wd,
rf_out_rd1,rf_out_rd2);
 always@(*)
 $monitor($time,,"输出:进入寄存器组后 的各项数据RF_IN_WD=%h,rf_in_ra1=%h,rf_in_ra2=%h,rf_in_wre=%h,rf_in_wa=%h,rf_in_wd=%h,rf_out_rd1=%h,rf_out_rd2=%h\n\n",
 RF_IN_WD,rf_in_ra1,rf_in_ra2,rf_in_wre,rf_in_wa,rf_in_wd,rf_out_rd1,rf_out_rd2);
 
//符号扩展器
    assign se_in_sign=instruct[15];
    
    Sign_Extender SE(se_in_sign,se_out_data);
    always@(*)
    $monitor($time,,"输出:进入符号扩展器后的 各项数据se_in_sign=%h,se_out_data=%h,instruct[15:0]=%h\n\n",
    se_in_sign,se_out_data,instruct[15:0]);
    
    //ALU
    assign alu_in_a=rf_out_rd1;//获取ALU参与运算的A
    always@(*)//获取ALU参与运算的B
     	if(AluSrcB)
		ALU_IN_B={se_out_data[15:0],instruct[15:0]};
	else
		ALU_IN_B=rf_out_rd2;
    assign alu_in_b=ALU_IN_B;
    assign alu_in_contr= AluOP;//获取ALU控制信号
    /* always@(*)
   $display($time,,"输出:进入ALU前 的各项数据ALU_IN_B=%h,alu_in_a=%h,alu_in_b=%h,alu_in_contr=%h,alu_out_equ=%h,alu_out_rl=%h,alu_out_rh=%h",ALU_IN_B,alu_in_a,alu_in_b,
alu_in_contr,alu_out_equ,alu_out_rl,alu_out_rh);*/

    Arith_Logic_Unit ALU(
    sys_rst_n,
alu_in_a,alu_in_b,
alu_in_contr,
alu_out_equ,alu_out_rl,alu_out_rh);
    always@(*)
   $monitor($time,,"输出:进入ALU后 的各项数据ALU_IN_B=%h,alu_in_a=%h,alu_in_b=%h,alu_in_contr=%h,alu_out_equ=%h,alu_out_rl=%h,alu_out_rh=%h\n\n",ALU_IN_B,alu_in_a,alu_in_b,
alu_in_contr,alu_out_equ,alu_out_rl,alu_out_rh);

    //数据存储器
    assign dm_in_wre=MemWrite;//获取数据存储器使能信号,W1,R0
    assign dm_in_rwa=alu_out_rl[11:2];
    assign dm_in_wd=rf_out_rd2;
    /*always@(*)
$display($time,,"输出:进入前数据存储器 各项数据dm_in_wre=%h,dm_in_rwa=%h,dm_in_wd=%h,dm_out_rd=%h",
 dm_in_wre,dm_in_rwa,dm_in_wd,dm_out_rd);*/
    
    Data_Memory DM(
sys_clk_in,sys_rst_n,
dm_in_wre,
dm_in_rwa,dm_in_wd,
dm_out_rd);
always@(*)
 $monitor($time,,"输出:进入后数据存储器 各项数据dm_in_wre=%h,dm_in_rwa=%h,dm_in_wd=%h,dm_out_rd=%h\n\n",
 dm_in_wre,dm_in_rwa,dm_in_wd,dm_out_rd);
    
    

    //控制器
    always @(*)
    $monitor($time,," Op=%h, Func=%h,Halt=%h, MemtoReg=%h,MemWrite=%h,Beq=%h,Bne=%h,AluOP=%h,AluSrcB=%h,RegWrite=%h,RegDst=%h",
    Op, Func,Halt, MemtoReg,MemWrite,Beq,Bne,AluOP,AluSrcB,RegWrite,RegDst);
    assign Op=instruct[31:26];
    assign Func=instruct[5:0];
    Controller_Uint CU(
    Op, //输入信号
    Func,

    Halt, //输出信号
    MemtoReg,
    MemWrite,
    Beq,
    Bne,
    AluOP,
    AluSrcB,
    RegWrite,
    RegDst
    );
endmodule

9:sim_CPU(总体的测试代码)

module sim_CPU(

    );
    reg clk,rst_l;
    //reg[31:0]PC;
    initial
    begin 
    clk=1'b1;
    rst_l=1'b0;
    #1
    rst_l=1'b1;
    end
    CPU T(clk,rst_l);
    
    always #5  
    clk=~clk;
endmodule

  • 9
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值