systemc实现RTL的并行设计

systemc调度策略可以使得C++代码很好地模拟RTL的并行执行效果,轻松实现寄存器的功能。
假设有这样一个模块:两个10bit的输入信号,经过一个加法器,计算结果经过寄存器打一拍后输出。以下分别用verilog和systemc来实现,源代码见文章末尾。

Systemc实现:

sc_clock 和sc_in_clock为时钟,前者产生时钟,初始化时需要配置period,默认占空比为50%。
信号绑定时,sc_in/sc_out 必须和sc_signal进行绑定,不能不绑定,也不能sc_in和sc_out进行绑定。如果是嵌套模块,子模块的sc_in/sc_out可以传递给top层的sc_in/sc_out的。
所以,如果想将A模块的sc_in和B模块的sc_out进行绑定,必须再定义一个sc_signal,分别将A的sc_in和B的sc_out绑定到这个sc_signal上。形式为sc_in (sc_signal ) 和sc_out (sc_signal ),不能颠倒为sc_signal (sc_in /sc_out )。
如果make没有报错,执行时发现报以下错误,说明port没绑定。
complete binding failed: port not bound: port In file: …/…/…/…/src/sysc/communication/sc_port.cpp:231
sc_in/sc_out/ sc_signal分别通过read()和write()函数来读信号值和给信号赋值。
产生波形文件使用sc_create_vcd_trace_file,添加dump波形信号使用sc_trace。仿真结束后使用gtkwave打开vcd波形文件。
产生类似与RTL的阻塞赋值方式,在构造函数中固定使用如下方式。
SC_METHOD(AddMethod); sensitive<<m_clk.pos(); dont_initialize();
SC_METHOD的敏感列表都配置成clock的上升沿 (下降沿或双边沿也可以自行配置),一般加上dont_initialize()使其初始化时不执行。
需要特别注意的是,所有sc_in/sc_out/ sc_signal信号,在write新值的当拍,是read不到这个新值的。Write的新值在下一拍才会被所有的method看到(包括write的这个自身method)。我们可以从打印信息中看出,TestTask::PrintResultMethod()的打印结果总是比Adder::AddMethod()的结果晚一拍。
在这里插入图片描述
但是,在sc_trace的波形文件中,信号被write新值的那一时刻就能显示在波形上。所以,我们会发现t_in_0、t_in_1和t_result是在同一拍变化的,但是m_test_result_print就比t_in_0、t_in_1晚了一拍。
在这里插入图片描述

Verilog实现

写Verilog的测试激励时,需要注意,不能再clock的上升沿的当时刻,更新input0和input1的值,否则会发现result在当拍就更新了,没有延时一拍的效果,此处具体原因等待后续慢慢研究。
verilog 仿真可以使用轻量版工具icarus verilog + gtkwave,使用方法可以参考我之前的博客:
https://blog.csdn.net/zgcjaxj/article/details/104853081
在这里插入图片描述

systemc实现

/*
Original 2020-03-28
README:
	This is a example to teach you how to implement a adder with systemc
	and how to bind signal, dump waveform, 

execute:	
	g++ -g -Wall -lsystemc -m64 -pthread main.cpp  -L/$(your systemc path)/lib-linux64 -I/$(your systemc path)/include  -I/$(your systemc path)/src/tlm_utils -o sim

you need particular attention that  
	1. sc_in/sc_out/ sc_signal write() new value at 0T, can be read() at 1T.
	2. for other points, can check my csdn blog
*/

#include <iostream>
#include "systemc.h"

#define ADD_WIDTH 10
#define RESULT_WIDTH 11

using namespace std;

class Adder: public sc_module
{
	public:
		SC_HAS_PROCESS(Adder);
		Adder(const sc_module_name&    name)
        : sc_module(name)
		{
			SC_METHOD(AddMethod);
    		sensitive<<m_clk.pos();//sensitive event list
			dont_initialize();		
		};
	public:
		void AddMethod();
		
		~Adder() {;}		
	public:
		sc_in_clk 						m_clk;
		sc_in<sc_uint<ADD_WIDTH> >   	m_input_0;
		sc_in<sc_uint<ADD_WIDTH> >   	m_input_1;
		sc_out<sc_uint<RESULT_WIDTH> >  m_output_result;
};
void Adder::AddMethod()
{
	sc_uint<RESULT_WIDTH> t_result = m_input_0.read() + m_input_1.read();
	m_output_result.write(t_result);

	cout<<"["<<sc_time_stamp()
		<<"] input is "<<dec<< m_input_0.read()
		<<"  and "<<m_input_1.read()
		<<" , result = "<<t_result
		<<endl;
}


