单周期CPU设计与实现

Posted by LvKouKou on November 29, 2022

单周期CPU设计与实现

一. 实验目的

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

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

二. 实验内容

实验的具体内容与要求。

  1. 利用 HDL 语言,基于 Xilinx FPGA basys3 实验平台,用 Verilog HDL 语言或 VHDL 语言来编写,实现单周期CPU 的设计,这个单周期 CPU 能够完成 多于16 条MIPS 指令,至少包含以下指令:
    • 支持基本的内存操作如 lw,sw 指令
    • 支持基本的算术逻辑运算如 add,sub,and,ori,slt,addi 指令
    • 支持基本的程序控制如 beq,j 指令
  2. 掌握各个指令的相关功能并输出仿真结果进行验证,并最后在 FGPA 上实现,将其中的 运算结果在开发板数码管上显示出来。

  3. 拓展添加其他指令及功能。

要求:

1、PC和寄存器组写状态使用 时钟边缘触发。

2、指令存储器存储单元宽度使用32位,数据存储器存储单元宽度使用8位,(便于添加字节存取)即一个字节的存储单位。深度的选择满足指令要求即可(测试代码的长度)。

3、控制器部分要学会用控制信号真值表方法分析问题并写出逻辑表达式;或者用case语句方法逐个产生各指令控制信号。

4、基本实现必须按统一测试用的汇编程序段进行测试所设计的CPU,增加功能设计可自行编写测试代码,包含所设计的所有指令。

5、必须注意,实验报告中,对每条指令必须有指令执行的波形(截图),且图上必须包含关键信号,同时还要对关键信号以文字说明,这样才能说明该指令的正确性。

三. 实验原理

简述实验原理和方法,必须有数据通路图及相关图

1. 单时钟周期CPU

单周期 CPU 的特点是每条指令的执行只需要一个时钟周期,一条指令执行完再执行下一条指令。再这一个周期中,完成更新地址,取指,解码,执行,内存操作以及寄存器操作。由于每个时钟上升沿时更新地址,因此要在上升沿到来之前完成所有运算,而这所有的运算除可以利用一个下降沿外,只能通过组合逻辑解决。这给寄存器和存储器RAM的制作带来了些许难度。且因为每个时钟周期的时间长短必须统一,因此在确定时钟周期的时间长度时,要依照最长延迟的指令时间来定,这也限制了它的执行效率。

单周期CPU在每个CLK上升沿时更新PC,并读取新的指令。此指令无论执行时间长短,都必须在下一个上升沿到来之前完成。其时序示意如图Ⅰ。

img

图I 单时钟周期CPU时序示意图

下图是一个单周期 CPU 的顶层结构实现。主要器件有程序计数器PC、程序存储器、寄存器堆、ALU、数据存储器和控制部件等。所有的控制信号简单地说明如下:

img

图Ⅱ 单时钟周期CPU详细逻辑设计图参考

其中,控制单元(Ctrl Unit)定义如下:

(1)regDst:目的地址,为1时,选择rd;为0时,选择rt。

(2)aluSrc:ALU操作数B的选择,为1时,选择扩展的立即数;为0时,选择寄存器数据;

(3)memToReg:为1时,选择存储器数据;为0时,选择ALU 输出的数据;

(4)regWrite:为1时写入寄存器堆,目的寄存器号是由RegDst选出的rt或rd,写入数据是由MemToReg选出的存储器数据或ALU的输出结果;

(5)memRead

(6)memWrite:为1时写入存储器。存储器地址由ALU的输出决定,写入数据为寄存器rt的内容;

(7)branch:为1时,选择转移目标地址;为0时,选择PC +4(图中的 NextPC);

(8)ExtOp:符号扩展。为1时,符号扩展;为0时,0扩展;

(9)aluop:ALU控制码;(具体表示见控制器的设计)

(10)jmp:为1时,选择跳转目标地址;为 0时,选择由Branch选出的地址;

(11)jal:为1时,写入地址选择31号寄存器,写入内容为PC+4

2. MIPS指令集

本次实验共涉及三种类型的MIPS指令,分别为R型、I型和J型,三种类型的MIPS指令格式定义如下:

  • R(register)类型的指令从寄存器堆中读取两个源操作数,计算结果写回寄存器堆;

  • I(immediate)类型的指令使用一个 16位的立即数作为一个源操作数;

  • J(jump)类型的指令使用一个 26位立即数作为跳转的目标地址(target address);

