从零开始学RISC-V之初探IFU

背景介绍

CPU内核设计几大块,主要包含取指单元(IFU),执行单元(EXU),访存单元(LSU),异常与中断(INT)以及调试单元(DBG)。一条指令的从取指单元开始,经过执行单元完成具体运算,按指令类型确定是否会访问内部或外部存储器,异常和中断负责处理特殊的情况,调试则是软件与硬件的窗口。每个模块的设计原则和侧重点都不一样。本节将从取指单元开始,介绍一条最简单的指令的运行过程。由于项目定位为一个入门级学习型项目,因此可能不会涉及到同步异常,不会涉及到中断以及调试单元等复杂模块。

让IFU开始读数据吧

终于开始IFU的设计了,这也就意味着你正式踏入CPU Core设计的领域了。不得不说,这是一个很大的进步。

IFU全称叫Instruction Fetch Unit,即指令获取单元。顾名思义,该单元的主要功能是从IRAM中按需获取指令,并将该指令发送给后续单元,比如说执行单元,进行处理。按照不同的应用场景介绍其工作原理,列述如下:

  • 当CPU处于上电之初状态时,IFU会使用指定的初始地址,也就是CPU的复位地址,从IRAM中获取指令。

  • 如果该指令没有发生普通跳转(或异常等其他情况),则IFU会根据指令长度(对于xf100来说,指令长度为4字节),计算出下一次取指的地址,就是当前使用地址(PC)+4。

  • 如果该指令是一条跳转指令,那么IFU同样需要根据一些历史信息来判断其下一次取指的地址。这种常见于高级编程语言中的if-else结构。由于IFU在判断时,某些历史信息并不是很准确,因此原则上会发生预测错误的情况,这个时候就需要将已经取到的预测地址的数据丢弃,而用正确的真实的地址重新取指。以一个if-else为例说明:

if (a == b)
    branch_a
else
   branch_b

上述实例中,对于不同的a和b的值,会执行不同的语句体。一般来讲,当IFU取到if语句对应的指令(这是一条跳转指令)时,它会根据当前的信息预测到底是取branch_a代表的指令,还是branch_b代表的指令。不论怎样,它都会有一个预测的结果P。但是if语句的执行结果是依赖a和b的值的,IFU并不知道这两个值的比较结果(因为指令的具体执行有专门的执行单元负责),因此有可能IFU预测的是错的。比如IFU预测的是后面应该执行branch_a,但是实际执行结果却是下一条指令应该执行branch_b,这样,IFU提前取到的branch_a就是一个错误的不该在此时被取到的指令。在错误的时间遇到的人,注定是要放弃的,希望大家明白。这个过程,就是大名鼎鼎的分支预测机制。分支预测算法对处理器性能有很大的影响,毕竟频繁的预测错误-丢弃的结果就是极大浪费时间,因此属于CPU设计的重难点之一。

总的来说,IFU的功能,就是保证在正确的时间取到正确的数据。

作为刚刚一脚踏入IFU设计的人来讲,我们暂时将这些复杂的分支预测机制放一放,先把IFU最最最基本的功能实现吧。那就是:你是一个成熟的IFU了,应该要知道从指定的地址去获取数据了。

一个“成熟”的IFU的实现代码如下:

    ////////////////////////////
    // 成熟的IFU
    module xf100_ifu  (
     // 与下一单元(EXU)的接口,本节暂时先不管
     output         o_ifu_exu_valid,
     input          o_ifu_exu_ready,
     output [31:0]  o_ifu_exu_pc,
     output [31:0]  o_ifu_exu_instr,
    ​
     // 与IRAM单元的接口
     output         ifu2ram_cs  ,
     output         ifu2ram_wen , 
     output [14:0]  ifu2ram_addr, 
     input  [31:0]  ifu2ram_din , 
    ​
     input   clk   ,
     input   rst_n
    );
    ​
    wire ifu_exu_valid;
    xf100_gnrl_dfflr #(1) ifu_valid_dfflr (clk, rst_n, 1'b1, 1'b1, ifu_exu_valid);
    ​
    // set pc
    `define RST_PC 32'h80000000  // 复位PC,当CPU上电时,IFU会从此地址获取到第一条指令。
    reg [31:0] ifu_pc;
    ​
    wire ifu_upena = o_ifu_exu_valid & o_ifu_exu_ready;
    // 就目前来讲,IFU会无条件自动获取相邻的下一条指令,所以IFU地址会一直+4
    wire [31:0] ifu_pc_nxt = ifu_pc + 32'h4; 
    always @ (posedge clk or negedge rst_n) begin
     if (rst_n == 1'b0) begin
     ifu_pc <= `RST_PC;
     end else if(ifu_upena) begin
     ifu_pc <= ifu_pc_nxt;
     end
    end
    ​
    assign o_ifu_exu_valid = ifu_exu_valid; // for now we just fix the ifu2exu valid to 1, assuming that the instr data from sram is always valid.
    assign o_ifu_exu_pc = ifu_pc;
    assign o_ifu_exu_instr = ifu2ram_din;
    ​
    // set the ifu req to instr sram.
    assign ifu2ram_cs  = 1'b1 & o_ifu_exu_ready; 
    assign ifu2ram_wen = 1'b0;  //IFU总是读IRAM,因此写使能一直拉低。
    assign ifu2ram_addr = ifu_pc; //每次IFU取指令的地址
    ​
    endmodule

