Shop OBEX P1 Docs P2 Docs Learn Events
Catalina - ANSI C and Lua for the Propeller 1 & 2 - Page 13 — Parallax Forums

Catalina - ANSI C and Lua for the Propeller 1 & 2

11011131516

Comments

  • evanhevanh Posts: 16,045
    edited 2024-05-19 05:15

    @RossH said:

    @evanh said:
    Any idea why the existing symlinks don't work?

    No. I was going to suggest that myself as a possible alternative. Perhaps a permissions problem? Does stty -F work on the symlinks?

    Okay, found the error message in the source code and added a %s for path string and it prints this: /dev/serial/by-id/usb-Parallax_I instead of this /dev/serial/by-id/usb-Parallax_Inc_Propeller_P2-ES_EVAL_P23YOO42-if00-port0. So that explains the failed open.

    Right and the array is this: char comports[PORT_COUNT][NAME_LENGTH+1]={ ...

    Changing NAME_LENGTH = 80 fixes it without actually adding the entry to that string array! Now I wonder why even have the array ...

  • RossHRossH Posts: 5,485
    edited 2024-05-19 06:13

    @evanh

    I found spin2cpp - see here.

    Looks like there have been a few changes since I last used it, and it has a few bugs - but with some manual tweaking here is the output from using it to convert the Spin2 code, plus my own manual re-coding of the PASM functions. The spin2cpp command I used was:

    spin2cpp --ccode pllset.spin2

    Note: I had to comment out all the PASM stuff, avoid the --main and --p2 command line options, manually change #include <propeller.h> to #include <propeller2.h> and manually add the main() function.

    Also, I am not sure what the "compiled constants" clkfreq_ and clkmode_ represent - presumably this is a Flexspin C thing - so I just left them undefined - define them before use, or on the command line to make the code compile.

    For example, it compiles with the following command - but HAS NOT BEEN TESTED AT ALL!!!

    catalina -p2 pllset.c -lci -D clkmode_=0 -D clkfreq_=0

    Here is pllset.h ...

    // automatically generated by spin3cpp v7.0.0-beta-- on Sun May 19 00:55:39 2024
    // command line: build/spin2cpp --ccode pllset.spin2 
    
    // automatically generated by spin2cpp v7.0.0-beta-- on Sun May 19 00:55:39 2024
    // command line: build/spin2cpp --ccode pllset.spin2 
    
    #ifndef pllset_Class_Defined__
    #define pllset_Class_Defined__
    
    #include <stdint.h>
    
    
    typedef struct pllset {
      char dummy__;
    } pllset;
    
      int32_t pllset_pllset(int32_t targetfreq);
      int32_t pllset_muldiv65(int32_t mult1a, int32_t mult2a, int32_t divisora);
      int32_t pllset_div33(int32_t dividend, int32_t divisor);
    #endif
    

    Here is pllset.c ...

    /* 
      PLLSET() - An extension to CLKSET() that automatically finds optimal component multiplier and dividers
        when setting the PLL.  Based on Chip's posted routine:
        https://forums.parallax.com/discussion/comment/1486815/#Comment_1486815
    
      Takes one parameter: Desired sysclock frequency.  Also uses the compiled constants:  clkfreq_ and clkmode_.
    
      Returns the clock frequency of XI pin.  This may default to 20 MHz when no clock constants defined,
        or -1 if clock mode wasn't changed due to out of range calculation.
    
      MULDIV65() - A revision of MULDIV64() modified to round-to-nearest instead of rounding down.
      DIV33() - Regular 32-bit integer divide but with round-to-nearest.
     */
    #include <stdlib.h>
    #define __SPIN2CPP__
    #include <propeller2.h>
    #include "pllset.h"
    
    #if defined(__GNUC__)
    #define INLINE__ static inline
    #else
    #define INLINE__ static
    #ifndef __FLEXC__
    #define waitcnt(n) _waitcnt(n)
    #define coginit(id, code, par) _coginit((unsigned)(par)>>2, (unsigned)(code)>>2, id)
    #define cognew(code, par) coginit(0x8, (code), (par))
    #define cogstop(i) _cogstop(i)
    #endif /* __FLEXC__ */
    #ifdef __CATALINA__
    #define _CNT CNT
    #define _clkfreq _clockfreq()
    
    #endif
    #endif
    
    INLINE__ int32_t Shr__(uint32_t a, uint32_t b) { return (a>>b); }
    int32_t pllset_pllset(int32_t targetfreq)
    {
      int32_t   mode, mult, divd, divp, post, Fpfd, Fvco, error, besterror, vcolimit;
      int32_t xinfreq;
      // keep %CC, force %SS, ditch the rest
      mode = (clkmode_ & 0xf) | 0x3;
      if ((Shr__(clkmode_, 24)) & 0x1) {
        // compiled with PLL on
        divd = ((Shr__(clkmode_, 18)) & 0x3f) + 1;
        mult = ((Shr__(clkmode_, 8)) & 0x3ff) + 1;
        divp = ((Shr__(clkmode_, 4)) + 1) & 0xf;
        divp = ((divp) ? divp * 2 : 1);
        xinfreq = pllset_muldiv65(divp * divd, clkfreq_, mult);
      } else {
        if ((Shr__(clkmode_, 2)) & 0x3) {
          // compiled with PLL off
          // clock pass-through
          xinfreq = clkfreq_;
        } else {
          // unknown build mode
          // default to 20 MHz crystal
          xinfreq = 20000000;
          // default to 15 pF loading
          mode = 11;
        }
      }
      // _errfreq at 1.0% of targetfreq
      besterror = pllset_div33(targetfreq, 100);
      vcolimit = targetfreq + besterror;
      vcolimit = ((vcolimit < 201000000) ? 201000000 : vcolimit);
      for(post = 0; post < 16; post++) {
        divp = ((post) ? post * 2 : 1);
        for(divd = 64; divd >= 1; --divd) {
          Fpfd = pllset_div33(xinfreq, divd);
          mult = pllset_muldiv65(divp * divd, targetfreq, xinfreq);
          Fvco = pllset_muldiv65(xinfreq, mult, divd);
          if ((((Fpfd >= 250000) && (mult <= 1024)) && (Fvco > 99000000)) && (Fvco <= vcolimit)) {
            error = pllset_div33(Fvco, divp) - targetfreq;
            if ((abs(error)) <= (abs(besterror))) {
              // the last iteration at equality gets priority
              besterror = error;
              mode = ((((mode & 0xf) | (1 << 24)) | ((divd - 1) << 18)) | ((mult - 1) << 8)) | (((post - 1) & 0xf) << 4);
            }
          }
        }
      }
      if ((Shr__(mode, 24)) & 0x1) {
        // PLL-ON bit set when calculation is valid
        // make the frequency change, also sets debug port baud as of Pnut v36
        clkset(mode, targetfreq + besterror);
      } else {
        // failed, no change
        xinfreq = -1;
      }
      return xinfreq;
    }
    
    int32_t pllset_muldiv65(int32_t mult1, int32_t mult2, int32_t divisor)
    {
      return PASM(
         "      qmul    _PASM(mult1), _PASM(mult2)\n"
         "          mov     r0, _PASM(divisor)\n"
         "          shr     r0, #1\n"
         "          getqx   _PASM(mult1)                  ' lower 32 bits of 64-bit intermediate\n"
         "          getqy   _PASM(mult2)                  ' upper 32 bits of 64-bit intermediate\n"
         "          add     _PASM(mult1), r0   wc         ' round-to-nearest\n"
         "          addx    _PASM(mult2), #0              ' round-to-nearest\n"
         "          setq    _PASM(mult2)\n"
         "          qdiv    _PASM(mult1), _PASM(divisor)\n"
         "          getqx   r0\n"
      );
    }
    
    int32_t pllset_div33(int32_t dividend, int32_t divisor)
    {
      return PASM(
        "           mov     r0, _PASM(divisor)\n"
        "           shr     r0, #1\n"
        "           add     _PASM(dividend), r0           ' round-to-nearest\n"
        "           qdiv    _PASM(dividend), _PASM(divisor)\n"
        "           getqx   r0\n"
      );
    }
    
    /*  license  */
    /* 
    
      Terms of Use: MIT License
    
      Permission is hereby granted, free of charge, to any person obtaining a copy
      of this software and associated documentation files (the "Software"), to deal
      in the Software without restriction, including without limitation the rights
      to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      copies of the Software, and to permit persons to whom the Software is
      furnished to do so, subject to the following conditions:
    
      The above copyright notice and this permission notice shall be included in all
      copies or substantial portions of the Software.
    
      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      SOFTWARE.
    
     */
    
    void main() {
    }
    
    

    Ross.

    EDIT: I decided to just leave clkmode_ and clkfreq_ undefined. You need to define them to compile the program.

  • evanhevanh Posts: 16,045
    edited 2024-05-19 06:17

    @RossH said:
    Also, I am not sure what the "compiled constants" clkfreq_ and clkmode_ represent - presumably this is a Flexspin C thing - so I just defined them to be Catalina's_clockmode() and _clockfreq() functions to make the code compile.

    Fair enough. I'll have to make backups at boot time to perform the equivalent.

    Those symbols originated in Spin2. EDIT: Or maybe more specifically, Pasm2. They are documented in the section "Clock Setup" of the Spin2 Manual. They always exist based on what the compiler finally decides to be the compile time settings for clockmode and clockfreq. Eric later added them to his Spin2 compiler to match what Chip had done.

    I think they were originally there for early pure Pasm2 programs that were devoid of any wrapper code. They provided a precomputed constant for an easy to use clock mode settings. They have since become an excellent Spin-time resource for managing the adjusting of clock frequency because they provide a reliable detail of the board level crystal info.

  • evanhevanh Posts: 16,045
    edited 2024-05-19 06:45

    @RossH said:
    Note: I had to comment out all the PASM stuff, ...

    What options do I have for adding Pasm as functions? I have a rather lot of streamer/FIFO cogexec code I want to try out.

  • RossHRossH Posts: 5,485

    @evanh said:

    Now I wonder why even have the array ...

    You'd have to ask the original author, but I think it was because on Windows the names of com ports are much simpler (i.e. COM1 .. COMn) whereas on Linux they are not, so it is not so easy to step through the set of all ports if you are auto-discovering.

  • RossHRossH Posts: 5,485

    @evanh said:

    @RossH said:
    Note: I had to comment out all the PASM stuff, ...

    What options do I have for adding Pasm as functions? I have a rather lot of streamer/FIFO cogexec code I want to try out.

    Look at what I did to re-code the Spin2 inline PASM using Catalina's inline PASM syntax. It's pretty mechanical. The only thing that may not be obvious is that I replaced "result" with "r0" because in Catalina all PASM statements return the integer in register r0.

  • RossHRossH Posts: 5,485
    edited 2024-05-19 07:11

    @evanh said:

    @RossH said:
    Also, I am not sure what the "compiled constants" clkfreq_ and clkmode_ represent - presumably this is a Flexspin C thing - so I just defined them to be Catalina's_clockmode() and _clockfreq() functions to make the code compile.

    Fair enough. I'll have to make backups at boot time to perform the equivalent.

    I edited that code after I first posted it - the new version doesn't define them at all, so you can define them yourself.

    Now that I know what they are, I know that I don't have C symbols defined for them, but I do have PASM symbols _CLOCKMODE and _CLOCKFREQ, so you could simply add a couple of C functions to access the PASM symbols from C ...

    #define clkfreq_ clockfreq_()
    #define clkmode_ clockmode_()
    
    uint32_t clockfreq_() {
       return PASM (" mov r0, ##_CLOCKFREQ\n");
    }
    
    uint32_t clockmode_() {
       return PASM (" mov r0, ##_CLOCKMODE\n");
    }
    

    Ross.

  • RossHRossH Posts: 5,485

    @evanh said:

    @RossH said:
    Note: I had to comment out all the PASM stuff, ...

    What options do I have for adding Pasm as functions? I have a rather lot of streamer/FIFO cogexec code I want to try out.

    Cogexec is different. Catalina's Inline PASM currently always uses hubexec, not cogexec. To execute a significant chunk of code using cogexec, you have to turn it into a plugin which you then load into a cog manually whenever you need to execute it. For instance, see the new RANDOM example in the current release. That example is for a Propeller 1, but it would be similar for a Propeller 2.

    I suppose I could write a C "wrapper" that would take an arbitrary chunk of PASM and do all the messy stuff like loading it into a cog automatically. I'll give that some thought. It would be be a nice facility to have.

    Ross.

  • evanhevanh Posts: 16,045
    edited 2024-05-19 08:28

    @RossH said:
    ... do all the messy stuff like loading it into a cog automatically.

    What about running in the C cog instead of starting a fresh cog? That loads into the executing cog, or is already sitting there, and then calls that. More like an actual function then.

    PS: There is two options in Flexspin C. First option is the commonly used Fcache which is not so much a function call but a special attribute added to an inline __asm {} definition. This produces a temporary use of cogRAM where the code gets copied then jumped to as needed. The optimiser will automatically use this itself for small C loops.

    The second option is at the function definition, an attribute specifies if the function is going to be resident to cogRAM or lutRAM instead of the default hubRAM. The attribute applies to the whole function, C code and Pasm equally. I've not used this much for fear of swamping cog space.

  • RossHRossH Posts: 5,485

    @evanh said:

    @RossH said:
    ... do all the messy stuff like loading it into a cog automatically.

    What about running in the C cog instead of starting a fresh cog? That loads into the executing cog, or is already sitting there, and then calls that. More like an actual function then.

    That would work in NATIVE mode. But a better option (since it could be supported in all modes) would be provide a function to load and execute a PASM function from the LUT. I already do that, in fact. But I load them only once - it happens automatically on startup. I could provide a means to load the function manually, but it would be better if it could automatically detect if it was loaded and did not load itself again if it was not necessary. Maybe offer both options - a wrapper function that first checks if the function it calls is loaded, loads it if necessary and then calls it, plus a manual load method and a wrapper function that calls it assuming it is already loaded.

    I'll see what I can do.

  • evanhevanh Posts: 16,045

    @RossH said:
    Now that I know what they are, I know that I don't have C symbols defined for them, but I do have PASM symbols

    Nice, thanks, that's working perfect.

    PS: I now see precomputed 180 MHz sysclock is using 360 MHz VCO frequency! Not the ideal setting for full temperature range. You're safe from crashing conditions, because DIVP=2, but the PLL will self-limit the VCO below 360 MHz if the die temperature gets high. Which means a non-set frequency.

  • evanhevanh Posts: 16,045
    edited 2024-05-19 10:22

    @RossH said:

    @evanh said:

    Now I wonder why even have the array ...

    You'd have to ask the original author, but I think it was because on Windows the names of com ports are much simpler (i.e. COM1 .. COMn) whereas on Linux they are not, so it is not so easy to step through the set of all ports if you are auto-discovering.

    I've not investigated but have had no issue so far with chopping the Linux port array down to 8 with increased item size of 100.

    #ifndef _WIN32   /* Linux */
    
    #define PORT_COUNT 8
    
    //#define NAME_LENGTH 32 // OS X has long names ("/dev/cu.usbserial.XXXXXXXX")
    #define NAME_LENGTH 100 // Linux has even longer device specific names
    
    int Cport[PORT_COUNT];
    int error;
    
    struct termios new_port_settings;
    struct termios old_port_settings[PORT_COUNT];
    
    char comports[PORT_COUNT][NAME_LENGTH+1]={
       "/dev/ttyS0","/dev/ttyS1","/dev/ttyS2","/dev/ttyS3",
       "/dev/ttyUSB0","/dev/ttyUSB1","/dev/ttyUSB2","/dev/ttyUSB3"
    };
    
  • RossHRossH Posts: 5,485
    edited 2024-05-19 11:13

    @evanh said:

    @RossH said:
    Now that I know what they are, I know that I don't have C symbols defined for them, but I do have PASM symbols

    Nice, thanks, that's working perfect.

    PS: I now see precomputed 180 MHz sysclock is using 360 MHz VCO frequency! Not the ideal setting for full temperature range. You're safe from crashing conditions, because DIVP=2, but the PLL will self-limit the VCO below 360 MHz if the die temperature gets high. Which means a non-set frequency.

    If the defaults are not what you want, you can set all the clock options in the appropriate platform support file (e.g. P2CUSTOM.inc) or on the Catalina command line. Look up the Clock Configuration support section in Catalina's Reference Manual for the Propeller 2.

  • evanhevanh Posts: 16,045
    edited 2024-05-19 12:03

    Oh, I see, the old time PLL components have to be set manually. No biggie, of course I had assumed one could just specify a compile time sysclock frequency, then the compiler computes the clock mode for you.

    EDIT: Huh, it does compute a clock mode, and seems to do a good job, when -f <frequency> is specified in the compile command.

    So it's just the P2CUSTOM.inc that needs a little love then.

  • RossHRossH Posts: 5,485

    @evanh said:
    Oh, I see, the old time PLL components have to be set manually. No biggie, of course I had assumed one could just specify a compile time sysclock frequency, then the compiler computes the clock mode for you.

    EDIT: Huh, it does compute a clock mode, and seems to do a good job, when -f <frequency> is specified in the compile command.

    So it's just the P2CUSTOM.inc that needs a little love then.

    Yes, I usually set the frequency on the command line, which uses Chip's algorithm internally to calculate all the values. I don't often use on the defaults specified in the platform files, which I have never adjusted since I first wrote them. I'll do that in the next release.

  • RossHRossH Posts: 5,485
    edited 2024-05-20 00:58

    @evanh

    Using cogexec in NATIVE mode turns out to be trivial if you use the LUT:

    /*****************************************************************************
     *              Executing code from the LUT on a P2                          *
     *                                                                           *
     * This program demonstrates how inline PASM can be used to execute          *
     * code from the LUT using cogexec mode.                                     *
     *                                                                           *
     * This program only works in NATIVE mode on a Propeller 2.                  *
     *                                                                           *
     * Compile this program for using a command like:                            *
     *                                                                           *
     *    catalina -p2 cogexec.c -y -lci                                         *
     *                                                                           *
     *****************************************************************************/
    
    #include <propeller.h>
    #include <stdio.h>
    
    // make sure we are compiled using the correct processor and model
    #if !defined(__CATALINA_P2) || !defined(__CATALINA_NATIVE)
    #error PROGRAM MUST BE COMPILED IN NATIVE MODE ON THE PROPELLER 2
    #endif
    
    // lut_function - load and then call a cogexec function 
    int lut_function(int a, int b, int c, int d) {
       return PASM(
    
          " setq2 #(lut_end-lut_start-1)\n" // load code ...
          " rdlong 0, ##@lut_start\n"       // ... into LUT
          " call #lut_start\n"              // call the LUT code (cogexec)
          " jmp #\\@pasm_exit\n"            // jump to PASM exit (hubexec)
    
          " org $200\n"                     // locate next code at start of LUT
          "lut_start\n"                     // this function ...
          " mov r0, _PASM(d)\n"             // ...
          " add r0, _PASM(c)\n"             // ...
          " add r0, _PASM(b)\n"             // ... just sums ...
          " add r0, _PASM(a)\n"             // ...
          " ret\n"                          // ... its four arguments
          "lut_end\n"                       // 
    
          " orgh\n"                         // now back to hubexec
          "pasm_exit\n"                     
    
       );
    }
    
    void main(void) {
    
      // delcare some local variables
      int a = 1;
      int b = 2;
      int c = 3;
      int d = 4;
    
      printf("\nInline PASM can be executed using cogexec from the LUT\n\n");
      printf("lut_function returns %d (expecting 10)\n", lut_function(a,b,c,d));
    
      while(1);
    }
    

    Because it uses setq2, this version will only work in NATIVE mode. But in the next release I will add a primitive to do the LUT load in other modes, so it can also be done in TINY, COMPACT or XMM modes.

    Ross.

  • evanhevanh Posts: 16,045
    edited 2024-05-20 01:24

    Oh, man! That line formatting is going to drive me up the wall. I do a lot of coding in pasm.

    I gather the only way to keep the pasm formatting as normal is to make it a plugin? Which also always requires a fresh cog to run in? EDIT: Hang-on you've got a multitasking kernel feature. Can that run a plugin? Having lots of handler tasks would suit me fine I think.

  • evanhevanh Posts: 16,045

    Another idea I guess is to have a tool that generates the inline formatting from a regular Pasm source file.

  • RossHRossH Posts: 5,485
    edited 2024-05-20 02:13

    @evanh said:
    Oh, man! That line formatting is going to drive me up the wall. I do a lot of coding in pasm.

    You don't need to do it the way I do it, but remember that it is a single C string being passed to the PASM function, so while you can pass it as a single multi-line string, it at least needs \n added as line terminations. Here is another way you could format the function:

    // lut_function - load and then call a LUT function using cogexec
    int lut_function(int a, int b, int c, int d) {
       return PASM("                                 \n \
            setq2 #(lut_end-lut_start-1)             \n \
            rdlong 0, ##@lut_start                   \n \
            call #lut_start                          \n \
            jmp #\\@pasm_exit                        \n \
                                                     \n \
            org $200                                 \n \
           lut_start                                 \n \
            mov r0, _PASM(d)                         \n \
            add r0, _PASM(c)                         \n \
            add r0, _PASM(b)                         \n \
            add r0, _PASM(a)                         \n \
            ret                                      \n \
           lut_end                                   \n \
                                                     \n \
            orgh                                     \n \
           pasm_exit                                 \n \
       ");
    }
    

    It would be possible to write a script to handle the formatting job. I don't use inline PASM enough to make that a priority.

    I gather the only way to keep the pasm formatting as normal is to make it a plugin? Which also always requires a fresh cog to run in? EDIT: Hang-on you've got a multitasking kernel feature. Can that run a plugin? Having lots of handler tasks would suit me fine I think.

    Plugins are ordinary PASM programs, so the formatting is as normal PASM. Plugins are nothing special - Catalina is not even required to compile them - just a PASM assembler. Catalina just adds a standard way of managing and interacting with plugins (i.e. the Registry). Can't really see how multitasking would help here.

    Ross.

  • evanhevanh Posts: 16,045
    edited 2024-05-20 02:33

    Is FAT file access to SD cards using any plugin?
    I suspect there is a block driver level plugin. That's probably what I want to target replicating. There is lots of areas I've bounced around but SD cards, 4-bit SD protocol mode, is where I'm most active at right now. I'm hoping to apply what I've learned to both Flexspin and Catalina equally.

  • RossHRossH Posts: 5,485

    @evanh said:
    Is FAT file access to SD cards using any plugin?
    I suspect there is a block driver level plugin. That's probably what I want to target replicating. There is lots of areas I've bounced around but SD cards, 4-bit SD protocol mode, is where I'm most active at right now.

    Yes - see the file target\p2\cogsd.t

    This plugin implements both SD and CLOCK functions - but there is also a separate CLOCK plugin, so it really only needs to implement three functions - one to initialize the SD, one to read a sector, and one write a sector.

    Ross.

  • evanhevanh Posts: 16,045

    Thanks, good read.

  • RossHRossH Posts: 5,485
    edited 2024-05-28 22:26

    @evanh

    Just experimenting with a possible implementation of macros to simplify the definition of PASM functions to be executed from the LUT using cogexec ...

    Here are the macros in a header file (save it in a file called lut.h)...

    #ifndef LUT_H
    #define LUT_H
    
    /******************************************************************************
     *                                                                            *
     * Macros to simplify the definition of inline PASM functions to be           *
     * loaded into the LUT and executed using cogexec.                            *
     *                                                                            *
     * Use LUT_START and LUT_END to load and then call the function.              *
     *                                                                            *
     * Use LUT_BEGIN and LUT_END to load but not call the function, and then      *
     * use LUT_CALL to call the function.                                         *
     *                                                                            *
     * In either case, the function can be called (or called again) by using      *
     * LUT_CALL if it is still loaded                                             *
     *                                                                            *
     * For example ...                                                            *
     *                                                                            *
     * int function_1() {                                                         *
     *    LUT_START("$200","function_1","1");                                     *
     *    PASM(" mov r0, #99");                                                   *
     *    return LUT_END;                                                         *
     * }                                                                          *
     *                                                                            *
     * ... or ...                                                                 *
     *                                                                            *
     * load_function_2() {                                                        *
     *    LUT_BEGIN("$200","function_2","2");                                     *
     *    PASM(" mov r0, #101");                                                  *
     *    LUT_END;                                                                *
     * }                                                                          *
     *                                                                            *
     * int function_2() {                                                         *
     *    return LUT_CALL("function_2");                                          *
     * }                                                                          *
     *                                                                            *
     *                                                                            *
     ******************************************************************************/
    
    // load (if not loaded) and then call a LUT function
    //
    // ADDR - string address to put function (e.g. "$200")
    // NAME - string name of function (e.g. "function")
    // SIGN - string signature of function (e.g. "$DEADBEEF")
    //
    // Note: the ADDR should be a PASM constant >= $200 and
    //       the SIGN should be a PASM constant that is unique
    //       for all functions loaded at that address
    //
    #define LUT_START(ADDR, NAME, SIGN) \
       PASM("\n \
           rdlut r0,#"ADDR"-$200\n \
           cmp r0,##"SIGN" wz\n \
            if_z jmp #.lut_loaded\n \
           setq2 #(.lut_end-.lut_start-1)\n \
           rdlong "ADDR"-$200, ##@.lut_start\n \
          .lut_loaded\n \
           call #"NAME"\n \
           jmp #\\@.pasm_exit\n \
           org "ADDR"\n \
          .lut_start\n \
           long "SIGN"\n \
          "NAME"\n")
    
    // load (if not loaded) but do not call a LUT function
    //
    // ADDR - string address to put function (e.g. "$200")
    // NAME - string name of function (e.g. "function")
    // SIGN - string signature of function (e.g. "$DEADBEEF")
    //
    // Note: the ADDR should be a PASM constant >= $200 and
    //       the SIGN should be a PASM constant that is unique
    //       for all functions loaded at that address
    //
    #define LUT_BEGIN(ADDR, NAME, SIGN) \
       PASM("\n \
           rdlut r0,#"ADDR"-$200\n \
           cmp r0,##"SIGN" wz\n \
            if_z jmp #.lut_loaded\n \
           setq2 #(.lut_end-.lut_start-1)\n \
           rdlong "ADDR"-$200, ##@.lut_start\n \
          .lut_loaded\n \
           jmp #\\@.pasm_exit\n \
           org " ADDR " \n \
          .lut_start\n \
           long " SIGN" \n \
          " NAME "\n")
    
    // terminate the definition of a LUT function, which
    // was defined by either LUT_BEGIN or LUT_START
    //
    #define LUT_END \
       PASM("\n \
           ret\n \
          .lut_end\n \
           orgh\n \
          .pasm_exit\n")
    
    // call an already loaded LUT function, which was 
    // loaded by either LUT_BEGIN or LUT_START
    //
    // NAME - string name of LUT function (e.g. "function")
    //
    // Note: whatever function is currently loaded at the
    //       LUT address of this function will be called
    //
    #define LUT_CALL(NAME) \
       PASM("\n \
           call #" NAME "\n \
           jmp #\\@.pasm_exit2\n \
           orgh\n \
          .pasm_exit2\n")
    
    #endif
    

    And here is an example of using them ...

    /*****************************************************************************
     *              Executing code from the LUT on a P2                          *
     *                                                                           *
     * This program demonstrates how inline PASM can be used to execute          *
     * code from the LUT using cogexec mode.                                     *
     *                                                                           *
     * This program only works in NATIVE mode on a Propeller 2.                  *
     *                                                                           *
     * Compile this program for using a command like:                            *
     *                                                                           *
     *    catalina -p2 lut.c -y -lci                                         *
     *                                                                           *
     *****************************************************************************/
    
    #include <propeller.h>
    #include <stdio.h>
    #include "lut.h"
    
    // make sure we are compiled using the correct model for our PASM code
    #if !defined(__CATALINA_P2) || !defined(__CATALINA_NATIVE)
    #error PROGRAM MUST BE COMPILED IN NATIVE MODE ON THE PROPELLER 2
    #endif
    
    // function_1 - load (if not already loaded) 
    // the LUT function and also call it
    //
    int function_1(int a, int b, int c, int d) {
       LUT_START("$200","function_1","$DEADBEEF");
       PASM("                                        \n \
            mov r0, _PASM(d)                         \n \
            add r0, _PASM(c)                         \n \
            add r0, _PASM(b)                         \n \
            add r0, _PASM(a)                         \n \
       ");
       return LUT_END;
    }
    
    
    // load_function_2 - load (if not already loaded)
    // the LUT function but do not call it
    //
    void load_function_2(int a, int b, int c, int d) {
       LUT_BEGIN("$200", "function_2", "$FEEDFACE");
       PASM("                                        \n \
            mov r0, _PASM(d)                         \n \
            add r0, _PASM(c)                         \n \
            add r0, _PASM(b)                         \n \
            add r0, _PASM(a)                         \n \
            add r0, #100 ' differ from function_1!   \n \
       ");
       LUT_END; 
    }
    
    // function_2 - call an already loaded LUT function
    //
    // Note that the parameter profile must match the 
    // function that was used to load the LUT function,
    // but the names themselves do not need to match 
    //
    int function_2(int d, int e, int f, int g) {
       return LUT_CALL("function_2");
    }
    
    void main(void) {
    
      int a, b, c, d; // these are dummy parameters and not strictly required
    
      // load and execute function 1 ...
      printf("function_1 returned %d (expected 10)\n", function_1(1,2,3,4));
    
      // load function 2 but do not execute it ...
      load_function_2(a, b, c, d); 
    
      // now execute function 2 ...
      printf("function_2 returned %d (expected 110)\n", function_2(1,2,3,4));
    
      // load function 2 but do not execute it ...
      load_function_2(a, b, c, d); 
    
      // function 1 is no longer loaded, it will be re-load it and executed ...
      printf("function_1 returned %d (expected 26)\n", function_1(5,6,7,8));
    
      // load function 2 but do not execute it ...
      load_function_2(a, b, c, d); 
    
      // execute the loaded function 2 twice! ...
      printf("function_2 returned %d (expected 126)\n", function_2(5,6,7,8));
      printf("function_2 returned %d (expected 166)\n", function_2(15,16,17,18));
    
      // function 1 is no longer loaded, it will be re-load it and executed ...
      printf("function_1 returned %d (expected 66)\n", function_1(15,16,17,18));
    
      while(1);
    }
    

    These only work for NATIVE mode. I'll include something similar in the next release, also implemented for TINY, COMPACT and XMM SMALL and LARGE modes.

    EDIT: Fixed an issue if the ADDR parameter was not "$200" and a bug in checking if the code was already loaded.

  • Wingineer19Wingineer19 Posts: 291
    edited 2024-05-21 03:01

    Hi @RossH,

    In my spare time I've continued to experiment with the Multi-Memory model for the Prop1 using the XEPROM model for the Primary and the CMM model for the Secondary. I'm using just a FLiP module by itself for this test, and this arrangement has worked well in the past.

    But now that I've removed my previous 6.X version of Catalina and installed version 7.3 I've encountered a new error while attempting to compile the Secondary: Incompatible file size for layout 8.

    I have no idea what that means. I've never encountered that error before. The compiler didn't complain about any syntax errors or anything else wrong with the code, it just printed this error and thus refused to output the binary file.

    While you think that over, I will continue to examine both the Secondary and Primary code to see if I'm attempting to do something weird but so far I don't think so. I'll attach the two files to a later post once my analysis is complete and if the error remains.

    UPDATE

    Yup, it appears something is broken.

    I went over to the previous thread entitled, The Great Printf() Challenge and extracted the code that pertains to the Secondary and placed it into the attached file called secondary.c, while also extracting the code that pertained to the Primary and placed it into the attached file called primary.c.

    These two are known good files and compiled and worked perfectly under Catalina version 6.X.

    Now, while compiling using Catalina 7.3 the Secondary gives me the Incompatible file size for layout 8 error. It also didn't print the normal output showing the code, const, init, and file sizes but oddly enough it did output a secondary.binary file.

    I haven't attempted to compile the Primary yet because I need the correct output from the Secondary first...

    Definitely something strange going on here...

  • RossHRossH Posts: 5,485

    @Wingineer19 said:
    Hi @RossH,

    But now that I've removed my previous 6.X version of Catalina and installed version 7.3 I've encountered a new error while attempting to compile the Secondary: Incompatible file size for layout 8.

    I'll check it out and let you know.

    Ross.

  • RossHRossH Posts: 5,485
    edited 2024-05-21 23:26

    @Wingineer19 said:
    Hi @RossH,

    it appears something is broken.

    Yup. There was an incorrect file size check in the new binstats program (introduced in 7.1) which was causing it to abort when it tried to print the statistics for Propeller 1 programs where the binary ended up larger than 32k (which was ok in this case because the binary was not intended to be run, it was being compiled to extract the blob to be used in a multi-model program - I hadn't remembered that case!).

    Printing the file stats was definitely broken, but the binary itself was probably ok.

    In any case, attached is a patch that should fix it. Now, when I compile and load your programs, I first get ...

    Error - Change secondary Memory To 0x57D4

    ... which is probably to be expected, since the binary output will have changed very slightly with the 7.x releases.

    When I do that, I get a whole bunch of lines like ...

    theta=0.000000,sine=0.000000,cosine=1.000000
    theta=0.006283,sine=0.006283,cosine=0.999980
    theta=0.012566,sine=0.012566,cosine=0.999921
    theta=0.018850,sine=0.018848,cosine=0.999822
    theta=0.025133,sine=0.025130,cosine=0.999684
    ...
    

    Which also looks like it is expected.

    Please apply the attached patch (which should work for 7.1, 7.2, 7.3 or 7.4) and let me know how it goes. If it is all good I will release the patch officially.

    Ross.

    EDIT: Official patch release now available here. I realized I should not have called it 7.3.1 since there was already a release 7.3.1, so the patch is now called 7.4.1 but it is applicable to 7.1,7.2,7.3 or 7.4

  • @RossH said:
    Please apply the attached patch (which should work for 7.1, 7.2, 7.3 or 7.4) and let me know how it goes. If it is all good I will release the patch officially.

    Thanks. Yes, that appears to have fixed the problem.

    I will continue to experiment with compiling my code and let you know if I see any other strange stuff.

  • RossHRossH Posts: 5,485

    @Wingineer19 said:

    @RossH said:
    Please apply the attached patch (which should work for 7.1, 7.2, 7.3 or 7.4) and let me know how it goes. If it is all good I will release the patch officially.

    Thanks. Yes, that appears to have fixed the problem.

    I will continue to experiment with compiling my code and let you know if I see any other strange stuff.

    Ok, good. The official patch release is now available here. It is called 7.4.1 but is applicable to Catalina releases 7.1, 7.2, 7.3 or 7.4.

    I should not have called the beta 7.3.1 since there was already a release 7.3.1, so the official patch is 7.4.1 - but it is the same code as the beta version you have apart from the version number.

    Ross.

  • RossHRossH Posts: 5,485
    edited 2024-05-29 05:10

    Interesting ...

    I've just realized that the cogexec demo in this post is not restricted to executing PASM code from the LUT. It can also execute C code from the LUT.

    For instance, the following PASM function is taken from the example in that post:

    int function_1(int a, int b, int c, int d) {
       LUT_START("$200","function_1","$DEADBEEF");
       PASM("                                        \n \
            mov r0, _PASM(d)                         \n \
            add r0, _PASM(c)                         \n \
            add r0, _PASM(b)                         \n \
            add r0, _PASM(a)                         \n \
       ");
       return LUT_END;
    }
    

    This function could also have been written as:

    int function_1(int a, int b, int c, int d) {
       int result;
       LUT_START("$200","function_1","$DEADBEEF");
       result = a + b + c + d;
       LUT_END;
       return result;
    }
    

    The result is the same. Any C code between LUT_START and LUT_END (in this case result = a + b + c + d;) is loaded into the LUT if it is not already loaded and executed from there (if it is already loaded, it will not be re-loaded, it will just be executed). You can even mix multiple PASM and C statements between the LUT_START and LUT_END macros.

    There are (naturally) some restrictions on this. The main ones are:

    1. Executing C code from the LUT only works for NATIVE programs (whereas for executing PASM from the LUT I now have a version of the macros that work for ANY program type - NATIVE, TINY, COMPACT, SMALL or LARGE).
    2. The LUT C code must compile to 254 longs or less (to allow one long for the signature and one long for the 'ret' instruction which is now added automatically).
    3. The LUT C code has to be leaf code - i.e. it cannot call any other C functions.

    I will think about whether I can loosen these restrictions, but as it would probably require yet another code generator back end, it may not be worthwhile for C code that would still be limited to 254 longs (I use the other 256 LUT longs for library functions, but I could potentially reduce that to make more of the LUT available for C code - more thinking required!)

    Ross.

  • evanhevanh Posts: 16,045
    edited 2024-05-28 06:10

    []

Sign In or Register to comment.