getting results back from assembly cog
David B
Posts: 592
I'd like to have a Spin program kick off an assembly program running in its own cog. The assembly program will do some work, then return a full 32 bits to the main Spin program.
But I'm only able to get up to 9 bits returned.
Here is a working demo of what I've tried so far. It blinks the on-board LED to indicate YES/NO results, and shows that the cog is returning values, but only up to 9 bits. Bits beyond 9 seem to be truncated.
What am I doing wrong? How can I get a full 32 bit result returned back to spin?
thanks,
David
''
'' Propeller "Hello, World!" demo
CON
_clkmode = xinput + pll16x ' external clock source; multiply by 16
_xinfreq = 4_000_000
Led = 27 ' on-board LED
VAR
LONG asmData
LONG asmResult
PUB main
dira[noparse][[/noparse]Led] := 1 ' Make LED pin output
cognew(@asmTest, @asmData)
repeat
if asmResult > $0
blink
PUB blink | t
outa[noparse][[/noparse]Led] := !outa[noparse][[/noparse]Led] ' toggle the pin state
t := cnt + 32_000_000
waitcnt(t)
DAT
ORG
asmTest
' MOV testNumber, #8 ' low numbers returned
MOV testNumber, #$1FF ' up to 9 bits are returned
' MOV testNumber, #$200 ' bits beyond that are cut off
MOV asmResultAddr, par
ADD asmResultAddr, #4
WRLONG testNumber, asmResultAddr
loop JMP #loop
testNumber RES 1
asmResultAddr RES 1
But I'm only able to get up to 9 bits returned.
Here is a working demo of what I've tried so far. It blinks the on-board LED to indicate YES/NO results, and shows that the cog is returning values, but only up to 9 bits. Bits beyond 9 seem to be truncated.
What am I doing wrong? How can I get a full 32 bit result returned back to spin?
thanks,
David
''
'' Propeller "Hello, World!" demo
CON
_clkmode = xinput + pll16x ' external clock source; multiply by 16
_xinfreq = 4_000_000
Led = 27 ' on-board LED
VAR
LONG asmData
LONG asmResult
PUB main
dira[noparse][[/noparse]Led] := 1 ' Make LED pin output
cognew(@asmTest, @asmData)
repeat
if asmResult > $0
blink
PUB blink | t
outa[noparse][[/noparse]Led] := !outa[noparse][[/noparse]Led] ' toggle the pin state
t := cnt + 32_000_000
waitcnt(t)
DAT
ORG
asmTest
' MOV testNumber, #8 ' low numbers returned
MOV testNumber, #$1FF ' up to 9 bits are returned
' MOV testNumber, #$200 ' bits beyond that are cut off
MOV asmResultAddr, par
ADD asmResultAddr, #4
WRLONG testNumber, asmResultAddr
loop JMP #loop
testNumber RES 1
asmResultAddr RES 1
Comments
You need to use DATA statements and pointers to the data you want to MOVe.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Don't visit my new website...
The work around as Gadgetman said is to declare a constant in the memory space and MOV using the address to the constant. You are actually doing this already, but you are declaring it as a variable then filling it in run time, instead state its value at compile time, ie testNumber·long $00000200.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
1+1=10
Jim C
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
1+1=10
I just found out about the Propeller Tricks & Traps document today (http://forums.parallax.com/forums/default.aspx?f=25&m=114128). That, at least, would have kept us both from falling victim to that trap.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
OS-X: because making Unix user-friendly was easier than debugging Windows
I gather there is a big difference between "buffer RES 1" and "buffer LONG $0" but I'm not clear how they differ. They both allocate a memory location, they both name it, and both allow it to be modified. I thought the only difference was that one has a predefined value and one doesn't, but now I guess there's more to it than that. And it's kind of confusing to call the second form a constant if it can be modified.
The tips and trap discussion on transferring an array to the hub has been helpful, but its hard for a Propeller assembly newbie to follow the fragments of code. A complete example would be helpful there.
I'll work on this when I get home tonight, if I have time, and post my results.
Thanks for all the tips,
David
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
1+1=10
Can you explain why your code should transfer all 32 bits where mine only transfers 9 bits? Are the names processed differently by the code, with one dereferenced where the other isn't or something?
There are 4 ways to deal with values larger than $1FF:
1) define it as a compile time constant as I have shown.
2) generate it through other operations which operate on all bits (ADD, SUB, SHL,....)
3) define it as a run time constant, here you use the RES, then (in your spin program) before you start the cog running assembly you load the location in hub memory with the value you want "testNumber = $0000_0200" followed by the cognew.
4) use four sets of MOV/SHL to load the value 8 bits at a time, but this is akward and wasteful so don't use it.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
1+1=10
When you use a register as the source, all 32 bits are moved. e.g.
mov testnumber,testvalue
...
testvalue long $FFFF_FFFF
will move the entire value of $FFFF_FFFF into testnumber.
Do you have a copy of the Propeller Early Documentation? If you look at the binary representation of the assembly instructions on page 4, you can see that the source value is limited to nine bits. That's because each cog has 512 (2^9) bytes of memory. Those nine bits can either be a literal value or an address.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
OS-X: because making Unix user-friendly was easier than debugging Windows
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
OS-X: because making Unix user-friendly was easier than debugging Windows
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
1+1=10
Do you have a suggestion on finding to reference "Propeller Early Documentation"? Tips on assembly programming are always appreciated.
Jim C
There's an interesting story behind this. I connected my GPS 1 pulse-per-second output to a Propeller input, and wrote a Spin program to capture the system counter and display the count on an LCD display on each rise of the GPS pulse. It worked, but I was surprised to see that the difference between successive counters was missing the low 4 bits. I guess that the time or the processing it takes to interpret the next Spin commands results in this effect, but I wanted better than 4 mHz resolution.
Thats what led to writing assembly to run in a separate cog to do the same thing but in assembler. It works, and I'm happy to report that this captures every last bit. So now I have a counter that can measure events with 64 mHz resolution and display the counts on an LCD.
My propeller board is run by a 4 mHz discrete canned crystal oscillator, the style that are commonly used on motherboards. At room temp, this project reported counts ranging from 64,000,513 Hz to 64,000,497 Hz; with about 16 Hz of noise. I held an ice cube against the can and the clock rate immediately dropped, fairly linearly. After a minute the rate was down to 63,999,993 Hz. Removing the cube resulted in the clock rising again.
I don't know how much noise was due to the crystal and how much to noise on the GPS pulse. It would be nice to temperature stabilize my local oscillator, then use the Propeller to adjust it's rate from the GPS signal, but that's one just more project among many...
Spin is great to work with. I've never gotten the LCD display to work so fast on any project; I basically pasted in a few lines of code and it just worked! I love how I can change the code, hit F10, and the new code is instantly running. It really helps speed experimenting and development.
Attached is the Spin archive of this project. There is still a part that isn't working properly - in the assembly part, there is an oddity where I assign one LONG with a value, but that value appears while reading the following LONG. But I imagine that's just some simple mistake I made somewhere.
David
It's attached to the first message in this thread: http://forums.parallax.com/showthread.php?p=572669
-Steve
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
OS-X: because making Unix user-friendly was easier than debugging Windows
Thanks for the reference. I've seem most of that info before from a different thread--but it's good to confirm there's not more.
David:
Thanks for the GPS code. That's pretty interesting--and tight! Not much code required to get your data.
Paul:
After going through it all again (including "Tricks and Traps"), the literal<>source<>destination business is starting to gel.
I have one more question, though, after reviewing this thread again: when returning a variable result from assembly back to the hub, why do you have to add #4 to the address that is being written into? Using the example below, seems like asmResultAddr is where you want to write to, not four bytes beyond that.
MOV asmResultAddr, par
ADD asmResultAddr, #4
WRLONG testNumber, asmResultAddr
Checking the Spin.95.1 manual for info on PAR I found a little snippet (below). Why does the example above need the #4 added, but the example below doesn't?
MOV Mem, PAR 'Retrieve shared memory addr
:loop <do something>
WRLONG ValReg, Mem 'Move ValReg value to Shared
JMP :loop
Thanks!
Jim
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
1+1=10
Great. That makes sense.
Thanks.
Jim
Writing one LONG to the hub worked perfectly. But writing the second would crash the Spin program! It would lock up, and garbage was written to the LCD display. Like Paul suggested, I was adding the #4 to prepare for writing the second LONG.
So in the code I posted, I just let the system counter snapshot LONG by itself be written to the hub, and it works perfectly. I'd sure like to know what I'm doing wrong with the second write, but I guess that's probably just a matter of gaining more experience with Propeller assembly coding.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
1+1=10
I'm not really sure what I want this to do; I'm mostly playing around with it, learning Spin and Propeller assembly and seeing what the Propeller can do.
But if nothing else, for my own education, I'd like to know what I'm doing wrong in getting multiple LONGs written to the hub, and also what I'm doing wrong in the assembly part where the value of the initialized LONGs shows an odd behavior that I commented on in the code.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
1+1=10
I am having trouble compiling the attached routine. It has something to do with my "ADCval" variable that is supposed to be shared between the ASSM and SPIN code. Specifically, at line 24 in "Shiftin_test_A_001" in the command "test.dec(ADCval)" is says that it is "expecting and expression term".
Any thoughts?
Thanks,
-Parsko
Graham
2) This is incomplete (there are no instructions after the last wrlong).
3) Shiftin_test_A_001 is not in the attached archive
Try something more like this:
If you need more than one word of data, you can try making common an array (long common ):
You do have to have a way to tell the SPIN routine that the work is done and the results have been written to the HUB RAM.
You can use the sign bit of one of the long values. The initial value of common is always positive. The COG sets the sign bit of temp2 to a one. When it writes temp2 into common, that long will have a sign bit of one and both common[noparse][[/noparse]0] and common will have been updated (since the second long is stored last).
I am starting object1. Calling object2 to start and run an assembly program. This program does something with external hardware, then writes the value to main memory. Object1 then writes this value to the TV, then loops back to read in another value. It's a wicked slow loop, but I am only trying to test the high speed shiftin assembly code to see if it works.
I can't get the writing to main memory to work. I am begoggled! The code in the shiftin object might be a bit messy cause I have been trying everything I know/can read.
Please help.
-Parsko
This writes the "finalValue" into ADCval. You can get an initial value the same way with "RDLONG value,PAR".