class TestTask: public sc_module
{
	public:
		SC_HAS_PROCESS(TestTask);
		TestTask(const sc_module_name&    name)
        : sc_module(name)
		{
			SC_THREAD(GeneTestThread);

			SC_METHOD(PrintResultMethod);
    		sensitive<<m_clk.pos();
			dont_initialize();	
		};
	public:
		void GeneTestThread();
		void PrintResultMethod();
		~TestTask() {;}		
	public:
		sc_in_clk 						m_clk;
		sc_out<sc_uint<ADD_WIDTH> >   	m_input_0;
		sc_out<sc_uint<ADD_WIDTH> >   	m_input_1;
		sc_in <sc_uint<RESULT_WIDTH> >  m_output_result;
		sc_signal<sc_uint<RESULT_WIDTH> > m_test_result_print;
};
void TestTask::GeneTestThread()
{
	//start test here
	wait(1,SC_NS);
	m_input_0.write(1);
	m_input_1.write(2);

	wait(1,SC_NS);
	m_input_0.write(3);
	m_input_1.write(4);

	wait(1,SC_NS);
	m_input_0.write(5);
	m_input_1.write(6);
}
void TestTask::PrintResultMethod()
{
	sc_uint<RESULT_WIDTH> t_result = m_output_result.read() ;
	m_test_result_print.write(t_result);
	cout<<"["<<sc_time_stamp()
		<<"] output result is "<<t_result
		<<endl;
}

int sc_main(int argc, char** argv)
{
    Adder *    m_adder = new Adder("Adder");
	TestTask * m_test  = new TestTask("TestTask");

	//initial clock, set period,
	sc_clock t_clock("Clock",sc_time(1,SC_NS));

	sc_signal<sc_uint<ADD_WIDTH> >   	t_test_in_0;
	sc_signal<sc_uint<ADD_WIDTH> >   	t_test_in_1;
	sc_signal<sc_uint<RESULT_WIDTH> >  	t_output;

	//bind signal
	// sc_in/sc_out must bind to sc_signal
	m_adder->m_clk(t_clock);
	m_adder->m_input_0(t_test_in_0);
	m_adder->m_input_1(t_test_in_1);
	m_adder->m_output_result(t_output);

	m_test->m_clk(t_clock);
	m_test->m_input_0(t_test_in_0);
	m_test->m_input_1(t_test_in_1);
	m_test->m_output_result(t_output);	

	//add vcd waveform
	sc_trace_file *g_trace_file = sc_create_vcd_trace_file("my_trace");
	sc_trace(g_trace_file,t_clock,"Clock");
	sc_trace(g_trace_file,t_test_in_0,"t_in_0");
	sc_trace(g_trace_file,t_test_in_1,"t_in_1");
	sc_trace(g_trace_file,t_output,"t_result");
	sc_trace(g_trace_file,m_test->m_test_result_print,"m_test_result_print");
	
    sc_start(8,SC_NS);
	return 0;
}

verilog实现

`timescale 100ps/10ps
module test_adder;
reg   clk;
reg   [9:0]   t_in_0;     //在激励中设置信号值,必须定义为reg类型
reg   [9:0]   t_in_1;  
wire  [10:0]  t_result;   //必须定义为wire类型

Adder  test0 (clk,t_in_0, t_in_1, t_result);

//Clock generation
always #5 clk = ~clk;      // always #(cycle /2)   

initial    
   begin
        clk     = 1'b1;    		//clk begin is high
        t_in_0  = 10'd0; 
        t_in_1  = 10'd0;
	#10;				 //  delay  1 ns 
	  @(posedge clk);#1; //此处的 #1 (也就是延时100ps) 必须要加上
                         //否则会导致t_result当拍就有结果,没有延时1T的效果 
        t_in_0  = 10'd1; 
        t_in_1  = 10'd2; 	
	#8;						
	  @(posedge clk);#1;
        t_in_0  = 10'd3;    
        t_in_1  = 10'd4; 	
	#8;						  
	  @(posedge clk);#1;
        t_in_0  = 10'd5; 
        t_in_1  = 10'd6; 		
	# 30 ;		
	$finish();    //finish simulation
   end

//dump waveform 
initial
   begin
        $dumpfile("test_adder.vcd");  //这两行主要是给gtkwave这个工具使用的...
        $dumpvars(0,test_adder);    
   end
endmodule

module Adder(clk,t_in_0, t_in_1, t_result);
input           clk;
input   [9:0]   t_in_0;  // input和output信号  缺省为wire类型
input   [9:0]   t_in_1;
output  [10:0]  t_result;

reg     [10:0]  t_test;
reg     [10:0]  t_result; //  阻塞赋值,t_result设置为register类型
always @( posedge clk )
begin
    t_result <= t_in_0 + t_in_1; //过程块内赋值的的每个信号必须为reg类型
    t_test   <= t_result;        //阻塞赋值,达到的效果就是寄存器延时1T
end
endmodule

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

123axj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值