|
|
2 Ports and Data Types
2.1 SystemC I/O Ports
Table 2.1 shows some basic SystemC I/O ports and their VHDL counter parts.
|
VHDL Port |
SystemC Port |
SystemC Notes |
|
in |
sc_in |
|
|
out |
sc_out |
Behaves like a VHDL buffer port |
|
inout |
sc_inout |
|
|
buffer |
sc_inout |
|
Table 2.1 SystemC I/O Ports types
The port's data type is passed on between the angle brackets (<>, templated class), for example:
|
VHDL |
SystemC |
|
port (clk : in std_logic; count : inout std_logic_vector(7 downto 0); zero : out std_logic); |
sc_in<sc_logic > clk; sc_inout<sc_lv<8> > count; sc_out<sc_logic > zero; |
Note the space between the last 2 closing angle brackets for the count declaration "> >". The >> without the space is a C++ stream operator. Although it might be clear from the context what is intended, some compilers can't handle it (gcc 3.4.4 complains, Visual C++ seems OK).
In VHDL you can specify a range for your vectors but you can't do this in SystemC! This can leads to translation difficulties between VHDL and SystemC since bit positions are no longer the same!
| VHDL |
SystemC |
|
xyz1 : out std_logic_vector(7 downto 3); |
sc_out<sc_lv<5> > xyz1; |
2.1.1 Reading and writing from ports
Reading from and writing to an IO ports is straightforward. Unlike in VHDL you only have one assignment operator "=" for both signals and variables. It is therefore recommended to use the port methods when reading from (.read()) and writing to (.write()) a port. A method is a function call that can be overwritten if required (so called virtual member function). Example of some R/W operations:
|
VHDL |
SystemC |
SystemC Notes |
|
count <= X"55"; |
count=0x55; |
Style not recommended |
|
count <= "0101XZ01"; |
count.write("0101XZ01"); |
|
|
zero <= '0'; |
zero.write('0'); |
|
|
zero <= '0'; |
zero.write(SC_LOGIC_0) |
SC_LOGIC_0, SC_LOGIC_1, SC_LOGIC_X, SC_LOGIC_Z |
|
clk='1' |
clk=='1' |
Style not recommended |
|
if (clk='1') then |
if (clk.read()==SC_LOGIC_1) { |
|
Note the == operator in the last SystemC example. It is quite easy to make a mistake like:
int test;
if (test = 42) { // Mistake, should have been test==42
which is perfectly legal C/C++/SystemC syntax since the return value of the assignment is true but probably not what the programmer intended.
There are cases where the use of '0'/'1'/'X' and 'Z' literals are ambiguous, if you get a compile error then try to use the SystemC std_logic constants SC_LOGIC_0/SC_LOGIC_1/SC_LOGIC_X and SC_LOGIC_Z respectively.
2.1.2 Initialising a ports
Initialising a port is not as simple as in VHDL. As a VHDL programmer you might have thought of using something like this:
|
VHDL |
SystemC |
SystemC Notes |
|
ENTITY (test) is port (count : out std_logic_vector(7 downto 0):=X"AA"; zero : out std_logic:='1'); |
SC_MODULE(test) { sc_inout<sc_lv<8> > count=0xAA; sc_out<sc_logic > zero='1'; |
** error: ISO C++ forbids initialization of member .. ** |
As was mentioned in the beginning of the tutorial SC_MODULE can be considered as a combined Entity Architecture pair. Although the comparison is valid since both encapsulate ports, processes, functions etc, in reality SC_MODULE is a user defined C++ type (a class). If you then drawn the analogy with a VHDL type (albeit a very powerful one with additional processes, ports and functions) then you can understand that you cannot assign constant values to any of its data members as shown in the above code snippet. An SC_MODULE is created when it is instantiated and at that time the constructor (SC_CTOR) is automatically called. It is during this construction phase that ports, signals and variable are initialised. To initialise a port you have to use the .initialize() method during construction as shown below.
|
VHDL |
SystemC |
|
ENTITY (test) is port (count : out std_logic_vector(7 downto 0):=X"AA"; zero : out std_logic:='1'); |
SC_MODULE(test) { sc_inout<sc_lv<8> > count; sc_out<sc_logic > zero; SC_CTOR(count) { count.initialize(0x55); zero.initialize('1'); |
2.2 SystemC Data Types
SystemC extends the C++ data types with some additional types useful for modeling hardware. Table 2.2 list a few of these data types and their equivalent VHDL ones.
|
VHDL Type |
SystemC Type |
SystemC Notes |
|
bit |
sc_bit |
Use native C++ bool type instead |
|
bit_vector |
sc_bv |
Faster in simulation than sc_lv |
|
std_ulogic |
sc_logic |
Only support 4 types, 'X','Z','0' and '1' |
|
std_ulogic_vector |
sc_lv |
Only support 4 types, 'X','Z','0' and '1' |
|
std_logic |
sc_logic_resolved |
Resolution function for 'X','Z','0' and '1' |
|
std_logic_vector |
sc_signal_rv |
Resolution function for 'X','Z','0' and '1' |
|
boolean |
bool |
recommended instead of sc_bit |
|
integer |
int |
int size is platform dependent, use sizeof(int) |
|
integer |
sc_int<N>/sc_uint<N> |
N bits signed/unsigned integer vector (N <=64) |
|
integer |
sc_bigint<M>/sc_biguint<M> |
arbitrary length signed/unsigned integer vector |
|
Coming in VHDL2006 |
sc_fixed/sc_ufixed/sc_fix/sc_ufix |
signed and unsigned fixed point number |
|
float |
float |
Native C/C++ 32bits floating point number |
Table 2.2 SystemC Data types
Note that the SystemC sc_logic and sc_lv are unresolved types and only support 4 signal types ('X','Z','0','1').
The general recommendation is to use the native C++ types as much as possible since they simulate faster than the SystemC types. If you don't need the 'X' and 'Z' values then use bool for std_logic and sc_int/sc_uint for std_logic_vector.
2.2.1 Data Types Operations
Table 2.2.1 shows some VHDL operations and their SystemC equivalent.
|
Operation |
VHDL Example |
SystemC Equivalent |
|
Bit Select |
count(3) |
count[3] |
|
Part Select |
count(5 downto 2) |
count(5,2) |
|
Alt. Part Select |
count(5 downto 2) |
count.range(5,2) |
|
Concatenate |
count(1)&count(3 downto 2) |
(count[1],count(3,2)) |
|
Alt. Concatenate |
count(1)&count(3 downto 2) |
.concat(count[1],count(3,2)) |
| Attributes |
count'LEFT |
Not Supported, Note 1 |
Table 2.2.1 Data Type Operations
Note1: some attributes have equivalent methods, for example count'HIGH can be replaced with count.length().
The bit select operator is the overloaded angle brackets [n] operator. The part select (slice in VHDL) is the overloaded round bracket operator (n,n), you can also use the .range() method. Finally the concatenation operator is the overloaded comma (,) operator or you can use the .concat() method. Note, the term operator overloading refers to C++ capability to create new behaviour/functions for standard C++ operators like '+','-','=', '&' etc based on their arguments.
The above described bit/part select and concatenations seems straightforward, however, it is quite easy to make mistakes. The VHDL code fragment below shows a 1 bit ROtate Right register and one that swaps a nibble.
port (din : in std_logic_vector(7 downto 0);
dout1 : out std_logic_vector(7 downto 0);
dout2 : out std_logic_vector(7 downto 0));
Architecture
dout1 <= din(0) & din(7 downto 1); -- ROR
dout2 <= din(3 downto 0) & din(7 downto 4); -- Swap Nibble
To translate this to SystemC one might try the following bit of code:
sc_in<sc_uint<8> > din;
sc_out<sc_uint<8> > dout1;
sc_out<sc_uint<8> > dout2;
dout1.write( din[0] , din(7,1) ); //** Error **
dout2.write( din(3,0) , din(7,4) ); //** Error **
This code fragment has several errors, the first one is that you can not apply part select, bit select on a port or signal directly, you need to use the .read() method first. The reason will become clear later in the tutorial. The second problem is that the concatenation (overloaded comma) operator. If you familiar with say the C-language you might think that the following code fragment has 2 redundant brackets:
write((a,b));
And if this was a C function call you would be right, however, for SystemC the comma operator is of the form (,) and the surrounding brackets are part of the overloaded operator so they are required. The correct form of the VHDL code fragment is shown below:
dout1.write( ( din.read()[0] , din.read()(7,1) ) ); // correct
dout2.write( ( din.read()(3,0) , din.read()(7,4) ) ) ;// correct
or using methods
dout1.write( concat(din.read()[0], din.read().range(7,1)) ); // correct
dout2.write( concat(din.read()(3,0),din.read().range(7,4)) ); // correct
2.2.2 Logical/Assignment/Equality Operations
Table 2.2.2 list some VHDL operations and their SystemC counter part.
|
VHDL |
SystemC |
Notes |
|
Bit wise AND OR XOR NOT |
& | ^ ~ |
|
|
srl sll |
>> << |
|
|
Signal assignment <= |
= |
|
|
Variable assignment := |
= |
Note same operator as for signals |
|
Equality = /= |
== != |
Easy to make mistake and use one '=' |
|
Relational < <= > >= |
< <= > >= |
same |
Table 2.2.2 Operations
2.2.3 Arithmetic Operations
The SystemC integer types (sc_int/sc_uint/sc_bigint/sc_biguint) can all be used with the normal integer operations such as +,-,*,/ and %. The SystemC vector types (sc_bv/sc_lv) on the other hand do not support any type of arithmetic operations. Although in VHDL you can add to an std_logic_vector using the obsolete std_logic_unsigned package, the recommendation is to use the numeric_std package which means like SystemC you have to convert to unsigned first.
|
VHDL |
SystemC |
|
use ieee.std_logic_unsigned.all; -- Obsolete package |
|
Note SystemC also supports the C/C++ autoincrement ++ and autodecrement -- operators which adds 1 and subtracts 1 respectively.
2.2.4 Data Type Conversions
Conversion between different SystemC types is done using methods. Table 2.2.4 shows some simple examples:
|
VHDL |
SystemC |
|
use ieee.numeric_std.all; |
|
|
if (std_logic='1') then boolean=TRUE; else boolean=FALSE; |
sc_logic.to_bool(); if (bool) sc_logic='1'; else sc_logic='0'; |
Table 2.2.4 Example type conversion
Note the static_cast operation is not really necessary since there is an implicit conversion from sc_uint to sc_lv, however, this is good coding practise?