C - Geek went Freak!

C

GCC: -fprofile-arcs va -ftest-coverage

Two flags we use in gcc to enable code coverage are -fprofile-arcs and -ftest-coverage.

So, what exactly do these flags do?

Block graph and source location information

-ftest-coverage generates .gcno files for corresponding translation units during compile time. These files have information regarding block graphs and their associated line numbers in platform independent format.

Profile statistics and counts

Code coverage is performed by instrumenting code into the target source code. This instrumented code then keeps track of statistics and counts of number of times a statement or a block has executed.

Functions in libgcov.a are responsible for recording these statistics information to .gcda file after execution.

The flag -fprofile-arcs links libgcov.a library into the executable.

Making sense of the information

Thus, during compile time -ftest-coverage generates .gcno and after runtime, -fprofile-arcs generates .gcda. gcov command uses these two files to link statistics in .gcda to block graph and source association information in .gcno.

C++: vector of arrays

In C++ arrays cannot be stored in vectors. This restriction is due to the fact that arrays cannot be copy constructed or assigned. For example, this won’t compile in c++

To work around this limitation, c++11 has introduced a new container called std::array. std::array is just a template class that encapsulates a static array. It cannot be resized and its size should be know at compile time. Here is a small example on how to create a vector of arrays.

C++11 regex using g++

Boost regex has been accepted into C++11. So, I decided to give it a try using g++.

#include
#include
#include

using namespace std;

int main() {
        string str("2 a\n3 b\n4 c\n5 d\n6 c");
        regex r("[0-9]");

        regex_iterator start(str.begin(), str.end(), r);
        regex_iterator end;

        while(start != end) {
                cout << start->str() << endl;
                ++start;
        }
}

I was surprised to get a big fat link-time error.

Seems like gcc 4.8.2, delivered by my distribution doesn’t support regex yet. So, we are forced to use Boost regex for now. Or use g++ 4.9.0 instead, which has regex implemented. To install the latest g++ release, execute the following command in the terminal:

sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install g++-4.9

To compile the code with g++4.9:

g++-4.9 --std=c++11 regex1.cpp -o regex1

C: declaring multipe pointer variables in same declaration statement

First thing first, never declare multiple pointer variables in same declaration statement. Don’t mix declaration of pointer and non-pointer variables in same line. Following this will save you a lot of debugging time in the future.

But in case, you are forced do it or you are debugging code written by somebody else, this short post can help you.

What does this code really mean?

int* c, d;

It is clear that c is a pointer to integer. But what about d? Is it integer or pointer to integer? Ofcourse, it is integer not a pointer to integer. Using declaration as above is very ambigious. Avoid it any cost. Instead, declare them on their own statements.

int* c;
int d;

Much clear. Isn’t it?

It is also better to follow this rule when all variables are pointers. Also, I prefer to put the star next to varaible name instead of the variable type.

int *p, *q;

is more intuitive than

int* p, *q;

Passing and receiving different SystemVerilog datatypes with C/C++ using DPI

SystemVerilog makes interaction with C/C++ a piece of cake. Now, you can directly call C/C++ functions as functions or tasks in SystemVerilog(aka. Importing) and call SystemVerilog functions or tasks from C/C++(aka. Exporting). In this article, we will see how we can pass and receive different SystemVerilog datatypes through DPI.

Return type of imported C/C++ functions

First thing to note is that imported functions can only return single bit types and 2-state int or double types that are not vectors.

This rule rules out all 4-state types except single bit types

  • logic packed arrays, logic unpacked arrays
  • wire, wire packed arrays, wire unpacked arrays
  • reg, reg packed arrays, reg unpacked arrays

and all vector types

  • bit packed arrays, bit unpacked arrays
  • logic packed arrays, logic unpacked arrays
  • wire packed arrays, wire unpacked arrays
  • reg packed arrays, reg unpacked arrays

So what we are left with are fixed width int and double types:

  • bit
  • byte
  • shortint
  • int
  • shortreal
  • real
  • logic

You can pass these types as arguments and receive these types as return from an imported functions. Here is an example of passing and receiving single bit bit and logic.

import "DPI-C" context function bit and_bit(input bit a, b);
import "DPI-C" context function logic and_logic(input logic a, b);
svBit
and_bit(
    svBit a,
    svBit b) {
	return a && b;
}

svLogic
and_logic(
    svLogic a,
    svLogic b) {
	return a && b;
}