img

图Ⅲ MIPS指令集

一条指令的执行过程一般有下面的五个阶段,指令的执行过程就是这五个状态的重复过程:

img

图IV MIPS指令集

在本次实验中,完成了下列31条指令的功能,包括R型、I型和J型。

MIPS的31条指令

助记符

指 令 格 式

示 例

示例含义

操作及解释

BIT #

31..26

25..21

20..16

15..11

10..6

5..0

R-类型

op

rs

rt

rd

shamt

func

add

000000

rs

rt

rd

00000

100000

add $1,$2,$3

$1=$2+S3

(rd)←(rs)+(rt); rs=$2,rt=$3,rd=$1

addu

000000

rs

rt

rd

00000

100001

addu $1,$2,$3

$1=$2+S3

(rd)←(rs)+(rt); rs=$2,rt=$3,rd=$1,无符号数

sub

000000

rs

rt

rd

00000

100010

sub $1,$2,$3

$1=$2-S3

(rd)←(rs)-(rt); rs=$2,rt=$3,rd=$1

subu

000000

rs

rt

rd

00000

100011

subu $1,$2,$3

$1=$2-S3

(rd)←(rs)-(rt); rs=$2,rt=$3,rd=$1,无符号数

and

000000

rs

rt

rd

00000

100100

and $1,$2,$3

$1=$2&S3

(rd)←(rs)&(rt); rs=$2,rt=$3,rd=$1

or

000000

rs

rt

rd

00000

100101

or $1,$2,$3

$1=$2|S3

(rd)←(rs) | (rt); rs=$2,rt=$3,rd=$1

xor

000000

rs

rt

rd

00000

100110

xor $1,$2,$3

$1=$2^S3

(rd)←(rs)^(rt); rs=$2,rt=$3,rd=$1

nor

000000

rs

rt

rd

00000

100111

nor $1,$2,$3

$1= ~($2 | S3)

(rd)←~((rs) | (rt)); rs=$2,rt=$3,rd=$1

slt

000000

rs

rt

rd

00000

101010

slt $1,$2,$3

if($2<$3)

$1=1 else

$1=0

if (rs< rt) rd=1 else rd=0;rs=$2,rt=$3, rd=$1

sltu

000000

rs

rt

rd

00000

101011

sltu $1,$2,$3

if($2<$3)

$1=1 else

$1=0

if (rs< rt) rd=1 else rd=0;rs=$2,rt=$3, rd=$1, 无符号数

sll

000000

00000

rt

rd

shamt

000000

sll $1,$2,10

$1=$2<<10

(rd)←(rt)<<shamt,rt=$2,rd=$1,shamt=10

srl

000000

00000

rt

rd

shamt

000010

srl $1,$2,10

$1=$2>>10

(rd)←(rt)>>shamt, rt=$2, rd=$1, shamt=10, (逻辑右移)

sra

000000

00000

rt

rd

shamt

000011

sra $1,$2,10

$1=$2>>10

(rd)←(rt)>>shamt, rt=$2, rd=$1, shamt=10, (算术右移,注意符号位保留)

sllv

000000

rs

rt

rd

00000

000100

sllv $1,$2,$3

$1=$2<<$3

(rd)←(rt)<<(rs), rs=$3,rt=$2,rd=$1

srlv

000000

rs

rt

rd

00000

000110

srlv $1,$2,$3

$1=$2>>$3

(rd)←(rt)>>(rs), rs=$3,rt=$2,rd=$1, (逻辑右移)

srav

000000

rs

rt

rd

00000

000111

srav $1,$2,$3

$1=$2>>$3

(rd)←(rt)>>(rs), rs=$3,rt=$2,rd=$1, (算术右移,注意符号位保留)

jr

000000

rs

00000

00000

00000

001000

jr $31

goto $31

(PC)←(rs)

I-类型

op

rs

rt

immediate

addi

001000

rs

rt

immediate

addi $1,$2,10

$1=$2+10

(rt)←(rs)+(sign-extend)immediate,rt=$1,rs=$2

addiu

001001

rs

rt

immediate

addiu $1,$2,10

