Calling C functions from NEURON

NMODL and the Channel Builder.
Post Reply
christina
Posts: 10
Joined: Wed Jun 08, 2005 3:08 pm
Location: Franklin & Marshall College, Lancaster, PA
Contact:

Calling C functions from NEURON

Post by christina »

Hi all,

In general, I have a few questions about calling C functions from within NEURON.

Specifically, I am looking for a NEURON function that takes a floating point number, and returns the 32-bit representation (sign bit + exponent + mantissa; 64 bits for doubles) of that number in the computer. In C, this can be done easily by defining the function

Code: Select all

int float2hex(float theFloat) {
	int hexval;

	hexval = *(int *)&theFloat;
	return hexval;
}
and calling it as follows:

Code: Select all

float 	theFloat;
int		bit_rep;

theFloat  = 1.5;
bit_rep   = float2hex(theFloat);
yielding the result bit_rep = 3fc0 0000 in hexadecimal, or 1.09955e+09 in decimal.

Q1: Is there a way to do this float-address-to-int typecasting in HOC? I didn't think so, which is why I tried to implement this as a C function in the nrn/src/oc/ directory:

Code: Select all

/****************************  FILE src/oc/float2hex.c ********/

#include <stdio.h>
#include <ctype.h>

int  	float2hex(float);

int float2hex(float theFloat) {
	int hexval;

	hexval = *(int *)&theFloat;
	return hexval;
}

/* Hoc interface */
extern double 	hoc_pushx();

int hoc_float2hex(x) 
double x;
{
	int hexval;

	fprintf(stderr,"in hoc_float2hex()\nfound x = %g\n",x);

	hexval = float2hex(x);
	fprintf(stderr,"Float %f = 0x%x = %g\n",x,hexval,(double)hexval);

	hoc_pushx((double)hexval);

	return 0;
}
Including this file and making a few changes to src/oc/hoc_init.c, I recompiled my version of Neuron. Now, executing the float2hex() function gives the following result:

Code: Select all

oc>val = float2hex(1.5)
first instance of val
in hoc_float2hex()
found x = 1.5
Float 1.500000 = 0x3fc00000 = 1.06955e+09
oc>print val
NaN 
Using a printf statement, I verified that the hoc_pushx() call does push the correct value, 1.06955e+09, onto the stack. But for some reason, 'val' gets assigned the value of 'NaN' instead.

Q2: It looks to me like the number that I want returned (1.06955e+09) is too large to be returned properly. Is this a correct assessment, or have I made some programming error? Might there be a different way to work around this problem?

Also, a few general questions on calling C functions within NEURON:

Q3: I want this function to be available whenever I run my version of Neuron. Is there a different place in the source code (rather than its current home of nrn/src/oc) where I should put this file?

Q4: Do I need to use the functions hoc_pgetarg(), getarg(), or hoc_ret()? Looking at the way other C functions in src/oc interface with HOC, I should probably use them, but I couldn't quite figure out why they were needed. When I did call these functions in my code, I kept getting ambiguous segmentation violations; so, I left them out.

Thanks in advance for any suggestions.

Best,

Christina
hines
Site Admin
Posts: 1692
Joined: Wed May 18, 2005 3:32 pm

Post by hines »

The easiest way to add c code to NEURON is with a mod file.
Search for VERBATIM in ModelDB. NMODL provides many of the
interface details automatically.

But if you do want to add a c function directly into the nrn/src/oc
interpreter then you are very close, only missing 3 points.
Don't forget to add the
c file to the Makefile.am or add the c function to one of the existing
c files. If you change Makefile.am you will need to have autoconf,
automake, and libtool.

The first point is that you have to notify the interpreter that a new
function has been added and this is done in nrn/src/oc/hoc_init.c
Insert the line
"float2hex", hoc_float2hex
into the long fun_bltin[] list and also add a declaration line
extern void hoc_float2hex;
in the large group of such statements near the beginning of the file.
This tells NEURON that the hoc_float2hex c function can be called
from hoc using the float2hex(float) syntax.

