Shop OBEX P1 Docs P2 Docs Learn Events
How are propeller objects loaded into hub RAM? — Parallax Forums

How are propeller objects loaded into hub RAM?

borisgborisg Posts: 39
edited 2013-02-11 22:55 in Propeller 1
I hope this isn't a real newbie question, but I haven't been able to find out anything about how code is loaded into hub RAM. So far I've just been playing around with assembly language programs and today, with some trepidation, tried to do some programming in Spin. It didn't take long to realize that a "Global" variable in my 1 msec timer object was anything but global. What I had hoped to do was to put a longword in low RAM where it would accumulate counts from my 1 msec timer which runs in a cog and have that variable available to other cogs by just doing a RDLONG. As it was, I had to add a spin function which returns the value of my "global" SysTimer which doesn't help me at all when it comes to performing inter-cog communication without Spin. When I look at the object info display in Propeller tool, every object has it's code start at location $10. Presumably at some point there's an object load map created of how all of the objects are loaded into RAM but I haven't been able to find it anywhere. With the amount of assembly code I'm going to potentially have, I'll also need a way of reclaiming the hub RAM that is just used to hold DAT structures with the cog code.

Is there any way to reserve the first 2 Kb of hub RAM for my exclusive use? Right now the only dedicated locations are 0 and 5 dealing with clock speed. What I had planned on doing for my project was to use direct addressing of global locations in low hub RAM with a literal addresses which would give me a maximum of 2048 bytes when accessing hub RAM as longs as well as not requiring a longword in the cog to store the address. I was planning on having a Propeller "page0" in a similar fashion to how the 6502 chip had shorter instructions to access commonly used variables in the first page of RAM. Given the complexity of my application which will do ambulatory physiologic monitoring from multiple sensors, I need every cog location and it would be more satisfying to use only a single propeller chip rather than go to a 2 chip solution.

I've found very little on the details of the Spin interpreter and, given the efficiencies which one can attain with addressing "Page 0" locations, it wouldn't surprise me if some of these locations were used by the spin interpreter as it would facilitate cramming the whole interpreter into a single cog.

While I'm not very impressed with Spin, I'm very impressed with the efficiency with which one can code routines in propeller assembly language. I do have to admit that I prefer programming in assembler and in my days as a programmer my PDP-11 programs were written almost exclusively in assembly with a bit of FORTRAN glue code to tie the routines together. Right now I'm trying to decide if my time would be better spent writing my own supervisor code in assembler rather than trying to learn Spin.

