Native assembly files (*.S) in propgcc
altosack
Posts: 132
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).
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
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.
Attached is my code
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).
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:
And the code is attached.
Attached is updated code
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:
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.
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.
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.
multi-cog.zip
propeller.h
propeller1.h
propeller2.h
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
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 .
-- 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."
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.
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.
How about a concrete example (using character arrays instead of integer, because it makes the initialization easier): produces (for clarity I've omitted some .global and .balign directives generated by the compiler):
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
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.
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: or
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
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: 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!!!
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 But on some compilers you can do . It doesn't make much sense, though, unless you re-use the result e.g. (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:
===Jac