以上就是IFU初探的所有设计。代码中涉及到了一种非常有效的控制机制,valid-ready握手机制。这一机制严格把控每一条指令的执行,每一个状态的翻转,从逻辑思路上保证CPU运行的严谨性,需要好好体会。后续文章会逐步深入解析该握手机制的运行原理。

将上述IFU和IRAM,按照预先设计好的逻辑层次例化,就完成了本节内容所预定的任务。在我们的xf100中,它是这样的:

  • tb_top是最顶层文件,它会例化xf100_soc

  • xf100_soc包含xf100_core和xf100_inst_ram两个模块,分别对于cpu core和IRAM。

  • xf100_core包含xf100_ifu和其他模块(暂时用不到)。

例化完成之后,就可以执行加载与仿真了。

加载与仿真

  • 首先在tb/tb_top.v文件里添加对IRAM的上电初始化操作。
// initial instruction ram.
`define INSTR_RAM u_xf100_soc.inst_ram.u_gnrl_inst_ram
integer i;
reg [7:0] instr_mem [0:16384*4-1];
​
initial begin
 $readmemh("../../riscv-tools/riscv-tests/isa/generated/rv32ui-p-add.verilog", instr_mem);
​
 for (i=0;i<16384*4;i=i+1) begin
 `INSTR_RAM.ram_r[i] = instr_mem[i];
 end
​
​
end 
  • 修改vsim/Makefile中的install部分,让新建的.v代码文件能被编译工具识别。需要注意的是此处的路径千万不能出错,否则会在编译过程中出现模块未定义的错误。
install: 
 mkdir -p ${SIM_DIR}/install/tb
 mkdir -p ${SIM_DIR}/install/rtl/${CORE}
 cp ${SIM_DIR}/../tb/tb_top.v ${SIM_DIR}/install/tb/ -rf
 cp ${SIM_DIR}/../../design ${SIM_DIR}/install/rtl/${CORE}/ -rf
  • 其余改动,你可以根据自身使用工具的情况自行修改。再次提醒注意各个文件的路径,包括存储路径,或者编译过程中使用的其他路径。一般而言,出现模块未定基本上都是这类原因。

上述改动完成之后,就可以运行仿真并查看波形了。实例波形图如下:

ifu接口波形图.png

从图中可以看出,Addr(注,此处只使用了15bit),按照步长4依次递增。同时从IRAM中返回于地址对应的指令数据。该数据可以与dump文件,也就是程序的反汇编文件对比,用于后续代码的调试。

下一步,我们将进入指令的执行模块,看看第一条加法指令到底是如何被执行的。

总结一下

  • IFU主要功能是从指定地址获取指令数据,并自行预测下一次读取指令的地址。有可能会预测错误,所以会产生一定的代价,IFU的设计目标之一是减少预测的准确率,并同时降低错误预测代价。

  • valid-ready握手机制在此处刚刚露了脸,但是由于仅仅只有IFU,因此其作用并未显现,但该机制是保证CPU功能的核心,需要注意。

同系列文章首发于微信公众号:ICLiker,愿逢有缘人
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,874评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,102评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,676评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,911评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,937评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,935评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,860评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,660评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,113评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,363评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,506评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,238评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,861评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,486评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,674评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,513评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,426评论 2 352