Shop OBEX P1 Docs P2 Docs Learn Events
LLVM Backend for Propeller 2 - Page 11 — Parallax Forums

LLVM Backend for Propeller 2



  • I fixed most of those issues, but need to redo it for a pull request.


  • This code does not need to be this way:

        // if settings weren't patched in, set to a default
        if (_clkfreq == 0) _clkfreq = 24000000;
        if (_dbgbaud == 0) _dbgbaud = DBG_UART_BAUD;

    Setting that memory location is the memory location that is changed when it gets uploaded so whatever is there is fine and should be the default values.

        _clkfreq = 200000000;
        _clkmode = 0x14c00f8;
        _Program = 0x4c4c3250;
        _Program1 = 0x4d56;
        // configure clock to run at the speed selected
        hubset(_clkmode); // clock config (20,000,000 * 10)
        r = _clkfreq / 1000000 - 1;
        _clkmode |= (r << 8) | 3;
        hubset(_clkmode); // set clock pll

    This is the new code that sets the clock up before things get going so baud rate and other items are correct.

    Is 24Mhz the right value here. I set mine to 200Mhz.


  • I set it up this way on purpose: library code should not make any assumptions about the hardware it is running on. Imagine someone builds a board that has an odd clock configuration that those equations/setup won't work for (for example, I did this on P1--I'd use a 5MHz cmos oscillator instead of a crystal because they were smaller and nicer for layout, which required using a non-standard clock configuration). We don't want the library to restrict what the hardware clock can be, so the library should just set up the hardware in a default state: RC fast running at 20+ MHz (I found 24 MHz to be the real value, at least on my board), and a default UART of 2Mbps. Then the application layer can change it to whatever is desired, OR for convenience, the desired clock frequency, mode, and UART can be patched into the binary via loadp2.

    So in my code, if nothing is patched in, set up a default that is guaranteed to work across all hardware (use the internal clock), but let patching override if necessary (loadp2 will always set 0x14, 0x18, and 0x1c when patching). And _clkfreq and _dbgbaud need to be set otherwise other code that relies on knowing the clock frequency won't work, like setting up UART or waiting in a loop.

  • On that fundamental fact I disagree.

    The compiler should be setup to work with Parallaxes product line and be ready to run at some designed configuration. The end user should not need code in his program to setup the board.

    I hate patching or suppling config parameters for defaults that should already be there.

    But so be it, if that's what you're doing.


  • @iseries The hope is that this will extend beyond parallax's product line--such that Propeller 2 can be used beyond just hobbyists, and make it's way into real products (that was why I started this whole effort in the first place). And that requires customizability to work with any supported hardware configuration, not just what parallax makes for their eval boards. And if you don't do any patching or anything, it will run fine, in the chip's default configuration, just not using the PLL/crystal. If using the pll is a desired run mode for the user (which it will be for basically all users), then they need to the software to match the hardware they want to run. But how the PLL is configured can vary greatly, so it shouldn't be restricted in the library, otherwise the library will need to change any time a new clock type comes is used. I feel like that's a pretty typical approach for all embedded products. I used an XMC4700 not too long ago and it was the same deal: set up the clock configuration registers in software to match the hardware design.

    @Rayman I updated the examples, they should all work now (I got rid of the incomplete ones, but the 4 that are there should work now). One thing to be careful of: a simple while(1); statement to block will get optimized out. If you want to block, put a waitx in the while loop so that it doesn't remove the loop. That's why you were seeing that weird behavior; the while(1) would fall through and the chip would do something undetermined.

  • RaymanRayman Posts: 14,145
    edited 2022-02-21 19:27

    @n_ermosh Thanks, that seems to fix it, makes sense I guess.

    Visual Studio Pro is complaining about things like this:
    #define dirh(pin) asm volatile ("dirh %0" : : "ri"((int)pin))
    Thinks it's missing a parenthesis, but it's not and compiles fine.

    682 x 746 - 47K
  • That's odd--yeah the parentheses are lined up and VSCode doesn't complain about that for me--seems like it's trying to guess what that line will do with a different compiler? maybe it expects a different syntax for asm statements? either way, sounds like something specific to your visual studio setup.

  • VSCode complains about it for me, It even doesn't like volatile.


  • In VSC, there's a few config variables that you need to set your compiler type. Usually it can autoconfigure itself if you just provide compiler path.

    I'd assume(?) that VS has a similar setting. somewhere.

  • @Rayman ,

    I could not get your example to work. I think it wanted to compile the program natively and not use the batch file.

    VScode works much better and allows me to set the compiler to clang. I also can see all my programs and just pick one and compile it and then run it.


    I changed all the propeller includes to use functions and documented them. This helps with code prompting and completion as you can see from the picture.

    Got patching to work so now I don't have to set it in the program and everything works out of the box.


  • RaymanRayman Posts: 14,145

    @iseries Sorry it didn't work for you. Maybe it was in the wrong configuration? It should be "Debug" and "x86" to work... I've seen it switch to "x64" sometimes for some reason and stop working...

    BTW: cogstart() isn't returning the cog# for me. Just gives 1. I think that may be why @n_ermosh removed that output from the demo.

  • @Rayman ,

    Right, my cogstart does return the cog number were as the one you have just returned 1 - true;

    Changing it x86 did the trick. Don't get code prompting though?

    Also, it looks like bloating is back. The program size is small but the load size is back 507k. Looking into it.


  • RaymanRayman Posts: 14,145

    @iseries Which kind of code prompting do you mean? It all seems to work for me...

    Did you overload cogstart() ? Do you have a different propeller.h with that in it?

  • @Rayman ,

    Yes, I rewrote all of propeller.h and propeller.c to provide code prompting. I also went through simpletools and simplei2c.


  • Well, I guess the bloat never left.

    Load Address:0, Size:22932, Memory:22932
    Load Address:5998, Size:382, Memory:382
    Load Address:5B18, Size:220, Memory:484584

    The file size is small because the elf file doesn't store the zeros only the data that goes in that section. The loadp2 code loads all the zeros though.

    I guess if the stack size and heap size were set to zero it wouldn't do that.


  • Huh. Those sections are marked as “NOLOAD” yet it still includes them to be loaded? Odd.

    @Rayman i updated my propeller.h to also return cog #. Mike and I need to do some work to merge our propeller.c and propeller.h to be more consistent

  • It looks like when I first tried out lld a couple years ago, NOLOAD would actually remove the section from the elf file. At some point, the behavior of NOLOAD changed in LLD and the section is now included and marked to not be loaded with the SHT_NOBITS flag in the section header. But looks like loadp2 doesn't check for that and loads the section anyway. Updating loadp2 might be the best solution here, in which case I'll take a stab at it and make a PR. @ersmith is that expected behavior for loadp2 to still load a section even if it is flagged with SHT_NOBITS?

  • David Betz wrote the elf file code way back for the P1. From what I can tell there is only a TYPE code that is checked to see if is a one or not which indicates loadable code.

    In my example there is 220 bytes of code that needs to be loaded but it marks the section as needing 487k of memory so don't know what should be done.

    In my program I use the file size and not the memory size and just load that and skip the zeros.


  • To load smaller elf files you only need to change two lines of code in loadp2.c:

            if (program.memsz < program.filesz) {
                printf("bad ELF file: program size in file too big\n");
                return -1;
            if (program.paddr + program.filesz > top) {  <--- change memsz to filesz
                top = program.paddr + program.filesz; // memsz; filesz <--- change memsz to filesz


  • Yeah--that's initially what I was thinking, but I'm curious if it will break something somewhere else, although for an embedded system, if it's not in the elf file, then it shouldn't be loaded, so probably okay to just do it. I'm seeing if there's a solution with making the linker set memsz correctly. But for now, that's a feasible solution.

  • iseriesiseries Posts: 1,475
    edited 2022-02-22 20:21

    Right, so data at the end of the file will not get zeroed out and just left to whatever was there. Since I think it's the stack and heap pointer there should be no issue.

    Could maybe you just have a pointer to the end of the program and then just set the Heap and Stack based on that and not have a section.


    PS I have tested several programs of different sizes and have found no issues.

  • Sample Test program for SD card support.

    #define PIN_MISO 20
    #define PIN_CLK  21
    #define PIN_MOSI 22
    #define PIN_SS   23
    #include <sys/types.h>
    #include <sys/time.h>
    #include <errno.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <string.h>
    #include <propeller2.h>
    void dodir(void);
    void doread(void);
    void dowrite(void);
    char Buffer[512];
    FILE *fp;
    int i;
    unsigned p;
    struct tm ti;
    const char message[] = "What is this:\n";
    int main(int argc, char** argv)
        struct timeval tv;
        time_t t;
        ti.tm_year = 2021 - 1900;
        ti.tm_mon = 2;
        ti.tm_mday = 7;
        ti.tm_hour = 6;
        ti.tm_min = 0;
        ti.tm_sec = 0;
        t = mktime(&ti);
        tv.tv_sec = t;
        settimeofday(&tv, 0);
        i = sd_mount(PIN_SS, PIN_CLK, PIN_MOSI, PIN_MISO);
        if (i != 0)
            printf("Mount Failed\n");
            while (1)
        while (1)
    void dodir()
        DIR *dir;
        struct dirent ent;
        time_t t;
        dir = sd_opendir("/");
        if (dir == NULL)
            printf("Directory Failed(%d)!\n", errno);
            while (1)
        while (!sd_readdir(dir, &ent))
            ti.tm_year = (ent.d_date >> 9) + 80;
            ti.tm_mon = ((ent.d_date >> 5) & 0x0f) - 1;
            ti.tm_mday = (ent.d_date) & 0x01f;
            ti.tm_hour = (ent.d_time >> 11);
            ti.tm_min = (ent.d_time >> 5) & 0x3f;
            ti.tm_sec = (ent.d_time) & 0x1f;
            if ((ent.d_type & ATTR_DIRECTORY) != 0)
                printf("d ");
                printf("  ");
            printf("%s %s\n", ent.d_name, asctime(&ti));
            printf("%s\n", asctime(&ti));
    void doread()
        printf("Opening file OneCall\n");
        fp = fopen("SD:/OneCall.txt", "r");
        if (fp == NULL)
            printf(" File Not Found!(%d)\n", errno);
            while (1)
        i = fread(Buffer, 1, 256, fp);
        printf("Read %d bytes\n", i);
        Buffer[i] = 0;
        printf("Buffer: %s\n", Buffer);
    void dowrite()
        printf("Writing log file\n");
        fp = fopen("SD:/logfile.txt", "a");
        if (fp == NULL)
            printf("File Creation Error!\n");
            while (1)
        printf("File Pointer %lx\n", (unsigned long)fp->drvarg[0]);
        printf("Buffer Base: %lx\n", (unsigned long)fp->_base);
        printf("Buffer Size %d\n", (int)fp->_bsiz);
        printf("File Flags: %x\n", fp->_flag);
        printf("File Lock: %d\n", fp->_lock);
        printf("Buffer Pointer: %lx\n", (unsigned long)fp->_ptr);
        printf("Buffer Count: %ld\n", fp->_cnt);
        printf("Driver Open: %lx\n", (unsigned long)fp->_drv->fopen);
        printf("Driver Read: %lx\n", (unsigned long)fp->_drv->read);
        printf("Driver Write: %lx\n", (unsigned long)fp->_drv->write);
        printf("Driver Close: %lx\n", (unsigned long)fp->_drv->fclose);
        printf("About to write...\n");
        i =  fwrite("Test Data only\n", 1, 15, fp);
        printf("Wrote %d\n", i);
        printf("Error: %d\n", errno);

    SD card with some files required.


  • RaymanRayman Posts: 14,145

    What code is this using for SD access?
    FatFs is really nice... Hope it's that.

  • @Rayman ,

    Yep, that's the one I used.


  • RaymanRayman Posts: 14,145

    @iseries said:
    @Rayman ,

    Yes, I rewrote all of propeller.h and propeller.c to provide code prompting. I also went through simpletools and simplei2c.


    @iseries Is this already in the github repo?

  • RaymanRayman Posts: 14,145

    Trying to compile ACK, I get an error on _Bool ...
    Tried adding #include <stdbool.h>, but that didn't help.
    Is there a way to fix this?

    1>./src/ack_user_platform.h(71,56): error G58EA42A4: unknown type name '_Bool'
    1>void ACKPlatform_WriteDigitalPin(ACKHardwarePin_t pin, bool state);
    1>                                                       ^
    1>C:\Prop2\Clang\bin\../libc/include\stdbool.h:4:14: note: expanded from macro 'bool'
    1>#define bool _Bool
    1>             ^
  • @Rayman ,

    Did you check the order of your includes? _Bool needs to be defined.


  • RaymanRayman Posts: 14,145
    edited 2022-03-04 16:49

    Ok, maybe I'm doing something wrong with include order.
    Was able to fix by adding this:

    #ifndef MYBOOLEAN_H
    #define MYBOOLEAN_H
    #define false 0
    #define true 1
    typedef int _Bool; 

    From a google search. Seems C99 has it but C90 doesn't...

  • If I recall, bool/_Bool was made native in C99.

  • RaymanRayman Posts: 14,145

    Getting an error trying to compile the ACK file "ack_core_lifecycle.c", that looks like this:

    1>EXEC : fatal error : error in backend: Cannot select: 0x1e6662f27a8: i32,ch = load<(dereferenceable load (s8) from @sg_ackModuleInitiatedFactoryReset), zext from i1> 0x1e666383678, 0x1e6662ec678, undef:i32
    1>  0x1e6662ec678: i32 = P2GAWRAPPER TargetGlobalAddress:i32<i1* @sg_ackModuleInitiatedFactoryReset> 0
    1>    0x1e6662eafe8: i32 = TargetGlobalAddress<i1* @sg_ackModuleInitiatedFactoryReset> 0
    1>  0x1e6662eea10: i32 = undef
    1>In function: ACK_Process
    1>PLEASE submit a bug report to and include the crash backtrace, preprocessed source, and associated run script.
    1>Stack dump:
    1>0.    Program arguments: clang -Os -ffunction-sections -fdata-sections -fno-jump-tables --target=p2 -Dprintf=__simple_printf -DP2_TARGET_MHZ=160 -o ack_core_lifecycle.o -c ./src/ack_core_lifecycle.c
    1>1.    <eof> parser at end of file
    1>2.    Code generation
    1>3.    Running pass 'Function Pass Manager' on module './src/ack_core_lifecycle.c'.
    1>4.    Running pass 'P2 DAG->DAG Pattern Instruction Selection' on function '@ACK_Process'
    1> #0 0x00007fff65844f69 (C:\Windows\System32\KERNELBASE.dll+0x34f69)
    1> #1 0x00007ff75e4f6bea (C:\Prop2\Clang\bin\clang.exe+0x9e6bea)
    1> #2 0x00007ff75e4fa643 (C:\Prop2\Clang\bin\clang.exe+0x9ea643)

    Any idea what might be causing that?

Sign In or Register to comment.