$1=$2+10

(rt)←(rs)+(sign-extend)immediate,rt=$1,rs=$2

andi

001100

rs

rt

immediate

andi $1,$2,10

$1=$2&10

(rt)←(rs)&(zero-extend)immediate,rt=$1,rs=$2

ori

001101

rs

rt

immediate

ori $1,$2,10

$1=$2|10

(rt)←(rs)|(zero-extend)immediate,rt=$1,rs=$2

xori

001110

rs

rt

immediate

xori $1,$2,10

$1=$2^10

(rt)←(rs)^(zero-extend)immediate,rt=$1,rs=$2

lui

001111

00000

rt

immediate

lui $1,10

$1=10*65536

(rt)←immediate<<16 & 0FFFF0000H,将16位立即数放到目的寄存器高16位,目的寄存器的低16位填0

lw

100011

rs

rt

offset

lw $1,10($2)

$1=Memory[

$2+10]

(rt)←Memory[(rs)+(sign_extend)offset],

rt=$1,rs=$2

sw

101011

rs

rt

offset

sw $1,10($2)

Memory[

$2+10] =$1

Memory[(rs)+(sign_extend)offset]←(rt),

rt=$1,rs=$2

beq

000100

rs

rt

offset

beq $1,$2,40

if($1=$2)
goto PC+4+40

if ((rt)=(rs)) then (PC)←(PC)+4+( (Sign-Extend) offset<<2), rs=$1, rt=$2

bne

000101

rs

rt

offset

bne $1,$2,40

if($1≠$2)
goto PC+4+40

if ((rt)≠(rs)) then (PC)←(PC)+4+(

(Sign-Extend) offset<<2) , rs=$1, rt=$2

slti

001010

rs

rt

immediate

slti $1,$2,10

if($2<10)
$1=1 else
$1=0

if ((rs)<(Sign-Extend)immediate) then (rt)←1; else (rt)←0, rs=$2, rt=$1

sltiu

001011

rs

rt

immediate

sltiu $1,$2,10

if($2<10)
$1=1 else
$1=0

if ((rs)<(Zero-Extend)immediate) then (rt)←1; else (rt)←0, rs=$2, rt=$1

J-类型

op

address

j

000010

address

j 10000

goto 10000

(PC)←( (Zero-Extend) address<<2),

address=10000/4

jal

000011

address

jal 10000

$31=PC+4

goto 10000

($31)←(PC)+4;

(PC)←( (Zero-Extend) address<<2),

address=10000/4

图V 指令详细结构

add和addu区别是有无溢出检测

四. 实验器材

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

五. 实验过程与结果

1. CPU的设计

该MIPS subset单周期分五个执行阶段,可分功能模块设计,各个子模块分别设计其特定的功能,最终利用一个总的模块进行子模块间连接,使得整个CPU能连贯执行指令,在仿真结果中观察设计结果,最终进行硬件下载,验证设计。其中各个模块简单功能如下:

(1)指令存储器模块(IM):具备基本的读写功能,用于存放指令。

(2)寄存器堆模块(RegFile):由32个32位的寄存器组成,提供较大的存储空间,用于存放暂存数据和指令。

(3)算术逻辑运算器模块(ALU):执行加减法等算术运算,与非或等逻辑运算,以及比较移位传送等操作的功能部件,是该CPU的设计核心部分,存在不同的运算处理功能,是体现实验设计结果正确性的模块。

(4)立即数扩展模块(signext):执行I型指令时需要立即数扩展,该模块用于MIPS符号扩展,将16位数据扩展为32位数据。

(5)主控制模块(ctr):用于控制各个模块之间的分工运行,产生不同数据通路的控制信号,保证指令顺序执行不发生紊乱。

(6)ALU控制模块(aluctr):用于生成ALU执行各种功能的控制信号,使ALU内部运行不发生紊乱。

(7)数据存储器模块(DM),用于LW/SW指令数据存取。

(8)取指模块(next_PC):进行指令的取出及译码,同时包括程序计数器PC运行设计。

(9)显示模块DISPLAY (display):basys3板子上数码管显示

2.各模块具体设计

(1)指令存储器(IM)

