该系列中的先前文章
实际上,这篇文章早在6月就以DOC文件的形式出现。然后,同时写了五篇文章。但是将DOC文件上传到Habr是另一项任务。因此,碰巧它的时间只是现在才出现(还有另外两个正在等待中)。在上载时,我注意到,如果您不了解前几篇文章的精神,那么这篇文章看起来会很无聊。因此,如果有这样的需求,请至少刷新上一篇文章,或者至少刷新这两篇文章(“使USB总线分析仪成为领导……”和“模拟Quartus项目的行为……”)。
介绍
那么,完成的模型在哪里可以得到呢?有一个项目可以解决与我们正在开发的分析仪完全相同的问题,但是具有几个功能。第一个功能是用于Xilinx FPGA。其次,它是完全无证的。它以某种方式起作用。您甚至可以购买现成的面包板,并用现成的二进制代码填充它……并获得一些功能。任何需要任何设备的人都可以简单地遵循这条道路。但是没人知道如何发展它。该项目在这里。在\ ulpi_wrapper \ testbench目录中有一组文件用于测试围绕ULPI的包装子系统。他们建议在Icarus Verilog环境中进行建模,但我四处逛逛,但没有找到关于如何使用SystemC语言执行此操作的明智描述。因此,我决定继续在ModelSim环境中工作。如果我知道结局如何...但是我不知道。因此,我开始研究。在演示过程中,将显示成功和失败。让我们从失败开始,让每个人都可以看到不这样做的方法。
尝试“正面”进行所有操作均未成功
最初,我决定以现成的示例为例,并通过建模进行运行。通过惯常的动手(并且在上一篇文章中塞入了手),我创建了一个包含Verilog和SystemC上文件的测试套件。结果是这样的:我
启动了ModelSim,但是在工作组中没有看到与SystemC相关的任何内容。我看到了Verilogov代码,但Sishny代码没有。
如果查看日志,可以看到他们没有尝试收集日志。怎么了?
有关配置* .do文件的有用信息
已知* .do文件可运行ModelSim。但是,作为一个爱用“鼠标”做所有事情的恋人,我从没看过他的内心。让我们寻找它并打开它!在项目目录中只有一个这样的文件。这可能是我们需要的。
我们打开它。首先-组装项目中包括的各种服务项目和文件。
观看文字
transcript on
if ![file isdirectory verilog_libs] {
file mkdir verilog_libs
}
if ![file isdirectory vhdl_libs] {
file mkdir vhdl_libs
}
vlib verilog_libs/altera_ver
vmap altera_ver ./verilog_libs/altera_ver
vlog -vlog01compat -work altera_ver {c:/intelfpga_lite/17.1/quartus/eda/sim_lib/altera_primitives.v}
vlib verilog_libs/lpm_ver
vmap lpm_ver ./verilog_libs/lpm_ver
vlog -vlog01compat -work lpm_ver {c:/intelfpga_lite/17.1/quartus/eda/sim_lib/220model.v}
vlib verilog_libs/sgate_ver
vmap sgate_ver ./verilog_libs/sgate_ver
vlog -vlog01compat -work sgate_ver {c:/intelfpga_lite/17.1/quartus/eda/sim_lib/sgate.v}
但是最后-显然是我们需要的东西的组装,我用ulpi_wrapper.v文件的名称来判断:
vlog -vlog01compat -work work +incdir+C:/Work/UsbHead1/SystemCPlay {C:/Work/UsbHead1/SystemCPlay/ulpi_wrapper.v}
vsim -t 1ps -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_lnsim_ver -L cycloneive_ver -L rtl_work -L work -L UsbHead1 -voptargs="+acc" lalala
add wave *
view structure
view signals
run 10 us
真。没有Verilog模块的组装,也没有在SystemC上组装模块的提示。唯一可惜的是,每次启动模拟时都会自动创建此DO文件,因此您不能随便进行编辑。它是由一个非常复杂的TCL脚本创建的。没有欲望去统治它。但是,在有关搞笑季度的文章发表之后,很明显,这样的琐事并不是放弃的理由。当然,一切都已经存在。唯一可惜的是文档中说“您可以用这种方式制作脚本,也可以这样做”,并且没有示例提示。好吧,让我们通过实验来推断一切。创建一个文件C:\ Work \ UsbHead1 \ SystemCPlay \ myrun.do,然后尝试将控制权转移给它。首先,我们尝试这样做:
主DO文件仍然继续生成,但是其结尾变为:
vlog -sv -work UsbHead1 +incdir+C:/Work/UsbHead1/UsbHead1/synthesis/submodules {C:/Work/UsbHead1/UsbHead1/synthesis/submodules/UsbHead1_master_0_b2p_adapter.sv}
vlog -sv -work UsbHead1 +incdir+C:/Work/UsbHead1/UsbHead1/synthesis/submodules {C:/Work/UsbHead1/UsbHead1/synthesis/submodules/UsbHead1_master_0_timing_adt.sv}
vlog -vlog01compat -work work +incdir+C:/Work/UsbHead1/SystemCPlay {C:/Work/UsbHead1/SystemCPlay/ulpi_wrapper.v}
vsim -t 1ps -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_lnsim_ver -L cycloneive_ver -L rtl_work -L work -L UsbHead1 -voptargs="+acc" lalala
do C:/Work/UsbHead1/SystemCPlay/myrun.do
我们看到仍在编译Verilog文件,然后仍开始建模过程(尽管我在测试运行过程中看到了,但是现在可以肯定地说vsim命令启动了此过程),然后将控制权转移到我们的脚本。该脚本应控制显示过程。但是我们仍然无法管理程序集。如果收集的文件不足,系统将在允许我们执行任何操作之前错误地掉线。好吧,太好了,让我们尝试最后的设置。
从这里开始乐趣。非常重要,我将其框定。
我选择了一个脚本,但未选中。我进入设置(我有先前选择的选项)。我选择,没有选择。等等-甚至变成蓝色。直到我注意到它,直到找到如何赢取-我杀死了晚上!原来,如果您只是选择一个文件,则“应用”按钮将保持灰色。并且这些更改将不被记住。必须通过编辑其他对话框参数使“应用”按钮变为黑色!在上图中,它完全是黑色的。如果仍然显示为灰色,则更改将不会保存,并且所有内容都不会重新配置为使用脚本。
该脚本仍在形成中,但其结尾对我们来说更加方便。
vlog -sv -work UsbHead1 +incdir+C:/Work/UsbHead1/UsbHead1/synthesis/submodules {C:/Work/UsbHead1/UsbHead1/synthesis/submodules/UsbHead1_master_0_timing_adt.sv}
do "C:/Work/UsbHead1/SystemCPlay/myrun.do"
最后,为项目建立资源的过程完全由我们决定!精彩!那时,我只能找到为Xilinx编写的SystemC Verification with ModelSim文档。但是ModelSim在非洲。使用本文档中的示例以及过去实验中创建的DO文件的示例,我编写了以下脚本文本(不要被过多的键所惊吓,下面我们将丢弃几乎所有内容,然后我们还将用绝对路径替换绝对路径,在这一阶段,我从示例中提取了所有内容并自动生成样本)。
vlog -vlog01compat -work work +incdir+C:/Work/UsbHead1/SystemCPlay {C:/Work/UsbHead1/SystemCPlay/ulpi_wrapper.v}
vlib sc_work
sccom –g –I C:/intelFPGA_lite/17.1/quartus/cusp/systemc/include –work sc_work C:/Work/UsbHead1/SystemCPlay/ulpi_driver.cpp
鼓声……ModelSim向我们宣告:
如果我们省略所有淫秽的词语,那么我无话可说……但是这样的道路已经过去了!在哪里可以获得另一个ULPI模型?当然,我与专业从事FPGA重大项目的外国朋友达成了协议。特别是对我来说,他们在周末开放了对具有许可的ModelSim的计算机的远程访问。第二个薄煎饼也显得笨拙:即使是许可形式的64位版本也不能与SystemC一起使用。但是最后,我可以使用许可的ModelSim的32位版本。因此,我们继续讲故事...
关于文档的几句话
所以。现在,我可以使用许可软件了,现在该讨论在哪里查找信息以及从哪里获得灵感。在网络上,有关该语言的信息相当粗略。但是在系统交付时,有以下有用的目录:
C:\ modeltech_10.2c \ docs \ pdfdocs-文档,包括PDF格式的文件。我喜欢以下文件:modelsim_se_ref.pdf(《 ModelSim SE命令参考手册》),modelsim_se_user.pdf(《 ModelSim SE用户手册》)和modelsim_se_tut.pdf(《 ModelSim SE教程》)。语言本身并不多,但是关于如何连接文件以及如何解决方言问题的内容却不多。
接下来,有用的目录C:\ modeltech_10.2c \ examples... 有现成的* .do文件以及现成的cpp和h文件的示例。对我们来说最有用的示例是C:\ modeltech_10.2c \ examples \ systemc \ vlog_sc。它显示了如何从Verilog代码访问SystemC代码。最后,我们将完全按照这种方式进行。
在C:\ modeltech_10.2c \包括\ SystemC的目录包含了语言的类型库的源代码。不错的参考。就像他们说的,有鱼类可以预防无鱼和癌症。
目录中的所有内容。现在是一本很棒的书的标题,从中您可以学到很多有关语言及其编程技术的知识。SystemC-从头开始,第二版。大卫·布莱克(David C. Black),杰克·多诺万(Jack Donovan),比尔·邦顿(Bill Bunton),安娜·基斯特(Anna Keist)。
SystemC方言
所以。根据先前创建的脚本,获得了对工作系统的访问权限后,我很高兴地组装了该项目。他没有错误地组装!GitHub的第一个模型同意与我们合作!为了运行基准测试,我将同一目录中的ulpi_wrapper_tb.cpp文件添加到了项目中,并出现了大量错误。假设该行中存在错误:
m_vpi_handle = vpi_handle_by_name(((const char *)name,NULL);
难以修复,但仍然可行。但是线
// Update systemC TB
if(sc_pending_activity())
sc_start((int)(time_value-m_last_time),SC_NS);
想到了坏主意。库中没有sc_pending_activity()函数。有一个sc_pending_activity_at_current_time()函数,但我什至不理会它。我将不做一千个单词的解释,而是转储:
共有44个文件带有此文本(* .exe,* .dll等)。
您可以尝试重写所有内容……但这是否必要?让我提醒您,我实际上已经开始了所有这一切,因为我想使用所有准备就绪的东西。如果我真的浪费很多时间,我可以在纯净的SystemVerilog上的免费环境中开发所有内容……我去这里是为了避免浪费时间,而是节省时间!但是实际上……最主要的是不要忘记我们在做什么。我们要使用ULPI总线模型。她聚集了自己。尝试从示例构建完整的测试系统时出现问题。这是为什么?好吧,完整的系统无法正常工作,还可以。我们将通过反复试验来掌握一个模型,而不用看系统的工作。
消除基于方言的误解
所以。我们将做一个混合系统。该模型的模块将使用SystemC语言编写,我将向其提交测试操作,并使用Verilog语言开发该模块。也就是说,您需要使ulpi_driver模块出现在工作组中。
通过查看ModelSim交付的* .do文件的示例,我大大简化了脚本,最后,我做到了:
vlog +../../SystemCPlay {../../MyCores/ULPIhead.sv}
sccom -g ../../SystemCPlay/ulpi_driver.cpp
sccom -link
没有错误,但是模块也没有出现在组中。检查示例文件(记住,实现这种混合语言的最佳示例在C:\ modeltech_10.2c \ examples \ systemc \ vlog_sc目录中),我意识到应将以下行添加到ulpi_driver.cpp文件的末尾:
SC_MODULE_EXPORT(ulpi_driver);
ModelSim的文档说这些是方言功能。瞧!这是我们的模块:
正确,“创建波浪”菜单(我们在上一篇文章中讨论了此菜单)不可用。而且他没有港口。从历史上看,我首先处理端口,但是有条不紊地进行处理-我将有关它们的故事推迟到以后。否则,您必须编辑两次代码。为了不这样做,首先我们将做一些准备。
制作时钟发生器
事实证明,该模型与实际ULPI有一些差异。第一个区别是芯片必须生成66 MHz时钟。我们在模型中看到什么?
sc_in<bool> clk_i;
紊乱!让我们开始返工!除非另有说明,否则所有工作均在ulpi_driver.h文件中进行。
更改端口类型。它是:
sc_in<bool> clk_i;
变成了(我也更改了端口名):
sc_inout<bool> clk;
我从书中得知,通过添加变量可以插入真正的生成器:
sc_clock oscillator;
我们在构造函数中设置参数。结果,构造函数采用以下形式:
//-------------------------------------------------------------
// Constructor
//-------------------------------------------------------------
SC_HAS_PROCESS(ulpi_driver);
ulpi_driver(sc_module_name name): sc_module(name),
m_tx_fifo(1024),
m_rx_fifo(1024),
oscillator ("clk66",sc_time(15,SC_NS))
{
最后一行仅用于此目的。如果你愿意,你甚至可以开始了模拟,双击usb_driver模块,然后拉clk66的临时小屋和运行仿真过程中一点点。我们已经了解了生成器的工作原理:
不要忘了在主线程启动的地方更改时钟信号的名称。它是:
SC_CTHREAD(drive, clk_i.pos());
成为:
SC_CTHREAD(drive, clk.pos());
内部链接已被替换。但是我没有发现将信号带到外面有多漂亮。也许我只是缺乏资格。但是,以某种方式,将端口拉出的所有尝试均未成功。总是有障碍。我什至在一个论坛上找到了一个讨论,作者需要这样做。团队决定只能将其转发到输入端口。但是我们需要出去!因此,我们这样做。
在构造函数下方添加一个流函数:
void clkThread(void)
{
while (true)
{
wait(oscillator.posedge_event());
clk.write (true);
wait(oscillator.negedge_event());
clk.write (false);
}
}
并在类构造函数中添加一个指向它的链接:
SC_THREAD(clkThread);
让我展示当前的构造器区域,以给出当前结果的整体视图:
SC_HAS_PROCESS(ulpi_driver);
ulpi_driver(sc_module_name name): sc_module(name),
m_tx_fifo(1024),
m_rx_fifo(1024),
oscillator ("clk66",sc_time(15,SC_NS))
{
SC_CTHREAD(drive,clk.pos());
SC_THREAD(clkThread);
m_reg[ULPI_REG_VIDL] = 0x24;
m_reg[ULPI_REG_VIDH] = 0x04;
m_reg[ULPI_REG_PIDL] = 0x04;
m_reg[ULPI_REG_PIDH] = 0x00;
m_reg[ULPI_REG_FUNC] = 0x41;
m_reg[ULPI_REG_OTG] = 0x06;
m_reg[ULPI_REG_SCRATCH] = 0x00;
}
void clkThread(void)
{
while (true)
{
wait(oscillator.posedge_event());
clk.write (true);
wait(oscillator.negedge_event());
clk.write (false);
}
}
所有。第一次编辑完成。
制作双向数据总线
ULPI具有双向数据总线。在模型中,我们看到以下描述:
sc_out <sc_uint<8> > ulpi_data_o;
sc_in <sc_uint<8> > ulpi_data_i;
紊乱!首先,我们将基于输出总线进行空白处理,然后将所有内容切换为空白。从哪里开始?从总线必须能够进入第三状态这一事实出发,sc_uint <8>类型仅适用于二进制数据。sc_lv <8>类型将帮助我们。因此,我们将轮胎声明更改为:
sc_inout <sc_lv<8> > ulpi_data_o;
现在转到ulpi_driver.cpp文件,查找在那里对ulpi_data_o总线的所有调用。凭直觉,我意识到只有一个地方可以解决:
相同的文字。
void ulpi_driver::drive_input(void)
{
// Turnaround
ulpi_dir_o.write(false);
ulpi_nxt_o.write(false);
ulpi_data_o.write(0x00);
wait(oscillator.posedge_event());
}
将所选行更改为
ulpi_data_o.write("ZZZZZZZZ");
所有。现在您可以代替两行:
sc_inout <sc_lv<8> > ulpi_data_o;
sc_in <sc_uint<8> > ulpi_data_i;
写一个:
sc_inout <sc_lv<8> > ulpi_data;
并用对ulpi_data变量的引用替换所有在h-nick和cpp-shnik中对旧变量的引用。
添加端口别名
所以。经过长时间的搜索,我得出的结论(可能是错误的)是,在ModelSim环境中,很容易使用GUI来查看SystemC上单独模块的端口,这很幸运。但是,如果将此模块插入测试系统,则会显示它们。但是在进行理论探讨时,我发现了如何为端口名设置精美的别名。现在,最终的类构造函数如下所示:
SC_HAS_PROCESS(ulpi_driver);
ulpi_driver(sc_module_name name): sc_module(name),
m_tx_fifo(1024),
m_rx_fifo(1024),
oscillator ("clk66",sc_time(15,SC_NS)),
rst_i ("rst"),
ulpi_data ("data"),
ulpi_dir_o ("dir"),
ulpi_nxt_o ("nxt"),
ulpi_stp_i ("stp")
{
SC_CTHREAD(drive,clk.pos());
SC_THREAD(clkThread);
m_reg[ULPI_REG_VIDL] = 0x24;
m_reg[ULPI_REG_VIDH] = 0x04;
m_reg[ULPI_REG_PIDL] = 0x04;
m_reg[ULPI_REG_PIDH] = 0x00;
m_reg[ULPI_REG_FUNC] = 0x41;
m_reg[ULPI_REG_OTG] = 0x06;
m_reg[ULPI_REG_SCRATCH] = 0x00;
}
制作测试系统
好吧。我没有自动完成所有操作,因此两个调试模块(分析仪头和ULPI总线模型)本身跳入了测试文件。但是,让我们至少进行一个头部测试,然后向其中添加ULPI。使用上一篇文章中的技术,我为ULPIhead.sv文件制作了一个测试系统。我有一个名为sim1.v并立即更名为sim1.sv。
然后,我添加了带有手柄的ulpi_driver模块。最终脚本myrun.do如下所示:
vlog +../../SystemCPlay {../../MyCores/ULPIhead.sv}
sccom -g ../../SystemCPlay/ulpi_driver.cpp
sccom -link
vlog +../../SystemCPlay {../../SystemCPlay/sim1.sv}
vsim -voptargs="+acc" sim1
最后一行受了折磨。没有它,Verilog代码将没有端口。通过更改优化参数,我们消除了这个问题。我在那个.do文件中看到了该文件,该文件最初是为了模拟我们的系统而创建的,当时一切仍在计算机上完成。没错,这是一条长长的线。我刚刚找到了解决问题的密钥并进行了复制。所以-我不喜欢排长队,我扔掉了所有不必要的东西。
现在,我们将ULPI模块添加到测试系统并进行虚拟测试。只是要确保所有时钟信号都在滴答作响,并且将总线设置为正确的值。
我得到了这个测试。
观看文字。
`timescale 1ns / 1ns
module sim1 ;
reg ulpi_dir ;
wire source_valid ;
wire ulpi_stp ;
reg ulpi_clk ;
reg ulpi_nxt ;
reg reset_n ;
reg read ;
reg [31:0] writedata ;
wire ulpi_rst ;
reg clk ;
wire [7:0] source_data ;
reg write ;
wire [7:0] ulpi_data ;
reg source_ready ;
reg [1:0] address ;
wire [31:0] readdata ;
always
begin
clk = 1;
#5;
clk = 0;
#5;
end
ULPIhead DUT
(
.ulpi_dir (ulpi_dir ) ,
.source_valid (source_valid ) ,
.ulpi_stp (ulpi_stp ) ,
.ulpi_clk (ulpi_clk ) ,
.ulpi_nxt (ulpi_nxt ) ,
.reset_n (reset_n ) ,
.read (read ) ,
.writedata (writedata ) ,
.ulpi_rst (ulpi_rst ) ,
.clk (clk ) ,
.source_data (source_data ) ,
.write (write ) ,
.ulpi_data (ulpi_data ) ,
.source_ready (source_ready ) ,
.address (address ) ,
.readdata (readdata ) );
ulpi_driver ULPI
(
.clk (ulpi_clk),
.rst (ulpi_rst),
.data (ulpi_data),
.dir (ulpi_dir),
.nxt (ulpi_nxt),
.stp (ulpi_stp)
);
initial
begin
reset_n = 1'b0;
source_ready = 1;
writedata = 0;
address = 0;
read = 0;
write = 0;
#20
reset_n = 1'b1;
end
endmodule
结论
至少,我们已经使用ModelSim系统掌握了SystemC语言的建模。但是,事实证明,这需要访问许可的32位版本。免费版本和许可的64位版本不提供这样的机会。据我了解,一切都可以在Icarus Verilog系统中完全免费完成,但是我没有确切地知道如何实现。对于我来说,访问所需的ModelSim更加容易。在下一篇文章中,我们将使用这些知识来建模我们的头部。
在工作过程中,对模型进行了相当复杂的修改。生成的文件可以在这里下载。