小言_互联网的博客

Verilog 简易单周期CPU

340人阅读  评论(0)

目录

 本实验包含:

 简易结构图:

各部件代码或实现:

控制器:

寄存器堆:

ALU:

数据存储器:

指令存储器:

CPU:

tp(仿真文件):

 仿真结果:

 单周期CPU压缩包下载


 本实验包含:

        指令存储器和数据存储器的ip核调用,控制器,寄存器堆,ALU,单周期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


  
  1. //
  2. //创建日期:2022/12/19 10:46:36
  3. //设计名称:控制器
  4. //课程名称:Controler
  5. //说明:
  6. //输入:op,func
  7. //输出:MemtoReg,MemWrite,Branch,ALUOP,ALUSrc,RegWrite,RegDst
  8. //依赖项:
  9. //
  10. //版次:
  11. //版本0.01-文件已创建
  12. //其他注释:
  13. //
  14. //
  15. module Controler(op,func,MemtoReg,MemWrite,Branch,ALUOP,ALUSrc,RegWrite,RegDst);
  16. input [ 5: 0] op;
  17. input [ 5: 0] func;
  18. output MemtoReg;
  19. output MemWrite;
  20. output Branch;
  21. output [ 11: 0] ALUOP;
  22. output ALUSrc;
  23. output RegWrite;
  24. output RegDst;
  25. reg MemtoReg,MemWrite,Branch,ALUSrc,RegWrite,RegDst;
  26. reg [ 11: 0] ALUOP;
  27. // R:
  28. // 指令 [31:26] [25:21] [20:16] [15:11] [10:6] [5:0] 功能
  29. // add 000000 rs rt rd 000000 100000 寄存器加
  30. // sub 000000 rs rt rd 000000 100010 寄存器减
  31. // and 000000 rs rt rd 000000 100100 寄存器与
  32. // or 000000 rs rt rd 000000 100101 寄存器或
  33. // nor 000000 rs rt rd 000000 100111 寄存器或非
  34. // sll 000000 rs 000000 rd sa 000000 逻辑左移
  35. // srl 000000 rs 000000 rd sa 000010 逻辑右移
  36. // sra 000000 rs 000000 rd sa 000011 算术右移
  37. //I:
  38. // 指令 [31:26] [25:21] [20:16] [15:0] 功能
  39. // addi 001000 rs rt immediate 立即数加
  40. // lw 100011 rs rt immediate 取字数据
  41. // sw 101011 rs rt immediate 存字数据
  42. // beq 000100 rs rt immediate 相等转移
  43. //J:
  44. // 指令 [31:26] [25:21] [20:16] [15:0] 功能
  45. // j 000010 00000 00000 immediate 转移
  46. always @(*)
  47. begin
  48. case(op)
  49. 6 'b000000: //寄存器操作
  50. begin
  51. MemtoReg= 0; //输出ALU的输出
  52. MemWrite= 0; //数据存储器不写入
  53. Branch= 0; //正常PC
  54. ALUSrc= 0; //ALU输入2选择寄存器输出
  55. RegWrite= 1; //寄存器写入
  56. RegDst= 1; //有rd
  57. case(func) //控制ALU操作
  58. 6 'b100000: // 寄存器加
  59. ALUOP= 12 'b010000000000;
  60. 6 'b100010: // 寄存器减
  61. ALUOP= 12 'b100000000000;
  62. 6 'b100100: // 寄存器与
  63. ALUOP= 12 'b000010000000;
  64. 6 'b100101: // 寄存器或
  65. ALUOP= 12 'b000000100000;
  66. 6 'b100111: // 寄存器或非
  67. ALUOP= 12 'b000001000000;
  68. 6 'b100100: // 逻辑左移
  69. ALUOP= 12 'b000000001000;
  70. 6 'b100101: // 逻辑右移
  71. ALUOP= 12 'b000000000100;
  72. 6 'b100111: // 算术右移
  73. ALUOP= 12 'b000000000010;
  74. default:ALUOP= 12 'b010000000000;
  75. endcase
  76. end
  77. 6 'b001000: // 立即数加
  78. begin
  79. MemtoReg= 0; //输出ALU结果
  80. MemWrite= 0; //数据存储器不写入
  81. Branch= 0; //正常PC
  82. ALUOP= 12 'b010000000000; //ALU加操作
  83. ALUSrc= 1; //数据2选择立即数输出
  84. RegWrite= 1; //寄存器写入
  85. RegDst= 0; //无rd选择rt
  86. end
  87. 6 'b100011: // 取字数据
  88. begin
  89. MemtoReg= 1; //输出数据存储器结果
  90. MemWrite= 0; //数据存储器不写入
  91. Branch= 0; //正常PC
  92. ALUOP= 12 'b000000000000; //ALU无操作,输出第一个输入
  93. ALUSrc= 1; //数据2随意
  94. RegWrite= 1; //寄存器写入
  95. RegDst= 0; //无rd选择rt
  96. end
  97. 6 'b101011: // 存字数据
  98. begin
  99. MemtoReg= 1; //输出随意
  100. MemWrite= 1; //数据存储器写入
  101. Branch= 0; //正常PC
  102. ALUOP= 12 'b000000000000; //ALU无操作,输出第一个输入
  103. ALUSrc= 1; //数据2随意
  104. RegWrite= 0; //寄存器不写入
  105. RegDst= 0; //不写入随意
  106. end
  107. 6 'b000100: // 相等转移
  108. begin
  109. MemtoReg= 1; //输出随意
  110. MemWrite= 0; //数据存储器不写入
  111. Branch= 1; //PC可能改变
  112. ALUOP= 12 'b000000000000; //ALU无操作,输出第一个输入
  113. ALUSrc= 0; //ALU输入2选择寄存器输出
  114. RegWrite= 0; //寄存器不写入
  115. RegDst= 0; //不写入随意
  116. end
  117. 6 'b000010: //跳转
  118. begin
  119. MemtoReg= 1; //输出随意
  120. MemWrite= 0; //数据存储器不写入
  121. Branch= 1; //PC可能改变
  122. ALUOP= 12 'b000000000000; //ALU无操作,输出第一个输入
  123. ALUSrc= 0; //数据2选择寄存器输出
  124. RegWrite= 0; //寄存器不写入
  125. RegDst= 0; //不写入随意
  126. end
  127. default:
  128. begin
  129. MemtoReg= 0;
  130. MemWrite= 0;
  131. Branch= 0;
  132. ALUOP = 12 'b000000000000; //ALU无操作,输出第一个输入
  133. ALUSrc= 0;
  134. RegWrite= 1;
  135. RegDst= 1;
  136. end
  137. endcase
  138. end
  139. endmodule

