前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >单周期CPU

单周期CPU

作者头像
ttony0
发布于 2022-12-26 10:30:05
发布于 2022-12-26 10:30:05
2.5K00
代码可运行
举报
运行总次数:0
代码可运行

一. 实验目的

1、理解 MIPS 常用的指令系统并掌握单周期 CPU 的工作原理与逻辑功能实现。

2、通过对单周期CPU 的运行状况进行观察和分析,进一步加深理解。

二. 实验内容

利用 HDL 语言,基于 Xilinx FPGA basys3 实验平台,用 Verilog HDL 语言或 VHDL 语言来编写,实现单周期CPU 的设计,这个单周期 CPU 至少能够完成 20 条MIPS 指令,至少包含以下指令:

  • 支持基本的内存操作如 lw,sw 指令;
  • 支持基本的算术逻辑运算如 add,sub,and,ori,slt,addi 指令;
  • 支持基本的程序控制如 beq,j 指令;

并将其中的 alu 运算结果在开发板数码管上显示出来。

三. 实验原理

单周期CPU在每一个时钟周期会读取并执行一条指令,然后开始下一条指令的执行。时钟周期根据时钟边沿控制,两个时钟上升沿之间的时间间隔为一个时钟周期,在每一个时钟周期,CPU执行如下操作:

  1. 取指令IF:根据程序计数器PC中的指令地址,从指令存储器中取出一条指令,并且指令自增为PC+4,等待下一步操作;如果有跳转或者分支指令,PC指针也会随之修改。
  2. 译码ID:对上一步中得到的指令进行译码,确定这条指令需要完成的操作,从而产生相应的控制信号,并得到指令所访问的寄存器的位置。
  3. 执行EX:根据指令译码得到的控制信号,进行算术逻辑运算。
  4. 访问存储器MEN:根据数据存储器的数据地址,把数据写入到对应的存储单元或者从存储器中的存储单元中取出数据。
  5. 写回寄存器WB:将指令执行的结果或者数据存储器中得到的数据写回相应的目的寄存器中。

单周期CPU包含控制单元、程序计数器PC、指令存储器、数据存储器、ALU、寄存器堆、加法器与多个复用器,数据通路图如下:

具体设计时,我们使用模块化的方法,对控制单元、存储器、ALU、寄存器堆等各个部分作为各个模块分别进行设计,再利用一个顶层文件将其组合起来,模块化的结构便于各个部件的设计与检查。

我们设计的单周期CPU所执行的MIPS指令,是长度为32 bit的指令格式,分为R型、J型、I型三种类型:

R类型

31-26

25-21

20-16

15-11

10-6

5-0

op

rs

rt

rd

shamt

func

操作码

第1个源操作数寄存器

第2个源操作数寄存器

目的操作数寄存器

位移量

功能码

6位

5位

5位

5位

5位

6位

I类型

31-26

25-21

20-16

15-0

op

rs

rt

immediate

操作码

第1个源操作数寄存器

第2个源操作数寄存器

16位立即数

6位

5位

5位

16位

J类型

31-26

25-0

op

address

操作码

26位地址

6位

26位

四. 实验器材

电脑一台,Xilinx Vivado 软件一套,Basys3板一块。

五. 实验过程与结果

1、组件构成

各个模块的构成如下:

模块名称

模块功能

top

顶层模块,连接各个模块组件,管理其输入输出信号,并进行程序计数器PC运算。

ctr

控制单元模块,通过解析指令op段得到控制信号,将其输出。

ALUctr

ALU控制模块,通过解析指令funct段与控制信号aluOP,得出alu操作码。

ALU

ALU模块,根据alu操作码进行算数逻辑运算,输出结果与零标志位。

Imem

指令存储器模块,根据输入的PC地址读取对应的指令并输出,传递给其他模块进行后续处理。

Dmem

数据存储器模块,使用256x8的模式模拟内存,用于内存读写指令的实现。

regFile

寄存器堆模块,用于存储32个寄存器,根据指令对寄存器进行读写操作。

Shifter

移位模块,用于处理移位指令,得到移位结果。

signext

符号扩展模块,用于立即数扩展。

display

数码管显示模块,用于将ALU结果/移位结果译码并显示到四位数码管上。

2、各模块实现

(1)ctr.v:
输入信号:

信号

功能

opCode

指令op段,第31-26位。

funct

指令func段,第5-0位。

控制信号表(输出信号):

控制单元模块主要得出以下控制信号,各控制信号的功能和相关指令如下表所示:

控制信号

功能

相关指令

regDst

写寄存器的地址,状态0时写寄存器为rt,状态1时写寄存器为rd。

aluSrcA

ALU的第二个输入,状态0时为寄存器rt,状态1时为扩展的立即数。

memToReg

写回寄存器的值,状态0时为ALU运算结果,状态1时为从内存取出的值。

regWrite

是否写回寄存器,状态0时写入寄存器无效,状态1时寄存器堆写使能。

memRead

是否从内存读取数据,状态0时读取无效,状态1时内存读取有效。

memWrite

是否将数据写入内存,状态0时写入无效,状态1时内存写入有效。

imm_expand

立即数扩展方式,状态0时为0扩展,状态1时为符号扩展。

branch

是否分支指令。

aluop

ALU控制信号,3位,用于与funct段结合得出ALU操作码。

jmp

是否跳转指令。

jal

是否跳转链接指令,

jr

是否跳转寄存器指令。

lui

是否立即数高位取指令。

shift

是否移位指令

bne

是否不等分支指令。

bltz

是否小于0分支指令。

bgtz

是否大于0分支指令。

flag

内存操作字节数,2位,00-> byte,01->half word,1x->word

代码实现:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module ctr(
    input [5:0] opCode,//
    input [5:0] funct,//
    output reg regDst,//
    output reg aluSrcA,//
    output reg [1:0] aluSrcB,//
    output reg memToReg,//
    output reg regWrite,//
    output reg memRead,//
    output reg memWrite,//
    output reg branch,//
    output reg [2:0] aluop,//
    output reg jmp,
    output reg jal,
    output reg jr,
    output reg lui,
    output reg shift,
    output reg bne,
    output reg bltz,
    output reg bgtz,
    output reg imm_expand,
    output reg [1:0] flag
);

always @(opCode,funct) 
begin
    case(opCode)
    
        6'b001001: 
        begin
            regDst = 0;    
            aluSrcA = 1'b1;//
            aluSrcB = 2'b01;//
            memToReg = 0;  //
            regWrite = 1;  //
            memRead = 0;   //
            memWrite = 0;  //
            branch = 0;    //
            aluop = 3'b000;//
            jmp = 0;
            jal=0;
            jr=0;
            lui=0; 
            bne=0;
            bltz=0;
            bgtz=0;   
            shift=0;
            imm_expand=0; 
            flag=2'b11;       
        end             
       /*
       以上为控制单元对某一条指令得出控制信号的代码示例,根据输入的opCode,funct得出各个控制信号并输出。
       */
end
endmodule
(2)aluctr.v:
引脚说明:

引脚信号

功能

ALUop

输入信号,ALU控制信号。

funct

输入信号,指令func段,第5-0位。

ALUctr

输出信号,ALU操作码,4位,用于确定不同类型的算术逻辑运算。

ALU一共会执行加、减、与、或、异或、或非、小于设置、lui(左移16位)、左移、逻辑右移、算术右移共11种操作,我们使用四位编码对不同的操作进行表示,ALUctr模块根据指令得到最终的ALU操作码,操作码将传入ALU模块中进行算术逻辑运算。

ALU功能表:

操作

ALU操作码

功能

相关指令

0010

A + B

add、addi、addiu、lw、sw

0110

A - B

sub、beq、bne、bgtz、bltz

0000

A & B

and、andi

0001

A | B

or、ori

异或

0100

A ^ B

xor、xori

或非

0101

~ ( A | B )

nor、nori

小于设置

0111

A < B

slt、slti

左移16位

1100

A « 16

lui

左移

0011

A « B

