目录
verilator详细内容可以查看官方手册Overview — Verilator 5.003 documentation
Verilator是一种开源的Verilog/SystemVerilog仿真器,可用于编译代码以及代码在线检查,Verilator能够读取Verilog或者SystemVerilog文件,并进行lint checks(基于lint工具的语法检测),并最终将其转换成C++的源文件.cpp和.h。
Verilator不直接将Verilog HDL转换为C++或者SystemC,反之Verilator将代码编译成更快的优化过的并且支持多线程的模型,该模型被依次包装在(wrapped)在C++/SystemC模型中。这样就生成一个编译的Verilog模型,其功能和Verilog是一致的,但效率由于基于C++即使是单线程模型也可以10倍快于SystemC,100倍快于基于解释Verilog的仿真器,并且通过多线程可以进一步加速。
# Prerequisites:
#sudo apt-get install git perl python3 make autoconf g++ flex bison ccache
#sudo apt-get install libgoogle-perftools-dev numactl perl-doc
#sudo apt-get install libfl2 # Ubuntu only (ignore if gives error)
#sudo apt-get install libfl-dev # Ubuntu only (ignore if gives error)
#sudo apt-get install zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
git clone https://github.com/verilator/verilator # Only first time
# Every time you need to build:
unsetenv VERILATOR_ROOT # For csh; ignore error if on bash
unset VERILATOR_ROOT # For bash
cd verilator
git pull # Make sure git repository is up-to-date
git tag # See what versions exist
#git checkout master # Use development branch (e.g. recent bug fixes)
#git checkout stable # Use most recent stable release
#git checkout v{version} # Switch to specified release version
autoconf # Create ./configure script
./configure # Configure and create Makefile
make -j `nproc` # Build Verilator itself (if error, try just 'make')
sudo make install
安装好verilator后可以在文件目录下找到官方提供的example。以make_hello_c为例
top.v文件
module top;
initial begin
$display("Hello World!");
$finish;
end
endmodule
sim_main.cpp文件
#include <verilated.h>// verilator官方库
#include "Vtop.h"//top.v会被封装为头文件供c++调用
int main(int argc, char** argv, char** env) {
if (false && argc && argv && env) {}
Vtop* top = new Vtop;// 构建verilator模型,可以通过类型调用top中的参数
while (!Verilated::gotFinish()) {// 开始仿真直到$finish
top->eval();// Evaluate model
}
top->final();//结束仿真
delete top;// 清除模型
return 0;// Return good completion status
}
Makefile文件
ifeq ($(VERILATOR_ROOT),)
VERILATOR = verilator
else
export VERILATOR_ROOT
VERILATOR = $(VERILATOR_ROOT)/bin/verilator
endif
default:
@echo "-- Verilator hello-world simple example"
@echo "-- VERILATE & BUILD --------"
$(VERILATOR) -Wall -cc --exe --build -j top.v sim_main.cpp
@echo "-- RUN ---------------------"
obj_dir/Vtop
@echo "-- DONE --------------------"
@echo "Note: Once this example is understood, see examples/make_tracing_c."
@echo "Note: Also see the EXAMPLE section in the verilator manpage/document."
Makefile用于文件构建,主要的语句只有
$(VERILATOR) -cc --exe --build -j top.v sim_main.cpp
-Wall:让verilator执行强类型警告--cc:得到C++输出--exe:和wrapper文件一起,为了创建一个可执行文件--build:让verilator能让自己执行运行程序,可以看到命令行中打印出"Hello World"
./obj_dir/Vour
Hello World
- our.v:2: Verilog $finish
事实上,这只是一个最简单的案例,在example下还有一个真正的案例tracing可以实现波形的输出,目录结构如下:
❯ tree -d
.
├── cmake_hello_c
├── cmake_hello_sc
├── cmake_protect_lib
├── cmake_tracing_c
├── cmake_tracing_sc
├── make_hello_c
│ └── obj_dir
├── make_hello_sc
├── make_protect_lib
├── make_tracing_c
├── make_tracing_sc
└── xml_py
头文件需要提供仿真所需内容,包含:
#include "verilated_vcd_c.h" //用于生成波形
#include "Vtop.h"
#include "verilated.h"
//dpi-c
#include "Vtop__Dpi.h"
#include <verilated_dpi.h>
//glibc
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
// Difftest
#include <dlfcn.h>
//readline
#include <readline/readline.h>
#include <readline/history.h>
//system time
#include <sys/time.h>
在仿真环境中,定义全局变量 top 实例化模块,其中包含两个变量top->clk,top->rst;定义上下文指针 contextp;定义波形指针 tfp;定义仿真时间 main_time;定义ref寄存器(用于difftest)
//================= Environment ===============
VerilatedContext* contextp;
Vtop* top;
VerilatedVcdC* tfp;
vluint64_t main_time = 0; //initial 仿真时间
double sc_time_stamp()
{
return main_time;
}
uint64_t ref_regs[33];
void hit_exit(int status) {}
//============ Main ============
int main(int argc, char** argv, char** env) {
contextp = new VerilatedContext;
contextp->commandArgs(argc, argv);
top = new Vtop{contextp};
//VCD波形设置 start
Verilated::traceEverOn(true);
tfp = new VerilatedVcdC;
top->trace(tfp, 0);
tfp->open("wave.vcd");
//VCD波形设置 end
//initial data
pmem_init();
cpu_init();
#ifdef CONFIG_DIFFTEST
init_difftest();
#endif
sdb_mainloop();
return 0;
}
在执行函数内实现单步运行,初始化后将复位信号拉高,时钟每周期变更一次。要注意每次eval后都要用dump函数来记录波形,不然wave中会按照之前的状态输出。
//================= Exec =====================
void cpu_init() {
//cpu_gpr[32] = CONFIG_MBASE;
top -> clk = 0;
top -> rst_n = 0;
top -> eval();
tfp->dump(main_time);
main_time ++;
top -> clk = 1;
top -> rst_n = 0;
top -> eval();
tfp->dump(main_time);
main_time ++;
top -> rst_n = 1;
}
void exec_once(VerilatedVcdC* tfp) {
top->clk = 0;
//printf("======clk shoule be 0 now %d\n",top->clk);
// top->mem_inst = pmem_read(top->mem_addr);
// printf("excute addr:0x%08lx inst:0x%08x\n",top->mem_addr,top->mem_inst);
top->eval();
tfp->dump(main_time);
main_time ++;
top->clk = 1;
//printf("======clk should be 1 now %d\n",top->clk);
top->eval();
tfp->dump(main_time);
main_time ++;
}
void cpu_exec(uint64_t n) {
for(int i; i < n; i++){
exec_once(tfp);
#ifdef CONFIG_DIFFTEST
difftest_exec_once();
#endif
}
}
//================= Memory ====================
addr_t img_size = 0;
uint8_t pmem[10485760] = {0};
uint8_t* cpu2mem(addr_t addr) {}
void pmem_init() {
char image_path[] = "/home/springkiss/ysyx-workbench/npc/image.bin";
}
基础设施主要包含各种trace工具,difftest和sdb。
itrace需要借助dpi-c读取出当前正在执行的指令,再链接llvm库进行反汇编输出;
difftest是一生一芯项目中最重要+好用的工具,是处理器调试的一大杀手锏。具体实现方式可以参考讲义内容;
sdb可以参考nemu的实现,能够进行单步运行和寄存器打印我认为就足够支持处理器的debug。
//================== Itrace ==================
// extern "C" void itrace(int itrace_data,addr_t itrace_addr){
// printf("excute inst %016x: %08x",itrace_addr,itrace_data);
// }
//================= Difftest =================
#ifdef CONFIG_DIFFTEST
void init_difftest() {}
void checkregs(uint64_t *ref_regs){}
void difftest_exec_once(){}
#endif
//=================== Sdb ====================
void gpr_display() {}
static int cmd_c(char *args) {}
static int cmd_q(char *args) {}
static int cmd_help(char *args);
static int cmd_si(char *args) {}
static int cmd_info(char *args) {}
#define NR_CMD ARRLEN(cmd_table)
static int cmd_help(char *args) {}
void sdb_mainloop() {}
以下是完成仿真框架时自己的Makefile构建,仅供参考。
all:
@echo "Write this Makefile by your self."
VSRCS = $(shell find $(./vsrc ) -name "*.v")
# CSRCS = $(shell find $(./csrc ) -name "*.c" -or -name "*.cc" -or -name "*.cpp")
INCLUDE = ./vsrc/include
sim:
$(call git_commit, "sim RTL") # DO NOT REMOVE THIS LINE!!!
@echo $(VSRCS)
verilator --trace --cc --exe --build \
--top-module top \
-I$(INCLUDE) ./csrc/sim_main.cpp $(VSRCS) \
-LDFLAGS -"lreadline"
wave: sim
./obj_dir/Vtop
gtkwave wave.vcd
count:
find . -name "sim_main.cpp" -or -name "*.[vc]" | xargs wc -l
clean:
rm -rf obj_dir
rm wave.vcd
include ../Makefile
Verilator支持systemverilog直接编程接口导入和导出语句。通过Dpi-C机制,可以实现仿真用c++文件和RTL文件的交互,基于此可以实现ebreak,env来通知仿真环境结束仿真,以及在实现总线之前的访存行为。
通常的仿真文件会定义MAX_SIMTIME来决定仿真何时结束。但是在处理器设计中,我们并不知道程序会执行多少条指令,因此可以设置ebreak指令:当程序执行到ebreak指令时,通知仿真环境结束仿真,并通过寄存器a0的值来判定程序执行是pass还是fail
//ebreak in c++
extern "C" void ebreak(){
printf(COLOR_GREEN);
printf("excute the ebreak inst\n");
printf(COLOR_END);
hit_exit(cpu_gpr[10]);
}
//ebreak in verilog
import "DPI-C" function void ebreak();
module EBREAK(
input wire [31:0] inst_i
);
always @(*) begin
if(inst_i == `INST_EBREAK)
ebreak();
end
endmodule
首先在c++中定义ebreak函数,打印执行指令,并调用hit_exit函数判断输出状态。verilog中,将函数import,当检测到ebreak时,就会调用c++的函数执行,实现仿真的结束。
env的实现思路和ebreak是一致的,主要用于取到不在译码列表中的指令时通知仿真环境结束仿真,并报出“invalid inst”的信息。在前期书写riscv指令时,方便debug。确认指令实现完整且正确后可以注释掉。
//env
extern "C" void env(){
printf(COLOR_RED);
printf("invalid inst\n");
printf(COLOR_END);
hit_exit(NPC_BAD);
}
由于单周期处理器设计时尚未接入总线,因此访存也是通过Dpi-C机制实现。其原理和ebreak一致,只不过添加了输入输出的信号,一生一芯讲义中已经给出了模板和伪代码,将其内容补全即可。实现过程中发现rdata信号会存在UNoptflat的警告,该警告会在另一个笔记中总结,这里使用/*verilator split_var*/进行消除。后续在实现输入输出及运行马里奥,也需要在c++的函数中书写mmio。
//Dpi-C in c++
//memory read
extern "C" void pmem_read(addr_t raddr, addr_t *rdata) {
//mmio-rtc
if(raddr == RTC_ADDR) {}
//memory
else { *rdata = ret;}
}
//memory write
extern "C" void pmem_write(addr_t waddr, addr_t wdata, char wmask) {
if (waddr < CONFIG_MBASE) return;
//memory
else if((waddr >= CONFIG_MBASE) && (waddr < CONFIG_MAX)) {
wdata >>= 8, wmask >>= 1, pt++;
}
}
//mmio-serial_port
else if(waddr == SERIAL_PORT) {}
}
Dpi-C in verilog
import "DPI-C" function void pmem_read(
input longint raddr, output longint rdata);
import "DPI-C" function void pmem_write(
input longint waddr, input longint wdata, input byte wmask);
module MEM(
//from EXU
input wire[63:0] raddr,
input wire[63:0] waddr,
input wire[63:0] wdata,
input wire[7:0] wmask,
input wire ren,
input wire wen,
//to EXU
output reg[63:0] rdata/*verilator split_var*/
);
//reg [63:0] rdata_buf;
always @(*) begin
if (ren) pmem_read(raddr, rdata);
else rdata = 64'b0;
if (wen) pmem_write(waddr, wdata, wmask);
else pmem_write(waddr, wdata, 0);
end
endmodule
根据讲义内容
在verilog中,通用寄存器一般会用二维数组实现。但是由于Dpi-C的二维数组机制较为复杂,因此可以使用一种高性能的实现方式:引用传递。
具体地,首先在c++中定义一个set_gpr_ptr函数,该函数接受一个类型为 svOpenArrayHandle 的参数,并将全局变量 cpu_gpr 设置为数组句柄的数据指针。这样,就可以通过 cpu_gpr 全局变量访问 svOpenArrayHandle 句柄表示的数组中的值。
接着,在 SystemVerilog 中导入了 set_gpr_ptr() 函数,并在 initial 块中调用了该函数,将 rf 数组作为参数传递给它。通过这种方式,就可以在 SystemVerilog 中调用 set_gpr_ptr() 函数,并将 rf 数组中的值作为参数传递给该函数,从而通过 cpu_gpr 全局变量访问 rf 数组中的值。
//================= Dpi-c =====================
//gpr info
uint64_t *cpu_gpr = NULL;
extern "C" void set_gpr_ptr(const svOpenArrayHandle r) {
cpu_gpr = (uint64_t *)(((VerilatedDpiOpenVar*)r)->datap());
}
//gpr dpi-c in verilog
import "DPI-C" function void set_gpr_ptr(input logic [63:0] a []);
initial set_gpr_ptr(rf); // rf为通用寄存器的二维数组变量
参考资料:
Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图
我想开始使用“Sinatra”框架进行编码,但我找不到该框架的“MVC”模式。是“MVC-Sinatra”模式或框架吗? 最佳答案 您可能想查看Padrino这是一个围绕Sinatra构建的框架,可为您的项目提供更“类似Rails”的感觉,但没有那么多隐藏的魔法。这是使用Sinatra可以做什么的一个很好的例子。虽然如果您需要开始使用这很好,但我个人建议您将它用作学习工具,以对您来说最有意义的方式使用Sinatra构建您自己的应用程序。写一些测试/期望,写一些代码,通过测试-重复:)至于ORM,你还应该结帐Sequel其中(imho
目录0专栏介绍1平面2R机器人概述2运动学建模2.1正运动学模型2.2逆运动学模型2.3机器人运动学仿真3动力学建模3.1计算动能3.2势能计算与动力学方程3.3动力学仿真0专栏介绍?附C++/Python/Matlab全套代码?课程设计、毕业设计、创新竞赛必备!详细介绍全局规划(图搜索、采样法、智能算法等);局部规划(DWA、APF等);曲线优化(贝塞尔曲线、B样条曲线等)。?详情:图解自动驾驶中的运动规划(MotionPlanning),附几十种规划算法1平面2R机器人概述如图1所示为本文的研究本体——平面2R机器人。对参数进行如下定义:机器人广义坐标
一、机器人介绍 此处是基于MATLABRVC工具箱,对ABB-IRB-1200型号的微型机械臂进行正逆向运动学分析,并利Simulink工具实现对机械臂进行具有动力学参数的末端轨迹规划仿真,最后根据机械模型设计Simulink-Adams联合仿真。 图1.ABBIRB 1200尺寸参数示意图ABBIRB 1200提供的两种型号广泛适用于各作业,且两者间零部件通用,两种型号的工作范围分别为700 mm 和 900 mm,大有效负载分别为 7 kg 和5 kg。 IRB 1200 能够在狭小空间内能发挥其工作范围与性能优势,具有全新的设计、小型化的体积、高效的性能、易于集成、便捷的接
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。我一直在Rails上做两个项目,它们运行良好,但在这个过程中重新发明了轮子,自来水(和热水)和止痛药,正如我随后了解到的那样,这些已经存在于框架中。那么基本上,正确了解框架中所有智能部分的最佳方法是什么,这将节省时间而不是自己构建已经实现的功能?从第1页开始阅读文档?是否有公开所有内容的特定示例应用程序?一个特定的开源项目?所有的rails交通?还是完全
关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。关闭4年前。Improvethisquestion我希望能够将模板化的YARD文档样式注释插入到我现有的Rails遗留应用程序中。目前它的评论很少。我想要具有指定参数的类header和方法header(通过从我假定的方法签名中提取)和返回值的占位符。在PHP代码中,我有一些工具可以检查代码并在适当的位置创建插入到代码中的文档header注释。在带有Ducktyping等的Ruby中,我确信诸如@params等类型之类
我尝试用Ruby设计一个基于Web的应用程序。我开发了一个简单的核心应用程序,在没有框架和数据库的情况下在六边形架构中实现DCI范例。核心六边形中有小六边形和网络,数据库,日志等适配器。每个六边形都在没有数据库和框架的情况下自行运行。在这种方法中,我如何提供与数据库模型和实体类的关系作为独立于数据库的关系。我想在将来将框架从Rails更改为Sinatra或数据库。事实上,我如何在这个核心Hexagon中实现完全隔离的rails和mongodb的数据库适配器或框架适配器。有什么想法吗? 最佳答案 ROM呢?(Ruby对象映射器)。还有
据我了解,Python的扭曲框架为网络通信提供了更高级别的抽象(?)。我正在寻找在Rails应用程序中使用与twisted等效的Ruby。 最佳答案 看看EventMachine.它不像Twisted那样广泛,但它是围绕事件驱动网络编程的相同概念构建的。 关于python-Ruby是否有相当于Python的扭曲框架作为网络抽象层?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/9
我想使用比Rails(Sinatra/Ramaze/Camping)更轻的框架,但我担心这样做我将无法使用许多以插件形式为Rails定制的共享库.这是一个主要问题,还是这些插件中的大多数都可以跨不同的Ruby框架使用?使用Ruby框架而不是Rails是否还有其他潜在的缺点? 最佳答案 您仍然可以使用gems在你提到的所有框架中,很多东西都是可重用的。想要交换一个新的ORM,没问题。想要一个花哨的shmacy语法高亮,没问题。Rails一直在大力插入摆脱旧的插件模型,转而使用gems。如果其他框架之一符合您的需求,最好使用它。请记住,
我将以下代码放入RSpec测试中:it{shouldvalidate_format_of(:email).not_with('test@test')}并设置实际的类:validates:email,:presence=>true,:format=>/\b[A-Z0-9._%-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b/i当我运行测试时,我得到:失败:1)用户失败/错误:它{应该validate_format_of(:email).not_with('test@test')}当电子邮件设置为“test@test”时,预期错误包括“can'tbeblank”,得到错误