章将实现MIPS32指令集架构中定义的加载存储指令,分两步:首先实现除ll、sc指令外的一般加载存储指令,其次实现比较特殊的加载存储指令ll、sc。

9.1加载存储指令说明

MIPS32指令集架构中定义的加载存储指令共有14条,如下:

--8条加载指令:lb、lbu、lh、lhu、ll、lw、lwl、lwr

--6条存储指令:sb、sc、sh、sw、swl、swr

9.1.1加载指令lb、lbu、lh、lhu、lw说明

 这5条加载指令可以根据指令中26~31bit的指令码加以区分,另外,加载指令的第0~15bit是offset、第21~15bit是base,加载地址的计算方法如下,先将16位的offset符号扩展至32位,然后与地址为base的通用寄存器的值相加,即可得到加载地址。

加载地址 = signed_extended(offset) + GPR[base]

9.1.2存储指令sb、sh、sw说明

从图9-2可知,这3条存储指令可以根据指令中26~31bit的指令码加以区分,另外,存储指令的第0~15bit是offset、第21~25bit是base,存储地址的计算方法如下,先将16位的offset符号扩展至32位,然后与地址为base的通用寄存器的值相加,即可得到存储地址。

存储地址 = signed_extended(offset) + GPR[base]

9.1.3加载存储指令用法示例

OPenMIPS处理器是按照字节寻址,并且是大端模式,在这种模式下,数据的高位保存在存储器的低地址中,而数据的低位保存在存储器的高地址中。比如:使用指令sb在0x50处存储0x81,存储器的实际存储效果如图所示。

9.1.4加载指令lwl、lwr说明

9.1.5存储指令swl、swr说明

2加载存储指令实现思路

节介绍除了ll、sc之外的加载存储指令的实现思路,ll、sc指令的实现思路将在9.7节专题介绍

1.加载指令实现思路

加载指令在译码阶段进行译码,得到运算类型alusel_o、aluop_o,以及要写的目的寄存器信息。这些信息传递到执行阶段,然后又传递到访存阶段,访存阶段依据这些信息,设置对数据存储器RAM的访问信号。从RAM读取回来的数据需要按照加载指令的类型、加载地址进行对齐调整,调整后的结果作为最终要写入的目的寄存器的数据。

2.存储指令实现思路

存储指令在译码阶段进行译码,得到运算类型alusel_o、aluop_o,以及要存储的数据,这些信息传递到执行阶段,然后又传递到访存阶段,访存阶段依据这些信息,设置对数据存储器RAM的访问信号,将数据写入RAM。

需要特别注意的是:本章假设可以在一个时钟周期内完成对外部数据存储器RAM的读写操作,在后续章节实现实践版OpenMIPS处理器的时候会考虑复杂情况。

9.2.1数据流图的修改

为了实现除ll、sc之外的加载存储指令,修改数据流图。主要是在访存阶段增加了对数据存储器RAM的访问,同时,由于要写入目的寄存器的数据可能是执行阶段的结果,也可能是在访存阶段从数据存储器RAM加载得到的数据,所以在访存阶段增加了一个多路选择器,进行选择。

9.2.2系统结构的修改

为了实现除ll、sc之外的加载存储指令,需要对系统结构进行修改,增加部分模块的接口。

主要修改内容如下。

(1)译码阶段的ID模块增加了输出信号inst_o,其值就是处于译码阶段的指令,该信号会传递到执行阶段,在执行阶段的EX模块会利用该信号的值计算加载、存储地址mem_addr_o.

(2)执行阶段的EX模块将运算子类型aluop_o、加载存储地址mem_addr_o、读取的第二个操作数reg2_o等信息,通过EX/MEM模块传递到访存阶段的MEM模块。

(3)访存阶段的MEM模块依据加载、存储指令的类型,确定对数据存储器RAM的访问信息,通过mem_ce_o接口送出数据存储器使能信号,mem_addr_o接口送出访问地址,mem_we_o接口指出是加载还是存储操作、mem_sel_o接口送出字节选择信号,如果是存储指令,那么还通过mem_data_o接口输出要存储的数据,如果是加载指令,那么会从mem_data_i接口获得读取到的数据,然后MEM模块依据具体的加载指令类型、加载地址,对获取的数据进行对齐调整,最终得到要写入目的寄存器的数据。

