使用SystemC语言的模型在ModelSim环境中进行固件仿真

上一篇文章中,我们熟悉了在ModelSim环境中对“固件”进行建模的过程,在该过程中,目标代码和测试操作的生成器均以Verilog语言编写。遗憾的是,但这还不足以解决周期中解决的目标。我一再提倡Redd复杂区的开发应该花费最少的精力的想法。如果快速编写设备模型,则可以从头开始编写。上一次我们制作了一个总线模型,该模型用于将字节写入加法器。但是ULPI是一件非常复杂的事情。从头开始编写她的模型-哦,这很困难。如果您可以找到现成的,那就更好了。我发现……las,哦,原来是SystemC语言的。现在,我们将考虑如何开始使用这种语言。











实际上,这篇文章早在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更加容易。在下一篇文章中,我们将使用这些知识来建模我们的头部。



在工作过程中,对模型进行了相当复杂的修改。生成的文件可以在这里下载



All Articles