调用系统的ip核,用Mars编写汇编程序后,转为16进制的指令,然后写到coe文件中,导入ip核。需要注意的是,IP核中Port A Options-Port A Optional Output Registers-Primitives Output Register不要勾选,因为勾选后会使得IP核的Latency为2,使得PC无法从0开始。

(2)寄存器堆(RegFile)

寄存器组是指令操作的主要对象,MIPS 处理器里一共有 32 个 32 位的寄存器,故可以声明一个包含 32 个 32 位的寄存器数组。读寄存器时需要 Rs,Rd 的地址,得到其数据。写寄存器 Rd 时需要所写地址,所写数据,同时需要写使能。以上所有操作需要在时钟和复位信号控制下操作。故寄存器组设计如下:

img

(3)控制器

根据指令中的指令码(op)和功能码(funct)的不同组合输出相应的控制信号。

主控 aluop 设计图 ALU 译码单元
img img

主控Main Control(ctr)

Main Control
input Op opcode[5:0] 00 0000 00 1101 10 0011 10 1011 00 0100 00 0101 00 1000 00 1010 00 1110 00 1111 00 0010 00 0011 00 1100 00 1001 00 1011
output Rtype ori lw sw beq bne addi slti xori lui jump Jal andi addiu sltiu
regDst 1 0 0 X X X 0 0 0 0 X X 0 0 0
aluSrc 0 1 1 1 0 0 1 1 1 1 X X 1 1 1
memToReg 0 0 1 X X X 0 0 0 0 X X 0 0 0
regWrite 1 1 1 0 0 0 1 1 1 1 0 0 1 1 1
memRead 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
memWrite 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
branch 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
ExtOp X 0 1 1 X X 1 1 0 1 X X 1 0 0
aluop[3:0] 1111 0010 0000 0000 0001 0110 0000 0011 1100 1011 xxxx xxxx 0101 0111 0100
jmp 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
jal 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0

ALU控制译码模块(aluctr)

ALUctr。(拓展了lui指令和移位指令,增加输入lui和Asel输出,jr指令加一个控制信号jr)

上表中31条指令指令与 ALU 操作码及功能码的对应关系如下:(17R+12I+2J)

ALU Control

input

output

ALUop

func

lui

shift

ALU operation

ALUctr

Asel

{lui,shift}

jr

1001

j-type

xxxxxx

0

0

xxx

xxxx

00

0

j

1010

j-type

xxxxxx

0

0

xxx

xxxx

00

0

jal

0000(0)

I-type

xxxxxx

0

0

add

0010 (2)

00

0

lw/sw

0000(0)

I-type

xxxxxx

0

0

add

0010 (2)

00

0

addi

0111(7)

xxxxxx

0

0

add

0010 (2)

00

0

addiu

0101(5)

xxxxxx

0

0

and

0000 (0)

00

0

andi

0001(1)

xxxxxx

0

0

sub

0110 (6)

00

0

beq

0010(2)

I-type

xxxxxx

0

0

or

0001 (1)

00

0

ori

1000(8)

xxxxxx

0

0

xor

1100(12)

00

0

xori

0011(3)

I-type

xxxxxx

0

0

slt

0111 (7)

00

0

slti

0100(4)

xxxxxx

0

0

sltu

1001(9)

00

0

sltiu

0110(6)

xxxxxx

0

0

sub

0110(6)

00

0

bne

1011(11)

I-type

xxxxxx

1

0

sll

0011 (3)

10

0

lui

1111

R-type

100000

0

0

add

0010 (2)

00

0

add

1111

100001

0

0

addu

0010 (2)

00

0

addu

1111

100010

0

0

sub

0110 (6)

00

0

sub

1111

100011

0

0

subu

0110 (6)

00

0

subu

1111

100100

0

0

and

0000 (0)

00

0

and

1111

100101

0

0

or

0001 (1)

00

0

or

1111

100110

0

0

xor

1100(12)

00

0

xor

1111

100111

0

0

nor

1000 (8)

00

0

nor

1111

101010

0

0

slt

0111 (7)

00

0

slt

1111

101011

0

0

sltu

1001 (9)

00

0

sltu

1111

000000

0

1

Shift left

0011 (3)

01

0

sll

1111

000100

0

0

Shift left

0011 (3)

00

0

sllv

1111

000011

0

1

Shift A right

0100 (4)

01

0

sra

1111

000111

0

0

