自己动手写CPU之第九阶段(7)——MIPS32中的LL、SC指令说明

将陆续上传新书《自己动手写CPU》,今天是第46篇。

在MIPS32指令集中有两条特殊的存储加载指令:链接加载指令LL、条件存储指令SC,本次将介绍这两条指令,在后续将实现这两条指令。

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

在本章前面的部分,笔者花费很多笔墨介绍了OpenMIPS中除ll、sc之外的加载、存储指令的实现过程,本节至9.9节将专门介绍链接加载指令ll、条件存储指令sc的实现过程。ll、sc指令是MIPS32指令集架构中比较特殊的加载存储指令,用来实现信号量机制。

在多线程系统中,需要RMW(Read-Modify-Write)操作序列保证对某个资源的独占性,RMW操作序列的含义是,读取内存某个地址的数据,读取的数据经过修改,然后再保存回内存原地址,这个过程不能有任何打扰,因此需要建立一个临界区域(Critical Region),临界区域中完成的操作通常称为原子操作,原子操作不被打扰。操作系统建立临界区域的方式通常是信号量机制,如下。

wait(semaphore);

原子操作;

 signal(semaphore);

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

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

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

ll指令同一般的加载指令一样,从内存中加载一个字,但是,有一点不同,ll指令还会将处理器内部的一个链接状态位LLbit置为1,表明发生了一个链接加载操作,并将链接加载的地址保存到一个特殊寄存器LLAddr中(这个寄存器在多处理器中有作用,OpenMIPS是单处理器,所以在OpenMIPS实现过程中并没有实现LLAddr寄存器)。

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

  • 在ll、sc指令之间产生异常,从而进入异常处理例程,或者发生线程切换,导致RMW序列受到干扰。
  • 多处理器的系统中,另一个CPU改写了RMW序列要操作的内存空间。

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

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

ll、sc指令的格式如图9-28所示。从图中可知,可以依据指令码对这2条指令进行区分。

  • 当指令中的指令码为6‘b110000时,是ll指令,链接加载指令

指令用法为:ll rt, offset(base)

指令作用为:从内存中指定的加载地址处,读取一个字节,然后符号扩展至32位,保存到地址为rt的通用寄存器中。其中加载地址的计算方法如下。

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

此外,还要设置链接状态位LLbit为1。

  • 当指令中的指令码为6‘b111000时,是sc指令,条件存储指令

指令用法为:sc rt, offset(base)

指令作用为:如果RMW序列没有受到干扰,也就是LLbit为1,那么将地址为rt的通用寄存器的值保存到内存中指定的存储地址处,同时设置地址为rt的通用寄存器的值为1,设置LLbit为0。如果RMW序列受到了干扰,也就是LLbit为0,那么不修改内存,同时设置地址为rt的通用寄存器的值为0。其中存储地址的计算方法如下。

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

下面通过一个例子体会ll、sc指令的作用,这个例子实现了上面介绍的wait函数,不过此处是使用ll、sc指令实现的。

wait:
ori $1, $0, sem         // sem是信号量的地址,将这个地址赋给寄存器$1

TryAgain:
ll  $2, 0($1)           // 获取信号量的值,保存到寄存器$2
bne $2, $0, WaitForSem  // 如果信号量被占用(其值为1),那么转移到地址WaitForSem
                        // 继续等待;如果信号量空闲(其值为0),那么执行下面的指令

nop
ori $2, $0, 1
sc  $2, 0($1)           // 如果没有被干扰,那么设置信号量被占用(将1保存到信号
                        // 量中),同时,设置寄存器$2为1,反之,不修改信号量,
                        // 设置寄存器$2为0

beq $2, $0, TryAgain    // 如果寄存器$2为0,表示ll、sc指令没有成功,未获取到
                        // 信号量,回到TryAgain继续尝试
nop

jr  $31                  // 反之, 表示ll、sc指令成功,获取到信号量,可以进入
                         // “临界区域”了。调用wait函数时,会将返回地址放在
                         // 寄存器$31,所以此处jr $31指令就是回到调用过程,
                         // 进入临界区域

下一次将修改OpenMIPS以实现LL、SC指令

时间: 2024-10-05 16:36:41

自己动手写CPU之第九阶段(7)——MIPS32中的LL、SC指令说明的相关文章

自己动手写CPU之第九阶段(4)——加载存储指令实现思路

将陆续上传新书<自己动手写CPU>,今天是第40篇,我尽量每周四篇,但是最近已经很久没有实现这个目标了,一直都有事,不好意思哈. 开展晒书评送书活动,在亚马逊.京东.当当三大图书网站上,发表<自己动手写CPU>书评的前十名读者,均可获赠<步步惊芯--软核处理器内部设计分析>一书,大家踊跃参与吧!活动时间:2014-9-11至2014-10-30 9.2 加载存储指令实现思路 9.2.1 实现思路 本节介绍除ll.sc之外的加载存储指令的实现思路,ll.sc指令的实现思路

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

