CPU设计实战-加载存储指令(1)

目录

一 加载存储指令说明

1.地址对齐的指令

1.1加载指令load

1.2存储指令store

 2.非对齐指令

2.1加载指令load

2.2存储指令

二 具体实现

1系统结构的修改

2.译码阶段

3.EX模块

4.访存阶段

5. 数据存储器的实现

 6.修改最小SOPC


为什么需要加载存储指令?

这其实是因为计算器体系机构的组成,我们知道当代计算机的结构基本是基于冯诺依曼结构。而其中最大的特点就是实现存储分离,也就是数据和指令单独存储,CPU随用随取。因此我们需要从数据存储器中加载数据,也需要把数据存储到内存中也就是数据存储器,故此我们需要加载存储指令。

一 加载存储指令说明

1.地址对齐的指令

1.1加载指令load

所谓加载进行的操作就是把数据存储器中的数据写入到CPU的寄存器中,写入地址由基地址+偏移地址确定

唯一不同的是写入值的多少,一般分为写入一个字节,两个字节(半个字),一个字(四个字节)

分别对应:lb,lh,lw操作

值得注意的是,这些指令都有地址对齐的要求,例半字加载存储命令要求计算出来的存储地址的最低位为0,以字为单位的加载存储命令要求计算出来的存储地址的最低位为00。至于为什么需要地址对齐以及具体如何要求下面再讲。

1.2存储指令store

 格式与功能与加载指令类似,本质是把CPU中寄存器的数据存储到数据存储器中:

存储值的多少同样分为一个字节,两个字节(半个字),一个字(四个字节)

分别对应:sb,sh,sw操作

到这里大家有没有一些疑问,我是有几个问题与大家一起分享:

为什么加载存储地址采用基地址+偏移量的形式而不直接指定地址?

一方面便于指定具体地址,使用基地址加偏移量的形式可以提供更大的灵活性。通过指定一个基地址和一个相对于该基地址的偏移量,可以轻松地访问存储器中的不同区域,而不需要显式地指定每个地址。

另一方面与内存结构有关,我们存储器的结构不是一个长条状的,而是被分为一块一块的区域。

内存实际上是有多个内存芯片共同组成的,为了提高访存的带宽,通常的做法是将地址分开,放到不同的芯片上,就算使用fpga来实现32位的存储器也会考虑分为四个八位存储器,比如,第0-7bit在芯片0上储存,8-15bit在芯片2上组成,以此类推,如下图:

基地址相当于指定哪一块芯片,偏移量再去指定具体哪个位置。由于这个结构也引出了为什么要地址对齐真正原因,对于一个32位的CPU来说,CPU 的访问粒度为 4 字节当我们从地址0访问一个字时可以成功读到0-3地址的四个字节,但是如果我们从2开始读一个字节就会发生问题,由于内存是以四个字节为单位分开的,所以只能读到23地址的两个字节,而45字节的数据在另一块芯片上,需要重新改变基地址。因此CPU在加载存储指令中还要求地址对齐。

对于32位CPU,通常要求整型数据(例如4字节的整数)的地址应该是4的倍数。同样的,要求整型数据(例如2字节的整数)的地址应该是2的倍数。

这也就解释上面“半字(两字节)加载存储命令要求计算出来的存储地址的最低位为0,以字(四字节)为单位的加载存储命令要求计算出来的存储地址的最低位为00。”的原因。

大小端模式

大小端模式是指存储数据是数据与地址的不同匹配方式,本CPU采用大端模式:在这种模式下,数据的高位保存在存储器的低地址中,而数据的低位保存在存储器的高地址中。

 2.非对齐指令

2.1加载指令load

为什么还需要非对齐指令?

这主要是为了解决如何高效的按非对齐地址加载存储数据,根据上文我们知道一般加载存储指令都会要求地址对齐,在mips架构中还支持非对齐地址的加载和存储,如下例:

当需要从地址7开始非地址对齐加载一个字节,也就是加载7,8,9,10地址处的四个字节,使用之前的指令很麻烦,考虑分两步走如下图:

1.加载地址4-7芯片块的地址7的一个字节存储到寄存器的高位

2.加载地址8-11芯片块的地址8,9,10的三个字节存储到寄存器的低位

 其实指令lwl和lwr就是做的这两件事情

lwl:指令作用为:从内存中指定的加载地址处,加载一个字的最高有效部分。Iwl指令对加载
地址没有要求,从而允许地址非对齐加载,这是与前面介绍的Ih、lhu、Iw指令的不同之处。

lwr:指令作用为:从内存中指定的加载地址处,加载一个字的最低有效部分。还是假设计算
出来的加载地址是loadaddr, loadaddr 的最低两位的值为n,将loadaddr最低两位设为0后的
值称为loadaddr_ align。

