Verilog 简易单周期CPU

时间:2022-12-21 12:02:27

目录

 本实验包含:

 简易结构图:

各部件代码或实现:

控制器:

寄存器堆:

ALU:

数据存储器:

指令存储器:

CPU:

tp(仿真文件):

 仿真结果:

 单周期CPU压缩包下载


 本实验包含:

        指令存储器和数据存储器的ip核调用,控制器,寄存器堆,ALU,单周期CPU的实现。

 简易结构图:

Verilog 简易单周期CPU

各部件代码或实现:

控制器:

控制器有13条指令,需要可以再加,照着之前格式注释加就行了,对于同RAM相关的指令未测试

R:
 指令   [31:26]    [25:21]    [20:16]   [15:11]  [10:6]   [5:0]  功能
add 000000 rs rt rd 000000 100000 寄存器加
sub 000000 rs rt rd 000000 100010 寄存器减
and 000000 rs rt rd 000000 100100 寄存器与
or 000000 rs rt rd 000000 100101 寄存器或
nor 000000 rs rt rd 000000 100111 寄存器或非
sll 000000 rs 000000 rd sa 000000 逻辑左移
srl 000000 rs 000000 rd sa 000010  逻辑右移
sra 000000 rs 000000 rd sa 100111 算术右移
I:
指令  [31:26]  [25:21]  [20:16]  [15:0]  功能
addi 001000 rs rt immediate 立即数加
lw 100011 rs rt immediate 取字数据
sw 101011 rs rt immediate 存字数据
beq 000100 rs rt immediate 相等转移
J:
指令 [31:26] [25:21] [20:16] [15;0] 功能
j 000010 00000 00000 immediate 转移

输入:op,func
输出:MemtoReg,MemWrite,Branch,ALUOP,ALUSrc,RegWrite,RegDst

