当前位置: 首页 > news >正文

[SystemC]SystemC中的模块和程序

SystemC中的模块和程序

       摘要:本节包含一个完整的简单设计,用于演示 SystemC 中模块和流程的使用。 为简单起见,它是非常低级的 - 不是您通常在系统级设计语言中期望的编码风格!主要有以下几个部分:

  • Creating hierarchy
  • The sc_signal primitive channel
  • (Specialized) ports
  • Processes (SC_METHOD, SC_THREAD, SC_CTHREAD)
  • A simple test bench

一、SystemC Background

       为什么要看模块和流程? 原因是 SystemC 旨在同时处理硬件和软件,并允许对大型系统进行建模。

       进程是与其他进程同时运行的一小段代码。 几乎所有已开发的高级系统级设计 (SLD) 工具都使用流程网络的底层模型。 SystemC 提供了支持构建独立(并发/并行)代码段网络的过程。

       SLD 需要处理大型设计。 为了解决这个问题,通常使用层次结构。 层次结构在 SystemC 中通过使用模块实现,该类可以使用端口链接到其他模块。 模块允许单独处理一个设计。 模块可能包含进程和其他模块的实例。

二、设计实例分析

       该设计包括一个用四个 NAND 门实现的 EXOR 门。 同样,重要的是要注意这不是典型的设计风格 - 但它很好而且易于理解。 设计如下所示:

                            

       第一步是对与非门进行建模。 与非门是组合电路; 它的输出纯粹是输入值的函数。 它没有记忆,也不需要时钟。 因此,模型可以使用最简单的 SystemC 进程,即 SC_METHOD。

       SC_METHOD 只是 C++ 函数。 因此,SystemC 类库必须使它们表现得像进程。 尤其是:

  • SystemC 类库包含一个模拟内核——一段模拟时间流逝的代码,并在输入发生变化时调用函数来计算其输出。
  • 该函数必须声明为 SC_METHOD 并对其输入敏感。

        这是与非门的代码,在一个文件中,nand.h

#include "systemc.h"
SC_MODULE(nand2)          // declare nand2 sc_module
{
  sc_in<bool> A, B;       // input signal ports
  sc_out<bool> F;         // output signal ports

  void do_nand2()         // a C++ function
  {
    F.write( !(A.read() && B.read()) );
  }

  SC_CTOR(nand2)          // constructor for nand2
  {
    SC_METHOD(do_nand2);  // register do_nand2 with kernel
    sensitive << A << B;  // sensitivity list
  }
};

       SystemC 中的层次结构是使用类 sc_module 创建的。 sc_module 可以直接使用,也可以使用宏 SC_MODULE “隐藏”。 上面的示例 SC_MODULE 创建了一个名为 nand2 的 sc_module 类对象。

       接下来是声明的输入和输出端口。 通常,使用类 sc_port 声明端口。 例如,将声明使用 sc_signal 的输入端口:

sc_port<sc_signal_in_if<bool>,1> A,B;

       但正如你所看到的,这是很多打字。 为方便起见,还可以创建和使用专用端口。 sc_in 是 sc_signal 类的专用端口的示例。

       端口可以是任何 C++ 或 SystemC 类型 - 该示例使用 bool,一种内置的 C++ 类型。

       接下来,声明完成这项工作的函数。 输入和输出(专用)端口包括方法 read() 和 write() 以允许读取和写入端口。 读取 A 和 B,计算 NAND 函数,并使用 write() 方法将结果写入 F。

       请注意,您通常可以在不使用 read() 和 write() 方法的情况下摆脱困境,因为 = 运算符和类型转换运算符已被重载。 所以你可以写:

F = !(A && B);

       但是使用 read() 和 write() 是一个好习惯,因为它可以帮助 C++ 编译器消除表达式的歧义。

       函数do_nand2()写完后,就有了sc_module实例nand2的构造函数。 SystemC 提供了一种简写方式,使用宏 SC_CTOR。 构造函数执行以下操作:

  • 创建层次结构(在这种情况下没有)
  • 将函数注册为仿真内核的进程
  • 声明进程的敏感度列表
     

       也可以在这里初始化任何需要初始化的东西——例如,可以初始化一个类数据成员。

       在上面的示例中,构造函数声明 do_nand2 是一个 SC_METHOD,并表示端口 A 和 B 上的任何事件都必须使内核运行该函数(从而为 F 计算一个新值)。

