当前位置:   article > 正文

MIPS指令集单周期CPU兼Verilog学习_cpu单周期指令

cpu单周期指令

1.单周期CPU原理(单个时钟周期内的操作):

        (1)取指,PC+4

        (2)译码

        (3)取操作数,ALU运算

        (4)访存(MEM)

        (5)写回(RegWr)

        将每一级操作抽象为CPU中的若干个模块:

                (1)指令读取模块(指令存储器)

                (2)指令寄存器(IR)

                (3)数据寄存器(rs,rt,rd)

                (4)逻辑运算器件(ALU

                (5)数据存储器

                (6)控制单元

2.实验要求

        MIPS指令集三种指令:

        R型指令

                汇编代码格式:op rd,rs,rt

                机器中存储:

                

                含义:[rs]+[rt]  \rightarrow [rd]

         I型指令:

                汇编代码格式:op rt,rs,imm16

                机器中存储:

                

                含义:[rs]+imm16 \rightarrow [rt] 

        J型指令:  

                汇编代码格式:op imm26

                机器中存储:

                

                需要注意的是,这里的跳转地址为: PC高四位+imm26+00   ,构成了32的地址

                来看下MIPS指令集:

        来看一下整体的数据通路:


3.代码实现        

         看上去非常复杂,我们把每个元件单拎出来看输入和输出:

            1)符号扩展单元Extender

                   符号扩展单元看起来比较简单,它的作用:

                            (1)将imm16符号扩展至32位后送至ALU

                               

                   ExtOp=\left\{\begin{matrix} 0 &,ZeroExt& \\ 1&,SignExt \end{matrix}\right.

              input1:[15:0] imm16,           input2:ExtOp

              output:[31:0] imm32

        扩展器模块代码如下(extender.v)         

  1. module extender(
  2. input [15:0] imm16,
  3. input ExtOp,
  4. output [31:0] imm32
  5. );
  6. //ExtOp 为 0 做零扩展,为 1 做符号扩展
  7. //imm16是以补码储存在机器中的
  8. assign imm32 = {imm16[15],{16{1'b0}},imm16[14:0]};
  9. endmodule

     进行一下简单的仿真(extender_tb.v)

  1. module extender_tb();
  2. reg [15:0] imm16;
  3. reg ExtOp;
  4. wire [31:0] imm32;
  5. initial begin
  6. imm16 = 16'b1111111111111111;
  7. ExtOp = 1;
  8. end
  9. extender test(imm16,ExtOp,imm32);
  10. endmodule

          11...11经过符号扩展后变成了10...0011...111,观察仿真波形:

 扩展器完成!

           2)pc模块

                  pc的输入输出:input1:pc_new(经过+4的pc 或者 跳转地址)

                                            input2:clk(在板子上是手动的按键/上升沿触发)

                                            input3:reset(pc复位信号,由板子上的SW0给出)

                                            output1:pc_out(输出到InsMem中)

                                            output2:pc_plus(经过+4的pc,这样可以节省一个专门pc+4器件)  

                   pc模块参考代码(pc.v): 注:这里的pc_out设置一个初始值0

  1. `timescale 1ns / 1ps
  2. module pc(
  3. input [31:0] pc_in,
  4. input clk,
  5. input reset,
  6. output reg[31:0] pc_out, //输出到mem进行取指
  7. output [31:0] pc_plus //经过+4的pc
  8. );
  9. initial begin
  10. pc_out = 0;
  11. end
  12. always @(posedge clk) begin
  13. if(reset == 0) begin
  14. pc_out=pc_in;
  15. end
  16. else begin
  17. pc_out=0;
  18. end
  19. end
  20. assign pc_plus = pc_out + 4;
  21. endmodule

         pc模块测试用的仿真文件(pc_tb.v):

  1. module pc_tb();
  2. reg clk;
  3. reg[31:0] pc_in;
  4. reg reset;
  5. wire[31:0] pc_out;
  6. wire[31:0] pc_plus;
  7. initial begin
  8. clk=0;
  9. pc_in=0;
  10. reset=0;
  11. end
  12. always @(*) begin
  13. #5 clk <= ~clk;
  14. end
  15. pc pc_test(pc_in,clk,reset,pc_out,pc_plus);
  16. endmodule

               pc和自增模块完成!

       3)指令存储器模块(InsMem)

                功能:根据输入的pc,在存储器中找到pc所指向的那条指令后,将其发送给各个部件。

                输入:input1:pc

                           output:经过拆解过的指令

                注:(1)存储器的指令写入要在模块中实现而不是在仿真中实现

                       (2)将存储器设计为32位的寄存器组而不是8位的寄存器组有一个好处:当读入16进制的文件时,8位的寄存器会两个两个数据读,因此在读入单条(正常顺序)                            的指令时,顺序会错乱。比如:

                           

                         这时8位寄存器组的存储内容会为:0111_1000_0101_0110_0011_0100_ 0001_0010

                         这样做指令分割的时候会非常麻烦。

                下面来看下InsMem模块的代码(InsMem.v):

  1. module InsMem(
  2. input [31:0]addr, //即pc值
  3. output [5:0]op, //31:26
  4. output [4:0]rs, //25:21
  5. output [4:0]rt, //20:16
  6. output [4:0]rd, //15:11
  7. output [4:0]shamt, //10:6
  8. output [5:0]func //5:0
  9. );
  10. reg [31:0] mem [63:0]; //最多可存64条指令
  11. initial begin
  12. $readmemh("D:/meiyong/testdata.txt",mem);
  13. end
  14. assign op = mem[addr >> 2][31:26]; //这里用的是32位的指令寄存器组,因此pc要除以4
  15. assign rs = mem[addr >> 2][25:21];
  16. assign rt = mem[addr >> 2][20:16];
  17. assign rd = mem[addr >> 2][15:11];
  18. assign shamt = mem[addr >> 2][10:6];
  19. assign func = mem[addr >> 2][5:0];
  20. endmodule

                写个仿真测试一下(test_insmem_tb.v):

  1. module test_insmem_tb( );
  2. reg [31:0] addr;
  3. wire [5:0]op; //31:26
  4. wire [4:0]rs; //25:21
  5. wire [4:0]rt; //20:16
  6. wire [4:0]rd; //15:11
  7. wire [4:0]shamt; //10:6
  8. wire [5:0]func;
  9. InsMem tset_insmem(addr,op,rs,rt,rd,shamt,func);
  10. initial begin
  11. addr = 0;
  12. end
  13. endmodule

                  检验一下输出波形:

                                                                                      

               注:第一条指令是12345678

        4)数据存储器模块(dataMem)

                数据存储器也跟指令存储器一样采用32位的寄存器组来实现。

                输入:访问/写入地址,写入的数据,写使能信号Wr。

                输出:读出的数据

                数据存储器模块代码(dataMem.v):

  1. `timescale 1ns / 1ps
  2. module dataMem(
  3. input clk,
  4. input [31:0] dataAdd,
  5. input [31:0] dataIn,
  6. input dataWr,
  7. output reg[31:0] dataOut
  8. );
  9. integer i;
  10. reg [31:0] mem [511:0];
  11. initial begin
  12. for(i=0;i<512;i=i+1) begin
  13. mem[i] <= 0;
  14. end
  15. end
  16. always @(posedge clk) begin
  17. if(dataWr == 1) begin
  18. mem[dataAdd>>2] <= dataIn;
  19. end
  20. end
  21. always @(*) begin
  22. dataOut <= mem[dataAdd>>2];
  23. end
  24. endmodule

        

          5)数据寄存器组模块(Register)

                寄存器组内有32个32位的通用寄存器,通过rs、rt、rd来指定访问的是哪个寄存器。

                输入:input1:clk        input2:rs,rt,rd        input3:distSel(写哪个寄存器,rt还是rd)        input4:RegWr        input5:将写入rd的dataIn

                输出:两条线out1和out2

                数据寄存器模块源代码(Register.v):

  1. module Register(
  2. input clk,
  3. input [4:0]rs,
  4. input [4:0]rt,
  5. input [4:0]rd,
  6. input distSel, //写入rt还是rd,0写rt,1rd
  7. input RegWr,
  8. input [31:0]dataIn,
  9. output reg[31:0]out1,
  10. output reg[31:0]out2
  11. );
  12. reg [31:0] Reg[31:0]; //32个通用寄存器
  13. integer i;
  14. //对寄存器初始化
  15. initial begin
  16. for (i = 0; i < 32; i = i+ 1) Reg[i] <= 0;
  17. end
  18. always @(posedge clk) begin
  19. if(RegWr == 1) begin
  20. case(distSel)
  21. 0: Reg[rt] = dataIn; //写rt
  22. 1: Reg[rd] = dataIn; //rd
  23. endcase
  24. end
  25. end
  26. always @(*) begin
  27. out1 <= Reg[rs];
  28. out2 <= Reg[rt];
  29. end
  30. endmodule

                测试用的仿真代码(Reg_test_tb.v): //这里把Register中Reg的初始化改成了 Reg[i] = i ;

  1. module Reg_test_tb( );
  2. reg clk;
  3. reg [4:0]rs;
  4. reg [4:0]rt;
  5. reg [4:0]rd;
  6. reg distSel;
  7. reg RegWr;
  8. reg [31:0]dataIn;
  9. wire [31:0]out1;
  10. wire [31:0]out2;
  11. Register myReg(clk,rs,rt,rd,distSel,RegWr,dataIn,out1,out2);
  12. initial begin
  13. clk=0;
  14. rs=5'b00101;
  15. rt=5'b00110;
  16. rd=5'b00111;
  17. distSel=1;
  18. RegWr=0;
  19. dataIn=0;
  20. end
  21. always @(*) begin
  22. #5 clk <= ~clk;
  23. end
  24. endmodule

                out1和out2的波形如下:

         6)运算单元模块(ALU)

                这是和控制单元一样最为复杂的模块,先来看看需要实现多少个函数:

         虽然有20(21)个函数,但是总结起来,ALU部件要用的也就9种操作,因此ALUop码只需要四位(0000~1000)。

ALUop功能涉及到的指令

0000

两数相加add,addi
0001两数相减sub,subi
0010按位与and,andi
0011按位或or,ori
0100按位异或xor,xori
0101逻辑右移(直接移)srl
0110逻辑左移(直接移)sll
0111算术右移(补符号位)sra
1000比较是否相等beq,bne
1001设置高位lui

               注意区分算术移位和逻辑移位。

                逻辑移位:直接移位,空的地方补0。

                算术右移:空出的位全部补符号位。

                运算器模块代码(ALU.v):

  1. `timescale 1ns / 1ps
  2. module ALU(
  3. input [3:0]ALUop,
  4. input [31:0]in1, //可能是rs或者shamt
  5. input [31:0]in2, //可能是rt或者imm16
  6. output reg zero, //用来指示beq是否相等,1相等,0不相等
  7. output reg[31:0] res
  8. );
  9. integer i;
  10. always @(*) begin
  11. case (ALUop)
  12. //求和
  13. 4'b0000: begin
  14. res = in1 + in2;
  15. zero = 0;
  16. end
  17. //求差
  18. 4'b0001: begin
  19. res = in1 - in2;
  20. end
  21. //按位与
  22. 4'b0010: begin
  23. res = in1 & in2;
  24. end
  25. //按位或
  26. 4'b0011: begin
  27. res = in1 | in2;
  28. end
  29. //按位异或
  30. 4'b0100: begin
  31. res = in1 ^ in2;
  32. end
  33. //直接右移,in1是shamt,in2是rt
  34. 4'b0101: begin
  35. res = in2 >> in1[4:0];
  36. end
  37. //直接左移
  38. 4'b0110:begin
  39. res = in2 << in1[4:0];
  40. end
  41. //补符号位右移
  42. 4'b0111: begin
  43. res = in2 >> in1[4:0];
  44. if(in2[31]==1'b1) begin
  45. for(i=0;i<in1;i=i+1) res[31-i] = 1;
  46. end
  47. else begin
  48. for(i=0;i<in1;i=i+1) res[31-i] = 0;
  49. end
  50. end
  51. //判断相等
  52. 4'b1000: begin
  53. zero = in1==in2 ? 1'b1 : 1'b0;
  54. end
  55. //lui指令:设置rt的高16
  56. 4'b1001: begin
  57. res = {in2[31],in2[14:0],{16{0}}};
  58. end
  59. endcase
  60. end
  61. endmodule

                测试一下算术右移功能(ALU_test_tb.v):

  1. module ALU_test_tb();
  2. reg[3:0] ALUop;
  3. reg[31:0] in1;
  4. reg[31:0] in2;
  5. wire zero;
  6. wire[31:0] res;
  7. initial begin
  8. ALUop = 4'b0111;
  9. in1 = 5'b00100;
  10. in2 = 32'hf1234567;
  11. end
  12. ALU testALU(ALUop,in1,in2,zero,res);
  13. endmodule

      7)pc选择(PCselect)

               该模块用一个四选一的数据选择器来实现,输出的是pc的下一个值,即下一步要运行指的令的地址

               输入的四个信号分别为:

序号功能所服务的指令
0正常进行下一条指令正常指令

1

beq或bne指令生效所跳转到的指令地址beq,bne
2无条件跳转j
3跳转到rs内的值jr

                jr指令:jr rs        含义:rs寄存器中存有下条指令的地址,pc改为rs内存的值。

                PC选择器模块代码(PCselect.v):

  1. `timescale 1ns / 1ps
  2. //本模块用来选择最后的pc
  3. module PCselect(
  4. input [31:0]addedPC, //加过4的PC
  5. input [31:0]ex_imm32,
  6. input [25:0]jAddr,
  7. input [31:0]jr_rs,
  8. input [1:0]sel,
  9. output reg[31:0]finalPC
  10. );
  11. always@(*) begin
  12. case (sel)
  13. 0: finalPC <= addedPC; //正常执行下一条指令
  14. 1: finalPC <= addedPC+(ex_imm32<<2); //beq或者bne跳转指令生效
  15. 2: finalPC <= {addedPC[31:28],jAddr[25:0],0,0}; //无条件跳转指令
  16. 3: finalPC <= jr_rs;
  17. endcase
  18. end
  19. endmodule

      8)控制单元(control)

                输入:指令的op和funct码,ALU的zero(判断rs、rt,0不相等,1相等)

                输出:(1)PC的reset         (2)PCsel的选择信号,选择下条指令地址                                                                                                                                                                                     (3)ALU要进行的操作ALUop         (4)reg的写信号RegWr;选择哪个寄存器reg_distSel;进入寄存器的是ALUresult还是从数据存储器来的信号:regIn_sel                                 (5)ALU的两个输入ALUin1_sel, ALUin2_sel:第一个选rs还是shamt , 第二个选rt还是imm32.                                                                                                                               (6)dataMem的写信号memWr.

                控制模块源代码(control.v):

  1. `timescale 1ns / 1ps
  2. module control(
  3. input [5:0] op,
  4. input [5:0] funct,
  5. input zero,
  6. // input PC_reset,
  7. output reg[3:0]ALUop,
  8. output reg[1:0]PCsel,
  9. output reg reg_distSel,
  10. output reg regWr,
  11. output reg dataWr,
  12. output reg ALUin1_sel, //选rs还是shamt,0是rs,1是shamt
  13. output reg ALUin2_sel, //选rt还是imm,0是rt,1是imm
  14. output reg regIn_sel //选ALUout还是dataMem_out,0是ALUout,1是dataMem_out
  15. );
  16. always @(*) begin
  17. case(op)
  18. //r型指令
  19. 6'b000000:begin
  20. case(funct)
  21. //add
  22. 6'b100000:begin
  23. // PC_reset <= 0;
  24. ALUop <= 4'b0000;
  25. PCsel <= 2'b00;
  26. reg_distSel <= 1;
  27. regWr <= 1;
  28. dataWr <= 0;
  29. ALUin1_sel <= 0;
  30. ALUin2_sel <= 0;
  31. regIn_sel <= 0;
  32. end
  33. //sub
  34. 6'b100010:begin
  35. // PC_reset <= 0;
  36. ALUop <= 4'b0001;
  37. PCsel <= 2'b00;
  38. reg_distSel <= 1;
  39. regWr <= 1;
  40. dataWr <= 0;
  41. ALUin1_sel <= 0;
  42. ALUin2_sel <= 0;
  43. regIn_sel <= 0;
  44. end
  45. //and
  46. 6'b100100:begin
  47. // PC_reset <= 0;
  48. ALUop <= 4'b0010;
  49. PCsel <= 2'b00;
  50. reg_distSel <= 1;
  51. regWr <= 1;
  52. dataWr <= 0;
  53. ALUin1_sel <= 0;
  54. ALUin2_sel <= 0;
  55. regIn_sel <= 0;
  56. end
  57. //or
  58. 6'b100101:begin
  59. // PC_reset <= 0;
  60. ALUop <= 4'b0011;
  61. PCsel <= 2'b00;
  62. reg_distSel <= 1;
  63. regWr <= 1;
  64. dataWr <= 0;
  65. ALUin1_sel <= 0;
  66. ALUin2_sel <= 0;
  67. regIn_sel <= 0;
  68. end
  69. //xor
  70. 6'b100110:begin
  71. // PC_reset <= 0;
  72. ALUop <= 4'b0100;
  73. PCsel <= 2'b00;
  74. reg_distSel <= 1;
  75. regWr <= 1;
  76. dataWr <= 0;
  77. ALUin1_sel <= 0;
  78. ALUin2_sel <= 0;
  79. regIn_sel <= 0;
  80. end
  81. //sll
  82. 6'b000000:begin
  83. // PC_reset <= 0;
  84. ALUop <= 4'b0110;
  85. PCsel <= 2'b00;
  86. reg_distSel <= 1;
  87. regWr <= 1;
  88. dataWr <= 0;
  89. ALUin1_sel <= 1;
  90. ALUin2_sel <= 0;
  91. regIn_sel <= 0;
  92. end
  93. //srl
  94. 6'b000010:begin
  95. // PC_reset <= 0;
  96. ALUop <= 4'b0101;
  97. PCsel <= 2'b00;
  98. reg_distSel <= 1;
  99. regWr <= 1;
  100. dataWr <= 0;
  101. ALUin1_sel <= 1;
  102. ALUin2_sel <= 0;
  103. regIn_sel <= 0;
  104. end
  105. //sra
  106. 6'b000011:begin
  107. // PC_reset <= 0;
  108. ALUop <= 4'b0111;
  109. PCsel <= 2'b00;
  110. reg_distSel <= 1;
  111. regWr <= 1;
  112. dataWr <= 0;
  113. ALUin1_sel <= 1;
  114. ALUin2_sel <= 0;
  115. regIn_sel <= 0;
  116. end
  117. //jr
  118. 6'b001000:begin
  119. // PC_reset <= 0;
  120. ALUop <= 4'b0111; //无所谓
  121. PCsel <= 2'b11; //PC选择rs的内容
  122. reg_distSel <= 0;
  123. regWr <= 0;
  124. dataWr <= 0;
  125. ALUin1_sel <= 0;
  126. ALUin2_sel <= 0;
  127. regIn_sel <= 0;
  128. end
  129. endcase
  130. end
  131. //下面是I型指令
  132. //addi
  133. 6'b001000:begin
  134. // PC_reset <= 0;
  135. ALUop <= 4'b0000;
  136. PCsel <= 2'b00;
  137. reg_distSel <= 0;
  138. regWr <= 1;
  139. dataWr <= 0;
  140. ALUin1_sel <= 0;
  141. ALUin2_sel <= 1;
  142. regIn_sel <= 0;
  143. end
  144. //andi
  145. 6'b001100:begin
  146. // PC_reset <= 0;
  147. ALUop <= 4'b0001;
  148. PCsel <= 2'b00;
  149. reg_distSel <= 0;
  150. regWr <= 1;
  151. dataWr <= 0;
  152. ALUin1_sel <= 0;
  153. ALUin2_sel <= 1;
  154. regIn_sel <= 0;
  155. end
  156. //ori
  157. 6'b001101:begin
  158. // PC_reset <= 0;
  159. ALUop <= 4'b0011;
  160. PCsel <= 2'b00;
  161. reg_distSel <= 0;
  162. regWr <= 1;
  163. dataWr <= 0;
  164. ALUin1_sel <= 0;
  165. ALUin2_sel <= 1;
  166. regIn_sel <= 0;
  167. end
  168. //xori
  169. 6'b001110:begin
  170. // PC_reset <= 0;
  171. ALUop <= 4'b0100;
  172. PCsel <= 2'b00;
  173. reg_distSel <= 0;
  174. regWr <= 1;
  175. dataWr <= 0;
  176. ALUin1_sel <= 0;
  177. ALUin2_sel <= 1;
  178. regIn_sel <= 0;
  179. end
  180. //lw
  181. 6'b100011:begin
  182. // PC_reset <= 0;
  183. ALUop <= 4'b0000;
  184. PCsel <= 2'b00;
  185. reg_distSel <= 0;
  186. regWr <= 1;
  187. dataWr <= 0;
  188. ALUin1_sel <= 0;
  189. ALUin2_sel <= 1;
  190. regIn_sel <= 1;
  191. end
  192. //sw
  193. 6'b101011:begin
  194. // PC_reset <= 0;
  195. ALUop <= 4'b0000;
  196. PCsel <= 2'b00;
  197. reg_distSel <= 0;
  198. regWr <= 0;
  199. dataWr <= 1;
  200. ALUin1_sel <= 0;
  201. ALUin2_sel <= 1;
  202. regIn_sel <= 0;
  203. end
  204. //beq
  205. 6'b000100:begin
  206. // PC_reset <= 0;
  207. ALUop <= 4'b1000;
  208. reg_distSel <= 0;
  209. regWr <= 0;
  210. dataWr <= 0;
  211. ALUin1_sel <= 0;
  212. ALUin2_sel <= 1;
  213. regIn_sel <= 0;
  214. if(zero == 1) //1表示相等,要跳转
  215. PCsel <= 2'b01;
  216. else
  217. PCsel <= 2'b00;
  218. end
  219. //bne
  220. 6'b000101:begin
  221. // PC_reset <= 0;
  222. ALUop <= 4'b1000;
  223. reg_distSel <= 0;
  224. regWr <= 0;
  225. dataWr <= 0;
  226. ALUin1_sel <= 0;
  227. ALUin2_sel <= 1;
  228. regIn_sel <= 0;
  229. if(zero == 0)
  230. PCsel <= 2'b01;
  231. else
  232. PCsel <= 2'b00;
  233. end
  234. //lui,设置rt寄存器的高十六位,后面是160
  235. 6'b001111: begin
  236. // PC_reset <= 0;
  237. ALUop <= 4'b1001;
  238. PCsel <= 2'b00;
  239. reg_distSel <= 0;
  240. regWr <= 1;
  241. dataWr <= 0;
  242. ALUin1_sel <= 0;
  243. ALUin2_sel <= 1;
  244. regIn_sel <= 0;
  245. end
  246. //j
  247. 6'b000010: begin
  248. // PC_reset <= 0;
  249. ALUop <= 4'b1001; //无所谓
  250. reg_distSel <= 0; //无所谓
  251. PCsel <= 2'b10;
  252. regWr <= 0;
  253. dataWr <= 0;
  254. ALUin1_sel <= 0; //无所谓
  255. ALUin2_sel <= 1; //无所谓
  256. regIn_sel <= 0; //无所谓
  257. end
  258. endcase
  259. end
  260. endmodule

            这个模块不设置仿真,因为要需要调用很多其他模块。

        9)顶层仿真

                这个仿真文件是用来统筹所有其他的模块并且进行运行MIPS指令。这里还未写顶层模块

                下面直接贴上仿真代码(main_tb.v):

  1. `timescale 1ns / 1ps
  2. module main_tb( );
  3. reg clk;
  4. wire pc_reset; wire push; wire [31:0]pc_out; wire [31:0]pc_in; wire[31:0] plused_pc;
  5. pc my_pc(pc_in,clk,pc_reset,pc_out,plused_pc);
  6. wire [5:0]op; wire [4:0]rs; wire [4:0]rt; wire [4:0]rd; wire [4:0]shamt; wire [5:0]funct; wire [31:0]imm32;
  7. InsMem my_Insmem(pc_out,op,rs,rt,rd,shamt,funct); extender my_extender({rd,shamt,funct},imm32);
  8. wire ctrl_distReg; wire [31:0]reg_dataIn; wire RegWr; wire [31:0]reg_out1; wire [31:0]reg_out2;
  9. Register my_Register(clk, rs, rt, rd, ctrl_distReg, RegWr, reg_dataIn, reg_out1, reg_out2);
  10. wire [31:0]ALU_in1; wire[31:0]ALU_in2; wire ALUsel_in1; wire ALUsel_in2;
  11. dataSel_2 ALU1(reg_out1,shamt,ALUsel_in1,ALU_in1); dataSel_2 ALU2(reg_out2,imm32,ALUsel_in2,ALU_in2);
  12. wire [3:0]ALUctrl; wire ALU_zero; wire [31:0]ALU_out;
  13. ALU my_ALU(ALUctrl,ALU_in1,ALU_in2,ALU_zero,ALU_out);
  14. wire dataWr; wire [31:0]dataOut;
  15. dataMem mt_dataMem(clk,ALU_out,reg_out2,dataWr,dataOut);
  16. wire regIn_sel_wire;
  17. dataSel_2 regIn_sel(ALU_out,dataOut,regIn_sel_wire,reg_dataIn);
  18. wire [1:0]pcSel;
  19. PCselect my_PCselect(plused_pc,imm32,{rs,rt,rd,shamt,funct},reg_out1,pcSel,pc_in);
  20. control my_control(op,funct,ALU_zero,pc_reset,ALUctrl,pcSel,ctrl_distReg,RegWr,dataWr,ALUsel_in1,ALUsel_in2,regIn_sel_wire);
  21. initial begin
  22. clk=0;
  23. end
  24. always @(*) begin
  25. #5 clk <= ~clk;
  26. end
  27. endmodule

        在testdata.txt中写入几行指令来测试CPU是否能正常工作。

                经过测试后观察波形,这些指令都能够正常运行。

        测试过后,将顶层写成一个模块,然后将程序烧入CPU中。

        顶层模块代码(top.v):

  1. `timescale 1ns / 1ps
  2. //结合所有CPU器件的模块,但还没有通过数码管进行输出
  3. module top(
  4. input push,
  5. input clk_7seg,
  6. input [1:0]SW,
  7. output [11:0]display,
  8. // input clk,
  9. input pc_reset
  10. // output effective_clk
  11. );
  12. wire [31:0]pc_out;
  13. wire [31:0]pc_in;
  14. wire [31:0]plused_pc;
  15. wire [5:0]op;
  16. wire [4:0]rs;
  17. wire [4:0]rt;
  18. wire [4:0]rd;
  19. wire [4:0]shamt;
  20. wire [5:0]funct;
  21. wire [31:0]imm32;
  22. wire ctrl_distReg;
  23. wire [31:0]reg_dataIn;
  24. wire RegWr;
  25. wire [31:0]reg_out1;
  26. wire [31:0]reg_out2;
  27. wire [31:0]ALU_in1;
  28. wire [31:0]ALU_in2;
  29. wire ALUsel_in1;
  30. wire ALUsel_in2;
  31. wire [3:0]ALUctrl;
  32. wire ALU_zero;
  33. wire [31:0]ALU_out;
  34. wire dataWr;
  35. wire [31:0]dataOut;
  36. wire regIn_sel_wire;
  37. wire [1:0]pcSel;
  38. wire effective_clk;
  39. wire [31:0]extended_shamt;
  40. clk_dura my_clkdura(clk_7seg,push,effective_clk);
  41. extend5 shamt_ex(shamt,extended_shamt);
  42. pc my_pc(pc_in,effective_clk,pc_reset,pc_out,plused_pc);
  43. InsMem my_Insmem(pc_out,op,rs,rt,rd,shamt,funct); extender my_extender({rd,shamt,funct},imm32);
  44. Register my_Register(effective_clk, rs, rt, rd, ctrl_distReg, RegWr, reg_dataIn, reg_out1, reg_out2);
  45. dataSel_2 ALU1(reg_out1,extended_shamt,ALUsel_in1,ALU_in1); dataSel_2 ALU2(reg_out2,imm32,ALUsel_in2,ALU_in2);
  46. ALU my_ALU(ALUctrl,ALU_in1,ALU_in2,ALU_zero,ALU_out);
  47. dataMem mt_dataMem(effective_clk,ALU_out,reg_out2,dataWr,dataOut);
  48. dataSel_2 regIn_sel(ALU_out,dataOut,regIn_sel_wire,reg_dataIn);
  49. PCselect my_PCselect(plused_pc,imm32,{rs,rt,rd,shamt,funct},reg_out1,pcSel,pc_in);
  50. control my_control(op,funct,ALU_zero,ALUctrl,pcSel,ctrl_distReg,RegWr,dataWr,ALUsel_in1,ALUsel_in2,regIn_sel_wire);
  51. _7seg my_7seg(clk_7seg,SW,pc_out,pc_in,rs,reg_out1,rt,reg_out2,ALU_out,reg_dataIn,display);
  52. endmodule

                之前顶层模块把所有参数都写进了模块的参数列表(即input,output)中,但在约束文件中并未将他们接至引脚上,因此会造成输出悬空的现象,在进行硬件implementation会报错如下:

        顶层模块仿真(top_test_tb.v):

  1. `timescale 1ns / 1ps
  2. module top_test_tb();
  3. reg push;
  4. reg clk_7seg;
  5. reg [1:0]SW;
  6. reg pc_reset;
  7. wire [11:0]display;
  8. // wire effective_clk;
  9. top test_top(push,clk_7seg,SW,display,pc_reset);
  10. initial begin
  11. push = 0;
  12. clk_7seg = 0;
  13. SW = 0;
  14. pc_reset = 0;
  15. end
  16. always @(clk_7seg) begin
  17. #5 clk_7seg <= ~clk_7seg;
  18. end
  19. always @(push) begin
  20. #80 push <= ~push;
  21. end
  22. endmodule

        指令文件(test.txt):

  1. 20010008 //add $1,$0,8
  2. 34020002 //ori $2,$0,2
  3. 00411820 //add $3,$2,$1
  4. 00622822 //sub $5,$3,$2
  5. 00a22024 //and $4,$5,$2
  6. 00824025 //or $8,$4,$2
  7. 00084040 //sll $8,$8,1
  8. ac080004 //sw $8,4($0)
  9. 8c090004 //lw $9,4($0)

        0~2条指令波形(pc:00~08):

         3~5条指令波形(pc:0C~14):

         6~8条指令(pc:18~20):

 3.CPU烧板

        本实验还有在basys3板子上展示的要求,要求如下:

         七段数码管

                为了在数码管上显示,要写一个七段数码管显示的模块,通过当前的pc,rs,rt,ALU_out值来进行显示。

                该模块代码(_7seg.v):

  1. `timescale 1ns / 1ps
  2. module _7seg(
  3. input clk_7seg,
  4. input [1:0]SW, //(sw15,sw14)
  5. input [31:0]pc_now, //当前pc值
  6. input [31:0]pc_next, //下条pc值
  7. input [4:0]rs, //rs寄存器的编号
  8. input [31:0]reg_out1, //rs寄存器的内容
  9. input [4:0]rt, //rt寄存器的编号
  10. input [31:0]reg_out2, //rt寄存器的内容
  11. input [31:0]ALU_out, //ALU结果
  12. input [31:0]dataOut, //DB总线
  13. output reg[11:0] display
  14. );
  15. //count == T1MS 用来分频
  16. reg [19:0] count = 0;
  17. reg [2:0] sel = 0;
  18. parameter T1MS = 50000;
  19. always @(posedge clk_7seg) begin
  20. count <= count+1;
  21. if(count == T1MS) begin
  22. count <= 0;
  23. if(sel==3) sel<=0;
  24. else sel <= sel+1;
  25. end
  26. end
  27. always @(posedge clk_7seg) begin
  28. case(SW)
  29. //pc
  30. 0: begin
  31. case(sel)
  32. 0: begin
  33. display[11:8] <= 4'b0111;
  34. //第一位输出
  35. case(pc_now[7:4])
  36. 4'b0000: display[7:0] <= 8'b1100_0000;
  37. 4'b0001: display[7:0] <= 8'b1111_1001;
  38. 4'b0010: display[7:0] <= 8'b1010_0100;
  39. 4'b0011: display[7:0] <= 8'b1011_0000;
  40. 4'b0100: display[7:0] <= 8'b1001_1001;
  41. 4'b0101: display[7:0] <= 8'b1001_0010;
  42. 4'b0110: display[7:0] <= 8'b1000_0010;
  43. 4'b0111: display[7:0] <= 8'b1101_1000;
  44. 4'b1000: display[7:0] <= 8'b1000_0000;
  45. 4'b1001: display[7:0] <= 8'b1001_0000;
  46. 4'b1010: display[7:0] <= 8'b1000_1000;
  47. 4'b1011: display[7:0] <= 8'b1000_0011;
  48. 4'b1100: display[7:0] <= 8'b1100_0110;
  49. 4'b1101: display[7:0] <= 8'b1010_0001;
  50. 4'b1110: display[7:0] <= 8'b1000_0110;
  51. 4'b1111: display[7:0] <= 8'b1000_1110;
  52. default : display[7:0] = 8'b1111_1111; //全灭
  53. endcase
  54. end
  55. 1: begin
  56. display[11:8] <= 4'b1011;
  57. //第二位输出
  58. case(pc_now[3:0])
  59. 4'b0000: display[7:0] <= 8'b1100_0000;
  60. 4'b0001: display[7:0] <= 8'b1111_1001;
  61. 4'b0010: display[7:0] <= 8'b1010_0100;
  62. 4'b0011: display[7:0] <= 8'b1011_0000;
  63. 4'b0100: display[7:0] <= 8'b1001_1001;
  64. 4'b0101: display[7:0] <= 8'b1001_0010;
  65. 4'b0110: display[7:0] <= 8'b1000_0010;
  66. 4'b0111: display[7:0] <= 8'b1101_1000;
  67. 4'b1000: display[7:0] <= 8'b1000_0000;
  68. 4'b1001: display[7:0] <= 8'b1001_0000;
  69. 4'b1010: display[7:0] <= 8'b1000_1000;
  70. 4'b1011: display[7:0] <= 8'b1000_0011;
  71. 4'b1100: display[7:0] <= 8'b1100_0110;
  72. 4'b1101: display[7:0] <= 8'b1010_0001;
  73. 4'b1110: display[7:0] <= 8'b1000_0110;
  74. 4'b1111: display[7:0] <= 8'b1000_1110;
  75. default : display[7:0] = 8'b1111_1111; //全灭
  76. endcase
  77. end
  78. 2: begin
  79. display[11:8] <= 4'b1101;
  80. //第三位输出
  81. case(pc_next[7:4])
  82. 4'b0000: display[7:0] <= 8'b1100_0000;
  83. 4'b0001: display[7:0] <= 8'b1111_1001;
  84. 4'b0010: display[7:0] <= 8'b1010_0100;
  85. 4'b0011: display[7:0] <= 8'b1011_0000;
  86. 4'b0100: display[7:0] <= 8'b1001_1001;
  87. 4'b0101: display[7:0] <= 8'b1001_0010;
  88. 4'b0110: display[7:0] <= 8'b1000_0010;
  89. 4'b0111: display[7:0] <= 8'b1101_1000;
  90. 4'b1000: display[7:0] <= 8'b1000_0000;
  91. 4'b1001: display[7:0] <= 8'b1001_0000;
  92. 4'b1010: display[7:0] <= 8'b1000_1000;
  93. 4'b1011: display[7:0] <= 8'b1000_0011;
  94. 4'b1100: display[7:0] <= 8'b1100_0110;
  95. 4'b1101: display[7:0] <= 8'b1010_0001;
  96. 4'b1110: display[7:0] <= 8'b1000_0110;
  97. 4'b1111: display[7:0] <= 8'b1000_1110;
  98. default : display[7:0] = 8'b1111_1111; //全灭
  99. endcase
  100. end
  101. 3: begin
  102. display[11:8] <= 4'b1110;
  103. //第四位输出
  104. case(pc_next[3:0])
  105. 4'b0000: display[7:0] <= 8'b1100_0000;
  106. 4'b0001: display[7:0] <= 8'b1111_1001;
  107. 4'b0010: display[7:0] <= 8'b1010_0100;
  108. 4'b0011: display[7:0] <= 8'b1011_0000;
  109. 4'b0100: display[7:0] <= 8'b1001_1001;
  110. 4'b0101: display[7:0] <= 8'b1001_0010;
  111. 4'b0110: display[7:0] <= 8'b1000_0010;
  112. 4'b0111: display[7:0] <= 8'b1101_1000;
  113. 4'b1000: display[7:0] <= 8'b1000_0000;
  114. 4'b1001: display[7:0] <= 8'b1001_0000;
  115. 4'b1010: display[7:0] <= 8'b1000_1000;
  116. 4'b1011: display[7:0] <= 8'b1000_0011;
  117. 4'b1100: display[7:0] <= 8'b1100_0110;
  118. 4'b1101: display[7:0] <= 8'b1010_0001;
  119. 4'b1110: display[7:0] <= 8'b1000_0110;
  120. 4'b1111: display[7:0] <= 8'b1000_1110;
  121. default : display[7:0] = 8'b1111_1111; //全灭
  122. endcase
  123. end
  124. endcase
  125. end
  126. //rs
  127. 1: begin
  128. case(sel)
  129. 0: begin
  130. display[11:8] <= 4'b0111;
  131. //第一位输出
  132. case(rs[4])
  133. 0: display[7:0] <= 8'b1100_0000;
  134. 1: display[7:0] <= 8'b1111_1001;
  135. default : display[7:0] = 8'b1111_1111; //全灭
  136. endcase
  137. end
  138. 1: begin
  139. display[11:8] <= 4'b1011;
  140. //第二位输出
  141. case(rs[3:0])
  142. 4'b0000: display[7:0] <= 8'b1100_0000;
  143. 4'b0001: display[7:0] <= 8'b1111_1001;
  144. 4'b0010: display[7:0] <= 8'b1010_0100;
  145. 4'b0011: display[7:0] <= 8'b1011_0000;
  146. 4'b0100: display[7:0] <= 8'b1001_1001;
  147. 4'b0101: display[7:0] <= 8'b1001_0010;
  148. 4'b0110: display[7:0] <= 8'b1000_0010;
  149. 4'b0111: display[7:0] <= 8'b1101_1000;
  150. 4'b1000: display[7:0] <= 8'b1000_0000;
  151. 4'b1001: display[7:0] <= 8'b1001_0000;
  152. 4'b1010: display[7:0] <= 8'b1000_1000;
  153. 4'b1011: display[7:0] <= 8'b1000_0011;
  154. 4'b1100: display[7:0] <= 8'b1100_0110;
  155. 4'b1101: display[7:0] <= 8'b1010_0001;
  156. 4'b1110: display[7:0] <= 8'b1000_0110;
  157. 4'b1111: display[7:0] <= 8'b1000_1110;
  158. default : display[7:0] = 8'b1111_1111; //全灭
  159. endcase
  160. end
  161. 2: begin
  162. display[11:8] <= 4'b1101;
  163. //第三位输出
  164. case(reg_out1[7:4])
  165. 4'b0000: display[7:0] <= 8'b1100_0000;
  166. 4'b0001: display[7:0] <= 8'b1111_1001;
  167. 4'b0010: display[7:0] <= 8'b1010_0100;
  168. 4'b0011: display[7:0] <= 8'b1011_0000;
  169. 4'b0100: display[7:0] <= 8'b1001_1001;
  170. 4'b0101: display[7:0] <= 8'b1001_0010;
  171. 4'b0110: display[7:0] <= 8'b1000_0010;
  172. 4'b0111: display[7:0] <= 8'b1101_1000;
  173. 4'b1000: display[7:0] <= 8'b1000_0000;
  174. 4'b1001: display[7:0] <= 8'b1001_0000;
  175. 4'b1010: display[7:0] <= 8'b1000_1000;
  176. 4'b1011: display[7:0] <= 8'b1000_0011;
  177. 4'b1100: display[7:0] <= 8'b1100_0110;
  178. 4'b1101: display[7:0] <= 8'b1010_0001;
  179. 4'b1110: display[7:0] <= 8'b1000_0110;
  180. 4'b1111: display[7:0] <= 8'b1000_1110;
  181. default : display[7:0] = 8'b1111_1111; //全灭
  182. endcase
  183. end
  184. 3: begin
  185. display[11:8] <= 4'b1110;
  186. //第四位输出
  187. case(reg_out1[3:0])
  188. 4'b0000: display[7:0] <= 8'b1100_0000;
  189. 4'b0001: display[7:0] <= 8'b1111_1001;
  190. 4'b0010: display[7:0] <= 8'b1010_0100;
  191. 4'b0011: display[7:0] <= 8'b1011_0000;
  192. 4'b0100: display[7:0] <= 8'b1001_1001;
  193. 4'b0101: display[7:0] <= 8'b1001_0010;
  194. 4'b0110: display[7:0] <= 8'b1000_0010;
  195. 4'b0111: display[7:0] <= 8'b1101_1000;
  196. 4'b1000: display[7:0] <= 8'b1000_0000;
  197. 4'b1001: display[7:0] <= 8'b1001_0000;
  198. 4'b1010: display[7:0] <= 8'b1000_1000;
  199. 4'b1011: display[7:0] <= 8'b1000_0011;
  200. 4'b1100: display[7:0] <= 8'b1100_0110;
  201. 4'b1101: display[7:0] <= 8'b1010_0001;
  202. 4'b1110: display[7:0] <= 8'b1000_0110;
  203. 4'b1111: display[7:0] <= 8'b1000_1110;
  204. default : display[7:0] = 8'b1111_1111; //全灭
  205. endcase
  206. end
  207. endcase
  208. end
  209. //rt
  210. 2: begin
  211. case(sel)
  212. 0: begin
  213. display[11:8] <= 4'b0111;
  214. //第一位输出
  215. case(rt[4])
  216. 0: display[7:0] <= 8'b1100_0000;
  217. 1: display[7:0] <= 8'b1111_1001;
  218. default : display[7:0] = 8'b1111_1111; //全灭
  219. endcase
  220. end
  221. 1: begin
  222. display[11:8] <= 4'b1011;
  223. //第二位输出
  224. case(rt[3:0])
  225. 4'b0000: display[7:0] <= 8'b1100_0000;
  226. 4'b0001: display[7:0] <= 8'b1111_1001;
  227. 4'b0010: display[7:0] <= 8'b1010_0100;
  228. 4'b0011: display[7:0] <= 8'b1011_0000;
  229. 4'b0100: display[7:0] <= 8'b1001_1001;
  230. 4'b0101: display[7:0] <= 8'b1001_0010;
  231. 4'b0110: display[7:0] <= 8'b1000_0010;
  232. 4'b0111: display[7:0] <= 8'b1101_1000;
  233. 4'b1000: display[7:0] <= 8'b1000_0000;
  234. 4'b1001: display[7:0] <= 8'b1001_0000;
  235. 4'b1010: display[7:0] <= 8'b1000_1000;
  236. 4'b1011: display[7:0] <= 8'b1000_0011;
  237. 4'b1100: display[7:0] <= 8'b1100_0110;
  238. 4'b1101: display[7:0] <= 8'b1010_0001;
  239. 4'b1110: display[7:0] <= 8'b1000_0110;
  240. 4'b1111: display[7:0] <= 8'b1000_1110;
  241. default : display[7:0] = 8'b1111_1111; //全灭
  242. endcase
  243. end
  244. 2: begin
  245. display[11:8] <= 4'b1101;
  246. //第三位输出
  247. case(reg_out2[7:4])
  248. 4'b0000: display[7:0] <= 8'b1100_0000;
  249. 4'b0001: display[7:0] <= 8'b1111_1001;
  250. 4'b0010: display[7:0] <= 8'b1010_0100;
  251. 4'b0011: display[7:0] <= 8'b1011_0000;
  252. 4'b0100: display[7:0] <= 8'b1001_1001;
  253. 4'b0101: display[7:0] <= 8'b1001_0010;
  254. 4'b0110: display[7:0] <= 8'b1000_0010;
  255. 4'b0111: display[7:0] <= 8'b1101_1000;
  256. 4'b1000: display[7:0] <= 8'b1000_0000;
  257. 4'b1001: display[7:0] <= 8'b1001_0000;
  258. 4'b1010: display[7:0] <= 8'b1000_1000;
  259. 4'b1011: display[7:0] <= 8'b1000_0011;
  260. 4'b1100: display[7:0] <= 8'b1100_0110;
  261. 4'b1101: display[7:0] <= 8'b1010_0001;
  262. 4'b1110: display[7:0] <= 8'b1000_0110;
  263. 4'b1111: display[7:0] <= 8'b1000_1110;
  264. default : display[7:0] = 8'b1111_1111; //全灭
  265. endcase
  266. end
  267. 3: begin
  268. display[11:8] <= 4'b1110;
  269. //第四位输出
  270. case(reg_out2[3:0])
  271. 4'b0000: display[7:0] <= 8'b1100_0000;
  272. 4'b0001: display[7:0] <= 8'b1111_1001;
  273. 4'b0010: display[7:0] <= 8'b1010_0100;
  274. 4'b0011: display[7:0] <= 8'b1011_0000;
  275. 4'b0100: display[7:0] <= 8'b1001_1001;
  276. 4'b0101: display[7:0] <= 8'b1001_0010;
  277. 4'b0110: display[7:0] <= 8'b1000_0010;
  278. 4'b0111: display[7:0] <= 8'b1101_1000;
  279. 4'b1000: display[7:0] <= 8'b1000_0000;
  280. 4'b1001: display[7:0] <= 8'b1001_0000;
  281. 4'b1010: display[7:0] <= 8'b1000_1000;
  282. 4'b1011: display[7:0] <= 8'b1000_0011;
  283. 4'b1100: display[7:0] <= 8'b1100_0110;
  284. 4'b1101: display[7:0] <= 8'b1010_0001;
  285. 4'b1110: display[7:0] <= 8'b1000_0110;
  286. 4'b1111: display[7:0] <= 8'b1000_1110;
  287. default : display[7:0] = 8'b1111_1111; //全灭
  288. endcase
  289. end
  290. endcase
  291. end
  292. //ALUout:DB数据
  293. 3: begin
  294. case(sel)
  295. 0: begin
  296. display[11:8] <= 4'b0111;
  297. //第一位输出
  298. case(ALU_out[7:4])
  299. 4'b0000: display[7:0] <= 8'b1100_0000;
  300. 4'b0001: display[7:0] <= 8'b1111_1001;
  301. 4'b0010: display[7:0] <= 8'b1010_0100;
  302. 4'b0011: display[7:0] <= 8'b1011_0000;
  303. 4'b0100: display[7:0] <= 8'b1001_1001;
  304. 4'b0101: display[7:0] <= 8'b1001_0010;
  305. 4'b0110: display[7:0] <= 8'b1000_0010;
  306. 4'b0111: display[7:0] <= 8'b1101_1000;
  307. 4'b1000: display[7:0] <= 8'b1000_0000;
  308. 4'b1001: display[7:0] <= 8'b1001_0000;
  309. 4'b1010: display[7:0] <= 8'b1000_1000;
  310. 4'b1011: display[7:0] <= 8'b1000_0011;
  311. 4'b1100: display[7:0] <= 8'b1100_0110;
  312. 4'b1101: display[7:0] <= 8'b1010_0001;
  313. 4'b1110: display[7:0] <= 8'b1000_0110;
  314. 4'b1111: display[7:0] <= 8'b1000_1110;
  315. default : display[7:0] = 8'b1111_1111; //全灭
  316. endcase
  317. end
  318. 1: begin
  319. display[11:8] <= 4'b1011;
  320. //第二位输出
  321. case(ALU_out[3:0])
  322. 4'b0000: display[7:0] <= 8'b1100_0000;
  323. 4'b0001: display[7:0] <= 8'b1111_1001;
  324. 4'b0010: display[7:0] <= 8'b1010_0100;
  325. 4'b0011: display[7:0] <= 8'b1011_0000;
  326. 4'b0100: display[7:0] <= 8'b1001_1001;
  327. 4'b0101: display[7:0] <= 8'b1001_0010;
  328. 4'b0110: display[7:0] <= 8'b1000_0010;
  329. 4'b0111: display[7:0] <= 8'b1101_1000;
  330. 4'b1000: display[7:0] <= 8'b1000_0000;
  331. 4'b1001: display[7:0] <= 8'b1001_0000;
  332. 4'b1010: display[7:0] <= 8'b1000_1000;
  333. 4'b1011: display[7:0] <= 8'b1000_0011;
  334. 4'b1100: display[7:0] <= 8'b1100_0110;
  335. 4'b1101: display[7:0] <= 8'b1010_0001;
  336. 4'b1110: display[7:0] <= 8'b1000_0110;
  337. 4'b1111: display[7:0] <= 8'b1000_1110;
  338. default : display[7:0] = 8'b1111_1111; //全灭
  339. endcase
  340. end
  341. 2: begin
  342. display[11:8] <= 4'b1101;
  343. //第三位输出
  344. case(dataOut[7:4])
  345. 4'b0000: display[7:0] <= 8'b1100_0000;
  346. 4'b0001: display[7:0] <= 8'b1111_1001;
  347. 4'b0010: display[7:0] <= 8'b1010_0100;
  348. 4'b0011: display[7:0] <= 8'b1011_0000;
  349. 4'b0100: display[7:0] <= 8'b1001_1001;
  350. 4'b0101: display[7:0] <= 8'b1001_0010;
  351. 4'b0110: display[7:0] <= 8'b1000_0010;
  352. 4'b0111: display[7:0] <= 8'b1101_1000;
  353. 4'b1000: display[7:0] <= 8'b1000_0000;
  354. 4'b1001: display[7:0] <= 8'b1001_0000;
  355. 4'b1010: display[7:0] <= 8'b1000_1000;
  356. 4'b1011: display[7:0] <= 8'b1000_0011;
  357. 4'b1100: display[7:0] <= 8'b1100_0110;
  358. 4'b1101: display[7:0] <= 8'b1010_0001;
  359. 4'b1110: display[7:0] <= 8'b1000_0110;
  360. 4'b1111: display[7:0] <= 8'b1000_1110;
  361. default : display[7:0] = 8'b1111_1111; //全灭
  362. endcase
  363. end
  364. 3: begin
  365. display[11:8] <= 4'b1110;
  366. //第四位输出
  367. case(dataOut[3:0])
  368. 4'b0000: display[7:0] <= 8'b1100_0000;
  369. 4'b0001: display[7:0] <= 8'b1111_1001;
  370. 4'b0010: display[7:0] <= 8'b1010_0100;
  371. 4'b0011: display[7:0] <= 8'b1011_0000;
  372. 4'b0100: display[7:0] <= 8'b1001_1001;
  373. 4'b0101: display[7:0] <= 8'b1001_0010;
  374. 4'b0110: display[7:0] <= 8'b1000_0010;
  375. 4'b0111: display[7:0] <= 8'b1101_1000;
  376. 4'b1000: display[7:0] <= 8'b1000_0000;
  377. 4'b1001: display[7:0] <= 8'b1001_0000;
  378. 4'b1010: display[7:0] <= 8'b1000_1000;
  379. 4'b1011: display[7:0] <= 8'b1000_0011;
  380. 4'b1100: display[7:0] <= 8'b1100_0110;
  381. 4'b1101: display[7:0] <= 8'b1010_0001;
  382. 4'b1110: display[7:0] <= 8'b1000_0110;
  383. 4'b1111: display[7:0] <= 8'b1000_1110;
  384. default : display[7:0] = 8'b1111_1111; //全灭
  385. endcase
  386. end
  387. endcase
  388. end
  389. endcase
  390. end
  391. endmodule

            这里的display[0]~diplay[6]分别是数码管的A~G。

        按键消抖

                消抖原理:FPGA板子上的按键在按下时,由于弹性形变,因此会导致信号不稳定,在极短的时间内会产生多次高电平,因此要在信号保持稳定后再进行读取,这样              能避免按下一次按键后执行多条指令。

                脉冲信号为push,写一个模块消抖(clk_dura.v):

  1. `timescale 1ns / 1ps
  2. module clk_dura(
  3. input clk,
  4. input push,
  5. output reg effective_clk
  6. );
  7. reg [31:0] count;
  8. integer i;
  9. initial begin
  10. for(i=0;i<32;i=i+1) count[i] = 0;
  11. end
  12. always @(posedge clk) begin
  13. if(push && count<=32'd30000000) count = count + 1;
  14. else if(!push) count = 0;
  15. end
  16. always @(count) begin
  17. if(count == 32'd10000000) effective_clk = 1'b1;
  18. else effective_clk = 0;
  19. end
  20. endmodule

        虽然加了延迟消除抖动,但是在上板子的时候会发现仍会出现按一下跳两条指令的情况,(猜测是板子问题)因此加入了简易的松手检测,如下图:

   

        强行抹除了长按可能造成跳多条指令的影响(计数器到了30ms后不会再增加)。

        修改过后按键运行正常。

  需要全项目源代码的,链接已经贴在下面,需要自取。

  项目源代码:https://download.csdn.net/download/wuyzh39/87244971

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/272177
推荐阅读
相关标签
  

闽ICP备14008679号