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

propeller2.h for C compilers

1234568

Comments

  • msrobots wrote: »
    am I over reacting?

    no
  • msrobotsmsrobots Posts: 3,709
    edited 2019-08-31 02:33
    how about
    _pinDrive(int pin, int val)  =>  if val then drvh pin, else drvl pin
    _pinDriveLow(int pin)           =>  drvl pin
    _pinDriveHigh(int pin)           =>  drvh pin
    _pinDriveNot(int pin)         =>  drvnot pin
    _pinDriveRandom(int pin)         =>  drvrnd pin
    _pinFloat(int pin)           =>  fltl pin ***
    _pinRead(int pin)            =>  if testp pin then return 1, else return 0
    

    same goes for the other ones like _pinHigh, _pinLow. _pinWrite(int pin, int value) for single bit in/out

    Just think about it guys, you will need all of them for every pin, even to configure your smart pins. Missing is Setting the pullup/pulldown values and the combine with neighboring pins settings, they will need macros too, since they work in smart pin modes and in digital modes.

    For what is _drvrnd good? randomly switching the pullups or the pulldowns on a input pin on and off?

    Mike
  • RossHRossH Posts: 5,476
    msrobots wrote: »
    am I over reacting?

    Maybe a little :)

    It may just be my personal taste, but I think it is directly related to the "expressiveness" (or "density") of the language itself - by which I mean the amount of code you have to type to express some high level concept, or the amount of code you have read to understand a concept someone else has coded.

    When I am programming with C, which is a language with very low expressiveness, I find short names to be useful, because it means that on one line - or in one block, or on one page - you can type (or read) enough code to embody (or grasp) the entire concept being expressed. And so in C I use - and expect others to use - short names. Many people claim long names improve the readability of C, and this is true - but at the same time they reduce the expressiveness.

    However, when I am programming in Ada, which is a very highly expressive language - i.e. a language in which you can express in a single line concepts that you might struggle to express in a page of C - I find long variable names more useful. In this case they can help you unpack the dense constructs that the language uses.
  • msrobotsmsrobots Posts: 3,709
    edited 2019-08-31 03:37
    interesting. Expressiveness.

    I strongly disagree. Because that what 'you easy see in one page', you see it BECAUSE you have the concept in your mind. 20 years later you or someone else does not have that concept in his or her mind.

    There is the actual problem.

    COBOL is on the other end of that scale, you will need to type a lot to write really less dense code, the expressiveness is quite low in COBOL. But most of it is clear readable English language even the Manager of the sales department or the accountant could read the used formula and understand it enough to say YES that is correct FIRST the TAXes, than rounding THEN ….

    But code density was newer something COBOL is known for. Latest studies say that still 90% of all source code is in COBOL, but just running ~70% of major Government, Banking and Insurance systems, USPS, Airlines, they all still use Mainframes. But the Source Code is humongous at every place I had to show up. Alas most people there very happy and relaxed. Even other programmer working there for years.

    Readability is the important factor, not "do I really need to type 'loat' behind _pinf ???"

    And modern editors take care of that code completion, you do just type it once completely...

    - an yes working on mainframes is part of me hating single letter variables and commands. - you have always to look them up again.

    Mike
  • A devilish concept would be to steal all the names FROM Arduino, even for SPIN, when possible, to make porting more easy.

    No need for much discussion, if Arduino has something doing that, steal that name, if not create somethin alike them.

    On the end these are just NAMES.

    Why create new ones when a big group already is familiar with some syntax

    just saying...

    Mike
  • RossHRossH Posts: 5,476
    edited 2019-08-31 04:14
    msrobots wrote: »
    I strongly disagree. Because that what 'you easy see in one page', you see it BECAUSE you have the concept in your mind. 20 years later you or someone else does not have that concept in his or her mind.

    Again, this might be a personal thing, but I rarely "read" code a symbol or even a line at a time - at least not to grasp the overall concepts embodied in it. I scan the whole program, look at the structure, perhaps diving in to a few points here and there to clear up some details. At this level, apart from a few key entities that are generally easy to spot, the individual names of variables or functions matters very little.

    As for COBOL, well it is the "Ulysses" of programming languages - i.e. a classic that is extremely verbose, has no apparent structure, and is quite impossible to read :)
  • msrobotsmsrobots Posts: 3,709
    edited 2019-08-31 04:43
    That's not fair. :depressed: Ulysses. :depressed: its just old but not obsolete.

    Honestly in one of the movies you see source code of the terminator and it is COBOL source. no wonder.

    But back to my devilish plan. We have all important developer in close contact to each other. All of you have agreed to keep it in sync is good and you are willing to agree NOW since it would make so much sense to do. Even Chip has agreed to change one keyword. He will be the hardest to convince.

    And I am just talking about NAMES here, not concepts. And compatible parameter sets. There where it does the same, name it the same.

    If not keep the naming convention so that it looks familiar.

    Now is the time to form a basis where everything else will fall in. We got that clock thing settled. All agree on the same HUB location. All prior attempts on the P1failed because there never was a agreement. To much code out already. This is different.

    There might be a agreement to use the same names for basic inbuild functions, currently to use the SPIN ones. To make ports more easy from one P2 program language to the other P2 program language for examples out of the OBEX. Very good Idea. Currently is nothing in the OBEX we try that out later, but the Idea is perfect.

    What really would be nice is if people NOT using a P2 may consider to switch over. And as more common the language is for them as more likely they feel home.

    And this is especially important for the C and C++ part. Swim to far away from the Arduino pond and you are out of sight.

    Same Names where possible, same Parameters where possible, try to adapt to a consistent readable naming system. I never had a Arduino, but I am addicted to read source codes. And their system seem to attract people. Why not make it more easy for people to switch.

    There cant be that much basic macros in Arduino, thinking about how many different HW is supported. There must be some base set for I/O what libraries use.

    And there we need to get as close as possible to that.

    Mike
  • RossHRossH Posts: 5,476
    msrobots wrote: »
    And I am just talking about NAMES here, not concepts. And compatible parameter sets. There where it does the same, name it the same.

    I think you will find the instances where it "does the same" to be smaller than you think. Some basic pin operations would probably be about the extent of it. And in such cases, if you are just talking names then a simple set of #defines would do the job perfectly well.

    If you are Arduino literate (I am not) then you might try coming up with an "arduino.h" header file.
  • RossH wrote: »
    msrobots wrote: »
    And I am just talking about NAMES here, not concepts. And compatible parameter sets. There where it does the same, name it the same.

    I think you will find the instances where it "does the same" to be smaller than you think. Some basic pin operations would probably be about the extent of it. And in such cases, if you are just talking names then a simple set of #defines would do the job perfectly well.

    If you are Arduino literate (I am not) then you might try coming up with an "arduino.h" header file.

    well I am not either, just thinking about a possible opportunity here we could miss by naming things like the P1 was or just fishing it out of the air, reinventing the wheel again, instead of using a common accepted naming set.

    You know, the RISCV Idea, why different instruction sets for the same operation. Can be extended but should basically implement a 'common' subset.

    Mike
  • msrobots wrote: »
    That's not fair. :depressed: Ulysses. :depressed: its just old but not obsolete.

    Honestly in one of the movies you see source code of the terminator and it is COBOL source. no wonder.

    Sorry Mike, the Terminator source isn't all COBOL, even if there is some in there. The source that jumps out at me in the Terminator's listing is actually 6502 assembler.
  • msrobotsmsrobots Posts: 3,709
    edited 2019-08-31 09:55
    AJL wrote: »
    msrobots wrote: »
    That's not fair. :depressed: Ulysses. :depressed: its just old but not obsolete.

    Honestly in one of the movies you see source code of the terminator and it is COBOL source. no wonder.

    Sorry Mike, the Terminator source isn't all COBOL, even if there is some in there. The source that jumps out at me in the Terminator's listing is actually 6502 assembler.

    yes.

    6502 assembler and COBOL. As far as I now there never was a COBOL for 6502 so he had more then one processor.

    But COBOL survived and the 6502 too.

    WAY off Topic, sorry
  • AJL wrote: »
    msrobots wrote: »
    That's not fair. :depressed: Ulysses. :depressed: its just old but not obsolete.

    Honestly in one of the movies you see source code of the terminator and it is COBOL source. no wonder.

    Sorry Mike, the Terminator source isn't all COBOL, even if there is some in there. The source that jumps out at me in the Terminator's listing is actually 6502 assembler.

    That would be Benders brain. :)

    259 x 194 - 12K
  • Publison wrote: »
    AJL wrote: »
    msrobots wrote: »
    That's not fair. :depressed: Ulysses. :depressed: its just old but not obsolete.

    Honestly in one of the movies you see source code of the terminator and it is COBOL source. no wonder.

    Sorry Mike, the Terminator source isn't all COBOL, even if there is some in there. The source that jumps out at me in the Terminator's listing is actually 6502 assembler.

    That would be Benders brain. :)

    Yes, Bender too.
  • RossHRossH Posts: 5,476
    edited 2019-09-03 08:23
    It has just occurred to me that I may be misinterpreting what _waitcnt() and _pollcnt() are supposed to do.
    uint32_t  _pollcnt(uint32_t tick);
    void      _waitcnt(uint32_t tick);
    

    I was assuming that _waitcnt() pauses the cog until the counter reaches the specified number of ticks - as it does on the P1 - and I use CT1 for that purpose. But since we have no other way to set CT1 (at least, not in this set of functions), then what does _pollcnt() do?

    Perhaps _waitcnt() was supposed to set the value of ct1 and _pollcnt() tells you if ct1 has been reached?

    If so, then the name _waitcnt() is very bad indeed, since it is too easily confused with the P1 instruction of the same name. It would be better to call it _setcnt() or something similar.

    Can anyone elucidate?

    EDIT: "until the counter reaches the specified number of ticks", not "for the specified number of ticks"!
  • AJLAJL Posts: 517
    edited 2019-09-03 09:32
    RossH wrote: »
    It has just occurred to me that I may be misinterpreting what _waitcnt() and _pollcnt() are supposed to do.
    uint32_t  _pollcnt(uint32_t tick);
    void      _waitcnt(uint32_t tick);
    

    I was assuming that _waitcnt() pauses the cog until the counter reaches the specified number of ticks - as it does on the P1 - and I use CT1 for that purpose. But since we have no other way to set CT1 (at least, not in this set of functions), then what does _pollcnt() do?

    Perhaps _waitcnt() was supposed to set the value of ct1 and _pollcnt() tells you if ct1 has been reached?

    If so, then the name _waitcnt() is very bad indeed, since it is too easily confused with the P1 instruction of the same name. It would be better to call it _setcnt() or something similar.

    Can anyone elucidate?

    EDIT: "until the counter reaches the specified number of ticks", not "for the specified number of ticks"!

    ADDIT: GETCT reads the lower 32 bits of the current system tick counter if WC condition is not specified.

    ADDCTx sets the CTx event tick count by adding S into D.
    POLLCTx checks to see if the CTx event has occurred, setting the defined flag and clearing the CTx event flag, but continues execution.
    WAITCTx pauses execution until the CTx event occurs, then clears the CTx event flag, with an optional timeout set by a prior SETQ which is indicated in the defined flag.

    There are three timers that can be used simultaneously (CT1, CT2, and CT3), and jumps that can occur based on the status on the event flag (JCTx and JNCTx).
  • Cluso99Cluso99 Posts: 18,069
    when you do an
    add ctx,someval
    the instruction magically also sets the counter for waitcnt/pollcnt in parallel to saving in ctx (x=1/2/3).
    It was a way to have more counters, and save instruction opcodes. But it's not intuitive.
  • evanhevanh Posts: 16,023
    edited 2019-09-03 09:21
    WAITxxx and POLLxxx monitor event flags in their expected ways, they don't configure the event. The event can also generate an interrupt.

    ADDCT1 sets that particular event running. ADDCTx needs a working register to hold the last calculated target count for the event trigger. It is intended explicitly for metronomic events like a 1 kHz timer interrupt. Although, it still needs rearmed with another ADDCTx for each recurring event.

    The alternative much simpler pause, that the prop1 WAITCNT was mostly used for, now has its own instruction - WAITX

  • evanhevanh Posts: 16,023
    So, depending on what the program is doing, now dictates which instructions are the right ones to use.


    Also, because of the different way to setup the prop2's events, there is no way to have an instruction timing exact replacement for the prop1's WAITCNT.

  • RossHRossH Posts: 5,476
    AJL wrote: »
    RossH wrote: »
    It has just occurred to me that I may be misinterpreting what _waitcnt() and _pollcnt() are supposed to do.
    uint32_t  _pollcnt(uint32_t tick);
    void      _waitcnt(uint32_t tick);
    

    I was assuming that _waitcnt() pauses the cog until the counter reaches the specified number of ticks - as it does on the P1 - and I use CT1 for that purpose. But since we have no other way to set CT1 (at least, not in this set of functions), then what does _pollcnt() do?

    Perhaps _waitcnt() was supposed to set the value of ct1 and _pollcnt() tells you if ct1 has been reached?

    If so, then the name _waitcnt() is very bad indeed, since it is too easily confused with the P1 instruction of the same name. It would be better to call it _setcnt() or something similar.

    Can anyone elucidate?

    EDIT: "until the counter reaches the specified number of ticks", not "for the specified number of ticks"!

    ADDIT: GETCT reads the lower 32 bits of the current system tick counter if WC condition is not specified.

    ADDCTx sets the CTx event tick count by adding S into D.
    POLLCTx checks to see if the CTx event has occurred, setting the defined flag and clearing the CTx event flag, but continues execution.
    WAITCTx pauses execution until the CTx event occurs, then clears the CTx event flag, with an optional timeout set by a prior SETQ which is indicated in the defined flag.

    There are three timers that can be used simultaneously (CT1, CT2, and CT3), and jumps that can occur based on the status on the event flag (JCTx and JNCTx).

    Yes, I know all that. I have all the relevant functions elsewhere - i.e. _add_XXX(), _wait_XXX(), _poll_XXX(), where XXX = (CT1, CT2, CT3).

    It seems to me that the best solution is to just delete both _waitcnt() and _pollcnt() from "propeller2.h", since they serve no useful purpose.

    This is what I will do.
  • ersmithersmith Posts: 6,068
    edited 2019-09-03 10:53
    _waitcnt can do the same thing as Spin1 WAITCNT. e.g.:
    void _waitcnt(unsigned x)
    {
        __asm {
            addct1 x, #0
            waitct1
        }
    }
    

    I don't know what the Spin2 WAITCNT does, but probably something similar. I guess POLLCNT probably returns a nonzero value if ct1 is greater than the argument x.

  • evanh wrote: »
    Also, because of the different way to setup the prop2's events, there is no way to have an instruction timing exact replacement for the prop1's WAITCNT.

    I think "addctX" followed by "waitctX" should be the same as prop1 WAITCNT. I suppose there is a slight hazard in that it's not atomic, but if the delay is anything reasonable (more than a few cycles) that shouldn't matter.

  • RossHRossH Posts: 5,476
    ersmith wrote: »
    _waitcnt can do the same thing as Spin1 WAITCNT. e.g.:
    void _waitcnt(unsigned x)
    {
        __asm {
            addct1 x, #0
            waitct1
        }
    }
    

    I don't know what the Spin2 WAITCNT does, but probably something similar. I guess POLLCNT probably returns a nonzero value if ct1 is greater than the argument x.

    I already have a _waitcnt() that does that, to emulate what that function did on the P1. I can duplicate the definition in propeller2.h if you want.

    But since we don't have a function to set ct1, I don't see any benefit in including _pollcnt() in propeller2.h.
  • evanhevanh Posts: 16,023
    edited 2019-09-03 11:33
    I can't remember the details at all. Cluso was attempting to automatically port prop1 OBEX packages and that was one of the things that wasn't solvable without case by case intervention.

    A simple pause was easy enough but metronomic timing had a caveat that might need recrafting attention. But then, I guess there was likely attention needed for other reasons as well.

  • evanhevanh Posts: 16,023
    edited 2019-09-03 11:46
    WAITX can be used for metronomic looping as well. Just have to compute the loop time in instructions then subtract that from the desired period. It even has less instructions inside the loop because no longer having to rearm the event.

    EDIT: Doesn't work with the erratic hub accesses though, including CORDIC, so pretty limited method.
    EDIT2: But using the FIFO would work. And with RD/WRFAST D bit31 set you can even alternate between reading and writing hubRAM without any instruction stalling.
  • evanh wrote: »
    I can't remember the details at all. Cluso was attempting to automatically port prop1 OBEX packages and that was one of the things that wasn't solvable without case by case intervention.
    Perhaps it was that the Prop2 won't let you sleep more than 2^31 ticks, whereas I think Prop2 allowed 2^32-1? But I don't find that too onerous in practice (although to emulate the 64 bit cnt with an interrupt routine I had to interrupt twice as often as I thought I would).


  • RossH wrote: »
    ersmith wrote: »
    _waitcnt can do the same thing as Spin1 WAITCNT. e.g.:
    void _waitcnt(unsigned x)
    {
        __asm {
            addct1 x, #0
            waitct1
        }
    }
    

    I don't know what the Spin2 WAITCNT does, but probably something similar. I guess POLLCNT probably returns a nonzero value if ct1 is greater than the argument x.

    I already have a _waitcnt() that does that, to emulate what that function did on the P1. I can duplicate the definition in propeller2.h if you want.

    I think if there's a Spin2 function named WAITCNT then there should be a _waitcnt function in propeller2.h.
    But since we don't have a function to set ct1, I don't see any benefit in including _pollcnt() in propeller2.h.

    I expect pollcnt would be implemented as something like:
    int _pollcnt(unsigned destcycle)
    {
        unsigned curcycle;
    
        // get current value of the counter into curcyle
        curcycle = _cnt();
     
        // if (destcycle - curcycle) has the high bit set,
        // then the curcycle is past destcycle and the event
        // should be signalled
        return (destcycle - curcycle) >> 31;
    }
    

    That would be pretty easy to write in user code, so it's redundant in propeller2.h. On the other hand if Spin2 has it, we should probably keep it as well.
  • Cluso99Cluso99 Posts: 18,069
    In P1 waitcnt effectively put the cog to sleep and so reducing power. Other than this, there are alternate solutions to replace waitcnt.

    Biggest problem in converting P1 PASM to P2 is the call/jmpret/ret and inline instruction modification.
  • evanhevanh Posts: 16,023
    edited 2019-09-05 15:04
    ersmith wrote: »
    evanh wrote: »
    I can't remember the details at all. Cluso was attempting to automatically port prop1 OBEX packages and that was one of the things that wasn't solvable without case by case intervention.
    Perhaps it was that the Prop2 won't let you sleep more than 2^31 ticks, whereas I think Prop2 allowed 2^32-1? But I don't find that too onerous in practice (although to emulate the 64 bit cnt with an interrupt routine I had to interrupt twice as often as I thought I would).
    It was only something of concern for the automatic conversion process.. Looking up the prop1 WAITCNT, I see it does both the waiting and the relative add in the one instruction. That'll be it, in the prop2 that is now split between ADDCTx and WAITCTx.
  • RossHRossH Posts: 5,476
    I have finally gotten back to working on this library, and I have found yet another problem I should have anticipated ... :(

    Everything was going well until I got to the COMPACT mode. Catalina uses the same code generator for COMPACT mode on both the P1 and P2, and of course that code generator assumes that DIRA, OUTA, INA etc (instead of _DIRA, _OUTA, _INA etc) are the special register names. This is because it was originally written for the P1 and those were the names used by various C compilers on the P1.

    I can't figure out a way to change that without requiring different code generators for COMPACT mode on the P1 and P2, which is something I actually worked hard to avoid. **

    So - at least for the moment - I am going to stick with DIRA, OUTA, INA etc, and add some #defines to support the Propeller 2 naming convention, rather than the other way around - something like ...
    /*
     * special cog register names
     */
    
    extern volatile uint32_t IJMP3; 
    extern volatile uint32_t IRET3;
    extern volatile uint32_t IJMP2;
    extern volatile uint32_t IRET2;
    extern volatile uint32_t IJMP1;
    extern volatile uint32_t IRET1;
    extern volatile uint32_t PA;
    extern volatile uint32_t PB;
    extern volatile uint32_t PTRA;
    extern volatile uint32_t PTRB;
    extern volatile uint32_t DIRA;
    extern volatile uint32_t DIRB;
    extern volatile uint32_t OUTA;
    extern volatile uint32_t OUTB;
    extern volatile uint32_t INA;
    extern volatile uint32_t INB;
    
    #ifdef _PROP2_NAMES
    /*
     * For compatibility with other compilers, where the special register
     * names have a leading underscore, we provide #defines to allow
     * programs written for those compilers to work with Catalina names:
     */
    #define _IJMP3 IJMP3
    #define _IRET4 IRET3
    #define _IJMP2 IJMP2
    #define _IRET2 IRET2
    #define _IJMP1 IJMP1
    #define _IRET1 IRET1
    #define _PA    PA
    #define _PB    PB
    #define _PTRA  PTRA
    #define _PTRB  PTRB
    #define _DIRA  DIRA
    #define _INA   INA
    #define _OUTA  OUTA
    #define _DIRB  DIRB
    #define _INB   INB
    #define _OUTB  OUTB
    #endif
    
    

    Note that this solution does not cause problems if (for example) DIRA or _DIRA are used consistently to refer to either the special register or to a local variable - it only causes problems if both DIRA and _DIRA are used in the same scope with the expectation that one refers to the special register while the other refers to a local variable (either way around). Such code would compile but then fail silently. However, I can't help wondering how common this situation is likely to be in practice.

    So this is not a perfect solution, but having to define the symbol _PROP2_NAMES to get the program to compile at least alerts the user that there may be a problem. I may be able to come up with a better solution, but this will at least allow me to finish the library and get the next release out.

    ** For various reasons I do currently have different code generators for TINY mode, but I had expected to be able to re-unify those again soon. Only NATIVE mode should need different code generators for the P1 and the P2. And yes, Catalina did have a NATIVE mode on the P1. It was never released.
  • RossH wrote: »
    Note that this solution does not cause problems if (for example) DIRA or _DIRA are used consistently to refer to either the special register or to a local variable - it only causes problems if both DIRA and _DIRA are used in the same scope with the expectation that one refers to the special register while the other refers to a local variable (either way around).
    It isn't legal for programs to use the underscore version -- names beginning with underscore and a capital letter are reserved for the system / compiler. So _DIRA should never be used in a user program.

    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.

Sign In or Register to comment.