In the next post in this series, we look at the numeric data types that are included in the SystemC library.
As a result of this, the basic syntax of the SystemC language is taken directly from C++.
However, in these tutorials we will only look at the SystemC extensions which we can use in FPGA design and verification.
Therefore, if you are not already familiar with the C++ language then it is a good idea to take a beginners C++ course before reading through these tutorials.
When we are designing an FPGA, almost all of our signals fundamentally consist of one or more logical bits. We use the different data types in SystemC to tell our tools how these bits should be interpreted.
For example, when we declare an integer type variable in SystemC then we are actually declaring a 32 bit two's complement number.
The simplest types of data we can use in SystemC consists of a number of logical bits which are interpreted as binary words. We typically use one of the two data types for this purpose in SystemC.
Let's take a closer look at both of these data types.
The sc_logic type is the most basic of all of the SystemC data types. We use this type to model a single binary value within our FPGA.
The code snippet below shows the syntax we use to declare an sc_logic data type in SystemC.
We use the <name> field in the above construct to give a name to our variable.
When we design digital circuits in SystemC, we often use the C++ bool type to model single binary values.
However, we can also have cases where we need to model values other than 0b or 1b in SystemC. We could have a case where we need to drive the pin of an FPGA high impedance, for example.
The sc_logic type makes it possible to for us to model these different states in our design.
In addition to this, it also models conditions where the logic value is unknown or non-deterministic. This typically occurs due to errors in our design such as attempting to drive a signal from 2 different sources.
The table below shows the full list of values that the sc_logic type can have.
|X||Unknown value as it's impossible to determine the value.|
|0||Logic level 0|
|1||Logic level 1|
We typically use SystemC constants to assign values to the sc_logic data type. These constants are SC_LOGIC_0 (for 0b), SC_LOGIC_1 (for 1b), SC_LOGIC_Z (high impedance) and SC_LOGIC_X (unknown value).
However, we can also directly assign the sc_logic type to '0', '1', 'z' or 'x'.
The code snippet below show how we assign values to a signal or port which uses the sc_logic type in SystemC. We can also simulate this example on EDA playground.
// Assigning a signal to 1 sc_logic sc_example1 = SC_LOGIC_1; sc_example1 = '1'; // Assigning a signal to 0 sc_logic sc_example0 = SC_LOGIC_0; sc_example0 = '0'; // Assigning a signal to high impedance sc_logic sc_examplez = SC_LOGIC_Z; sc_examplez = 'z'; // Assigning a signal to an unknown state sc_logic sc_examplex = SC_LOGIC_X; sc_examplex = 'x';
When we design digital circuits, we often use data buses which consist of multiple bits. We can't model these signals with the sc_logic data type as it consists of only 1 bit.
Therefore, we need to use a different data type to model data buses in our designs. In SystemC, we use the sc_lv data type for this purpose.
The sc_lv data type consists of a number of bits which are each modeled in a similar way to the sc_logic data type.
In order to allow for different size buses to be declared, the sc_lv data type is implemented as a generic C++ classes in SystemC. This means that we have to provide a parameter which defines how many bits are in the signal.
The code snippet below shows the general syntax we use to declare a sc_lv data type in SystemC.
In the above construct we use the <bits> to declare the number of bits in our signal.
We use the <name> field in the above construct to give a name to our signal.
When we assign data to the sc_lv data type we use quotation marks (") to enclose the binary data.
The code snippet below shows the general syntax we use to assign data to a sc_lv type variable.
// Declare a four bit vector sc_lv<4> example_vector; // Assign the value of 1010b (0xA) example_vector = "1010";
We can also access single bits or data slices when we are working with sc_lv data types in SystemC.
In order to access a single bit in our sc_lv data we simply specify the bit which we want to access inside of square brackets. The code snippet below shows the general syntax we use to do this.
<variable_name>[<bit_number>] = <data>;
In this construct, we use the <bit_number> field to select the relevant bit in our variable.
When we want to access a slice of data in an sc_lv data type, we simply specify the range of the bits which we are selecting.
However, we do this inside of parentheses rather than square brackets when we want to take a slice of data.
The code snippet below shows the general syntax for this.
<variable_name>(<first_bit>, <last_bit>) = <data>;
In this construct, we use the <first_bit> field to indicate the starting point of our data slice. We use the <last_bit> field to indicate the last bit to include in our data slice.
The code snippet below shows how we can access single bits and slices of our data when using the sc_lv data type. We can also simulate this example on EDA playground.
// Declare a 4 bit sc_lv data type sc_lv<4> example_lv = "1010"; // Set bit 0 to 1b xample_lv = "1"; // Set bits 3 to 1 to 010b example_lv(3,1) = "010";
The sc_lv data type comes with a number of in built methods which we can use in our SystemC code.
The table below summarizes the most commonly used sc_lv methods which are available to us.
|lrotate(N)||Shift the data left by N bits. The dropped bit is appended to the end of the the variable.|
|rrotate(N)||Shift the data right by N bits. The dropped bit is appended to the beginning of the variable.|
|reverse()||Reverse the order of the bits.|
|range(START, END)||Select a slice of the data from the variable (same as bit slicing)|
|and_reduce()||Reduce the vector to a single bit by performing a logical and of all bits.|
|or_reduce()||Reduce the vector to a single bit by performing a logical or of all bits.|
|xor_reduce()||Reduce the vector to a single bit by performing a logical exclusive or of all bits.|
The code snippet below shows an example of how each of these methods is used in practise. This example can also be simulated on EDA playground.
// Declare an sc_lv type sc_lv<4> example = "0110"; // Examples of the sc_lv methods cout << "lrotate(1) = " << example.lrotate(1) << endl; cout << "rrotate(2) = " << example.rrotate(2) << endl; cout << "reverse() = " << example.reverse() << endl; cout << "range(2,0) = " << example.range(2,0) << endl; cout << "and_reduce() = " << example.and_reduce() << endl; cout << "or_reduce() = " << example.or_reduce() << endl; cout << "xor_reduce() = " << example.xor_reduce() << endl;
Running this example on EDA playground results in the output below.
lrotate(1) = 1100 rrotate(2) = 0011 reverse() = 1100 range(2,0) = 100 and_reduce() = 0 or_reduce() = 1 xor_reduce() = 0
What is the most important difference between the sc_logic type and the bool type?show answer
We can model high impedance and unknown states using the sc_logic type.hide answer
Write some code which declares an sc_logic type and assigns it to high impedance.show answer
// Using the SystemC constants sc_logic example = SC_LOGIC_Z; // Directly assigning values sc_logic example = 'z';
Write the code which declares an 8 bit sc_lv data type and assign it the value of 0xFF.show answer
sc_lv<8> example = "11111111";