Shop OBEX P1 Docs P2 Docs Learn Events
PropGCC / PASM question - Page 5 — Parallax Forums

PropGCC / PASM question

1235

Comments

  • I wanted to double check with Ken before I went into too much detail, but I have the go-ahead.

    Sensors: gyro, accel, mag, baro
    - gyro/accel/mag are used to maintain orientation and perform auto-leveling
    - baro + accel are fused to produce an altitude reading that's accurate to about 5 to 10cm (though it wanders a bit)

    I/O:
    - 8 receiver inputs (technically general purpose, can be reassigned)
    - 6 motor outputs (technically general purpose, can be reassigned)
    - 1 expansion connection (tx/rx, intended for GPS/NAV add on)
    - 1 WS2812 LED output (board includes one, driver code handles MANY more)
    - XBee socket, firmware will talk to ground app wired or wireless
    - i2c port connected to prop I2C / EEPROM pins to allow external i2c devices
    - Firmware currently supports PWM or SBUS inputs (SBUS uses a single physical port, leaving you 7 general I/O)
    - Battery voltage monitor (untested as yet - on the new version of the hardware)

    There's also a PC-side application that talks to the board for configuration and testing that'll show all the sensor readings, your R/C input values, IMU orientation, and so on.

    The code and the hardware will all be open source, documented, with schematics available. It's intended to be a teaching tool.
  • First, I'm very excited. I might actually throw down the money one the new Elev8. I've wanted a multirotor for a long time... this is getting me extra excited.

    Second...
    I forked your project and began importing into PropWare. I noticed two issues right away:

    1) #include capitalization. If this is going to be a public project, it needs to be capitalized correctly so it works on linux/mac too. I didn't push any harder before because I thought it was just a personal project.
    2) GCC determines language based on file extension, so all the C files need to be renamed to .cpp (since you're including C++ code in them or in the headers). I'm not sure why this works in SimpleIDE.... I would think it would fail there... but it definitely failed in PropWare, as expected.

    I've already made the necessary changes (and no others) and would be happy to create a pull request if you'd like.
  • I've already started doing both of those things (lower-casing files, and moving to cpp files). SimpleIDE has a global "compile as" setting that presumably you can set as well, but it'll be moot soon enough. I'll probably have it all moved over by the end of tonight or tomorrow.
  • JasonDorie wrote: »
    I've already started doing both of those things (lower-casing files, and moving to cpp files). SimpleIDE has a global "compile as" setting that presumably you can set as well, but it'll be moot soon enough. I'll probably have it all moved over by the end of tonight or tomorrow.

    Oh excellent :) glad to hear it.
  • While converting to PropWare, I noticed you're pulling in FullDuplexSerial. Was that on purpose?
  • Yes, for the moment. I'll likely port a 4-port driver soon so I can run the XBee and FTDI from the same cog, but I need something that runs async so the comms don't slow down the main loop.
  • I've just committed changes that make all files (and #includes) lower case, and change all sources to cpp. I've also updated the quaternion command stream to be 16-bit values. The total compiled size of the cmm version is now within 600 bytes of the Spin one (though to be fair, I still have a couple functions to port).
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-10-23 14:11
    JasonDorie wrote: »
    but I need something that runs async so the comms don't slow down the main loop.

    Ah, that makes sense. I just wanted to make sure it wasn't a long forgotten relic of the past that was giving you unnecessary code bloat :)
    JasonDorie wrote: »
    I'll likely port a 4-port driver soon so I can run the XBee and FTDI from the same cog

    And again, if you don't want to have to port more Spin code, you can use PropWare's UART and Printer classes, which do not require a dedicated cog and run very fast (burst transmit speeds of 4.4 MBaud). They're also reasonably compact.
  • JasonDorieJasonDorie Posts: 1,930
    edited 2015-10-23 14:30
    In CMM? That's the only way this project is realistically going to fit in memory. I also have to handle incoming data at random times (commands from the PC app), and that I can't see working without a dedicated connection.
  • JasonDorie wrote: »
    In CMM? That's the only way this project is realistically going to fit in memory.

    CMM/LMM doesn't matter for PropWare classes - I test with both (see speed tests here for both LMM and CMM).
    JasonDorie wrote: »
    I also have to handle incoming data at random times (commands from the PC app), and that I can't see working without a dedicated connection.

    I assumed when you said you wanted two serial ports running in the same cog, you no longer needed async. PropWare doesn't have any async comms yet :(
  • There are a couple multi-port objects that handle full duplex operation on multiple ports simultaneously (up to 4).

    I'll likely take one of those and alter the interface a little so I can do bulk transmits. Most of the code I've seen only allows for adding one character at a time to the tx buffer, meaning you spend a lot of time checking if there's space. It's possible to copy multiple chats in at one time with a single check, and it eliminates a lot of overhead. In Spin it was the only way for me to do it fast enough. Might not be necessary with C, but I want to try to leave as much free time as possible.
  • The following only applies when you need a single async receiver, which I know probably isn't your case:

    The reason I haven't bumped async comms to a higher priority, is that it can be pretty quickly hacked into place with existing classes. You can start a dedicated cog running a receive-only UART instance (simply use FullDuplexUART with TX pin set to null) which monitors the RX line and dumps data to a Queue. For transmitting, instantiate a SimplexUART instance and run it from whatever cog needs to communicate. If multiple cogs need to transmit, run it through SynchronousPrinter.
  • I have need for a simple direct way to get my existing PASM code into a C program. I am using the SimpleIDE.
    I have no linking / compiling experience in Linux.
    Please show me a simple no fail way with a simple example with a "par" variable address used.
    Thanks in advance.
  • This is from the Elev8-FC source code - I use the PAR method of passing structs around quite a bit. Here's the relevant code from the Servo32-HighRes.cpp file and corresponding PASM driver:
    // C++ code
    
    // This is the structure I'm sending the address of to the PASM code
    struct ServoData {
      long FastPins, SlowPins;
      long MasterLoopDelay, SlowUpdateCounter;
      long Cycles;
      long ServoData[32];		//Servo Pulse Width information
    } Data;
    
    void Servo32_Start(void)
    {
      use_cog_driver(servo32_highres_driver);
      load_cog_driver(servo32_highres_driver, &Data);  // Notice here, passing the address of "Data" - the struct
    }
    
    
    // PASM code starts here:
    
    org
    '-----------------
    ServoStart
                            mov     Index,                  par                'Set Index Pointer
                            rdlong  _FastPinMask,           Index              'Get I/O pin directions
                            
                            add     Index,                  #4                 'Increment Index to next Pointer
                            rdlong  _SlowPinMask,           Index              'Get slow I/O pin directions
    
                            add     Index,                  #4
                            rdlong  MasterLoopDelay,        Index 
    
                            add     Index,                  #4
                            rdlong  SlowUpdateCounter,      Index 
    
                            add     Index,                  #4                 'Increment Index to next Pointer
                            mov     _Cycles,                Index              'Get HUB address to write cycle time
                            
                            add     Index,                  #4                 'Increment Index to hub servo array
                            mov     _ServoHubArrayPtr,      Index              'Set Pointer for hub servo array
    
    

    Basically, if you use 32-bit quantities in a struct in C/C++, the layout will exactly match in PASM - you can just pass the address of the structure itself, and then iterate through, 4 bytes at a time, to get the individual values out. In my case, it's not even really necessary to store the addresses of the various bits - Since the struct layout is known, you could just store one address and add the offsets for each of the members when you need to access them, but since the "add" instruction costs 4 bytes anyway there's no savings over storing the address of each member.
  • What is the connection between "ServoStart" the PASM function code and "servo32_highres_driver" ?
  • The file containing the PASM code is called "servo32_highres_driver.spin". The content gets spit out with a couple of values that use the filename as a prefix, and the macros use_cog_driver and load_cog_driver macros are referencing those values (the address of the pasm routine and its length).
  • I still don't follow. How does the C code know what to run in the line load_cog_driver(servo32_highres_driver, &Data);
    Is " ServoStart" the only code in "servo32_highres_driver.spin" ?
    If there were 3 PASM functions in "servo32_highres_driver.spin" how would the cog know which to run?
  • For a different example, doing the same thing but slightly shorter, check out the Github here:

    https://github.com/parallaxinc/Flight-Controller/tree/master/Firmware-C

    and look at SBUS.cpp / SBUS_driver.spin

    The SBUS.cpp file has a data struct near the top, and the address of that struct is passed to the load_cog_driver function. The sbus_driver label is auto-generated from the "sbus_driver.spin" filename when building the SPIN file in SimpleIDE.
  • JasonDorieJasonDorie Posts: 1,930
    edited 2016-03-07 23:16
    MByron wrote: »
    I still don't follow. How does the C code know what to run in the line load_cog_driver(servo32_highres_driver, &Data);
    Is " ServoStart" the only code in "servo32_highres_driver.spin" ?
    If there were 3 PASM functions in "servo32_highres_driver.spin" how would the cog know which to run?

    Ahh - The pasm assembler in SimpleIDE just outputs a single binary blob, and that blob is given two labels - one for the start of it, and one for the length. Those labels are what the use_cog and load_cog macros use, and the start is assumed to be the entry point.

    In normal PASM obviously you're not limited to a single entry point - this is just a limitation of using SimpleIDE to compile and wrap PASM code for use with C/C++.
  • Ok, so the only code in the file "servo32_highres_driver.spin" is the one PASM function and the file only has to be in the project folder to be found?
    Jason, thank you!
  • JasonDorieJasonDorie Posts: 1,930
    edited 2016-03-07 23:20
    There's a different way to do it that's more flexible, using GCC's inline assembly format. Doing it that way makes the PASM code more easily accessible from the C/C++, because it's not a hack trying to parse and reference Spin DAT sections.

    There's more work involved there though because the syntax is a little different. You wouldn't be able to just drop your existing PASM code into a project and have it work right away, but you'd be able to have multiple PASM functions in a single file. I haven't done this yet, and don't know the assembler syntax well enough to give advice for this, but @DavidZemon probably can.
  • MByron wrote: »
    Ok, so the only code in the file "servo32_highres_driver.spin" is the one PASM function and the file only has to be in the project folder to be found?
    Jason, thank you!

    There are many different PASM routines in the servo32_highres_driver.spin file, but the entry point for the cog is at the beginning of the dat section, so that's the only one accessible from C/C++, and the only one that code really cares about anyway. :)

    You have to add the .spin file to the SimpleIDE project for it to be used. I think it's "add file copy" under the "Project" menu, and you might need to be in the project view to get that option.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2016-03-08 00:57
    All of my examples of inline assembly also use fcache manually, so they're a bit more complicated.

    Use inline assembly + fcache when you want a very fast, blocking function call.
    Use a cog driver (exactly the same principle as the DAT section of a Spin file) when you want a block of code to run asynchronously, and communicate through the PAR register.

    Here's an example of sending a single word of data out using the UART protocol. It comes from line 400 of PropWare/uart/abstractsimplexuart.h.
    /**
     * @brief       Shift out one word of data (FCache function)
     *
     * @param[in]   data        A fully configured, ready-to-go, data word
     * @param[in]   bits        Number of shiftable bits in the data word
     * @param[in]   bitCycles   Delay between each bit; Unit is clock cycles
     * @param[in]   txMask      Pin mask of the TX pin
     */
    inline void shift_out_data (uint32_t data, uint32_t bits, const uint32_t bitCycles,
                                const uint32_t txMask) const {
        volatile uint32_t waitCycles = bitCycles;
        __asm__ volatile (
                "        fcache #(ShiftOutDataEnd%= - ShiftOutDataStart%=)                 \n\t"
                "        .compress off                                                     \n\t"
    
                "ShiftOutDataStart%=:                                                      \n\t"
                "        add %[_waitCycles], CNT                                           \n\t"
    
                "loop%=:                                                                   \n\t"
                "        waitcnt %[_waitCycles], %[_bitCycles]                             \n\t"
                "        shr %[_data],#1 wc                                                \n\t"
                "        muxc outa, %[_mask]                                               \n\t"
                "        djnz %[_bits], #__LMM_FCACHE_START+(loop%= - ShiftOutDataStart%=) \n\t"
    
                "        jmp __LMM_RET                                                     \n\t"
                "ShiftOutDataEnd%=:                                                        \n\t"
                "        .compress default                                                 \n\t"
        : [_data] "+r"(data),
        [_waitCycles] "+r"(waitCycles),
        [_bits] "+r"(bits)
        : [_mask] "r"(txMask),
        [_bitCycles] "r"(bitCycles));
    }
    
  • If you have the PASM in a spin file, I have two versions of a method for using the PASM in a C program using SIMPLEIDE. The first link is a thread where I learned how to do this with multiple variables passed to the PASM. It also includes learning how to turn the functions into a library.

    The second link is a port of a Spin + PASM code that only needs to pass one variable and is simpler.

    forums.parallax.com/discussion/157441/can-spi-in-simple-libraries-be-speeded-up/p1


    forums.parallax.com/discussion/comment/1319703/#Comment_1319703

    Tom
  • Jason,
    Referring to earlier question about using PASM in C code, I have not been able to make this work.
    What I get is..
    Project Directory: C:/Users/Dad/mydocuments/simpleIDE/Learn/Examples/Multicore/

    SimpleIDE Version 1.0.2
    C:/Users/Dad/mydocuments/SimpleIDE/Learn/Simple Libraries/
    C:/Users/Dad/mydocuments/SimpleIDE/ Updated on: 2016-02-26

    propeller-elf-gcc.exe -v GCC 4.6.1 (propellergcc_v1_0_0_2408)
    propeller-elf-gcc.exe -I . -L . -I C:/Users/Dad/mydocuments/SimpleIDE/Learn/Simple Libraries/Utility/libsimpletools -L C:/Users/Dad/mydocuments/SimpleIDE/Learn/Simple Libraries/Utility/libsimpletools/cmm/ -I C:/Users/Dad/mydocuments/SimpleIDE/Learn/Simple Libraries/TextDevices/libsimpletext -L C:/Users/Dad/mydocuments/SimpleIDE/Learn/Simple Libraries/TextDevices/libsimpletext/cmm/ -I C:/Users/Dad/mydocuments/SimpleIDE/Learn/Simple Libraries/Protocol/libsimplei2c -L C:/Users/Dad/mydocuments/SimpleIDE/Learn/Simple Libraries/Protocol/libsimplei2c/cmm/ -o cmm/C with PASM mb mar 9 16.elf -Os -mcmm -m32bit-doubles -fno-exceptions -std=c99 C with PASM mb mar 9 16.c -lm -lsimpletools -lsimpletext -lsimplei2c -lm -lsimpletools -lsimpletext -lm -lsimpletools -lm
    C:\Users\Dad\AppData\Local\Temp\ccihC6Rq.o: In function `_blink':
    (.text+0x8): undefined reference to `_binary_Test2_of_PASM_in_C_dat_start'
    collect2: ld returned 1 exit status
    Done. Build Failed!

    Check source for bad function call or global variable name `_binary_Test2_of_PASM_in_C_dat_start'

    =========================================================

    // Test2_of_PASM_in_C.c


    #include "simpletools.h" // Library include

    void blink(void); // Forward declaration
    int *cog; // For storing process ID

    long mb_data;
    int main() // Main function
    {
    blink( );

    pause(2000); // New rate for 2 s
    }
    void blink(void) // Function for other cog
    {
    high(26);
    use_cog_driver(Test2_of_PASM_in_C);
    load_cog_driver(Test2_of_PASM_in_C, &mb_data );
    }
  • That looks like it's all one file. The file containing the PASM should be a valid Spin file with a DAT section containing PASM code. It's the fact that it's a Spin file that triggers the creation of those special labels.
  • Oops - The spin file "Test2_of_PASM_in_C.spin" is in the SIDE C project folder and the C file is "Cog with PASM mb mar 9 16.c"
    I mis-text edited the c file name into the listing of the file.
    Was not quite awake.

  • The SPIN file is :
    '=====================================
    {Test_of_PASM_in_C.spin
    This works!
    }

    CON
    _clkmode = xtal1 + pll16x
    _xinfreq = 5_000_000

    VAR
    long first_pin
    long second_pin
    long third_pin

    PUB main

    DAT
    org 0
    mb_test mov base_adr, par
    mov dira, #%0111111
    mov a_pin_adr, base_adr
    add base_adr , #4
    mov b_pin_adr, base_adr
    add base_adr , #4
    mov c_pin_adr, base_adr
  • If you zip the two files along with your project file and attach them, I'll try to have a look tonight and see what's wrong.
  • Try making everything lower-case. It wouldn't surprise me if mixed case caused problems, because it's the file name being used to produce the labels. Both files also have to be named in the project file.
Sign In or Register to comment.