Shop OBEX P1 Docs P2 Docs Learn Events
propeller2.h for C compilers - Page 4 — Parallax Forums

propeller2.h for C compilers

1246789

Comments

  • If you are going to be using stdint.h types you really should be consistent and use them exclusively instead of the built in integer types. In addition to uintX_t/intX_t there are unit_fastX_t/int_fastX_t (the unsigned/signed types with at least X bits that offer the best performance), uint_leastX_t/int_leastX_t (the smallest unsigned/signed types that have at least X bits), uintmax_t/intmax_t (the maximum width integer types), and uintptr_t/intptr_t (the integer types capable of holding pointers). These types are guaranteed to exists unlike the truly fixed width types. The only times you should really use the truly fixed width types are when you are directly interacting with hardware, or you need exact wrap/overflow behavior.
  • @cgracey : I've started implementing the new functions, and ran into a small snag: "PIN" is a really common variable name, it would be nice if it didn't conflict with a built in function name. Could we rename this to "GETPIN" or something else that makes it obvious that we're fetching a pin's value (I guess "PINVAL" would work too)? To a lesser degree it would be nice to use "GETRND" and "GETCNT" in place of "RND" and "CNT", too.
  • cgracey wrote: »
    DavidZemon wrote: »
    I've re-read parts of this thread and read through the Spin2 interpreter thread (aka: the editor war thread) as well. I want to address something directly that I may have been saying (thinking?) too subtly.
    I do not think propeller2.h should take Spin into consideration. This header should not be about providing Spin compatibility in C. It should be about providing access to all of the P2's hardware from C. IF that happens to be the same feature set as Spin, so be it, but if Spin offers extra features that are not hardware-specific - maybe some string manipulation extra trig functions outside the scope of what the hardware does - I think it should be left out of propeller2.h. It can go elsewhere.
    But it's this idea of bringing Spin to C that I want to address. It should not be the goal of propeller2.h. It's a valiant goal, but this is the wrong place for it.

    I agree, but there are a lot of Spin instructions which relate to PASM instructions which will be needed in C. They are going to need names and Spin has names for them. Might as well harmonize those names. I'm thinking about the obvious RDPIN/WRPIN and pin-related instructions. Most CORDIC operations don't conform to any industry standard, so maybe QLOG/QEXP are appropriate.

    Agreed.
    Type conformity is another matter when writing those prototypes though.
    Basically, in the decision tree of "should we do A or B," the node that reads "do whichever one Spin does" should be at the very bottom of the three.
  • DavidZemon wrote: »
    cgracey wrote: »
    DavidZemon wrote: »
    I've re-read parts of this thread and read through the Spin2 interpreter thread (aka: the editor war thread) as well. I want to address something directly that I may have been saying (thinking?) too subtly.
    I do not think propeller2.h should take Spin into consideration. This header should not be about providing Spin compatibility in C. It should be about providing access to all of the P2's hardware from C. IF that happens to be the same feature set as Spin, so be it, but if Spin offers extra features that are not hardware-specific - maybe some string manipulation extra trig functions outside the scope of what the hardware does - I think it should be left out of propeller2.h. It can go elsewhere.
    But it's this idea of bringing Spin to C that I want to address. It should not be the goal of propeller2.h. It's a valiant goal, but this is the wrong place for it.

    I agree, but there are a lot of Spin instructions which relate to PASM instructions which will be needed in C. They are going to need names and Spin has names for them. Might as well harmonize those names. I'm thinking about the obvious RDPIN/WRPIN and pin-related instructions. Most CORDIC operations don't conform to any industry standard, so maybe QLOG/QEXP are appropriate.

    Agreed.
    Type conformity is another matter when writing those prototypes though.
    Basically, in the decision tree of "should we do A or B," the node that reads "do whichever one Spin does" should be at the very bottom of the three.

    Not really right, since Spin is currently fluid, it does make sense to harmonize the both languages. No need to bend C/C++ but possible to bend Spin a bit, like with strsize and strlen.

    Enjoy!

    Mike
  • RossHRossH Posts: 5,476
    ersmith wrote: »
    That all sounds good to me, except that I'd like to distinguish the types in some way. If we're not going to use "_t" then perhaps we should make POLAR and CARTESIAN all upper case as well, or captialize at least the first letter, and/or put an underscore in front of them to avoid conflicts with user variables.

    The convention I was suggesting we adopt would be to use an underscore in front of the structure tag, but not the type name - i.e.
    // type for cartesian coordinates
    typedef struct _cartesian {
      long x, y;
    } cartesian;
    
    // type for polar coordinates
    typedef struct _polar {
      long r, t;
    } polar;
    
    

    You can, of course, do it the other way around, but this way around seems to be more in keeping with the convention that the leading underscore implies the implementation-defined entity, whereas the typedef is more of a naming convenience.
  • RossHRossH Posts: 5,476
    If you are going to be using stdint.h types you really should be consistent and use them exclusively instead of the built in integer types. In addition to uintX_t/intX_t there are unit_fastX_t/int_fastX_t (the unsigned/signed types with at least X bits that offer the best performance), uint_leastX_t/int_leastX_t (the smallest unsigned/signed types that have at least X bits), uintmax_t/intmax_t (the maximum width integer types), and uintptr_t/intptr_t (the integer types capable of holding pointers). These types are guaranteed to exists unlike the truly fixed width types. The only times you should really use the truly fixed width types are when you are directly interacting with hardware, or you need exact wrap/overflow behavior.

    Do you really hate C programmers that much? :)
  • RossHRossH Posts: 5,476
    DavidZemon wrote: »
    Basically, in the decision tree of "should we do A or B," the node that reads "do whichever one Spin does" should be at the very bottom of the three.

    We are by no means just "doing whatever Spin does", we are just proposing to do a few simple things that are easy and sensible to do to assist Propeller users transition between languages (or even just read a program written in another language).

  • RossHRossH Posts: 5,476
    edited 2019-07-03 23:51
    It's probably easier to comment if we can all see an example in front of us, so here is the current proposal ...
    #ifndef __PROPELLER2__H
    #define __PROPELLER2__H
    
    /*
     * common definitions
     */
    
    #define ANY_COG 0x10
    
    /*
     * common types
     */
    
    // cartesian coordinates
    typedef struct _cartesian {
       int32_t x, y;
    } cartesian;
    
    // polar coordinates
    typedef struct _polar {
       uint32_t r, t;
    } polar;
    
    /*
     * P2 32 Bit Clock Mode (see macros below to construct)
     *
     *      0000_000e_dddddd_mmmmmmmmmm_pppp_cc_ss
     *
     *   e          = XPLL (0 = PLL Off, 1 = PLL On)
     *   dddddd     = XDIV (0 .. 63, crystal divider => 1 .. 64)
     *   mmmmmmmmmm = XMUL (0 .. 1023, crystal multiplier => 1 .. 1024)
     *   pppp       = XPPP (0 .. 15, see macro below)
     *   cc         = XOSC (0 = OFF, 1 = OSC, 2 = 15pF, 3 = 30pF)
     *   ss         = XSEL (0 = rcfast, 1 = rcslow, 2 = XI, 3 = PLL)
     */
    
    // macro to calculate XPPP (1->15, 2->0, 4->1, 6->2 ... 30->14) ...
    #define XPPP(XDIVP) ((((XDIVP)>>1)+15)&0xF)  
    
    // macro to combine XPLL, XDIV, XDIVP, XOSC & XSEL into a 32 bit CLOCKMODE ...
    #define CLOCKMODE(XPLL,XDIV,XMUL,XDIVP,XOSC,XSEL) ((XPLL<<24)+((XDIV-1)<<18)+((XMUL-1)<<8)+(XPPP(XDIVP)<<4)+(XOSC<<2)+XSEL) 
    
    // macro to calculate final clock frequency ...
    #define CLOCKFREQ(XTALFREQ, XDIV, XMUL, XDIVP) ((XTALFREQ)/(XDIV)*(XMUL)/(XDIVP))
    
    /*
     * pre-defined functions
     */
    
    void      _clkset(uint32_t clkmode, uint32_t clkfreq);
    void      _hubset(uint32_t val);
    
    int       _coginit(int cog, void *pgm, void *ptr);
    void      _cogstop(int cog);
    
    int       _cogid(void);
    int       _cogchk(int cog);
    
    int       _locknew(void);
    void      _lockret(int lock);
    
    int       _locktry(int lock);
    int       _lockrel(int lock);
    int       _lockchk(int lock);
    
    void      _cogatn(uint32_t mask);
    int       _pollatn(void);
    int       _waitatn(void);
    
    cartesian _rotxy(cartesian coord, uint32_t t);
    cartesian _polxy(polar coord);
    polar     _xypol(cartesian coord);
    
    uint32_t  _rnd(void);
    
    uint32_t  _cnt(void);
    uint32_t  _pollcnt(uint32_t tick);
    void      _waitcnt(uint32_t tick);
    
    void      _waitx(uint32_t tick);
    
    void      _pinw(int pin, int val);
    void      _pinl(int pin);
    void      _pinh(int pin);
    void      _pinnot(int pin);
    void      _pinrnd(int pin);
    void      _pinf(int pin);
    int       _pin(int pin);
    
    void      _wrpin(int pin, int val);
    void      _wxpin(int pin, int val);
    void      _wypin(int pin, int val);
    void      _akpin(int pin);
    int       _rdpin(int pin);
    int       _rqpin(int pin);
    
    void      _bytemove(void *dst, void *src, uint32_t cnt);
    void      _bytefill(void *dst, uint8_t val, uint32_t cnt);
    void      _wordmove(void *dst, void *src, uint32_t cnt);
    void      _wordfill(void *dst, uint16_t val, uint32_t cnt);
    void      _longmove(void *dst, void *src, uint32_t cnt);
    void      _longfill(void *dst, uint32_t val, uint32_t cnt);
    
    uint32_t  _strsize(void *adr);
    int       _strcomp(void *adra, void *adrb);
    
    #endif
    

    More comments welcome!

    EDIT: _bytefill and _wordfill should accept uint8_t and uint16_t as the value to use.

    EDIT: X & Y are signed in cartesian coordinates.

    EDIT: addresses should use "void *"

    EDIT: remove MAX_COG
  • I like it. Straight forward and to the point.

    Mike
  • Strsize and strcmp are unnecessary aren't they? Other than that, looks good.
  • RossHRossH Posts: 5,476
    DavidZemon wrote: »
    Strsize and strcmp are unnecessary aren't they? Other than that, looks good.

    Strictly, yes. But if you are porting Spin code to C, they might make life a little easier.
  • David BetzDavid Betz Posts: 14,516
    edited 2019-07-03 14:32
    RossH wrote: »
    ersmith wrote: »
    That all sounds good to me, except that I'd like to distinguish the types in some way. If we're not going to use "_t" then perhaps we should make POLAR and CARTESIAN all upper case as well, or captialize at least the first letter, and/or put an underscore in front of them to avoid conflicts with user variables.

    The convention I was suggesting we adopt would be to use an underscore in front of the structure tag, but not the type name - i.e.
    // type for cartesian coordinates
    typedef struct _cartesian {
      long x, y;
    } cartesian;
    
    // type for polar coordinates
    typedef struct _polar {
      long r, t;
    } polar;
    
    

    You can, of course, do it the other way around, but this way around seems to be more in keeping with the convention that the leading underscore implies the implementation-defined entity, whereas the typedef is more of a naming convenience.
    I've never understood why the structure tag and the typedef name have to be different. I don't think that's required by the C standard is it? If so, it doesn't seem to be enforced by any of the compilers I use. However, in this case I think using cartesian_t and polar_t for the typedef names would be more consistent with the stdint.h typedef names. In that case, I don't think the structure tag should have the _t suffix. It doesn't need an _ prefix either though.
  • RossH wrote: »
    DavidZemon wrote: »
    Strsize and strcmp are unnecessary aren't they? Other than that, looks good.

    Strictly, yes. But if you are porting Spin code to C, they might make life a little easier.

    I think we should have a separate header file / library for Spin utilities like bytemove, strsize, etc., and keep propeller2.h for just the hardware specific features.
  • David Betz wrote: »
    RossH wrote: »
    ersmith wrote: »
    That all sounds good to me, except that I'd like to distinguish the types in some way. If we're not going to use "_t" then perhaps we should make POLAR and CARTESIAN all upper case as well, or captialize at least the first letter, and/or put an underscore in front of them to avoid conflicts with user variables.

    The convention I was suggesting we adopt would be to use an underscore in front of the structure tag, but not the type name - i.e.
    // type for cartesian coordinates
    typedef struct _cartesian {
      long x, y;
    } cartesian;
    
    // type for polar coordinates
    typedef struct _polar {
      long r, t;
    } polar;
    
    

    You can, of course, do it the other way around, but this way around seems to be more in keeping with the convention that the leading underscore implies the implementation-defined entity, whereas the typedef is more of a naming convenience.
    I've never understood why the structure tag and the typedef name have to be different. I don't think that's required by the C standard is it? If so, it doesn't seem to be enforced by any of the compilers I use. However, in this case I think using cartesian_t and polar_t for the typedef names would be more consistent with the stdint.h typedef names. In that case, I don't think the structure tag should have the _t suffix. It doesn't need an _ prefix either though.

    I'm pretty sure the C standard specifies that struct names (the "foo" in "struct foo") are in a different namespace from variable and type names, so they won't conflict with those. I think we can use whatever we'd like there, and most modern compilers even let you leave them off entirely to create anonymous structs (great for typedefs).

    I'm more concerned that the actual typedef name not conflict with user variables. So I'd like:
    typedef struct cartesian {
        int32_t x, y;
    } cartesian_t;
    
    Note that the coordinates should be signed, at least for cartesian ones (for polar we could go either way).
  • RossH wrote: »
    It's probably easier to comment if we can all see an example in front of us, so here is the current proposal ...
    Thanks Ross! I think it's coming together quite nicely. A few comments (nothing major, just trying to polish things):
    #define MAX_COG 0x07
    
    I think this should be 0x0f -- the architecture allows for up to 16 COGs, although the current implementation just has 8. Or we could leave this define out entirely; it may be misleading for some programmers, especially if Chip creates 2 or 4 COG versions of the P2.
    // cartesian coordinates
    typedef struct _cartesian {
       uint32_t x, y;
    } cartesian;
    
    As I mentioned above, I think these should be signed and I'd personally prefer "cartesian_t", "Cartesian", or "CARTESIAN" for the type name, to avoid conflict with user variables and types.
    int       _coginit(int cog, uint32_t pgm, uint32_t ptr);
    
    I'd probably use "void *" for pgm and ptr, since that's what the type of a pointer is in C (yes, it happens to be 32 bits, but we might was well use the type system to distinguish pointers and integers).
    int       _pin(int pin);
    
    @cgracey , any thought on renaming this? The function signature alone indicates why "PIN" might not be a good reserved function name. It's not such a big deal in C, where we've put an underscore in front, but in Spin I think "PINGET" or "PINVAL" might be a better name.
    void      _bytemove(uint32_t dst, uint32_t src, uint32_t cnt);
    void      _bytefill(uint32_t dst, uint32_t val, uint32_t cnt);
    void      _wordmove(uint32_t dst, uint32_t src, uint32_t cnt);
    void      _wordfill(uint32_t dst, uint32_t val, uint32_t cnt);
    void      _longmove(uint32_t dst, uint32_t src, uint32_t cnt);
    void      _longfill(uint32_t dst, uint32_t val, uint32_t cnt);
    
    uint32_t  _strsize(uint32_t adr);
    uint32_t  _strcomp(uint32_t adra, uint32_t adrb);
    
    I'd probably put all of these into a "spin2.h" header file (or something similar) and use appropriate pointer types for the src and dst arguments.

    Thanks for putting the draft together, Ross, it does make discussion easier.

    Eric
  • cgraceycgracey Posts: 14,206
    Eric, I really like PIN, CNT, and RND. Putting GET in front of each of those will certainly make user code more verbose, even though it would inhibit the user from naming variables PIN, CNT, and RND.
  • cgracey wrote: »
    Eric, I really like PIN, CNT, and RND. Putting GET in front of each of those will certainly make user code more verbose, even though it would inhibit the user from naming variables PIN, CNT, and RND.

    The thing that bothers me about it is that it kind of conflates the input value of the pin with the pin itself. Writing "pin(56)" makes it seem you're referring to pin 56 in general, rather than specifically the current input value of the pin. Someone might think they could write "pin(56) = 1" instead of "pinh(56)".

    Even a tiny bit longer name like "pini(56)" (pin input) or "pinv(56)" (pin value) seems less likely to cause confusion to the reader.

    I don't think we should try to make the code too concise. Code is much harder to read than to write, so helping the reader is probably more important than saving a few keystrokes. "pininp(56)" is only a little more typing than "pin(56)", and makes it pretty obvious that you're getting the input value. (I like "pinlo" and "pinhi" better than "pinl" and "pinh" for the same reason). There is a happy medium, of course: "pin_input_value(56)" is more informative, but even I think that one's too verbose :).

    Also, of course, "pin" is a super common variable / constant name in existing code, so using it will make porting code from Spin1 and from other languages harder.
  • cgraceycgracey Posts: 14,206
    ersmith wrote: »
    cgracey wrote: »
    Eric, I really like PIN, CNT, and RND. Putting GET in front of each of those will certainly make user code more verbose, even though it would inhibit the user from naming variables PIN, CNT, and RND.

    The thing that bothers me about it is that it kind of conflates the input value of the pin with the pin itself. Writing "pin(56)" makes it seem you're referring to pin 56 in general, rather than specifically the current input value of the pin. Someone might think they could write "pin(56) = 1" instead of "pinh(56)".

    Even a tiny bit longer name like "pini(56)" (pin input) or "pinv(56)" (pin value) seems less likely to cause confusion to the reader.

    I don't think we should try to make the code too concise. Code is much harder to read than to write, so helping the reader is probably more important than saving a few keystrokes. "pininp(56)" is only a little more typing than "pin(56)", and makes it pretty obvious that you're getting the input value. (I like "pinlo" and "pinhi" better than "pinl" and "pinh" for the same reason). There is a happy medium, of course: "pin_input_value(56)" is more informative, but even I think that one's too verbose :).

    Also, of course, "pin" is a super common variable / constant name in existing code, so using it will make porting code from Spin1 and from other languages harder.

    I understand what you are saying. I just wish there was a really good simple name for pin input.
  • RossHRossH Posts: 5,476
    ersmith wrote: »
    I'm pretty sure the C standard specifies that struct names (the "foo" in "struct foo") are in a different namespace from variable and type names, so they won't conflict with those. I think we can use whatever we'd like there, and most modern compilers even let you leave them off entirely to create anonymous structs (great for typedefs).

    I'm more concerned that the actual typedef name not conflict with user variables. So I'd like:
    typedef struct cartesian {
        int32_t x, y;
    } cartesian_t;
    

    You can indeed use the same name for both the tag and the type name in C. But not in C++ I believe. In any case, most C coding standard documents seem to think its a bad idea - and it is certainly confusing. I think the consensus is that using different names prompts you to remember that one requires the "struct" keyword and one doesn't.
    Note that the coordinates should be signed, at least for cartesian ones (for polar we could go either way).

    Yes, I wondered about that.
  • RossHRossH Posts: 5,476
    ersmith wrote: »
    int       _coginit(int cog, uint32_t pgm, uint32_t ptr);
    
    I'd probably use "void *" for pgm and ptr, since that's what the type of a pointer is in C (yes, it happens to be 32 bits, but we might was well use the type system to distinguish pointers and integers).

    Yes, good point. I will amend it. And also remove MAX_COG.
  • RossHRossH Posts: 5,476
    I have not yet used the P2 smart pins. Perhaps someone who has could come up with a meaningful set of names (and possibly macros) for the various smart pin configurations?
  • RossHRossH Posts: 5,476
    I'm not sure if this is relevant to all C compilers, but Catalina will also have the following definitions in its version of propeller2.h ...
    /*
     * special cog register names
     */
    
    extern volatile uint32_t IJMP3; 
    extern volatile uint32_t IRET3;
    extern volatile uint32_t IJMP2;
    extern volatile uint32_t IRET2;
    extern volatile uint32_t IJMP1;
    extern volatile uint32_t IRET1;
    extern volatile uint32_t PA;
    extern volatile uint32_t PB;
    extern volatile uint32_t PTRA;
    extern volatile uint32_t PTRB;
    extern volatile uint32_t DIRA;
    extern volatile uint32_t DIRB;
    extern volatile uint32_t OUTA;
    extern volatile uint32_t OUTB;
    extern volatile uint32_t INA;
    extern volatile uint32_t INB;
    

    These register names are known to the compiler, which will ensure that when the propeller header file is included, these names are reserved and will map to the correct cog registers (if the propeller header file is not included, the names can be used as normal variable names). I do it this way because always reserving them would lead to potential name collisions, but including the propeller header file is a fair indication that the program has been specifically written for the propeller.
  • samuellsamuell Posts: 554
    edited 2019-07-04 01:07
    If you are going to be using stdint.h types you really should be consistent and use them exclusively instead of the built in integer types. In addition to uintX_t/intX_t there are unit_fastX_t/int_fastX_t (the unsigned/signed types with at least X bits that offer the best performance), uint_leastX_t/int_leastX_t (the smallest unsigned/signed types that have at least X bits), uintmax_t/intmax_t (the maximum width integer types), and uintptr_t/intptr_t (the integer types capable of holding pointers). These types are guaranteed to exists unlike the truly fixed width types. The only times you should really use the truly fixed width types are when you are directly interacting with hardware, or you need exact wrap/overflow behavior.
    Something along the lines of this?
    https://en.cppreference.com/w/c/types/integer

    It would be nice to see Propeller C supporting the C99 standard fully, with the "bool" type defined, as well as exact length uintx_t types and also fast/least types.

    Regarding your last point, I can imagine many situations where you need to use exact length types, for example, interfacing with a DAC via I2C or SPI. Many DACs use shift registers, and the data length needs to be "cut exact" so that no more data is sent there, causing unwanted shifts and erroneous values.
    cgracey wrote: »
    ersmith wrote: »
    cgracey wrote: »
    Eric, I really like PIN, CNT, and RND. Putting GET in front of each of those will certainly make user code more verbose, even though it would inhibit the user from naming variables PIN, CNT, and RND.

    The thing that bothers me about it is that it kind of conflates the input value of the pin with the pin itself. Writing "pin(56)" makes it seem you're referring to pin 56 in general, rather than specifically the current input value of the pin. Someone might think they could write "pin(56) = 1" instead of "pinh(56)".

    Even a tiny bit longer name like "pini(56)" (pin input) or "pinv(56)" (pin value) seems less likely to cause confusion to the reader.

    I don't think we should try to make the code too concise. Code is much harder to read than to write, so helping the reader is probably more important than saving a few keystrokes. "pininp(56)" is only a little more typing than "pin(56)", and makes it pretty obvious that you're getting the input value. (I like "pinlo" and "pinhi" better than "pinl" and "pinh" for the same reason). There is a happy medium, of course: "pin_input_value(56)" is more informative, but even I think that one's too verbose :).

    Also, of course, "pin" is a super common variable / constant name in existing code, so using it will make porting code from Spin1 and from other languages harder.

    I understand what you are saying. I just wish there was a really good simple name for pin input.
    I hope there will be a function for reading the latched value as well as the value present in the pin (valid for output pins, at least). This was possible to do in the P1 by reading OUTA in the case of latches and INA in the case of pins.

    Kind regards, Samuel Lourenço


  • RossHRossH Posts: 5,476
    edited 2019-07-04 02:17
    samuell wrote: »
    It would be nice to see Propeller C supporting the C99 standard fully

    I doubt any propeller C compiler will ever fully implement the C99 standard. For a start, the Propeller has no native 64 bit types, and why would you want to simulate them?

    Ross.
  • samuell wrote: »
    Something along the lines of this?
    https://en.cppreference.com/w/c/types/integer

    That is what I was referring to. Seeing that there was some hangup in this thread on the fact that an implementation isn't required to support the truly fixed width types and performance considerations, which to me at least indicates some lack of familiarity with stdint.h, I decided to explicitly point out some of what is there. I should have provided a link to the documentation for the header as well.
    samuell wrote: »
    It would be nice to see Propeller C supporting the C99 standard fully, with the "bool" type defined, as well as exact length uintx_t types and also fast/least types.

    Indeed it would. Even better would be C11 support (the multi-threading additions in particular would be quite relevant) and beyond. I can see many people being put off if there is no support for anything beyond C89/90.
    samuell wrote: »
    Regarding your last point, I can imagine many situations where you need to use exact length types, for example, interfacing with a DAC via I2C or SPI. Many DACs use shift registers, and the data length needs to be "cut exact" so that no more data is sent there, causing unwanted shifts and erroneous values.

    Those are cases of directly interacting with hardware and thus you do want to use fixed width types as you pointed out and I had stated.
  • I believe language level support is out of scope, isn't it? That's entirely dependent on choosing the right compiler (and version) to port.
  • RossHRossH Posts: 5,476
    Seeing that there was some hangup in this thread on the fact that an implementation isn't required to support the truly fixed width types and performance considerations, which to me at least indicates some lack of familiarity with stdint.h

    Not lack of familiarity so much as active dislike of stdint.h in my case :)

    However, I think we have found the appropriate compromise - i.e. use uintN_t where a specific number of bits is appropriate, and use the fundamental C types elsewhere. This way, we maximize compatibility but minimize the loss of readability.
  • RossH wrote: »
    extern volatile uint32_t IJMP3; 
    extern volatile uint32_t IRET3;
    extern volatile uint32_t IJMP2;
    extern volatile uint32_t IRET2;
    extern volatile uint32_t IJMP1;
    extern volatile uint32_t IRET1;
    extern volatile uint32_t PA;
    extern volatile uint32_t PB;
    extern volatile uint32_t PTRA;
    extern volatile uint32_t PTRB;
    extern volatile uint32_t DIRA;
    extern volatile uint32_t DIRB;
    extern volatile uint32_t OUTA;
    extern volatile uint32_t OUTB;
    extern volatile uint32_t INA;
    extern volatile uint32_t INB;
    

    These register names are known to the compiler, which will ensure that when the propeller header file is included, these names are reserved and will map to the correct cog registers (if the propeller header file is not included, the names can be used as normal variable names). I do it this way because always reserving them would lead to potential name collisions, but including the propeller header file is a fair indication that the program has been specifically written for the propeller.

    I'm not sure how Catalina works, but for some compilers once a global name is used (for example in a library) then any other use of it could conflict. That is, for some compilers if we use OUTA or INA in a library function, e.g. to bit-bang serial, then any user variable named OUTA or INA will conflict with it. So it would be safer to put an underscore in front of those names. We could then provide a #define for an alias -- that's OK, since the preprocessor only affects things in the single file being compiled. That is:
    extern volatile uint32_t _IJMP3;
    #define IJMP3 _IJMP3
    
    would accomplish pretty much the same thing as your original proposal, without conflicting with user variables.

    (I'm pretty sure the C99 standard, at least, imposes restrictions on what global identifiers can be defined in the standard library, and it would be nice to be able to write libc mostly in C.)

    At least one set of compilers (the P2 RISCV emulation ones) won't be able to directly access the P2 registers as variables, but will be able to access them via functions. So we might want to consider get/set macros for some of these registers.
  • RossH wrote: »
    samuell wrote: »
    It would be nice to see Propeller C supporting the C99 standard fully

    I doubt any propeller C compiler will ever fully implement the C99 standard. For a start, the Propeller has no native 64 bit types, and why would you want to simulate them?

    Ross.
    Because I've already hit a brick wall when using 32-bit types. My prime number calculation program clearly proves it. They might not be as efficient as 32-bit types on a P2, and I understand that. But there is a reason why 64-bit types were supported even on 32-bit machines, about 20 years ago. So, why not?
    RossH wrote: »
    Seeing that there was some hangup in this thread on the fact that an implementation isn't required to support the truly fixed width types and performance considerations, which to me at least indicates some lack of familiarity with stdint.h

    Not lack of familiarity so much as active dislike of stdint.h in my case :)

    However, I think we have found the appropriate compromise - i.e. use uintN_t where a specific number of bits is appropriate, and use the fundamental C types elsewhere. This way, we maximize compatibility but minimize the loss of readability.
    Mind that "stdint.h" was an afterthought, and many typical programs don't need them. But I've already have shown you cases where it is essential to have fixed types, and therefore, "stdint.h" is useful. On another post, I even uploaded a code example, which you considered a good one, if I recall correctly.

    If you don't wish to include "stdint.h", don't include it. Speaking of which, "propeller.h" on SimpleIDE seems to include almost every dependency, which I don't think it is a good idea. People should learn to code in C correctly and make the decision by themselves on what to include and not to include. "stdint.h", "stddef.h", "math.h", "stdio.h" and any others should be included only when needed, by the code, explicitly. When not, the compiler should emit a warning, at least. Lets not facilitate (or jump into the Arduino bandwagon, that pretty much screwed its C implementation for the sake of simplicity).

    That means "propeller2.h" should include only what is needed for it to perform, and should only handle P2 related stuff (I/O pin direction and latches/inputs/outputs, smartpins, DACs, ADCs, CORDIC stuff, registers and the works). And that is plenty to handle (don't need to make system.d out of it).

    Kind regards, Samuel Lourenço
  • RossH wrote: »
    samuell wrote: »
    It would be nice to see Propeller C supporting the C99 standard fully

    I doubt any propeller C compiler will ever fully implement the C99 standard. For a start, the Propeller has no native 64 bit types, and why would you want to simulate them?

    PropGCC came very close to fully implementing C99, including int64_t (useful for holding cycle counters longer than a minute or so, or for intermediate results of multiplcation). The only compiler exception was some missing #pragmas for floating point rounding. There were a few minor library issues as well (mostly floating point related).

    But as David said, this isn't really the place for a language discussion, except insofar as it impacts propeller2.h. Catalina does a great job of implementing the things that users need in practice, as does GCC, and I hope someday fastspin's C support will be of similar quality.

    Eric
Sign In or Register to comment.