The second point is that c functions do not get their argument from
the argument list but by calling
extern double* getarg();
double x = *getarg(1);

The third point is that before adding the return value to the stack, the
function must first pop the callstack frame using the function:
ret();

As a concrete example see atan2 in hoc_init.c and math.c in the
nrn/src/oc directory.
christina
Posts: 10
Joined: Wed Jun 08, 2005 3:08 pm
Location: Franklin & Marshall College, Lancaster, PA
Contact:

Some success, but still some errors.

Post by christina »

Michael,

Thanks for your suggestions and your quick reply. I'll keep mode files + VERBATIM in mind when looking to use C functions. And looking at atan2() in math.c was indeed helpful; that's how I got my function running in the first place.

When I emailed yesterday, I had been running a variant of NEURON 5.5 on Windows XP, compiled under cygwin and installed after creating my own nrnsetup.exe file. I have now compiled the exact same code on our Mac cluster running Darwin (both my v5.5 variant and the most recent NEURON distribution), and got the correct result! That is, the function returned the correct value = 1.06955e+09, rather than "NaN". So, it seems my original implementation is functional under Darwin/Unix but not Windows, even though getarg() and hoc_ret() have been omitted. I definitely want to understand why the differences occur, but at least my function is working somewhere!

I had already implemented your suggested changes to hoc_init.c. And in fact, your second and third points were precisely the commands that gave me errors before. When my hoc_float2hex() function includes the getarg() function, like this:

Code: Select all

extern double  	hoc_pushx();
extern void  	hoc_ret();
extern double* 	getarg();

int hoc_float2hex()
{
	int hexval;
	double x;

	fprintf(stderr,"in hoc_float2hex()\n");
	x = *getarg(1);

 	fprintf(stderr,"past the getarg()\n");

	fprintf(stderr,"found x = %g\n",x);

	hexval = float2hex(x);
	fprintf(stderr,"Float %f = 0x%x = %g\n",x,hexval,(double)hexval);

	hoc_ret();
	hoc_pushx((double)hexval);

	return 0;
}
Here is the result - it crashes on the getarg() command:

Code: Select all

oc>float2hex(1.5)

in hoc_float2hex()
/private/var/automount/Users/christina/nrn/powerpc/bin/nrniv: Bus error See $NEURONHOME/lib/help/oc.help near line 1
float2hex(1.5)
               ^
The function "ret()" is not recognized by the compiler - is it somewhere strange? (The make error is: ld: Undefined symbols: _ret )

But it does recognize the function "hoc_ret()". When I use standard parameter passing and omit the getarg() function (since it causes an error), but include the hoc_ret() function like so:

Code: Select all

extern double  	hoc_pushx();
extern void  	hoc_ret();
extern double* 	getarg();

int hoc_float2hex(x)
double x;
{
	int hexval;

	fprintf(stderr,"in hoc_float2hex()\n");

	fprintf(stderr,"found x = %g\n",x);

	hexval = float2hex(x);
	fprintf(stderr,"Float %f = 0x%x = %g\n",x,hexval,(double)hexval);

	hoc_ret();
	fprintf(stderr,"Past the hoc_ret() command.\n");
	hoc_pushx((double)hexval);

	return 0;
}

Here is the result - it crashes on the hoc_ret() command:

Code: Select all

oc>float2hex(1.5)

in hoc_float2hex()
found x = 1.5
Float 1.500000 = 0x3fc00000 = 1.06955e+09
/private/var/automount/Users/christina/nrn/powerpc/bin/nrniv: Bus error See $NEURONHOME/lib/help/oc.help near line 1
float2hex(1.5)
               ^

Do you have other thoughts on why this might be happening? If it would help to diagnose the problem, I can send you the only files I have changed from the standard distribution: src/oc/Makefile.am, hoc_init.c, and float2hex.c.

Thanks again for your help.

Best,
Christina
Post Reply