Zebu学习笔记(4)- 运行

终于到了上板的时刻了,对于玩硬件的人来说上板总是让人激动的,毕竟可以看到码的代码可以变成实际看得见摸得着的东西了。不过对于Zebu来说,上板看起来就是换了一个地方仿真而已,并没有什么,代码还是代码,和仿真相比除了速度快点,似乎没有什么特殊的地方。不过,速度快点也是很不错的呀,嘿嘿。

Zebu的运行一般有以下几种方式,并且和系统设计采用的方法也是相关的。

  1. Cycle based C/C++程序控制运行
  2. Transaction Based C/C++程序控制运行
  3. 仿真器,比如VCS控制
  4. 或者直接就是一个可综合的Testbench,这种可以直接运行
  5. 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,那么建议用户做好如下配置:

  1. 确保Testbench和zRun的脚本保持同步,避免关掉Testbench的时候导致未运行的脚本命令无法运行,比如Dump_off命令。
  2. 通过命令关闭zRun: zRun -synchroClose, 这个时候Testbench调用zebu->close()之后,必须有一个命令去关闭zRun。
  3. 不要再代码中使用类似如下的代码
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中的仿真时钟一般会进行分组处理,这里对于时钟的控制也是按照组进行处理的,一个组的时钟都是同时开启和关闭的,对于时钟,我们一般可以进行如下的控制:

  1. 时钟运行N个周期 ZEBU_Clock_enable <clk_name> <nb_cycles>
  2. 释放时钟全速运行 ZEBU_Clock_enableForever <clk_name>
  3. 停止时钟运行 ZEBU_Clock_disable <clk_name>
  4. 获取时钟状态(当前是否运行)ZEBU_Clock_getStatus <clk_name>
  5. 获取时钟运行周期数(自 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]

本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

相关文章

发表新评论