9.3修改OpenMIPS以实现加载存储指令

9.3.1修改译码阶段

1.修改ID模块

ID模块要增加接口inst_o.在ID模块还要增加对加载存储指令的分析,这些指令的指令码都是不同的,所以可以直接依据指令码确定是哪一种指令

译码工作主要是确定要写的目的寄存器、要读取的寄存器和要执行的运算三个方面。以下对几个有代表性的指令的译码过程进行说明。

(1)lb指令

--要写的目的寄存器:加载指令lb需要将加载结果吸入目的寄存器,所以设置wreg_o为WriteEnable,同时可知,要写的目的寄存器地址是指令中的第16~20bit,所以设置wd_o为inst[20:16]。

--要读取的寄存器:计算加载目标地址需要使用地址为base的寄存器值,所以设置reg1_read_o为1,表示通过RegFile模块的读端口1读取寄存器的值,默认读取的寄存器地址reg1_addr_o是指令的第21~25bit,正是lb指令中的base。所以最终译码阶段的输出reg1_o就是地址为base的寄存器的值。

--要执行的运算:设置alusel_o为EXE_RES_LOAD_STORE,表示运算类型是加载存储,设置aluop_o为EXE_LB_OP,表示运算子类型是字节加载lb.

lbu、lh、lhu、lw指令与lb指令的译码过程类似,知识aluop_o的值不同。

(2)lwl指令

--要写的目的寄存器:加载指令lwl需要将加载结果吸入目的寄存器,所以设置wreg_o为WriteEnable,同时可知,要写的目的寄存器地址是指令中的第16~20bit,所以设置wd_o为inst[20:16]。

--要读取的寄存器:计算加载目标地址需要使用地址为base的寄存器值,所以设置reg1_read_o为1,表示通过RegFile模块的读端口1读取寄存器的值,默认读取的寄存器地址reg1_addr_o是指令的第21~25bit,正是lwl指令中的base。所以最终译码阶段的输出reg1_o就是地址为base的寄存器的值。此外,由于lwl指令只是部分地修改目的寄存器,因此,设置reg2_read_o也为1表示通过RegFile模块的读端口2读取寄存器的值,默认读取的寄存器地址reg2_addr_o是指令的第16~20bit,正是lwl指令中的rt。所以最终译码阶段的输出reg2_o就是地址为rt的寄存器的值。

--要执行的运算:设置alusel_o为EXE_RES_LOAD_STORE,表示运算类型是加载存储,设置aluop_o为EXE_LWL_OP,表示运算子类型是字节加载lwl.

lwr指令与lwl指令的译码过程类似,只是aluop_o的值不同。

(3)sb指令

--要写的目的寄存器:加载指令sb需要将加载结果吸入目的寄存器,所以设置wreg_o为WriteEnable,同时可知,要写的目的寄存器地址是指令中的第16~20bit,所以设置wd_o为inst[20:16]。

--要读取的寄存器:计算加载目标地址需要使用地址为base的寄存器值,所以设置reg1_read_o为1,表示通过RegFile模块的读端口1读取寄存器的值,默认读取的寄存器地址reg1_addr_o是指令的第21~25bit,正是sb指令中的base。所以最终译码阶段的输出reg1_o就是地址为base的寄存器的值。要存储的值是通用寄存器的值,因此,设置reg2_read_o也为1表示通过RegFile模块的读端口2读取寄存器的值,默认读取的寄存器地址reg2_addr_o是指令的第16~20bit,正是sb指令中的rt。所以最终译码阶段的输出reg2_o就是地址为rt的寄存器的值。

--要执行的运算:设置alusel_o为EXE_RES_LOAD_STORE,表示运算类型是加载存储,设置aluop_o为EXE_SB_OP,表示运算子类型是字节加载sb.

sh、sw、swr、swl指令与sb指令的译码过程类似,只是aluop_o的值不同。

2.修改ID/EX模块

