Simplescalar 模拟器介绍 及 sim-outorder超标量乱序执行步骤详解


        SimpleScalar模拟器是一个超标量、5级流水的RISC(Reduced Instruction Set Computing)体系结构模拟器,提供了从最简单的功能模拟到超标量乱序发射的不同的模拟程序。

       SimpleScalar模拟器在功能级上实现了执行驱动、解释执行,在行为级上实现了流水线模拟。该工具集提供了一个以GCC为主的编译器以及相关组件,能够产生基于SimpleScalar体系结构的目标代码,然后在SimpleScalar模拟器上运行。

运行模拟器时,主程序main( )做所有的初始化工作,并将二进制目标码载入内存,然后调用sim_main(),sim_main()在每个模拟器中单独说明,预先译码整个正文段,加快模拟。然后开始目标程序的模拟:


(1)Sim-fast

      Sim-fast是执行速度最快,最不关心模拟过程细节信息的子模拟器程序。它采用顺序执行指令的方式,没有指令并行;不支持cache的使用,也不进行指令正确性检查,由程序员保证每条指令的正确性;不支持模拟器本身内嵌的Dlite!调试器(类似于gdb调试器)。为了模拟器的速度优化,在缺省情况下,sim-fast模拟器不进行时间统计,不对指令的有关信息(如指令总数及访存指令数目)进行统计。当然,可以修改模拟器源程序,通过改变其设置,使模拟器更加符合设计人员的需求。


(2)Sim-safe

       在工具集中,是最简单的最友好的模拟器,在sim-fast的基础上添加了Dlite!调试支持,检查所有的指令错误,不讲究速度。


(3)Sim-bpred

      实现一个分支预测(branch prediction,也称作跳转预测)分析器,可采用五种分支预测方式:nottaken, taken, bimod,2lev, comb。

      特有参数:

      -bpred  <string> # bimod # branch predictor type{nottaken|taken|bimod|2lev|comb}

      -bpred:bimod <int>   # 2048 # bimodal predictor config (<tablesize>)

      -bpred:2lev<int list...> # 1 1024 8 0# 2-level predictor config


(4)Sim-cache &sim-cheetah

      Sim-cache实现cache模拟功能,为用户选择的cache和快表(TLB, translationlookaside buffer)设置生成cache统计,其中可能包含两级指令和数据cache ,还有一级指令和数据快表,不会生成时间信息。另外,实现cache模拟功能的还有sim-cheetah,能够有效地模拟全相联cache,并能同时生成各种cache set数配置下的cache统计量。同样地,sim-cheetah不会生成时间信息。

      特有参数:

      Sim-cache:

            -cache:dl1       <string># dl1:256:32:1:l # l1 datacache config, i.e., {<config>|none}

            -cache:dl2       <string># ul2:1024:64:4:l # l2data cache config, i.e., {<config>|none}

      Sim-cheetah:

            -refs         <string> #data #reference stream to analyze, i.e., {none|inst|data|unified}

            -R           <string> #lru # replacement policy, i.e., lru or opt

            -C             <string> # sa # cacheconfiguration, i.e., fa, sa, or dm


(5)Sim-eio

      这个模拟器支持生成外部输入/输出跟踪(EIO traces)和断点文件。外部事件跟踪俘获程序的执行,并且允许被打包到一个单独的文件,以备以后的再次执行。这个模拟器也提供在外部事件跟踪执行中在任意一点做断点。断点文件可被用于在程序运行中启动simplescalar 模拟器。

      特有参数:

      -fastfwd         <int># 0 # number of instsskipped before tracing starts

      -trace           <string># <null> # EIOtrace file output file name

      -perdump        <string list...> # <null> #periodic checkpoint every n instructions: <base fname> <interval>

      -dump     <string list...> #<null> # specify checkpoint file andtrigger: <fname> <range>

 


(6)Sim-outorder

      最完整的工具,前文中已提到。支持依序和乱序执行,branch predictor,memory hierarchy,function unit个数等参数设定。这个模拟器追踪潜在的所有流水(pipeline)操作。

特有参数:

      -fetch:ifqsize  <int>       #4 # instruction fetch queue size (in insts)

      -fetch:mplat    <int>     #3 # extra branchmis-prediction latency

      -fetch:speed    <int>     # 1 # speed offront-end of machine relative to execution core

      -decode:width   <int>    # 4 # instructiondecode B/W (insts/cycle)

 