Shift A right

0100 (4)

00

0

srav

1111

000010

0

1

Shift right

0101 (5)

01

0

srl

1111

000110

0

0

Shift right

0101 (5)

00

0

srlv

1111

001000

0

0

xxxx

00

1

jr

Asel={lui,shift}

指导书控制器只实现了部分指令的译码, 如果如图加入移位指令、lui指令,需要添加控制信号shift, lui(左移16位),按照提供的表格或自己编辑添加实现更多指令的控制逻辑。

img

加入jal指令,需译码jal信号

Shift and lui 增加控制信号ASEL

红色输入为 shamt:<10:6>

img

1
2
3
assign ALUSrcA =(Asel==2’b00)?RsData:
				(Asel==2b01)?instruction[10:6]:
				(Asel==2’b10?5’h10:RsData;

(4)ALU运算模块

img

ALU 主要执行 12种操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
4'b0000:begin F=A&B; end  //按位与                     	  0 and

4'b0001:begin F=A|B; end  //按位或                       	  1 or

4'b0010:begin {C,F}=A+B; end //加法                          2 add

4'b0011:begin F=B<<A; end  //将B左移shamt位 or rs位           3 sll sllv lui

4'b0100:begin F=( $signed(B) ) >>> A; end //算术右移          4 sra srav

4'b0101:begin F=B>>A; end //将B右移shamt位                    5 srl srlv

4'b0110:begin {C,F}=A-B; end //减法                       	6 sub

4'b0111:begin F=A[31]!=B[31]?A[31]>B[31]:A<B; end  //比较A<B带符号     7 slt slti 

4'b1000:begin F=~(A|B); end // 或非                       	8 nor

4'b1001:begin F=A<B; end//A<B则F=1,否则F=0                    9 sltu sltiu 

4'b1100:begin F=A^B; end  //按位异或                 	       12 xor 

4'b1111:begin F=32'h0; end       //                			  15 j和jal,jr不需要ALU

add和addu、addi和addiu的区别是有无溢出检测,slt和sltu、slti和sltiu区别是有符号数和无符号数比较

(5)符号扩展模块(signext)

包括了符号扩展和0扩展,如果ExtOp是0,则为0扩展,若ExtOp是1,则为符号扩展

(6)数据存储器模块(DM)

只有lw和sw指令需要用到

(7)数码管显示模块(display)

七段译码显示的内容是16位的,而ALU的运算结果是32位的,将运算结果分为高十六位和低十六位,分别传进七段译码模块;在顶层模块可用一个开关,来选择是显示高16位还是低16位;sm_wei 选择哪一个数码管亮,sm_duan 选择数码管的哪一段亮,sm_wei 变换的速度是 1 秒 1000 次,使人眼看起来数码管是同时显示数值的。

display输入的时钟是高频时钟,在模块内部需要分频使得容易看

(8)PC取指模块(next_PC)

根据控制信号和ALU运算结果等产生下一条PC的值,若该指令为跳转指令,则若jmp为1,则说明要发生跳转,同时根据jr的值来选择进行寄存器跳转还是立即数跳转;若jmp为0则根据branch来决定是否跳转,若branch为1且ALU运算结果符合跳转条件(用zero来标志)则发生跳转,同时利用beq和ben只有指令的第二十六位不同的性质,来区分是否需要跳转,bne且零标志为1则跳转,beq且零标志为0则跳转,否则不进行跳转,下条指令为PC+4。

(9)顶层模块

1.用板子上的按键输入clkORstep来区分是连续运行还是单步运行

1
2
3
wire clkin;

assign clkin=(clkORstep==1'b1)?clk_cpu:step;

2.同时,判断是否是jr指令,来确定寄存器堆A口读出来的是rs的值,还是31号寄存器的值(因为要将A口读出来的值传给PC模块)

1
2
3
wire [4:0]ReadAddrA;

assign ReadAddrA=(jr==1'b1)?5'b11111:instruction[25:21];

3.为了在bsysy3 上便于观看,取指令的时钟需要分频成约T=1.33s的时钟,这样才可以慢慢增加PC(在顶层模块中分频,所有的时钟都用分频后的时钟,只有display模块用分频前高频时钟,因为在display模块中有分频且是基于高频时钟的分频)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//-------------------分频,PC更新需要慢一点的时钟---------------------

integer clk_cnt=0;

reg clk_cpu=0;//1.33秒一个时钟周期,以此为cpu的时钟周期 10^8/0.75*10^8=4/3=1.33 always @(posedge clk)

always @(posedge clk)

if(clk_cnt==32'd75000000)

begin

clk_cnt <= 1'b0; 

clk_cpu <= ~clk_cpu;

end 

else

clk_cnt <= clk_cnt + 1'b1;

4.因为basys3上数码管只有4个,只能同时显示16位,所以用SW[2]来控制高16位和低16位

1
2
3
wire [31:0] display_content;

assign display_content = (SW[1:0] == 2'b00)? instruction:(SW[1:0] == 2'b01)? PC:(SW[1:0] == 2'b10)? aluRes: memreaddata;

5.显示的内容可以是指令、PC、ALU运算结果、DM中取出来的值,用SW[1:0]来区分

00为ins

01为PC

10为ALU

11为MEM

6.写回寄存器堆的地址有3个:rt、rd、ra。写回寄存器的值有3种:alu计算结果、DM中取出来的值、PC+4,由以下语句区分(WB_addr和WB_data是最终输入到寄存器堆的值)。

1
2
3
4
5
6
7
8
9
10
11
assign regWriteAddr = reg_dst ? instruction[15:11] : instruction[20:16];

assign WB_addr = (jal==1'b1)?5'b11111:regWriteAddr;

assign regWriteData = memtoreg ? memreaddata : aluRes;

reg [31:0]next_PC;

always@(posedge clkin) next_PC=PC+4;

assign WB_data=(jal==1'b1)?next_PC:regWriteData;

7.ALU中A口的输入值有三个,RsData(Rs寄存器的值)或者shamt(R型指令的移位码)或者16(lui要左移16位)

ALU中B口的输入值有两个,RtData(Rt寄存器的值)或者立即数扩展后的值。

1
2
3
4
5
assign input2 = alu_src ? expand : RtData;

assign Asel={lui,shift};

assign input1 =(Asel==2'b00)?RsData:(Asel==2'b01)?shamt:(Asel==2'b10)?5'b10000:RsData;

8.给指令寄存器中输入的是PC[9:2],因为指令存储器IM中存的是指令的字地址且是设计的MIPS是大端存储,只需要输入字地址就可以,而输入PC[9:2]相当于除了4,也就是字地址了。

9.IM在时钟沿下降沿写入,才能使得PC从0开始


3.CPU正确性验证

表格中为理论值,图中为仿真波形图

image-20240116181332562


image-20240116181456203


image-20240116181511301


image-20240116181523693


image-20240116181609022


image-20240116181622983


image-20240116181638374


经过比对,图中的仿真得到的数据与理论值符合,说明CPU设计正确,需要特别注意的是跳转指令

img

0x400064为jal指令,应该要跳转到0x400070的位置,跳转成功,并且看jr指令能否跳转回到0x400068的位置

img

0x40008c为jr指令,而下一条指令的地址为0x400068,是jal指令的下一条,说明jal指令成功跳转的同时也将PC+4的地址记录到了31号寄存器中,jr指令可以成功从31号寄存器读取地址并跳转

img

0x400070为bne指令:bne $1,$2,notequal #jal here 此时$1的值为6,$2的值为3,要发生跳转,到0x40007c的位置,图中可以看出下一条指令地址为0x40007c,说明成功跳转且可以分辨bne和零标志,因为bne是零标志位1时跳转

img

0x400080为beq指令:beq $1,$2,equal,此时$1=6,$2=6,会发生跳转,跳转到0x400088的位置,而图中可看出beq下一条指令地址为0x40088,说明成功跳转,也说明了可以区分beq和零标志。只有beq和零标志为0的才会发生跳转。

img

0x40006c是j指令,需要跳转到0x400090的位置,图中下一条指令位置为0x400090,说明跳转成功

img

还有需要注意的是slti和sltiu,slt和sltu的区别,就是有符号数比较和无符号数比较,这里以slti和sltiu为例:

0x400088处为slti:slti $9,$0,-2 #beq here,此时运算结果为0,说明0>-2, 所以不小于

img

0x4000a4处为sltiu:sltiu $16,$0,-2,此时运算结果为1,说明0<-2成立,这是因为-2以补码形式储存,实际存储的值是fffe,有因为是无符号比较,所以不会认为是-2,而是fffe,即0<fffe,所以可以实现有符号比较和无符号比较


总表

地址

汇编程序

运行结果

16进制

Next_PC

ALU运算结果

DM输出结果(LW)

WB_data写回数据

WB_addr写回地址

0x00400000

lui $1,4097

3c011001

0x00400004

268500992

x

268500992

1

0x00400004

ori $11,$1,0

342b0000

0x00400008

268500992

x

268500992

11

0x00400008

addi $1,$0,1

20010001

0x0040000C

1

x

1

1

0x0040000C

add $2,$1,$1

00211020

0x00400010

2

x

2

2

0x00400010

sub $3,$2,$1

00411822

0x00400014

1

x

1

3

0x00400014

add $1,$2,1

20410001

0x00400018

3

x

3

1

0x00400018

addi $2,$0,6

20020006

0x0040001C

6

x

6

2

0x0040001C

and $4,$1,$2

00222024

0x00400020

2

x

2

4

0x00400020

or $4,$1,$2

00222025

0x00400024

7

x

7

4

0x00400024

xor $4,$1,$2

00222026

0x00400028

5

x

5

4

0x00400028

nor $4,$1,$2

00222027

0x0040002C

-8

x

-8

4

0x0040002C

slt $5,$1,$2

0022282a

0x00400030

1

x

1

5

0x00400030

sll $5,$2,2

00022880

0x00400034

24

x

24

5

0x00400034

srl $5,$2,1

00022842

0x00400038

3

x

3

5

0x00400038

sra $5,$2,1

00022843

0x0040003C

3

x

3

5

0x0040003C

sllv $5,$2,$1

00222804

0x00400040

48

x

48

5

0x00400040

addi $10,$0,1

200a0001

0x00400044

1

x

1

10

0x00400044

srlv $5,$2,$10

01422806

0x00400048

3

x

3

5

0x00400048

srav $5,$2,$10

01422807

0x0040004C

3

x

3

5

0x0040004C

andi $6,$1,6

30260006

0x00400050

2

x

2

6

0x00400050

ori $6,$1,6

34260006

0x00400054

7

x

7

6

0x00400054

xori $6,$1,6

38260006

0x00400058

5

x

5

6

0x00400058

lui $7,1

3c070001

0x0040005c

65536

x

65536

7

0x0040005c

sw $2,0($11)

ad620000

0x00400060

xx

x

x

x

0x00400060

lw $8,0($11)

8d680000

0x00400064

xx

6

6

8

0x00400064

jal BranchTest

0c10001c,

0x00400070

xx

x

x

x

0x00400068

addi $17,$0,17

20110011

0x0040006c

17

x

17

17

0x0040006c

j end

08100024

0x00400090

xx

x

x

x

0x00400070

bne $1,$2,notequal #jal here

14220002

0x0040007c

-3

x

x

x

0x00400074

addi $18,$0,18 #不运行

20120012

xx

xx

x

x

xx

0x00400078

addi $19,$0,20 #不运行

20130014

xx

xx

x

x

xx

0x0040007c

addi $2,$0,3 #bne here

20020003

0x00400080

3

x

3

2

0x00400080

beq $1,$2,equal

10220001

0x00400088

0

x

x

x

0x00400084

addi $19,$0,19#不运行

20130013

xx

xx

x

x

xx

0x00400088

slti $9,$0,-2 #beq here

2809fffe

0x0040008c

0

x

0

9

0x0040008c

jr $31 #回到jal branch的下一条

03e00008

0x00400068

xx

x

x

x

0x00400090

addi $1,$0,2 #j here

20010002

0x00400094

2

x

2

1

0x00400094

addiu $12,$0,-2

240cfffe

0x00400098

-2

x

-2

12

0x00400098

addu $13,$0,$12

000c6821

0x0040009c

-2

x

-2

13

0x0040009c

subu $14,$0,$1#$1=2

00017023

0x004000a0

-2

x

-2

14

0x004000a0

sltu $15,$0,$12

000c782b

0x004000a4

1

x

1

15

0x004000a4

sltiu $16,$0,-2

2c10fffe

0x00400098

1

x

1

16


4.Basys 3板操作方法

顶层文件输入有6个,

clk接板子上的时钟

step接板子上的SW15,模拟一个上升沿和下降沿

clkORstep接板子上的SW14,为1则是连续运行,时钟为板子上时钟clk,为0则为单步运行,用step驱动

reset接板子上的SW13,为1时,在上升沿来到时清零,为0时,则正常运行

SW2来选择显示数据的高16位和低16位,为1则显示高16位,为0则显示低16位

SW1和SW0同时选择显示的数据:00则为instruction,01则为PC,10则为ALU计算结果,11则为DM中取出来的值

输出有7个

sm_duna选择一个数码管的显示,sm_wei选择是哪一个数据管,数据在4个数码管上显示,显示高16位或者低16位

而输出标志这有ZF(零标志)、OF(溢出标志)、SF(符号位)、CF(进/借位)、PF(奇偶标志位)

ZF,连接板子上LD15 //0标志位, 运算结果为0(全零)则置1, 否则置0

OF,连接板子上LD14 //溢出标志位,对有符号数运算有意义,溢出则OF=1,否则为0

SF,连接板子上LD13 //符号标志位,与F的最高位相同

CF,连接板子上LD12 //进借位标志位, 取最高位进位C,加法时C=1则CF=1表示有进位,减法时C=0则CF=1表示有借位

PF,连接板子上LD11 //奇偶标志位,F有奇数个1,则PF=1,否则为0


5.Basys 3板运行CPU

image-20240116182228638


image-20240116182351843


image-20240116182323503


image-20240116182411578


image-20240116182427828


image-20240116182456116


image-20240116182505640


image-20240116182514428

##

六. 实验心得

  1. 在做CPU的时候,我是一类指令一类指令的写的,在开始之前并没有总体规划,所以导致其实很多可以单独用到MUX的地方,都是在顶层文件通过两步来完成的,例如:
1
2
assign regWriteAddr = reg_dst ? instruction[15:11] : instruction[20:16];
assign WB_addr = (jal==1'b1)?5'b11111:regWriteAddr;

本来是可以用3选一选择器使得CPU的结构更加的清晰,但是由于没有增加jal指令之前写回地址只有rt和rd,所以只用了一个三元运算符,但是增加jal指令后就要增加31号寄存器,但是要是临时增加一个3选一的MUX又要改很多地方,所以就保留了上述这种不利于阅读的方法,自己看还好,但是不利于其他人的理解。 所以在开始设计CPU的时候最好先花一点时间看看自己都需要哪一些部件,能抽象成为一个模块的最好都抽象出来,便于阅读,也便于自己的修改。

2.添加移位指令时可以传入ALU的参数其实包含shamt,即移动位数,所以sll和sllv可以分成两条指令来写,但是我觉得这样同样操作的指令分成两条写不太优雅,所以ALU的A口传入的值有3种,rs的值或者是shamt或者是16(因为lui指令是要左移16位的),这样sll和sllv京可以用同一条指令完成,区别只是传入A口的值不同,这样只需要添加一个选择器即可,而不用在ALU中添加一条指令,但在软件层面上来说,在ALU中添加一条指令显然是更容易的,但是不符合简洁的设计初衷,所以我选择了添加选择器。

1
assign input1 =(Asel==2'b00)?RsData:(Asel==2'b01)?shamt:(Asel==2'b10)?5'b10000:RsData;

3.添加jal指令的时候需要存PC+4到$31中,但是如果直接写的话,会有时钟影响导致PC的值改变,因此写入的值将不是正确的值,因为verilog语言是一旦变化就会写入的,因此需要限定next_PC的值,保证下一个上升沿到来前next_PC的值不会改变:

1
2
3
reg [31:0]next_PC;
always@(posedge clkin) next_PC=PC+4;
assign WB_data=(jal==1'b1)?next_PC:regWriteData;

4.传入IM的值是PC[9:2],因为IM中根据字地址寻找指令,而我们规定了数据深度为256位,因此只需要2^8=256,第二位到第九位这8位数就可以了

5.仿真遇到zzzz(高阻态),一般是没有在仿真激励文件中例化。遇到xxxx是因为没有取到值。

6.在设计控制器的时候一定要先分配好自己的aluop对应的操作。