ID/EX模块需要增加部分接口,用于将ID模块新增加的输出信号inst_o传递到执行阶段的EX模块。

9.3.2修改执行阶段

1.修改EX模块

在执行阶段的EX模块会计算加载存储的目的地址,参考图9-19可知,EX模块会增加部分接口,如表9-3所示。

2.修改EX/MEM模块

参考图9-19可知,EX/MEM模块会增加部分接口,用于EX模块新增的输出传递到访存阶段

9.3.3修改访存阶段

访存阶段主要是修改MEM模块,参考图9-19可知,需要为其添加对数据存储器RAM的访问接口。

此处对mem_sel_o做进一步说明,分加载、存储两种操作分别说明。

(1)对于加载操作,MIPS32指令集架构中定义的加载指令可以加载字节、半字、字,但是数据总线的宽度是32位,占4个字节。如果执行加载字节指令lb、lbu,那么就要知道通过数据总线输入的4个字节中,哪个字节是要读取的数据:如果执行加载半字指令lh、lhu,那么就要知道哪个半字是要读取的数据,mem_sel_o的作用就是指出哪一部分是有效数据。mem_sel_o的宽度为4,分别对应数据总线的4个字节,比如:使用加载指令lb读取数据存储器地址0x1出的字节,那么可以设置mem_sel_o为4'b0100,意思就是,希望外部存储器在输出数据时,将地址0x1处的字节放在32位数据总线的次高字节,也就是第16~23bit的位置,当数据送到处理器时,处理器就取出其中第16~23bit对应的字节,作为数据存储器地址0x1处的值。

(2)对于存储操作,MIPS32指令集架构中定义的存储指令可以存储字节、半字、字,但数据总线的宽度是32位,占4个字节,如果执行字节存储指令sb、半字存储指令sh,那么外部数据存储器就要知道用过数据总线传递过来的4个字节中,哪个字节、哪个半字是要存储的数据,mem_sel_o作用就是指出哪一部分是要存储的有效数据。比如:使用存储指令sh向地址0x2处存储0x8281,那么可以设置mem_data_o为0x82818281、设置mem_sel_o为4'b0011,这样外部存储器就知道要存储的数据是0x82818281的最低两个字节,正是0x8281.

下面对其中几个典型指令的访存过程进行解释

(1)因为要访问数据存储器,所以设置mem_ce_o为ChipEnable

(2)因为是加载操作,所以设置mem_we_o为WriteDisable

(3)给出要访问的数据存储器地址mem_addr_o, 其值就是执行阶段计算出来的地址mem_data_i.

(4)依据mem_addr_i的最后两位,确定mem_sel_o的值,并据此从数据存储器的输入mem_data_i中获得要读取的字节,进行符号扩展。比如:如果mem_addr_i的最后两位是01,那么设置mem_sel_o为4'b0100,表示希望输出存储器给出的数据的第16~23bit就是要读取的字节,也就是mem_data_i[23:16],将其最高位进行符号扩展,得到最终结果wdata_o,作为要写入目标寄存器的数据。

2.lwl指令的访存过程

(1)因为要访问数据存储器,所以设置mem_ce_o为ChipEnable

(2)因为是加载操作,所以设置mem_we_o为WriteDisable

(3)给出要访问的数据存储器地址mem_addr_o, 其值就是执行阶段计算出来的地址mem_data_i.,但最后两位要设置为0,因为lwl指令要从RAM中读出一个字,所以需要将地址对齐,同时设置mem_sel_0为4'b1111。

(4)依据mem_addr_i的最后两位,将从数据存储器读取的数据mem_data_i与目的寄存器的原始值reg2_i进行组合,得到最终要写入目的寄存器的值wdata_o,

3.sb指令的访存过程

(1)因为要访问数据存储器,所以设置mem_ce_o为ChipEnable.

(2)因为是存储操作,所以设置mem_we_o为WriteEnable。

(3)给出要访问的数据存储器地址mem_addr_o,其值就是执行阶段计算出来的地址mem_addr_i。

(4)sb指令要写入的数据是寄存器的最低字节,将该字节复制到mem_data_o的其余部分,然后依据mem_data_i的最后两位,确定mem_sel_o的值。

