Shop OBEX P1 Docs P2 Docs Learn Events
flexspin compiler for P2: Assembly, Spin, BASIC, and C in one compiler - Page 114 — Parallax Forums

flexspin compiler for P2: Assembly, Spin, BASIC, and C in one compiler

1109110111112114

Comments

  • RaymanRayman Posts: 13,711

    @ersmith Thanks! Maybe I'll get the award for finding the last bug in the Spin2 compiler?

  • Is this suppose to work:

    enum { _clkfreq = 300000 };
    
    #include <stdio.h>
    #include <propeller.h>
    
    main()
    {
        printf("Hello\n");
        _waitms(5000);
    
        while (1)
        {
            _pinl(56);
            _pinh(57);
            _waitms(500);
            _pinl(57);
            _pinh(56);
            _waitms(500);
        }
    }
    

    I get this error:

    Starting build...
    d:/flexprop/bin/flexspinx.exe -2 -O1 -I D:/flexprop/include -I D:/Custom -o D:\Documents\MyProjects\P2/build/dummy.binary D:\Documents\MyProjects\P2\dummy.c
    Propeller Spin/PASM Compiler 'FlexSpin' (c) 2011-2023 Total Spectrum Software Inc. and contributors
    Version 6.7.1 Compiled on: Dec  7 2023
    error: Unable to find clock settings for freq 300000.000000 Hz with input freq 19200000.000000 Hz
    dummy.c
    
    Build finished with error(s).
    

    I have one of those odd frequency P2 and I have a project that needs to run really slooowww.
    I know the answer is 63, 0 (19200000/64*1=300000)

    There are a bunch of other frequencies that don't work as well.

    Mike

  • evanhevanh Posts: 15,043
    edited 2023-12-23 01:59

    That's 300 kHz! It's too low. I wouldn't expect anything below 3 MHz to work.

    There is a bottom limit on stable PLL operation. I don't know the details but basically the pre-DIVP VCO frequency is spec'd as 100 MHz minimum. You can go lower but jitter climbs very rapidly below 60 MHz.

    If we take 60 MHz as the hard minimum of the VCO, and the largest DIVP following that is /30, so 2 MHz is the absolute minium the PLL can realistically do.

  • I think you can manually specifiy _clkmode. Though yea it probably is out-of-spec to run so slowly. Why would you want that, anyways? For low power consumption, there's RCSLOW mode.

  • evanhevanh Posts: 15,043

    Is RCSLOW not an option? It also has the advantage of stopping the crystal oscillator for even lower power.

  • evanhevanh Posts: 15,043
    edited 2023-12-23 02:10

    @Wuerfel_21 said:
    I think you can manually specifiy _clkmode.

    On that note, here's my code for doing exactly that.

    Mike,
    You'll need to edit the coded limits in there but it should provide you with the tools to runtime adjust where the compile time won't go.

  • @evanh ,
    I have a blues application where I put the P2 in low power mode for an hour using RCSLOW.

    void Sleep(int t)
    {
        int i;
    
        i = _clkfreq;
       //Slow speed low power mode
        _clkset(_clkmode ^2, 20000);
    
        sleep(t);
    
        _clkset(_clkmode ^2, i);
    }
    

    I have an adafruit small LCD using an ST7789 chip through SPI that was not working and need to slow things down to see if it was a speed issue.
    Sure enough it was.

    Mike

  • evanhevanh Posts: 15,043

    You just wanted to slow down the SPI clock?

  • @iseries said:
    Is this suppose to work:
    ```
    enum { _clkfreq = 300000 };

    No, it is not supposed to work, and the similar Spin2 declarations produces an error ("PLL settings could not be achieved per _CLKFREQ") in PNut v43 as well. Looking at the PNut source code it appears that the minimum supported frequency is 3.3333 MHz. FlexProp has a slightly different calculation but I think comes out with the same minimum.

    I'm not sure of the hardware limitation behind this, I just copied Chip's suggested clock settings calculation from a forum post.

  • To my surprise I just found out that FlexC supports packed bit fields like this:

    typedef struct
    {
        unsigned SizeIndicator:  1;
        unsigned TransferType:   1;
        unsigned DataSetSize:    2;
        unsigned CompleteAccess: 1;
        unsigned Command:        3;
        int8_t   IndexLo;
        int8_t   IndexHi;
        int8_t   SubIndex;
    } TInitSdoHeader;
    

    I haven't used them so far but there are lots of type definitions like this in sample code for EtherCAT and libraries from Microchip. CAN packets have only 8 bytes of payload so people are extremely stingy with operand sizes. The generated code is quite efficient.

        TInitSdoHeader h;
        h.SizeIndicator= 0;
        h.TransferType= 0;
        h.DataSetSize= 1;
        h.CompleteAccess= 0;
        h.Command= 3;
    

    translates to

        rdlong  _var01, fp
        andn    _var01, #1
        wrlong  _var01, fp
        andn    _var01, #2
        wrlong  _var01, fp
        andn    _var01, #12
        or  _var01, #4
        wrlong  _var01, fp
        andn    _var01, #16
        wrlong  _var01, fp
        andn    _var01, #224
        or  _var01, #96
        wrlong  _var01, fp
    

    Just a suggestion for further optimization: (I don't really need it, I'm happy the way it is)
    1. all WRLONGs except for the last could be optimized out.
    2. setting of adjacent fields could be combined so that, for example, clearing all five fields could use one single ANDN

  • @ManAtWork said:
    The generated code is quite efficient.

    Not really :)

    Just a suggestion for further optimization: (I don't really need it, I'm happy the way it is)
    1. all WRLONGs except for the last could be optimized out.
    2. setting of adjacent fields could be combined so that, for example, clearing all five fields could use one single ANDN

    I don't think I ever got to enabling dead store optimization on fp. (Or did I? Try enabling optflags experimental and aggressive-mem...) Anyways, your main problem is that you're using a local variable and then taking the address of that somewhere in the function. Flexspin really doesn't like that and generates all those silly loads and stores through fp (though most of the loads get removed out as you can tell). You can use _builtin_alloca to acquire stack memory that you need to pass an address of into another function without triggering this local-on-stack behaviour.

    All the AND(N)s would be merged if the stores weren't using the intermediate values.

  • I'm puzzled by C, again. :| What is the correct way to do a forward declaration of a pointer to a struct?

    typedef struct
    {
      LinkedNode* next;
      LinkedNode* prev;
    } LinkedNode;
    

    ...doesn't work because I can't use the typedef before it's actually defined. I've read some hints on StackOverflow but I don't get it.

    typedef struct
    {
      struct LinkedNode* next;
      struct LinkedNode* prev;
    } LinkedNode;
    

    or

    typedef struct LN LinkedNode;
    typedef struct
    {
      LinkedNode* next;
      LinkedNode* prev;
    } LinkedNode;
    

    ... should work because the the initial declarations are empty and then replaced by the actual definitions. But all I get is lots of error messages from the compiler. So what is wrong?
    (full source code attached)

  • ElectrodudeElectrodude Posts: 1,606
    edited 2024-01-16 20:22

    @ManAtWork said:
    I'm puzzled by C, again. :| What is the correct way to do a forward declaration of a pointer to a struct?

    typedef struct
    {
      LinkedNode* next;
      LinkedNode* prev;
    } LinkedNode;
    

    ...doesn't work because I can't use the typedef before it's actually defined. I've read some hints on StackOverflow but I don't get it.

    typedef struct
    {
      struct LinkedNode* next;
      struct LinkedNode* prev;
    } LinkedNode;
    

    or

    typedef struct LN LinkedNode;
    typedef struct
    {
      LinkedNode* next;
      LinkedNode* prev;
    } LinkedNode;
    

    ... should work because the the initial declarations are empty and then replaced by the actual definitions. But all I get is lots of error messages from the compiler. So what is wrong?
    (full source code attached)

    This is why typedef struct is bad form. Write out the struct tag out everywhere:

    struct LinkedNode
    {
      struct LinkedNode *next;
      struct LinkedNode *prev;
    };
    

    EDIT: https://www.kernel.org/doc/html/latest/process/coding-style.html

  • Yes, C is confusing like that. Structs have their own namespace distinct from typedefs. If you write just typedef struct {} something, you're creating a struct without a name and typedef-ing that to something.

  • RaymanRayman Posts: 13,711
    edited 2024-01-16 22:21

    typedef struct thing gets me too, seems it just defines a potential structure.

    typedef struct I2C_Port
    {//I2C port instance
        int sclpin, sdapin;  //i2c bus pins
        int clktix;  //system ticks in 1/4 period
    } i2c_Port;
    

    You create an instance of it like this:
    i2c_Port I2C1;

    Not sure what purpose "I2C_Port" (with capitalized I2C) serves...

  • @Rayman said:
    Not sure what purpose "I2C_Port" (with capitalized I2C) serves...

    As explained above, that's the actual name of the struct. You could use it as struct I2C_Port I2C1;.

  • maccamacca Posts: 703
    edited 2024-01-17 07:04

    @ManAtWork said:
    I'm puzzled by C, again. :| What is the correct way to do a forward declaration of a pointer to a struct?

    typedef struct
    {
      LinkedNode* next;
      LinkedNode* prev;
    } LinkedNode;
    

    ...doesn't work because I can't use the typedef before it's actually defined. I've read some hints on StackOverflow but I don't get it.

    typedef means just that 'type define', you are defining a type (LinkedNode) as a unnamed struct, and since the type name is defined after the structure you can't use it to declare the members.
    As others wrote, just give the struct a name and use it.

    typedef struct _LinkedNode
    {
      struct _LinkedNode* next;
      struct _LinkedNode* prev;
    } LinkedNode;
    

    It is a common practice in these cases (maybe derived from the fact that early compilers can't assign the same name to the type) to name the structure as the type prefixed with an underscore.

    To make things clear, you may separate the typedef from the structure definition:

    struct _LinkedNode
    {
      struct _LinkedNode* next;
      struct _LinkedNode* prev;
    };
    
    typedef struct _LinkedNode LinkedNode;
    

    @Rayman said:
    typedef struct thing gets me too, seems it just defines a potential structure.

    typedef struct I2C_Port
    {//I2C port instance
        int sclpin, sdapin;  //i2c bus pins
        int clktix;  //system ticks in 1/4 period
    } i2c_Port;
    

    You create an instance of it like this:
    i2c_Port I2C1;

    Not sure what purpose "I2C_Port" (with capitalized I2C) serves...

    Means that you can create an instance also with it: struct I2C_Port I2C1; Is perfectly equal.
    The type is a short for 'struct ...'.

  • Ok, thanks to everyone. I have to admit that I don't speak C as native language. I've learned programming with Modula and Oberon where everything is much more clear and unambigous. There's a reason why there are things like the "obfuscated C contest" and web pages like "why I hate C". :D

    I've chosen Macca's first version.

    typedef struct _LinkedNode
    {
      struct _LinkedNode* next;
      struct _LinkedNode* prev;
    } LinkedNode;
    

    Even if typedef struct is said to be bad style I want to make clear that I intend to define a type here and not an instance.

    And I have to apologize for abusing this thread, again. It has nothing to do with FlexSpin and works perfectly, now. (there were some other issues in the code like forgotten "this" left from the C++ port. But this was easy to fix now that the countless error messages went away)

  • ElectrodudeElectrodude Posts: 1,606
    edited 2024-01-17 15:34

    @ManAtWork said:
    Ok, thanks to everyone. I have to admit that I don't speak C as native language. I've learned programming with Modula and Oberon where everything is much more clear and unambigous. There's a reason why there are things like the "obfuscated C contest" and web pages like "why I hate C". :D

    I've chosen Macca's first version.

    typedef struct _LinkedNode
    {
      struct _LinkedNode* next;
      struct _LinkedNode* prev;
    } LinkedNode;
    

    Even if typedef struct is said to be bad style I want to make clear that I intend to define a type here and not an instance.

    And I have to apologize for abusing this thread, again. It has nothing to do with FlexSpin and works perfectly, now. (there were some other issues in the code like forgotten "this" left from the C++ port. But this was easy to fix now that the countless error messages went away)

    struct LinkedNode { ... }; already makes it clear that you're defining a type and not an instance. It'd be rather pointless if the default were to define an instance.

    A significant part of what makes C difficult is people taking the "easy" way and doing things like using typedef struct everywhere and trying to pretend the declaration-follows-use rule wasn't an integral part of the language by putting the star on the wrong side int* x;; I struggled with C types until I stopped doing these things. Many tutorials and style guides teach people to do things like this wrong in an attempt to provide a "gentle" introduction, but it only leads you off a cliff later on.

    Also, if you didn't notice it, read the Linux Kernel Style Guide I linked to in an edit to my previous comment. It provides good rationales for most things, rather than just being a pile of arbitrary rules.

  • pik33pik33 Posts: 2,342
    edited 2024-01-18 19:52

    int* xor int *x- that is the question.

    I started my programming learning with Fortran, at the university, as a student, using the punched cards, then 8-bit Atari Basic. And they said, who learned Basic, has his brain permanently damaged.

    After that I learned Pascal.

    In Pascal, you have

    var p: ^integer

    so more natural for me is int *x and I still don't understand what is the difference between these declarations (if any)

    That's why I use C only when I really have to.

  • AribaAriba Posts: 2,678

    @pik33 said:
    int* xor int *x- that is the question.

    I started my programming learning with Fortran, at the university, as a student, using the punched cards, then 8-bit Atari Basic. And they said, who learned Basic, has his brain permanently damaged.

    After that I learned Pascal.

    In Pascal, you have

    var p: ^integer

    so more natural for me is int *x and I still don't understand what is the difference between these declarations (if any)

    That's why I use C only when I really have to.

    There is no difference.
    But if you define more than 1 variable on the same line, the second form is much more obvious:

      int* x, y;
      int *x, y;
    

    x is a pointer to an int, while y is just an int.

  • ElectrodudeElectrodude Posts: 1,606
    edited 2024-01-19 00:01

    There is no difference.
    But if you define more than 1 variable on the same line, the second form is much more obvious:

      int* x, y;
      int *x, y;
    

    x is a pointer to an int, while y is just an int.

    Yes. The compiler doesn't care about whitespace; you can horribly misformat your code any way you like and it will compile just the same. But since the syntax rules of C say that the star binds to the variable name and not the type name, you should put it next to the variable name. As I said before, this is all explained by the "declaration follows use" rule: if you declare int *x[4], y(int z); and later write *x[1] or (*y)(2), the result has type int (i.e. x is an array of pointers to ints, and y is a pointer to a function taking an int and returning an int). (But please never declare both of those things on the same line.)

    On the other hand, there are languages such as Go where the star binds to the type name; in these languages, you put the star next to the type.

  • evanhevanh Posts: 15,043
    edited 2024-02-03 07:02

    Eric,
    Is there a simple/proper way to substitute the sdmm.cc file without actually writing to the includes directories?
    Also, is there support for multiple concurrent physical devices? ie: A second SD card slot at different pins. Being able to copy between them for example.

    EDIT: I guess the answer to second question is yes. Something like ...

        mount("/sd1", _vfs_open_sdcard());   // SD booting pins: P58..P61
        mount("/sd2", _vfs_open_sdcardx(sdbase+5, sdbase+4, sdbase+3, sdbase+2));   // SD add-on board, #64009
    
  • iseriesiseries Posts: 1,420

    @evanh
    The simplest way is to create a new folder in filesys with your code and then in vfs.h create a new command that points to that folder

    I have my own version of the sd card functions with this function added to the vfs.h

    /**
     * @brief mount SD card for processing
     * @param drive volume number of drive 0/1
     * @param cs - chip select pin
     * @param clk - clock pin
     * @param mosi - master out slave in pin
     * @param miso - master in slave out pin
     * @return vfs - pointer to a vfs file structure
     */
    vfs_t *_vfs_open_sdm(int drive, int cs, int clk, int mosi, int miso) _IMPL("filesys/fatfsm/filesystem.c");
    

    Mike

  • evanhevanh Posts: 15,043
    edited 2024-02-03 20:58

    Thanks Mike, that should do the job better than I was envisaging ... oh, that's not a direct replacement of sdmm.cc, I got some reading ahead of me ...

  • Today I'm getting some really strange behaviour. I hope it's not me stepping into another C pitfall...

            int sdo= ReadSdo (slave, 0x1008, 0x00, buffer, 128);
            printf ("ReadSdo=%d\n", sdo);
            printf ("DevName=%s\n", buffer);
    

    executes correctly and prints out

    Execute task s=12
    Read SDO ok
    ReadSdo=12
    DevName=IHSV57/60-EC
    

    (The first two lines are printed by ReadSdo() internally to make sure it returns the correct number)
    However, if I place the function call directly inside the printf brackets like this

            printf ("ReadSdo=%d\n", ReadSdo (slave, 0x1008, 0x00, buffer, 128));
            printf ("DevName=%s\n", buffer);
    

    I get this:

    Execute task s=12
    Read SDO ok
    12
    DevName=IHSV57/60-EC
    

    The format string "ReadSdo=%d\n" gets reduced to "%d\n" somehow or a carriage return is inserted but strangely without a line feed.

    Posting the complete code doesn't make sense as it requires special hardware to execute. If nobody has a plausible explanation why this should happen (me messing up C, again) I'll try to figure out what's going on in the generated pasm code...

  • evanhevanh Posts: 15,043

    Stack or heap overflow maybe. Is this running on a second cog?

  • I think it has something to do with the fact that I call printf() from more than one cog. I think they should happen strictly in sequence and not concurrently because the client cog always waits for the server/driver cog to complete a command before doing the next step. But anyway, some of the characters in the buffer are swallowed.

  • IIRC that has to do with how flexspin attempts to avoid linking in the full dynamic printf implementation (awful and slow) by parsing the format string at compile time and generating hardcoded formatting calls instead. Having a function print while in a printf argument list causes some problem. I think you can use fprintf with stdout if you really want to, but that will link in all the bloatware.

  • I think I have to write my own debugging output functions, anyway. I just encounter some awful race conditions where everything works perfectly as long as there is enough delay caused by the printf()s and everything doesn't as soon as I remove them. :#

    It has to run damn fast so all parsing and serial communication has to be avoided alltogether. I think I'll implement some hardcoded functions that take a pointer to a string, zero to four integer arguments or a pointer and size for a memory dump. This is written to a large buffer like a logfile in RAM. After I have detected a fault condition the whole buffer is parsed and the output formatted and transferred over the serial port.

Sign In or Register to comment.