SystemC 学习之与 Verilog 的混合仿真(十)

news2024/11/29 2:48:40

1、SC 与 Verilog 的通信方式

Systemc 和 verilog 通信方式有两种,一种是 PLI,但是 PLI 只能 verilog 调用 c/c++,不能从 c/c++ 直接调用 verilog,想要从 c/c++ 调用 verilog 的话,需要先用 verilog 调用 c/c++ 函数,然后在 c++ 里面给对应的参数设置好值,然后 verilog 里面再拿这些数据,比较麻烦。还有一种是使用 DPI-C,DPI-C 是 system verilog 里面的,这样的话需要在编译的时候加上 -sverilog 编译选项

这里我们选用 DPI-C 将接口导入和导出,由于不会 verilog,所以对于 verilog 代码写的比较简单,如果有错误欢迎指正

2、一个简单例子

下面给出一个简单例子来说明 systemc 和 verilog 之间的数据传输

Makefile

SYSCAN = syscan -cpp g++ -cc gcc -tlm2                                          \
         -cflags -g                                                             \
         -cflags -DVCS                                                          \
         -cflags -std=c++11                                                     \
         -cflags -I${VCS_HOME}/etc/systemc/tlm/include/tlm/tlm_utils            \
         -cflags -I${UVMC_HOME}/src/connect/sc                                  \
         -cflags -I${UVMC_HOME}/src                                             \
         -cflags -Icpp                                                          \
         ${UVMC_HOME}/src/connect/sc/uvmc.cpp                                   \
         ${UVMC_HOME}/src/connect/sc/uvmc_export_stubs.cpp

VLOGAN = vlogan -q -sverilog                                                          \
         +incdir+${UVM_HOME}/src ${UVM_HOME}/src/uvm_pkg.sv                           \
         +incdir+${UVMC_HOME}/src/connect/sv ${UVMC_HOME}/src/connect/sv/uvmc_pkg.sv  \
         -timescale=1ns/1ps

VCS_ELAB = vcs -q -sysc=deltasync -lca                                          \
           -sysc -cpp g++ -cc gcc                                               \
           -timescale=1ns/1ps                                                   \
           -CFLAGS -DVCS ${UVM_HOME}/src/dpi/uvm_dpi.cc

