当前位置:首页 » 《随便一记》 » 正文

Verilog 简易单周期CPU

18 人参与  2022年12月21日 13:05  分类 : 《随便一记》  评论

点击全文阅读


目录

 本实验包含:

 简易结构图:

各部件代码或实现:

控制器:

寄存器堆:

ALU:

数据存储器:

指令存储器:

CPU:

tp(仿真文件):

 仿真结果:

 单周期CPU压缩包下载


 本实验包含:

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

 简易结构图:

各部件代码或实现:

控制器:

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

R:
 指令  [31:26]   [25:21]   [20:16]  [15:11] [10:6]  [5:0] 功能
add000000rsrtrd000000100000寄存器加
sub000000rsrtrd000000100010寄存器减
and000000rsrtrd000000100100寄存器与
or000000rsrtrd000000100101寄存器或
nor000000rsrtrd000000100111寄存器或非
sll000000rs000000rdsa000000逻辑左移
srl000000rs000000rdsa000010 逻辑右移
sra000000rs000000rdsa100111算术右移
I:
指令 [31:26] [25:21] [20:16] [15:0] 功能
addi001000rsrtimmediate立即数加
lw100011rsrtimmediate取字数据
sw101011rsrtimmediate存字数据
beq000100rsrtimmediate相等转移
J:
指令[31:26][25:21][20:16][15;0]功能
j0000100000000000immediate转移

输入: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]功能    //    add000000   rs rt       rd000000100000寄存器加    //    sub000000   rs rt       rd000000100010寄存器减    //    and000000   rs rt       rd000000100100寄存器与    //    or000000   rs rt       rd000000100101寄存器或    //    nor000000   rs rt       rd000000100111寄存器或非    //    sll000000   rs  000000   rd  sa000000逻辑左移    //    srl000000   rs  000000   rd  sa000010逻辑右移    //    sra000000   rs  000000   rd  sa000011算术右移    //I:    //    指令[31:26][25:21][20:16][15:0]功能    //    addi001000  rsrt    immediate立即数加    //    lw100011  rsrt  immediate取字数据    //    sw101011  rsrt  immediate存字数据    //    beq000100  rsrt  immediate相等转移    //J:    //    指令[31:26] [25:21] [20:16]  [15:0]     功能    //    j 0000100000000000 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    endendmodule

寄存器堆:

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

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

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    endendmodule

数据存储器:

采用IP核实现:

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

         

 

指令存储器:

采用IP核实现:

 

 

 

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

memory_initialization_radix=2;memory_initialization_vector=001000000000000100000000000010000010000000000010000000000000001000100000000000110000000000000000000000000100001100011000001000000001000000100011000000000000011100001000000000000000000000000011

这是一段测试用的指令段,具体功能在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

 仿真结果:

 

 

 

 

 单周期CPU压缩包下载

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


点击全文阅读


本文链接:http://zhangshiyu.com/post/50110.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1