sllv

逻辑右移

1000

A » B

srlv

算术右移

1111

(sign) A » B

srav

代码实现:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module aluctr(
    input [2:0] ALUOp,
    input [5:0] funct,
    output reg [3:0] ALUCtr
);
always @(ALUOp or funct)  
    casex({ALUOp, funct}) 
        //非R型
        9'b000xxxxxx: ALUCtr = 4'b0010; // add
        9'b001xxxxxx: ALUCtr = 4'b0110; // sub
        9'b010xxxxxx: ALUCtr = 4'b0000; // and
        9'b011xxxxxx: ALUCtr = 4'b0001; // or
        9'b100xxxxxx: ALUCtr = 4'b0100; // xor
        9'b101xxxxxx: ALUCtr = 4'b0111; // slt
        9'b110xxxxxx: ALUCtr = 4'b1100; // lui
        //R型
        9'b111_100000: ALUCtr = 4'b0010; // add
        9'b111_100001: ALUCtr = 4'b0010; // addu
        9'b111_100010: ALUCtr = 4'b0110; // sub
        9'b111_100100: ALUCtr = 4'b0000; // and
        9'b111_100111: ALUCtr = 4'b0101; // nor
        9'b111_100101: ALUCtr = 4'b0001; // or
        9'b111_100110: ALUCtr = 4'b0100; // xor
        9'b111_101010: ALUCtr = 4'b0111; // slt  
                                          
        9'b111_000100: ALUCtr = 4'b0011; // sllv
        9'b111_000110: ALUCtr = 4'b1000; // srlv
        9'b111_000111: ALUCtr = 4'b1111; // srav  
        
        default:ALUCtr = 4'b0010;
    endcase
endmodule
(3)ALU.v:
引脚说明:

引脚信号

功能

input1

输入信号,第一个操作数。

input2

输入信号,第二个操作数。

ALUctr

输入信号,ALU操作码,用于确定不同类型的算术逻辑运算。

ALUres

输出信号,ALU运算结果。

Zero

输出信号,零标志位。

ALU模块用于完成算术逻辑运算,输出有运算结果与零标志,零标志主要用于beq、bne、bltz等指令的判断。

代码实现:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module alu( 
input [31:0] input1, 
input [31:0] input2, 
input [3:0] aluCtr, 
output reg[31:0] aluRes, 
output reg zero 
); 
always @(input1 or input2 or aluCtr)
begin 
case(aluCtr) 

4'b0110: // 减
begin 
  aluRes = input1 - input2; 
  if(aluRes == 0) 
  zero = 1; 
  else 
  zero = 0; 
end 
  
4'b1100: // lui
aluRes =input2 << 16;

4'b0010: // 加
aluRes = input1 + input2; 
  
4'b0100: // 异或
aluRes = input1 ^ input2; 
  
4'b0000: // 与
aluRes = input1 & input2; 
  
4'b0001: // 或
aluRes = input1 | input2; 
  
4'b0101: // 或非
aluRes = ~(input1 | input2); 
  
4'b0011: // 左移
aluRes = input1 << input2; 
  
4'b1000: // 逻辑右移
aluRes = input1 >> input2;
  
4'b1111: // 算术右移
aluRes = $signed(input1) >> input2; 
  
4'b0111: // 小于设置
begin 
  if(input1<input2) 
  aluRes = 1; 
  else
  aluRes = 0; 
end 
  
default: 
aluRes = 0; 
endcase 
end 
  
endmodule 
(4)regFile.v:
引脚说明:

引脚信号

功能

clk

输入信号,时钟信号。

reset

输入信号,重置信号。

regWriteData,

输入信号,写入写寄存器的值,32位。

regWriteAddr

输入信号,写寄存器的地址,5位。

regWriteEn

输入信号,写寄存器使能

RsAddr

输入信号,RS 寄存器地址,5位。

RtAddr

输入信号,RT 寄存器地址,5位。

RsData

输出信号,RS 段寄存器的值,32位。

RtData

输出信号,RT 段寄存器的值,32位。

寄存器堆模块,用于储存32个32位寄存器,并根据寄存器地址对寄存器组进行读写。

输入信号包括寄存器地址、数值与控制信号,其中控制信号regWriteEn控制是否写入寄存器。因为只有32个寄存器,则寄存器的地址只需要5位就可以表示。

同时,对寄存器堆的所有操作需要在时钟和复位信号控制下操作。

代码实现:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module regFile( 
	input clk, 
	input reset, 
  input [31:0] regWriteData, //写寄存器的值
  input [4:0] regWriteAddr, //写寄存器时寄存器的地址
  input regWriteEn,	//写寄存器使能
  input [4:0] RsAddr,	//RS寄存器地址
  input [4:0] RtAddr,		//RT寄存器地址
	output [31:0] RsData,	//RS寄存器的值
  output [31:0] RtData  //RT寄存器的值
);
  
reg[31:0]regs[0:31]; //寄存器组
  