将陆续上传新书<自己动手写CPU>,今天是第38篇,我尽量每周四篇,但是最近已经很久没有实现这个目标了,一直都有事,不好意思哈. 开展晒书评送书活动,在亚马逊.京东.当当三大图书网站上,发表<自己动手写CPU>书评的前十名读者,均可获赠<步步惊芯--软核处理器内部设计分析>一书,大家踊跃参与吧!活动时间:2014-9-11至2014-10-20 今天继续对MIPS32中加载存储指令进行说明(主要是lwl.lwr),上次已经介绍一些其他的加载存储指令,大家可以回顾. 9.

自己动手写CPU之第九阶段(3)——加载存储指令说明2(swl、swr)

将陆续上传新书<自己动手写CPU>,今天是第39篇,我尽量每周四篇,但是最近已经很久没有实现这个目标了,一直都有事,不好意思哈. 开展晒书评送书活动,在亚马逊.京东.当当三大图书网站上,发表<自己动手写CPU>书评的前十名读者,均可获赠<步步惊芯--软核处理器内部设计分析>一书,大家踊跃参与吧!活动时间:2014-9-11至2014-10-30 今天继续对MIPS32中加载存储指令进行说明(主要是swl.swr),上次已经介绍一些其他的加载存储指令,大家可以回顾. 9.

自己动手写CPU之第九阶段(5)——实现加载存储指令2(修改执行阶段)

将陆续上传新书<自己动手写CPU>,今天是第42篇,我尽量每周四篇,但是最近已经很久没有实现这个目标了,一直都有事,不好意思哈. 开展晒书评送书活动,在亚马逊.京东.当当三大图书网站上,发表<自己动手写CPU>书评的前十名读者,均可获赠<步步惊芯--软核处理器内部设计分析>一书,大家踊跃参与吧!活动时间:2014-9-11至2014-10-30 9.3.2 修改执行阶段 1.修改EX模块 在执行阶段的EX模块会计算加载存储的目的地址,参考图9-19可知,EX模块会增加部

自己动手写CPU之第九阶段(8)——MIPS32中的LL、SC指令说明

将陆续上传新书<自己动手写CPU>,今天是第47篇. 9.7 ll.sc指令实现思路 9.7.1 实现思路 这2条指令都涉及到访问链接状态位LLbit,可以将LLbit当做寄存器处理,ll指令需要写该寄存器,sc指令需要读该寄存器,同时,与对通用寄存器的访问一样,对LLbit寄存器的写操作也放在回写阶段进行. ll指令在访存阶段要读取数据存储器中指定地址的数据,还要设置对LLbit寄存器的写操作,写入的值为1,这个写操作会通过MEM/WB模块传递到回写阶段,最终实现对LLbit寄存器的写. s

自己动手写CPU之第九阶段(9)——修改OpenMIPS以实现ll、sc指令

将陆续上传新书<自己动手写CPU>,今天是第48篇. 9.8 修改OpenMIPS以实现ll.sc指令 9.8.1 LLbit寄存器的实现 LLbit寄存器在LLbit模块中实现,模块接口如图9-30所示,各接口描述如表9-8所示. LLbit寄存器的代码如下,源文件是本书光盘Code\Chapter9_2目录下的LLbit_reg.v文件. module LLbit_reg( input wire clk, input wire rst, // 异常是否发生,为1表示异常发生,为0表示没有异

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

将陆续上传新书<自己动手写CPU>.今天是第38篇,我尽量每周四篇,可是近期已经非常久没有实现这个目标了.一直都有事,不好意思哈. 开展晒书评送书活动,在q=%E4%BA%9A%E9%A9%AC%E9%80%8A&ie=utf-8&src=se_lighten_f" style="color:rgb(51,102,153); text-decoration:none; font-family:Arial; font-size:14px; line-heigh

自己动手写CPU之第九阶段(6)——修改最小SOPC

将陆续上传新书<自己动手写CPU>,今天是第45篇. 这几天事情多,好久没更新了 前几篇实现了加载存储指令,今天将修改最小SOPC,用以测试加载存储指令是否实现正确.闲话少说,进入正题. 9.4 修改最小SOPC 为了验证上一节添加的加载存储指令是否实现正确,需要修改在第4章中设计的最小SOPC,为其添加数据存储器RAM. 9.4.1 添加数据存储器RAM 数据存储器RAM的接口如图9-24所示,还是采用左边是输入接口,右边是输出接口的方式绘制,这样便于理解.接口含义如表9-7所示. 数据存储

自己动手写CPU之第七阶段(2)——简单算术操作指令实现过程

将陆续上传本人写的新书<自己动手写CPU>,今天是第25篇,我尽量每周四篇 亚马逊的预售地址如下,欢迎大家围观呵! http://www.amazon.cn/dp/b00mqkrlg8/ref=cm_sw_r_si_dp_5kq8tb1gyhja4 China-pub的预售地址如下: http://product.china-pub.com/3804025 7.2 简单算术操作指令实现思路 虽然简单算术操作指令的数目比较多,有15条,但实现方式都是相似的,与前几章逻辑.移位操作指令的实现方式也