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

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

1110111113115116122

Comments

  • evanhevanh Posts: 15,916
    edited 2023-11-25 10:46

    Eric,
    Help, Ada has done something with the compiler I completely don't understand. It creates a compile time error for me: Unable to open file `libc': No such file or directory. She has a Spin2 object reference to "libc" and then proceeds to use a bunch of the typical functions available in the standard C library as if they're Spin methods.

    EDIT: Hmm, solved that one by replacing "libc" with "include/libc". But now there is more of these in her fsadapter.c source but these ones don't solve that way ...

  • @evanh said:
    Eric,
    Help, Ada has done something with the compiler I completely don't understand. It creates a compile time error for me: Unable to open file `libc': No such file or directory. She has a Spin2 object reference to "libc" and then proceeds to use a bunch of the typical functions available in the standard C library as if they're Spin methods.

    EDIT: Hmm, solved that one by replacing "libc" with "include/libc". But now there is more of these in her fsadapter.c source but these ones don't solve that way ...

    How are you invoking the compiler? Normally the "include" directory should be on the search path by default, so something like:

       obj c: "libc"
    

    should find include/libc.spin2, which is a standard part of the flexspin distribution and provides a simple interface between C and Spin.

  • evanhevanh Posts: 15,916
    edited 2023-11-25 12:13

    I don't install anything. haven't tried to work out where to put it. I put the includes in the working directory. They seem to be fine there except for this.

    When you say "search path" you mean in the PATH environment variable?

    EDIT: Ahh, got it: Use -I "./include" or even simpler -I include

  • The include directory needs to be one level up from your flexspin executable. I.e. if it is at ~/whatever/abcd/flexspin, it will find include files at ~/whatever/include/. This happens automagically if you use flexprop or build spin2cpp from source.

  • ersmithersmith Posts: 6,053
    edited 2023-11-25 12:26

    @evanh said:
    I don't install anything. haven't tried to work out where to put it. I put the includes in the working directory. They seem to be fine there except for this.

    When you say "search path" you mean in the PATH environment variable?

    EDIT: Ahh, got it: Use -I "./include" or even simpler -I include

    By "search path" I meant the path used by flexspin to search for include files. Typically a flexspin install goes in a directory somewhere using the structure in the .zip files (or the structure that's created by default in the git repository). For example, if you're working from the git repo then you're presumably running the binary that gets built at <git root>/spin2cpp/build/flexspin, either directly or because it's on your path. flexspin looks up the location of its executable at runtime and looks for include files by default in <path-to-flexspin-executable>/../include, so for the git build example it looks in <git root>/spin2cpp/include. Otherwise, you can (as you indicated) explicitly specify an include path with -I or with a FLEXSPIN_INCLUDE_PATH environment variable.

    If you don't want to add anything to your PATH environment variable you can use a symlink to point to the git build, e.g. I have a symlink in /home/ersmith/bin/flexspin that points to /home/ersmith/github/spin2cpp/build/flexspin. The runtime path lookup code knows to follow symlinks to find the real location of the binary.

  • evanhevanh Posts: 15,916

    Thank you, understood. I had wanted to clean up what I was doing anyway since I had multiple copies of the includes lying around and had got quite adept at ensuring they were up to date.

  • @ersmith
    Hi Eric,

    Is it expected behavior for spin1 that the standard comparison operators work as if the values were unsigned, provided the operands in the compare are symbols?
    In case that is unclear, consider the following (for the uninitiated, assume ser is a serial object):

        ser.dec(negx)    ' shows -2147483648 on the terminal: OK
        ser.newline()
        ser.dec(posx)    ' shows 2147483647 on the terminal: OK
        ser.newline()
        ser.dec(-2147483648 > 2147483647) ' as integer constants: shows 0 on the terminal (i.e., "no, the first operand is not greater than the second"): OK
        ser.newline()
        ser.dec(negx > posx) ' as built-in symbols: shows -1 on the terminal (i.e., "yes, the first is greater than the second"): ...OK?
    

    I'd have expected both comparisons to have the same result. Note I don't consider the ability to do compares like this a problem; in fact it's exactly what I need for my application (comparisons on TCP segment numbers, which are meant to be treated as unsigned 32bits) - I just didn't expect it to be working like this.
    In the first comparison, if I use the unsigned compare operator (+>), it changes the result (indeed I thought this operator was going to be a requirement for this).

    Thanks!
    Jesse

  • I think some bugs got introduced in constant folding when 64 bit support was added. Try assigning the values to variables, the frontend is too thick to fold them and the correct behaviour should be revealed.

  • Yup, that returns expected behavior.

  • ersmithersmith Posts: 6,053
    edited 2023-11-29 01:53

    The Spin comparision operators were not truncating to 32 bits before constant evaluation, which led to a number of inconsistencies with expressions that were evaluated at compile time. I've just pushed a fix for that.

  • I think masking of shift amounts is also broken, it should mask out everything but the bottom 5 bits

  • RaymanRayman Posts: 14,648

    Got a C code here that runs fine the regular way, but fails when loaded to flash...
    Flash works with a different version of the same code, so don't think its hardware related.

    It works a little bit and then freezes up...

    Does a smiley face and two exclamation points in the serial output mean anything?
    Here's what I get. It's normal up until the smiley face...

    ( Entering terminal mode.  Press Ctrl-] or Ctrl-Z to exit. )
    Setup...ftinit..
    SPI Started
    Power Cycling EVE
    EVE powercycle complete.
    Waking up EVE.
    
    Good ID: 0x7c
    Eve now ACTIVE
    Chip ID = 0x00011308
    First screen written
    Clock freq = 72000000
     ☺ ‼Press enter to continue...
    
  • @Rayman said:
    Got a C code here that runs fine the regular way, but fails when loaded to flash...
    Flash works with a different version of the same code, so don't think its hardware related.

    It works a little bit and then freezes up...

    Does a smiley face and two exclamation points in the serial output mean anything?
    Here's what I get. It's normal up until the smiley face...

    I'm sorry, there's not enough there for me to figure anything out. The smiley face doesn't mean anything, it's probably just line noise or otherwise garbled output characters.

    It could be a bug in the compiler, but it could equally well be a bug in the program -- the environment the program is running in will be slightly different between serial download and flash boot, so if it uses any uninitialized variables (for example) then the results will be different.

    Eric

  • RaymanRayman Posts: 14,648
    edited 2023-12-10 20:45

    @ersmith Ok, that's probably it... Uninitialized variables. Thanks.

    Is there any simple fix to zero all variables at start?

    There may have been a compiler warning about that, have to check...

  • All statically allocated variables are zero by default (or should be). Locals are not (except in Spin2 mode)

  • RaymanRayman Posts: 14,648

    I do have a questionable global static array of struct, but I don't think that's the problem...

    Appears that using Plan9 from flash makes things go sideways.
    Was using this:

        mount("/files", _vfs_open_host());
        chdir("/files");
    

    Switched code to use uSD and seems better...

  • Hi @ersmith,
    Evan and me had a discussion about memory allocation. Can you explain if/how FlexC supports malloc() and how it works? Do I have to assign a heap memory space before using malloc()? Does it use first-fit or best-fit?

  • I installed the latest flexprop on my Linux Dell server box, running xubuntu, and flexprop keeps telling me it can not find a P2. it is plugged into ttyUSB0, and selected in flexprop. I did the usermod dialout procedure and flexprop still can not find a P2. I know the P2 works because when I plug it into my Win 10 box, everything works fine. The ttyUSB0 shows up in /dev.

    Ray

  • Try sudo chmod 777 /dev/ttyUSB*

  • I finally got it to work. Sometimes it is like pulling teeth when working in Linux.

    Ray

  • @ManAtWork said:
    Hi @ersmith,
    Evan and me had a discussion about memory allocation. Can you explain if/how FlexC supports malloc() and how it works? Do I have to assign a heap memory space before using malloc()? Does it use first-fit or best-fit?

    There's some documentation in general.md / general.pdf. FlexC malloc() operates from the heap, the size of which is set at compile time with a HEAPSIZE enum. The allocator is a simple first fit allocator. You can also allocate "managed" memory from the heap (which will be garbage collected automatically) with _gc_alloc_managed.

  • evanhevanh Posts: 15,916
    edited 2023-12-19 09:23

    Ah, I must have used HEAPSIZE and discovered it enlarged the binary. For what I was wanting - reducing the binary size - that was no better than using a static declaration.

  • ManAtWorkManAtWork Posts: 2,176
    edited 2023-12-19 10:36

    @ersmith said:
    There's some documentation in general.md / general.pdf. FlexC malloc() operates from the heap, the size of which is set at compile time with a HEAPSIZE enum. The allocator is a simple first fit allocator. You can also allocate "managed" memory from the heap (which will be garbage collected automatically) with _gc_alloc_managed.

    Thanks Eric! I can use this for my first tests. But in the long run I think I'll write my own allocator with best-fit and use alloca() at the beginning of main() to setup the heap. This avoids bloating the binary and reduces fragmentation.

  • What are you all on about with the binary size? I don't think there's a case where it really matters how big the actual image is. The RAM is the same size regardless and the possible savings seem small compared to overall flash/SD storage sizes. Though if that really mattered, it'd be far more effective to look into compressing the binary.

  • evanhevanh Posts: 15,916

    I was running repeat tests using a large array, recompiling and downloading each time. The file size bothered me enough to change to a malloc() instead of a static array. To my annoyance it didn't achieve the desired outcome.

  • As a programmer of real-time systems I like real-time responsiveness. :D Although the P2 has a much higher download baud rate than the P2 it still makes a difference wether you have a 10k or a 500kB binary. I even sometimes switch to the Propeller Tool to try out small code snippets because it compiles faster. When I'm too lazy to implement user input for stimuli I simply change a constant in the code and compile again.

  • @ManAtWork said:
    As a programmer of real-time systems I like real-time responsiveness. :D Although the P2 has a much higher download baud rate than the P2 it still makes a difference wether you have a 10k or a 500kB binary.

    Fair enough. For me it takes around 2.6 seconds to load a fat ~450k binary, which is kinda just below the pain threshold. Though maybe compression can be fast enough to speed up the loading...

    I even sometimes switch to the Propeller Tool to try out small code snippets because it compiles faster. When I'm too lazy to implement user input for stimuli I simply change a constant in the code and compile again.

    Not really true, it's just that windows is somehow extremely slow at starting CLI processes and doing file operations. The reason PropTool is fast is because the compiler is built into the tool and is compiling from and to RAM and therefore sidesteps a number of awful detours into the NT kernel. Try compiling large C++ programs on win vs. linux, it's ridiculous how much faster it is.

  • Infact, I present the world's lowest-effort executable compressor. build.sh builds the project, probably buggy, etc etc ~

    One may use it by simply invoking p2crunch yourprogram.binary. A yourprogram.p2crunch.binary will be written to the same path. This can be loaded and otherwise used like the original program. Obviously programs with lots of empty space benefit most.

    Even when using the highest possible compression level provided by the LZ4HC library, it's basically instant to compress and the very unoptimized decompressor seems to be not too bad, either.

  • RaymanRayman Posts: 14,648

    Got what I think must be a Spin2 compiler error here in this LCD driver.
    The inline assemble version of this output loop doesn't work and then crashes when it tries to leave the function:

    PUB WrWordN_DAT(d,n)|i,busalt,nDelay,pwr     ' write same word of data n times
    
      if (n<=0)
        return
      GPIO.[CS]:=0
      GPIO.[DC]:=1
      WriteRegMCP(MCP23009_GPIO,GPIO)                   ' DC high: data
    
      busalt:=1
      nDelay:=100
      pwr:=wr
    
      ser.println(@"W")
      '{ This doesn't work...
      org
         _WrWordN_DAT_loop
                  ror       d,#8
                  altsb     BusAlt,#outb
                  setbyte   d
                  drvl      pWR
                  waitx     nDelay
                  drvh      pWR
                  waitx     nDelay
    
                  rol       d,#8
                  altsb     BusAlt,#outb
                  setbyte   d
                  drvl      pWR
                  waitx     nDelay
                  drvh      pWR
                  waitx     nDelay
    
                  djnz      n,#_WrWordN_DAT_loop
      end
      '}
      ser.println(@"R")
    
    { 'This works
      repeat i from 0 to n-1
        pinw(bus addpins 7, d>>8)           ' write data to bus
        pint(WR)                       ' clock out data byte
        pint(WR)
        pinw(bus addpins 7, d)           ' write data to bus
        pint(WR)                       ' clock out data byte
        pint(WR)
    
    }
      GPIO.[CS]:=1
      WriteRegMCP(MCP23009_GPIO,GPIO)
    
      ser.println(@"I")
    

    Note that you don't seem to need the hardware to test the code...
    It outputs the W and the R, but not the I

  • @Rayman : thanks for the bug report. The altsb BusAlt, #outb instruction was being compiled incorrectly; it turns out that any use of #hwreg is being incorrectly compiled in inline assembly :(. That'll be fixed in 6.8.0, which should be out Real Soon Now, but in the meantime you can work around it by using #$1fd instead of #outb.

Sign In or Register to comment.