4.swl指令的访存过程。

9.4修改最小SOPC

为了验证上一节添加的加载存储指令是否实现正确,需要修改在第四章中设计的最小SOPC,为其添加数据存储器RAM。

9.4.1添加数据存储器

9.6链接加载指令ll、条件存储指令sc说明

ll、sc指令是MIPS32指令MIPS32指令集架构中比较特殊的加载存储指令,用来实现信号量机制。在多线程系统中,需要RMW(Read-Mpdify-Write)操作序列保证对某个资源的独占性,RMW操作序列的含义是,读取内存某个地址的数据,读取饿得数据经过修改,然后再保存回内存原地址,这个过程不能有任何干扰,因此需要建立一个临界区域(Critical Region), 临界区域中完成的操作通常称为原子操作,原子操作不能被打扰。操作系统建立邻接区域的方式通常是信号量机制,如下。

wait(semaphore);

原子操作

signal(semaphore);

semaphore是一个信号量,为1表示信号量使用中,为0表示信号量空闲。进行原子操作前,使用wait函数查询semaphore的值,如果为1,则等待,否则,将其置为1,开始执行原子操作。操作结束后,signal函数将semaphore置为0,这样其他线程就可以执行原子操作了。

需要注意的是,wait函数的执行也是一个原子操作,是一种“先检测后设置”的操作(tesr-and -set operation),这种操作一般不希望被外部设备中断,也不希望被其他线程打断,很多处理器都有专门的指令用来实心"先检测后设置"操作。这也是一种信号量机制。

MIPS32架构采用特殊的方式实现信号量机制,对于原子操作,MIPS32架构并不保证它一定是原子性的,也就是允许检测和设置在没有原子性保证的情况下运行,但只在它确实是原子的运行了的时候才能让“设置”生效。MIPS32架构采用链接加载指令ll、条件存储指令sc来实现这种信号量机制。

ll指令同一般的加载指令一样,从内存中加载一个字,但是,有点不同 ,ll指令还会将处理器内部的一个链接状态位LLbit置为1,表明发生了一个链接加载操作,并将链接的地址保存到一个特殊寄存器LLAddr中。

ll指令执行完毕后,会进行一定的操作(如:修改加载得到的数据),然后执行sc指令,这可以认为是一个RMW序列。有如下两种情况干肉这个RMW序列,受到干扰后,处理器会设置链接状态位LLbit为0

--在ll、sv指令之间产生异常,从而进入异常处理例程,或者发生线程切换,熬制RMW徐磊受到干扰。

--多处理器的系统中,另一个CPU改写了RMW序列要操作的内存空间。

对于OpenMIPS而言,只有第1种情况。

执行sc指令时,会对从ll指令开始的RMW序列进行检查,判断是否受到干扰,实际就是判断LLbit是否wie1,如果没有受到任何干扰,LLbit保持为1,那么操作是原子的,sc指令会对ll指令加载数据的地址进行写回操作,并设置一个通用寄存器的值为1,表示成功,反之不进行写回操作,并设置一个通用寄存器的值为0,表示失败。

9.7ll、sc指令实现思路

9.7.2数据流图的修改

9.7.3系统结构的修改

9.8修改OpenMIPS以实现ll、sc指令

9.8.2修改译码阶段的ID模块

9.8.3修改访存阶段