三、Hierarchy

       EXOR 门由 NAND 门的四个副本(或实例)构成。 这是通过使用 EXOR 门构造函数连接 NAND 门实例来实现的。 这是 EXOR 门的代码:

#include "systemc.h"
#include "nand2.h"
SC_MODULE(exor2)
{
  sc_in<bool> A, B;
  sc_out<bool> F;

  nand2 n1, n2, n3, n4;

  sc_signal<bool> S1, S2, S3;

  SC_CTOR(exor2) : n1("N1"), n2("N2"), n3("N3"), n4("N4")
  {
    n1.A(A);
    n1.B(B);
    n1.F(S1);

    n2.A(A);
    n2.B(S1);
    n2.F(S2);

    n3.A(S1);
    n3.B(B);
    n3.F(S3);

    n4.A(S2);
    n4.B(S3);
    n4.F(F);
  }
};

       开头看起来与 NAND 门非常相似,但请注意它包含文件 nand2.h。这允许访问包含与非门的模块。

       创建了模块 exor2,并声明了端口。请注意,允许重复使用名称 A、B 和 F,因为这是层次结构的不同级别。

       原始图表显示了一些连接 NAND 门的“线”。这些是通过声明 sc_signals S1、S2 和 S3 创建的。 sc_signal 是一个带有模板参数的类,该参数指定信号可以保存的数据类型 - 在本例中为 bool。 sc_signal 是原始通道的示例,它是 SystemC 类库中的内置通道。它的行为类似于 VHDL 中的信号。

       EXOR 门的构造函数比 NAND 门的构造函数更复杂,因为它必须有四个 nand2 实例。在端口声明之后,声明了 nand2 的四个实例:n1、n2、n3 和 n4。必须给每个实例一个标签。四个标签“N1”、“N2”、“N3”和“N4”通过使用 exor2 构造函数上的初始化列表传递给 nand2 实例的构造函数。

       最后,将端口连接起来。如图所示,这是在构造函数中完成的。

四、验证环境

       为了测试设计,有一个刺激发生器。 这是另一个模块,与上面的非常相似。 唯一重要的一点是它使用了一个线程 (SC_THREAD),这是一种可以暂停的进程。 这是 stim.h 的代码:

#include "systemc.h"
SC_MODULE(stim)
{
  sc_out<bool> A, B;
  sc_in<bool> Clk;

  void StimGen()
  {
    A.write(false);
    B.write(false);
    wait();
    A.write(false);
    B.write(true);
    wait();
    A.write(true);
    B.write(false);
    wait();
    A.write(true);
    B.write(true);
    wait();
    sc_stop();
  }
  SC_CTOR(stim)
  {
    SC_THREAD(StimGen);
    sensitive << Clk.pos();
  }
};
  • 请注意对 sc_stop() 的最终调用,它使模拟停止。 监视器代码看起来非常相似,因此被省略 - 它位于文件 mon.h 中。

       这是顶层 - 它位于包含上述所有子模块的文件 main.cpp 中:

#include "systemc.h"
#include "stim.h"
#include "exor2.h"
#include "mon.h"

int sc_main(int argc, char* argv[])
{
  sc_signal<bool> ASig, BSig, FSig;
  sc_clock TestClk("TestClock", 10, SC_NS,0.5);

  stim Stim1("Stimulus");
  Stim1.A(ASig);
  Stim1.B(BSig);
  Stim1.Clk(TestClk);

  exor2 DUT("exor2");
  DUT.A(ASig);
  DUT.B(BSig);
  DUT.F(FSig);

  mon Monitor1("Monitor");
  Monitor1.A(ASig);
  Monitor1.B(BSig);
  Monitor1.F(FSig);
  Monitor1.Clk(TestClk);

  sc_start();  // run forever

  return 0;

};

       包括模块的头文件,声明用于布线的顶层信号,以及使用 sc_clock 创建的时钟; 然后每个模块都被实例化和连接。之后,调用 sc_start() 会启动模拟并永远运行(或者更确切地说,直到在刺激模块中遇到对 sc_stop() 的调用)。

  • 这是此示例的输出
      Time A B F
       0 s 0 0 1
     10 ns 0 0 0
     20 ns 0 1 1
     30 ns 1 0 1
     40 ns 1 1 0
  • 如果你看它,你会注意到一些非常奇怪的东西——时间 0 的第一行说 F 是 1(真),而 A 和 B 是 0——这不是一个非常令人信服的异或门!到 10 ns,一切正常。时间 0 发生了什么?