而有了lwl、Iwr指令后,只需要2条指令即可实现非对齐地址的加载。

2.2存储指令

存储指令相当于前面加载指令的逆操作。当我们需要把一个寄存器的值(一个字,四个字节)按照非对齐地址(如7位地址)存储到内存中时,需要分两步进行操作:

1.由于4-7地址是一个字块,所以只能在7处存储一个字节,具体是把寄存器的高位存储到内存中的最低位

2.把寄存器值的低三个字节存储到内存中5-8字块的高三位也就是567地址处

如下图:

 swl和swr指令就是做的这两件事情

swl指令作用为:将地址为rt的通用寄存器的高位部分存储到内存中指定的地址处,存储地址的最后两位确定了要存储rt通用寄存器的哪几个字节。swl 指令对存储地址没有对齐要求,这是与前面介绍的sh、sw 指令的不同之处。

swr指令作用为:将地址为rt的通用寄存器的低位部分存储到内存中指定的地址处,存储地址的最后两位确定了要存储rt通用寄存器的哪几个字节。

二 具体实现

1系统结构的修改

数据存储器和指令存储器一样都放在访存阶段,mem要读写数据存储器,所以肯定要有读写的地址,写入数据值,读到数据值,读写使能以及选择读几个字节(这些是基于加载存储指令的不同,因此还需要把译码阶段的op传递过来),又因为在非地址对齐对齐加载指令lwl和lwr中需要去读寄存器的值(因为他们实现的本质是选取某几个字节的值与寄存器的值进行组合成为一个新的字),所以需要传递译码阶段的操作数2过来。

2.译码阶段

加载指令读指定地址到写入寄存器,所以写寄存器使能,因为地址base在通用寄存器中,所以还是要读寄存器1。

除了lwl和lwr指令,因为非地址对齐加载,只是部分的修改不低寄存器,需要读目的寄存器也就是rt与加载的结果进行组合,如下图所示。

存储指令要读寄存器中的值写入内存地址,所以两个寄存器都要读(包含一个base),但是不写入通用寄存器

中间模块传输一下就行了不多讲了

3.EX模块

1.传递aluop到访存阶段,需要据此判断加载和存储指令

2.计算目标存储地址,计算方式就是基地址base+偏移地址offset(需要有符号扩展至32位)

3.传递rt寄存器的值,访存阶段要用,是存储指令存储的值,是非对齐加载信号要读取的值

4.访存阶段

访存阶段主要就是根据不同的加载存储指令去和数据存储器进行访问

lb指令加载内存地址的数据给寄存器,需要访问数据存储器,不需要写入,因为是加载一个字节,而内存数据的传输是一个字一个字传输的,也就是四个字节,所以需要判断选择哪一个字节给到寄存器,根据内存地址最低两位可以判断选择哪一个字节,如下图:

从而赋值mem_sel_o去选择哪一个字节给到寄存器,并进行有符号的扩展。lbu步骤一样,只是无符号的扩展,lh加载一个半字,故mem_sel_o情况只有1100和0011两种,lw加载一个字,只有1111一种情况,也就是把一个字全部赋值给寄存器。

猜想:此处应该是已经根据加载地址取到一个字的数据才对,但我感觉这不是一个时钟周期可以完成的,第一个时钟周期根据地址取到字mem_addr_i,第二个时钟周期才能赋值给wdata_o。

 lwl指令特殊一些,因为支持非地址对齐读存储器,而存储器本身不支持,这里其实是用软件的方式去实现,因为一共也就四种情况,如下:

所以我们输出到数据存储器的读地址应该清零最后两位,然后根据加载地址判断输出到寄存器的值如何,我们已经读到寄存器的值与内存的值,只需要按照上面四种情况进行组合就可以。如果加载地址最后两位为0那么其实是地址对齐的,对应的上图n=0的情况,把从内存读来的数据全部写到寄存器中;如果为01,那么对应n=1的情况,以此类推。

 lwr的情况类似,参考下图:

下面来看存储指令,存储指令作用是把寄存器的值加载到内存数据中,所以需要写数据存储器,,至于写入的数据就是根据寄存器的值也就是操作数2,这里同样的,因为寄存器读出的值是一个字(四个字节),所以还是需要根据地址最后两位判断需要哪一个字节,如下图:

swl指令同样面临一个字的寄存器值如何写入到内存中,首先同样的读寄存器的地址不能是非地址对齐的,要留空最后两位,然后根据加载地址去判断。如后两位为00时,如n=0时,全部写入;当后两位01,与加载指令不同的是,加载指令可以读源寄存器的值直接与数据寄存器的值进行组合,而存储指令无法提前去读内存的值,所以需要根据sel信号来选择指定的字节。

 swr的情况也类似

