Early success followed by confusion...
dspread
Posts: 6
in Propeller 1
As a retired electronic (hardware) design engineer who has experience of code and HDL abstraction and bloat I was delighted to discover the Propellor! I was very pleased that my first simple veroboard P8X32A-D40 ciruit worked and ran code from the Propeller Tool, with very low current consumption. Then I tried a serial LCD display and have become quite confused, mainly about how to creat strings or byte arrays in Spin that are a mixture of hex and text. I have a long-winded routine that sets every byte in an array as a number, and passing this to an assembly serial transmit routine works, but when I attempt something more elegant it seems to upset the Tool or do something strange (I can monitor the serial line on an oscilloscope). Does Spin natively support strings, or do I have to include an additional library? Are there any subtleties about how an assembly cog works its way through a string?
Comments
in DAT sections you can use
mystringname1 byte 1,2,3,"A","B","C",$0D,$0A,0
mystringname2 byte "Hello World",0
in code you can use the string function
myaddress:=string("Hello",$0D,$0A,0)
to do the same.
Note: You can NOT use variables to build a string, this is compile time only. So myAddress:=string("Hello", myNumber,0) will not work.
In both cases you basically have a byte-array terminated by a zero, also known as zero-terminated string.
myaddress would already be the address of the array in memory, to access the string at mystringname1 you need to get the address of mystringname1 by using the @ operator giving you the hub-address of the label.
myaddress1 := @mystringname1 to get the address of the byte-array.
Most serial drivers have a str(address) method to output a string. That method takes a address as starting point and then sends out byte by byte over the serial line stopping at the first 0.
You can access (and change) the content of bytes in the byte-arrays thru indexing.
value := byte[myaddress][4] gives you the 5th byte so the "o" from "Hello" in the example string.
byte[myaddress][0] :="J" makes "Jello" out of "Hello"
for more complex manipulations you can use a library, there are a lot of them to use, but provided with the library folder in Proptool you have "SimpleStrings" to start with.
Enjoy!
Mike
BYTEMOVE(@buffer[5],STRING("label xxx",CR,LF,"more yyy"),20)
The xxx and yyy would be overlaid later with something else.
BTW possible typo:
myaddress:=string("Hello",$0D,$0A,0)
gives an error message for the final zero - "only values 1 - 255 are allowed". I think the string function adds it own zero at the end.
Kind regards,
David
The zero at the end is not needed as the string() function puts it there automatically.
If you have a function that expect a pointer to a location in memory that for example is a block of ascII text.
If you instead of a pointer just insert "MY TEXT" the compiler will put that text in flash and insert the pointer.
" " adds a zero, ' ' doesn't
if your function expect raw data and length, you could create a macro
#define TXSTRING(pnt) (TXdata((pnt), sizeof(pnt)-1)) // macro to get string and string len to function TXdata()
example of use:
TXSTRING("AT&K=0\r"); // hardware flow off
My application uses pipelining, i.e. one COG performs a fixed function, then another COG processes that result and outputs its own result, etc. Of course they all run together, and I am arranging them all to have exactly the same loop time so that they stay in synchronism (phase is irrelevant).
So far, so good.
What I am stuck on now is how to write a spin file containing independent assembly code for several cogs, which have to read and write from the correct main RAM locations. I haven't come across an example or tutorial that gives an explicit example of multicog ASM.
Helpful comments or examples welcome!
Each PASM cog is in its own DAT section, with its own local constants and variables.
Each PASM cog is launched with its own beginning address and PARM values, one a time.
Each PASM cog is passed pointers to the variables in hub memory it needs to know about.
If I recall correctly, there are examples in this text.
Have fun! It all makes perfect sense one you understand it.
Important is an ORG 0 before every code section, and that the register variables are defined in the same section. Don't use RES to reserve variables (exept maybe in the last section). And don't use register variables with the same name in different sections.
The Assembler does not warn you if you use a variable name from another section, so be careful.
Here is the structure: Andy
That confirms what I was thinking, including that each PASM section has to have its own independent copy of any shared variables.
How about passing multiple shared variables from the SPIN code? I tried passing the first through PAR and then adding offsets of 4 in the PASM code to find subsequent ones, which compiled OK but didn't seem to work (a WRLONG followed by a RDLONG seems not to return the data that I start with, and which I can monitor on my oscilloscope through MOV OUTA and an R/2R network). I also tried assigning the SPIN location (@) of each variable to a declared PASM variable, with the same result.
What I find particularly difficult is that there seems to be no way of seeing in the Propeller tool what locations the compiler has assigned to the variables etc., which would be a good sanity check (the hex file display doesn't look easy to navigate), and no steppable simulator.
On the bright side, I have made real progress with other areas. One simple point that dawned on me yesterday is that for optimal timing there must be 2 + 4N instructions between hub operations, where N≥0.
Here is how I pass multiple addresses: May not be the best but it works every time.
I have an array of longs. I put the addresses of each shared variable in an entry in the array, ordered in a manner known to both the spin and the specific PASM section. Then I pass the beginning address of the array as the PARM. The PASM section fetches the addresses, incrementing the PARM by four and saves them in registers for subsequent use in WRwhatever and RDwhatevers.
Don't forget that the syntax for WRwhatevers has the source first, unlike all other instructions.
You can get a listing by compiling with bst or homespun.
Some parts of the pasm code can show an address after you compile with PropTool and then point the mouse to the line/label. It shows the address (in cog) in yellow.
While these may not be quite what you are looking for, but they may help at some point intime.
Cluso99: You mentioned some tantalising tools, but I can't find any active links to BST or Homespun.
I have an array of longs into which I stick the addresses of the variables the first PASM cog needs to know about. I pass the address of the list of addresses in PARM.
Now the cog can get those addresses and stash them locally:
Attached is code that starts up 4 PASM cogs, each with variables known to all, and variable(s) known only to itself and the spin program.
I have been using PASM declarations of the form <label> long <value> (i.e. one label and one line for each variable), and fell into thinking that each was declaring a named variable. It seemed seemed natural then to leave out the value when no initialisation was necessary - the compiler didn't complain. Yesterday a careful study of the label COG locations revealed that if no value was specified, no space was allocated! Having gone back to the manual and seen the more general <label> long value1, value2, value3, etc. it make sense that a value is required to identify the presence of each variable: the label just points to the start of the allocated space.
Putting in a value for every variable restored my expected allocation, and the program works as intended.
Thanks everyone.
This is the way you can name (label) the same location more than once. It maybe a block of longs where the block has a name, and the individual long/word/byte/(s) are individually named as well.