//
//创建日期:2022/12/19 10:46:36
//设计名称:控制器
//课程名称:Controler
//说明: 
//输入:op,func
//输出:MemtoReg,MemWrite,Branch,ALUOP,ALUSrc,RegWrite,RegDst
//依赖项:
//      
//版次:
//版本0.01-文件已创建
//其他注释:
//
//
module Controler(op,func,MemtoReg,MemWrite,Branch,ALUOP,ALUSrc,RegWrite,RegDst);
    input [5:0] op;
    input [5:0] func;
    output MemtoReg;
    output MemWrite;
    output Branch;
    output [11:0] ALUOP;
    output ALUSrc;
    output RegWrite;
    output RegDst;
    reg MemtoReg,MemWrite,Branch,ALUSrc,RegWrite,RegDst;
    reg [11:0] ALUOP;
    //    R:
    //    指令	[31:26]	[25:21]	[20:16]	[15:11]	[10:6]	[5:0]	功能
    //    add	000000	   rs	 rt	       rd	000000	100000	寄存器加
    //    sub	000000	   rs	 rt	       rd	000000	100010	寄存器减
    //    and	000000	   rs	 rt	       rd	000000	100100	寄存器与
    //    or	000000	   rs	 rt	       rd	000000	100101	寄存器或
    //    nor	000000	   rs	 rt	       rd	000000	100111	寄存器或非
    //    sll	000000	   rs  000000	   rd	  sa	000000	逻辑左移
    //    srl	000000	   rs  000000	   rd	  sa	000010	逻辑右移
    //    sra	000000	   rs  000000	   rd	  sa	000011	算术右移
    //I:
    //    指令	[31:26]	[25:21]	[20:16]	[15:0]	功能
    //    addi	001000	  rs	rt    immediate	立即数加
    //    lw	100011	  rs	rt	  immediate	取字数据
    //    sw	101011	  rs	rt	  immediate	存字数据
    //    beq	000100	  rs	rt	  immediate	相等转移
    //J:
    //    指令	[31:26] [25:21] [20:16]  [15:0]     功能
    //    j 	000010	00000	00000	 immediate	转移

    always @(*)
    begin
        case(op)
            6'b000000://寄存器操作
            begin
                MemtoReg=0;//输出ALU的输出
                MemWrite=0;//数据存储器不写入
                Branch=0;//正常PC
                ALUSrc=0;//ALU输入2选择寄存器输出
                RegWrite=1;//寄存器写入
                RegDst=1;//有rd
                case(func)  //控制ALU操作
                    6'b100000:// 寄存器加
                        ALUOP=12'b010000000000;
                    6'b100010:// 寄存器减
                        ALUOP=12'b100000000000;
                    6'b100100:// 寄存器与
                        ALUOP=12'b000010000000;
                    6'b100101:// 寄存器或
                        ALUOP=12'b000000100000;
                    6'b100111:// 寄存器或非
                        ALUOP=12'b000001000000;
                    6'b100100:// 逻辑左移
                        ALUOP=12'b000000001000;
                    6'b100101:// 逻辑右移
                        ALUOP=12'b000000000100;
                    6'b100111:// 算术右移
                        ALUOP=12'b000000000010;
                    default:ALUOP=12'b010000000000;
                endcase
            end
            6'b001000:// 立即数加
            begin
                MemtoReg=0;//输出ALU结果
                MemWrite=0;//数据存储器不写入
                Branch=0;//正常PC
                ALUOP=12'b010000000000;//ALU加操作
                ALUSrc=1;//数据2选择立即数输出
                RegWrite=1;//寄存器写入
                RegDst=0;//无rd选择rt
            end
            6'b100011:// 取字数据
            begin
                MemtoReg=1;//输出数据存储器结果
                MemWrite=0;//数据存储器不写入
                Branch=0;//正常PC
                ALUOP=12'b000000000000;//ALU无操作,输出第一个输入
                ALUSrc=1;//数据2随意
                RegWrite=1;//寄存器写入
                RegDst=0;//无rd选择rt
            end
            6'b101011:// 存字数据
            begin
                MemtoReg=1;//输出随意
                MemWrite=1;//数据存储器写入
                Branch=0;//正常PC
                ALUOP=12'b000000000000;//ALU无操作,输出第一个输入
                ALUSrc=1;//数据2随意
                RegWrite=0;//寄存器不写入
                RegDst=0;//不写入随意
            end
            6'b000100:// 相等转移
            begin
                MemtoReg=1;//输出随意
                MemWrite=0;//数据存储器不写入
                Branch=1;//PC可能改变
                ALUOP=12'b000000000000;//ALU无操作,输出第一个输入
                ALUSrc=0;//ALU输入2选择寄存器输出
                RegWrite=0;//寄存器不写入
                RegDst=0;//不写入随意
            end
            6'b000010://跳转
            begin
                MemtoReg=1;//输出随意
                MemWrite=0;//数据存储器不写入
                Branch=1;//PC可能改变
                ALUOP=12'b000000000000;//ALU无操作,输出第一个输入
                ALUSrc=0;//数据2选择寄存器输出
                RegWrite=0;//寄存器不写入
                RegDst=0;//不写入随意
            end
            default:
            begin
                MemtoReg=0;
                MemWrite=0;
                Branch=0;
                ALUOP = 12'b000000000000;//ALU无操作,输出第一个输入
                ALUSrc=0;
                RegWrite=1;
                RegDst=1;
            end
        endcase
    end
endmodule

寄存器堆:

采用之前的寄存器堆(短版)代码,没有初始化,不影响使用,需要的话加上就行

//
//
//创建日期:2022/10/16 21:37:00
//设计名称:寄存器堆
//课程名称:regfile
//说明:
// 实现 32 个寄存器, 其中 0 号寄存器读出的值恒为 0,
// 寄存器堆为异步读同步写, 
// 共有 1 个写端口和 2 个读端口
//依赖项:
//      
//版次:
//版本0.01-文件已创建
//其他注释:
//
 