5. 数据存储器的实现

为了方便实现对数据存储器按字节寻址,在设计的时候使用4个8位存储器代替一个32位存储器,如图9-25所示,读操作时,从4个8位存储器中各读出一一个字节,组合为一个32位的数据输出,写操作时,依据sel的值,修改其中特定存储器对应的字节即可

1.定义四个8字节的存储器,datamemnum定义存储器的大小,这里定为128k=128*1024=131071

2.写操作,根据sel去指定输入的写数据哪一个字节就可以了

3.读操作, 把四个字节的数值拼接为一个字即可

 6.修改最小SOPC

因为我们openmips与外界新增加对数据存储器的访问,根据下图连线就可以了:

  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在第8关中,我们需要设计一个单周期MIPS CPU,支持以下8条指令: 1. ADD $1, $2, $3 2. SUB $4, $5, $6 3. ADDI $7, $8, 10 4. LW $9, 100($10) 5. SW $11, 200($12) 6. BEQ $13, $14, label 7. J label 8. JR $15 其中,ADD和SUB指令为R型指令,ADDI为I型指令,LW和SW为加载/存储指令,BEQ为分支型指令,J和JR为跳转型指令。 我们可以根据指令的类型分别设计对应的控制信号和数据通路,具体实现如下: 1. ADD $1, $2, $3 指令格式:R型指令 操作:将$2和$3的值相加,结果存储在$1中 控制信号:RegWrite=1,ALUSrcA=0,ALUSrcB=10,ALUOp=00,MemWrite=0,MemRead=0,RegDst=1 数据通路:从寄存器文件读取$2和$3的值,将它们输入ALU,ALU进行加法运算,结果写入寄存器$1中 2. SUB $4, $5, $6 指令格式:R型指令 操作:将$5和$6的值相减,结果存储在$4中 控制信号:RegWrite=1,ALUSrcA=0,ALUSrcB=10,ALUOp=10,MemWrite=0,MemRead=0,RegDst=1 数据通路:从寄存器文件读取$5和$6的值,将它们输入ALU,ALU进行减法运算,结果写入寄存器$4中 3. ADDI $7, $8, 10 指令格式:I型指令 操作:将$8的值加上常数10,结果存储在$7中 控制信号:RegWrite=1,ALUSrcA=0,ALUSrcB=01,ALUOp=00,MemWrite=0,MemRead=0,RegDst=1 数据通路:从寄存器文件读取$8的值,将它与常数10输入ALU,ALU进行加法运算,结果写入寄存器$7中 4. LW $9, 100($10) 指令格式:加载指令 操作:将存储器地址$10+100处的值加载到$9中 控制信号:RegWrite=1,ALUSrcA=1,ALUSrcB=00,ALUOp=00,MemWrite=0,MemRead=1,RegDst=0 数据通路:从寄存器文件读取$10的值,将它与常数100输入ALU,ALU进行加法运算得到存储器地址,将地址输入存储器,将读取到的值写入寄存器$9中 5. SW $11, 200($12) 指令格式:存储指令 操作:将$11的值存储存储器地址$12+200处 控制信号:RegWrite=0,ALUSrcA=1,ALUSrcB=00,ALUOp=00,MemWrite=1,MemRead=0,RegDst=X 数据通路:从寄存器文件读取$12的值,将它与常数200输入ALU,ALU进行加法运算得到存储器地址,将地址和$11的值输入存储器,将存储器的写使能信号打开 6. BEQ $13, $14, label 指令格式:分支型指令 操作:如果$13和$14的值相等,则将PC设置为label的地址 控制信号:RegWrite=0,ALUSrcA=0,ALUSrcB=00,ALUOp=01,MemWrite=0,MemRead=0,RegDst=X,Branch=1,Jump=0 数据通路:从寄存器文件读取$13和$14的值,将它们输入ALU,ALU进行比较,如果结果为真,则将PC设置为label的地址 7. J label 指令格式:跳转型指令 操作:将PC设置为label的地址 控制信号:RegWrite=0,ALUSrcA=0,ALUSrcB=00,ALUOp=00,MemWrite=0,MemRead=0,RegDst=X,Branch=0,Jump=1 数据通路:将PC的高四位与label的高四位拼接,得到跳转地址 8. JR $15 指令格式:跳转型指令 操作:将PC设置为$15的值 控制信号:RegWrite=0,ALUSrcA=0,ALUSrcB=00,ALUOp=00,MemWrite=0,MemRead=0,RegDst=X,Branch=0,Jump=1 数据通路:从寄存器文件读取$15的值,将它作为跳转地址

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值