跟我写ARM处理器之二:主体结构的确定
ls if ( cod_is_mult )
code_rs =code_rsa;
else begin
code_rs =32b0;
code_rs[code_rot_num] = 1b1;
end
always @ ( * )
case ( code[11:8] )
4h0 : code_rsa =r0;
4h1 : code_rsa =r1;
4h2 : code_rsa =r2;
4h3 : code_rsa =r3;
4h4 : code_rsa =r4;
4h5 : code_rsa =r5;
4h6 : code_rsa =r6;
4h7 : code_rsa =r7;
4h8 : code_rsa =r8;
4h9 : code_rsa =r9;
4ha : code_rsa =ra;
4hb : code_rsa =rb;
4hc : code_rsa =rc;
4hd : code_rsa =rd;
4he : code_rsa =re;
4hf : code_rsa =rf;
endcase
*/
Sec_operand的例子就不用举了吧,无非是根据指令选择符合该指令的要求,来送给下一级的加/减法器。
所以说,这样的两级流水线我们同样可以完成。现在使用三级流水线,关键路径是26ns。如果使用两级流水线,绝对在50 ns以内。工作在20MHz的ARM,同样也是受低功耗用户们欢迎的。有兴趣的,在看完我的文章后,把ARM核改造成两级流水线。
现在要转换一个观念。以前的说法:第一级取代码;第二级解释代码,第三级执行代码。现在要转换过来,只有两级,第一级:取代码;第二级执行代码。而现在我做成第三级,是因为一级执行不完,所以要分两级执行。所以是:第一级取代码;第二级执行代码阶段一(主要是乘法);第三级执行代码阶段二(主要是加/减法)。
也许有人要问,那解释代码为什么不安排一级?是因为我觉得解释代码太简单,根本不需要安排一级,这一点,我在下一节会讲到。
既然这个核是三级流水线,还是从三级流水线讲起。我把三级流水线的每一级给了一个标志信号,分别是:rom_en, code_flag, cmd_flag。rom_en对应第一级取代码,如果rom_en==1b1表示需要取代码,那这个代码其实还处在ROM内,我们命名为“胎儿”;如果code_flag==1b1表示对应的code处于执行阶段一,可以命名为“婴儿”;如果cmd_flag==1b1,表示对应的code处于执行阶段二,命名为“小孩”。当这个指令最终执行结束,可以认为它死去了,命名为“幽灵”。
rom_encode_flagcmd_flag
-----------------
|胎儿|婴儿小孩-->幽灵
-----------------
现在,我们模拟一下这个执行过程吧。一般ROM里面从0开始的前几条指令都是跳转指令,以hello这个例程为例,存放的是:LDR PC,[PC,#0x0018];连续五条都是这样的。
刚上电时,rom_en==1b1,表示要取number 0号指令:
rom_en==1b1code_flagcmd_flag
(addr=0)
-----------------
|胎儿|婴儿小孩-->幽灵
-----------------
LDR PC,[PC,#0x0018]
第一个clock后;第一条指令LDR PC,[PC,#0x0018]到了婴儿阶段。
rom_en==1b1code_flagcmd_flag
(addr=4)
-----------------
|胎儿|婴儿小孩-->幽灵
-----------------
LDR PC,[PC,#0x0018]LDR PC,[PC,#0x0018]
第二个clock后,第一条指令LDR PC,[PC,#0x0018]到了小孩阶段。
rom_en==1b1code_flagcmd_flag
(addr=8)
-----------------
|胎儿|婴儿小孩-->幽灵
-----------------
(addr=8)(addr=4)(addr=0)
LDR PC,[PC,#0x0018]LDR PC,[PC,#0x0018]LDR PC,[PC,#0x0018]
当“小孩”== LDR PC,[PC,#0x0018]时,不能再取addr==8的指令了。因为addr=0时的LDR PC,[PC,#0x0018]更改了PC的值,不仅不能取新的code,连处于婴儿阶段的code也不能执行了。如果执行的话,那就是错误执行。为了避免addr=4的LDR PC,[PC,#0x0018]执行,我们可以给每一个阶段打一个标签tag,比如code_flag对应婴儿,cmd_flag对应小孩。只有在cmd_flag==1b1时,指令才执行。如下图所示。
rom_en==1b0code_flagcmd_flag
(addr=8)0-->0 -->
-----------------
|胎儿|婴儿小孩-->幽灵
-----------------
(addr=8)(addr=4)(addr=0)
LDR PC,[PC,#0x0018]LDR PC,[PC,#0x0018]LDR PC,[PC,#0x0018]
(修改PC)
发出读指令
一旦有修改PC,那么rom_en立即赋值为1b0。code_flag, cmd_flag在下一个时钟赋给1b0。表示在下一个时钟“婴儿”和“小孩”都是非法的,不能执行。但是新的PC值不是立即得到的,因为LDR指令是要从RAM取数据,在小孩阶段只能发出读指令,在一个时钟,新的PC值才出现在ram_rdata,但还没有出现在R15里面,所以要等一个时钟。
rom_en==1b0code_flag==1b0cmd_flag==1b0
(addr=8)
-----------------
|胎儿|婴儿小孩-->幽灵
-----------------
(addr=8)(addr=8)(addr=4)(addr=0 )
XLDR PC,[PC,#0x0018]LDR PC,[PC,#0x0018]LDR PC,[PC,#0x0018]
ram_rdata=NEW PC
在空闲的这个周期内,为了让指令不执行,只要赋值:rom_en, code_flag, c
ARM处理器主体结 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)