(7)Sim-profile

      也叫functional simulation,但提供较完整的模拟参数,可依照使用者之设定,决定所要模拟的统计量,如instruction classes andaddresses、text symbols、memory accesses、branches and data segmentsymbols等,以方便使用者整理收集数据材料。

      特有参数:

      -all             <true|false>     #       false # enable all profile options

      -iclass          <true|false>     #       false # enable instruction class profiling

      -iprof           <true|false>     #       false # enable instruction profiling

      -brprof          <true|false>     #       false # enable branch instruction profiling

      -amprof          <true|false>     #       false # enable address mode profiling



超标量流水线详解:

      sim-outorder 是一个具有完整功能的模拟程序,在sim-outorder中使用了几乎所有的模拟资源。

具体乱序执行策略:

      有五种很重要的功能单元支持sim-outorder对指令序列的乱序执行:保留站与重定序缓冲(RUU)、Load/Store队列(LSQ)、取指队列、输入输出相关链和寄存器忙闲表。它们在simplescalar中是通过五种数据结构来实现的。

      RUU单元实现寄存器的同步和通讯功能,它将再定序缓冲和保留站统一起来,作为一个循环队列来管理。RUU队列记录了指令的操作类型、源操作数、数据有效性标识。其中的数据项在指令发射时分配,在提交时回收;当寄存器数据和存储器数据相关性满足时,实现乱序流出;

      Load/Store队列处理存储器的相关性问题。如果store操作是猜测执行的,其值就被放入队列中。当所有之前的写入地址都已知之后,Load操作就可以访存。如果地址匹配,load操作可以在存储系统或者Load/Store队列中以前的store值的允许下进行。

      取指队列是由取指段建立,在调度段译码并调度的指令队列;没有被调度的指令仍留在其中。它是用一个结构数组来实现的。

      所谓输入输出相关链,即是用来记录前一条指令的输出数据(结果操作数)与后几条指令的输入数据(源操作数)的相关性的链表。

      所谓寄存器忙闲表,即是用来记录当前各个寄存器被哪一条指令占用的结构数组。


Sim-outorder的具体的乱序过程如下:

      A.取指段:根据配置的各种参数的要求,从一级指令Cache里预取指令,加入到取指队列里。如果在一级cache里找不到指令,同时配置了二级cache,就试图从二级cache里再找,否则就从存储器里寻找。

      1. 根据分支预测的要求、cache 容量的支持、事先配置的取指队列的大小,确定预取多少条指令。

      2. 在地址有效的条件下,取出指令,并根据一级指令CACHE的延时和一级指令TLB的延时计算出其取指延时的大小。

      3. 若是分支指令,则要根据事先配置的分支预测策略预测下一条指令地址;若不是,指令地址自加一。

      4. 把这一条指令加入取指队列里,更新取指队列。

 

      B.调度段:从取指队列调度指令。指令首先被译码,然后为其分配RUU资源,判断是否存在数据相关性。如果不存在就可以发射出去,存在的话仍旧留在RUU队列里等待发射。若是访存指令则分配LSQ资源,最后更新输入输出的相关链。

 

      C.发射执行段:检查从调度段发射出来的指令所需的功能部件是否可用(结构相关性),如果可用则将其发射执行。

      1. 查看指令所需数据、功能部件是否准备好;

      2. 如果是 store 指令,执行之。由于数据可先存在LSQ队列中,执行时间为零,实际的访存操作在 ruu_commit() 中执行。其他指令则需先查看无功能部件。

      3. 如果是 load 指令,要确定 cache 访问的延时,先扫描LSQ队列看其前是否无访存地址相同的 store 指令。如果确实没有,那么store 指令存的数据就是 load 指令要取的数据,因而访存延时为一周期;如果没就并行访问数据 cache 和数据TLB,访存延时为二者中较大者。

      4. 如果是非访存指令,操作时间为其功能部件的执行时间;不需功能部件的指令,操作时间为一个周期。如果是空指令操作时间为零。

 

      D.LSQ队列更新:此过程是找出下一条数据相关性被满足了的指令,并将其发射。而这是通过检查LSQ队列,查找存储器阻塞的情况来实现的。

 

      E.写回段:完成把功能部件的输出数据(结果操作数)写入RUU(registerupdate unit) 的任务。就这点来说,模拟器根据正在完成的指令的输出数据,确定取指队列中的后续指令的输入数据是否与其相关,如果是这样,将把这条指令从取指队列中调度出来进行发射。

 

      F.提交段:这个阶段把已经完成的结果从RUU和LSQ提交到寄存器文件中,并且LSQ中的store 指令将把其存储数据提交到数据 cache 中。

      1. RUU和LSQ中结果可提交,就执行提交。

      2. 让LSQ中的 store 指令把其存储数据提交到数据 cache 中并计算其操作时间,其中要考虑TLB的延时。

      3. 按序把已经完成的结果从RUU和LSQ提交到寄存器文件中,并更新RUU和LSQ。

 