五、仿真

       SystemC 库包含一个仿真内核。这决定了运行哪些进程(软件线程)。在时间 0,所有 SC_METHOD 和 SC_THREAD 将以未定义的顺序运行,直到它们挂起。然后 SC_CTHREADs 将在时钟沿出现时运行。上述问题是由于多种情况造成的:

  1. sc_clock 语句在时间 0 产生一个上升沿,因此监视器和激励进程都将运行(以未定义的顺序,不知道哪个将首先运行)。
  2. C++ 中的变量并不总是具有定义的初始值(除非它们被声明为静态)。所以 F 持有的数据值恰好从 1 开始(真)。
  3. do_nand2 SC_METHOD在时间0运行,调度F更新,但是F是一个信号,不能瞬间更新,所以monitor进程运行时值1仍然存在。
     

       为了证明是这样,可以修改sc_clock语句来延迟时钟的第一个边沿,如下:

sc_clock TestClk("TestClock", 10, SC_NS,0.5, 1, SC_NS);

       最后的 1,SC_NS 参数指定第一个时钟边沿出现之前的 1 ns 延迟。 现在时间已经过去,所以 F 将被更新。 这是对应的输出:

     Time A B F
      1 ns 0 0 0
     11 ns 0 0 0
     21 ns 0 1 1
     31 ns 1 0 1
     41 ns 1 1 0
  • 现在你可以看到 F 总是正确的。

六、结论

       对模块和流程的快速浏览到此结束。 您已经看到了解 SystemC 仿真内核的并发特性以及 sc_signal 原始通道的行为的重要性。

       您还看到了在顶级模块中实例化较低级别模块的一些基本示例,以及如何使用 sc_main。

相关文章:

  • SQL常见函数及题目整理
  • java毕业生就业登记分析管理系统springboot
  • 如果你是Java程序员,你会选择Cloud Studio进行云端开发,放弃IDEA吗?
  • STC15单片机-ADC获取环境温度(NTC热敏电阻)
  • 脚本语言和编译语言的区别 什么是解释器? 什么是编译器 ?解释器和编译器有什么区别?
  • volatile的作用是什么
  • Linux的shell脚本爬虫实战之图片爬取
  • 浏览器缓存顺序
  • LeetCode高频题:子串权值定义为,最长有效括号子序列的长度,请你返回字符串s的所有子串权值的和是多少
  • 使用Python,dlib进行对象实时追踪
  • Pytorch量化感知训练
  • 设计模式——迭代器模式
  • STM32F407的时钟
  • Opencv形态学——腐蚀、膨胀、开运算与闭运算、梯度运算、礼帽、黑帽
  • [Django开源学习 1]django-vue-admin
  • 4. 路由到控制器 - Laravel从零开始教程
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • Java超时控制的实现
  • Java到底能干嘛?
  • maya建模与骨骼动画快速实现人工鱼
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • SpiderData 2019年2月13日 DApp数据排行榜
  • 我看到的前端
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 硬币翻转问题,区间操作
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • #LLM入门|Prompt#1.8_聊天机器人_Chatbot
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • $.ajax,axios,fetch三种ajax请求的区别
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (排序详解之 堆排序)
  • (转)memcache、redis缓存
  • (状压dp)uva 10817 Headmaster's Headache
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .net 4.0 A potentially dangerous Request.Form value was detected from the client 的解决方案
  • .net Application的目录
  • .NET CORE Aws S3 使用
  • .net6解除文件上传限制。Multipart body length limit 16384 exceeded
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • @EnableConfigurationProperties注解使用
  • @RequestBody的使用
  • [20180129]bash显示path环境变量.txt
  • [Android开源]EasySharedPreferences:优雅的进行SharedPreferences数据存储操作
  • [ANT] 项目中应用ANT
  • [Assignment] C++1
  • [BZOJ2208][Jsoi2010]连通数
  • [CVPR 2023:3D Gaussian Splatting:实时的神经场渲染]
  • [GN] DP学习笔记板子
  • [HDU3710]Battle over Cities
  • [javaSE] 看知乎学习工厂模式
  • [Jquery] 实现鼠标移到某个对象,在旁边显示层。
  • [luogu P1527]矩阵乘法(矩形k小)
  • [Oh My C++ Diary]一元作用域运算符::的使用
  • [Python]Selenium-自动化测试