Passing arrayref to C

72 views
Skip to first unread message

Mike Jones

unread,
Nov 17, 2015, 8:04:07 PM11/17/15
to ats-lang-users
Suppose I have:

%{^
uint8 funA(uint8* data) { return(0); } // Some array operation happens in here
uint8_t data[256]; // Some place to hold data for funA to work with
%}

// A macro to access the data as an arrayref
macdef my_data =
  $extval(arrayref(uint8, 256),"data")

// And ATS version of the function
fun funA_ (cPtr0(uint8)) : uint8   = "mac#funA"

val v = funA_(ptr2cptr(my_data)) // The compiler does not like this

// Get a value for use
val v' = my_data[0]


Given above, ptr2cptr does not work on the array ref. What is the proper way to pass my_data into the C call?

The assumption here is that funA will never go beyond 256 values into the passed in data. This is because of a hardware limitation where a count is a byte. So it is safe. So if ATS deals with arrayref(uint8, 256), things can't get into trouble. But I need to pass the ATS array into C to get something in hardware done.

A secondary question.

fun funB(val: arrayref(uint8,256)):uint8 =

val v = funB(my_data)

Is this the proper way to define a function that takes the arrayref? Or is there some signature with and '&' that does this like for a var?

Hongwei Xi

unread,
Nov 17, 2015, 8:20:23 PM11/17/15
to ats-lan...@googlegroups.com
Why funA_ is not given the following type instead:

fun funA_ : arrayref(uint8, 256) -> uint8


>>val v = funA_(ptr2cptr(my_data)) // The compiler does not like this

ptr2cptr takes pointer but my_data is an arrayref.


>>fun funB(val: arrayref(uint8,256)):uint8 =

Yes. 'val' is a keyword and should probably be replaced.

--
You received this message because you are subscribed to the Google Groups "ats-lang-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ats-lang-user...@googlegroups.com.
To post to this group, send email to ats-lan...@googlegroups.com.
Visit this group at http://groups.google.com/group/ats-lang-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/ats-lang-users/e5d3c479-17fa-449a-a0da-641573f976ab%40googlegroups.com.

Hongwei Xi

unread,
Nov 17, 2015, 8:44:17 PM11/17/15
to ats-lan...@googlegroups.com
By the way, I would like to suggest a different style, which you
may try later when you become more comfortable with ATS.

When I see:

uint8_t data[256];

my immediate reflex is that I can now have the following two functions:

fun{} data_get_at : natLt(256) -> uint8
fun{} data_set_at : (natLt(256), uint8) -> void

If 'data' is read-only, then 'data_set_at' is not available. Also, 'data' can be
shared among threads if that is what you want.

If you really need to call 'funA' in ATS, you can do

val () = $extfcall(void, "funA", $extval(ptr, "data"))

On Tue, Nov 17, 2015 at 8:04 PM, Mike Jones <proc...@gmail.com> wrote:

--

Mike Jones

unread,
Nov 17, 2015, 11:02:26 PM11/17/15
to ats-lang-users
Are you saying the following below would work? That by defining funA_ with an array ref a pointer would be passed to the C version funA?


%{^
uint8 funA(uint8* data) { return(0); } // Some array operation happens in here
uint8_t data[256]; // Some place to hold data for funA to work with
%}

