赞
踩
(1)取指,PC+4
(2)译码
(3)取操作数,ALU运算
(4)访存(MEM)
(5)写回(RegWr)
将每一级操作抽象为CPU中的若干个模块:
(1)指令读取模块(指令存储器)
(2)指令寄存器(IR)
(3)数据寄存器(rs,rt,rd)
(4)逻辑运算器件(ALU)
(5)数据存储器
(6)控制单元
MIPS指令集三种指令:
R型指令:
汇编代码格式:op rd,rs,rt
机器中存储:
含义:[rs]+[rt] [rd]
I型指令:
汇编代码格式:op rt,rs,imm16
机器中存储:
含义:[rs]+imm16 [rt]
J型指令:
汇编代码格式:op imm26
机器中存储:
需要注意的是,这里的跳转地址为: PC高四位+imm26+00 ,构成了32的地址
来看下MIPS指令集:
来看一下整体的数据通路:
看上去非常复杂,我们把每个元件单拎出来看输入和输出:
符号扩展单元看起来比较简单,它的作用:
(1)将imm16符号扩展至32位后送至ALU
input1:[15:0] imm16, input2:ExtOp
output:[31:0] imm32
扩展器模块代码如下(extender.v):
- module extender(
- input [15:0] imm16,
- input ExtOp,
- output [31:0] imm32
- );
-
- //ExtOp 为 0 做零扩展,为 1 做符号扩展
- //imm16是以补码储存在机器中的
-
- assign imm32 = {imm16[15],{16{1'b0}},imm16[14:0]};
-
-
- endmodule
进行一下简单的仿真(extender_tb.v):
- module extender_tb();
- reg [15:0] imm16;
- reg ExtOp;
- wire [31:0] imm32;
-
- initial begin
- imm16 = 16'b1111111111111111;
- ExtOp = 1;
- end
-
- extender test(imm16,ExtOp,imm32);
-
- endmodule
11...11经过符号扩展后变成了10...0011...111,观察仿真波形:
扩展器完成!
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
- `timescale 1ns / 1ps
-
- module pc(
- input [31:0] pc_in,
- input clk,
- input reset,
- output reg[31:0] pc_out, //输出到mem进行取指
- output [31:0] pc_plus //经过+4的pc
- );
-
- initial begin
- pc_out = 0;
- end
-
- always @(posedge clk) begin
- if(reset == 0) begin
- pc_out=pc_in;
- end
- else begin
- pc_out=0;
- end
- end
-
- assign pc_plus = pc_out + 4;
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
pc模块测试用的仿真文件(pc_tb.v):
- module pc_tb();
- reg clk;
- reg[31:0] pc_in;
- reg reset;
- wire[31:0] pc_out;
- wire[31:0] pc_plus;
-
- initial begin
- clk=0;
- pc_in=0;
- reset=0;
- end
-
- always @(*) begin
- #5 clk <= ~clk;
- end
-
- pc pc_test(pc_in,clk,reset,pc_out,pc_plus);
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
pc和自增模块完成!
功能:根据输入的pc,在存储器中找到pc所指向的那条指令后,将其发送给各个部件。
输入:input1:pc
output:经过拆解过的指令
注:(1)存储器的指令写入要在模块中实现而不是在仿真中实现
(2)将存储器设计为32位的寄存器组而不是8位的寄存器组有一个好处:当读入16进制的文件时,8位的寄存器会两个两个数据读,因此在读入单条(正常顺序) 的指令时,顺序会错乱。比如:
这时8位寄存器组的存储内容会为:0111_1000_0101_0110_0011_0100_ 0001_0010
这样做指令分割的时候会非常麻烦。
下面来看下InsMem模块的代码(InsMem.v):
-
- module InsMem(
- input [31:0]addr, //即pc值
- output [5:0]op, //31:26
- output [4:0]rs, //25:21
- output [4:0]rt, //20:16
- output [4:0]rd, //15:11
- output [4:0]shamt, //10:6
- output [5:0]func //5:0
-
- );
-
- reg [31:0] mem [63:0]; //最多可存64条指令
- initial begin
- $readmemh("D:/meiyong/testdata.txt",mem);
- end
-
- assign op = mem[addr >> 2][31:26]; //这里用的是32位的指令寄存器组,因此pc要除以4
- assign rs = mem[addr >> 2][25:21];
- assign rt = mem[addr >> 2][20:16];
- assign rd = mem[addr >> 2][15:11];
- assign shamt = mem[addr >> 2][10:6];
- assign func = mem[addr >> 2][5:0];
-
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
写个仿真测试一下(test_insmem_tb.v):
- module test_insmem_tb( );
- reg [31:0] addr;
- wire [5:0]op; //31:26
- wire [4:0]rs; //25:21
- wire [4:0]rt; //20:16
- wire [4:0]rd; //15:11
- wire [4:0]shamt; //10:6
- wire [5:0]func;
-
- InsMem tset_insmem(addr,op,rs,rt,rd,shamt,func);
-
- initial begin
- addr = 0;
- end
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
检验一下输出波形:
注:第一条指令是12345678
数据存储器也跟指令存储器一样采用32位的寄存器组来实现。
输入:访问/写入地址,写入的数据,写使能信号Wr。
输出:读出的数据
数据存储器模块代码(dataMem.v):
- `timescale 1ns / 1ps
-
- module dataMem(
- input clk,
- input [31:0] dataAdd,
- input [31:0] dataIn,
- input dataWr,
- output reg[31:0] dataOut
- );
-
- integer i;
- reg [31:0] mem [511:0];
-
- initial begin
- for(i=0;i<512;i=i+1) begin
- mem[i] <= 0;
- end
- end
-
- always @(posedge clk) begin
- if(dataWr == 1) begin
- mem[dataAdd>>2] <= dataIn;
- end
- end
-
- always @(*) begin
- dataOut <= mem[dataAdd>>2];
- end
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
寄存器组内有32个32位的通用寄存器,通过rs、rt、rd来指定访问的是哪个寄存器。
输入:input1:clk input2:rs,rt,rd input3:distSel(写哪个寄存器,rt还是rd) input4:RegWr input5:将写入rd的dataIn
输出:两条线out1和out2
数据寄存器模块源代码(Register.v):
- module Register(
- input clk,
- input [4:0]rs,
- input [4:0]rt,
- input [4:0]rd,
- input distSel, //写入rt还是rd,0写rt,1写rd
- input RegWr,
- input [31:0]dataIn,
- output reg[31:0]out1,
- output reg[31:0]out2
- );
-
- reg [31:0] Reg[31:0]; //32个通用寄存器
-
- integer i;
- //对寄存器初始化
- initial begin
- for (i = 0; i < 32; i = i+ 1) Reg[i] <= 0;
- end
-
- always @(posedge clk) begin
- if(RegWr == 1) begin
- case(distSel)
- 0: Reg[rt] = dataIn; //写rt
- 1: Reg[rd] = dataIn; //写rd
- endcase
- end
- end
-
- always @(*) begin
- out1 <= Reg[rs];
- out2 <= Reg[rt];
- end
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
测试用的仿真代码(Reg_test_tb.v): //这里把Register中Reg的初始化改成了 Reg[i] = i ;
- module Reg_test_tb( );
- reg clk;
- reg [4:0]rs;
- reg [4:0]rt;
- reg [4:0]rd;
- reg distSel;
- reg RegWr;
- reg [31:0]dataIn;
- wire [31:0]out1;
- wire [31:0]out2;
-
- Register myReg(clk,rs,rt,rd,distSel,RegWr,dataIn,out1,out2);
-
- initial begin
- clk=0;
- rs=5'b00101;
- rt=5'b00110;
- rd=5'b00111;
- distSel=1;
- RegWr=0;
- dataIn=0;
- end
-
- always @(*) begin
- #5 clk <= ~clk;
- end
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
out1和out2的波形如下:
这是和控制单元一样最为复杂的模块,先来看看需要实现多少个函数:
虽然有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):
- `timescale 1ns / 1ps
-
-
-
- module ALU(
- input [3:0]ALUop,
- input [31:0]in1, //可能是rs或者shamt
- input [31:0]in2, //可能是rt或者imm16
- output reg zero, //用来指示beq是否相等,1相等,0不相等
- output reg[31:0] res
- );
-
- integer i;
-
-
- always @(*) begin
- case (ALUop)
- //求和
- 4'b0000: begin
- res = in1 + in2;
- zero = 0;
- end
- //求差
- 4'b0001: begin
- res = in1 - in2;
- end
- //按位与
- 4'b0010: begin
- res = in1 & in2;
- end
- //按位或
- 4'b0011: begin
- res = in1 | in2;
- end
- //按位异或
- 4'b0100: begin
- res = in1 ^ in2;
- end
- //直接右移,in1是shamt,in2是rt
- 4'b0101: begin
- res = in2 >> in1[4:0];
- end
- //直接左移
- 4'b0110:begin
- res = in2 << in1[4:0];
- end
- //补符号位右移
- 4'b0111: begin
- res = in2 >> in1[4:0];
- if(in2[31]==1'b1) begin
- for(i=0;i<in1;i=i+1) res[31-i] = 1;
- end
- else begin
- for(i=0;i<in1;i=i+1) res[31-i] = 0;
- end
- end
- //判断相等
- 4'b1000: begin
- zero = in1==in2 ? 1'b1 : 1'b0;
- end
- //lui指令:设置rt的高16位
- 4'b1001: begin
- res = {in2[31],in2[14:0],{16{0}}};
- end
- endcase
- end
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
测试一下算术右移功能(ALU_test_tb.v):
- module ALU_test_tb();
- reg[3:0] ALUop;
- reg[31:0] in1;
- reg[31:0] in2;
- wire zero;
- wire[31:0] res;
-
- initial begin
- ALUop = 4'b0111;
- in1 = 5'b00100;
- in2 = 32'hf1234567;
- end
-
- ALU testALU(ALUop,in1,in2,zero,res);
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
该模块用一个四选一的数据选择器来实现,输出的是pc的下一个值,即下一步要运行指的令的地址。
输入的四个信号分别为:
序号 | 功能 | 所服务的指令 |
0 | 正常进行下一条指令 | 正常指令 |
1 | beq或bne指令生效所跳转到的指令地址 | beq,bne |
2 | 无条件跳转 | j |
3 | 跳转到rs内的值 | jr |
jr指令:jr rs 含义:rs寄存器中存有下条指令的地址,pc改为rs内存的值。
PC选择器模块代码(PCselect.v):
- `timescale 1ns / 1ps
-
- //本模块用来选择最后的pc
- module PCselect(
- input [31:0]addedPC, //加过4的PC
- input [31:0]ex_imm32,
- input [25:0]jAddr,
- input [31:0]jr_rs,
- input [1:0]sel,
- output reg[31:0]finalPC
- );
-
- always@(*) begin
- case (sel)
- 0: finalPC <= addedPC; //正常执行下一条指令
- 1: finalPC <= addedPC+(ex_imm32<<2); //beq或者bne跳转指令生效
- 2: finalPC <= {addedPC[31:28],jAddr[25:0],0,0}; //无条件跳转指令
- 3: finalPC <= jr_rs;
- endcase
- end
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
输入:指令的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):
- `timescale 1ns / 1ps
-
- module control(
- input [5:0] op,
- input [5:0] funct,
- input zero,
- // input PC_reset,
- output reg[3:0]ALUop,
- output reg[1:0]PCsel,
- output reg reg_distSel,
- output reg regWr,
- output reg dataWr,
- output reg ALUin1_sel, //选rs还是shamt,0是rs,1是shamt
- output reg ALUin2_sel, //选rt还是imm,0是rt,1是imm
- output reg regIn_sel //选ALUout还是dataMem_out,0是ALUout,1是dataMem_out
- );
-
-
- always @(*) begin
- case(op)
- //r型指令
- 6'b000000:begin
- case(funct)
- //add
- 6'b100000:begin
- // PC_reset <= 0;
- ALUop <= 4'b0000;
- PCsel <= 2'b00;
- reg_distSel <= 1;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 0;
- regIn_sel <= 0;
- end
- //sub
- 6'b100010:begin
- // PC_reset <= 0;
- ALUop <= 4'b0001;
- PCsel <= 2'b00;
- reg_distSel <= 1;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 0;
- regIn_sel <= 0;
- end
- //and
- 6'b100100:begin
- // PC_reset <= 0;
- ALUop <= 4'b0010;
- PCsel <= 2'b00;
- reg_distSel <= 1;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 0;
- regIn_sel <= 0;
- end
- //or
- 6'b100101:begin
- // PC_reset <= 0;
- ALUop <= 4'b0011;
- PCsel <= 2'b00;
- reg_distSel <= 1;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 0;
- regIn_sel <= 0;
- end
- //xor
- 6'b100110:begin
- // PC_reset <= 0;
- ALUop <= 4'b0100;
- PCsel <= 2'b00;
- reg_distSel <= 1;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 0;
- regIn_sel <= 0;
- end
- //sll
- 6'b000000:begin
- // PC_reset <= 0;
- ALUop <= 4'b0110;
- PCsel <= 2'b00;
- reg_distSel <= 1;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 1;
- ALUin2_sel <= 0;
- regIn_sel <= 0;
- end
- //srl
- 6'b000010:begin
- // PC_reset <= 0;
- ALUop <= 4'b0101;
- PCsel <= 2'b00;
- reg_distSel <= 1;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 1;
- ALUin2_sel <= 0;
- regIn_sel <= 0;
- end
- //sra
- 6'b000011:begin
- // PC_reset <= 0;
- ALUop <= 4'b0111;
- PCsel <= 2'b00;
- reg_distSel <= 1;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 1;
- ALUin2_sel <= 0;
- regIn_sel <= 0;
- end
- //jr
- 6'b001000:begin
- // PC_reset <= 0;
- ALUop <= 4'b0111; //无所谓
- PCsel <= 2'b11; //PC选择rs的内容
- reg_distSel <= 0;
- regWr <= 0;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 0;
- regIn_sel <= 0;
- end
- endcase
- end
- //下面是I型指令
- //addi
- 6'b001000:begin
- // PC_reset <= 0;
- ALUop <= 4'b0000;
- PCsel <= 2'b00;
- reg_distSel <= 0;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 1;
- regIn_sel <= 0;
- end
- //andi
- 6'b001100:begin
- // PC_reset <= 0;
- ALUop <= 4'b0001;
- PCsel <= 2'b00;
- reg_distSel <= 0;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 1;
- regIn_sel <= 0;
- end
- //ori
- 6'b001101:begin
- // PC_reset <= 0;
- ALUop <= 4'b0011;
- PCsel <= 2'b00;
- reg_distSel <= 0;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 1;
- regIn_sel <= 0;
- end
- //xori
- 6'b001110:begin
- // PC_reset <= 0;
- ALUop <= 4'b0100;
- PCsel <= 2'b00;
- reg_distSel <= 0;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 1;
- regIn_sel <= 0;
- end
- //lw
- 6'b100011:begin
- // PC_reset <= 0;
- ALUop <= 4'b0000;
- PCsel <= 2'b00;
- reg_distSel <= 0;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 1;
- regIn_sel <= 1;
- end
- //sw
- 6'b101011:begin
- // PC_reset <= 0;
- ALUop <= 4'b0000;
- PCsel <= 2'b00;
- reg_distSel <= 0;
- regWr <= 0;
- dataWr <= 1;
- ALUin1_sel <= 0;
- ALUin2_sel <= 1;
- regIn_sel <= 0;
- end
- //beq
- 6'b000100:begin
- // PC_reset <= 0;
- ALUop <= 4'b1000;
- reg_distSel <= 0;
- regWr <= 0;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 1;
- regIn_sel <= 0;
- if(zero == 1) //1表示相等,要跳转
- PCsel <= 2'b01;
- else
- PCsel <= 2'b00;
- end
- //bne
- 6'b000101:begin
- // PC_reset <= 0;
- ALUop <= 4'b1000;
- reg_distSel <= 0;
- regWr <= 0;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 1;
- regIn_sel <= 0;
- if(zero == 0)
- PCsel <= 2'b01;
- else
- PCsel <= 2'b00;
- end
- //lui,设置rt寄存器的高十六位,后面是16个0
- 6'b001111: begin
- // PC_reset <= 0;
- ALUop <= 4'b1001;
- PCsel <= 2'b00;
- reg_distSel <= 0;
- regWr <= 1;
- dataWr <= 0;
- ALUin1_sel <= 0;
- ALUin2_sel <= 1;
- regIn_sel <= 0;
- end
- //j
- 6'b000010: begin
- // PC_reset <= 0;
- ALUop <= 4'b1001; //无所谓
- reg_distSel <= 0; //无所谓
- PCsel <= 2'b10;
- regWr <= 0;
- dataWr <= 0;
- ALUin1_sel <= 0; //无所谓
- ALUin2_sel <= 1; //无所谓
- regIn_sel <= 0; //无所谓
- end
- endcase
- end
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
这个模块不设置仿真,因为要需要调用很多其他模块。
这个仿真文件是用来统筹所有其他的模块并且进行运行MIPS指令。这里还未写顶层模块
下面直接贴上仿真代码(main_tb.v):
- `timescale 1ns / 1ps
-
- module main_tb( );
- reg clk;
- wire pc_reset; wire push; wire [31:0]pc_out; wire [31:0]pc_in; wire[31:0] plused_pc;
- pc my_pc(pc_in,clk,pc_reset,pc_out,plused_pc);
-
- 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;
- InsMem my_Insmem(pc_out,op,rs,rt,rd,shamt,funct); extender my_extender({rd,shamt,funct},imm32);
-
- wire ctrl_distReg; wire [31:0]reg_dataIn; wire RegWr; wire [31:0]reg_out1; wire [31:0]reg_out2;
- Register my_Register(clk, rs, rt, rd, ctrl_distReg, RegWr, reg_dataIn, reg_out1, reg_out2);
-
- wire [31:0]ALU_in1; wire[31:0]ALU_in2; wire ALUsel_in1; wire ALUsel_in2;
- dataSel_2 ALU1(reg_out1,shamt,ALUsel_in1,ALU_in1); dataSel_2 ALU2(reg_out2,imm32,ALUsel_in2,ALU_in2);
-
- wire [3:0]ALUctrl; wire ALU_zero; wire [31:0]ALU_out;
- ALU my_ALU(ALUctrl,ALU_in1,ALU_in2,ALU_zero,ALU_out);
-
- wire dataWr; wire [31:0]dataOut;
- dataMem mt_dataMem(clk,ALU_out,reg_out2,dataWr,dataOut);
- wire regIn_sel_wire;
- dataSel_2 regIn_sel(ALU_out,dataOut,regIn_sel_wire,reg_dataIn);
-
- wire [1:0]pcSel;
- PCselect my_PCselect(plused_pc,imm32,{rs,rt,rd,shamt,funct},reg_out1,pcSel,pc_in);
-
- control my_control(op,funct,ALU_zero,pc_reset,ALUctrl,pcSel,ctrl_distReg,RegWr,dataWr,ALUsel_in1,ALUsel_in2,regIn_sel_wire);
-
- initial begin
- clk=0;
-
- end
-
-
- always @(*) begin
- #5 clk <= ~clk;
- end
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
在testdata.txt中写入几行指令来测试CPU是否能正常工作。
经过测试后观察波形,这些指令都能够正常运行。
测试过后,将顶层写成一个模块,然后将程序烧入CPU中。
顶层模块代码(top.v):
- `timescale 1ns / 1ps
- //结合所有CPU器件的模块,但还没有通过数码管进行输出
- module top(
- input push,
- input clk_7seg,
- input [1:0]SW,
- output [11:0]display,
- // input clk,
- input pc_reset
- // output effective_clk
-
- );
- wire [31:0]pc_out;
- wire [31:0]pc_in;
- wire [31:0]plused_pc;
- 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;
- wire ctrl_distReg;
- wire [31:0]reg_dataIn;
- wire RegWr;
- wire [31:0]reg_out1;
- wire [31:0]reg_out2;
- wire [31:0]ALU_in1;
- wire [31:0]ALU_in2;
- wire ALUsel_in1;
- wire ALUsel_in2;
- wire [3:0]ALUctrl;
- wire ALU_zero;
- wire [31:0]ALU_out;
- wire dataWr;
- wire [31:0]dataOut;
- wire regIn_sel_wire;
- wire [1:0]pcSel;
-
- wire effective_clk;
- wire [31:0]extended_shamt;
-
- clk_dura my_clkdura(clk_7seg,push,effective_clk);
-
- extend5 shamt_ex(shamt,extended_shamt);
-
- pc my_pc(pc_in,effective_clk,pc_reset,pc_out,plused_pc);
-
- InsMem my_Insmem(pc_out,op,rs,rt,rd,shamt,funct); extender my_extender({rd,shamt,funct},imm32);
- Register my_Register(effective_clk, rs, rt, rd, ctrl_distReg, RegWr, reg_dataIn, reg_out1, reg_out2);
-
- dataSel_2 ALU1(reg_out1,extended_shamt,ALUsel_in1,ALU_in1); dataSel_2 ALU2(reg_out2,imm32,ALUsel_in2,ALU_in2);
- ALU my_ALU(ALUctrl,ALU_in1,ALU_in2,ALU_zero,ALU_out);
- dataMem mt_dataMem(effective_clk,ALU_out,reg_out2,dataWr,dataOut);
- dataSel_2 regIn_sel(ALU_out,dataOut,regIn_sel_wire,reg_dataIn);
- PCselect my_PCselect(plused_pc,imm32,{rs,rt,rd,shamt,funct},reg_out1,pcSel,pc_in);
-
- control my_control(op,funct,ALU_zero,ALUctrl,pcSel,ctrl_distReg,RegWr,dataWr,ALUsel_in1,ALUsel_in2,regIn_sel_wire);
-
- _7seg my_7seg(clk_7seg,SW,pc_out,pc_in,rs,reg_out1,rt,reg_out2,ALU_out,reg_dataIn,display);
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
之前顶层模块把所有参数都写进了模块的参数列表(即input,output)中,但在约束文件中并未将他们接至引脚上,因此会造成输出悬空的现象,在进行硬件implementation会报错如下:
顶层模块仿真(top_test_tb.v):
- `timescale 1ns / 1ps
-
- module top_test_tb();
- reg push;
- reg clk_7seg;
- reg [1:0]SW;
- reg pc_reset;
-
- wire [11:0]display;
- // wire effective_clk;
-
-
- top test_top(push,clk_7seg,SW,display,pc_reset);
-
- initial begin
- push = 0;
- clk_7seg = 0;
- SW = 0;
- pc_reset = 0;
- end
-
- always @(clk_7seg) begin
- #5 clk_7seg <= ~clk_7seg;
- end
-
- always @(push) begin
- #80 push <= ~push;
- end
-
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
指令文件(test.txt):
- 20010008 //add $1,$0,8
- 34020002 //ori $2,$0,2
- 00411820 //add $3,$2,$1
- 00622822 //sub $5,$3,$2
- 00a22024 //and $4,$5,$2
- 00824025 //or $8,$4,$2
- 00084040 //sll $8,$8,1
- ac080004 //sw $8,4($0)
- 8c090004 //lw $9,4($0)
0~2条指令波形(pc:00~08):
3~5条指令波形(pc:0C~14):
6~8条指令(pc:18~20):
本实验还有在basys3板子上展示的要求,要求如下:
为了在数码管上显示,要写一个七段数码管显示的模块,通过当前的pc,rs,rt,ALU_out值来进行显示。
该模块代码(_7seg.v):
- `timescale 1ns / 1ps
-
- module _7seg(
- input clk_7seg,
- input [1:0]SW, //(sw15,sw14)
- input [31:0]pc_now, //当前pc值
- input [31:0]pc_next, //下条pc值
-
- input [4:0]rs, //rs寄存器的编号
- input [31:0]reg_out1, //rs寄存器的内容
-
- input [4:0]rt, //rt寄存器的编号
- input [31:0]reg_out2, //rt寄存器的内容
-
- input [31:0]ALU_out, //ALU结果
- input [31:0]dataOut, //DB总线
-
- output reg[11:0] display
-
- );
-
- //count == T1MS 用来分频
- reg [19:0] count = 0;
- reg [2:0] sel = 0;
- parameter T1MS = 50000;
- always @(posedge clk_7seg) begin
- count <= count+1;
- if(count == T1MS) begin
- count <= 0;
- if(sel==3) sel<=0;
- else sel <= sel+1;
- end
- end
-
- always @(posedge clk_7seg) begin
- case(SW)
- //pc
- 0: begin
- case(sel)
- 0: begin
- display[11:8] <= 4'b0111;
- //第一位输出
- case(pc_now[7:4])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- 1: begin
- display[11:8] <= 4'b1011;
- //第二位输出
- case(pc_now[3:0])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- 2: begin
- display[11:8] <= 4'b1101;
- //第三位输出
- case(pc_next[7:4])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- 3: begin
- display[11:8] <= 4'b1110;
- //第四位输出
- case(pc_next[3:0])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- endcase
- end
- //rs
- 1: begin
- case(sel)
- 0: begin
- display[11:8] <= 4'b0111;
- //第一位输出
- case(rs[4])
- 0: display[7:0] <= 8'b1100_0000;
- 1: display[7:0] <= 8'b1111_1001;
-
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- 1: begin
- display[11:8] <= 4'b1011;
- //第二位输出
- case(rs[3:0])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- 2: begin
- display[11:8] <= 4'b1101;
- //第三位输出
- case(reg_out1[7:4])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- 3: begin
- display[11:8] <= 4'b1110;
- //第四位输出
- case(reg_out1[3:0])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- endcase
- end
- //rt
- 2: begin
- case(sel)
- 0: begin
- display[11:8] <= 4'b0111;
- //第一位输出
- case(rt[4])
- 0: display[7:0] <= 8'b1100_0000;
- 1: display[7:0] <= 8'b1111_1001;
-
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- 1: begin
- display[11:8] <= 4'b1011;
- //第二位输出
- case(rt[3:0])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- 2: begin
- display[11:8] <= 4'b1101;
- //第三位输出
- case(reg_out2[7:4])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- 3: begin
- display[11:8] <= 4'b1110;
- //第四位输出
- case(reg_out2[3:0])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- endcase
- end
- //ALUout:DB数据
- 3: begin
- case(sel)
- 0: begin
- display[11:8] <= 4'b0111;
- //第一位输出
- case(ALU_out[7:4])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- 1: begin
- display[11:8] <= 4'b1011;
- //第二位输出
- case(ALU_out[3:0])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- 2: begin
- display[11:8] <= 4'b1101;
- //第三位输出
- case(dataOut[7:4])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- 3: begin
- display[11:8] <= 4'b1110;
- //第四位输出
- case(dataOut[3:0])
- 4'b0000: display[7:0] <= 8'b1100_0000;
- 4'b0001: display[7:0] <= 8'b1111_1001;
- 4'b0010: display[7:0] <= 8'b1010_0100;
- 4'b0011: display[7:0] <= 8'b1011_0000;
- 4'b0100: display[7:0] <= 8'b1001_1001;
- 4'b0101: display[7:0] <= 8'b1001_0010;
- 4'b0110: display[7:0] <= 8'b1000_0010;
- 4'b0111: display[7:0] <= 8'b1101_1000;
- 4'b1000: display[7:0] <= 8'b1000_0000;
- 4'b1001: display[7:0] <= 8'b1001_0000;
- 4'b1010: display[7:0] <= 8'b1000_1000;
- 4'b1011: display[7:0] <= 8'b1000_0011;
- 4'b1100: display[7:0] <= 8'b1100_0110;
- 4'b1101: display[7:0] <= 8'b1010_0001;
- 4'b1110: display[7:0] <= 8'b1000_0110;
- 4'b1111: display[7:0] <= 8'b1000_1110;
- default : display[7:0] = 8'b1111_1111; //全灭
- endcase
- end
- endcase
- end
- endcase
- end
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
这里的display[0]~diplay[6]分别是数码管的A~G。
消抖原理:FPGA板子上的按键在按下时,由于弹性形变,因此会导致信号不稳定,在极短的时间内会产生多次高电平,因此要在信号保持稳定后再进行读取,这样 能避免按下一次按键后执行多条指令。
脉冲信号为push,写一个模块消抖(clk_dura.v):
- `timescale 1ns / 1ps
-
- module clk_dura(
- input clk,
- input push,
- output reg effective_clk
- );
-
- reg [31:0] count;
- integer i;
- initial begin
- for(i=0;i<32;i=i+1) count[i] = 0;
- end
-
- always @(posedge clk) begin
- if(push && count<=32'd30000000) count = count + 1;
- else if(!push) count = 0;
- end
-
- always @(count) begin
- if(count == 32'd10000000) effective_clk = 1'b1;
- else effective_clk = 0;
- end
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
虽然加了延迟消除抖动,但是在上板子的时候会发现仍会出现按一下跳两条指令的情况,(猜测是板子问题)因此加入了简易的松手检测,如下图:
强行抹除了长按可能造成跳多条指令的影响(计数器到了30ms后不会再增加)。
修改过后按键运行正常。
需要全项目源代码的,链接已经贴在下面,需要自取。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。