You should have noticed two new types on C/C++ side: svBit and svLogic. These are usually 8 bit unsigned char types but only the lsb bit is used.

Passing and receiving packed arrays

Passing and receiving packed arrays is easier than dealing with unpacked arrays. On C side, packed bit vectors are called svBitVecVal and packed logic vectors are called svLogicVecVal. Bit vectors are plain int types. If the vector size exceeds the size that can be stored inside an integer, the bit vectors are passed as array of svBitVecVal.

Logic vectors are little bit more complicated because they represent 4-state values. svLogicVecVal is a structure with two members aval and bval. aval stores the plain bit representation of logic vector while bval stores whether the corresponding bit is ‘0’, ‘1’ or ‘x’, ‘z’. If the vector size exceeds the size that can be stored inside an integer, the logic vectors are p assed as array of svLogicVecVal.

import "DPI-C" context function void add_bpv(input bit [3:0] a, b, output bit [3:0] c);
import "DPI-C" context function void add_lpv(input logic [3:0] a, b, output logic [3:0] c);
void
add_bpv(
    const svBitVecVal* a,
    const svBitVecVal* b,
    svBitVecVal* c) {
	*c = *a + *b;
}

void
add_lpv(
    const svLogicVecVal* a,
    const svLogicVecVal* b,
    svLogicVecVal* c) {
	c->aval = a->aval + b->aval;
	c->bval = 0;
}

As you can see, we are using output arguments to receive vector types because imported functions cannot return vector types.

Passing and receiving unpacked vectors

Unpacked arrays are sent as arrays of svBit or scLogic to C. There is no way to directly access the integer represented by putting all the individual bits together without calculating it.

import "DPI-C" context function void and_buv(input bit a[3:0], b [3:0], output bit c [3:0]);
import "DPI-C" context function void or_luv(input logic a[3:0], b [3:0], output logic c [3:0]);
void
and_buv(
    const svBit* a,
    const svBit* b,
    svBit* c) {
	for(int i = 0; i < 4; i++) {
		c[i] = a[i] & b[i];
	}
}
void
or_luv(
    const svLogic* a,
    const svLogic* b,
    svLogic* c) {
	for(int i = 0; i < 4; i++) {
		c[i] = a[i] | b[i];
	}
}

Passing and receiving mixed packed and unpacked arrays

As expected, mixed packed and unpacked arrays are represented as arrays of svBitVecVal or svLogicVecVal. If the size of the packed dimension is greater than what can fit inside an integer, mixed packed and unpacked arrays are represented as arrays of arrays of svBitVecVal or svLogicVecVal.

import "DPI-C" context function void add_bpuv(input bit [0:3]a[0:3], b [0:3], output bit [0:3]c [3:0]);
void
add_bpuv(
    const svBitVecVal* a,
    const svBitVecVal* b,
    svBitVecVal* c) {
	for(int i = 0; i < 4; i++) {
		c[i] = a[i] + b[i];
	}
}

Passing and receiving structs

Structs are passed can be passed and received directly. Individual members are representing based on their data type as discussed above.

typedef struct {
shortint mem1;
logic [7:0] mem2;
bit [7:0] mem3;
} struct_t;

import "DPI-C" context function void add_struct(input struct_t a, b, output struct_t c);
void
add_struct(
    const struct_t* a,
    const struct_t* b,
    struct_t* c) {
	c->mem1 = a->mem1 + b->mem1;
	c->mem2[0].aval = a->mem2[0].aval + b->mem2[0].aval;
	c->mem2[0].bval = 0;
	c->mem3[0] = a->mem3[0] + b->mem3[0];
}

Here is the full verilog and c code.

Golang: Call C code from Go code

Be it a library written in C or parts of your implementation you want to implement in C, it is very easy to call them from Golang.
All C functions, types, convenience function, etc are e,ported through C package in Golang. The C code can be directly embedded in Go code by writing it as comments directly above the statement that imports C package.

To run the above e,ample e,ecute the following command:

go run cgo1.go

You can also write C code in a separate .c file and access them in Go.

To run the above e,ample e,ecute the following command:

go run cgo2.go

To call a Go function in C code, e,port the Go function by writing the following snippet directly above the Go function:

//e,port func-name

All the e,ported Go functions can be accessed from C by including the auto-generated header file _cgo_e,port.h.

It should noted that this e,ample won’t work unless you place the project under $GOPATH/src and e,ecute go build on the project. For e,ample move the two files to $GOPATH/src/cgo3 and e,ecute the following commands:

go build cgo3
./cgo3