module regfile(
input clk,  // 时钟
input wen,  // 写使能
input [4 :0] raddr1,    // 读地址1
input [4 :0] raddr2,    // 读地址2
input [4 :0] waddr,     // 写地址
input [31:0] wdata,     // 写数据
output reg [31:0] rdata1,   // 读到的数据1
output reg [31:0] rdata2,   // 读到的数据2
input [4 :0] test_addr,     // 测试读端口
output reg [31:0] test_data // 测试输出
); 
reg [31:0] rf[31:0];  // 定义32个32位的寄存器
always @(posedge clk) // 时钟上升沿
begin
    if (wen)    // 如果写使能wen为1则写入寄存器
        begin
           rf[waddr] <= wdata;
        end
end
 
//读端口 1
always @(*)
begin
    if (raddr1==5'd0)
        rdata1 <= 32'd0;
    else
        rdata1 <= rf[raddr1];
end
//读端口 2
always @(*)
begin
    if (raddr2==5'd0)
        rdata2 <= 32'd0;
    else
        rdata2 <= rf[raddr2];
end
//测试读端口
always @(*)
begin
    if (test_addr==5'd0)
        test_data <= 32'd0;
    else
        test_data <= rf[test_addr];
end
endmodule

ALU:

对照之前的ALU增加了比较相等的输出,用于PC的跳转,采用独热编码,相当于13种简易运算。

//
//创建日期:2022/11/6 20:06:00
//设计名称:ALU算术逻辑单元
//课程名称:alu
//说明: 
//输入:   [11:0] alu_control;  // ALU控制信号
//        [31:0] alu_src1;     // ALU操作数1
//        [31:0] alu_src2;     // ALU操作数2
//输出:   [31:0] alu_result;   // ALU结果
//          Equal  两个输入是否相等
//依赖项:
//      
//版次:
//版本0.01-文件已创建
//其他注释:
//
//
module alu(alu_control,alu_src1,alu_src2,alu_result,Equal);
    input  [11:0] alu_control;  // ALU控制信号
    input  [31:0] alu_src1;     // ALU操作数1
    input  [31:0] alu_src2;     // ALU操作数2
    output [31:0] alu_result;   // ALU结果
    output Equal;   //相等
    wire Equal;
    reg [31:0] alu_result;
    // 控制信号为独热编码
    assign Equal = alu_src1==alu_src2;
    always @(*)
    begin
        case(alu_control)   // 下面的1,2指操作数1,操作数2
            12'b000000000001:alu_result<=alu_src1<<16;         // 高位加载       1
            12'b000000000010:alu_result<=alu_src1>>>alu_src2;         // 算术右移       2
            12'b000000000100:alu_result<=alu_src1>>alu_src2; // 逻辑右移      4
            12'b000000001000:alu_result<=alu_src1<<alu_src2; // 逻辑左移      8
            12'b000000010000:alu_result<=alu_src1^alu_src2; // 按位异或    16
            12'b000000100000:alu_result<=alu_src1|alu_src2;// 按位或    32
            12'b000001000000:alu_result<=~(alu_src1|alu_src2); // 按位或非       64
            12'b000010000000:alu_result<=alu_src1&alu_src2; // 按位与       128
            12'b000100000000:alu_result<=alu_src1<alu_src2?32'd1:32'd0;// 无符号比较,小于置位  256
            12'b001000000000:alu_result<=$signed(alu_src1)<$signed(alu_src2)?32'd1:32'd0;// 有符号比较,小于置位  512
            12'b010000000000:alu_result<=alu_src1+alu_src2;// 1加     1024
            12'b100000000000:alu_result<=alu_src1-alu_src2;// 1减     2048
            default: alu_result<=alu_src1;
        endcase
    end
endmodule

数据存储器:

采用IP核实现:

没有测试,功能或许有问题

Verilog 简易单周期CPU

        Verilog 简易单周期CPU 

Verilog 简易单周期CPU Verilog 简易单周期CPU

指令存储器:

采用IP核实现:

Verilog 简易单周期CPU

 Verilog 简易单周期CPU

 Verilog 简易单周期CPU

 Verilog 简易单周期CPU

 第四张图的ROM.coe数据如下:

memory_initialization_radix=2;
memory_initialization_vector=
00100000000000010000000000001000
00100000000000100000000000000010
00100000000000110000000000000000
00000000010000110001100000100000
00010000001000110000000000000111
00001000000000000000000000000011

这是一段测试用的指令段,具体功能在tp文件种有注释

这个文件什么名字和位置都可以,后缀是.coe就行。

CPU:

//
//创建日期:2022/12/19 16:32:56
//设计名称:CPU
//课程名称:CPU
//说明: 
//调用各个部件,进行运算
//依赖项:
//      控制器,寄存器,ALU
//版次:
//版本0.01-文件已创建
//其他注释:

module CPU(clk);
    input clk;
    // PC
    reg [7:0] PC=8'd0;//PC从第0条指令开始
    wire[31:0] SignImm;//指令后16位扩展结果
    wire PCSrc;//是否跳转
    always@(posedge clk)//上升沿
    begin
        if (PCSrc == 0)
            PC = PC+1;
        else
            PC = SignImm[7:0];
	end
    // 指令存储器
    wire [31:0] instructions;//指令存储器输出
    ROM_D IROM(
        .a(PC),//地址
        .spo(instructions));//指令输出
    wire[5:0] op,func;//控制器输入
    wire[4:0] rs,rt,rd;//三个寄存器地址
    assign op = instructions[31:26];
    assign func = instructions[5:0];
    assign rs = instructions[25:21];
    assign rt = instructions[20:16];
    assign rd = instructions[15:11];
    assign SignImm = {{(16){instructions[15]}},instructions[15:0]};
    // 控制器
    wire MemtoReg,MemWrite,Branch,ALUSrc,RegWrite,RegDst;//控制器输出控制信号
    wire[11:0] ALUOP;//ALU所做的操作
    Controler Contr(op,func,MemtoReg,MemWrite,Branch,ALUOP,ALUSrc,RegWrite,RegDst);
    // 寄存器堆
    wire[31:0] R1,R2,WriteBackData;//寄存器输出和数据输入
    wire[4:0] reg_w;//寄存器写地址
    assign reg_w = RegDst?rd:rt;
    regfile regfile_(clk,RegWrite,rs,rt,reg_w,WriteBackData,R1,R2);
    // ALU
    wire[31:0] srcB,ALUResult;//ALU第二个数据输入和数据输出
    wire Equal;//输入是否相等
    assign srcB = ALUSrc?SignImm:R2;
    alu ALU(ALUOP,R1,srcB,ALUResult,Equal);
    assign PCSrc = Branch&Equal;
    // 数据存储器
    wire [31:0] ReadData;//数据存储器输出
    data_RAM DRM(
        .clka  (clk          ),
        .wea   (MemWrite      ),
        .addra (ALUResult[7:0]),
        .dina  (R2            ),
        .douta (ReadData      ));
    assign WriteBackData = MemWrite?ReadData:ALUResult;
endmodule

tp(仿真文件):

就一个clk和CPU的调用,所用指令段的注释。

`timescale 1ns / 1ps

//001000 00000 00001 0000000000001000       第0个寄存器和8相加存入第1个寄存器
//001000 00000 00010 0000000000000010       第0个寄存器和2相加存入第2个寄存器
//001000 00000 00011 0000000000000000       第0个寄存器和0相加存入第3个寄存器
//000000 00010 00011 00011 00000 100000     第3个寄存器和第2个寄存器相加,结果存入第3个寄存器
//000100 00001 00011 0000000000000111       第1个寄存器和第3个相等转移到7
//000010 00000 00000 0000000000000011       转移到3
//相当于以下程序:
// reg[1] = 8
// reg[2] = 2
// reg[3] = 0
//M: reg[3] = reg[3]+reg[2]
// if reg[1] == reg[3]: goto N
// goto M
//N:
module tp;
    reg clk=0;
    CPU cpu_(clk);
    always #10 clk = ~clk;
endmodule

 仿真结果:

Verilog 简易单周期CPU

Verilog 简易单周期CPU 

Verilog 简易单周期CPU 

 Verilog 简易单周期CPU

 Verilog 简易单周期CPU

 单周期CPU压缩包下载

开了动态调分,初始积分是0.