《自己动手写CPU》--第九章--学习笔记相关推荐

  1. OpenGL蓝宝书第九章学习笔记:片段着色器和帧缓存

    前言 本篇在讲什么 OpenGL蓝宝书第九章学习笔记之片段着色器和帧缓存 本篇适合什么 适合初学OpenGL的小白 本篇需要什么 对C++语法有简单认知 对OpenGL有简单认知 最好是有OpenGL ...

  2. 工程伦理第九章学习笔记2020最新

    工程伦理第九章学习笔记2020最新 继续更新

  3. 《Java从入门到精通》第九章学习笔记

    第9章 类的高级特性 一.抽象类 抽象类只声明方法的存在,不能被实例化,就是说抽象类不能创建其对象,在定义抽象类时要在class前面加上abstract关键字. 1 /*定义一个抽象类Fruit,并定 ...

  4. 王爽汇编第九章学习笔记

    在这里0-1=ffff 有关于根据位移进行转移的意义:跳转指令与浮动装配(汇编相关),汇编语言交流,技术交流区,鱼C论坛 - Powered by Discuz! 实验八:重点关注jmp short ...

  5. dx12 龙书第九章学习笔记 -- 纹理贴图

    1.纹理与资源的回顾 我们其实很早就接触过纹理了,之前的深度缓冲区与后台缓冲区,它们都是通过ID3D12Resource接口表示,并以D3D12_RESOURCE_DESC::Dimension成员中 ...

  6. 自己动手写CPU之第九阶段(2)——加载存储指令说明2(lwl、lwr)

    将陆续上传新书<自己动手写CPU>,今天是第38篇,我尽量每周四篇,但是最近已经很久没有实现这个目标了,一直都有事,不好意思哈. 开展晒书评送书活动,在亚马逊.京东.当当三大图书网站上,发 ...

  7. 自己动手写CPU之第九阶段(2)——载入存储指令说明2(lwl、lwr)

    将陆续上传新书<自己动手写CPU>.今天是第38篇,我尽量每周四篇,可是近期已经非常久没有实现这个目标了.一直都有事,不好意思哈. 开展晒书评送书活动,在亚马逊.京东.当当三大图书站点上, ...

  8. 自己动手写CPU(1)五级流水线及CPU第一条指令ori

    自己动手写CPU(1)五级流水线及CPU第一条指令ori 动机 不知为何研一的自由时间突然多起来,可能人一闲下来就容易焦虑吧,hhhhhh.正好之前看到一本<自己动手写CPU>,就按照此书 ...

  9. 【自己动手写CPU】异常相关指令的实现

    MIPS架构中定义的异常类型 MIPS32架构中,有些事情打断程序的正常的执行流程,这些事情称为中断.陷阱.系统调用以及其他打断程序执行流程的情况,统称为异常. 此处的OpenMIPS处理器只实现了其 ...

最新文章

  1. Spark源码分析之七:Task运行(一)
  2. 本地虚拟机的linux文件,连接虚拟机上传本地文件 打包Linux的文件 下载的本地
  3. Exchange 2010 EMC 删除 DisconnectedMailbox
  4. 配置一台路由器,两台电脑的重点
  5. java jsp Struts2.X 文件上传
  6. Android,UI主线程与子线程
  7. php$_GET 变量
  8. 音视频技术开发周刊 | 152
  9. Python3.6+Django2.0+Xadmin2.0学生信息管理系统-2
  10. debian php安装pdo扩展,docker安装PHP扩展2020-05-25
  11. 容易遗忘的JS知识点整理
  12. 《数学之美》—有限状态机和动态规划
  13. Google搜索技巧大全:101个谷歌搜索技巧推荐
  14. 【转】 Pro Android学习笔记(五八):Preferences(2):CheckBoxPreference
  15. C# + opengl + Tao 环境配置
  16. NLPCC tutorial
  17. OpenHarmony在Amlogic A311D芯片平台的快速开发上手指南
  18. SpringBoot注解整理历史笔记
  19. 2021-04-28-飞机大战-001-day5-游戏滚动背景
  20. 大小限制_微信传文件有大小限制怎么办?教你3秒把100MPPT压缩成10M

热门文章

  1. Linux 根目录结构,英文全称、文件夹作用
  2. 产品运输和使用振动环境的分类
  3. 关于opengl中glColor3f的应用
  4. 云手机虚拟机服务器配置,云手机虚拟机服务器
  5. “没有银弹”的由来!
  6. LaTeX - 如何在图片说明(caption)中使用脚注(footnote)
  7. 字节跳动 文林福_AI实力赋能,让汽车营销玩出抖音范
  8. 2022中国大学python语言程序设计测试六答案(北理工嵩天 、黄天羽 、礼欣)
  9. 电磁阀peakhold驱动
  10. Payoneer取人民币全过程(ATM)