Shop OBEX P1 Docs P2 Docs Learn Events
Native assembly files (*.S) in propgcc — Parallax Forums

Native assembly files (*.S) in propgcc

altosackaltosack Posts: 132
edited 2013-03-24 15:14 in Propeller 1
While I didn't do an exhaustive search, I didn't find any *.S files in the demos. I created some of my own as cog device drivers (ported from my SPIN DAT sections) and they work just fine. I *love* having conditional compilation and pre-processing in PASM, as well as being able to include the same header files (#defines) for both C and PASM, so it's now easy to keep things in sync.

I have a few tidbits that may be useful to others trying the same thing:

1. It doesn't seem useful to me to use the feature of loading SPIN binary files; by using *.S files, you get the features noted above, which are indispensable to me, and porting from .spin PASM to .S PASM is trivial.

2. Labels will always work if placed in column 1; however, you can place them in any column you want by putting a colon at the end of the label (this is not standard PASM syntax, but it is standard for other assemblers that I've used).

3. To repeat something that *is* in the documentation, you cannot use local labels.


There are two things that are not working as I expect:

1. If I use the RES keyword, it doesn't update the address of subsequent labels unless I put a number afterwards (i.e., "1"); however, with a number, it acts like ".long 0", and takes space in the object file (and in Hub RAM, unless it's an ecog).

2. "<label>: .float <some number>" doesn't work; it gives me a syntax error no matter what I put there. My work-around has been to write a little program that converts my 32-bit floating point number to the equivalent long integer and use "<label>: .long <converted float>, which works just fine. It's just a little cumbersome, I have to put in a comment so I know what the number actually is, and it must be hard-coded.


I've included an example file (pwm.S) to whet your appetite for what you can do with propgcc PASM. It's a system clock speed PWM driver with 2 outputs in 1 cog and with optional stepping to increase the PWM resolution up to 16 bits while keeping a high PWM rate (up to 391 kHz @ 100 MHz) to reduce component costs. The 2nd output and the stepping can easily be skipped by conditional compilation if you don't need it, saving Hub RAM.

All the #ifdefs make it look a little messy, but it accomplishes the goal of using the driver for multiple purposes while only using as much code as you need with a single change to either the Makefile or the header file (wherever your defines are).
/*******************************************************************************
 Module:        pwm.S, v.0.1, 16 August 2012
 
 Written by:    David Voss, Tree of Life Foundation Renewable Electronics,
                Patagonia, AZ


 Target:        Parallax Propeller P8X32A, prop-gcc, memory model = cog


 Purpose:       System Clock Speed PWM driver with (2) outputs


 PAR holds the hub RAM address of the [2] 16-bit duty cycles.  If you only need
 1 PWM output, don't #define PWM_B_PIN in the header file, and the code will be
 a little smaller.
 
 Software stepping of the remaining bits of the (max) 16-bit duty cycle is
 implemented to increase the duty cycle resolution while still having a high PWM
 rate (short PWM period). To implement this, both the PWM resolution and the PWM
 native resolution should be a power of 2. If you don't need this, comment out
 #define PWM_STEPPING in the header file, and the code will be a lot smaller.


 The following symbols should be #defined in the processor-specific header file:
 
        PWM_A_PIN
        PWM_B_PIN (if implemented)
        PWM_RES                     // In System Clocks (max = 65536)
    #ifdef  PWM_STEPPING
        PWM_NATIVE_RES              // In System Clocks (min = 256 ==> 391 kHz)
        PWM_STEPPING_BITS           // = log2(PWM_RES) - log2(PWM_NATIVE_RES)
    #endif
*******************************************************************************/


#include "main.h"                   // Pin assignments, PWM resolution, and 
                                    //   #define PWM_STEPPING (or not) are here


#define CTR_PWM_MODE            %0_00100_000
#define dutyA_ptr               PAR
#define dutyA                   OUTB            // Save a little Hub RAM...
#define periodStart             DIRB            // ""


            .org      0
            .cog_ram


pwm_driver: movs    CTRA, #PWM_A_PIN            // Set the output pin
            movi    CTRA, #CTR_PWM_MODE         // Set counter mode to PWM
            mov     FRQA, #1                    // Set Frequency to System Clock


            or      DIRA, diraSet               // Set data direction register


    #ifdef PWM_B_PIN
            mov     dutyB_ptr, PAR              // PAR is a ptr to PWMmbox {
            add     dutyB_ptr, #2               //      unsigned short  DutyA;
                                                //      unsigned short  DutyB; }
            movs    CTRB, #PWM_B_PIN
            movi    CTRB, #CTR_PWM_MODE
            mov     FRQB, #1
    #endif


            mov     periodStart, CNT            // Initialize start from the
    #ifdef PWM_B_PIN                            //   system counter
            add     periodStart, halfPeriod
    #else
            add     periodStart, period
    #endif


pwm_loop:
        #ifdef PWM_STEPPING
                rdword  unsteppedDutyA, dutyA_ptr
                mov     carryA, unsteppedDutyA
                shr     unsteppedDutyA, #PWM_STEPPING_BITS
                shl     carryA, #(32 - PWM_STEPPING_BITS)
                mov     steppingCtr, steppingCount
        #else
                rdword  dutyA, dutyA_ptr
        #endif
    #ifdef PWM_B_PIN
        #ifdef PWM_STEPPING
                rdword  unsteppedDutyB, dutyB_ptr
                mov     carryB, unsteppedDutyB
                shr     unsteppedDutyB, #PWM_STEPPING_BITS
                shl     carryB, #(32 - PWM_STEPPING_BITS)
        #else
                rdword  dutyB, dutyB_ptr
        #endif
    #endif
        #ifdef  PWM_STEPPING
            native_pwm_loop:
                    mov     dutyA, unsteppedDutyA
                    add     accumulatorA, carryA        wc
            if_c    add     dutyA, #1
        #endif
    #ifdef  PWM_B_PIN
                    waitcnt periodStart, halfPeriod   // Start PWM B 180 deg out
    #else                                             //   of phase from PWM A to
                    waitcnt periodStart, period       //   reduce electrical noise
    #endif
                    neg     PHSA, dutyA


    #ifdef  PWM_B_PIN
        #ifdef  PWM_STEPPING
                    mov     dutyB, unsteppedDutyB
                    add     accumulatorB, carryB        wc
            if_c    add     dutyB, #1
        #endif
                    waitcnt periodStart, halfPeriod
                    neg     PHSB, dutyB
    #endif


    #ifdef  PWM_STEPPING
                    djnz    steppingCtr, #native_pwm_loop
    #endif
                jmp     #pwm_loop


#ifdef PWM_B_PIN
    diraSet:            .long   (1 << PWM_A_PIN) + (1 << PWM_B_PIN)
    #ifdef PWM_STEPPING
        halfPeriod:     .long   PWM_NATIVE_RES / 2
    #else
        halfPeriod:     .long   PWM_RES / 2
    #endif
#else
    diraSet:            .long   (1 << PWM_A_PIN)
    #ifdef PWM_STEPPING
        period:         .long   PWM_NATIVE_RES
    #else
        period:         .long   PWM_RES
    #endif
#endif




#ifdef  PWM_STEPPING
    steppingCount:      .long   1 << PWM_STEPPING_BITS
    steppingCtr:        res     1
    unsteppedDutyA:     res     1
    accumulatorA:       res     1
    carryA:             res     1
#endif


#ifdef  PWM_B_PIN
    dutyB_ptr:          res     1
    dutyB:              res     1


    #ifdef  PWM_STEPPING
        unsteppedDutyB: res     1
        accumulatorB:   res     1
        carryB:         res     1
    #endif
#endif

Comments

  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-14 13:19
    One big difference between PASM and PropGCC assembly is how COG symbol addresses are handled. In PASM, symbols use 9-bit long addresses in the range from 0 to 511. In the PropGCC assembler, the symbols use byte addresses. The jump instructions automatically compensate for this, and a "jmp #loop" does the right thing. However, a "mov temp,#loop" will put the byte address of loop in temp, and not the long address. There is a special "mova" psuedo-op that will automatically divide the symbol address by four, so you would use "mova temp,#loop" instead of "mov temp,#loop".
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-03 23:44
    I would like to create an assembly array with the length define at the top of the file like so:
    #define ARRAY_SIZE    16
    ....
    ....
    array:    res    ARRAY_SIZE
    

    But the assembler is yelling at me saying: "Error: .space specifies non-absolute value". Is there a way to do this?

    --Edit--
    #define isn't working at all for me. It's not working with something as simple as "cmp offset, #ARRAY_SIZE wc".
    Here is my full output. The commands are based off of what Eclipse is creating for me and then edited to (hopefully) combine assembly and C.
    [B]david@Balrog-Quantal:~/things.../Tachometer_gcc$[/B] propeller-elf-gcc -I/things2.../Library -I/opt/parallax/include -Os -c -fmessage-length=0 -mlmm -fno-exceptions -MMD -MP -MF"Tachometer_gcc.d" -MT"Tachometer_gcc.d" -o "Tachometer_gcc.o" "Tachometer_gcc.c"
    [B]david@Balrog-Quantal:~/[/B][B]things...[/B][B]/Tachometer_gcc$[/B] propeller-elf-as Tachometer_gcc.s -o Tachometer_gcc_as.o
    [B]david@Balrog-Quantal:~/[/B][B]things...[/B][B]/Tachometer_gcc$[/B] propeller-elf-gcc -L/opt/parallax/lib -o "Tachometer_gcc"  *.o
    Tachometer_gcc_as.o: In function `decArray':
    (.cogtachometer+0x4c): undefined reference to `ARRAY_SIZE'
    ..... Many other errors involving undefined references to things that should be defined by "#define"
    

    Attached is my code
  • altosackaltosack Posts: 132
    edited 2013-03-04 07:52
    Hi SwimDude,

    I may live in AZ right now, but I'm from MO, will be back there soon, and went to college (Mechanical Engineering) in your town.

    In what you quoted above, I see that your input file is "Tachometer_gcc.s", which will not be run through the pre-processor, and the #defines will not be processed. It needs to have a capital ".S" to make it run through the pre-processor. It should be compiled with gcc, not as; gcc is the pre-processor that will automatically invoke the c compiler or the assembler, depending on the filename extension .c or .S.

    Usually, ".s" files are only created when you request assembly output from the compilation of .c files; as such, they don't need to be run through the pre-processor since they have already done so during the .c compilation. All assembly *source* files (i.e., written by humans) should have a capital .S; even if they are trivial enough to work without the pre-processor, it's not standard practice and can be misunderstood.

    It's not clear to me that you are typing in the above, or it is being done by the IDE or a make file (and in your zip file, it does have a capital in the filename extension), so what I'm saying may not apply to you.

    I hope this helps and it is this simple.

    [Edit] As I said in my first post, I don't think RES is working properly (I don't think it's been fixed, but I haven't tested it recently), so I don't know how to create a variable array size in assembly. Workarounds could be to combine a cogc file with a .S file (I don't know how to do this, and don't bother since I just make my cog driver completely in assembly, but I think Eric Smith has alluded to it being possible), or to simply place the array at the end of the cog memory and let it access beyond the end of the defined code, as long as it doesn't exceed the available cog memory (not good programming practice, but it would work).
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-04 21:21
    Nice to find another Rolla grad :) I'll be graduating with a computer engineering degree this December.

    In other news, I was able to complete compilation and building with Eclipse. I've created a simpler, test project that will simply blink two LED (one on each of two cogs). I set Eclipse to use gcc as the "assembler" and copied/pasted all flags from the compiler to the assembler's miscellaneous section. Below is the result:
    make all Building file: ../Blinky_gcc.c
    Invoking: Cross GCC Compiler
    propeller-elf-gcc -I/home/david/Desktop/Kits/Embedded/Parallax/Library -I/opt/parallax/include -Os -c -fmessage-length=0 -mlmm -fno-exceptions -MMD -MP -MF"Blinky_gcc.d" -MT"Blinky_gcc.d" -o "Blinky_gcc.o" "../Blinky_gcc.c"
    Finished building: ../Blinky_gcc.c
     
    Building file: ../Blinky_gcc_as.S
    Invoking: Cross GCC Assembler
    propeller-elf-gcc -I/home/david/Desktop/Kits/Embedded/Parallax/Library -I/opt/parallax/include -Os -c -fmessage-length=0 -mlmm -fno-exceptions -o "Blinky_gcc_as.o" "../Blinky_gcc_as.S"
    Finished building: ../Blinky_gcc_as.S
     
    Building target: Blinky_gcc
    Invoking: Cross GCC Linker
    propeller-elf-gcc  -o "Blinky_gcc"  ./Blinky_gcc.o ./Blinky_gcc_as.o   
    Finished building target: Blinky_gcc
     
    make --no-print-directory post-build
    Build hex file
    propeller-elf-objdump -h Blinky_gcc && cp Blinky_gcc a.out
    

    And the code is attached.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-04 23:25
    Well I give up for tonight. I've added some comments, added some debugging functionality, and still nothing. It must just be one little thing that I'm not doing correctly...

    Attached is updated code
  • altosackaltosack Posts: 132
    edited 2013-03-05 07:30
    Unfortunately, it isn't just one little thing. Running drivers in cogs (assembly or c) takes quite a few steps to get it right. Keep in mind that I learned how to do this some months ago with release 0.8.5 of SimpleIDE (but I don't use SimpleIDE; I use a makefile), so it might be easier now. You might want to look at some of the demo code included with the release, particularly toggle, which includes many different ways to toggle a pin, including LMM, LMM thread, COGC, PASM, etc.

    C that is run directly in a cog needs to be compiled with the memory model "cog", or "-mcog", meaning that it must be in a separate file, just like the assembly driver.

    For either COGC or assembly, you need to rename the .text section in the object file (with OBJCOPY) to something that ends in .cog. The linker will then make two labels in the object file called _load_start_mydriver_cog and _load_stop_mydriver_cog, which you can use to start a cog by using (for example) cognew(&_load_start_mydriver_cog, &mydriver_mailbox);. In this case, the address of mydriver_mailbox will be available to the cog program as PAR (read about PAR in the propeller manual if you don't know what this means). If, as for a simple test like blinky, you don't need a pointer to hub RAM, you can put zero here.

    A simplified sequence for both cogc and pasm cogs would be:
    gcc blinky.c -o blinky.o
    gcc -mcog blinkycogc.c -o blinkycogc.o
    gcc -mcog blinkypasm.S -o blinkypasm.o
    objcopy --localize-text --rename-section .text=blinkycogc.cog  blinkycogc.o
    objcopy --localize-text --rename-section .text=blinkypasm.cog  blinkypasm.o
    gcc blinky.o blinkycogc.o blinkypasm.o -o wholeblinky.elf
    

    The lines in blinky.c that loads the cogs would then be cognew(&_load_start_blinkycogc_cog, 0) and cognew(&_load_start_blinkypasm_cog, 0);.

    I automate all of this with a Makefile, which I think you'll want to move to eventually, anyway, if you're a control freak like me. I have lines at the top where I list my main source files (PRJSRC), my cog drivers (COGSRC), and my ecog drivers (ECOGSRC), and then everything (except the cognews) is done for me (see attached example Makefile). Beware that if you've never used make before, it has a few quirks (such as a required tab for whitespace - make sure to set your editor properly for that) and a steep learning curve, but you should be able to change what you need to get started and understand the rest as necessary as time goes on.

    To save space in hub RAM, you'll probably eventually want to load all your cog drivers directly from EEPROM (the upper 32k of 64k) and use .ecog instead of .cog (see cogload.h in /opt/parallax/bin/include), but for now just use .cog.
    PRJ=resys
    MEMORY_MODEL=lmm
    PRJSRC=main.c regulate.c prop.c
    PRJHDR=main.h nonvol.h common.h prop.h
    COGSRC=
    ECOGSRC=adc.S pwm.S lcd.c
    
    
    DEFS=-D_prop_
    DEFS+=-D_LOCAL_DISPLAY_
    #DEFS+=-D_DEBUG_
    
    #DEFS+=-D_SOLAR_
    #DEFS+=-D_SOLAR_MPPT_
    #DEFS+=-D_SOLAR_MPPT_SWEEP_
    
    # additional includes (e.g. -I/path/to/mydir)
    #INC=
    
    # libraries to link in (e.g. -lmylib)
    LIBS=-lm
    
    # Optimization level, use s (size opt), 1, 2, 3 or 0 (off)
    OPTLEVEL=s
    OPTLEVEL+=-m32bit-doubles# -fno-exceptions -fno-inline
    OPTLEVEL+=-mno-fcache
    #DEFS+=-Dprintf=__simple_printf
    
    ##### Flags ####
    
    CFLAGS=-m$(MEMORY_MODEL) $(DEFS) -O$(OPTLEVEL) -Wall
    COGFLAGS= $(DEFS) -Os -r -mcog
    LDFLAGS=-m$(MEMORY_MODEL) $(LIBS)# -Xlinker -Map=$(PRJ).mmap
    
     ##### executables ####
    EXEC_PREFIX=propeller-elf
    CC=$(EXEC_PREFIX)-gcc
    AS=$(EXEC_PREFIX)-as
    LD=$(EXEC_PREFIX)-ld
    OBJCOPY=$(EXEC_PREFIX)-objcopy
    OBJDUMP=$(EXEC_PREFIX)-objdump
    SIZE=$(EXEC_PREFIX)-size
    STRIP=$(EXEC_PREFIX)-c++filt --strip-underscore
    NM=$(EXEC_PREFIX)-nm -n
    #BSTC=bstc.linux -c
    REMOVE=rm -f
    EDIT=gedit --new-window
    LOAD=propeller-load -b $(PRJ) -r
    
    ##### automatic target names ####
    TRG=$(PRJ).elf
    DUMPTRG=$(PRJ).dump
    
    # List all object files we need to create
    OBJ=$(patsubst %.c,%.o,$(patsubst %.S,%.o,$(PRJSRC)))
    COGOBJ=$(patsubst %.S,%.cog,$(patsubst %.c,%.cog,$(COGSRC)))
    ECOGOBJ=$(patsubst %.S,%.ecog,$(patsubst %.c,%.ecog,$(ECOGSRC)))
    ALLOBJ=$(OBJ) $(COGOBJ) $(ECOGOBJ)
    
    .SUFFIXES:    .c .o .cog .ecog .out .map .s .S .dump .elf .h 
    .PHONY:        all d dump s symbols c clean r run e edit p program
    
    # Make targets:
    all: $(TRG)
        @ $(SIZE) $(ALLOBJ) $(TRG)
        @#$(STRIP) < $(PRJ).mmap | perl map.pl > $(PRJ).map
        @ $(REMOVE) $(PRJ).mmap
    
    p: program
    program: $(TRG)
        @ $(LOAD) $(TRG)
    
    pt: $(TRG)
        @ $(LOAD) -t $(TRG)
    
    d: dump
    dump: $(DUMPTRG)
        @ $(EDIT) $(DUMPTRG) &
    
    t: $(ALLOBJ)
        @ $(SIZE) $(ALLOBJ)
    
    s: symbols
    symbols: $(TRG)
        @ $(NM) $(TRG) | less
    
    c: clean
    clean:
        $(REMOVE) $(ALLOBJ) $(PRJ).map $(DUMPTRG) *~
    
    $(DUMPTRG): $(TRG) Makefile
        @ $(OBJDUMP) -z -S -r $(TRG) > $(DUMPTRG)
    
    $(TRG): $(ALLOBJ) Makefile
        $(CC) $(LDFLAGS) $(ALLOBJ) -o $(TRG)
    
    %.o: %.c $(PRJHDR) Makefile
        $(CC) $(CFLAGS) -c $< -o $@
    
    %.o: %.S $(PRJHDR) Makefile
        $(CC) $(CFLAGS) -c $< -o $@
    
    %.cog: %.c $(PRJHDR) Makefile
        $(CC) $(COGFLAGS) $< -o $@
        $(OBJCOPY) --localize-text --rename-section .text=$@ $@
    
    %.cog: %.S $(PRJHDR) Makefile
        $(CC) $(COGFLAGS) -c $< -o $@
        $(OBJCOPY) --localize-text --rename-section .text=$@ $@
    
    %.ecog: %.c $(PRJHDR) Makefile
        $(CC) $(COGFLAGS) $< -o $@
        $(OBJCOPY) --localize-text --rename-section .text=$@ $@
    
    %.ecog: %.S $(PRJHDR) Makefile
        $(CC) $(COGFLAGS) -c $< -o $@
        $(OBJCOPY) --localize-text --rename-section .text=$@ $@
    
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-06 18:50
    Okay - tired of uploading zips so here's a github repo: https://github.com/SwimDude0614/Propeller_Blinky_gcc

    I
    'm also no good with makefiles so I wrote a bash script to do the compilation for me. Once I get this whole thing working I'll look through your Makefile example (thank you btw!) enough to understand it.

    The source code is the same as before except for one small thing - I commented out the line calling the C implementation of blinky in another cog. Now the code *should* blinky 3 LEDs showing the cod ID of the PASM cog and one LED being blinked by the PASM cog.

    The remains the same though - 3 LEDs blink, showing me that cog 1 is running PASM, but a fourth LED is not blinking. If you could look through the bash script to ensure I didn't miss something, I'd appreciate it.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-03-06 19:41
    FYI, there is a demo called multi-cog (in propgcc/demos/multi-cog) that loads all 8 COGs with C code. Each COG blinks a different LED on a QuickStart board.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-06 19:43
    I did notice that and that's what first got me started trying to do this. Unfortunately, it using LMM on the new cogs as well. I would really like to know how to run code directly in a cog without any overhead :(
  • David BetzDavid Betz Posts: 14,516
    edited 2013-03-06 19:52
    I did notice that and that's what first got me started trying to do this. Unfortunately, it using LMM on the new cogs as well. I would really like to know how to run code directly in a cog without any overhead :(
    It's not running LMM in the COGs that it starts up. It's running -mcog code.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-06 20:01
    I can see from thread.h:
    /* * start a new cog thread running C code
     * (as if "func(arg)" was called in that thread)
     * tls is a pointer to the thread local storage area to use
     */
    int _start_cog_thread(void *stacktop, void (*func)(void *), void *arg, _thread_state_t *tls);
    
    But "new cog thread" sounds like LMM to me. How would it pass parameters into the function if it's running in cog mode without a kernel?

    Also, I can how this ^^^ might sound like I'm saying you're wrong. Please don't take it that way. I mean it as nothing more than a question.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-03-06 20:08
    I can see from thread.h:
    /* * start a new cog thread running C code
     * (as if "func(arg)" was called in that thread)
     * tls is a pointer to the thread local storage area to use
     */
    int _start_cog_thread(void *stacktop, void (*func)(void *), void *arg, _thread_state_t *tls);
    
    But "new cog thread" sounds like LMM to me. How would it pass parameters into the function if it's running in cog mode without a kernel?

    Also, I can how this ^^^ might sound like I'm saying you're wrong. Please don't take it that way. I mean it as nothing more than a question.
    We must be talking about two different things. Here is the demo I'm talking about.

    multi-cog.zip
  • David BetzDavid Betz Posts: 14,516
    edited 2013-03-06 20:14
    David Betz wrote: »
    We must be talking about two different things. Here is the demo I'm talking about.

    multi-cog.zip
    Ugh. I just realized that you probably won't be able to build this because it uses some new header files from the Propeller 2 version of PropGCC. Here are the required headers.

    propeller.h

    propeller1.h

    propeller2.h
  • jazzedjazzed Posts: 11,803
    edited 2013-03-06 22:05
    I did notice that and that's what first got me started trying to do this. Unfortunately, it using LMM on the new cogs as well. I would really like to know how to run code directly in a cog without any overhead :(

    Assuming I understand what you mean ....

    You need cognew as described here.
    https://sites.google.com/site/propellergcc/documentation/libraries/propeller-h-library#TOC-cognew

    There are several examples in the toggle demos: cog_c_toggle, gas_toggle, and pasm_toggle.

    https://code.google.com/p/propgcc/source/browse/#hg%2Fdemos%2Ftoggle%2Fcog_c_toggle
    https://code.google.com/p/propgcc/source/browse/#hg%2Fdemos%2Ftoggle%2Fgas_toggle
    https://code.google.com/p/propgcc/source/browse/#hg%2Fdemos%2Ftoggle%2Fpasm_toggle
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-06 23:16
    Thanks both for all the examples. That's what I really needed and was unable to find. Don't know how I missed that whole folder :-\
  • altosackaltosack Posts: 132
    edited 2013-03-07 00:20
    Please don't use bash ! Make will do exactly what you need a lot easier. For now, you don't need to understand the Makefile; the example I included can be modified for your purposes by just modifying the first 6 lines. Put the name of your project in PRJ, your lmm sources in PRJSRC, your headers in PRJHDR, and cogc and pasm sources in COGSRC, and type make.

    Each cog driver should have its own separate file (either .c [cogc] or .S), and be loaded with cognew from an lmm source file.

    Even though I don't think you should use bash, I checked your bash script; it has errors. The new code section you're changing from .text with objcopy *must* end with .cog, so it should be blinky.cog, not .cogblinky. However, I think it's bad programming practice to name it the same as your project name; I would use blinky-cogc.cog or blinky-pasm.cog, respectively. Also, while you may be able to use *o to include all the object files (I'm not sure; I've never done it), I think you should explicitly name them so you don't get bitten as the project expands. The Makefile will do all this automatically :smile:.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-09 17:53
    Wow.... finally got it. Using makefiles in Eclipse and everything. The final problem... any guesses? I was declaring the variable as a pointer rather than an array ("extern unsigned int *_load_start_blinky_gcc_as_cog" as opposed to "extern unsigned int _load_start_blinky_gcc_as_cog[]"). Why did that matter?

    -- Update --
    Someone elsewhere has suggested the following, but I could use some clarification from those who actually know the context of the question.
    "The difference is subtle. x[] holds a reference within its current scope where as x* can hold a memory reference elsewhere."
  • David BetzDavid Betz Posts: 14,516
    edited 2013-03-09 20:43
    Wow.... finally got it. Using makefiles in Eclipse and everything. The final problem... any guesses? I was declaring the variable as a pointer rather than an array ("extern unsigned int *_load_start_blinky_gcc_as_cog" as opposed to "extern unsigned int _load_start_blinky_gcc_as_cog[]"). Why did that matter?

    -- Update --
    Someone elsewhere has suggested the following, but I could use some clarification from those who actually know the context of the question.
    "The difference is subtle. x[] holds a reference within its current scope where as x* can hold a memory reference elsewhere."
    I'm afraid I don't understand that explanation but the two are definitely different types.
    extern int foo[]; // foo is the address of the first element of an array of integers
    extern int *foo;  // foo is the address of a 32 bit pointer to an integer
    

    They aren't interchangeable in the context of a declaration. I think the confusion is that in code you can use the two interchangably but that's just because the compiler automatically converts a reference to an array to a pointer.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-10 00:39
    Can you elaborate on that? I'm still not understanding the difference.

    I will assume the difference is something to do with the compiler, not run-time. Run-time, both of those variable are interpreted as addresses, exactly the same correct? So the compiler is doing some magic in the declaration of those two variables (or in the case of "extern", is expecting said magic to have been done previously) that I have never heard of.
  • ersmithersmith Posts: 6,054
    edited 2013-03-10 05:42
    Can you elaborate on that? I'm still not understanding the difference.

    How about a concrete example (using character arrays instead of integer, because it makes the initialization easier):
    char foo_array[] = "hello";
    char *foo_ptr = "hello";
    
    produces (for clarity I've omitted some .global and .balign directives generated by the compiler):
    _foo_array
            .ascii "hello\0"
    
    .LC0
            .ascii "hello\0"
    _foo_ptr
            long    .LC0
    

    As you can see, the label "foo_array" directly contains the string, whereas "foo_ptr" contains a reference (.LC0) to the actual string. The compiler allows you to use both of these the same way in many contexts, but not in all. In particular the declaration has to be correct, otherwise GCC won't know whether it should be getting the characters directly from memory referred to by the symbol (foo_array) or via the pointer that's at the symbol location (foo_ptr).

    Eric
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-10 12:35
    If I'm understanding that correctly, then
    char bar = foo_array;
    
    would place 'h' into bar. But it ought to place the address of 'h' into bar, correct?
  • jazzedjazzed Posts: 11,803
    edited 2013-03-10 13:15
    If I'm understanding that correctly, then
    char bar = foo_array;
    
    would place 'h' into bar. But it ought to place the address of 'h' into bar, correct?

    char *bar = foo_array; // places the address of 'h' into bar. Then *bar contains 'h'.

    The point is that the pointer is a variable that points to the array.
    While you can use the pointer to access items of the array, the storage is different.
  • ersmithersmith Posts: 6,054
    edited 2013-03-10 15:49
    If I'm understanding that correctly, then
    char bar = foo_array;
    
    would place 'h' into bar. But it ought to place the address of 'h' into bar, correct?

    The line above will produce an error from the compiler, actually, since you're trying to assign an address (foo_array) to a character. You could do either:
    char bar = foo_array[0];
    
    or
    char *bar = foo_array;
    

    The first will assign a character, the second will assign a pointer. In most contexts C will automatically convert an array name into a pointer to the first element of the array (which is why arrays and pointers act so similarly). Note that this is very different from Spin, which automatically converts an array name into a reference to the first element of the array!

    Eric
  • DavidZemonDavidZemon Posts: 2,973
    edited 2013-03-24 01:07
    Woh.... woh... mind = blown.

    I had this detailed post written up ready to question you and say "WTF???" again but realized I could just answer my question by running a quick program on my PC. And my question is answered. char str[] is actually a *constant* as your example implies. I verified this with:
    void main (void) {
         char str[] = "hello\n";
         str++;
         printf("%s", str);
    
         return;
    }
    
    And upon compilation I received an error saying "str++" was invalid - it expected an lvalue. I had no idea...... For some further validation, I removed the braces and changed it to a pointer (char str[] --> char *str) and it compiled without errors. HOW WEIRD!!!
  • jac_goudsmitjac_goudsmit Posts: 418
    edited 2013-03-24 15:14
    char str[] is actually a *constant* as your example implies.
    For some further validation, I removed the braces and changed it to a pointer (char str[] --> char *str) and it compiled without errors. HOW WEIRD!!!

    Not weird, but standard C. (Okay maybe a little weird :-) )

    Arrays and pointers are interchangeable but not in all cases. When you do char str[ ], the characters are stored in memory, and str is the address of the first byte. This is not an lvalue meaning you can't change str itself, you can only change *str. str itself is not stored in memory but the characters are. So let's say the characters are stored at location 100 in memory, then str simply represents the value 100 and str[0] is the character that's stored there. You can't change the value of 100 so you can't use it on the left side of an assignment.

    When you do str *, the characters are still stored in memory, but there is another place in memory which initially holds the address where the string starts, but can be changed. So in the case where the characters are stored at address 100, str in this case doesn't represent the address 100 but represents a location in memory at (let's say) 200. And stored at location 200 in memory, there is a pointer to address 100. You can change the value stored at 200 of course, e.g. you can increment it by one to point to the next character in the string.

    By the way, the quoted string is stored as an array and represents an array: you can't do
    "hello, World\n" = "H";
    
    But on some compilers you can do
    "hello, World"[0] = 'H';
    
    . It doesn't make much sense, though, unless you re-use the result e.g.
    printf(&("hello, World\n"[0] = 'H'));
    
    (Again, this doesn't work on all compilers, I don't expect it to work on gcc).

    There are fun things you can do with literal string indexing. Example:
    int i;
    for(;;)
      for (i = 0; i < 3; i++)
        printf("Please wait %c\r", "-/|\"[i]);
    

    ===Jac
Sign In or Register to comment.