一生一芯学习记录(一):简单介绍 + 建立Verilator仿真环境
一开始先来简单讲述下我的个人情况,研一在读,目前C阶段结束,然后想简单记录一下做一生一芯中遇到的困难和想法,以便后人学习。我学习的版本是2306的版本,所以2407我就不做了,2407据说是给大一或者更小年纪的人平滑的学习一生一芯所用的版本,简单看了看,感觉对于时间紧张的研究生来说有点没必要。我并不会Markdown,所以可能博客写的会很丑,后面慢慢学应该会好看点,前面先别喷我😭。本人的github主页:https://github.com/yuweijie-20030124/ysyx-workbench
所用的系统:Windows10上使用WSL2的Ubuntu22.04,并且用vscode进行远程连接。
预学习阶段其实当时还是过了很久的,可能也得将近一年吧,然后完成C也用了两个月,所以之前东西可能忘了,多多包涵。
-
如何科学的提问:这个其实就是告诉你如何高效的寻求他人帮助,很简单,这里不再赘述。
-
Linux系统安装和使用:对于linux初学者和并不想那么折腾的人来说,我的建议是使用Ubuntu,下载东西直接用sudo apt install下载即可,只要找到合适的源。所以现在选择要么就是虚拟机,要么就是wsl,我的选择是wsl,可以根据你电脑的内存大小来进行选择。我电脑只有512G,之前使用过Linux,所以选择了wsl,wsl的优点就是运行速度快,缺点就是没有图形化界面。虚拟机优点就是有图形化界面,缺点就是内存占比会比较大,运行速度会慢一点,但是对于现代电脑来说也不会慢到哪里去了,看你电脑内存吧。
-
C语言:纯看教程,这个我觉得还挺重要的,因为后面nemu和C阶段都会涉及大量的C语言知识,你看不懂nemu如何运行你就写不了NPC了,所以其实还是非常重要。2407有两个章节要求学C,我的建议是没啥必要,没必要吧太多时间花费在C语言上,你可以先去看nemu是如何跑起来,如何执行一条指令的去一边学C一边用C,不会了再去前面看教程,效率会高一点,这里针对的是时间比较短的同学来说,如果你0基础或者大一那我建议你可以老老实实按照他的要求来,你们的时间还好多呢😭。
-
搭建verilator仿真环境:前期就是简单写一个让你仿真与门的一个建议testbench,等到C阶段,你自己写出你的RTL代码后要把这个RTL代码放到你类似NEMU的仿真环境下去跑,此时verilator就很重要了,会大量使用DPI-C功能,等你C就知道了,这里不赘述,总之预学习会先告诉你需要学什么,后面慢慢学精通就可以完成任务了。
来简单讲述下Verilator,首先我们来看官方手册的定义:
官方手册连接:https://verilator.org/guide/latest/overview.html
The Verilator package converts Verilog 1 and SystemVerilog 2 hardware description language (HDL) designs into a C++ or SystemC model that, after compiling, can be executed. Verilator is not a traditional simulator but a compiler.
翻译过来就是会将硬件描述语言转换为C++或者SystemC语言,编译之后会生成可执行文件,以此来达到仿真的行为,由此可见他是一个编译器,而非模拟器。
运行示例:照抄这里的C++ Example即可
仿真代码:
点击查看代码
#include "Vour.h"
#include "verilated.h"
int main(int argc, char** argv) {
VerilatedContext* contextp = new VerilatedContext;
contextp->commandArgs(argc, argv);
Vour* top = new Vour{contextp};
while (!contextp->gotFinish()) { top->eval(); }
delete top;
delete contextp;
return 0;
}
.V文件代码:
点击查看代码
module our;
initial begin $display("Hello World"); $finish; end
endmodule
别忘了更新环境变量
之后输入
verilator --cc --exe --build -j 0 -Wall sim_main.cpp our.v
上述操作没错的话就会生成一个build文件,其中会有一个可执行文件,运行它就会打印hello world出来。
示例: 双控开关
仿真文件
点击查看代码
#include <assert.h> //下面要用到assert
#include <stdio.h>
#include <stdlib.h>
#include "Vtop.h" //包含top模块的顶层类
#include <verilated.h>
#include <verilated_vcd_c.h> //向VCD文件中写入文件
int main(int argc, char** argv) {
VerilatedContext* contextp = new VerilatedContext;
contextp->commandArgs(argc, argv);
contextp->traceEverOn(true);//打开追踪功能
Vtop* top = new Vtop{contextp};
VerilatedVcdC *wave = new VerilatedVcdC;
top->trace(wave,0);
wave->open("waveform.vcd");
while (!contextp->gotFinish()) {
int a = rand() & 1;
int b = rand() & 1;
top->a = a;
top->b = b;
top->eval();
printf("a = %d, b = %d, f = %d\n", a, b, top->f);
assert(top->f == (a ^ b));//验证一下电路正确性,不对的话直接退出
wave->dump(contextp->time());
contextp->timeInc(1);//推动仿真时间
}
wave->close();
delete top;
delete contextp;
delete wave;
return 0;
}
RTL代码
点击查看代码
module top(
input a,
input b,
output f
);
assign f = a ^ b;//按位异或
endmodule
之后再obj_dir中就会生成一个名为Vtop的可执行文件,可以用./Vtop来执行他,随后会出现这样子的永不结束的仿真输出
tips:此时可以用ctrl + c 强行中断程序以达到退出的效果。
此时可以用GTKWAVE命令行输入
gtkwave waveform.vcd
以查看波形文件。
重点是
点击查看代码
while (!contextp->gotFinish()) {
int a = rand() & 1;
int b = rand() & 1;
top->a = a;
top->b = b;
top->eval();
printf("a = %d, b = %d, f = %d\n", a, b, top->f);
assert(top->f == (a ^ b));//验证一下电路正确性,不对的话直接退出
wave->dump(contextp->time());
contextp->timeInc(1);//推动仿真时间
}
编写Makefile
点击查看代码
clean:
rm -r obj_dir
sim:
verilator -Wall --cc --trace --exe --build sim_main.cpp top.v
all:
verilator -Wall --cc --trace --exe --build sim_main.cpp top.v
gtkwave obj_dir/waveform.vcd
接入NVBoard
直接复制粘贴他的example过来,然后修改一下仿真环境和管脚约束和.v文件即可算是接入NVBoard了。具体步骤如下:
修改仿真环境
点击查看代码
#include <nvboard.h>
#include <Vtop.h>
static TOP_NAME dut;
void nvboard_bind_all_pins(TOP_NAME* top);
int main() {
nvboard_bind_all_pins(&dut);
nvboard_init();
while(1) {
nvboard_update();
dut.eval();
}
}
修改RTL
点击查看代码
module top(
input clk,
input rst,
input [1:0] sw,
output reg led
);
always @(*) begin
led = sw[0] ^ sw[1]; // 按位异或
end
endmodule
修改管脚约束
点击查看代码
top=top
led (LD0)
sw (SW1, SW0)
然后make run
就可以开启NVBoard,拨动最后两个拨码开关看实验结果了。
实例:流水灯
和前面一样直接复制nvboard的example文件然后改三个地方:①RTL ②仿真 ③管脚约束
①RTL
点击查看代码
module top(
input clk,
input rst,
output reg [15:0] led
);
reg [31:0] count;
always @(posedge clk) begin
if (rst) begin led <= 1; count <= 0; end
else begin
if (count == 0) led <= {led[14:0], led[15]};
count <= (count >= 5000000 ? 32'b0 : count + 1);
end
end
endmodule
②仿真
点击查看代码
#include <nvboard.h>
#include <Vtop.h>
static TOP_NAME dut;
void nvboard_bind_all_pins(TOP_NAME* top);
static void single_cycle() {
dut.clk = 0; dut.eval();
dut.clk = 1; dut.eval();
}
static void reset(int n) {
dut.rst = 1;
while (n -- > 0) single_cycle();
dut.rst = 0;
}
int main() {
nvboard_bind_all_pins(&dut);
nvboard_init();
reset(10);
while(1) {
nvboard_update();
single_cycle();
}
}
③管脚约束
点击查看代码
top=top
led (LD15, LD14, LD13, LD12, LD11, LD10, LD9, LD8, LD7, LD6, LD5, LD4, LD3, LD2, LD1, LD0)
rst BTNL
此时命令行输入make run
即可查看流水灯现象。
更多Verilator学习可以使用这个网站https://www.itsembedded.com/,上面很多例程。
一些思考题和个人感想
1. 思考题
不知道NVBoard如何工作
阅读verilator编译出的C++代码, 然后结合verilog代码, 尝试理解仿真器进行仿真的时候都发生了什么.
理解RTL仿真的行为
试试从make命令开始, 看看一切是如何发生的. 通过前面的学习, 你已经掌握了足够的知识背景去理解NVBoard如何工作了: 包括Makefile的使用, C语言和C++中类的基本用法. 现在就试试阅读代码(Makefile也是代码), 看看示例中的verilog顶层端口, 约束文件, 以及NVBoard是如何建立联系的.
要求我们看NVboard和Verilator如何工作的,后面各出一片关于NVBoard的和Verilator的教程,写完了把链接放在这里。
verilator进阶学习
链接(还没写)
2. 个人思考
Verilator在C阶段中起到很重要的作用,预学习阶段的后续阶段中我们通过PA了解到了NEMU是如何运行的,此时我们可以在npc文件夹中写一个类似nemu的仿真环境,然后用Verilator将自己写的RTL代码编译成C++,将nemu取指,译码,执行,访存,回写的操作仿真我们自己的处理器中,其余操作仿真仿真环境中,那我们就可以在仿真环境中存储指令,通过Verilator中DPI-C的特性将指令进行取指,译码,执行,访存,回写等操作,从而在NPC上”跑起来“。
而NVBoard,虽然我还没到B阶段,但是我从周围已经完成B阶段的人看到他们后续要将NPC接入SoC中要用到NVBoard。
因此了解verilator和nvboard是如何运行的也十分重要。