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).
I think we are pretty close with the currently proposed propeller2.h
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.
I think we may be overdoing the underscores a bit, but ok.
Catalina will have a function called _outa(), a global variable called _OUTA and a #defined OUTA
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.
I think we may be overdoing the underscores a bit, but ok.
Catalina will have a function called _outa(), a global variable called _OUTA and a #defined OUTA
What could possibly go wrong?
Isn't case sensitivity wonderful?
What does the _outa() function do? Does it return the contents of the OUTA register? If so that's very useful, and I think I'll adopt it as well.
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.
I think we may be overdoing the underscores a bit, but ok.
Catalina will have a function called _outa(), a global variable called _OUTA and a #defined OUTA
What could possibly go wrong?
Isn't case sensitivity wonderful?
What does the _outa() function do? Does it return the contents of the OUTA register? If so that's very useful, and I think I'll adopt it as well.
_outa() would be similar to getpin(), except it wouldn't use a mask to mask out other pins except the pretended one, right?
Anyway, as long as you are consistent throughout. And don't use cryptic names, but simple names that covey the real meaning of what the function really does (eg: getdir(int pin), setdir(int pin), getpin(int pin), getlatch(int pin), setpin(int pin)...).
What does the _outa() function do? Does it return the contents of the OUTA register? If so that's very useful, and I think I'll adopt it as well.
That is true for _ina(), but it is a bit more complicated for _outa() and _dira(). Here are the details:
/*
* Special Register access functions - you can either use these functions,
* or use the Special Register definitions directly (note that you also
* need to include <propeller.h> to do that). For example:
*
* x = _ina() is equivalent to x = INA
* x = _get_dira() is equivalent to x = DIRA
* _dira(m,d) is equivalent to DIRA = ((DIRA & ~m) | d)
* _dira(0,d) is equivalent to DIRA |= d
* _dira(m,0) is equivalent to DIRA &= ~m
*
* NOTE: _inb, _dirb and _outb (or INB, DIRB, OUTB) are not implemented on
* the propeller v1
*/
extern unsigned _ina();
extern unsigned _inb();
extern unsigned _get_dira();
extern unsigned _get_dirb();
extern unsigned _dira(unsigned mask, unsigned direction);
extern unsigned _dirb(unsigned mask, unsigned direction);
extern unsigned _outa(unsigned mask, unsigned output);
extern unsigned _outb(unsigned mask, unsigned output);
In hindsight, for consistency I should have made _dira() just return the current value of the register, and used something like _set_dira() to set the value.
I could be convinced to change, because the compiler would detect the change and alert you to any old code that needed to be updated.
Does Catalina support variadic macros? If so we could get fancy and have:
x = _outa(); // reads outa
_outa(y); // sets outa to y
_outa(m, d); // sets outa to (outa & ~m) | d
Or maybe just changing the function names to be consistent would be better.
The Catalina preprocessor in independent of the compiler, and could fairly easily be replaced with one that accepts variadic macros. There are several such "open source" options available. I may look into that when I get time - if ever!
In the meantime, I will look at just changing the function names. I could easily change them just for just the P2, but I am wondering whether I should also go back and change them for the P1. It would be preferable to keep them consistent.
The Catalina preprocessor in independent of the compiler, and could fairly easily be replaced with one that accepts variadic macros. There are several such "open source" options available. I may look into that when I get time - if ever!
In the meantime, I will look at just changing the function names. I could easily change them just for just the P2, but I am wondering whether I should also go back and change them for the P1. It would be preferable to keep them consistent.
mcpp (mcpp.sourceforge.net) is a nice one that's easy to integrate into other projects. But that would be more work than changing the names, obviously .
Anyway, I do like your register accessor functions -- having the mask and or value is very elegant, and doing the access through functions makes register access possible in compilers where the COG memory space is hard to access via C variables. It also makes porting P1 programs to P2 easier, since for example the P1 CNT register no longer exists on P2, so you were really thinking ahead there!
I agree with Eric that the *move and *fill functions need to go away.
I also agree with him that creating a type "polar" is not good. You are far too likely to conflict with what a user would like to name their own variable. polar_t please.
And finally, would you be alright using "unsigned int" for parameters that take a pin number?
Here's my proposal, based on Ross's example. I think we're pretty close to agreement on most things. @DavidZemon I've left the pin number as "int" for now, but perhaps "unsigned int" would be better. I also haven't put in the register access functions.
@cgracey : you listed PINL, PINH, etc. functions in Spin, but how about the OUTx, DIRx, and FLTx variants? Should there be versions of those as well?
#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;
/*
* 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
/*
* 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;
/*
* 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);
#define _cognew(pgm, ptr) _coginit(ANY_COG, pgm, 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_t _rotxy(cartesian_t coord, uint32_t t);
cartesian_t _polxy(polar_t coord);
polar_t _xypol(cartesian_t coord);
uint32_t _rnd(void);
uint32_t _cnt(void);
uint32_t _pollcnt(uint32_t tick);
void _waitcnt(uint32_t tick);
void _waitx(uint32_t delay);
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, 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);
#endif
I'm not sure that these should be in propeller2.h. Don't they basically undo what the underscore is supposed to achieve. Any program that includes propeller2.h can't use those symbols in any other way without a conflict with the macros. I suppose it would be possible to #undef them but it wouldn't be transparent usage.
/*
* 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
Maybe put an "#ifndef PROPELLER2_NO_SHORTHANDS" around anything that may conflict with user stuff in ported programs (programs written specifically for P2 will likely not want to have those annoying underscores everywhere, i'd assume)
I'm not sure that these should be in propeller2.h. Don't they basically undo what the underscore is supposed to achieve. Any program that includes propeller2.h can't use those symbols in any other way without a conflict with the macros. I suppose it would be possible to #undef them but it wouldn't be transparent usage.
/*
* 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
Agreed. I could even see a "propeller1compat.h" to add macros like this.
I'm not sure that these should be in propeller2.h. Don't they basically undo what the underscore is supposed to achieve. Any program that includes propeller2.h can't use those symbols in any other way without a conflict with the macros.
#defines in a header file are OK; they only affect the source file that actually #includes propeller2.h. What we don't want is for any global symbols (functions or variables) to be introduced that could conflict with globals in other source files.
In other words, any source code that has "#include <propeller2.h>" in it is already not standard conforming, and the user is going to have to work around any defines in that file (which should be documented, of course!). What we don't want is for "#include <propeller2.h>" in one file to mess up an unrelated library file or something like that because of a symbol conflict.
I'm not sure that these should be in propeller2.h. Don't they basically undo what the underscore is supposed to achieve. Any program that includes propeller2.h can't use those symbols in any other way without a conflict with the macros.
#defines in a header file are OK; they only affect the source file that actually #includes propeller2.h. What we don't want is for any global symbols (functions or variables) to be introduced that could conflict with globals in other source files.
In other words, any source code that has "#include <propeller2.h>" in it is already not standard conforming, and the user is going to have to work around any defines in that file (which should be documented, of course!). What we don't want is for "#include <propeller2.h>" in one file to mess up an unrelated library file or something like that because of a symbol conflict.
So it's only global symbols that you're worried about. In that case, putting the #defines in the header file should be okay.
// a macro for referencing the system clock frequency
// by convention this is placed in low HUB RAM at address $14
#define _clkfreq (*((uint32_t*)0x14))
#define _clkmode (*((uint32_t*)0x18))
Also, in the "final" P2 chip there will actually be a 64 bit frequency counter, so we'll need a way to fetch the high word of that. @cgracey , is the Spin2 CNT function going to return two values (low and high 32 bits) or will there be two functions CNT for the low and CNTH for the high? The two functions version would be easier for C and slightly more efficient, but having both values returned from CNT might make sense too.
// a macro for referencing the system clock frequency
// by convention this is placed in low HUB RAM at address $14
#define _clkfreq (*((uint32_t*)0x14))
#define _clkmode (*((uint32_t*)0x18))
Also, in the "final" P2 chip there will actually be a 64 bit frequency counter, so we'll need a way to fetch the high word of that. @cgracey , is the Spin2 CNT function going to return two values (low and high 32 bits) or will there be two functions CNT for the low and CNTH for the high? The two functions version would be easier for C and slightly more efficient, but having both values returned from CNT might make sense too.
Cool! Didn't know that. Wouldn't it make sense for C to provide two functions for high and low words, and one function returning a 64-bit var?
Also, in the "final" P2 chip there will actually be a 64 bit frequency counter, so we'll need a way to fetch the high word of that. @cgracey , is the Spin2 CNT function going to return two values (low and high 32 bits) or will there be two functions CNT for the low and CNTH for the high? The two functions version would be easier for C and slightly more efficient, but having both values returned from CNT might make sense too.
Cool! Didn't know that. Wouldn't it make sense for C to provide two functions for high and low words, and one function returning a 64-bit var?
That's a good idea. For now the propeller2.h file I put in the RISC-V compiler distribution has separate _cnt() and _cnth() macros, but adding a _cnt64() function as well would make sense.
// a macro for referencing the system clock frequency
// by convention this is placed in low HUB RAM at address $14
#define _clkfreq (*((uint32_t*)0x14))
#define _clkmode (*((uint32_t*)0x18))
// a macro for referencing the system clock frequency
// by convention this is placed in low HUB RAM at address $14
#define _clkfreq (*((uint32_t*)0x14))
#define _clkmode (*((uint32_t*)0x18))
Also, in the "final" P2 chip there will actually be a 64 bit frequency counter, so we'll need a way to fetch the high word of that. @cgracey , is the Spin2 CNT function going to return two values (low and high 32 bits) or will there be two functions CNT for the low and CNTH for the high? The two functions version would be easier for C and slightly more efficient, but having both values returned from CNT might make sense too.
Cool! Didn't know that. Wouldn't it make sense for C to provide two functions for high and low words, and one function returning a 64-bit var?
// a macro for referencing the system clock frequency
// by convention this is placed in low HUB RAM at address $14
#define _clkfreq (*((uint32_t*)0x14))
#define _clkmode (*((uint32_t*)0x18))
Also, in the "final" P2 chip there will actually be a 64 bit frequency counter, so we'll need a way to fetch the high word of that. @cgracey , is the Spin2 CNT function going to return two values (low and high 32 bits) or will there be two functions CNT for the low and CNTH for the high? The two functions version would be easier for C and slightly more efficient, but having both values returned from CNT might make sense too.
Cool! Didn't know that. Wouldn't it make sense for C to provide two functions for high and low words, and one function returning a 64-bit var?
What will Spin do in this case? Does anyone know?
Not sure, yet. I'll probably make two different read functions, because most usage will be for the lower 32 bits. Hmmm.... maybe I''ll need 64-bit POLLCNT and WAITCNT, too.
// a macro for referencing the system clock frequency
// by convention this is placed in low HUB RAM at address $14
#define _clkfreq (*((uint32_t*)0x14))
#define _clkmode (*((uint32_t*)0x18))
Also, in the "final" P2 chip there will actually be a 64 bit frequency counter, so we'll need a way to fetch the high word of that. @cgracey , is the Spin2 CNT function going to return two values (low and high 32 bits) or will there be two functions CNT for the low and CNTH for the high? The two functions version would be easier for C and slightly more efficient, but having both values returned from CNT might make sense too.
Cool! Didn't know that. Wouldn't it make sense for C to provide two functions for high and low words, and one function returning a 64-bit var?
What will Spin do in this case? Does anyone know?
Not sure, yet. I'll probably make two different read functions, because most usage will be for the lower 32 bits. Hmmm.... maybe I''ll need 64-bit POLLCNT and WAITCNT, too.
// a macro for referencing the system clock frequency
// by convention this is placed in low HUB RAM at address $14
#define _clkfreq (*((uint32_t*)0x14))
#define _clkmode (*((uint32_t*)0x18))
Also, in the "final" P2 chip there will actually be a 64 bit frequency counter, so we'll need a way to fetch the high word of that. @cgracey , is the Spin2 CNT function going to return two values (low and high 32 bits) or will there be two functions CNT for the low and CNTH for the high? The two functions version would be easier for C and slightly more efficient, but having both values returned from CNT might make sense too.
Cool! Didn't know that. Wouldn't it make sense for C to provide two functions for high and low words, and one function returning a 64-bit var?
What will Spin do in this case? Does anyone know?
Not sure, yet. I'll probably make two different read functions, because most usage will be for the lower 32 bits. Hmmm.... maybe I''ll need 64-bit POLLCNT and WAITCNT, too.
OK, here's my latest proposal. Again, very close to what @RossH had posted earlier:
#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;
/*
* 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
/*
* 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;
/*
* 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 *arg, 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);
cartesian_t _rotxy(cartesian_t coord, uint32_t t);
cartesian_t _polxy(polar_t coord);
polar_t _xypol(cartesian_t coord);
uint32_t _rnd(void);
uint32_t _cnt(void);
uint32_t _cnth(void); /* high 32 bits of CNT, on processors that support it */
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 */
#define _clockfreq() (*(uint32_t *)0x14)
#define _clockmode() (*(uint32_t *)0x18)
#endif
Changes:
(1) _clockfreq() and _clockmode() macros. These are the same as the ones Catalina has.
(2) A new function _cogstart() for starting C code in another COG (_coginit() is for starting PASM code). Some compilers might be able to just use _coginit() for both, but in general the C runtime will need some setup so it seems wise to have a different function for this.
(3) Multi-word return types are polar_t and cartesian_t. If there are strong objections to these I could certainly live with POLAR and CARTESIAN instead.
Still missing:
(1) We're missing some of the cordic functions
(2) dirh/dirl/dirw, outh/outl/outw
(3) not sure about the flth/fltl family of instructions... @cgracey , what does "PINF" do in Spin? It sounds like it sets the pin to float, but what exactly does it do to the hardware.
Any comments / suggestions?
Current githubs of fastspin and riscvp2 have subsets of this header file, and I plan to update to keep those current.
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"
"int _cogstart(void (*func)(void *), void *arg, void *arg, uint32_t stack_size);"
incorrectly named "stack" parameter
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.
"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?
These are all pretty minor comments. Overall, I do like this version a lot.
Comments
I think we are pretty close with the currently proposed propeller2.h
I think we may be overdoing the underscores a bit, but ok.
Catalina will have a function called _outa(), a global variable called _OUTA and a #defined OUTA
What could possibly go wrong?
Isn't case sensitivity wonderful?
What does the _outa() function do? Does it return the contents of the OUTA register? If so that's very useful, and I think I'll adopt it as well.
Mike
Anyway, as long as you are consistent throughout. And don't use cryptic names, but simple names that covey the real meaning of what the function really does (eg: getdir(int pin), setdir(int pin), getpin(int pin), getlatch(int pin), setpin(int pin)...).
It performs miracles and avoids disasters. Works great on Linux for filenames too.
Kind regards, Samuel Lourenço
That is true for _ina(), but it is a bit more complicated for _outa() and _dira(). Here are the details:
In hindsight, for consistency I should have made _dira() just return the current value of the register, and used something like _set_dira() to set the value.
I could be convinced to change, because the compiler would detect the change and alert you to any old code that needed to be updated.
The Catalina preprocessor in independent of the compiler, and could fairly easily be replaced with one that accepts variadic macros. There are several such "open source" options available. I may look into that when I get time - if ever!
In the meantime, I will look at just changing the function names. I could easily change them just for just the P2, but I am wondering whether I should also go back and change them for the P1. It would be preferable to keep them consistent.
mcpp (mcpp.sourceforge.net) is a nice one that's easy to integrate into other projects. But that would be more work than changing the names, obviously .
Anyway, I do like your register accessor functions -- having the mask and or value is very elegant, and doing the access through functions makes register access possible in compilers where the COG memory space is hard to access via C variables. It also makes porting P1 programs to P2 easier, since for example the P1 CNT register no longer exists on P2, so you were really thinking ahead there!
Still waiting for someone - anyone - to come up with some definitions for Smart Pin configurations ...
I also agree with him that creating a type "polar" is not good. You are far too likely to conflict with what a user would like to name their own variable. polar_t please.
And finally, would you be alright using "unsigned int" for parameters that take a pin number?
@cgracey : you listed PINL, PINH, etc. functions in Spin, but how about the OUTx, DIRx, and FLTx variants? Should there be versions of those as well?
Agreed. I could even see a "propeller1compat.h" to add macros like this.
In other words, any source code that has "#include <propeller2.h>" in it is already not standard conforming, and the user is going to have to work around any defines in that file (which should be documented, of course!). What we don't want is for "#include <propeller2.h>" in one file to mess up an unrelated library file or something like that because of a symbol conflict.
Also, in the "final" P2 chip there will actually be a 64 bit frequency counter, so we'll need a way to fetch the high word of that. @cgracey , is the Spin2 CNT function going to return two values (low and high 32 bits) or will there be two functions CNT for the low and CNTH for the high? The two functions version would be easier for C and slightly more efficient, but having both values returned from CNT might make sense too.
Cool! Didn't know that. Wouldn't it make sense for C to provide two functions for high and low words, and one function returning a 64-bit var?
That's a good idea. For now the propeller2.h file I put in the RISC-V compiler distribution has separate _cnt() and _cnth() macros, but adding a _cnt64() function as well would make sense.
These should be functions, not macros.
What will Spin do in this case? Does anyone know?
Not sure, yet. I'll probably make two different read functions, because most usage will be for the lower 32 bits. Hmmm.... maybe I''ll need 64-bit POLLCNT and WAITCNT, too.
Does Spin2 have a 64 bit data type?
No. That would take a lot of code to support.
Changes:
(1) _clockfreq() and _clockmode() macros. These are the same as the ones Catalina has.
(2) A new function _cogstart() for starting C code in another COG (_coginit() is for starting PASM code). Some compilers might be able to just use _coginit() for both, but in general the C runtime will need some setup so it seems wise to have a different function for this.
(3) Multi-word return types are polar_t and cartesian_t. If there are strong objections to these I could certainly live with POLAR and CARTESIAN instead.
Still missing:
(1) We're missing some of the cordic functions
(2) dirh/dirl/dirw, outh/outl/outw
(3) not sure about the flth/fltl family of instructions... @cgracey , what does "PINF" do in Spin? It sounds like it sets the pin to float, but what exactly does it do to the hardware.
Any comments / suggestions?
Current githubs of fastspin and riscvp2 have subsets of this header file, and I plan to update to keep those current.
The names are the same, but these are not macros in Catalina, they are functions. But they could be macros on some compilers.
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"
"int _cogstart(void (*func)(void *), void *arg, void *arg, uint32_t stack_size);"
incorrectly named "stack" parameter
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.
"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?
These are all pretty minor comments. Overall, I do like this version a lot.