fun funA_ (arrayref(uint8, 256) : uint8   = "mac#funA"


Hongwei Xi

unread,
Nov 17, 2015, 11:13:32 PM11/17/15
to ats-lan...@googlegroups.com
It should work.

By the way, you could just use the name 'funA' for 'funA_'.

--
You received this message because you are subscribed to the Google Groups "ats-lang-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ats-lang-user...@googlegroups.com.
To post to this group, send email to ats-lan...@googlegroups.com.
Visit this group at http://groups.google.com/group/ats-lang-users.

Mike Jones

unread,
Nov 18, 2015, 12:03:28 AM11/18/15
to ats-lang-users
I only used different names to make the example clear.

I'm having difficulty getting it to compile, so here is the exact code:

fun i2c_stop                           (arrayref(uint8, 256))                : uint8   = "mac#i2c_stop"

%{^
uint8_t i2c_data[256];
%}

macdef i2c_data =
  $extval(arrayref(uint8, 256),"i2c_data")

implement i2c_read_byte(address: uint8): (uint8, uint8) = let
  var b: uint8 = u8(0)
  var err: uint8 =                            err (0, u8)
  val () = ifnerr(i2c_start(),                err, 1, u8)
  val () = ifnerr(i2c_write(wa address),      err, 2, u8)
  val () = ifnerr(i2c_read(),                 err, 3, u8)
  val () = ifnerr(i2c_stop(i2c_data),         err, 4, u8)
  in (err, i2c_data[0]) end


I'm getting errors like:

/*
emit_instr: loc0 = /opt/ATS/ATS2-Postiats-0.2.4/prelude/DATS/array.dats: 1971(line=63, offs=45) -- 1989(line=63, offs=63)
*/
ATSINSmove(tmpret33__1, PMVtmpltcstmat[0](ptr0_get<S2Eapp(S2Ecst(g0uint_t0ype); S2Eextkind(atstype_uint8))>)(tmp34__1)) ;

DATS/smbus_dats.c:1036:43: error: 'ptr0_get' undeclared (first use in this function)
 ATSINSmove(tmpret33__1, PMVtmpltcstmat[0](ptr0_get<S2Eapp(S2Ecst(g0uint_t0ype); S2Eextkind(atstype_uint8))>)(tmp34__1)) ;

DATS/smbus_dats.c:1036:66: error: 'g0uint_t0ype' undeclared (first use in this function)
 ATSINSmove(tmpret33__1, PMVtmpltcstmat[0](ptr0_get<S2Eapp(S2Ecst(g0uint_t0ype); S2Eextkind(atstype_uint8))>)(tmp34__1)) ;



Hongwei Xi

unread,
Nov 18, 2015, 12:05:56 AM11/18/15
to ats-lan...@googlegroups.com
It seems that you need the following line:

staload _ = "prelude/DATS/unsafe.dats"

--
You received this message because you are subscribed to the Google Groups "ats-lang-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ats-lang-user...@googlegroups.com.
To post to this group, send email to ats-lan...@googlegroups.com.
Visit this group at http://groups.google.com/group/ats-lang-users.

Mike Jones

unread,
Nov 18, 2015, 12:23:27 AM11/18/15
to ats-lang-users
I had this:
   staload "prelude/SATS/unsafe.sats"

But this works when added:
   staload _ = "prelude/DATS/unsafe.dats"

Hongwei Xi

unread,
Nov 18, 2015, 12:29:13 AM11/18/15
to ats-lan...@googlegroups.com

ptr0_get is a template whose definition is in unsafe.dats.
So you need to staload unsafe.dats (not just unsafe.sats).
Otherwise, the compiler would not be able to compile ptr0_get
(and you saw error messages saying something about PMVtmpltcstmat[0])

--
You received this message because you are subscribed to the Google Groups "ats-lang-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ats-lang-user...@googlegroups.com.
To post to this group, send email to ats-lan...@googlegroups.com.
Visit this group at http://groups.google.com/group/ats-lang-users.

Mike Jones

unread,
Nov 18, 2015, 12:38:27 AM11/18/15
to ats-lang-users
Ok, I see now.

So now I have:

implement i2c_read_byte(address: uint8): (uint8, uint8) = let


How do I export this so that C can call it?

I saw from the book that the tuple becomes a struct. But it did not cover how to make the function. Is there some example of how to do this?

Hongwei Xi

unread,
Nov 18, 2015, 12:46:52 AM11/18/15
to ats-lan...@googlegroups.com

This can be done but it is a bit tricky.

Usually, such a function is given the following interface:

fun i2c_read_byte
  (address: uint8, byte1: &uint8 >> _, byte2: &uint8 >> _): void

Also, how about the following interface:

fun i2c_read_byte(address: uint8): uint16

--
You received this message because you are subscribed to the Google Groups "ats-lang-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ats-lang-user...@googlegroups.com.
To post to this group, send email to ats-lan...@googlegroups.com.
Visit this group at http://groups.google.com/group/ats-lang-users.

Mike Jones

unread,
Nov 18, 2015, 12:47:34 AM11/18/15
to ats-lang-users
Note, calling this from C is silly, but I want to test before I wrap a whole state machine around it. Even so, eventually, even that has to be called from C. So the need is general.

gmhwxi

unread,
Nov 18, 2015, 1:02:24 AM11/18/15
to ats-lang-users
The following line cause a type of the name 'uint8_pair_t'
to be defined in the generated C code:

extern typedef "uint8_pair_t" = (uint8, uint8)

So the interface for i2c_read_byte in C is

extern
uint8_pair_t i2c_read_byte (uint8_t address) ;

Mike Jones

unread,
Nov 18, 2015, 9:42:03 AM11/18/15
to ats-lang-users
Ok, let's start with a simpler one:

fun i2c_write_byte_data                (uint8, uint8, uint8)                 : uint8
implement i2c_read_byte_data(address: uint8, command: uint8): (uint8, uint8) = let


This generated:
/*
local: wa_0$0(level=0)
global: wa_0$0(level=0), i2c_write_byte_data$54$0(level=0)
local: 
global: 
*/
ATSextern()
atstkind_t0ype(atstype_uint8)
_057_home_057_mike_057_cypress_057_workspace_057_UsbI2cRegMode_057_SATS_057_smbus_056_sats__i2c_write_byte_data(atstkind_t0ype(atstype_uint8) arg0, atstkind_t0ype(atstype_uint8) arg1, atstkind_t0ype(atstype_uint8) arg2)
{

I then call it like this:

extern uint8_t _057_home_057_mike_057_cypress_057_workspace_057_UsbI2cRegMode_057_SATS_057_smbus_056_sats__i2c_write_byte_data(uint8_t arg0, uint8_t arg1, uint8_t arg2);

uint8_t v = _057_home_057_mike_057_cypress_057_workspace_057_UsbI2cRegMode_057_SATS_057_smbus_056_sats__i2c_write_byte_data(0x00, 0x00, 0x00);


And it compiles. But what is a good way to deal with this long prefix? All I can think of is to wrap a macro around it like:

#define i2c_write_byte_data _057_home_057_mike_057_cypress_057_workspace_057_UsbI2cRegMode_057_SATS_057_smbus_056_sats__i2c_write_byte_data

But this will result in messy compiler errors.


gmhwxi

unread,
Nov 18, 2015, 9:46:21 AM11/18/15
to ats-lang-users

This issue is mention in Chapter 8 of the Intro-to-ATS book:

http://ats-lang.sourceforge.net/DOCUMENT/INT2PROGINATS/HTML/HTMLTOC/c2005.html

extern
fun i2c_write_byte_data (...): .. = "ext#whatever_name_you_like_in_c_for_this_function"

Mike Jones

unread,
Nov 18, 2015, 11:06:05 AM11/18/15
to ats-lang-users
Oh, I was reading after that point and missed that paragraph. I'll test now and confirm the array ref passes a pointer, etc.

Mike Jones

unread,
Nov 18, 2015, 11:12:39 AM11/18/15
to ats-lang-users
The compiler seems to generate the _c names for the write, but not the read. Looks like the compiler can't handled the struct return and just ignores the ext def.


fun i2c_read_byte_data                 (uint8, uint8)                        : (uint8, uint8) = "ext#i2c_read_byte_data_c"
fun i2c_write_byte_data                (uint8, uint8, uint8)                 : uint8 = "ext#i2c_write_byte_data_c"
fun i2c_read_word_data                 (uint8, uint8)                        : (uint8, uint16) = "ext#i2c_read_word_data_c"
fun i2c_write_word_data                (uint8, uint8, uint16)                : uint8 = "ext#i2c_write_word_data_c"


Hongwei Xi

unread,
Nov 18, 2015, 11:19:24 AM11/18/15
to ats-lan...@googlegroups.com

If i2c_read_byte_data is implemented, then the compiler should generate i2c_read_byte_data_c.


--
You received this message because you are subscribed to the Google Groups "ats-lang-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ats-lang-user...@googlegroups.com.
To post to this group, send email to ats-lan...@googlegroups.com.
Visit this group at http://groups.google.com/group/ats-lang-users.

Mike Jones

unread,
Nov 18, 2015, 11:20:57 AM11/18/15
to ats-lang-users
I think there may have been a problem with my make. After a make clean, it showed up.

Mike Jones

unread,
Nov 18, 2015, 1:57:11 PM11/18/15
to ats-lang-users
Ok, I'm closer. Compiles. Runs. But having some problem with return values.

In ATS:

%{^
uint8_t i2c_data[256];
%}

macdef i2c_data =
  $extval(arrayref(uint8, 256),"i2c_data")

fun i2c_read_byte_data                 (uint8, uint8)                        : (uint8, uint8) = "ext#i2c_read_byte_data_c"
implement i2c_read_byte(address: uint8): (uint8, uint8) = let
  var err: uint8 =                            err (0, u8)
  val () = ifnerr(i2c_start(),                err, 1, u8)
  val () = ifnerr(i2c_write(wa address),      err, 2, u8)
  val () = ifnerr(i2c_repeated_start(),       err, 3, u8)
  val () = ifnerr(i2c_read(),                 err, 4, u8)
  val () = ifnerr(i2c_stop(i2c_data),         err, 5, u8)
  in (err, i2c_data[0]) end

In C, supporting i2c_stop(i2c_data) above in ATS:

uint8_t i2c_stop(uint8_t *data)
{
CyU3PReturnStatus_t status = CY_U3P_SUCCESS;

CyU3PDebugPrint (2, "I2C Stop\r\n");

if (read)
{
status = CyU3PI2cReceiveBytes (&shared_preamble, shared_data, shared_data_len, 0);
memcpy(data, shared_data, shared_data_len);
}
else
{
if (shared_data_len == 0)
shared_data[shared_data_len++] = shared_preamble.buffer[--shared_preamble.length];
status = CyU3PI2cTransmitBytes (&shared_preamble, shared_data, shared_data_len, 0);
}

return((uint8_t)status);
}


In C application code:

typedef

struct {

uint8_t err;

uint16_t byte;

} err_byte;


extern err_byte i2c_read_byte_data_c(uint8_t, uint8_t);


    err_byte sb;

    uint8_t b; 

    sb = i2c_read_byte_data_c(0x30, 0x00);

    status = sb.err;

    b = sb.byte;


In the debugger while stopped inside i2c_stop() (C code), I look at i2c_data and see that it is set to 0x01. But what is returned to the err_byte struct is a 0x00.

This implies that in the ATS, the code in (err, i2c_data[0]) end is failing to index into the C array i2c_data, or the mapping to the returned struct is failing. Is there some problem with my C declaration of the struct? Could there be a memory alignment issue requiring some forced packing or something?

Note this is a 32 bit ARM 9. And I am not using the C99 option during compilation, I'm using whatever the default is for the Cypress make system.

Mike Jones

unread,
Nov 18, 2015, 2:01:42 PM11/18/15
to ats-lang-users
Yep, was packing. This fixed it.

#pragma pack(push, 1)


typedef

struct {

uint8_t err;

uint16_t word;

} err_word;


#pragma pack(pop)

gmhwxi

unread,
Nov 18, 2015, 2:11:14 PM11/18/15
to ats-lang-users
Interesting :)

Mike Jones

unread,
Nov 18, 2015, 2:46:26 PM11/18/15
to ats-lang-users
Yep. I have a ThreadX app thread in the Cypress FX3 USB3 device calling ATS, which is then calling back into C to operate the I2C bus. Now I can write a whole application in ATS. Given the level or reliability required for a USB Device, this will be a big win.
Reply all
Reply to author
Forward
0 new messages