从零开始学RISC-V之邮箱和邮件的秘密

背景介绍

当一个应用软件被编译成可执行代码后,这种二进制数据会保存在各种不同类型的存储器中。对于xf100项目来讲,这个存储器就是指令RAM(简称IRAM,下同)。注意由于RAM在掉电后会丢失内部保存的数据,因此在实际应用中真实的程序会保存在ROM中,器件在上电之初会将该代码搬运到RAM中并按照设定的起始地址开始工作。如果将指令比喻为邮件的话,那么指令RAM就是专用的邮箱。

基于以上介绍,本节内容将围绕指令存储器展开。它包含两个任务:首先是生成一个实际的RISC-V可执行文件,我们将看到具体的risv指令,并分析cpu是如何识别该指令的。然后将会基于逻辑门设计一个SRAM模型。这两个任务的完成与否将直接关系到IFU(Instruction Fetch Unit,取指令单元)的功能。因此需要重视。

可执行文件的生成

我们常见的编程语言,基本上越好读懂,越高级,也就越抽象。所有的高级语言,需要被计算机认识,必须有一套翻译工具,就是编译,链接等等。以最常见的C代码为例,需要经过如下步骤才能成为可执行文件:

  • 预处理:将代码中的各种文件包含,宏定义,以及注释等内容做替换或者展开,生成一份“明文”式的代码,这个时候还是高级语言的代码,只不过看起来就完整一些。你可以理解成把论文的参考文献直接贴在论文后面,预处理之前的参考文献链接,变成了处理之后的参考文献全文。

  • 编译:属于整个程序构建的核心,这一步会将预处理后的代码文件,转化成汇编代码文件。这种文件是早期计算机程序形式的一种。这一过程是将高级编程语言的代码转换成低级语言代码,它与计算机指令集强相关。这里,人类还能从中大致看出源程序的一些影子,也就是说我们还能看懂部分这转换后的代码。从此处开始,后面的代码就完全脱离了人类的思维了。那个时候的产物就只有电脑能认识了。

  • 汇编:将之前生成的汇编代码转化成机器码。

  • 链接: 将机器码做最后的规整,包括代码的重定位之类的,生成最终的可执行文件。

那么具体到本项目,按如下过程操作:

  • 进入xf100/verify/riscv-tools/riscv-tests/isa目录。在该目录下你将看到各种官方的测试样例。此处我们仅关心一个例子rv32ui-p-add,它在rv32ui目录下。

  • 修改该路径下的Makefile文件

    • 修改35行的RISCV_PREFIX,这个参数用于定位gcc-riscv工具链。你需要将其正确指向工具链的存放位置,就是第二节里的那个软连接。如果该参数错误,会导致生成时出现如下错误:
make: xxx/bin/riscv-nuclei-elf-gcc: Command not found
  • 删除第79~88行,并修改78行的-march=rv32i。此处修改一是为了节省生成可执行文件的时间(毕竟我们只关注一个测试样例);其次是指定可执行文件所支持的指令集。由于xf100仅作为一个简单的核,因此不支持MACFD这一系列特性。为避免可执行文件中出现xf100不支持的指令,需要修改这个参数。

  • 在该路径下执行生成可执行文件命令

sh clean.sh  // 删除之前生成的文件
sh regen.sh  // 重新生成可执行文件

如果中间没有任何错误,经过一段时间后,你将会在该路径下的generate文件夹下看到生成的文件。其对应的汇编dump文件如下图所示:

riscv汇编代码.png

更多关于指令如何被识别的知识,请参见这里

设计一个简单的IRAM

RAM是一种掉电易丢失的存储器。一种常见的仿真模型是使用基于DFF触发器的一维数组来构建RAM的仿真模型。数组的下标就是RAM访问的地址,数组的每个元素记录的就是需要读写的内容。就本项目来讲,我们对IRAM做如下约束:

  • IRAM的读写数据位宽为32bit,即我们不支持16bit位宽的读写访问。由于xf100仅支持RV32I指令集,因此这个设置是合理的。

  • IRAM地址位宽设置为16bit,即访问空间从0~65535字节。

  • IRAM不存在写访问,由于IRAM只存储指令,也没必要对其存储内容进行修改,因此该设置也是合理的。

  • 读IRAM的数据在当拍就可以输出。这当然是一种理想的状况,尽管存在与实际RAM的工作状态不相符的缺点,但是xf100是一个简单的入门级项目,没必要在此处花费精力。就RAM模型来讲,只要符合其存储数据的基本应用需求即可。我们的重点在于厘清核内部的原理机制,此处简单实用即可。

