计算机组成原理P4

Cdostan MVP++

单周期CPU设计草稿(verilog)

本次课下需要我们通过verilog来设计一个支持10条指令的单周期CPU。由于在上周我们已经通过logisim搭建了一个单周期CPU,所以本次的任务其实简单来说就是通过一个硬件描述语言去描述搭建好的电路。
我们还是先来简单捋一下构建思路。

构建思路

  • 工作流程:每一个周期,PC给出指令所在的地址,CPU去寻找这个地址中的指令,在之后,根据得到的指令判断具体执行什么操作,并判断下一个周期的PC的取值。
  • 模块需求:我们希望在编写verilog的时候能像在logisim里一样使用不同的模块,并在最后使用这些模块来构成整个CPU。而通过相应的指令和流程,我们可以编写以下几个模块:im(指令存储器),grf(寄存器堆),alu(算术运算模块),dm(数据存储器),controller(控制器)以及最顶层的模块mips。

(附上单周期CPUlogisim搭建图)

模块分析

    • mips:作为最顶层的模块,这个模块也即CPU本身,它包含了其他的子模块,在这个模块当中,只需要定义相关的信号将子模块联系起来,除此之外其唯一需要做的就是计算下一个pc的值,因此该模块中应有一个pc的计算部分。图示如下:
    • im:指令存储器模块,需要一个地址信号输入,并输出指令编码。
    • grf:寄存器堆模块,需要三个地址信号,一个写入数据,一个写入数据使能信号,时钟和复位信号。
    • alu:算术运算模块,在这个模块本人设计了与或加减左移及相等6种运算,需要两个运算数输入,一个操作选择信号,一个结果输出,为了方便并再加一个等于输出信号(判断输入的两数是否相等)
    • dm:数据存储器模块,需要时钟和复位信号,写使能和读使能信号,地址和写入数据信号,并输出一个读出的数据。
    • controller:控制器模块,输入仍然是指令的操作码和功能码,输出包括了该电路中所有的控制信号。但本次增加了两条跳转指令,分别是jal和jr,因此在确定控制信号时还要考虑进这两条指令。简单分析即可知,由于这两条都是跳转指令,所以数据存储器既不写数据也不读数据即可,但对于jal来说,需要对31号寄存器存入下一条指令的地址,因此grf的写入地址应为31,写入数据为下一条指令的地址,对于jr来说就简单了,寄存器堆也不用写数据,因此将其写使能信号置零即可。
      jal:regwe->1,memwe->0,loadwe->0
      jr:regwe->0,memwe->0,loadwe->0

      由于grf写入地址现在有三种情况,下一个pc的值也有三种情况,因此我们还得在上一次控制信号的基础上再加上两个控制信号,但这两个控制信号直接由jal和jr来当即可,也即当目前指令为jal(jr)时,jal(jr)置1.

测试方案

  1. 基本指令测试(不含jal和jr)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
ori $1,$0,0x1011
ori $2,$1,0x0111
ori $11,$0,2
lui $3,0
lui $3,0x0111
lui $3,0xffff
ori $4,$3,0xffff
lui $3,0x0111
add $5,$3,$0
add $5,$4,$0
add $5,$3,$1
add $5,$3,$4
add $5,$4,$4
add $5,$5,$5
loop3:add $11,$11,$4
beq $11,$0,loop4
sub $6,$3,$0
sub $6,$4,$0
sub $6,$3,$1
sub $6,$3,$4
ori $7,$0,1
sub $8,$4,$7
sub $6,$4,$8
sub $6,$6,$6
sub $6,$3,$4
loop3:sw $5,4($1)
sw $6,4($2)
nop
lw $9,4($1)
lw $10,4($2)
beq $5,$10,loop1
beq $5,$9,loop2
loop1:sw $10,8($2)
loop2:beq $6,$10,loop3
loop4:sw $9,12($2)
  1. jal与jr测试
1
2
3
4
5
6
7
8
ori $1,$0,1
ori $31,$0,0x3014
loop:add $2,$1,$1
jr $ra
add $3,$2,$0
add $4,$3,$2
jal loop
ori $5,$4,$0

思考题

根据你的理解回答,这个 addr 信号又是从哪里来的?地址信号 addr 位数为什么是 [11:2] 而不是 [9:0] ?

addr信号是从alu的输出中得来的。我们自行写的dm当中,一个地址可以存储一个字,但在MIPS里,地址是字对齐的,也即存一个字的地址都是4的倍数,因此实际上dm对应的地址是真正的地址除以4,dm地址有10位,也就对应aluout的第11到第2位。

思考上述两种控制器设计的译码方式,给出代码示例,并尝试对比各方式的优劣

  1. 指令对应控制信号
    代码示例(在此仅给出ori指令相关代码作为示例):
1
2
3
4
5
6
7
8
9
10
11
12
module controller(
//信号定义
)
always @(*) begin
if(ori == 1)begin
regwe=1;
aluopt='b001
ori=1;
end
//其余指令判断相关控制信号
end
endmodule
  1. 控制信号对应指令
    代码示例:
module controller(
	//信号定义
	)
	always @(*)begin
	regwe = add | sub | ori | lw | lui | jal;
	regdst = add | sub ;
	//其余信号
endmodule
  • 第二种方式代码量可能更小,第一种方式更为直观,且在运行时出错的话较容易定位到问题

请对比同步复位与异步复位这两种方式的 reset 信号与 clk 信号优先级的关系

在同步复位下,clk信号的优先级高于reset信号,要等到每一个时钟周期上升沿到来时才能进行复位;而异步复位下,reset信号的优先级高于clk信号,只要reset为高电平,便可进行复位,无需考虑时钟的限制。

请说明为什么在忽略溢出的前提下,addi 与 addiu 是等价的,add 与 addu 是等价的

  • add:If the addition results in 32-bit 2’s complement arithmetic overflow, the destination register is not modified and an Integer Overflow exception occurs
  • addu:The term “unsigned” in the instruction name is a misnomer; this operation is 32-bit modulo arithmetic that does not trap on overflow. This instruction is appropriate for unsigned arithmetic, such as address arithmetic, or integer arithmetic environments that ignore overflow, such as C language arithmetic
    根据以上解释可知,addu其实是一种32位的模运算,就算溢出了也不会有影响,取其模值即可,而add如果溢出,也不会修改目标寄存器的值,但会抛出一个溢出异常,因此在不考虑溢出的情况下,目标寄存器的值其实也是32位模运算下的值,所以二者等价,同理对addi与addiu
  • Title: 计算机组成原理P4
  • Author: Cdostan
  • Created at : 2025-02-09 21:05:31
  • Updated at : 2025-02-10 18:10:15
  • Link: https://cdostan.github.io/2025/02/09/计组/计组P4/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments