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

propeller2.h for C compilers

1234689

Comments

  • RossH wrote: »
    ersmith wrote: »
    (1) _clockfreq() and _clockmode() macros. These are the same as the ones Catalina has.

    The names are the same, but these are not macros in Catalina, they are functions. But they could be macros on some compilers.

    OK, we can declare them as:
    uint32_t _clockfreq(void);
    uint32_t _clockmode(void);
    
    instead. Although ideally all the compilers would use the same implementation as the macros I posted (Tachyon, p2gcc, fastspin, and micropython all keep the clock frequency at 0x14 in order to facilitate the work-around to clock changing necessary on the first P2 silicon).

  • DavidZemon wrote: »
    What if the we wrap the "#define DIRA _DIRA" with a guard, such as "#ifndef NO_P1_COMPAT"? Or, personally.... I'd prefer that it be off by default, but I'm guessing that won't be a very popular opinion: "#ifdef P1_COMPAT"
    I'm OK with leaving those defines out entirely (and putting them in a <propeller.h> header file instead). I'm also OK with leaving them in, so they're only going to affect the actual file that #includes <propeller2.h>. @RossH , do you have a preference there.
    "int _cogstart(void (*func)(void *), void *arg, void *arg, uint32_t stack_size);"
    incorrectly named "stack" parameter

    Whoops, yes, should be:
    int _cogstart(void (*func)(void *), void *arg, void *stack_base, uint32_t stack_size);
    
    I thought of calling this _coginit_C, like Catalina does, but this version has a different signature. The function it takes has a parameter (to make running the same code in multiple threads easier) and the stack base and size are explicit so that it will work regardless of the compiler's stack growth order.
    Did you really intend to type "stack_size" and not "stackSize"? I'm fine either way, just want to make sure it was on purpose.
    We can use either convention, I don't care either way (especially since the actual argument names are basically ignored by the compiler and are just there for documentation).
    "uint32_t _rnd(void);"
    does this need to be abbreviated, or can it be spelled out as _random()? I first read it as "round()" and before realizing that was probably incorrect. Or maybe the standard C function, rand(), could be used and no special Propeller function is necessary?
    "_rnd" gives direct access to the hardware random number generator. This may or may not be used by the C rand() function, that's up to the library writers.

    Thanks for your feedback,
    Eric
  • RossHRossH Posts: 5,336
    ersmith wrote: »
    @RossH , do you have a preference there.

    Not a strong one. I'd be ok with putting a #ifdef ... #endif guard around them so you have to specifically enable them if you want P1 compatibility.


  • ersmith wrote: »
    DavidZemon wrote: »
    What if the we wrap the "#define DIRA _DIRA" with a guard, such as "#ifndef NO_P1_COMPAT"? Or, personally.... I'd prefer that it be off by default, but I'm guessing that won't be a very popular opinion: "#ifdef P1_COMPAT"
    I'm OK with leaving those defines out entirely (and putting them in a <propeller.h> header file instead). I'm also OK with leaving them in, so they're only going to affect the actual file that #includes <propeller2.h>. @RossH , do you have a preference there.

    Ah yes, I forgot you've mentioned this multiple times, and each time I forgot to give you my rebuttal :smile:. For header-only libraries (like PropWare... I may be biased), it propeller2.h will be almost everywhere. That's certainly a valid argument not to use header-only libraries, but that's not the point.
    ersmith wrote: »
    Did you really intend to type "stack_size" and not "stackSize"? I'm fine either way, just want to make sure it was on purpose.
    We can use either convention, I don't care either way (especially since the actual argument names are basically ignored by the compiler and are just there for documentation).

    I prefer camelCase, but like you said, not important at all.
  • RossHRossH Posts: 5,336
    ersmith wrote: »
    OK, here's my latest proposal. Again, very close to what @RossH had posted earlier:
    ...
    uint32_t  _cnt(void);
    uint32_t  _cnth(void); /* high 32 bits of CNT, on processors that support it */
    ...
    

    I believe that you must fetch both 32 bit counters in consecutive instructions if you want the true "current" value, so I think we should add:
    // Both 32 bit counters:
    typedef struct _hl_counters {
       int32_t high, low;
    }  hl_counters_t;
    
    hl_counters_t cnthl(); /* Fetch both upper and lower 32 bits of counter. */
    
    

    This should return "high" as zero if the processor does not support it.

    I don't care about the particular names.

    Ross.
  • evanhevanh Posts: 15,126
    Yes, it's only an atomic reading when executed as an instruction pair, with high half leading low half.

    It might be worth keeping the low half only function though. Much of the time that one instruction is plenty for short timings.

  • RossHRossH Posts: 5,336
    evanh wrote: »
    Yes, it's only an atomic reading when executed as an instruction pair, with high half leading low half.

    It might be worth keeping the low half only function though. Much of the time that one instruction is plenty for short timings.

    Yes, I would have all three - i.e. cnt(), cnth() and cnthl().
  • Cluso99Cluso99 Posts: 18,066
    Not sure if running hubexec will ALWAYS work for the two successive get cnt instructions ???
  • Cluso99 wrote: »
    Not sure if running hubexec will ALWAYS work for the two successive get cnt instructions ???

    If there's a problem with the atomic reads we can always implement cnthl() by fetching high1, low, high2, comparing high1 with high2, and repeating until they are the same. In the vast majority of the cases they will be, so the branch won't be taken.
  • evanhevanh Posts: 15,126
    edited 2019-07-18 12:25
    Yes, hubexec will be fine very much because they are consecutive instructions. Straight line fetches never stall - even if the cog could exec at one instruction per clock, the FIFO would still keep up flawlessly.

  • Here's a new proposal. I've added _cnthl with a slight modification, that the struct has the low then the high (so it should match the bit layout of 64 bit integers on compilers that have those).

    I've also added "_rev()" (to reverse a value), "_clz()" (to count leading zeros), and "_popcount()" (to count the number of ones that are set in a value), and "_qexp()" and "_qlog()". These should be useful in doing floating point.
    #ifndef __PROPELLER2__H
    #define __PROPELLER2__H
    
    #include <stdint.h>
    
    /*
     * special cog register names (the actual types are compiler dependent)
     * Not all compilers will necessarily support these
     */
    
    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;
    
    #ifdef _PROP1_COMPATIBLE
    /*
     * For compatibility with previous programs, where the special register
     * names did not have the leading underscore, we provide the #defines 
     * to allow older programs to work with the new names:
     */
    #define DIRA _DIRA
    #define INA  _INA
    #define OUTA _OUTA
    #define DIRB _DIRB
    #define INB  _INB
    #define OUTB _OUTB
    #endif
    
    /*
     * common definitions
     */
    
    #define ANY_COG 0x10
    
    /*
     * common types
     */
    
    // cartesian coordinates
    typedef struct _cartesian {
       int32_t x, y;
    } cartesian_t;
    
    // polar coordinates
    typedef struct _polar {
       uint32_t r, t;
    } polar_t;
    
    // 64 bit counter
    typedef struct _counter64 {
        uint32_t low, high;
    } counter64_t;
    
    /*
     * 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);
    
    /* start PASM code in another COG */
    int       _coginit(int cog, void *pgm, void *ptr);
    #define _cognew(pgm, ptr) _coginit(ANY_COG, pgm, ptr)
    
    /* start C code in another COG */
    int _cogstart(void (*func)(void *), void *arg, void *stack_base, uint32_t stack_size);
    
    /* stop/check status of COGs */
    void      _cogstop(int cog);
    int       _cogchk(int cog);
    
    int       _cogid(void);
    
    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);
    
    /* CORDIC instructions */
    cartesian_t _rotxy(cartesian_t coord, uint32_t t);
    cartesian_t _polxy(polar_t coord);
    polar_t     _xypol(cartesian_t coord);
    uint32_t    _qexp(uint32_t val);
    uint32_t    _qlog(uint32_t val);
    
    /* miscellaneous instructions */
    uint32_t  _rnd(void);
    uint32_t  _rev(uint32_t val);
    int       _popcount(uint32_t val); /* number of set bits in val */
    int       _clz(uint32_t val);      /* count leading 0s in val */
    
    /* counter related functions */
    uint32_t  _cnt(void);
    uint32_t  _cnth(void); /* high 32 bits of CNT, on processors that support it */
    counter64_t _cnthl();  /* fetch both together */
    
    uint32_t  _pollcnt(uint32_t tick);
    void      _waitcnt(uint32_t tick);
    
    void      _waitx(uint32_t delay);
    
    /* regular pin I/O */
    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);
    
    /* smart pin controls */
    void      _wrpin(int pin, uint32_t val);
    void      _wxpin(int pin, uint32_t val);
    void      _wypin(int pin, uint32_t val);
    void      _akpin(int pin);
    uint32_t  _rdpin(int pin);
    uint32_t  _rqpin(int pin);
    
    /* access to previously set clock mode and frequency */
    extern uint32_t _clockfreq(void);
    extern uint32_t _clockmode(void);
    
    #define _clockfreq() (*(uint32_t *)0x14)
    #define _clockmode() (*(uint32_t *)0x18)
    
    #endif
    
  • RossHRossH Posts: 5,336
    ersmith wrote: »
    I've also added "_rev()" (to reverse a value), "_clz()" (to count leading zeros), and "_popcount()" (to count the number of ones that are set in a value), and "_qexp()" and "_qlog()". These should be useful in doing floating point.

    I don't see the point of putting individual instructions in propeller2.h. I think we should (largely) stick to those things that are functions in Spin2 (to ensure they do the same thing across different compilers or languages), or which - like cnthl - are difficult to reproduce if you are unaware of the constraints involved.

    If you need to use a specific individual instruction you can do inline assembly, or call an assembly language function.

    Otherwise, I worry that we will never finish! :)

    Ross.
  • RossH wrote: »
    ersmith wrote: »
    I've also added "_rev()" (to reverse a value), "_clz()" (to count leading zeros), and "_popcount()" (to count the number of ones that are set in a value), and "_qexp()" and "_qlog()". These should be useful in doing floating point.

    I don't see the point of putting individual instructions in propeller2.h. I think we should (largely) stick to those things that are functions in Spin2 (to ensure they do the same thing across different compilers or languages), or which - like cnthl - are difficult to reproduce if you are unaware of the constraints involved.
    The only reason that "_rev()" is not a function in Spin is that it's a built in operator in Spin. So I think that one, at least, should be in propeller2.h, and probably any other instructions that correspond to operators that aren't native to C. For example, "_clz()" is basically the Spin encode operator, but with an offset so it corresponds to the GCC __builtin_clz() function; I could be persuaded to just do a raw "_encod()" instruction instead.
    If you need to use a specific individual instruction you can do inline assembly, or call an assembly language function.
    I have some sympathy for this. However, different compilers have different ways of doing inline assembly and different syntaxes for assembly language in general. So if we want to promote portable C code that can access the P2 hardware functionality then I think we want to put functional equivalents of most of the basic instructions, or at lesat the ones that will be useful in common algorithms. I'm OK with taking out "_qexp()" and "_qlog()" (these are probably better expressed in C with floating point) but I think providing equivalents for the Spin integer operations would be useful.

    Regards,
    Eric
  • RossHRossH Posts: 5,336
    ersmith wrote: »
    ... I think we want to put functional equivalents of most of the basic instructions ...

    We should not be using C - or any high-level language, for that matter - to just code individual assembler instructions.

    That's what assemblers are for :(
  • RossH wrote: »
    ersmith wrote: »
    ... I think we want to put functional equivalents of most of the basic instructions ...

    We should not be using C - or any high-level language, for that matter - to just code individual assembler instructions.

    That's what assemblers are for :(

    Well, I prefer to avoid assembler as much as possible, so if I can do things in C so much the better. But that is a matter of taste I suppose. Could we at least agree to put equivalents for the Spin builtin operators into the header file? That way programmers (and tools like spin2cpp) will be able to easily convert Spin to C, and do so in a way that's portable to all of the P2 compilers.
  • RossHRossH Posts: 5,336
    ersmith wrote: »
    Could we at least agree to put equivalents for the Spin builtin operators into the header file? That way programmers (and tools like spin2cpp) will be able to easily convert Spin to C, and do so in a way that's portable to all of the P2 compilers.

    Yes.
  • OK, here's an updated proposal with the CORDIC functions slimmed down and with Spin operators that don't have natural C equivalents implemented:
    #ifndef __PROPELLER2__H
    #define __PROPELLER2__H
    
    #include <stdint.h>
    
    /*
     * special cog register names (the actual types are compiler dependent)
     * Not all compilers will necessarily support these
     */
    
    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;
    
    #ifdef _PROP1_COMPATIBLE
    /*
     * For compatibility with previous programs, where the special register
     * names did not have the leading underscore, we provide the #defines 
     * to allow older programs to work with the new names:
     */
    #define DIRA _DIRA
    #define INA  _INA
    #define OUTA _OUTA
    #define DIRB _DIRB
    #define INB  _INB
    #define OUTB _OUTB
    #endif
    
    /*
     * common definitions
     */
    
    #define ANY_COG 0x10
    
    /*
     * common types
     */
    
    // cartesian coordinates
    typedef struct _cartesian {
       int32_t x, y;
    } cartesian_t;
    
    // polar coordinates
    typedef struct _polar {
       uint32_t r, t;
    } polar_t;
    
    // 64 bit counter
    typedef struct _counter64 {
        uint32_t low, high;
    } counter64_t;
    
    /*
     * 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);
    
    /* start PASM code in another COG */
    int       _coginit(int cog, void *pgm, void *ptr);
    #define _cognew(pgm, ptr) _coginit(ANY_COG, pgm, ptr)
    
    /* start C code in another COG */
    int _cogstart(void (*func)(void *), void *arg, void *stack_base, uint32_t stack_size);
    
    /* stop/check status of COGs */
    void      _cogstop(int cog);
    int       _cogchk(int cog);
    
    int       _cogid(void);
    
    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);
    
    /* CORDIC instructions */
    cartesian_t _rotxy(cartesian_t coord, uint32_t t);
    cartesian_t _polxy(polar_t coord);
    polar_t     _xypol(cartesian_t coord);
    
    /* miscellaneous operations */
    uint32_t  _rnd(void);
    uint32_t  _rev(uint32_t val);   /* like Spin reverse operator */
    int       _encod(uint32_t val); /* Spin encode operator */
    uint32_t  _isqrt(uint32_t val); /* Spin integer square root */
    
    /* counter related functions */
    uint32_t  _cnt(void);
    uint32_t  _cnth(void); /* high 32 bits of CNT, on processors that support it */
    counter64_t _cnthl();  /* fetch both together */
    
    uint32_t  _pollcnt(uint32_t tick);
    void      _waitcnt(uint32_t tick);
    
    void      _waitx(uint32_t delay);
    
    /* regular pin I/O */
    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);
    
    /* smart pin controls */
    void      _wrpin(int pin, uint32_t val);
    void      _wxpin(int pin, uint32_t val);
    void      _wypin(int pin, uint32_t val);
    void      _akpin(int pin);
    uint32_t  _rdpin(int pin);
    uint32_t  _rqpin(int pin);
    
    /* access to previously set clock mode and frequency */
    extern uint32_t _clockfreq(void);
    extern uint32_t _clockmode(void);
    
    #define _clockfreq() (*(uint32_t *)0x14)
    #define _clockmode() (*(uint32_t *)0x18)
    
    #endif
    

    I didn't bother with rotate operators because there's already a common C idiom for rotation and I suspect optimizers will recognize it. Similarly, the usual MIN/MAX code can probably be optimized by the compiler, and the Spin decode operator "|<" is trivially implemented in C.
  • RossHRossH Posts: 5,336
    ersmith wrote: »
    OK, here's an updated proposal with the CORDIC functions slimmed down and with Spin operators that don't have natural C equivalents implemented ...

    That looks manageable! :)

    I think the main thing we are missing now is all the smart pin configuration constants. I've not used the smart pins yet - anyone else want to have a go?
  • evanhevanh Posts: 15,126
    .
    RossH wrote: »
    I think the main thing we are missing now is all the smart pin configuration constants. I've not used the smart pins yet - anyone else want to have a go?
    I can probably help. There is distinct fields of configuration in the WRPIN instruction. Not all of it can be classed as just smartpins.

    Might have to give me some examples of formatting of the header file too.

  • RossHRossH Posts: 5,336
    evanh wrote: »
    Might have to give me some examples of formatting of the header file too.

    Here is an example showing how Catalina defines the clock modes on the P1 and the P2 (some of this is already included in the proposed propeller2.h):
    #ifdef __CATALINA_P2
    
    /*
     * 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)
     */
    
    // constants for 'e' field
    #define XPLL_OFF 0x0
    #define XPLL_ON  0x1
    
    // constants for 'cc' field:
    #define XOSC_OFF  0x0
    #define XOSC_OSC  0x1
    #define XOSC_15PF 0x2
    #define XOSC_30PF 0x3
    
    // constants for 'ss' field:
    #define XSEL_RCFAST 0x0
    #define XSEL_RCSLOW 0x1
    #define XSEL_XI     0x2
    #define XSEL_PLL    0x3
    
    // 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))
    
    #else
    
    /*
     * P1 clock modes
     */
    #define RCFAST 0x00
    #define RCSLOW 0x01
    #define XINPUT 0x02
    #define XTAL_1 0x2A
    #define XTAL_2 0x32
    #define XTAL_3 0x3A
    #define PLL1X  0x41
    #define PLL2X  0x42
    #define PLL4X  0x43
    #define PLL8X  0x44
    #define PLL16X 0x45
    
    #endif
    
    

    Note: C doesn't have binary constants, so everything must be done in hexadecimal.

    The P1 constants are very simple and therefore don't need macros to construct the final modes.

    The P2 is more complex, so we have macros to help.

    I imagine the smart pins will need both constants and macros.
  • evanhevanh Posts: 15,126
    edited 2019-07-20 04:16
    Hmm, well I guess, for a starters, we need some shorter names for the smartpin mode list:
    %MMMMM:	00000 = smart pin off (default)
    	00001 = long repository (P[12:10] != %101)
    	00010 = long repository (P[12:10] != %101)
    	00011 = long repository (P[12:10] != %101)
    
    	00001 = DAC noise (P[12:10] = %101)
    	00010 = DAC 16-bit dither, noise (P[12:10] = %101)
    	00011 = DAC 16-bit dither, PWM (P[12:10] = %101)
    
    	00100* = pulse/cycle output
    	00101* = transition output
    	00110* = NCO frequency
    	00111* = NCO duty
    	01000* = PWM triangle
    	01001* = PWM sawtooth
    	01010* = PWM switch-mode power supply, V and I feedback
    	01011 = periodic/continuous: A-B quadrature encoder
    	01100 = periodic/continuous: inc on A-rise & B-high
    	01101 = periodic/continuous: inc on A-rise & B-high / dec on A-rise & B-low
    	01110 = periodic/continuous: inc on A-rise {/ dec on B-rise}
    	01111 = periodic/continuous: inc on A-high {/ dec on B-high}
    	10000 = time A-states
    	10001 = time A-highs
    	10010 = time X A-highs/rises/edges -or- timeout a-/high/rise/edge
    	10011 = for X periods, count time
    	10100 = for X periods, count states
    	10101 = for periods in X+ clocks, count time
    	10110 = for periods in X+ clocks, count states
    	10111 = for periods in X+ clocks, count periods
    	11000* = USB host, low-speed (even/odd pin pair = DM/DP)
    	11001* = USB host, high-speed (even/odd pin pair = DM/DP)
    	11010* = USB device, low-speed (even/odd pin pair = DM/DP)
    	11011* = USB device, high-speed (even/odd pin pair = DM/DP)
    	11100* = sync serial transmit (A-data, B-clock)
    	11101 = sync serial receive (A-data, B-clock)
    	11110* = async serial transmit (baudrate)
    	11111 = async serial receive (baudrate)
    
    	* OUT signal overridden
    

    EDIT: Ha, and there is a few changes for the final silicon too. Eg: USB high and low speeds are merged and those two freed up modes are for specialised ADC digital filtering.
  • evanhevanh Posts: 15,126
    edited 2019-07-20 08:34
    Okay, first hack (I gave up on short names for all the timer modes):
    /*
     * Smartpin modes
     *
     *         30    25     20   15   10      5     0
     *          |     |      |    |    |      |     |
     *  D/# = %AAAA_BBBB_FFF_PPPPPPPPPPPPP_TT_MMMMM_0
     */
    
    #define SP_OFF		0x00			// M = %00000 = smart pin off (default)
    
    #define SP_REPOSITORY	0x02			// M = %00001 = long repository (P[12:10] != %101)
    
    #define SP_DAC_RANDOM	(0x02+(0x05<<18))	// M = %00001 = DAC noise (P[12:10] = %101)
    #define SP_DAC_DITH_RND	(0x04+(0x05<<18))	// M = %00010 = DAC 16-bit dither, noise (P[12:10] = %101)
    #define SP_DAC_DITH_PWM (0x06+(0x05<<18))	// M = %00011 = DAC 16-bit dither, PWM (P[12:10] = %101)
    
    #define SP_PULSES	(0x08+0x40)		// M = %00100* = pulse/cycle output
    #define SP_STEPS	(0x0a+0x40)		// M = %00101* = transition output
    #define SP_NCO_FREQ	(0x0c+0x40)		// M = %00110* = NCO frequency
    #define SP_NCO_DUTY	(0x0e+0x40)		// M = %00111* = NCO duty
    #define SP_PWM_TRI	(0x10+0x40)		// M = %01000* = PWM triangle
    #define SP_PWM_SAW	(0x12+0x40)		// M = %01001* = PWM sawtooth
    #define SP_PWM_SAW_FB	(0x14+0x40)		// M = %01010* = PWM switch-mode power supply, V and I feedback
    
    #define SP_CNT_QUAD	0x16			// M = %01011 = periodic/continuous: A-B quadrature encoder
    
    #define SP_CNT_UP_ENA	0x18			// M = %01100 = periodic/continuous: inc on A-rise & B-high
    #define SP_CNT_UP_DIR	0x1a			// M = %01101 = periodic/continuous: inc on A-rise & B-high / dec on A-rise & B-low
    #define SP_CNT_UP	0x1c			// M = %01110 = periodic/continuous: inc on A-rise
    #define SP_CNT_UP_DN	0x1c			// M = %01110 = periodic/continuous: inc on A-rise / dec on B-rise
    
    #define SP_ACC_UP	0x1e			// M = %01111 = periodic/continuous: inc on A-high
    #define SP_ACC_UP_DN	0x1e			// M = %01111 = periodic/continuous: inc on A-high / dec on B-high
    
    #define SP_TIM_STEP	0x20			// M = %10000 = time A-states
    #define SP_TIM_PULSE	0x22			// M = %10001 = time A-highs
    #define SP_TIM_1	0x24			// M = %10010 = time X A-highs/rises/edges
    #define SP_TIM_2	0x24			// M = %10010 = timeout X A-highs/rises/edges
    #define SP_TIM_3	0x26			// M = %10011 = for X periods, count time
    #define SP_TIM_4	0x28			// M = %10100 = for X periods, count states
    #define SP_TIM_5	0x2a			// M = %10101 = for periods in X+ clocks, count time
    #define SP_TIM_6	0x2c			// M = %10110 = for periods in X+ clocks, count states
    #define SP_TIM_7	0x2e			// M = %10111 = for periods in X+ clocks, count periods
    
    #define SP_USB_HOSTL	(0x30+0x40)		// M = %11000* = USB host, low-speed (even/odd pin pair = DM/DP)
    #define SP_USB_HOSTH	(0x32+0x40)		// M = %11001* = USB host, high-speed (even/odd pin pair = DM/DP)
    #define SP_USB_DEVL	(0x34+0x40)		// M = %11010* = USB device, low-speed (even/odd pin pair = DM/DP)
    #define SP_USB_DEVH	(0x36+0x40)		// M = %11011* = USB device, high-speed (even/odd pin pair = DM/DP)
    #define SP_SSER_TX	(0x38+0x40)		// M = %11100* = sync serial transmit (A-data, B-clock)
    #define SP_SSER_RX	0x3a			// M = %11101 = sync serial receive (A-data, B-clock)
    #define SP_ASER_TX	(0x3c+0x40)		// M = %11110* = async serial transmit (baudrate)
    #define SP_ASER_RX	0x3e			// M = %11111 = async serial receive (baudrate)
    
    

    EDIT: Plural SP_STEPS/SP_PULSES.
  • RossHRossH Posts: 5,336
    evanh wrote: »
    Okay, first hack ...

    A good start, but it seems a little opaque because of the unexplained "magic numbers" in various places.

    For instance, what does the "+0x40" mean in many of the definitions? That seems to set a bit in the TT field. Wouldn't it be better to give that field some named constants and use those instead?
  • evanhevanh Posts: 15,126
    edited 2019-07-20 11:32
    RossH wrote: »
    evanh wrote: »
    Okay, first hack ...

    A good start, but it seems a little opaque because of the unexplained "magic numbers" in various places.

    For instance, what does the "+0x40" mean in many of the definitions? That seems to set a bit in the TT field. Wouldn't it be better to give that field some named constants and use those instead?
    Done. Those are just the most basic needs. The combinations with the pin modes road gets big really quickly though.

    I've finished the mode naming, although I'm a tad uncertain on some detail with those last ones. Some turned out not to be timers at all:
    /*
     * Custom pin modes
     *
     *               30    25     20   15   10      5     0
     *                |     |      |    |    |      |     |
     * WRPIN  D/# = %AAAA_BBBB_FFF_PPPPPPPPPPPPP_TT_MMMMM_0
     */
    
    #define P_DACMODE	(0x05 << 18)		// WRPIN configuration constant for turning on custom pin DAC output
    #define P_DIR_ON	0x40			// WRPIN constant for forcing on DIR signal for the custom pin logic output
    
    
    
    /*
     * Smartpin modes
     *
     *               30    25     20   15   10      5     0
     *                |     |      |    |    |      |     |
     * WRPIN  D/# = %AAAA_BBBB_FFF_PPPPPPPPPPPPP_TT_MMMMM_0
     */
    
    #define SP_OFF		0x00			// M = %00000 = smart pin off (default)
    
    #define SP_REPOSITORY	0x02			// M = %00001 = long repository (P[12:10] != %101)
    
    #define SP_DAC_RANDOM	(0x02 + P_DACMODE)	// M = %00001 = DAC noise (P[12:10] = %101)
    #define SP_DAC_DITH_RND	(0x04 + P_DACMODE)	// M = %00010 = DAC 16-bit dither, noise (P[12:10] = %101)
    #define SP_DAC_DITH_PWM (0x06 + P_DACMODE)	// M = %00011 = DAC 16-bit dither, PWM (P[12:10] = %101)
    
    #define SP_PULSES	(0x08 + P_DIR_ON)	// M = %00100* = pulse/cycle output
    #define SP_STEPS	(0x0a + P_DIR_ON)	// M = %00101* = transition output
    #define SP_NCO_FREQ	(0x0c + P_DIR_ON)	// M = %00110* = NCO frequency
    #define SP_NCO_DUTY	(0x0e + P_DIR_ON)	// M = %00111* = NCO duty
    #define SP_PWM_TRI	(0x10 + P_DIR_ON)	// M = %01000* = PWM triangle
    #define SP_PWM_SAW	(0x12 + P_DIR_ON)	// M = %01001* = PWM sawtooth
    #define SP_PWM_SAW_FB	(0x14 + P_DIR_ON)	// M = %01010* = PWM switch-mode power supply, V and I feedback
    
    #define SP_CNT_QUAD	0x16			// M = %01011 = periodic/continuous: A-B quadrature encoder
    
    #define SP_CNT_UP_ENA	0x18			// M = %01100 = periodic/continuous: inc on A-rise & B-high
    #define SP_CNT_UP_DIR	0x1a			// M = %01101 = periodic/continuous: inc on A-rise & B-high / dec on A-rise & B-low
    #define SP_CNT_UP	0x1c			// M = %01110 = periodic/continuous: inc on A-rise
    #define SP_CNT_UP_DN	0x1c			// M = %01110 = periodic/continuous: inc on A-rise / dec on B-rise
    
    #define SP_ACC_UP	0x1e			// M = %01111 = periodic/continuous: inc on A-high
    #define SP_ACC_UP_DN	0x1e			// M = %01111 = periodic/continuous: inc on A-high / dec on B-high
    
    #define SP_TIM_STEP	0x20			// M = %10000 = time A-states
    #define SP_TIM_PULSE	0x22			// M = %10001 = time A-highs
    #define SP_TIM_ACC	0x24			// M = %10010 = time X A-highs/rises/edges
    #define SP_TIMEOUT	0x24			// M = %10010 = timeout X A-highs/rises/edges
    
    #define SP_ACC_PULSES	0x26			// M = %10011 = for X periods, count time
    #define SP_CNT_STEPS	0x28			// M = %10100 = for X periods, count states
    #define SP_ACC_PULS_TO	0x2a			// M = %10101 = for periods in X+ clocks, count time
    #define SP_CNT_STEP_TO	0x2c			// M = %10110 = for periods in X+ clocks, count states
    #define SP_CNT_PULS_TO	0x2e			// M = %10111 = for periods in X+ clocks, count periods
    
    #define SP_USB_HOSTL	(0x30 + P_DIR_ON)	// M = %11000* = USB host, low-speed (even/odd pin pair = DM/DP)
    #define SP_USB_HOSTH	(0x32 + P_DIR_ON)	// M = %11001* = USB host, high-speed (even/odd pin pair = DM/DP)
    #define SP_USB_DEVL	(0x34 + P_DIR_ON)	// M = %11010* = USB device, low-speed (even/odd pin pair = DM/DP)
    #define SP_USB_DEVH	(0x36 + P_DIR_ON)	// M = %11011* = USB device, high-speed (even/odd pin pair = DM/DP)
    #define SP_SSER_TX	(0x38 + P_DIR_ON)	// M = %11100* = sync serial transmit (A-data, B-clock)
    #define SP_SSER_RX	0x3a			// M = %11101 = sync serial receive (A-data, B-clock)
    #define SP_ASER_TX	(0x3c + P_DIR_ON)	// M = %11110* = async serial transmit (baud)
    #define SP_ASER_RX	0x3e			// M = %11111 = async serial receive (baud)
    
    
  • evanhevanh Posts: 15,126
    edited 2019-07-20 11:51
    Here's the P field detailed for custom pin DAC_MODE
             0      5    0
    	%101_VV_DDDDDDDD = DAC_MODE (%TT = 00 and %MMMMM = 00000), 8-bit flash
    		OUT enables PinA ADC (ADC config %011), sysclocked bitstream on IN
    		DIR enables PinA DAC output
    		%VV = PinA DAC config
    			00: 990 ohm, 3.3 volt range
    			01: 600 ohm, 2.0 volt range
    			10: 123.75 ohm, 3.3 volt range
    			11: 75 ohm, 2.0 volt range
    		%DDDDDDDD = DAC level
    
    		for %TT = %01 and %MMMMM = %00000, %101_VV_xxxxSSSS = COG_DAC mode
    			%SSSS = Cog/streamer select: sets DAC level (registered?)
    
    		for %00000 < %MMMMM < %00100 = SMART_DAC mode
    			DIR/IN are usual smartpin ctrl
    			%DDDDDDDD ignored, smartpin sets DAC level (registered?)
    
    		for %MMMMM >= %00100 or (%TT = %1x and %MMMMM = %00000) = BIT_DAC mode
    			OUT sets DAC level (clocked?, ADC disabled?, IN = ?)
    				0: 0 = GIO level
    				1: %DDDDDDDD
    
    
  • evanhevanh Posts: 15,126
    This likely occurs on other places, but I'll use what I've recently worked on as an example: With the baud setting for asynchronous serial the calculation is based around sysclock-frequency divided by bits-per-second. If the clock frequency is not a compile time constant, which it certainly doesn't have to be, then the calculation has to be done at runtime.

    I'm not sure what macros can be done to support this. Here's what I've written out using a filler for clock freqency:
    #define SP_ASYNC_PARAMS(BAUD,BITS) (((64.0*clk_freq/BAUD)<<10)+(BITS-1))
    
  • RossHRossH Posts: 5,336
    Hi @evanh

    A couple of suggestions - don't just number the pins 0 5 10 15 etc. That doesn't really help since you still have to count to find where the fields start and end.

    Instead, just number the first bit of each field. For example:
    /*
     * Smartpin modes
     *
     *                 28   24   21            8  6     1 0
     *                  |    |   |             |  |     | |
     * WRPIN  D/# = %AAAA_BBBB_FFF_PPPPPPPPPPPPP_TT_MMMMM_0
     */
    
    
    Then you can use actual values in the constants instead of pre-shifting them, with the bit numbers above telling you how far you have to shift each field to the left when you use them. This means you can define the field values to match what is written in the documentation. For example, in the M field:
    #define SP_TIM_STEP	0x10	// M = %10000 = time A-states
    #define SP_TIM_PULSE	0x11	// M = %10001 = time A-highs
    #define SP_TIM_ACC	0x12	// M = %10010 = time X A-highs/rises/edges
    #define SP_TIMEOUT	0x12	// M = %10010 = timeout X A-highs/rises/edges
    
    Finally, use all the above in a macro to put them all together in the correct position. For example:
    #define SMARTPIN_MODE(A, B, F, P, T, M)  (A<<28 + B<<24 + F<<21 + P<<8 + T<<6 + M<<1)
    
    This reduces the possibility of error. Also, if there are different submodes, you can have different macros for each one - For example (not a real example, but assume A_VAL, B_VAL, F_VAL & P_VAL are fixed and pre-defined for the various SP modes and all you have to do is specify the T & M fields and you'll get the idea):
    #define SP_SUBMODE(T,M)  SMARTPIN_MODE(A_VAL, B_VAL, F_VAL, P_VAL, T, M)
    
  • evanhevanh Posts: 15,126
    edited 2019-07-21 01:56
    That's the pin modes. Smartpins are only the M field. There is a lot of shifting sub-fields within the P field depending on the highest few bits of the P field. I did that numbering mainly for locating these sub-fields. It's a tad daunting looking at them.

    For smartpin macros, as my example highlighted above, there will be a bunch of macros needed for the WXPIN and WYPIN parameters of the smartpins. But most of it is runtime parameters, so I'm unsure the recommended way to format macros for this.
  • evanhevanh Posts: 15,126
    I've addressed an example of the complexities for Pilot in the smartpin docs topic - https://forums.parallax.com/discussion/comment/1474059/#Comment_1474059
  • RossHRossH Posts: 5,336
    I just noticed there is a name collision with my existing library code for _coginit().

    I would therefore like to propose the following names, instead of _coginit() and _cogstart():
    /* start PASM code in another COG */
    int       _cogstart_PASM(int cog, void *pgm, void *ptr);
    
    /* start C code in another COG */
    int       _cogstart_C(void (*func)(void *), void *arg, void *stack_base, uint32_t stack_size);
    

    This actually makes more sense, because some compilers may support starting code written in language "X" as well as C code, and in that case they might need both _cogstart_C() and _cogstart_X(). For instance, Catalina will also have _cogstart_Spin().

    Apologies for not noticing this before :(
Sign In or Register to comment.