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

propeller2.h for C compilers

12345679»

Comments

  • RossHRossH Posts: 5,436
    ersmith wrote: »
    For the same reason I don't think you need the _PROP2_NAMES define to protect the defines of _DIRA, etc. You can just unconditionally define them. New programs written for Prop2 should always use the underscore version, for compatibility with other compiilers and to leave the non-underscore version free for the user's use.

    I take your first point, but a problem still occurs if a program uses (say) DIRA as a local variable name, but also uses _DIRA, expecting it to refer to the special register. This would be quite legal. But if the #defines are unconditional then such a program would compile without error, but it would not execute correctly. By requiring the definition of _PROP2_NAMES to get it to compile, the user is at least made aware that there is a potential name conflict.

    Of course, I am suggesting this only for Catalina's version of the include file, not for other compilers, which may not have the same issue.

    One advantage of this solution is that it can be fixed on the command line, and does not require a source code change - unless of course there actually is a conflict. I am posting this mainly to see if there is an easier or better solution that I may have missed.
  • ersmithersmith Posts: 6,025
    edited 2019-09-13 12:22
    Requiring _PROP2_NAMES to be defined before the standard _DIRA, _INA, etc. register names are present kind of defeats the purpose of propeller2.h, which is to provide a set of standard names for all programs that #include it. I mean, we could require that programs always #define _PROP2_NAMES before including propeller2.h, but then why even do that? (edit: Oh, I see you were suggesting it be defined on the command line; that's better, it does avoid source code changes, but still feels awkward to me.)

    It seems to me that the Prop2 is a chance for a clean slate for developers. We may need some ugly hacks to get Prop1 code to compile on Prop2, and that seems OK to me, but let's make things simple for new Prop2 projects.

    Could you invert the situation: that is, could you make _DIRA, _INA, etc. the standard names, and define DIRA, INA, etc. only in one of the legacy header files (propeller.h or catalina_cog.h)? And make those definitions conditional? So the old propeller.h could contain something like:
    #ifndef _PROP2_NAMES
    #define DIRA _DIRA
    #define INA _INA
    // etc
    #endif
    
    Then existing Prop1 code (and your library code) should still compile, while not impacting new Prop2 code and having minimal impact on the ANSI user namespace.

    (Having a global variable named DIRA that the library depends on is a violation of the C standard, so it may be worth taking this second approach for compatibility.)
  • RossHRossH Posts: 5,436
    ersmith wrote: »
    Requiring _PROP2_NAMES to be defined before the standard _DIRA, _INA, etc. register names are present kind of defeats the purpose of propeller2.h, which is to provide a set of standard names for all programs that #include it. I mean, we could require that programs always #define _PROP2_NAMES before including propeller2.h, but then why even do that? (edit: Oh, I see you were suggesting it be defined on the command line; that's better, it does avoid source code changes, but still feels awkward to me.)

    It seems to me that the Prop2 is a chance for a clean slate for developers. We may need some ugly hacks to get Prop1 code to compile on Prop2, and that seems OK to me, but let's make things simple for new Prop2 projects.

    Could you invert the situation: that is, could you make _DIRA, _INA, etc. the standard names, and define DIRA, INA, etc. only in one of the legacy header files (propeller.h or catalina_cog.h)? And make those definitions conditional? So the old propeller.h could contain something like:
    #ifndef _PROP2_NAMES
    #define DIRA _DIRA
    #define INA _INA
    // etc
    #endif
    
    Then existing Prop1 code (and your library code) should still compile, while not impacting new Prop2 code and having minimal impact on the ANSI user namespace.

    (Having a global variable named DIRA that the library depends on is a violation of the C standard, so it may be worth taking this second approach for compatibility.)

    I will have a think on this. The idea of inverting the names and then redefining them in the old propeller.h file would have legs if such a file had itself been standardized and used consistently. But alas .... :(
  • RossHRossH Posts: 5,436
    Well, D'oh!

    The correct answer was staring me in the face - I even mentioned it yesterday, but I hadn't fully thought it through.

    This is to define (for example) both DIRA and _DIRA in the code generator. It is the declaration of these names as "extern volatile" in the user's C program that triggers the association by the code generator with the special register DIRA. Otherwise, they are just symbols like any other.

    So if you include "propeller2.h" - or manually declare "extern volatile _DIRA" - then _DIRA means the special register and DIRA is just a symbol with no special meaning.

    And if you include the old "propeller.h" - or manually declare "extern volatile DIRA" (which some programs do) - then DIRA means the special register and _DIRA is just a symbol with no special meaning.

    I think this will work for all P1 and P2 programs - no conditional compilation or command line options required!


  • RossHRossH Posts: 5,436
    Ok - I've finished implementing and also written C test programs for all the "propeller2.h" functions ... except for the smart pin functions:
    /* 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);
    

    Does anyone have any C code - or code in any other language that could be converted to C code - that would make a good test for these functions? Note that we are not actually trying to test the smart pins themselves, just the C function calls - so quite trivial examples are perfectly fine. But it would be nice if they produced results that were evident on one or more of the LEDs on the P2_EVAL board.
  • Here's a really simple blinking LED demo that uses a smart pin:
    #include <stdio.h>
    #include <stdint.h>
    #include <propeller2.h>
    
    #define ledpin 56
    
    #define ncomode 0x4C // 0b01001100
    
    void main()
    {
        uint32_t sysfreq = _clockfreq();
        uint32_t pinfreq = sysfreq / 4; // how often the pin should toggle
        uint32_t bitperiod = 16000;
        uint32_t incr = 0x80000000U / (pinfreq / bitperiod);
    
        _dirl(ledpin);
        _wrpin(ledpin, ncomode);
        _wxpin(ledpin, bitperiod);
        _wypin(ledpin, incr);
        _dirh(ledpin); // enable smart pin
        for(;;) {
            _waitx(sysfreq);
            printf("ping!\n");
        }
    }
    

    Another way to test the smart pins is to put one in repository mode; then you can write data to it with _wrpin() and read it back with _rdpin().
  • RossHRossH Posts: 5,436
    ersmith wrote: »
    Here's a really simple blinking LED demo that uses a smart pin:
    #include <stdio.h>
    #include <stdint.h>
    #include <propeller2.h>
    
    #define ledpin 56
    
    #define ncomode 0x4C // 0b01001100
    
    void main()
    {
        uint32_t sysfreq = _clockfreq();
        uint32_t pinfreq = sysfreq / 4; // how often the pin should toggle
        uint32_t bitperiod = 16000;
        uint32_t incr = 0x80000000U / (pinfreq / bitperiod);
    
        _dirl(ledpin);
        _wrpin(ledpin, ncomode);
        _wxpin(ledpin, bitperiod);
        _wypin(ledpin, incr);
        _dirh(ledpin); // enable smart pin
        for(;;) {
            _waitx(sysfreq);
            printf("ping!\n");
        }
    }
    

    Another way to test the smart pins is to put one in repository mode; then you can write data to it with _wrpin() and read it back with _rdpin().

    Thanks Eric. I really need to learn about smart pins ... when I get some time :(
  • evanhevanh Posts: 15,620
    Here's a routine I use for scanning all the smartpins before initialising the comport. I lifted it from someone else, Oz or Rog I think. It uses the repository mode to build a map of functioning smartpins, which was particularly useful with the FPGA builds.
    '--------- Detect available smart pins -------
    'Returns a 64 bit mask indicating active smart pins - mask_lsb/mask_msb.
    
    		mov	smart_pin, #63			'test all 64 pins
    .searchloop
    		wrpin	#%11_0, smart_pin		'long repository mode
    		dirh	smart_pin
    
    		mov	pa, ##$c0ffee00
    		add	pa, smart_pin			'unique pattern
    		wxpin	pa, smart_pin			'write long to smart pin
    		shl	mask_lsb, #1		wc	'build valid smart pin mask
    		rcl	mask_msb, #1
    		rdpin	pb, smart_pin			'get long from smart pin repository
    		cmp	pb, pa	wz			'valid response?
    	if_e	bith	mask_lsb, #0
    
    		dirl	smart_pin
    		wrpin	#0, smart_pin			'disable smart pin
    		djnf	smart_pin, #.searchloop		'next smart pin
    
  • RossHRossH Posts: 5,436
    evanh wrote: »
    Here's a routine I use for scanning all the smartpins before initialising the comport. I lifted it from someone else, Oz or Rog I think. It uses the repository mode to build a map of functioning smartpins, which was particularly useful with the FPGA builds.

    Thanks! I will use parts of this one as well.
  • evanhevanh Posts: 15,620
    edited 2019-09-15 06:34
    Reading the code now, I think it would be more efficient to use bit indexing in place of the shifts and set ...

    EDIT: Deleted previous attemps! There is an instruction for the job! Man, Chip has thought hard here. :) I'd missed it every time. I kept reading this particular instruction as ALTR, not ALTB, and not even noticed they were separately listed only a few entries apart in the docs!
    '--------- Detect available smartpins -------
    'Returns a 64-bit bitmap indicating active smartpins - mask_lsb/mask_msb.
    
    		mov	smart_pin, #63			'test all 64 pins
    .searchloop
    		mov	pa, ##$c0ffee00
    		add	pa, smart_pin			'unique pattern
    
    		wrpin	#%11_0, smart_pin		'longword repository smartpin mode
    		dirh	smart_pin
    		wxpin	pa, smart_pin			'write longword to smartpin
    		dirl	smart_pin			'compact use of smartpin sequencing, requires a gap between write and read
    		rdpin	pb, smart_pin			'get longword from smartpin repository
    		wrpin	#0, smart_pin			'turn off smartpin
    
    		cmp	pb, pa			wz	'valid response?
    		altb	smart_pin, #mask_lsb		'extracts cogRAM address as if bitfield addressing - bitwise ALTxx
    		bitz	0-0, smart_pin			'S value (smart_pin) is masked to least 5 bits by the instruction
    
    		djnf	smart_pin, #.searchloop		'next pin
    
    
  • RossHRossH Posts: 5,436
    edited 2019-09-15 08:52
    Ok, I give up. What is _lockchk() supposed to actually do?. None of the implementations I have tried do what I would have expected such a function to do, so if someone can just tell me what it does in Spin, or (better yet) in other C compilers, I will make it do that and forget about it :(

    EDIT: I've now looked at the Spin interpreter, and I can see that Spin interpreter apparently does "lockrel x wc" and then returns with the carry in bit 31. This is not what I had posted earlier, which Chip thought at the time was correct. However, I guess I will just do what Spin does and move on :(
  • RossHRossH Posts: 5,436
    edited 2019-09-15 08:52
    duplicate deleted.

  • RossHRossH Posts: 5,436
    Catalina is nearly ready for its next release, but I'm a bit unhappy releasing software that doesn't appear to do what it says on the box. I have still been unable to get _lockchk() to do anything useful, even though I believe I am now doing exactly what Spin does - i.e.
     lockrel r0 wc
     bitc r0, #31
    

    I am sure I must be doing something wrong, but I can't see what. Perhaps I am just completely misunderstanding what this instruction is supposed to do?

    Does anyone have a program (any language) that demonstrates a working example of _lockchk() (or "lockrel x wc") ?
  • I don't think anyone has ever used _lockchk. So for now I would recommend that we just leave it out of propeller2.h. Better to have an incomplete header than a broken one. When/if Spin is finished we can try implementing the missing features.
  • RossH wrote: »
    Catalina is nearly ready for its next release, but I'm a bit unhappy releasing software that doesn't appear to do what it says on the box. I have still been unable to get _lockchk() to do anything useful, even though I believe I am now doing exactly what Spin does - i.e.
     lockrel r0 wc
     bitc r0, #31
    

    I am sure I must be doing something wrong, but I can't see what. Perhaps I am just completely misunderstanding what this instruction is supposed to do?

    Does anyone have a program (any language) that demonstrates a working example of _lockchk() (or "lockrel x wc") ?

    Reading the code you have there in concert with the Parallax Propeller 2 documentation, the content of r0 needs to identify which of the 16 locks (semaphore bits) you are checking.
    If this cog holds the lock it is released, otherwise the lock is unaffected, with WC set and because r0 is a register the status of the lock is returned in C and the cogid of the current/last owner is returned in r0.

    So it seems to be useful for checking which cog holds a lock, e.g. if a locktry fails to capture the lock.

    I'm not sure what the behaviour of a locktry r0 wc would be if this cog already owns the lock.

    The LOCKCHK might be used in a watchdog cog in combination with a COGATN to ask another cog to release the lock to prevent a stall.
    If that fails then the watchdog cog could kill the non-responsive cog.

  • RossHRossH Posts: 5,436
    AJL wrote: »
    RossH wrote: »
    Catalina is nearly ready for its next release, but I'm a bit unhappy releasing software that doesn't appear to do what it says on the box. I have still been unable to get _lockchk() to do anything useful, even though I believe I am now doing exactly what Spin does - i.e.
     lockrel r0 wc
     bitc r0, #31
    

    I am sure I must be doing something wrong, but I can't see what. Perhaps I am just completely misunderstanding what this instruction is supposed to do?

    Does anyone have a program (any language) that demonstrates a working example of _lockchk() (or "lockrel x wc") ?

    Reading the code you have there in concert with the Parallax Propeller 2 documentation, the content of r0 needs to identify which of the 16 locks (semaphore bits) you are checking.
    If this cog holds the lock it is released, otherwise the lock is unaffected, with WC set and because r0 is a register the status of the lock is returned in C and the cogid of the current/last owner is returned in r0.

    So it seems to be useful for checking which cog holds a lock, e.g. if a locktry fails to capture the lock.

    I'm not sure what the behaviour of a locktry r0 wc would be if this cog already owns the lock.

    The LOCKCHK might be used in a watchdog cog in combination with a COGATN to ask another cog to release the lock to prevent a stall.
    If that fails then the watchdog cog could kill the non-responsive cog.

    Yes, r0 is a register that holds the lock to be checked, and is also the value returned by the _lockchk() function.

    As far as I can tell, the code does nothing at all. It (luckily!) does not appear to release the lock - that would be bad. But it also does not seem to return a useful value - i.e. the return value appears to be the same whether the lock has been allocated or not, or locked or not. You might be correct that the result is meaningless if this function is called by the cog that already has the lock - but how can you know that in advance? However, I will try that case when I get time.

    In the meantime, and on the basis that it is much more likely that I have made a silly mistake than Chip has :) I have decided to leave the code in for the present. It is present in the Spin interpreter, so presumably it does something. I'm just not sure what.
  • what I do in the ringnet is the following.
    PUB Lock | locknumber1
      locknumber1 := lockId
      ASM
    .try    	locktry locknumber1			wc		'Keep trying to capture LOCK until successful
    	if_nc	jmp     #.try
      ENDASM
      RETURN
    '-----------------------------------------------------------------------
    PUB Release  | locknumber2
      locknumber2 := lockId
      ASM
    	    	lockrel locknumber2			wc		'release lock
      ENDASM
      RETURN
    
    that seems to work, but I was not ale to figure out how to get a lock number, have to support it manually

    Mike
  • msrobots wrote: »
    what I do in the ringnet is the following.
    PUB Lock | locknumber1
      locknumber1 := lockId
      ASM
    .try    	locktry locknumber1			wc		'Keep trying to capture LOCK until successful
    	if_nc	jmp     #.try
      ENDASM
      RETURN
    '-----------------------------------------------------------------------
    PUB Release  | locknumber2
      locknumber2 := lockId
      ASM
    	    	lockrel locknumber2			wc		'release lock
      ENDASM
      RETURN
    
    that seems to work, but I was not ale to figure out how to get a lock number, have to support it manually

    Mike

    I think the LOCKNEW instruction is the one you want to allocate a new lock number, and LOCKRET frees it.

    Fastspin implements the Spin1 lock functions for both P1 and P2, so you could just use those instead of creating your own.

  • ersmith wrote: »
    msrobots wrote: »
    what I do in the ringnet is the following.
    PUB Lock | locknumber1
      locknumber1 := lockId
      ASM
    .try    	locktry locknumber1			wc		'Keep trying to capture LOCK until successful
    	if_nc	jmp     #.try
      ENDASM
      RETURN
    '-----------------------------------------------------------------------
    PUB Release  | locknumber2
      locknumber2 := lockId
      ASM
    	    	lockrel locknumber2			wc		'release lock
      ENDASM
      RETURN
    
    that seems to work, but I was not ale to figure out how to get a lock number, have to support it manually

    Mike

    I think the LOCKNEW instruction is the one you want to allocate a new lock number, and LOCKRET frees it.

    Fastspin implements the Spin1 lock functions for both P1 and P2, so you could just use those instead of creating your own.

    Yes,it does, the code is quite older and I had to do it in SPIN and PASM so I wanted to be sure it is the same.

    Locknew seems lo set the lock too and lockrel seems to release the lock AND frees the given lock number.

    I simply gave up and require the lock number as parameter given manually.

    Mike
  • RossHRossH Posts: 5,436

    @RossH said:
    Catalina is nearly ready for its next release, but I'm a bit unhappy releasing software that doesn't appear to do what it says on the box. I have still been unable to get _lockchk() to do anything useful, even though I believe I am now doing exactly what Spin does - i.e.
    lockrel r0 wc&#13
    bitc r0, #31

    I am sure I must be doing something wrong, but I can't see what. Perhaps I am just completely misunderstanding what this instruction is supposed to do?

    Does anyone have a program (any language) that demonstrates a working example of _lockchk() (or "lockrel x wc") ?

    I was just implementing the Propeller 2 lock functions in Lua, and again found that the Propeller 2 LOCKREL instruction doesn't appear to do what the documentation says it should do when you call it with WC - e.g. as LOCKREL reg WC. A quick search and I found this old thread on the same issue. The Spin LOCKCHK() function apparently uses this instruction, and that function is still in the Spin documentation.

    Has anyone actually used it and gotten it to work yet?

    Ross.

  • RossHRossH Posts: 5,436

    I found this in the Propeller 2 documentation ...

    LOCKREL can also be used to query the current lock status. When LOCKREL is executed with WC, the C flag will indicate whether the lock is currently taken. Additionally, if the D field references a register (not an immediate value), the register will be written with the cog ID of the current owner (if held) or last owner (if released). If the cog executing LOCKREL is also the cog that is holding the lock, the normal LOCKREL behavior will still be performed (i.e. the lock will be released).

    Even if LOCKREL worked as this suggests (and it doesn't seem to) that last sentence would be a kicker - i.e. "If the cog executing LOCKREL is also the cog that is holding the lock, the normal LOCKREL behavior will still be performed (i.e. the lock will be released)."

    This makes any LOCKCHK() type function quite tricky to implement - first, you have to use LOCKTRY to see if the lock is currently held by you. If it was not, but now is, then you have to use LOCKREL to release it again. If it was not and still is not then you must use LOCKREL to get the status of the lock. And this leads to a race condition if some other code is also trying to get the lock - i.e. they may not succeed when they should have, if not for the competing LOCKCHK(). Which means all LOCKTRYs must have additional retry code ... etc etc ...

    Fortunately, Catalina also implements Propeller 1 style lock functions (i.e. LOCKSET/LOCKCLR) on the Propeller 2, and the Propeller 1 functions just work. Catalina's multi-threading is only possible because they do.

    So those functions are what I will implement in Lua as well, and again give the whole issue a wide berth, until someone with more brains and more patience than me figures it out :(

    Ross.

Sign In or Register to comment.