Zebu学习笔记(4)- 运行
终于到了上板的时刻了,对于玩硬件的人来说上板总是让人激动的,毕竟可以看到码的代码可以变成实际看得见摸得着的东西了。不过对于Zebu来说,上板看起来就是换了一个地方仿真而已,并没有什么,代码还是代码,和仿真相比除了速度快点,似乎没有什么特殊的地方。不过,速度快点也是很不错的呀,嘿嘿。
Zebu的运行一般有以下几种方式,并且和系统设计采用的方法也是相关的。
- Cycle based C/C++程序控制运行
- Transaction Based C/C++程序控制运行
- 仿真器,比如VCS控制
- 或者直接就是一个可综合的Testbench,这种可以直接运行
- zRun:这个是一个基于tcl的程序,可以控制Zebu的运行过程,zRun可以独立使用,并且有GUI界面和命令行运行方式
在运行过程中,用户需要制定Design编译生成的zebu.work
目录。用于控制系统运行的参数都放在一个designFeatures
的文件。zRun会生成一个配置模板,在designFeatures
文件中我们可以定义系统运行的速度,设置Design中的时钟参数,对目标memory进行初始化(相当于加载不同的程序)。
运行时钟配置
之前我们在往Zebu上移植的时候,时钟是通过zceiClockPort
实现的。这些clock的参数一般会在designFeatures
文件中进行定义,design的时钟可以划分为几个组,但是一般情况下我们只需要一组时钟就好,每组时钟都可以包含多个时钟,当一个组中有多个时钟的是偶,我们必须为这些时钟定义一个频率比例,这个比例可以通过VirtualFrequency
参数或者用Waveform
参数指定。比如下面的例子中,我们定义clock2
的频率是clock1
频率的两倍:
$U0.M0.clock1.Mode = "controlled"
$U0.M0.clock1.Waveform = "_-"
$U0.M0.clock1.VirtualFrequency = 1;
$U0.M0.clock1.GroupName = "clock_group"
$U0.M0.clock2.Mode = "controlled"
$U0.M0.clock2.Waveform = "_-"
$U0.M0.clock2.VirtualFrequency = 2;
$U0.M0.clock2.GroupName = "clock_group"
如果通过波形来定义频率比例可以用如下的形式(不太直观,个人不推荐)
$U0.M0.clock1.Mode = "controlled"
$U0.M0.clock1.Waveform = "_-"
$U0.M0.clock1.VirtualFrequency = 1;
$U0.M0.clock1.GroupName = "clock_group"
$U0.M0.clock2.Mode = "controlled"
$U0.M0.clock2.Waveform = "_-_-"
$U0.M0.clock2.VirtualFrequency = 1;
$U0.M0.clock2.GroupName = "clock_group"
运行Memory初始化
在Zebu系统运行的时候,我们可以通过memoryInitDB
命令来对设计中的memory进行初始化,这部分也是放在designFeatures
文件中的。
$memoryInitDB = "memory.init"
这里的memory.init
是一个文本文件,文件中包含类似如下信息:
<hierarchical.name.of.module>.mem_core_logic memory.txt
zRun控制系统运行
zRun是一个基于tcl的控制Zebu系统运行的工具,可以单独GUI或命令行运行,也可以和我们的C/C++ transactional/cycle-based testbench一起运行。如果是远程SSH运行,那么必须正确定义$DISPLAY
来获取正常的显示。zRun内建的函数列表可以通过zRun -functionList
获取。
运行zRun的时候,我们可以将所有的操作放在一个tcl文件中,通过-do <tclscript>
来指定zRun运行的脚本,比如我们可以在GUI开启前做一些初始化操作,比如初始化内存。
zRun –design <designFeatures> –zebu.work <zebu.work> –do <Tcl script>
如果希望zRun和我们定义的C/C++ Testbench一起运行,可以通过如下的命令运行:
zRun –zebu.work <zebu.work> –testBench <testbench executable>
如果只想在终端模式运行,那么必须提供一个tcl脚本来控制仿真,这个脚本必须包含所有的时钟初始化操作:
zRun –design <designFeatures> –zebu.work <zebu.work> –do <Tcl script> -nogui
# or with a c/c++ testbench
zRun -nogui -do <Tcl script> -testbench <testbench executable>
如果希望在终端模式下运行zRun,那么建议用户做好如下配置:
- 确保Testbench和zRun的脚本保持同步,避免关掉Testbench的时候导致未运行的脚本命令无法运行,比如
Dump_off
命令。 - 通过命令关闭zRun:
zRun -synchroClose
, 这个时候Testbench调用zebu->close()
之后,必须有一个命令去关闭zRun。 - 不要再代码中使用类似如下的代码
while { [ZEBU_getStatus]=="open" && [ZEBU_Clock_getStatus clk]=="running" } { after 100 }
因为[ZEBU_getStatus]=="open"
不总是那么可靠,相应的我们应该写成如下的形式
while { [ZEBU_Clock_getStatus clk]=="running" && [ZEBU_synchroCloseStatus] == "false" } { after 100 }
这里奉上一个简单的脚本
ZEBU_Dump_file data.fsdb clk
ZEBU_Dump_on
ZEBU_Clock_enableForever clk
while { [ZEBU_Clock_getStatus clk]=="running" &&
[ZEBU_synchroCloseStatus] == "false" } { after 1000 }
ZEBU_Clock_disable clk
ZEBU_Dump_off
ZEBU_close
ZEBU_exit
zRun中对运行时钟的控制
zRun可以对仿真的时钟进行精准的控制,我们前面说过,Zebu中的仿真时钟一般会进行分组处理,这里对于时钟的控制也是按照组进行处理的,一个组的时钟都是同时开启和关闭的,对于时钟,我们一般可以进行如下的控制:
- 时钟运行N个周期
ZEBU_Clock_enable <clk_name> <nb_cycles>
- 释放时钟全速运行
ZEBU_Clock_enableForever <clk_name>
- 停止时钟运行
ZEBU_Clock_disable <clk_name>
- 获取时钟状态(当前是否运行)
ZEBU_Clock_getStatus <clk_name>
- 获取时钟运行周期数(自 ZEBU_open命令执行开始计算)
ZEBU_Clock_getCounter <clk_name>
zRun中对memory进行控制
我么design中的memory,比如BRAM, LUTRAMs等zrm都可以在运行过程中通过zRun的tcl脚本进行控制,比如:
保存memory内容
我们可以通过ZEBU_Memory_storeToFile
或者ZEBU_Memory_storeToBuffer
来将memory中的内容保存到文件或buffer中:
ZEBU_Memory_storeToFile <mem_name> <file_name> [<first_addr> <last_addr>] [mode]
ZEBU_Memory_storeToBuffer <mem_name> [<first_addr> <last_addr>] [format]
从memory文件初始化memory
我们通过ZEBU_Memory_loadFromFile
命令来初始化memory,这个命令是动态的,可以在系统运行的时候调用,如果运行过程更新,那么更新时间点无法精确控制,所以一般是停掉时钟之后再更新memory。
ZEBU_Memory_loadFromFile <mem_name> <file_name> [<startAddress> [<stopAddress> [<fileOffset> ]]]
ZEBU_Memory_loadFromBuffer <mem_name> <buf_name> [<first_addr> <last_addr>] [format]
最后更新于 2020-02-27 12:50:59 并被添加「仿真 FPGA Zebu」标签,已有 9686 位童鞋阅读过。
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。