//根据地址读出 Rs、Rt 寄存器数据
assign RsData = (RsAddr == 5'b0 ) ? 32'b0 : regs[RsAddr]; 
assign RtData = (RtAddr == 5'b0 ) ? 32'b0 : regs[RtAddr]; 
integer i;
  
always @( posedge clk ) //时钟上升沿操作
begin
    if(!reset) 
    begin
        if(regWriteEn == 1) //写使能信号为1时写操作
        begin
         regs[regWriteAddr] = regWriteData; //写入数据
        end
    end
    else 
    begin
         for(i = 0; i < 32; i = i + 1)
         regs[i] = 0; //重置时所有寄存器赋值为0,复位       
    end
end
endmodule
(5)Imem.xci:
引脚说明:

引脚信号

功能

clk

输入信号,时钟信号。

ena

输入信号,使能信号。

addra

输入信号,指令所在地址。

douta(inst)

输出信号,根据地址所取指令。

功能实现:

指令存储器通过IP核进行实现,设置为单通道ROM,数据宽度为32位,数据深度为32,即一共可以存储32条32位指令。

指令存储器中每一个地址为1个字节,所以每一条指令对应4个地址,一共4x8=32位;PC每次跳转到下一条指令时,都会将其地址+4;每次取出指令就是将这四个地址单元分别取出,取出后在其它模块中对指令进行分析、操作。

指令存储器的模块接口如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
irom imem (
  .clka(clka),    // 时钟输入
  .ena(ena),      // 使能信号
  .addra(addra),  // 指令所在地址
  .douta(douta)  // 对应指令输出
);
(6)Dmem.v:
引脚说明:

引脚信号

功能

clk

输入信号,时钟信号。

reset

输入信号,重置信号。

flag

输入信号,选择内存操作的字节数。

addr

输入信号,内存地址。

we

输入信号,控制是否将数据写入内存。

re

输入信号,控制是否读取内存数据。

wd

输入信号,写入内存的数据。

memreaddata

输出信号,从内存读取的数据。

数据存储器模块, 用于模拟内存,MIPS指令中只有lw、sw指令会直接对内存进行操作,将数值存进相应地址的内存中或根据取值地址将内存数据取出。用255大小的8位寄存器数组模拟内存,地址采用小端模式。

输入信号包括内存地址、数值,和控制信号we、re分别控制写入、读取使能;而flag作为字节或字、半字操作的标志,所对应存取字大小如下:

flag

存取字大小

00

8位byte

01

16位half word

1x

32位word

代码实现:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module dram (
  input clk, 
  input we,
  input re,
  input reset,
  input [1:0] flag,//00-> byte 01->half word  1x->word
  input [7:0] addr, 
  input [31:0] wd,
  output [31:0] rd
);
			 	 
reg [7:0] RAM[255:0]; 

//read
assign rd=re && flag[1]? 
{ {RAM[a+3]},{RAM[a+2]},{RAM[a+1]},{RAM[a+0]}} : //字
( 
    re && flag[0]?
    { {16{RAM[a+1][7]}} ,{RAM[a+1]},{RAM[a]} }://半字
    { {24{RAM[a][7]}} ,RAM[a]} //字节
);

//write
integer i;
always @ (posedge clk,posedge reset)
begin
    if(reset)begin
        for(i = 0; i < 256; i = i + 1) 
            RAM[i]=0;       
    end	
    else if (we) begin
        if(flag==2'b00)//字节
        begin
            RAM[a]=wd[7:0];
        end
        else if(flag==2'b01 )//半字
        begin
            { {RAM[a+1]},{RAM[a]} }=wd[15:0];
        end
        else if(flag[1])//字
        begin
            { {RAM[a+3]},{RAM[a+2]},{RAM[a+1]},{RAM[a+0]}}=wd;
        end           
    end  
end
endmodule
(7)shifter.v:
引脚说明:

引脚信号

功能

SHIN

输入信号,操作数。

Sa

输入信号,位移量。

ShiDir

输入信号,移位方向。

Arith

输入信号,逻辑移位/算术移位。

SHOUT

输出信号,移位结果。

移位模块用于得到移位结果,实现方式较为简单。

移位指令有sll、srl、sra与sllv、srlv、srav两类,其中sllv、srlv、srav可以使用ALU实现。

但在sll、srl、sra类指令中,由于ALU的第一个操作数只设置为寄存器RS,而移位指令的两个操作数分别是RD与SHAMT,无法同时写入ALU,只能另外启用一个模块实现,并增加一个选择器,用以选择ALU运算结果或移位结果,将结果写回寄存器中。

代码实现:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module Shifter(
		input [31:0] SHIN,
		input [4:0]  Sa,//sa
		input        ShiDir,
		input        Arith,
		output reg [31:0] SHOUT
    );

	always @(*) begin
		if (!ShiDir)      SHOUT = SHIN << Sa ;//逻辑左移
		else if (!Arith) SHOUT = SHIN >> Sa;//逻辑右移
		else       SHOUT = $signed(SHIN) >>> Sa;//算数右移
	end	

endmodule
(8)signext.v:
引脚说明:

引脚信号

功能

inst

输入信号,立即数输入。

imm_expand

输入信号,立即数扩展方式。

data

输出信号,扩展后的立即数。

符号扩展模块,实现也很简单。只需根据符号与立即数扩展方式,将立即数的高位全部补1或者全部补0即可。

代码实现:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module signext( 
input [15:0] inst, // 输入16位
input imm_expand,
output [31:0] data // 输出32位
); 
// 根据符号与立即数扩展方式补充符号位
assign data = imm_expand && inst[15:15]?{16'hffff,inst}:{16'h0000,inst}; 
endmodule 
(9)display.v:
引脚说明:

引脚信号

功能

clk

输入信号,时钟信号。

data

输入信号,用于输出的数据信息。

sm_wei

输出信号,对应四位数码管的某一位。

sm_duan

输出信号,对应当前位数码管的七段码显示。

为了更好地观察实验结果,利用显示模块将运算结果显示到数码管上。将待显示的数据输入显示模块,Basys3板会根据指令显示ALU结果或移位结果。

同时,由于数码管是高频扫描显示,我们输入的时钟信号为高频的CPU时钟,而每条指令的时钟周期经过了分频,停留足够的时间让我们可以观察到,用于显示的时钟周期与CPU的时钟周期并不是相同的时钟信号

代码实现:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module display(clk,data,sm_wei,sm_duan); 

input clk;
input [15:0] data; 
output [3:0] sm_wei; 
output [6:0] sm_duan;
//分频 

integer clk_cnt;
reg clk_400Hz; 
always @(posedge clk)

if(clk_cnt==32'd100000)
begin 
    clk_cnt <= 1'b0; 
    clk_400Hz <= ~clk_400Hz;
end 
else 
clk_cnt <= clk_cnt + 1'b1; 

//位控制
 
reg [3:0]wei_ctrl=4'b1110; always @(posedge clk_400Hz)
wei_ctrl <= {wei_ctrl[2:0],wei_ctrl[3]}; 

//段控制 

reg [3:0]duan_ctrl;
always @(wei_ctrl)
case(wei_ctrl) 
4'b1110:duan_ctrl=data[3:0]; 
4'b1101:duan_ctrl=data[7:4]; 
4'b1011:duan_ctrl=data[11:8];
4'b0111:duan_ctrl=data[15:12];
default:duan_ctrl=4'hf;
 endcase
 
//解码模块 

reg [6:0]duan;
 
always @(duan_ctrl) 
case(duan_ctrl) 
4'h0:duan=7'b100_0000;//0 
4'h1:duan=7'b111_1001;//1 
4'h2:duan=7'b010_0100;//2 
4'h3:duan=7'b011_0000;//3 
4'h4:duan=7'b001_1001;//4 
4'h5:duan=7'b001_0010;//5 
4'h6:duan=7'b000_0010;//6 
4'h7:duan=7'b111_1000;//7 
4'h8:duan=7'b000_0000;//8 
4'h9:duan=7'b001_0000;//9 
4'ha:duan=7'b000_1000;//a 
4'hb:duan=7'b000_0011;//b 
4'hc:duan=7'b100_0110;//c 
4'hd:duan=7'b010_0001;//d 
4'he:duan=7'b000_0111;//e       
4'hf:duan=7'b000_1110;//f
default : 
duan = 7'b100_0000;//0
endcase

assign sm_wei = wei_ctrl; 
assign sm_duan = duan; 
endmodule
(10)top.v:
引脚说明:

引脚信号

功能

clk

输入信号,时钟信号。

reset

输入信号,复位信号。

seg

输出信号,对应当前位数码管的七段码显示。

sm_wei

输出信号,对应哪一个数码管。

顶层模块将整个CPU的各个部件连接起来,并实现了复用器和PC更新的功能。

实际写入板子时,使用了两个时钟信号,第一个时钟信号Clk为CPU系统时钟,频率高,使用这一时钟作为周期无法观察到结果。但Clk需要用来作为数码管显示的周期,因为四位数码管的显示为高频脉冲显示。

所以分频得到了一个时间约为1.5s的时钟信号clkin,作为单周期CPU的周期,这样一来,结果才能在数码管上成功显示。

复用器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
assign mux1 = reg_dst ? inst[15:11] : inst[20:16]; 
//得到写回寄存器rd/rt
assign mux2 = alu_srcA ? expand : RtData; 
//得到立即数/寄存器rd
assign mux6 = shift ? shiftRe : aluRes;
//得到移位结果/ALU结果
assign mux3 = memtoreg ? memreaddata : mux6; 
//得到写回寄存器的数值来自内存/来自计算结果
assign mux7 = jr ? {RsData, 2'b00} : jmpaddr;
//得到跳转地址来自寄存器/指令内26位地址
assign mux5 = jmp ? mux7 : mux4;
//得到跳转地址/分支地址
assign choose5 = bne ? ~zero : zero;
//得到bne的判断结果
assign choose4 = branch & choose5; 
//得到是否分支指令
assign mux4 = choose4 ? address : add4; 
//得到是否分支指令

PC功能:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
assign jmpaddr = {add4[31:28], inst[25:0], 2'b00}; 
//指令内26位地址
assign expand2 = expand << 2; 
assign address = pc + expand2; 
//PC+4
代码实现:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module top( 
input Clk, 
input reset,
output [6:0] seg,
output [3:0] sm_wei
); 
// 指令寄存器pc 
reg[31:0] pc;
reg[31:0] add4; 
wire choose4, choose5; 

// 复用器信号线
wire[31:0] expand2, mux2, mux3, mux4, mux5,mux6,mux7,mux8,mux9, address, jmpaddr; 
wire[4:0] mux1; 
wire [31:0] inst;

// CPU控制信号线
wire reg_dst, jmp, branch, memread, memwrite, memtoreg; 
wire regwrite; 
wire[2:0] aluop; 
wire alu_srcA;

// ALU信号线
wire zero; 
wire[31:0] aluRes; 

// ALU控制信号线
wire[3:0] aluCtr;

// 内存信号线
wire[31:0] memreaddata; 

// 寄存器信号线
wire[31:0] RsData, RtData; 

// 扩展信号线
wire[31:0] expand; 

//拓展
wire jr;   
wire shift;
wire lui;
wire bne;
wire [31:0] shiftRe;
wire imm_expand;
wire [1:0] flag;

//分频得到clkin  
integer clk_cnt;
reg clkin;
always @(posedge Clk)
if(clk_cnt==32'd75_000_000) 
begin
clk_cnt <= 1'b0; 
clkin <= ~clkin;
end 
else
clk_cnt <= clk_cnt + 1'b1;

always @(negedge Clk) // 时钟下降沿操作
    begin 
    if(!reset) 
    begin
        pc = mux5; // 计算下一条pc,修改pc 
        add4 = pc + 4; 
    end 
else 
    begin
    pc = 32'b0; // 复位时pc写0 
    add4 = 32'h4; 
    end 
end 

// 实例化控制器模块
ctr mainctr( 
.opCode(inst[31:26]), 
.funct(inst[5:0]), 
.regDst(reg_dst), 
.aluSrcA(alu_srcA), 
.memToReg(memtoreg), 
.regWrite(regwrite), 
.memRead(memread), 
.memWrite(memwrite), 
.branch(branch), 
.aluop(aluop), 
.jmp(jmp),
.jal(jal),
.jr(jr),
.lui(lui),
.shift(shift),
.bne(bne),
.bltz(bltz),
.bgtz(bgtz),
.imm_expand(imm_expand),
.flag(flag)
); 

// 实例化ALU模块
alu alu(
.input1(RsData), 
.input2(mux2), 
.aluCtr(aluCtr), 
.zero(zero), 
.aluRes(aluRes));

// 实例化ALU控制模块
aluctr aluctr1( 
.ALUOp(aluop), 
.funct(inst[5:0]), 
.ALUCtr(aluCtr)
); 

// 实例化dmem模块
dram dmem( 
.clk(!clkin), 
.we(memwrite), 
.re(memread),
.reset(reset),
.flag(flag),
.a(aluRes[7:0]), 
.wd(RtData), 
.rd(memreaddata) 
); 

// 实例化imem模块
irom imem( 
.clka(!clkin),    // input wire clka
.ena(1'b1),      // input wire ena
.addra(pc[6:2]),  // input wire [4 : 0] addra
.douta(inst)  // output wire [31 : 0] douta
); 

// 实例化寄存器模块
regFile regfile( 
.RsAddr(inst[25:21]), 
.RtAddr(inst[20:16]), 
.clk(!clkin), 
.reset(reset), 
.regWriteAddr(mux1), 
.regWriteData(mux3), 
.regWriteEn(regwrite), 
.RsData(RsData), 
.RtData(RtData) 
); 

//实例化移位模块
Shifter shifte(
.SHIN(RtData),//rt
.Sa(inst[10:6]),//shamt
.ShiDir(inst[1:1]),
.Arith(inst[0:0]),
.SHOUT(shiftRe)//结果->rd
 );

// 实例化符号扩展模块
signext signext(
.inst(inst[15:0]), 
.imm_expand(imm_expand),
.data(expand)); 

//复用器
assign jmpaddr = {add4[31:28], inst[25:0], 2'b00}; 
assign mux1 = reg_dst ? inst[15:11] : inst[20:16]; 
assign mux2 = alu_srcA ? expand : RtData; 
assign mux6 = shift ? shiftRe : aluRes;; 
assign mux3 = memtoreg ? memreaddata : mux6; 
assign mux7 = jr ? {RsData, 2'b00} : jmpaddr;
assign mux5 = jmp ? mux7 : mux4;
assign choose5 = bne ? ~zero : zero;
assign choose4 = branch & choose5; 
assign mux4 = choose4 ? address : add4; 
assign expand2 = expand << 2; 
assign address = pc + expand2; 

//实例化数码管显示模块
smg_ip_model display (
.clk(Clk),
.sm_wei(sm_wei),
.data(mux6[15:0]),
.sm_duan(seg)
);
  
endmodule 

3、仿真检验

将指令转换成二进制代码,一共27类指令。为了测试方便,我们将代码分为三个表格,分别对应运算存取指令、分支指令、跳转指令,同时也为了保证测试时代码数量不会溢出(超过32条)。下面是三类代码对应的表格:

运算、存取测试:

指令

代码

op

rs

rt

rd/imm

addiu $1,$0,-8

2401fff8

001001

00000

00001

11111 11111 111000

addi $1,$0,8

20010008

001000

00000

00001

00000 00000 001000

addu $2,$0,$1

00011021

000000

00000

00001

00010 00000 100001

add $2 $2 $1

00411020

000000

00010

00001

00010 00000 100000

sub $3,$2,$1

00411822

000000

00010

00001

00011 00000 100010

ori $4,$0,3

34040003

001101

00000

00100

00000 00000 000011

or $4,$4,$1

00812025

000000

00100

00001

00100 00000 100101

andi $5,$1,10

3025000a

001100

00001

00101

00000 00000 001010

and $5,$5,$1

00a12824

000000

00101

00001

00101 00000 100100

xori $6,$1,2

38260002

001110

00001

00110

00000 00000 000010

xor $6,$6,$1

00c13026

000000

00110

00001

00110 00000 100110

nor $7,$2,$1

00413827

000000

00010

00001

00111 00001 000000

lui $9,12

3c09000c

001111

00000

01001

00000 00000 001100

slt $10,$4,$3

0083502a

000000

00100

00011

01010 00000 101010

slti $11,$6,4

28cb0004

001010

00110

01011

00000 00000 000100

sll $8,$8,1

00084040

000000

00000

01000

01000 00001 000000

srl $8,$8,1

00084042

000000

00000

01000

01000 00001 000010

sra $8,$8,1

00084043

000000

00000

01000

01000 00001 000011

sllv $8,$1,$8

01014004

000000

01000

00001

01000 00000 000100

srlv $8,$1,$8

01014006

000000

01000

00001

01000 00000 000110

srav $8,$1,$8

01014007

000000

01000

00001

01000 00000 000111

sw $4,4($2)

ac440004

101011

00010

00100

00000 00000 000100

lw $5,4($2)

8c450004

100011

00010

00101

00000 00000 000100

分支测试:

地址

指令

代码

op

rs

rt

rd/imm

00000000

addi $2,$0,-1

2002ffff

001000

00000

00010

11111 11111 111111

00000004

nop

00000000

000000

00000

00000

00000 00000 000000

00000008

lab:nop

00000000

000000

00000

00000

00000 00000 000000

0000000c

addi $2,$2,1

20420001

001000

00010

00010

00000 00000 000001

00000010

bne $2,$0,lab

1440fffb

000101

00010

00000

11111 11111 111011

00000014

beq $2,$0,lab

1040fffd

000100

00010

00000

11111 11111 111101

跳转测试:

地址

指令

代码

op

rs

rt

rd/imm

00000000

addi $1,$0,4

20010004

001000

00000

00001

00000 00000 000100

00000004

jr $1

00200008

000000

00001

00000

00000 00000 001000

00000008

nop

00000000

000000

00000

00000

00000 00000 000000

0000000c

lab:nop

00000000

000000

00000

00000

00000 00000 000000

00000010

nop

00000000

000000

00000

00000

00000 00000 000000

00000014

j lab

0c000003

000011

00000

00000

00000 00000 000011

将二进制代码写入coe文件,存入指令存储器中:

op.coe:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
memory_initialization_radix=16;
memory_initialization_vector=
2401fff8 20010008 00011021 00411020 00411822 34040003 00812025 3025000a 00a12824 38260002 00c13026 00413827 3c09000c 
20080008 20010002 00084040 00084042 00084043 01014004 01014006 01014007 
ac440004 8c450004;

b.coe:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
memory_initialization_radix=16;
memory_initialization_vector=2002ffff 00000000 00000000 20420001 1440fffb 1040fffd 00000000 0000000;

j.coe:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
memory_initialization_radix=16;
memory_initialization_vector=20010004 00200008 00000000 00000000 0c000003;

仿真模块代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module topsim;
// Inputs 
reg clkin;
reg reset;
// output
wire [31:0] inst;
wire [31:0] pc;
wire [2:0] aluop;
wire alu_srcA;
wire [31:0] add4;
wire choose4;
wire reg_dst; 
wire jmp; 
wire branch; 
wire memread; 
wire memwrite;
wire memtoreg; 
wire regwrite;
wire zero;
wire [31:0] aluRes;
wire [3:0] aluCtr;
wire [31:0] memreaddata;
wire [31:0] RsData;
wire [31:0] RtData;
wire [31:0] expand2,expand;
wire[31:0] shiftRe;
wire[31:0]  mux2, mux3, mux4, mux5,mux6, address, jmpaddr; 
wire[4:0] mux1; 
wire imm_expand;
wire shift;
wire [1:0] flag;
// Instantiate the Unit Under Test (UUT) 

//调用top模块
top uut( 
.Clk(clkin),
.reset(reset),
.inst(inst),
.pc(pc),
.aluop(aluop),
.alu_srcA(alu_srcA),
.add4(add4),
.choose4(choose4),
.reg_dst(reg_dst),
.jmp(jmp),
.branch(branch), 
.memread(memread),
.memwrite(memwrite),
.memtoreg(memtoreg),
.regwrite(regwrite),
.zero(zero),
.shift(shift),
.aluRes(aluRes),
.aluCtr(aluCtr),
.shiftRe(shiftRe),
.memreaddata(memreaddata),
.RsData(RsData),
.RtData(RtData),
.expand(expand),
.expand2(expand2), 
.mux2(mux2),
.mux3(mux3), 
.mux4(mux4), 
.mux5(mux5), 
.mux6(mux6), 
.address(address), 
.jmpaddr(jmpaddr),
.mux1(mux1),
.imm_expand(imm_expand),
.flag(flag)
);

initial begin 
// Initialize Inputs 
clkin = 0; 
reset = 1; 
// Wait 100 ns for global reset to finish 
#100; 
reset = 0; 
end 
parameter PERIOD = 20; 
always begin 
clkin = 1'b0; 
#(PERIOD / 2) clkin = 1'b1; 
#(PERIOD / 2) ; 
end 
endmodule 

接下来依次分析各条指令的仿真执行情况:

运算指令:

指令

代码

op

rs

rt

rd/imm

addiu $1,$0,-8

2401fff8

001001

00000

00001

11111 11111 111000

addi $1,$0,8

20010008

001000

00000

00001

00000 00000 001000

addu $2,$0,$1

00011021

000000

00000

00001

00010 00000 100001

add $2 $2 $1

00411020

000000

00010

00001

00010 00000 100000

sub $3,$2,$1

00411822

000000

00010

00001

00011 00000 100010

ori $4,$0,3

34040003

001101

00000

00100

00000 00000 000011

or $4,$4,$1

00812025

000000

00100

00001

00100 00000 100101

andi $5,$1,10

3025000a

001100

00001

00101

00000 00000 001010

and $5,$5,$1

00a12824

000000

00101

00001

00101 00000 100100

xori $6,$1,2

38260002

001110

00001

00110

00000 00000 000010

xor $6,$6,$1

00c13026

000000

00110

00001

00110 00000 100110

nor $7,$2,$1

00413827

000000

00010

00001

00111 00001 000000

lui $9,12

3c09000c

001111

00000

01001

00000 00000 001100

slt $10,$4,$3

0083502a

000000

00100

00011

01010 00000 101010

slti $11,$6,4

28cb0004

001010

00110

01011

00000 00000 000100

以上运算指令的仿真结果图如下:(以下结果均用16进制表示)

接下来对每条指令进行分析:

addiu:

addiu指令,将寄存器0与-8相加,得到无符号数aluRes=fffffff8;

控制信号aluSrcA=1选择立即数;

regwrite=1选择写回寄存器中;

regDst=0选择写回rt;

mux1为写回寄存器的地址,结果写回寄存器1。

addi:

addi指令,将寄存器0与8相加,得到有符号数aluRes=8,

控制信号aluSrcA=1选择立即数,

regwrite=1选择写回寄存器中,

regDst=0选择写回rt,

mux1为写回寄存器的地址,结果写回寄存器2。

addu:

addu指令,将寄存器0与寄存器1相加,得到无符号数aluRes=8,

控制信号aluSrcA=0选择寄存器rt,

regwrite=1选择写回寄存器中,

regDst=1选择写回rd,

mux1为写回寄存器的地址,结果写回寄存器2。

add:

add指令,将寄存器2与寄存器1相加,得到有符号数aluRes=10,

控制信号aluSrcA=0选择寄存器rt,

regwrite=1选择写回寄存器中,

regDst=1选择写回rd,

mux1为写回寄存器的地址,结果写回寄存器2。

sub:

sub指令,将寄存器2与寄存器1相减,得到有符号数aluRes=8,

控制信号aluSrcA=0选择寄存器rt,

regwrite=1选择写回寄存器中,

regDst=1选择写回rd,

mux1为写回寄存器的地址,结果写回寄存器3。

ori:

ori指令,将寄存器0与3相或,得到aluRes=3,

控制信号aluSrcA=1选择立即数,

regwrite=1选择写回寄存器中,

regDst=0选择写回rt,

mux1为写回寄存器的地址,结果写回寄存器4。

or:

or指令,将寄存器4与寄存器1相或,得到aluRes=b,

控制信号aluSrcA=0选择寄存器rt,

regwrite=1选择写回寄存器中,

regDst=1选择写回rd,

mux1为写回寄存器的地址,结果写回寄存器4。

andi:

andi指令,将寄存器1与10相与,得到aluRes=8,

控制信号aluSrcA=1选择立即数,

regwrite=1选择写回寄存器中,

regDst=0选择写回rt,

mux1为写回寄存器的地址,结果写回寄存器5。

and:

and指令,将寄存器5与寄存器1相与,得到aluRes=8,

控制信号aluSrcA=0选择寄存器rt,

regwrite=1选择写回寄存器中,

regDst=1选择写回rd,

mux1为写回寄存器的地址,结果写回寄存器5。

xori:

xori指令,将寄存器1与2相与,得到aluRes=a,

控制信号aluSrcA=1选择立即数,

regwrite=1选择写回寄存器中,

regDst=0选择写回rt,

mux1为写回寄存器的地址,结果写回寄存器6。

xor:

xor指令,将寄存器6与寄存器1异或,得到aluRes=2,

控制信号aluSrcA=0选择寄存器rt,

regwrite=1选择写回寄存器中,

regDst=1选择写回rd,

mux1为写回寄存器的地址,结果写回寄存器6。

nor:

nor指令,将寄存器7与寄存器1或非,得到aluRes=ffffffe7,

控制信号aluSrcA=0选择寄存器rt,

regwrite=1选择写回寄存器中,

regDst=1选择写回rd,

mux1为写回寄存器的地址,结果写回寄存器7。

lui:

lui指令,将数字c存到寄存器9的高16位中,得到aluRes=000c0000,

控制信号aluSrcA=1选择立即数,

regwrite=1选择写回寄存器中,

regDst=0选择写回rt,

mux1为写回寄存器的地址,结果写回寄存器9。

slt:

slt指令比较寄存器4<寄存器3是否成立,若成立,aluRes=1;

从上面的运算我们可以得到,寄存器4的值为b,寄存器3的值为8,b<8不成立,则aluRes=0;

控制信号aluSecA=0选择寄存器rt,

regDst=1选择写回寄存器rd,

mux1=a,结果写回寄存器10。

slti:

slti指令比较寄存器6<立即数4是否成立,若成立,aluRes=1;

从上面的运算我们可以得到,寄存器6的值为2,2<4成立,则aluRes=1;

控制信号aluSecA=1选择立即数,

regDst=0选择写回寄存器rt,

mux1=b,结果写回寄存器11。

移位指令:

指令

代码

op

rs

rt

rd/imm

sll $8,$8,1

00084040

000000

00000

01000

01000 00001 000000

srl $8,$8,1

00084042

000000

00000

01000

01000 00001 000010

sra $8,$8,1

00084043

000000

00000

01000

01000 00001 000011

sllv $8,$1,$8

01014004

000000

01000

00001

01000 00000 000100

srlv $8,$1,$8

01014006

000000

01000

00001

01000 00000 000110

srav $8,$1,$8

01014007

000000

01000

00001

01000 00000 000111

以上六条移位指令的仿真结果图如下:

在初始化时,我们令8=8、1=2,移位指令分为两大类,分别使用移位模块、ALU模块执行运算,接下来分别看各条指令的结果。(以下结果均用16进制表示)

sll:

第一大类的移位指令通过移位模块计算,shift=1表示启用移位模块,shamt=1,将$8左移一位,结果显示在shiftRe中,结果10正确。

srl:

shift=1表示启用移位模块,shamt=1,将$8逻辑右移一位,结果显示在shiftRe中,结果8正确。

sra:

shift=1表示启用移位模块,shamt=1,将$8右移算数一位,结果显示在shiftRe中,由于8为正数,得到运算结果4,正确。

sllv:

第二大类的移位指令通过ALU模块计算,shift=0表示不启用移位模块,1=2,将8左移2位,结果显示在aluRes中,结果10正确。

srlv:

第二大类的移位指令通过ALU模块计算,shift=0表示不启用移位模块,1=2,将8逻辑右移2位,结果显示在aluRes中,结果4正确。

srav:

第二大类的移位指令通过ALU模块计算,shift=0表示不启用移位模块,1=2,将8算术右移2位,结果显示在aluRes中,结果1正确。

存取指令:

指令

代码

op

rs

rt

rd/imm

sw $4,4($2)

ac440004

101011

00010

00100

00000 00000 000100

lw $5,4($2)

8c450004

100011

00010

00101

00000 00000 000100

存取指令将寄存器4的值先存入内存地址为:寄存器2的值+4的位置,再将数据取出至寄存器5。

根据上面指令的运算结果,4=b,2=10,则内存地址应为14,存取的数据为b。接下来看指令的仿真结果图:

sw:

RsData=10,对应寄存器2的值;

RtData=b,对应寄存器4的值;

aluSrcA=1,选择立即数;

expand=4,立即数的值为4;

memwrite=1,写入内存使能;

regwrite=0,不写入寄存器;

aluRes=14,得到内存地址14;

将寄存器4的值b写入内存地址14中。

lw:

RsData=10,对应寄存器2的值;

aluSrcA=1,选择立即数;

expand=4,立即数的值为4;

memwrite=0,不写入内存;

menread=1,读取内存使能;

regwrite=1,写入寄存器;

mux1=5,写入寄存器地址为5;

aluRes=14,得到内存地址14;

将内存地址14中的值(b)写入寄存器5中。

分支指令:

地址

指令

代码

op

rs

rt

rd/imm

00000000

addi $2,$0,-1

2002ffff

001000

00000

00010

11111 11111 111111

00000004

nop

00000000

000000

00000

00000

00000 00000 000000

00000008

lab:nop

00000000

000000

00000

00000

00000 00000 000000

0000000c

addi $2,$2,1

20420001

001000

00010

00010

00000 00000 000001

00000010

bne $2,$0,lab

1440fffb

000101

00010

00000

11111 11111 111011

00000014

beq $2,$0,lab

1040fffd

000100

00010

00000

11111 11111 111101

由于分支测试比较难以判断,我们另外使用一个测试文件,专门用于测试分支指令。 指令如上图所示,在执行两道加法指令之后,寄存器2的值为0,则在bne指令中不会跳转,在beq指令中跳转。为了便于观察,我们添加了几条空指令nop。

测试结果如下:

接下来对指令依次分析:

bne:

add4=14为PC+4;

expand2 = ffff ffec为相对跳转地址,跳转到-5x4=-20处;

bne=1,表示执行bne指令;

zero=1,表示运算结果为0;

choose4=0,choose4判断最终是否执行跳转,为0不执行跳转;

branch=1,表示分支指令。

beq:

add4=18为PC+4;

expand2 = ffff fff4为相对跳转地址,跳转到-4x4=-10处;

zero=1,表示运算结果为0;

choose4=1,choose4判断最终是否执行跳转,为1执行跳转;

branch=1,表示分支指令。

跳转后的指令:

分支跳转之后,PC地址为18-10=8。

跳转指令:

地址

指令

代码

op

rs

rt

rd/imm

00000000

addi $1,$0,4

20010004

001000

00000

00001

00000 00000 000100

00000004

jr $1

00200008

000000

00001

00000

00000 00000 001000

00000008

nop

00000000

000000

00000

00000

00000 00000 000000

0000000c

lab:nop

00000000

000000

00000

00000

00000 00000 000000

00000010

nop

00000000

000000

00000

00000

00000 00000 000000

00000014

j lab

0c000003

000011

00000

00000

00000 00000 000011

为了验证跳转指令,我们另外使用一个测试文件,专门用于测试跳转指令。 指令如上图所示,在执行加法指令之后,寄存器1的值为4,在jr指令中会跳转到4«2=10的指令地址上;同样,j指令会跳转到c的指令地址上。为了便于观察,我们添加了几条空指令nop。

测试结果如下:

jr:

jmp=1,执行跳转指令;

jr=1,执行jr指令;

RsData=4,为寄存器1的值;

mux5=10,为4«2后得到的指令地址。

jr的下一条指令:

执行jr指令后,PC=10,成功跳转到对应地址。

j:

jmp=1,执行跳转指令;

mux5=c,为我们得到的的指令地址。

j的下一条指令:

执行j指令后,PC=c,成功跳转到对应地址。

4、Basys3板实现

引脚设置:

输入输出的引脚如下:

输入为时钟信号Clk、复位信号Reset,Clk连接系统CPU时钟,复位信号连接按钮开关U18。

输出信号为各个数码管显示所需的高低电平信号,分别为4个位选信号和7个段选信号。具体连接方式如下:

仿真文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
set_property PACKAGE_PIN U7 [get_ports {seg[6]}]
set_property PACKAGE_PIN V5 [get_ports {seg[5]}]
set_property PACKAGE_PIN U5 [get_ports {seg[4]}]
set_property PACKAGE_PIN V8 [get_ports {seg[3]}]
set_property PACKAGE_PIN U8 [get_ports {seg[2]}]
set_property PACKAGE_PIN W6 [get_ports {seg[1]}]
set_property PACKAGE_PIN W7 [get_ports {seg[0]}]
set_property PACKAGE_PIN U2 [get_ports {sm_wei[0]}]
set_property PACKAGE_PIN U4 [get_ports {sm_wei[1]}]
set_property PACKAGE_PIN V4 [get_ports {sm_wei[2]}]
set_property PACKAGE_PIN W4 [get_ports {sm_wei[3]}]
set_property PACKAGE_PIN W5 [get_ports Clk]
set_property PACKAGE_PIN U18 [get_ports reset]

set_property IOSTANDARD LVCMOS33 [get_ports {seg[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sm_wei[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sm_wei[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sm_wei[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sm_wei[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports Clk]
set_property IOSTANDARD LVCMOS33 [get_ports reset]

端口映射:

运行结果:

数码管显示的结果为运算结果/PC地址,这里展示了不同类几条指令的显示结果,结果都与上述仿真结果一致:

add指令:运算结果为8。

sub指令:运算结果为8。

or指令:运算结果为b。

xor指令:运算结果为2。

nor指令:运算结果为ffe7。

lui指令:运算结果为0000。(高4位为000c无法显示)

sll指令:运算结果为10。

srl指令:运算结果为8。

sra指令:运算结果为4。

beq指令:当前地址为0014,分支跳转到0008。

j指令:当前地址为0014,分支跳转到000c。

六. 实验心得

本次实验是在之前实现各个小模块的基础上,将其整合起来,完成一个真正意义上的单周期CPU。这需要我们对CPU的工作原理有一个清晰的认识,而且对各个模块之间的联系、对每条指令控制信号的分析也需要清楚。

在实验里首先遇到的问题是语法问题,对我们来说,verilog语言是一个新的语言,虽然我们在之前的实验里,已经学会了看懂代码、理解其中的意思,但实际写代码的时候总会遇到各种各样的错误,有时候vivado的报错也让人花了很多时间。为了解决这些问题,我参考了网上的资料与以前实验的代码。

另外,对于各个模块的测试尽量先分别进行测试,在测试结果正确后再进行组合。而实验比较难的一部分是对各个指令的控制信号的测试。控制信号联系了各个模块,对控制信号进行分析时需要联系多个模块,并且对指令本身的功能需要完全理解。在根据控制信号进行测试花的时间是最长的,在测试遇到问题的时候,也需要仔细确认是哪一个模块出了问题。


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-12-17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
ERP vs MRP,傻傻分不清楚?
ERP和MRP听起来非常相似,以至于许多人认为它们是出于相同的原因而使用的系统。然而,这两个系统却有不同的目的。下面,让我们仔细看看制造业中的这两个系统。
人称T客
2019/05/08
1.4K0
ERP vs MRP,傻傻分不清楚?
企业用户ERP选型还需“门当户对”,必看 50 谏言!
T客汇官网:tikehui.com 译者 |杨丽 选择一款合适的 ERP 软件并非易事。 首先,ERP 系统的概念不断扩大,涉及的领域也在逐渐扩张。企业选购一款合适的 ERP 系统需要 RFI(信息邀
人称T客
2018/03/26
9650
企业用户ERP选型还需“门当户对”,必看 50 谏言!
CIO要谨防销售忽悠 选择ERP供应商如何才能做到万无一失?
ERP系统可以在管理内外部资源,保持业务平稳运营上提供很大帮助,ERP平台还可能是累赘,让CIO为难是否应该付出必须的资金和工作。本指南将介绍如何克服ERP实施障碍,平衡增长的可见度和效率。本指南还包含了厂商选择、预算等ERP信息。 ERP评估 对ERP产品的初期了解可以通过众多供应商开展,明智地选择ERP供应商和产品非常重要。制定可行的项目时间计划、预算和期望值,并与潜在供应商密切接触。紧密地合作可以建立更稳固的关系以实现双赢。下面让我们来分析如何能够平衡两者之间的关系并选择适合自身需要的供应商和ER
人称T客
2018/03/19
8960
ERP和MES的区别与联系,这篇接地气的文章终于讲明白了!
作为一个工厂,存在的目的只有两个,生产产品,然后卖出去。所以在工业企业中,通常会分为两个大的部门,一个是生产部门,一个是业务部门,前者通过MES(制造执行系统)管理,后者通过ERP(管理信息系统)来管理。
informat低代码
2023/12/07
9.6K0
浅谈ERP数据的重要性
影响一个ERP项目的因素有很多,数据无疑是其中很重要的一项,正所谓“正确的诊断源于准确的信息,准确的信息基于可靠的采集”,当我们抓住数据这个根基,大处着眼,小处着手的时候,我们距离ERP成功的日子就不会太远。
matinal
2023/10/12
2630
ERP系统上线成功与失败是否存在判断标准?
导读:各个企业的目的目标不同,成功的标准也可能不同。但是,其中还是有一些共性的评价参数,参考平衡记分法的精神,归纳为四类。
齐天大圣
2021/06/15
1K0
ERP系统上线成功与失败是否存在判断标准?
2025 SAP ERP上云的十大优势
ERP云系统的优势很多,且因企业而异。例如,专业服务企业选择 ERP系统的理由会与制造企业大相径庭。
SNP数据迁移
2025/01/20
1371
2025 SAP ERP上云的十大优势
如何一步步实施ERP规划?
导读:近年来,随着ERP在市场的广泛实施,可见到有些企业实施信息化管理不顺畅和不理想的案例,其中有不少企业实施困难多的直接原因是缺乏整体和全面的实施规划。
齐天大圣
2021/06/17
5970
如何一步步实施ERP规划?
ERP系统的效益评估与应用价值
1 引言 如何获取ERP的企业价值是ERP实施应用的核心问题,传统的实践和研究活动主要关注ERP的实施过程,然而持续改进确是获取企业价值的ERP生命周期的“长尾”。克里斯安德森在其《长尾理论》一书中揭示了传统需求曲线中过去常常被忽视的那条无穷长的尾巴所带来的巨大财富,而这一理论也同样适用于ERP领域。所有ERP系统实施方法论的最后一个阶段都是一个长长的尾巴,称为“持续改进”,并且被认为是企业获取价值的根本保障。 然而,由于ERP项目的阶段性特征,ERP实施顾问通常在系统上线之后就结束实施项目,将“持续改进”留给企业客户,而企业用户在该领域知识匮乏,因此往往使持续改进这个“长尾”被人所忽视。当人们质疑ERP企业价值的时候,“没有进行持续改进”常常成为一个不需要负责任的借口。ERP持续改进是一个伴随ERP应用的长期过程,因此本文认为ERP持续改进需要与ERP实施有一个明显的阶段划分,对于多数企业来讲,实施项目的结束,仅仅完成了企业管理重塑的80%,这80%更多的是ERP系统的共性特征,而真正体现和强化企业竞争优势的部分是在共性特征上的个性特征的塑造,虽然只有20%,但确实企业价值创造的“长尾”。 由于ERP持续改进的长期性和个性化特征,这就使得这一过程更难于管理和操作,方法之一就是设定阶段性的效益目标和持续改进的关键活动。很多学者就ERP冲效益问题进行了研究,从管理和经济角度,提出了许多定性、定量的评估方法,多数ERP效益评估重点关注ERP的投资回报,更关注外在的结果;王惠芬提出ERP系统实施应用过程中的企业管理模式趋同理论,使得ERP效益评估进一步关注企业内部,其著作中提出的“成熟度模型”强化了过程思想。然而,该理论中仍将研究重点放在实施过程,并且企业管理模式的辨识的可操作性不强。 与传统信息系统相比,ERP具有更高的战略地位,不仅提供基本的信息工具,而且在企业范围内建立起新的管理模式,逐步成为数字企业的核心。因此,面向持续改进的ERP效益评估,不仅要突出ERP的阶段性效益,更要反映出企业在管理模式变化,在重塑企业DNA过程中所发生的变化,能够具有较强的阶段对比性,以及良好的方向导向性。 2 持续改进的特点 本文在分析了国内大量有关ERP持续改进的文献后,认为目前国内的ERP持续改进工作具有以下三个特点: 2.1 持续改进的反复性 ERP的持续改进是一个长期的过程,需要不断的进行分析、修正。针对模块的应用首先要做两项工作:一方面要使已经上线的模块能够更平稳的运行,扩大企业效益;另一方面是对系统已有但尚未应用的功能进行深度的应用。因为ERP是一个非常庞大的系统,上线部分其实只是基本功能的基本应用,相对于系统本身具备的全部功能还差得很远。同时还有一些业务不能直接满足,可能需要做一些开发。对功能深入应用主要分为四个步骤:首先要综合系统的功能,对已经在系统或者没有在系统中运行的流程进行分析:其次分析两者之间的运行差异,找到系统功能可以增强的点;再次,综合系统功能设计新方案;最后,调整运行方式并实施。 2.2 持续改进是对管理的不断提升 ERP实施的主要目的是系统通过先进的管理思想规范企业流程,使企业获得效益。所以如何评估ERP的效益,成为持续改进的一个重要课题。在ERP解决方案中对企业的愿景进行了描述,而企业的高层应该定期以此为标准,对整套ERP系统进行评估,确保系统的总体运行与系统运行的目标相一致。持续改进是对管理提升的支撑,随着企业环境的变化必然会导致业务的变化,同时企业对管理的认识也在不断的深入,诸多因素导致企业不可避免地要进行组织、流程等方面的调整,相应的,系统要能支撑这样的变化。管理提升与系统应用是相辅相成的关系,系统应用的深化同样可以促使管理水平的提升。比如系统里的平衡计分卡功能,要求企业的绩效体系有非常高的水平,这是一个管理与IT应用相互交融不断循环的过程。 2.3 持续改进是技术和业务趋同的过程 持续改进的过程应该是一个保持系统与业务需要相匹配的过程。企业高层应该认识到,ERP项目组不单单是一个项目小组,同时也是一个企业业务运作小组。它的作用不仅仅是维护系统本身的问题,更重要的是要关注系统是否能够很好的满足不断变化的业务需求,并同时提出业务流程优化和改进的建议。一般来说,ERP项目小组由企业各业务部门骨干和信息技术部门的人员组成,在项目实施的过程中,无论是来自业务部门的成员还是来自信息技术部门的成员,都对企业的业务和系统有了新的认识,并且最清楚如何使系统和业务更好地融合在一起。所以,这批ERP冲项目小组的骨干成员都需要保留下来,只有他们才能在持续改进的过程中更好地使系统支持业务的发展。同时企业内部的信息主管在ERP项目完成之后应该重新考虑信息
matinal
2020/11/27
1.4K0
如何开发全球一流的云迁移战略
云计算解决方案提供商提供了更多的选择,这些选择使企业可以选择一条最能满足其战略需求和风险状况的路径。但是,确定正确的路径要求企业在考虑所有备选方案之前先做好功课,以确保找到最符合商业目标的战略。
静一
2020/07/14
3550
CRM/PLM/SCM/MES与ERP的联系与区别
企业通过专设信息机构、信息主管,配备适应现代企业管理运营要求的自动化、智能化、高技术硬件、软件、设备、设施,建立包括网络、数据库和各类信息管理系统在内的工作平台,提高企业经营管理效率的发展模式。那么,在企业信息化建设中所用到的技术CRM、PLM、SCM、MES与ERP这些概念之间又有什么联系与区别呢?
用户5495712
2019/10/31
2.4K0
如何选择适合企业的仓库管理系统:ERP或WMS系统?
WMS系统主要用于管理库存的存储和移动,跟踪每个库存项目的移动,例如接收、拣选、包装和运输的项目。WMS系统基于实时信息提供库存优化,可以根据历史趋势和数据生成信息,以显示要放置的每个项目的最佳位置。WMS通常是独立系统,需要其他模块,例如会计和客户关系管理。
云坞网络-Peter
2023/06/12
3380
如何选择适合企业的仓库管理系统:ERP或WMS系统?
先有ERP,再谈中台、BI、低代码
随着IT技术发展,与传统企业的运营改革,越来越多的企业开始借助IT系统工具提升管理能力,但是方向却不太正确。
科控物联
2023/12/11
3120
先有ERP,再谈中台、BI、低代码
如何保证ERP系统在上线后运营稳定?
  大家以为从此以后可以松口气,但事实恰恰相反。原来攻坚战组成的组员陆续回到原来的部门,ERP项目组只剩下IT部门的人,“攻坚战”的胜利很快遭到了质疑。因为新系统大家还没用熟,在细节上总出现一些问题,大家对新系统的埋怨也开始越来越多。ERP上线后,失去了最初的赞扬和掌声,反而面临了很多困难与痛苦,那么如何克服这些困难?有什么良药能医治ERP上线后的困扰呢?
用户5495712
2019/11/21
9310
CIO必读:用友U8、金蝶K/3、易飞三款ERP产品优劣分析比
一、用友U8   用友U8,在用友多年财务软件的开发经验的基础上,以财务管理为企业的目标核心,以业务管理为企业的行为核心,突破了平行思考的串行的价值链结构,提出了基于立体价值链结构的产品体系部署原理,适应了中国企业在不同发展阶段对于企业管理需求的不同特点。包括了单一企业模式、具有分支机构的单一企业模式、产业型和投资型的企业集团及连锁经营模式等。   1、界面   该软件秉承了原有的财务管理软件的版面风格,整体界面比较实用,但是比较简单。   2、功能   用友ERP-U8 财务管理,包括了财务会计
人称T客
2018/03/19
3.6K0
企业管理软件ERP为什么要上云?
在网上搜索“ERP”或“企业资源计划”时,各种信息铺天盖地——别提有多困惑了。每个网站对ERP好像都有自己的定义,每个ERP实施都不尽相同。虽说定义众多,但基本都强调了ERP系统的灵活性,正是这一点使其成为了一款强大的企业管理工具。
用户5495712
2020/06/10
2.1K0
ERP软件是否包括人力资源软件
企业使用企业资源计划(ERP)软件来集成和管理涉及信息技术,服务和人力资源的常见任务。ERP主要是为大型企业设计的,具有支持广泛管理系统所需的资源,ERP分为针对公司特定区域的模块。人力资源(HR)管理是用于自动化管理工作的一个基本模块,否则这些工作将是乏味且耗时的。
深圳ERP
2019/12/12
7610
ERP软件是否包括人力资源软件
​BPM和ERP、OA的区别关系
经常听到ERP、BPM、OA这些词,企业在ERP、BPM上个先上哪个后上方面也有很多讨论,今天简单整理一篇他们三者的关系说明 ,希望对大家理解和使用它们有帮助。 一:ERP与BPM ERP与BPM:ERP(Enterprise Resource Planning,企业资源计划),以MRP(物料资源计划)、MRPII为核心,其管理思想一般围绕供应链、生产制造和财务为核心。而BPM从提升企业整体业务绩效、降低反馈周期和适应变化调整为目标,以流程为导向焦聚客户体验和核心价值。首先在实施范围上,ERP以企业的业务
程序源代码
2018/03/09
2.3K0
​BPM和ERP、OA的区别关系
BPM与ERP软件的区别
即使您不了解ERP和BPM之间的所有差异,您也可能知道它们有一个共同的目标,即提高生产率和增加利润。尽管有这种共性,但它们不是可互换的系统。根据您的特定业务目标和功能需求为组织选择正确的软件至关重要。如果选择不正确,可能会浪费时间和金钱。软件选择可能很复杂并且很耗时,但是如果正确完成,将会带来很多积极的好处。
深圳ERP
2019/12/05
2K0
5个使用ERP软件改进业务流程的方法
组织变更管理和计划将是实施项目成功的主要因素。让组织的各个层次参与该项目是一个很好的开始,但是随着对日常运营的变更的开始,制定一个具体的计划将有助于加快用户采用速度,并避免错误信息和混乱情况的传播。成功的变更管理策略牢记公司的使命,并向员工明确说明这些变更将如何帮助公司实现其目标,从而形成共同的变更愿景。必须制定一个计划,以教育员工如何改变其日常工作的细节,并奖励鼓励个人承担新角色和责任的奖励。
深圳ERP
2019/12/10
5100
5个使用ERP软件改进业务流程的方法
推荐阅读
相关推荐
ERP vs MRP,傻傻分不清楚?
更多 >
LV.1
深圳市鑫北斗软件运营
目录
  • 一. 实验目的
  • 二. 实验内容
  • 三. 实验原理
  • 四. 实验器材
  • 五. 实验过程与结果
    • 1、组件构成
    • 2、各模块实现
      • (1)ctr.v:
      • (2)aluctr.v:
      • (3)ALU.v:
      • (4)regFile.v:
      • (5)Imem.xci:
      • (6)Dmem.v:
      • (7)shifter.v:
      • (8)signext.v:
      • (9)display.v:
      • (10)top.v:
    • 3、仿真检验
      • 运算指令:
      • 移位指令:
      • 存取指令:
      • 分支指令:
      • 跳转指令:
    • 4、Basys3板实现
      • 引脚设置:
      • 运行结果:
  • 六. 实验心得
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档