寄存器堆:

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


  
  1. //
  2. //
  3. //创建日期:2022/10/16 21:37:00
  4. //设计名称:寄存器堆
  5. //课程名称:regfile
  6. //说明:
  7. // 实现 32 个寄存器, 其中 0 号寄存器读出的值恒为 0,
  8. // 寄存器堆为异步读同步写,
  9. // 共有 1 个写端口和 2 个读端口
  10. //依赖项:
  11. //
  12. //版次:
  13. //版本0.01-文件已创建
  14. //其他注释:
  15. //
  16. module regfile(
  17. input clk, // 时钟
  18. input wen, // 写使能
  19. input [ 4 : 0] raddr1, // 读地址1
  20. input [ 4 : 0] raddr2, // 读地址2
  21. input [ 4 : 0] waddr, // 写地址
  22. input [ 31: 0] wdata, // 写数据
  23. output reg [ 31: 0] rdata1, // 读到的数据1
  24. output reg [ 31: 0] rdata2, // 读到的数据2
  25. input [ 4 : 0] test_addr, // 测试读端口
  26. output reg [ 31: 0] test_data // 测试输出
  27. );
  28. reg [ 31: 0] rf[ 31: 0]; // 定义32个32位的寄存器
  29. always @(posedge clk) // 时钟上升沿
  30. begin
  31. if (wen) // 如果写使能wen为1则写入寄存器
  32. begin
  33. rf[waddr] <= wdata;
  34. end
  35. end
  36. //读端口 1
  37. always @ (*)
  38. begin
  39. if (raddr1==5'd0)
  40. rdata1 <= 32'd0;
  41. else
  42. rdata1 <= rf[raddr1];
  43. end
  44. //读端口 2
  45. always @(*)
  46. begin
  47. if (raddr2== 5 'd0)
  48. rdata2 <= 32'd0;
  49. else
  50. rdata2 <= rf[raddr2];
  51. end
  52. //测试读端口
  53. always @ (*)
  54. begin
  55. if (test_addr==5'd0)
  56. test_data <= 32'd0;
  57. else
  58. test_data <= rf[test_addr];
  59. end
  60. endmodule

ALU:

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


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

数据存储器:

采用IP核实现:

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

         

 

指令存储器:

采用IP核实现:

 

 

 

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


  
  1. memory_initialization_radix=2;
  2. memory_initialization_vector=
  3. 00100000000000010000000000001000
  4. 00100000000000100000000000000010
  5. 00100000000000110000000000000000
  6. 00000000010000110001100000100000
  7. 00010000001000110000000000000111
  8. 00001000000000000000000000000011

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

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

CPU:


  
  1. //
  2. //创建日期:2022/12/19 16:32:56
  3. //设计名称:CPU
  4. //课程名称:CPU
  5. //说明:
  6. //调用各个部件,进行运算
  7. //依赖项:
  8. // 控制器,寄存器,ALU
  9. //版次:
  10. //版本0.01-文件已创建
  11. //其他注释:
  12. module CPU(clk);
  13. input clk;
  14. // PC
  15. reg [ 7: 0] PC= 8'd0; //PC从第0条指令开始
  16. wire[ 31: 0] SignImm; //指令后16位扩展结果
  17. wire PCSrc; //是否跳转
  18. always@(posedge clk) //上升沿
  19. begin
  20. if (PCSrc == 0)
  21. PC = PC+ 1;
  22. else
  23. PC = SignImm[ 7: 0];
  24. end
  25. // 指令存储器
  26. wire [ 31: 0] instructions; //指令存储器输出
  27. ROM_D IROM(
  28. .a(PC),//地址
  29. .spo(instructions)); //指令输出
  30. wire[ 5: 0] op,func; //控制器输入
  31. wire[ 4: 0] rs,rt,rd; //三个寄存器地址
  32. assign op = instructions[ 31: 26];
  33. assign func = instructions[ 5: 0];
  34. assign rs = instructions[ 25: 21];
  35. assign rt = instructions[ 20: 16];
  36. assign rd = instructions[ 15: 11];
  37. assign SignImm = {{( 16){instructions[ 15]}},instructions[ 15: 0]};
  38. // 控制器
  39. wire MemtoReg,MemWrite,Branch,ALUSrc,RegWrite,RegDst; //控制器输出控制信号
  40. wire[ 11: 0] ALUOP; //ALU所做的操作
  41. Controler Contr(op,func,MemtoReg,MemWrite,Branch,ALUOP,ALUSrc,RegWrite,RegDst);
  42. // 寄存器堆
  43. wire[ 31: 0] R1,R2,WriteBackData; //寄存器输出和数据输入
  44. wire[ 4: 0] reg_w; //寄存器写地址
  45. assign reg_w = RegDst?rd:rt;
  46. regfile regfile_(clk,RegWrite,rs,rt,reg_w,WriteBackData,R1,R2);
  47. // ALU
  48. wire[ 31: 0] srcB,ALUResult; //ALU第二个数据输入和数据输出
  49. wire Equal; //输入是否相等
  50. assign srcB = ALUSrc?SignImm:R2;
  51. alu ALU(ALUOP,R1,srcB,ALUResult,Equal);
  52. assign PCSrc = Branch&Equal;
  53. // 数据存储器
  54. wire [ 31: 0] ReadData; //数据存储器输出
  55. data_RAM DRM(
  56. .clka (clk ),
  57. .wea (MemWrite ),
  58. .addra (ALUResult[7:0]),
  59. .dina (R2 ),
  60. .douta (ReadData ));
  61. assign WriteBackData = MemWrite?ReadData:ALUResult;
  62. endmodule

tp(仿真文件):

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


  
  1. `timescale 1ns / 1ps
  2. //001000 00000 00001 0000000000001000 第0个寄存器和8相加存入第1个寄存器
  3. //001000 00000 00010 0000000000000010 第0个寄存器和2相加存入第2个寄存器
  4. //001000 00000 00011 0000000000000000 第0个寄存器和0相加存入第3个寄存器
  5. //000000 00010 00011 00011 00000 100000 第3个寄存器和第2个寄存器相加,结果存入第3个寄存器
  6. //000100 00001 00011 0000000000000111 第1个寄存器和第3个相等转移到7
  7. //000010 00000 00000 0000000000000011 转移到3
  8. //相当于以下程序:
  9. // reg[1] = 8
  10. // reg[2] = 2
  11. // reg[3] = 0
  12. //M: reg[3] = reg[3]+reg[2]
  13. // if reg[1] == reg[3]: goto N
  14. // goto M
  15. //N:
  16. module tp;
  17. reg clk= 0;
  18. CPU cpu_(clk);
  19. always #10 clk = ~clk;
  20. endmodule

 仿真结果:

 

 

 

 

 单周期CPU压缩包下载

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


转载:https://blog.csdn.net/weixin_58196051/article/details/128380444
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场