SimpleScalar主要数据结构简介:


      RUU(Register Update Unit):此结构将传统的保留站和排序缓冲站合并,实现了指令的乱序执行按序提交,用于支持处理器精确中断和误预测的状态恢复。而RUU将这两个部件合并可以减少硬件实现的花费。RUU被组织为循环队列,Head指向当前RUU中最先进入RUU的指令RUU单元,tail指针指向待分配RUU单元。在指令分配阶段,按取指先后顺序为每条指令分配RUU单元,指令在RUU中连续存放并且排列顺序与取指顺序一致,这使执行完成的指令能够按序提交。在执行阶段,RUU中的指令是乱序执行的,只要指令的操作数准备好,就可以将其送入RQ,为其分配执行单元执行指令。在回写段将执行结果广播到RUU,并解除相关。

      LSQ  (Load/Store Queue):用于处理Store/Load指令,其结构与RUU一致,一条Store/L

oad指令将被分成两个操作:地址计算和存取操作,地址计算操作作为加法运算放入RUU中,而存取操作放入LSQ中,提交的时候RUU和LSQ需要同步。

      IFQ (Instruction Fetch Queue):用于存放取指(Fetch)段取出的指令队列,它在一个周期内尽可能多的取出指令,并保存取出指令的一些相关信息,如当前指令地址、预测下一条指令地址等。RQ  (Ready Queue):如果指令执行所需所有操作数已准备好,则将指令放入此队列准备执行,它只是作为一种映射机制存在,将RUU中已经准备好的指令串联成一组,它本身并不保存任何信息,并且每当发射段运行一次后,RQ都被清空,准备在下一次运行分配段(Dispatch)的时候继续进行赋值。

      EQ (Event Queue):该队列记录已发射(Issue)指令何时执行完毕,回写段以此确定何时将指令执行结果写回RUU。


以下是模拟器主程序主要部分源代码:

      main(int argc, char **argv, char **envp)

      

             sim_check_options(sim_odb, argc, argv);//检验命令行参数,并对个模块初始化,sim_*.c定义 

            mem_init();//程序加载前Memory初始化,mem.c定义

            ld_load_prog();//程序加载,loader.c定义

            regs_init();//寄存器初始化,regs.c定义

            mem_init1();//程序加载后Memory初始化,mem.c定义

            sim_init();//模拟器初始化,sim_*.c定义

            sim_main()://模拟器执行入口函数,sim_*.c定义

      }


SimpleScalar中Out-of-Order模拟器内核

      Out -of-Order模拟器是提供了足够微体系结构细节的性能模拟器,是支持指令动态调度乱序执行的超标量模拟器,在2.0版的SimpleScalar中只为其提供了PISA一种指令集(4.0版还支持ARM、X86、Alpha指令集),PISA指令长度为64位,是一种类似MIPS的指令集,具有3种地址格式,包括32个整数寄存器,32个浮点寄存器。

      Out-of-Order模拟器主函数sim_main()的程序结构表示如下:

void sim_main(void)

{

    for ( ; ; )

    {   

                ruu_commit();//提交

                ruu_writeback();//写回

                lsq_refresh();//LSQ队列刷新

                ruu_issue();//发射执行

                ruu_dispatch();//分配

                ruu_fetch();//取指

                sim_cycle++;//模拟器周期,用于统计程序执行周期

     }

}

      for 循环执行一次代表流水线执行一个机器周期,每个ruu_*()函数代表流水线的一段,采取反向执行顺序是为确保流水线各段之间的互锁同步。整个流水线共分为取指(Fetch)、分配(Dispatch)、发射(Issue&Execute)、回写(Writeback)、提交(Commit)五段。


  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值