CURRENT_DIR = $(shell pwd)
CPP_DIR = $(shell find $(CURRENT_DIR)/cpp -maxdepth 20 -type d)
SRCS_CPP += $(foreach dir, $(CPP_DIR), $(wildcard $(dir)/*.cpp))
SRCS_CC += $(foreach dir, $(CPP_DIR), $(wildcard $(dir)/*.cc))
SRCS_C += $(foreach dir, $(CPP_DIR), $(wildcard $(dir)/*.c))
VERILOG_DIR = $(shell find $(CURRENT_DIR)/verilog -maxdepth 20 -type d)
SRCS_VERILOG += $(foreach dir, $(VERILOG_DIR), $(wildcard $(dir)/*.v))

comp:
    $(VLOGAN) -full64 $(SRCS_VERILOG) +define+UVM_OBJECT_MUST_HAVE_CONSTRUCTOR
    $(SYSCAN) -full64 $(SRCS_CPP) $(SRCS_CC) $(SRCS_C)
    $(VCS_ELAB) -full64 verilog_main sc_main  # 这里要写 verilog、sv、sc 对外的模块名字
    
clean:
    rm -rf simv* work csrc ucli.key vc_hdrs.h vcs.log AN* *.log *.log.cmp *.vpd DVE* .vlogan*
    
run:
    ./simv

Systemc 模块

对外的头文件,extern 表示要调用的 verilog 接口

// sc2v.h
#ifndef SC2V_H
#define SC2V_H

extern "C" {
    // export
    void VerilogSendToSCModule(char* data, int len);

    void SaveScope();

    // import
    extern void SCSendToVerilog(char* data, int len);
}

#endif // SC2V_H


// sc2v.cpp
#include "sc2v.h"
#include <iostream>
#include "instance_manager.h"

void VerilogSendToSCModule(char* data, int len) {
    std::shared_ptr<DataManager> data_manager = InstanceManager::CreateInstance()->GetDataManager("receive_module");
    data_manager->ReceiveData(data, len);
}

void SaveScope() {
    std::cout << "SaveScope" << std::endl;
    InstanceManager::CreateInstance()->my_scope = svGetScope();
    InstanceManager::CreateInstance()->init_ = true;
}

因为 SCSendToVerilog 代码是在 verilog 里面的,单纯编译 c++ 代码会报错,所以这里要在 c++ 里面声明一个弱符号

// sc2v_stubs.cpp
#include <cstdio>
#include "sc2v.h"

void SCSendToVerilog(char* data, int len) __attribute__((weak));
// 不然 c++ 会报错
void SCSendToVerilog(char* data, int len) {
    printf("fake func\n");
}

c++ 收到 verilog 发过来的数据后,先将数据存放在一个队列里面,然后 systemc 有一个进程以一定时钟周期访问这个队列获取数据

// data_manager.h
#pragma once
#include <string>
#include <queue>

class DataManager {
public:
    DataManager();
    ~DataManager();

    void ReceiveData(const std::string& data, int len);
    bool GetData(std::string& data);

private:
    std::queue<std::string> data_queue_{};
};

// data_manager.cpp
DataManager::DataManager() = default;

DataManager::~DataManager() = default;

void DataManager::ReceiveData(const std::string& data, int len) {
    data_queue_.push(data);
}

bool DataManager::GetData(std::string& data) {
    if (data_queue_.empty()) {
        return false;
    }
    data = data_queue_.front();
    data_queue_.pop();
}
// instance_manager.h
#pragma once
#include <unordered_map>
#include <memory>
#include <string>
#include <svdpi.h>
#include "data_manager.h"

class InstanceManager {
public:
    static InstanceManager* CreateInstance();

    void Init();
    
    std::shared_ptr<DataManager> GetDataManager(const std::string& module_name);

public:
    svScope my_scope;
    bool init_ = false;

private:
    InstanceManager();
    ~InstanceManager();

    InstanceManager(const InstanceManager&) = delete;
    InstanceManager operator=(const InstanceManager& ) = delete;

private:
    std::unordered_map<std::string, std::shared_ptr<DataManager>> data_manager_map_{};
};

// instance_manager.cpp
#include "instance_manager.h"

const std::string receive_name = "receive_module";

InstanceManager* InstanceManager::CreateInstance() {
    static InstanceManager* instance = new InstanceManager();
    return instance;
}
    
std::shared_ptr<DataManager> InstanceManager::GetDataManager(const std::string& module_name) {
    if(data_manager_map_.find(module_name) == data_manager_map_.end()) {
        return nullptr;
    }
    return data_manager_map_[module_name];
}

void InstanceManager::Init() {
    data_manager_map_[receive_name].reset(new DataManager());
}

InstanceManager::InstanceManager()  = default;

InstanceManager::~InstanceManager() {
    data_manager_map_.clear();
}
// receiver.h
#pragma once
#include <systemc.h>

class Receiver : public sc_module {
public:
    SC_HAS_PROCESS(Receiver);
    Receiver(sc_module_name ins_name);
    ~Receiver();

    void ReceiverData();

public:
    sc_in_clk clk;
};

// receiver.cpp
#include "receiver.h"
#include "instance_manager.h"

Receiver::Receiver(sc_module_name ins_name) : sc_module(ins_name) {
    SC_METHOD(ReceiverData);
    sensitive << clk.pos();
    dont_initialize();
}

Receiver::~Receiver() = default;

void Receiver::ReceiverData() {
    std::shared_ptr<DataManager> data_manager = InstanceManager::CreateInstance()->GetDataManager("receive_module");
    std::string data;
    if (data_manager->GetData(data)) {
        std::cout << sc_time_stamp() << " " << data << std::endl;
    }
}
// sender.h
#pragma once
#include <systemc.h>
#include <string>

class Sender : public sc_module {
public:
    SC_HAS_PROCESS(Sender);
    Sender(sc_module_name instname);
    ~Sender();
    void SendData();

public:
    sc_in_clk clk;

private:
    int val_{};
};

// sender.cpp
#include "sender.h"

#include <string>

#include "instance_manager.h"
#include "sc2v.h"

Sender::Sender(sc_module_name instname) : sc_module(instname) {
    SC_METHOD(SendData);
    sensitive << clk.pos();
    dont_initialize();
}

Sender::~Sender() = default;

void Sender::SendData() {
    if(!InstanceManager::CreateInstance()->init_) {
        return;
    }
    std::string data = "systemc " + std::to_string(val_++);
    svSetScope(InstanceManager::CreateInstance()->my_scope);
    SCSendToVerilog((char*)data.c_str(), data.length());
}
// main.cpp
#include <systemc.h>
#include "receiver.h"
#include "sender.h"
#include "instance_manager.h"

int sc_main(int argc, char* argv[]) {
    InstanceManager::CreateInstance()->Init();
    Receiver receiver("receiver");
    Sender sender("sender");
    sc_clock clk("clk", 20, SC_NS);
    receiver.clk(clk);
    sender.clk(clk);
    sc_start(200, SC_NS);
    return 0;
}

Verilog 模块

import:表示 verilog 调用 c++ 的接口

export:导出接口,表示提供给 c++ 可以调用的接口

module verilog_main;
    import "DPI-C" context function VerilogSendToSCModule(string data, int len);
    import "DPI-C" context function void SaveScope(); 
    export "DPI-C" function SCSendToVerilog;
    function void SCSendToVerilog(string data, int len);
        $display("Verilog::data:%s", data);
        VerilogSendToSCModule(data, len);
        endfunction
    initial begin
        SaveScope();
    end
endmodule

这里调用 SaveScope 是因为只有在 verilog 初始化之后才能拿到当前的 scope,每次 c++ 传输数据给 verilog 时需要先设置 scope,然后才能发送数据

编译运行

make comp
./simv

运行结果如下所示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1216004.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

企业邮箱认证指南:安全与高效的邮箱认证方法

企业邮箱是专门为企业提供的电子邮件服务&#xff0c;安全性和专业性更高。在开始使用企业邮箱之前&#xff0c;很多人会有一些问题&#xff0c;比如企业邮箱需要认证吗、如何开通企业邮箱&#xff0c;以及哪款企业邮箱好。 1、企业邮箱在使用前需要认证吗&#xff1f; 答案是肯…

div中的两个元素怎么实现上下排列

案例: 这里面的分享活动页和获取抽奖机会两个文字上下排列怎么实现? 答案: 父元素加上两个属性: display: flex; flex-direction: column; 就实现了

分布式下多节点WebSocket消息收发

1、使用场景 2、疑问 第一次发送请求后&#xff0c;通过N1&#xff0c;W2&#xff0c;到达service2&#xff0c;建立websocket连接。 1、接下来发送的消息&#xff0c;通过Ngixn后和网关gateway后还能落在service2上面吗&#xff1f; 如果不能落在service2上&#xff0c;需要怎…

【发表案例】IF6.5+,中科院2区,2个月录用,6天见刊!

计算机类SCIE 【期刊简介】IF&#xff1a;6.5-7.0&#xff0c;JCR1区&#xff0c;中科院2区 【检索情况】SCIE 在检&#xff0c;正刊 【征稿领域】数据表示、存储和处理、交换和访问、数据丢失处理等机器学习相关研究&#xff1b;云到物的连续体&#xff1b;数据结构架构&am…

ClickHouse联合创始人、前Google副总裁Yury到访杭州玖章算术公司,双方建立生态合作

10月31日&#xff0c;ClickHouse联合创始人Yury到访未来科技城&#xff0c;与玖章算术创始人叶正盛和国际总经理Ni Demai展开沟通与推进合作。 图片备注&#xff1a;Ni Demai(左),Yury(中),叶正盛(右) ClickHouse是深受开发者青睐的实时分析型数据库&#xff0c;成立2年就发展成…

微信加好友操作频繁了,怎么办?

近来&#xff0c;微信的风控是越来越严重&#xff0c;因为本身微信是作为一个社交软件&#xff0c;但流量大适合用来做私域营销。在日常使用微信中&#xff0c;我们也要了解下微信加好友的规则。 目前微信加人的规则是&#xff1a; 1、通过附近人功能加人上限15人/天&#xf…

制作企业期刊的必备工具,这个很强!

制作企业期刊是一项重要的任务&#xff0c;它不仅可以帮助企业展示自己的品牌形象&#xff0c;还可以促进内部员工的交流和合作。为了制作出一份高质量的企业期刊&#xff0c;你需要一些必备的工具。今天&#xff0c;给大家分享一款实用的工具-------FLBOOK在线制作电子杂志平台…

MS90C385B——+3.3V 150MHz 的 24bit 平板显示器(FPD) LVDS 信号发送器

MS90C385B 芯片能够将 28bit 的 TTL 数据转换成 4 通道的低压差分 信号 (LVDS) 。时钟通道经过锁相之后&#xff0c;与数据通道并行输出。当时钟频 率为 150MHz 时&#xff0c; 24bit 的 RGB 数据、 3bit 的 LCD 时序数据和 1bit 的控 制数据以 1050Mbps…

GLSL: Shader cannot be patched for instancing.

最近在 unity 里碰到了这么一个错误&#xff0c;只有这么点信息&#xff0c;让人看着挺懵逼的&#xff0c;后来发现&#xff0c;是因为 unity 的 terrain 组件在设置里勾了 Draw Instanced 选项导致的&#xff0c;感觉应该是 unity 的 bug。 因为错出在 2021&#xff0c;2022就…

11月编程榜最新出炉,第一名很离谱

这段时间&#xff0c;随着人工智能的崛起&#xff0c;Python的地位水涨船高。有不少朋友感觉到危机重重。 其中&#xff0c;最明显的&#xff0c;是市场环境的变化&#xff1a; 外部招聘&#xff1a;Python岗位日均需求量高达15000&#xff01;不仅是程序员&#xff0c;内容编…

Java 集合框架,泛型,包装类

文章目录 集合框架泛型Java 中的泛型裸类型&#xff08;了解&#xff09;原理泛型的上界泛型方法通配符 包装类ArrayList构造常见操作 LinkedListStackQueuePriorityQueueMapMap.Entry<K, V>Map 常用方法 Set常用方法 集合框架 Vector 一个古老的集合类&#xff0c;实现了…

【EI会议征稿】第四届机械设计与仿真国际学术会议(MDS 2024)

【高录用快检索】第四届机械设计与仿真国际学术会议&#xff08;MDS 2024) 2024 4th International Conference on Mechanical Design and Simulation 2024年第四届机械设计与仿真国际学术会议&#xff08;MDS 2024) 将于2024年03月01-03日在中国西安召开。MDS 2024将围绕“…

C# NAudio 音频库

C# NAudio 音频库 NAudio安装NAudio简述简单示例1 NAudio安装 项目>NuGet包管理器 搜索NAudio点击安装&#xff0c;自动安装依赖库。 安装成功后工具箱会新增NAudio.WinForms控件 NAudio简述 NAudio为.NET平台下的开源库&#xff0c;采用ML-PL协议&#xff0c;开源地址…

雷达模糊函数及MATLAB仿真

文章目录 前言一、雷达模糊函数二、Matlab 仿真1、单脉冲模糊函数①、MATLAB 源码②、仿真结果1&#xff09;不确定函数三维图2&#xff09;不确定函数的等高图3&#xff09;模糊函数的三维图4&#xff09;模糊函数的等高图 2、单脉冲多普勒频率轴上的切面①、MATLAB 源码②、仿…

srs webrtc推拉流环境搭建(公网)

本地环境搭建 官方代码https://github.com/ossrs/srs 拉取代码&#xff1a; git clone https://github.com/ossrs/srs.gitcd ./configure make ./objs/srs -c conf/https.rtc.confsrs在公网上&#xff0c;由于srs是lite-ice端&#xff0c;导致他不会主动到srs获取自己的公网i…

【python】OpenCV—Rectangle, Circle, Selective Search(1.2)

文章目录 1 画框画圈1.1 画矩形框1.2 画圆 / 点1.3 椭圆 2 Selective Search3 Resize 1 画框画圈 1.1 画矩形框 # Copy the image img_rgb_copy img_rgb.copy()# Draw a rectangle cv2.rectangle(img_rgb_copy, pt1 (405, 90), pt2 (740, 510),color (255, 0, 0), thickne…

德迅云安全和您聊聊关于DDOS高防ip的一些方面

德迅DDoS防护服务是以省骨干网的DDoS防护网络为基础&#xff0c;结合德迅自研的DDoS攻击检测和智能防护体系&#xff0c;向您提供可管理的DDoS防护服务&#xff0c;自动快速的缓解网络攻击对业务造成的延迟增加&#xff0c;访问受限&#xff0c;业务中断等影响&#xff0c;从而…

【Android 标题文字居中 快速实现】

背景&#xff1a; Android App系统默认setTitle左起展示(图左)&#xff0c;IOS App默认居中展示(图右)。现在美工设计 在Android中标题同样居中显示。 解决&#xff1a; 方案一&#xff1a;(传统方式,比较繁琐) 设置ToolBar样式&#xff0c;内嵌TextView来展示&#xff0c;具…

reids面试题

1 redis是单线程吗&#xff1f; Redis是单线程 主要是指Redis的网络10和键值对读写是由一个线程来完成的&#xff0c;Redis在处理客户端的请求时包括获取(socket 读)、解析、执行、内容返回(socket 写) 等都由一个顺序串行的主线程处理&#xff0c; 但Redis的其他功能&#xff…

2023.11.16 hivesql高阶函数之json

目录 1.数据准备 2.操作 -- 方式1: 逐个(字段)处理, get_json_object UDF函数 最大弊端是一次只能解析提取一个字段 -- 方式2: 逐条处理. json_tuple 这是一个UDTF函数 可以一次解析提取多个字段 -- 方式3: 在建表时候, 直接处理json, row format SerDe 能处理Json的SerDe类…