深亚微米半导体技术的进展与成熟使复杂的片上系统(SoC)设计变得越来越普遍,同时对传统的ASIC设计方法和流程提出了挑战。一些新的设计语言被开发出来以支持这些设计技术,例如SystemC[1]、SystemVerilog[6]等。Open SystemC Initive(OSCI)提出的基于C++的SystemC语言,已经逐渐被许多设计者用来对SoC体系结构进行建模以进行体系性能的分析及软硬件联合设计。SystemC的一个重要特性是它支持系统设计各个层次的模型抽象,包括算法层次的模型、无时间信息的功能模型、含时间信息的功能模型、业务级模型、行为级模型、寄存器传输级模型等[8]。这样在系统开发的各个阶段,不同的设计开发团队可以运用同一个设计语言,使用基于同一种设计语言描述的设计平台。
SystemC在1999年正式推出,并由Open SystemC Initiative (OSCI) 负责支持、维护和发展。这个组织包含了范围广泛的公司、研究机构、大学和个人成员。被选举出OSCI理事会成员(Board Member Coreporations)包括:ARM Ltd.,Cadence Design Systems, Inc.,CoWare,Fujitsu,Mentor Graphics,Motorola,NEC,Synopsys,Inc. ,涵盖了系统厂商、半导体厂商、EDA工具厂商、IP供应商等。
SystemC是建立C++基础上的开放的系统级设计语言。实际上SystemC由一系列用来进行系统描述的C++类构成,并包含了一个用来对系统行为进行模拟的仿真核。SystemC语言的发展大致经历了两个大的阶段:SystemC1.0和SystemC2.0。SystemC1.0可以用来进行硬件描述。SystemC2.0的推出使SystemC成为真正的系统级设计语言,能够用来对SoC体系结构进行更加自然和有效的描述。这样就使SystemC语言涵盖了从系统概念直到实现的针对系统软、硬件建模的能力。
SystemC的以下特性使它能够支持RTL或行为级的硬件描述。
● 数据类型 标准C/C++语言不提供可以用来进行硬件设计所需要的数据类型。SystemC语言支持所有的C/C++数据类型,同时SystemC所定义的以下数据类型可以用来进行定点或硬件设计和描述:sc_bit, sc_logic, sc_int, sc_uint, sc_bigint, sc_biguint, sc_lb, sc_lv, sc_fixed, sc_ufixed, sc_fix, sc_ufix 等。例如使用sc_int<8>可以表示一个8比特的有符号整数,或使用sc_uint<8>表示一个8比特的无符号整数。sc_(u)fix, sc_(u)fixed是定点数据类型,HDL语言不?支持类似的数据类型。丰富的数据类型使得SytemC可以支持从算法描述直到可综合的RTL描述。
● 设计描述 SC_MODULE是一个基本的用来进行设计描述的SystemC类。它类似与VHDL的ENTITY或VERILOG的MODULE。在SC_MODULE中可以定义输入、输出管脚,内部信号等,或实例化另外一个SystemC MODULE。设计的行为可以使用SC_METHOD或SC_THREAD进行描述。SC_METHOD或SC_THREAD在SC_MODULE的构造函数中进行声明,并可以指定相应的敏感信号列表。
SystemC在1999年正式推出,并由Open SystemC Initiative (OSCI) 负责支持、维护和发展。这个组织包含了范围广泛的公司、研究机构、大学和个人成员。被选举出OSCI理事会成员(Board Member Coreporations)包括:ARM Ltd.,Cadence Design Systems, Inc.,CoWare,Fujitsu,Mentor Graphics,Motorola,NEC,Synopsys,Inc. ,涵盖了系统厂商、半导体厂商、EDA工具厂商、IP供应商等。
SystemC是建立C++基础上的开放的系统级设计语言。实际上SystemC由一系列用来进行系统描述的C++类构成,并包含了一个用来对系统行为进行模拟的仿真核。SystemC语言的发展大致经历了两个大的阶段:SystemC1.0和SystemC2.0。SystemC1.0可以用来进行硬件描述。SystemC2.0的推出使SystemC成为真正的系统级设计语言,能够用来对SoC体系结构进行更加自然和有效的描述。这样就使SystemC语言涵盖了从系统概念直到实现的针对系统软、硬件建模的能力。
SystemC的以下特性使它能够支持RTL或行为级的硬件描述。
● 数据类型 标准C/C++语言不提供可以用来进行硬件设计所需要的数据类型。SystemC语言支持所有的C/C++数据类型,同时SystemC所定义的以下数据类型可以用来进行定点或硬件设计和描述:sc_bit, sc_logic, sc_int, sc_uint, sc_bigint, sc_biguint, sc_lb, sc_lv, sc_fixed, sc_ufixed, sc_fix, sc_ufix 等。例如使用sc_int<8>可以表示一个8比特的有符号整数,或使用sc_uint<8>表示一个8比特的无符号整数。sc_(u)fix, sc_(u)fixed是定点数据类型,HDL语言不?支持类似的数据类型。丰富的数据类型使得SytemC可以支持从算法描述直到可综合的RTL描述。
● 设计描述 SC_MODULE是一个基本的用来进行设计描述的SystemC类。它类似与VHDL的ENTITY或VERILOG的MODULE。在SC_MODULE中可以定义输入、输出管脚,内部信号等,或实例化另外一个SystemC MODULE。设计的行为可以使用SC_METHOD或SC_THREAD进行描述。SC_METHOD或SC_THREAD在SC_MODULE的构造函数中进行声明,并可以指定相应的敏感信号列表。
下面简单的例子给出了使用SystemC描述一个加法器的代码。更普遍的描述会使用一个头文件定义模块的接口及所包含方法、函数等的声明,而在一个或几个C++文件中实现所定义的方法、函数。实际上上面的例子是一个加法器的RTL描述,所定义的方法myadd_method()在时钟clk的上升沿被执行。现在已经有这样的工具支持对RTL的SystemC代码进行综合优化,并使用FPGA、ASIC或SoC完成硬件实现的设计流程[3]。
#i nclude
SC_MODULE(myadd)
{
//interface define
sc_in clk;
sc_in > in1;
sc_in > in2;
sc_out > out1;
void myadd_method()
{
out1 = in1.read() + in2.read();
};
// default constructor
SC_CTOR(myadd)
{
SC_METHOD(myadd_method);
//sensitive list for the process
sensitive_pos << clk;
}
}; // end module myadd
2 SYSTEMC2.0
SystemC2.0语言发布的一个主要目的是使用户可以在(相对于RTL)更高的层次对SoC系统进行描述。SystemC2.0引入了新的特性、概念以支持更方便、有效地对系统进行建模,包括:channel, interface, event等。HDL语言的RTL模型很适合于对一个特定的硬件模块进行建模,但通常很难高效率的对构成整个SoC的各模块之间的通信、同步机制进行描述。主要基于这个原因,Transaction Level(TL) 的建模思想被引入到了SoC设计流程之中[4]。TLM(Transaction Level Model)主要用来对整个SoC的体系结构进行描述,进而对系统结构进行仿真、分析、优化。同时TLM使得设计者在系统硬件的RTL描述完成之前就可以对SoC所包含的大量软件进行验证。使用SystemC2.0提供的channel、interface等概念以及基于此的Interface Method Call(IMC)的思想可以对SoC系统的TL模型进行描述,建立SoC虚拟平台。
为了更自然、更容易的对整个SoC体系结构进行描述,SystemC2.0引入了一些新的概念[5]:
● Interface interface是一组通信或同步方法的定义,使用这些方法可以对其对象进行访问。但interface本身并不包含这些方法的具体实现描述。
● Channel channel是一个对象,它具体的实现了一个或几个interface。同时一个interface也可以由多个channel在不同的抽象层次来实现。
实际上interface和channel的引入起到了对方法的封装的作用,是面向对象的设计思想在SoC描述上的具体应用,类似的思想在另一个新的硬件设计和验证语言SystemVerilog[6]中也有反映。这样使用了某一interface作为其管脚(port)类型的SoC模块可以通过方法调用的方式使用在interface中定义的方法,即interface method call。
图1是一个非常简化的SoC系统模型。
在这个简单的SoC系统中包含若干master模块,在真实的系统中,他们可以是CPU或DSP核或专有的硬件等。这个系统还包含若干slave模块,在真实的系统中他们可以是memory,buffer等。这些模块通过一个总线连结起来。总线协议中定义了各种总线访问方法,这些方法描述了基于这个总线的各SoC模块之间的通信、同步方法,例如read, write, burst_read, burst_write、direct_read、direct_write等。我们可以使用SystemC的interface来定义这个总线所提供的各种方法。然后可以由一个channel具体地实现所定义的方法。例如右面的代码分别给出定义了这个总线所支持的direct_read、direct_write的方法、实现这些interface的channel的部分代码。定义了一个interface之后,一个模块可以声明一个以这个interface为“类型”的管脚(port),并通过这个port调用在interface中所定义的方法。右面的代码也包含一个采用simple_bus_direct_if接口的master的例子。
SoC的设计者在使用SystemC描述interface所具有的基本的访问方法外还可以定义一些其他的方法用以对例如总线状态、访问冲突等进行统计、分析的方法,这样可以通过仿真对这些重要的系统性能数据进行统计、分析,并在此基础上对整个系统结构进行优化。一些商业工具在这方面可以提供更多的帮助[1]。
SoC是一个完整的系统,包含了硬件及运行于其中的软件。由于RTL代码的仿真速度的问题,在RTL代码的基础上进行软件的验证会耗费难以想象的时间。同时期待整个系统的RTL描述完成之后再进行软硬件的联合验证会延长整个开发周期[7]。SoC的TL模型则提供了一个进行软件验证的虚拟平台。 ?
● Interface:方法的定义
#i nclude
class simple_bus_direct_if
: public virtual sc_interface
{
public:
// direct BUS/Slave interface
virtual bool direct_read(int *data, unsigned int address) = 0;
virtual bool direct_write(int *data, unsigned int address) = 0;
}; // end class simple_bus_direct_if
● Channel:interface的实现
class simple_bus// Simple Bus
: public simple_bus_direct_if,
public simple_bus_non_blocking_if,
public simple_bus_blocking_if,
public sc_module
{
public: ?
…
bool direct_read (
int *data,
unsigned int address
)
{
if (address%4 != 0) {
// address not word alligned
fprintf(stdout, “BUS ERROR→
address %04X not word
alligned\n?address);
return false;
} ?
return slave→direct_read(data, address);
… ?
} ?
● Port和interface method call
#i nclude
#i nclude “simple_bus_direct_if.h?
// Simple Bus Master Direct
class simple_bus_master_direct
: public sc_module
{
…
//This port connects to the bus interface.
sc_port
…
void main_action()
{
int mydata[4];
while (true)
{
//interface method call
bus_port->direct_read(&mydata[0],
m_address);
…
wait(m_timeout, SC_NS);
…
}
3 基于TL模型的软件验证
在上面图1的例子中,某一个master的功能可能要用软件来实现,在得到总线、memory的RTL描述之前就可以对软件代码在TL的虚拟平台上进行尽可能充分的验证。此时的软件验证是“一般”的代码验证,因为它并没有被编译到相应的目标CPU或DSP中,而是使用通用的C/C++开发环境对软件代码进行调试、分析,例如VC++和GNU的C++开发环境。
CPU或DSP的指令集仿真器(ISS: Instruction Set Simulator)和调试环境可以用来对运行于其上的软件代码进行仿真、调试。但这样的调试环境通常只包含单个处理器及非常简单的存储系统,同时也不包含对存储单元(包括数据存储器和程序存储器)的访问接口模型。设计者基于SystemC可以开发出新的处理器的TL模型,它包含完整的仿真、调试环境以及用SystemC描述的 TL接口方法。这样这个模型就可以与系统的其他部分紧密地整合在一起。对多处理器系统这种方法尤为重要[7]。并且如果模型是cycle-accurate的,它将能提供足够高的仿真精度。由于使用TL模型,仿真速度也将远远高于RTL模型。图2是这种模型的构成示意。
这样的TL模型可以由使用CPU或DSP的用户开发出来,也可以由提供这些IP的供应商或其他厂家提供。
为了进行更真实的代码验证,可以使用相应处理器的编译器将软件代码进行编译,这样就可以使用上述处理器的TL模型在“虚拟”的SoC平台上运行、调试软件代码。包括EDA、IP等公司在内的一些供应商已经开始提供这类SystemC的处理器TL模型[1]。
4 应用实例
SystemC是一种开放的系统设计语言,用户可以在各种常见的平台中使用SystemC,例如windows平台下C++开发环境Visual C++,Linux平台下的GNU C++环境等。越来越多的商用软件也已经提供SystemC的开发环境[1]。本文的实例(图3)采用Synopsys提供的SystemC开发环境CoCentric System Studio。
这个实例由一个IP路由器内的AMBA总线、ARM处理器、中断控制器、DMA控制器、程序存储器、数据存储器等模块构成,这些模块采用SystemC建立TL模型,其中的ARM处理器TL模型包含完整的ARM ADU调试环境。在这个实例里还包含对AMBA总线进行统计和监控的模块,使用了Synopsys在SystemC开放语言开放基础上增添的monitor类,可以很方便的得到总线使用的统计特性以及在总线上所进行的数据传递、资源访问等信息。在这个实例中,设定了存储器大小、存储器读写周期、DMA的循环写周期等作为可以进行分析优化的参数以达到对整个系统体系结构进行分析的目的。
当这样一个SoC的结构模型建立并分析完成之后,软件设计人员可以在这样一个环境下比较充分的对软件代码在ARM的ADU环境下进行分析、调试。由于TL模型能够提供较RTL模型快得多的仿真速度,允许对较多数量的软件代码进行分析以发现可能的设计纰漏。同时硬件设计人员也可在此结构平台上进行硬件的开发。由于SystemC语言支持各种层次的抽象模型,设计人员可以采用设计“细化”[8]的方法将TL的SystemC代码转变为SystemC的RTL代码,然后在同样的测试环境下对RTL代码进行仿真。当然用户也可以依据TL的SystemC代码使用HDL语言完成RTL代码。SystemC与HDL代码之间的联合仿真可以通过PLI(VHPI)来实现。不过PLI的引入会增加冗余的仿真负载,降低仿真速度。为解决此问题,已经有一些公司推出了基于DKI(Direct Kernel Interface)的仿真方法,可以提高SystemC与HDL代码之间联合仿真的速度。
#i nclude
SC_MODULE(myadd)
{
//interface define
sc_in clk;
sc_in > in1;
sc_in > in2;
sc_out > out1;
void myadd_method()
{
out1 = in1.read() + in2.read();
};
// default constructor
SC_CTOR(myadd)
{
SC_METHOD(myadd_method);
//sensitive list for the process
sensitive_pos << clk;
}
}; // end module myadd
2 SYSTEMC2.0
SystemC2.0语言发布的一个主要目的是使用户可以在(相对于RTL)更高的层次对SoC系统进行描述。SystemC2.0引入了新的特性、概念以支持更方便、有效地对系统进行建模,包括:channel, interface, event等。HDL语言的RTL模型很适合于对一个特定的硬件模块进行建模,但通常很难高效率的对构成整个SoC的各模块之间的通信、同步机制进行描述。主要基于这个原因,Transaction Level(TL) 的建模思想被引入到了SoC设计流程之中[4]。TLM(Transaction Level Model)主要用来对整个SoC的体系结构进行描述,进而对系统结构进行仿真、分析、优化。同时TLM使得设计者在系统硬件的RTL描述完成之前就可以对SoC所包含的大量软件进行验证。使用SystemC2.0提供的channel、interface等概念以及基于此的Interface Method Call(IMC)的思想可以对SoC系统的TL模型进行描述,建立SoC虚拟平台。
为了更自然、更容易的对整个SoC体系结构进行描述,SystemC2.0引入了一些新的概念[5]:
● Interface interface是一组通信或同步方法的定义,使用这些方法可以对其对象进行访问。但interface本身并不包含这些方法的具体实现描述。
● Channel channel是一个对象,它具体的实现了一个或几个interface。同时一个interface也可以由多个channel在不同的抽象层次来实现。
实际上interface和channel的引入起到了对方法的封装的作用,是面向对象的设计思想在SoC描述上的具体应用,类似的思想在另一个新的硬件设计和验证语言SystemVerilog[6]中也有反映。这样使用了某一interface作为其管脚(port)类型的SoC模块可以通过方法调用的方式使用在interface中定义的方法,即interface method call。
图1是一个非常简化的SoC系统模型。
在这个简单的SoC系统中包含若干master模块,在真实的系统中,他们可以是CPU或DSP核或专有的硬件等。这个系统还包含若干slave模块,在真实的系统中他们可以是memory,buffer等。这些模块通过一个总线连结起来。总线协议中定义了各种总线访问方法,这些方法描述了基于这个总线的各SoC模块之间的通信、同步方法,例如read, write, burst_read, burst_write、direct_read、direct_write等。我们可以使用SystemC的interface来定义这个总线所提供的各种方法。然后可以由一个channel具体地实现所定义的方法。例如右面的代码分别给出定义了这个总线所支持的direct_read、direct_write的方法、实现这些interface的channel的部分代码。定义了一个interface之后,一个模块可以声明一个以这个interface为“类型”的管脚(port),并通过这个port调用在interface中所定义的方法。右面的代码也包含一个采用simple_bus_direct_if接口的master的例子。
SoC的设计者在使用SystemC描述interface所具有的基本的访问方法外还可以定义一些其他的方法用以对例如总线状态、访问冲突等进行统计、分析的方法,这样可以通过仿真对这些重要的系统性能数据进行统计、分析,并在此基础上对整个系统结构进行优化。一些商业工具在这方面可以提供更多的帮助[1]。
SoC是一个完整的系统,包含了硬件及运行于其中的软件。由于RTL代码的仿真速度的问题,在RTL代码的基础上进行软件的验证会耗费难以想象的时间。同时期待整个系统的RTL描述完成之后再进行软硬件的联合验证会延长整个开发周期[7]。SoC的TL模型则提供了一个进行软件验证的虚拟平台。 ?
● Interface:方法的定义
#i nclude
class simple_bus_direct_if
: public virtual sc_interface
{
public:
// direct BUS/Slave interface
virtual bool direct_read(int *data, unsigned int address) = 0;
virtual bool direct_write(int *data, unsigned int address) = 0;
}; // end class simple_bus_direct_if
● Channel:interface的实现
class simple_bus// Simple Bus
: public simple_bus_direct_if,
public simple_bus_non_blocking_if,
public simple_bus_blocking_if,
public sc_module
{
public: ?
…
bool direct_read (
int *data,
unsigned int address
)
{
if (address%4 != 0) {
// address not word alligned
fprintf(stdout, “BUS ERROR→
address %04X not word
alligned\n?address);
return false;
} ?
return slave→direct_read(data, address);
… ?
} ?
● Port和interface method call
#i nclude
#i nclude “simple_bus_direct_if.h?
// Simple Bus Master Direct
class simple_bus_master_direct
: public sc_module
{
…
//This port connects to the bus interface.
sc_port
…
void main_action()
{
int mydata[4];
while (true)
{
//interface method call
bus_port->direct_read(&mydata[0],
m_address);
…
wait(m_timeout, SC_NS);
…
}
3 基于TL模型的软件验证
在上面图1的例子中,某一个master的功能可能要用软件来实现,在得到总线、memory的RTL描述之前就可以对软件代码在TL的虚拟平台上进行尽可能充分的验证。此时的软件验证是“一般”的代码验证,因为它并没有被编译到相应的目标CPU或DSP中,而是使用通用的C/C++开发环境对软件代码进行调试、分析,例如VC++和GNU的C++开发环境。
CPU或DSP的指令集仿真器(ISS: Instruction Set Simulator)和调试环境可以用来对运行于其上的软件代码进行仿真、调试。但这样的调试环境通常只包含单个处理器及非常简单的存储系统,同时也不包含对存储单元(包括数据存储器和程序存储器)的访问接口模型。设计者基于SystemC可以开发出新的处理器的TL模型,它包含完整的仿真、调试环境以及用SystemC描述的 TL接口方法。这样这个模型就可以与系统的其他部分紧密地整合在一起。对多处理器系统这种方法尤为重要[7]。并且如果模型是cycle-accurate的,它将能提供足够高的仿真精度。由于使用TL模型,仿真速度也将远远高于RTL模型。图2是这种模型的构成示意。
这样的TL模型可以由使用CPU或DSP的用户开发出来,也可以由提供这些IP的供应商或其他厂家提供。
为了进行更真实的代码验证,可以使用相应处理器的编译器将软件代码进行编译,这样就可以使用上述处理器的TL模型在“虚拟”的SoC平台上运行、调试软件代码。包括EDA、IP等公司在内的一些供应商已经开始提供这类SystemC的处理器TL模型[1]。
4 应用实例
SystemC是一种开放的系统设计语言,用户可以在各种常见的平台中使用SystemC,例如windows平台下C++开发环境Visual C++,Linux平台下的GNU C++环境等。越来越多的商用软件也已经提供SystemC的开发环境[1]。本文的实例(图3)采用Synopsys提供的SystemC开发环境CoCentric System Studio。
这个实例由一个IP路由器内的AMBA总线、ARM处理器、中断控制器、DMA控制器、程序存储器、数据存储器等模块构成,这些模块采用SystemC建立TL模型,其中的ARM处理器TL模型包含完整的ARM ADU调试环境。在这个实例里还包含对AMBA总线进行统计和监控的模块,使用了Synopsys在SystemC开放语言开放基础上增添的monitor类,可以很方便的得到总线使用的统计特性以及在总线上所进行的数据传递、资源访问等信息。在这个实例中,设定了存储器大小、存储器读写周期、DMA的循环写周期等作为可以进行分析优化的参数以达到对整个系统体系结构进行分析的目的。
当这样一个SoC的结构模型建立并分析完成之后,软件设计人员可以在这样一个环境下比较充分的对软件代码在ARM的ADU环境下进行分析、调试。由于TL模型能够提供较RTL模型快得多的仿真速度,允许对较多数量的软件代码进行分析以发现可能的设计纰漏。同时硬件设计人员也可在此结构平台上进行硬件的开发。由于SystemC语言支持各种层次的抽象模型,设计人员可以采用设计“细化”[8]的方法将TL的SystemC代码转变为SystemC的RTL代码,然后在同样的测试环境下对RTL代码进行仿真。当然用户也可以依据TL的SystemC代码使用HDL语言完成RTL代码。SystemC与HDL代码之间的联合仿真可以通过PLI(VHPI)来实现。不过PLI的引入会增加冗余的仿真负载,降低仿真速度。为解决此问题,已经有一些公司推出了基于DKI(Direct Kernel Interface)的仿真方法,可以提高SystemC与HDL代码之间联合仿真的速度。