Comments

  • StefanL38StefanL38 Posts: 2,292
    edited 2013-02-10 01:55
    HI Boris,

    to be real honest I did'nt want to read all the text you have written.
    What I have understand so far is

    global variables are not really global.

    variables within a *.SPIN-file (most of the time an object are accessible within this file)
    If you wanthave access from everywhere you can write "set_variable" and "get_variable" methods inside your object
    and to acess them from outside this *.-spin-file call these method like any other method
    PUB Set_MyVar(My_Param)
      MyVar := My_Param
    
    PUB Get_MyVar
    
     result := MyVar
    
    
    PUB Main
      objectname.SetMyVar(100)
    
      MyOtherVar := objectname.GetMyVar
    

    as you come from assembler the other method is to pass the pointer to this variable
    to the other code

    As you are asking how are spin-files loaded into HUB-RAM
    why would you like to fiddle around with absolute adressing where the variable is stored and this place changes with every little change of the SPIN-code?
    just use the @-operator to get the valid HUB-RAM-adress

    best regards
    Stefan
  • AribaAriba Posts: 2,690
    edited 2013-02-10 02:18
    You can find a few documents in the forum which describe this "low level" stuff, but I don't think Parallax has a document about it. Attached is a document from hippy that shows you how the Spin/PASM code is stored in hub-memory. Especially the section "Wrapping up a main program" may be useful.

    As you see at the begin of hub memory 16 bytes are reserved for clock and Spin pointers, then comes a jump table for the Spin methodes of the top-object and then follow the DAT section of the top-object. So you can reserve 2kByte at the begin of the DAT section of the top object to reserve that space at begin of the hub ram (just don't use the first 32 bytes or so). But you can access only 512 bytes with the immediate form of WRBYTE and RDBYTE/-WORD/-LONG, not 2kB (hub ram is byte addressed, not long addressed like cog ram).

    Spin may not be so impressive for an Assembly programmer when it comes to speed,. Where Spin shines is compactness of code, easy to programm (objects) and very simple integration of PASM code for the fast parts of the project.

    Andy
  • borisgborisg Posts: 39
    edited 2013-02-10 02:22
    Stefan, these methods work fine as long as one is dealing with Spin code but I was hoping to bypass Spin as much as possible. For example, let's say that my SysClock 1 msec clock is located at absolute location $F0, then I can update that location from my 1 msec counter cog code by using:
    WRLONG msecCount, #F0
    instead of using:
    WRLONG msecCount, SysClock
    which requires me to have a cog variable SysClock holding the address of the 1 msec clock variable in hub RAM.

    Similarly, in any other cog that needs to know the value of the 1 msec clock to timestamp an event, all it would have to do is:
    RDLONG, msecCount, #F0
    This saves a whole long in the cog, but not knowing the location that my msec clock "global" is loaded, I can't directly access hub RAM to get at the value.

    Use of literal addressing allows one to access the first 512 bytes, words and longs in hub RAM using the RDBYTE, RDWORD and RDLONG instructions. What I was planning on doing in this low memory area was to hold commonly used variables and utilize the approach you outlined for less commonly used variables.

    My method does have the disadvantage that one needs to refer to memory locations by address rather than mnemonics but I'm already executing the Propeller assembly code in my head to check on timing and the longest routine I've written thus far has been some 30 instructions long.

    I can pass a single address to other cogs in the cognew call but I was hoping to be able to use most of the first 2048 bytes of hub RAM to create an inter-cog communication area with minimal Spin coding required. I'm probably dating myself when I bring up the Commodore 64 which used the first 256 bytes of RAM for storage of key system variables - the Propeller allows the same approach except one has fast cog access to 2048 bytes of low hub RAM.
  • borisgborisg Posts: 39
    edited 2013-02-10 04:08
    Ariba, thanks for the information. A bit disappointed that can't access 2048 bytes with WRLONG, but using the memory from $20 to $FF will be extremely useful in keeping most used common variables in a spot where literal addresses in cog WRLONG instructions can access them. Will be using a lot of shorter addresses for FIFO stack parameters than I'd planned on but then the Propeller is fast enough that small FIFO's will do.

    After perusing your document as well as the Propeller tool memory maps, discovered that main object DAT section comes first in physical memory. Can grab this RAM by creating array:
    PP0 Long $0[128]
    as the first statement in the main object's DAT section. Verified it by using
    WRLONG MsecTimer, #$20
    to write out a shared copy of my msec timer which will be used to timestamp all events in other cogs.

    Have been getting more proficient in Spin over the last couple of hours but still huge numbers of errors in comparison to the nice clean assembler instruction format. From my perspective, when one has such a powerful 32 bit instruction set as each cog has, why bother with a HLL? Again, I probably date myself as I can't see any use for OOP.

    Just looking at the timer values returned by my spin code (using Parallax serial terminal) and the values which are stored in RAM in my Spin test program shows that the serial terminal spin calls take from 1-3 msec which is fine in a user interface application but I've become used to the 20 MIPS/cog one can get programming in assembler. The Propeller is fast enough that one can create virtual peripherals with a small amount of assembler and get away with writing the main code in Spin. Curious that no-one has written a pure assembler system that takes full advantage of the Propeller's speed.
  • SapiehaSapieha Posts: 2,964
    edited 2013-02-10 04:46
    Hi borisg.

    It is One that program.

    Look for Linus demo program -- it is written entirely in PASM.
  • AribaAriba Posts: 2,690
    edited 2013-02-10 05:00
    borisg wrote: »
    ...
    Have been getting more proficient in Spin over the last couple of hours but still huge numbers of errors in comparison to the nice clean assembler instruction format. From my perspective, when one has such a powerful 32 bit instruction set as each cog has, why bother with a HLL? Again, I probably date myself as I can't see any use for OOP.
    ...

    Spin implements only a very simple form of OOP, the objects are more like modules or library files. The advantage is that you can mix them together without knowing how they work internally and you never will have nameing conflicts.
    I think there are alternative Assemblers (command line tools) but they are mostly used only by the creator. BST and Homespun are two alternative Spin compilers that also have improved Assemblers included. And they can generate a Listing with hub- and cog memory addresses.

    Programming a big project full in Assembly will be a pain, also if PASM is nice and clean. You have only a very limted memory inside the cogs, if you need longer code you can use the LMM technic (Large Memory Modell) which loads the instruction longs from hub and executes it. But then you soon wish you had a HLL that generates this LMM code ;-) . When you are at that point it's time to look at PropGCC. This C compiler generates can generate LMM and fast PASM code from a HLL.

    Andy
  • Heater.Heater. Posts: 21,230
    edited 2013-02-10 05:11
    Borisg,

    I would advise, just don't do it. Don't even think about reserveing any such "page 0" memory on the Prop.
    There have been hundreds of PASM codes written for the Prop that don't need to do that and I would imagin it's only an extreme circumstance that would absolutely require it.

    1) It's messy, you have to arrange for the data to be in that "page0" some how. Can be done but it's not nice.
    2) As you say you end up having to refer to those "page0" variable by number rather than symbol.
    2) It does not gain you any speed.
    3) Yes, you need a long in COG to hold the HUB address of your HUB variable. That has not cause problems for most people most of the time.

    So just use @ to get the address of you HUB variables and pass them into COGs via PAR as is normal practice.
    If you ever get to a situation where that thwarts your progress then come back here and shout and seare at me:)

    If you really don't want to work in Spin, a quite resonable approach there are some options:
    1) propasm will assemble pure PASM programs, no Spin required. It's a bit historic now I think as Cliff is not advancing it.

    2) Use propogcc. Even if you don't use C it can start up into your PASM that you write in GAS syntax. Then of course you can use your "page0" with ease.
  • borisgborisg Posts: 39
    edited 2013-02-10 05:31
    Sapieha, that was absolutely amazing! Downloaded the EEPROM code, loaded it into my propeller demo board and couldn't believe that 32 Kb of compressed data could do so much. Will be studying Linus's source code in the near future as there's huge potential here. My application isn't nearly as artistic; an ambulatory physiologic monitor for the Micromedic contest, but I'm sure I can pick up some good techniques from his code.

    Thanks a lot for that link.
  • StefanL38StefanL38 Posts: 2,292
    edited 2013-02-10 05:59
    Hi Boris,

    if you start a PASM-cog you can handover a long in the second parameter of the cognew.
    This can be any HUB-RAM-adress so then you have your memory location(s) for anything
    you want to exchange between any cogs

    best regards
    Stefan
  • MagIO2MagIO2 Posts: 2,243
    edited 2013-02-10 06:05
    If there is really a need of a global variable storage I'd do it like that:

    globalStore.spin
    CON
      SPECIAL_REG1 = 0
      SPECIAL_REG2 = 4
    
    PUB getAddress
      return @data
    
    DAT
    data long 0[2048]
    

    And use it like that:
    globalMain.spin
    obj
      gs: "globalStore"
    
    PUB start
      cognew( pasm_code, gs.getAddress )
      
    DAT
      org 0
    pasm_code
      add reg1Adr, par
      add reg2Adr, par
    
      rdlong reg1Val, reg1Adr
    
      ' ...
    
    reg1Adr long gs#SPECIAL_REG1
    reg2Adr long gs#SPECIAL_REG2
    
    reg1Val res 1
    

    You can "Import" the globalStore.spin in any other SPIN-file and the variables would not be duplicated. The PASM-code can access the variables as shown in the code. Some other SPIN-code can Access the variables with the long[]-Array:

    long[ gs.getAddress + gs#SPECIAL_REG1 ] := 0


    The idea behind the HLL in general is to have code which is much easier to understand. In Propeller world there is a much more important benefit: The bytecode is much more compact!
    So, with the 32k limit you can put more functionality in the code than in a PASM-only program.
  • borisgborisg Posts: 39
    edited 2013-02-11 01:16
    Thanks MagIO2 - will probably use this PAR based reference area. Had somehow overlooked PAR in my eagerness to start writing code for the Propeller. Getting the hang of Spin now but using my 1msec timer to look at the execution time of Spin have revealed horrendous results. For example, printing the value of the msec timer as well as the x, y and z acceleration data from MMA7455 takes 13-14 msec. This has ruled out my trying an SD based data logger at 115.2 Kbaud as my actigraphy test platform since I'd prefer to sample at 125 Hz in order to pick up high resolution data for each step while walking. So, now it's time to get on with the direct SD card interfacing portion of my project rather than use Spin based serial I/O.

    Have also discovered that loading of additional Spin objects seems to take up $10 bytes of low hub RAM for each object loaded. My 1msec timer test program worked well writing data to a long at $20 but when modified Greg Denson's MMA7455 interfacing program found out that the first free byte of Propeller "Page 0" was now at $30. Experimentally tried clobbering byte at $20 with my 1 msec timer and got a reboot from EEPROM. Have my EEPROM loaded with Turbulence so getting the audio from my Propeller Demo Board is a good indication that the system has rebooted itself and it's a reminder of how much one can do with PASM.

    While I agree with those who point out that byte code tokens are more efficient use of RAM than PASM longword instructions, I find it hard to go from an environment where I'm dealing with fractions of microseconds to one where single statement executions are measured in msec or tens of msec. Thus my desire to write as compact PASM code as possible to take advantage of the raw speed of the Propeller.
  • tonyp12tonyp12 Posts: 1,951
    edited 2013-02-11 13:48
    >executions are measured in msec or tens of msec
    A little faster than that, try 50ns for each 'regular' pasm instruction.

    I takes a little time to get used to the PAR.
    but if you create a set of VARibles in SPIN, they all will be in right order after each other in hub ram.
    So if you pass the address of the first variable to pasm though PAR, you will know the hub address and you offset from there.
    PAR is read only and you can not overwrite it by mistake, and you can not add to it so copy it to t1 etc and then add.

    Hub ram address is in bytes, so after writing a long you would need to add 4.
    A cog rams "byte" is always 32bit, so there you would add 1 after a long write to move to next location.
  • Heater.Heater. Posts: 21,230
    edited 2013-02-11 22:55
    I think borisg was talking about Spin. Each Spin byte code will take 50 to 80 PASM instructions of the interpreter to execute it. Numbers only approximate but you get the idea. Then each Spin statement may be compiled to many byte codes.

    @borisg,

    If you feel the need for speed perhaps you should look at using C instead of Spin. Both propgcc and Catalina compilers will compile down to so called LMM mode. That is where PASM code is generated but kept in HUB memory, a simple loop in COG fetches those instructions into COG one at a time and executes them. Basically it compiles to PASM that is run at one quarter speed of COG code.

    However don't forget that often a lot of your code does not actually need to be optimized for Speed. Once you have the serious work going in PASM in Cogs, those parts can be hooked together in Spin without much loss in performance.

    A simple made up example: Read from a serial line and log the incoming data to SD card. Here the stuff that needs to be fast is the serial driver and the SD block writer. Cranking up the speed of the "glue" code that connects them together may not be your bottleneck and may be easier to leave it as Spin.
Sign In or Register to comment.