"Spinless" objects, "coglet's", etc.
ctwardell
Posts: 1,716
I recall seeing discussion in the past, about objects with no dependency on spin.
The general idea is to have objects that can be loaded and used by any language.
Did the group, as in forum members, ever come to any consensus on best practices?
I'm working on an 1802 emulator and plan to use cogs as various sub-systems, video, keyboard driver, etc.
I'd like to not have any dependency on spin.
I can just roll my own, but if there is some agreed to "standard" I'd like to use it if possible.
Thanks,
C.W.
The general idea is to have objects that can be loaded and used by any language.
Did the group, as in forum members, ever come to any consensus on best practices?
I'm working on an 1802 emulator and plan to use cogs as various sub-systems, video, keyboard driver, etc.
I'd like to not have any dependency on spin.
I can just roll my own, but if there is some agreed to "standard" I'd like to use it if possible.
Thanks,
C.W.
Comments
The 'standard' is kind of dictated by the hardware, and basically you can't reference any hub locations (rdlong, wrlong) within the cog code unless you have explicitly passed that location in the startup parameters.
This means that some code in the obex won't work as it declares "myvariable" in the spin and then references "myvariable" in pasm. Of course, the compiler in this situation knows where "myvariable" is so the pasm will still work.
In a general sense, and it may be slightly politically incorrect to say this, all the code written by people employed by parallax seemed to work out of the box, and followed a standard of sending a list of variables at startup. It is the third party code that seemed to break the rule more often.
So I did the low hanging fruit - the keyboard driver, mouse driver, the standard video drivers.
The ones I didn't do were because they were quite hard to find those particular variables in the pasm. What you have to do is take a typical object, compile the pasm separately and then find where it fails, and then go through and put back in the variables passed at startup and see if it works.
There is another problem you may come across, and that is objects where there is a huge amount of supporting spin and only a bit of pasm. So if there is lots of cog space spare, and the spin looks like it could be translated easily into pasm, the temptation is to then start on that translation. Which can take a bit of time (but is well worth it).
The huge advantage of 'coglets' is you can put them all in eeprom or on an SD card and reclaim up to 14k of the 32k hub space.
Note, the support code (caller context) is currently written in SPIN, this would have to be rewritten in the host language.
It looks RossH's plan is still evolving and it may be more complex than what I need, looks promising for an "OS" based approach.
Not breaking the 'standard' of only accessing hub locations that are passed in PAR as mentioned by Dr_Acula is definately something to keep in mind.
kuroneko, your methods look like what I'm after. I may have a few questions for you after I spend a little more time reviewing your code.
Thanks again,
C.W.
I think that's above my paygrade. ;-)
For what I'm doing I'm looking for a "conventional" way to pass params to a cog during initialization and to not depend on any spin "helper" code.
At this point it looks like it will be having PAR point to a structure that holds the config info for each cog.
My plan is for the "initialization structure" to be temporary, once the cog loads and reads the info from the structure it will set the first long of the structure to $0000 to indicated that the memory is free for other use.
This of course means the first long will need to be some value that is never $0000 on entry.
The reason for the temporary structures is I want to free as much hub ram as possible to be used as "RAM" for the emulated system.
I seem to recall seeing someone use a similar idea of clearing the memory @PAR after initialization, there are so many ideas on the forum that it is very hard to keep them straight.
C.W.
Sphinx provided the base to separate the spin interface from the driver. My thread is here
http://forums.parallax.com/showthread.php?138251-A-Propeller-OS-that-can-run-on-multiple-hardware...&highlight=propeller+os
Hi C W
The current version is pretty much "final" as far as I'm concerned, and is already implemented in Catalina 3.5. I believe this model is actually pretty close to the "minimum" we will eventually need (although it may take a while yet before others reach the same conclusion ). Personally, I would have liked to see it go a bit further - but this is as much as I was willing to implement without help.
Ross.
I don't understand???
Ross sought comments before he implemented it in 3.5. Catalina 3.4 was using an older mechanism and Ross put it all out there for comment but there was really no consensus. The one Ross implemented seems to me to be middle ground with the option for more complexity. Currently I am implementing a simpler fixed allocation scheme following on from the work of mpark in sphinx. But I have had to dive in to use a plugin in catalina and have now seen the 3.4 implementation first hand. While it was a bit difficult for me to grasp, I think this is because I am not a C programmer and some of C concepts are really foreign to me.
Dave has implemented another complex scheme with different results.
Unfortunately, I still think it is early days for some to realise what is required. Some of us require an implementation now. So I am trying where possible to permit my method to be modified later easily, perhaps when consensus has been obtained. After all, CPM took some time to reach its standards, with a lot of intervening OSes out there first.
I think you are referring to Dr. Acula's comment.
C.W.
I'll try to go back this evening an read your documentation. I based my comment on the amount of discussion still going on in the thread.
C.W.
The last discussion in this thread was in Jan/Feb, which was probably the last time I spent any substantial amount of time in these forums. Or are you referring to some other thread?
Ross.
Ross,
I was referring to that thread, mostly post 21 and 22 where your reply to ersmith "There's some stuff in there that I will have to re-read and think about" lead me to think there may still be some changes.
C.W.
As an example, the the safe_spi.spin object from FSRW is split into two files. safe_spi_p.spin contains the start routine plus the PASM code. The original file was modified to contain only the Spin driver code. I should probably rename this to something else, such as safe_spi_d.spin. The boot program runs the start method in safe_spi_p.spin, and sets up the mailbox at a known location defined in sysdefs.spin. It also adds an entry in the process table so that Spinix knows which cog it is using, and that it is a PASM driver.
A better example of a self-contained driver is the PASM I2C driver. A program called loadi2c is used to load and unload the driver. It is self-contained, which means that it will check whether the I2C driver is already loaded, and start or stop a PASM cog with the driver. It also adds or removes the driver in the process list. That's the model that I plan on using for all Spinix drivers in the future.
My view is that a single driver standard is not necessary, or even practical. I see no problem with maintaining the status quo, and allowing various approaches to donig drivers. I think it promotes more creativity that way.
I agree that the drivers should remain as close to standard as possible, with a simple start spin method. If we can agree on a minimum base hub format and a couple of other OS type things we can pool our resources rather than the split atm. For example, if we combine the efforts, we only need 1 DIR module/program, etc. The one thing I see as necessary is the ability to change any program running to use a StdIn & StdOut simple interface so that the driver can be changed without the program knowing or caring. In this respect, I dont want it to be complex (not a *nix style file i/o scheme, but a character scheme) and for this reason I wanted a hub definition for this.
BTW: Your xtal concept is fantastic. I have modified Kyes driver to keep the xtal params (hub $0000-$0005) and not to load/clear above the hubtop (the allocation table). The one standard I really would like to see is that the allocation pointer to the top of usable hub be located at the long $7FFC (or whatever it will be in P2) i.e. the last hub sram long. I think we all agree that an alloc is required, a fixed cog table of some size, and some other os parameters.
I want space for my faster spin interpreter (the vector table) and it must be on a boundary (1KB on a 1KB boundary eg $7800-$7BFF ).
Anyway, I need to see what is happening in UPEW and will revisit this later in the week.
Postedit: Fixed some errors (in a rush to hear UPEW)
Fair point.I did in fact end up adopting one of Eric's suggestions (to do with memory allocation), but at the time I figured I would soon have Catalina 3.5 to release with a fully working implementation, and I haven't been back to the thread.
I hope to have Catalina 3.5 out "Real Soon Now"TM
Ross.
No way... ms must have beaten you to that TM.
I find it strange that ms hasn't prosecuted builders from using "windows" in houses!
First, don't define any protocols or standards for cog comms. That is up to the user to do.
Let's say I have a program and it has a single pasm section, and I want that pasm to be loaded from somewhere externally rather than included as part of the program.
You can put the pasm code on an sd card but the overhead of the SD code (maybe 1/4 to 1/3 of hub ram) kind of negates using SD. So I'm wondering about using an eeprom, and simply use a 64k instead of a 32k eeprom.
You may not need to always load the data to eeprom. What you need is a smart editor program and if you hit F10, it knows whether the pasm code was changed since last time you did a download, and it only sends the pasm part if there is a change. So most of the time the coglets are not updated.
It probably would need a tiny bootloader program on the prop. Send that first as part of the F10 download and it handles transferring any new coglets into eeprom. Then send the main program.
In the editor program, you might need a checkbox to select or deselect sending pasm separately. It might get a little complex if some pasm code you want included and some you want separate.
This could almost double the size of a spin program you could fit in a propeller.
But we need to keep the hub to a minimum. If you don't need FAT after you load up everything then that can be unloaded. Currently in my os (derived from your kyedos) the sd driver of kye's is not resident at all.
I have reserved a 2KB block of hub ram ($7800-$7FFF):
- a 1KB vector table for my faster spin interpreter (can be optional)
- a 512B sector buffer
- 8 x 32B cog table (undefined for now)
- 3 x 64B buffers (which can be joined as 1 x 192B buffer) eg for string buffers, etc
- leaving 64B (16 longs) for miscellaneous including the last long as a pointer to the start of protected hub sram (eg $7800)
- ..
Dave has a 24B cog table. I prefer this to be a block of longs to the power of 2 so that a simple shift of the cogid is all that is required to access your cog table. The first B should be a "type" flag indicating the cog is loaded, and what type of service (input, output, I/O, etc it provides). Something akin to Catalina seems perfect. The rest can depend on the useage, but we should define a couple of standard ones for keyboard, tv, vga, pc serial. If the next 11B is not required, then it should be used to store the cogs' filename it was loaded from. Now, presuming we allocated 32B (8 longs) per cog, that still leaves 20B (5 longs) free for buffer/whatever. The alternative is to trim to 16B (4 longs) per cog.I allocated a long to be used for: 1B with a bit for each cog to indicate it should remain loaded between each os/program load. 2B stores the screen size (8 bits columns, 7 bits row, and 1 bit for autoLF). 1B stores the clock mode.
1L stores the clockfreq; 1L stores the SD pins (1B per pin); 1L stores the serial pins (SI, SO, mode and the cogid); 1L stores the baud;
I used 4L to store rendezvous locations for: StdIn, StdOut, AuxIn, AuxOut. Perhaps with the Cog Tables, these could be reduced to a nibble each and just store the cog# to be used. I really liked the Sphinx idea of a single buffer for stdin and another single buffer for stdout, etc. But, this is a problem that means serial needs to internally buffer, or another buffer needs to be added to the mix (particularly for input). We have a few drivers (serial, tv, vga 1pin tv, keyboard, 1pin keyboard) already done with 1 character buffers courtesy of Sphinx. This method gives spinless objects. I would like to see some more discussion here.
1) Treating the prop as a general purpose computer running some type of OS with an SD card based file system.
2) More traditional "microcontroller type" applications running a fixed set of firmware to do one specific task.
Most of the ideas presented here seem to be more focused on case 1 than case 2.
My primary interest is case 2.
C.W.
Hi C.W.
Case 2 is a simple and practical approach as first presented here - resetting the propeller really complicates things and will be avoided. In the near future we will be adding a loader feature that put's COG code into upper 32KB EEPROM locations for loading in Propeller-GCC according to the object linking. This eliminates complexity required otherwise. That is, you always know before hand what will be loaded and can let the compiler and tools do the work for you with no overhead other than PAR mailbox info. The start-up I2C COG PASM buffer in the main program will be recycled for the code array space - the I2C PASM will read the code into the array and then we can do cognew with that. That is the general approach we will take.
Does that sound about right for your interest?
Thanks,
--Steve
re Cluso99, I am thinking simpler than a FAT. If you are going to put 7 coglets into high eeprom, you don't really need a FAT. Make them all the same length (2048 bytes) and put them one after the other in order. The compiler and the program then knows where they are.
I think this is the same as what jazzed is saying.
I'm also thinking you have a smart compiler that only reloads eeprom code that has changed. So when you first turn on your PC and start the compiler (GCC or whatever), maybe the first download it either sends all the coglets to eeprom, or if you want to be smart, reads them off the eeprom, compares with what might be downloaded, and only downloads if things have changed since before (this latter system avoids any unnecessary eeprom writes).
Then if the compiler program detected a change when you do an F10, it would send new code. Most of the time, coglets would be fairly stable code, eg keyboard, mouse, video driver, so they never need change. This could even speed up a download compared with the current proptool as you would not be sending all the pasm code each F10. I am thinking the way I often write programs, I start by grabbling the core bits out of the obex. Mouse. Keyboard. A video driver. So the first time you download all that, all the coglets go out to eeprom. But from that point on, as you write and rewrite your main program and do repeated F10 downloads to test it, no further coglets ever get written to eeprom. They are already there.
re
I'm not sure you need any rules about where things are in hub ram. That will change for each program you write, and you might not even need a buffer for keyboard or serial port (eg the GUI I'm writing with a touchscreen, where inputs and outputs are pictures, not text, and the pictures are too big to fit in hub anyway). So the only rule really is that all parameters must be passed to a cog via a par section at the beginning. That keeps it very simple and easy to explain.
It sounds like the GCC team is making great progress.
Steve,
It sounds very close. Will the COG code be able to be imported as a PASM binary?
By that I mean if I've already created a PASM binary using "normal" propeller assembly in the propeller tool will I be able to use that binary versus needing to code it using GCC or GAS.
It sounds like the only requirement of the COG PASM is that it gets all setup info via the address passed to PAR, is this correct.
Thanks,
C.W.
Perhaps so but what if you want to borrow such a "coglet" for use in language other than Spin? Arranging to get binary blobs of "firmware" started in a COG is hard enough with having to also start up a Spin interpreter to configure the thing and get it going.
To start
To stop
and to pass parameters and do clever things like pass different commands to a cog to run various routines, just designate one of the longs in the parameter list as a "command" long and change it and the cog code changes it to something else to say it has finished. It will all be very similar in different languages - you talk to a cog by changing a long in hub, and wait for the long to change to signal the routine has finished. All languages can write to and read longs in hub.
If the cog is a "start and run forever" cog, you just run the startup routine.
I suppose the key here is good documentation. For a particular coglet, you might define it has n longs as the parameter list, and they are in a fixed order. So you just need to explain that in a verbose way that is easy to understand.
re ctwardell
That is exactly the idea. You can compile these using the proptool. Save them as a .binary file. Or a .cog file, or whatever you want. Store them on an sd card, or in high eeprom, it does not matter. Once that binary file exists, any language can use it, Spin, C, Basic etc.
This is some code I was using for Catalina that is a skeleton pasm routine that compiles independently using the proptool and which passes a group of variables:
Yes, we support PASM, GAS, or GCC native COG C drivers. We have a few different ways of extracting PASM from the SPIN code.
Going the extra step to loading the COG code after boot just requires a loader enhancement and including the I2C driver.
Yes, that's correct.
Using SPIN methods to poke values into the PASM at SPIN run time before cognew should of course be avoided. Some obex code does this and would have to be ported. The Graphics.spin file is an example that had to be ported. Of course if you know the address of things to poke it doesn't matter except for portability and keeping up with whatever changes may happen.
I like the idea of a COG letting the main program know when it's ready, but it is not necessary to do that. When you have control over things at compile time, you can do whatever you like within reason.
Excellent. Do you have an ETA for when the loader will be available?
I'll move ahead making my objects having them get setup via PAR and can migrate to the new loader and GCC when it is ready.
Thanks,
C.W.