基于以上设定,一个简单版本的IRAM的实现代码如下所示:

///////////////////////////////////////////////////////////
// 首先我们定义一个带负边沿复位(rst_n)和更新使能(lden)的D触发器。
module xf100_gnrl_dfflr #(
 parameter DW = 32
)(
 input clk,
 input rst_n,
 input          lden,
 input [DW-1:0] din,
 input [DW-1:0] dout
);
​
reg [DW-1:0] dout_r;
always @ (posedge clk or negedge rst_n) begin
 if (~rst_n) begin
 dout_r <= 1'b0;
 end else if (lden) begin
 dout_r <= din;
 end
end
​
assign dout = dout_r;
endmodule
​
///////////////////////////////////////////////////////////
// 然后根据前述D触发器,构建一个32位宽的,深度可配置的RAM
module gnrl_sram #(
 parameter DW=32, //数据位宽(Data Width)固定不变
 parameter AW=8 ,
 parameter DP=256 // 深度可配置
)(
 input           clk,
 input           rst_n,
 input           cs ,
 input           wen,
 input [DW-1:0]  din,
 input [AW-1:0]  addr,
 output [DW-1:0] dout
);
​
// DFF数组,用于模拟RAM的存储区
reg [7:0] ram_r [DP-1:0];
​
//// 写端口实现,在IRAM中不会使用到 
wire [DW-1:0] ram_nxt = din;
wire ram_wen = cs  & wen;
xf100_gnrl_dfflr #(8) ram_dfflr_0  (clk, rst_n, ram_wen,ram_nxt[07:00], ram_r[addr+0]);
xf100_gnrl_dfflr #(8) ram_dfflr_1  (clk, rst_n, ram_wen,ram_nxt[15:08], ram_r[addr+1]);
xf100_gnrl_dfflr #(8) ram_dfflr_2  (clk, rst_n, ram_wen,ram_nxt[23:16], ram_r[addr+2]);
xf100_gnrl_dfflr #(8) ram_dfflr_3 (clk, rst_n, ram_wen,ram_nxt[31:24], ram_r[addr+3]);
​
//// 读端口实现,数据在读请求有效的当拍就被输出。
wire ram_ren = cs & ~wen;
​
assign dout [07:00]= ram_r[addr+0] & {DW{ram_ren}};
assign dout [15:08]= ram_r[addr+1] & {DW{ram_ren}};
assign dout [23:16]= ram_r[addr+2] & {DW{ram_ren}};
assign dout [31:24]= ram_r[addr+3] & {DW{ram_ren}};
​
endmodule
​
//// 例化一个IRAM,大小为16384*32bit
module xf100_inst_ram_16384x32 #(
 parameter DW=32,
 parameter AW=14,
 parameter DP=16384
)(
 input           clk,
 input           rst_n,
 input           cs ,
 input           wen,
 input [DW-1:0]  din,
 input [AW-1:0]  addr,
 output [DW-1:0] dout
);
​
gnrl_sram  #(
 .DW(DW),
 .AW(AW),
 .DP(DP)
) u_gnrl_inst_ram (
 .clk     (clk  ), 
 .rst_n   (rst_n),
 .cs      (cs   ),
 .wen     (wen  ),
 .din     (din  ),
 .addr    (addr ),
 .dout    (dout )
);
​
endmodule

至此,IRAM设计结束。后续在仿真时,只需要使用readmemh函数初始化该IRAM,就可以满足IFU取指的需求了。具体内容后续会有介绍。关于IRAM的仿真,将在下一节IFU的设计完成之后一起进行。

总结一下

  • 所有的高级语言写的代码,都需要通过编译工具做一道转化手续,这个看起来复杂,实际上也真的很复杂。

  • IRAM就是一个存储指令的只读存储器,掉电易丢失,只处理IFU发送过来的读请求信号。

  • 数据在读请求信号有效的当拍就可以输出。但是这个行为并不很好,因为实际的RAM器件的时序并不是如此理想。

  • IRAM的设计以符合要求为原则,大小可配置方便后续进一步优化设计。

同系列文章首发于微信公众号: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

推荐阅读更多精彩内容