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).
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 *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.
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 . 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.
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.
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.
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
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.
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.
... 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.
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.
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.
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?
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.
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.
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?
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)
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:
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:
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:
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)
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.
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().
Comments
OK, we can declare them as: 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).
Whoops, yes, should be: 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.
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).
"_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
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.
Ah yes, I forgot you've mentioned this multiple times, and each time I forgot to give you my rebuttal . 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.
I prefer camelCase, but like you said, not important at all.
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:
This should return "high" as zero if the processor does not support it.
I don't care about the particular names.
Ross.
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().
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.
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.
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
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.
Yes.
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.
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?
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):
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.
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.
EDIT: Plural SP_STEPS/SP_PULSES.
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?
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:
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:
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:
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:
Finally, use all the above in a macro to put them all together in the correct position. For example:
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):
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.
I would therefore like to propose the following names, instead of _coginit() and _cogstart():
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