Propeller C drivers important to YOU! *Uncle Sam point*
Daniel Harris
Posts: 207
Hey guys! In bringing a new language to the Propeller (C and C++ for those who have been away), it occurs to me that we will need to rewrite some of our drivers in C/C++ so that they can be used with other C/C++ programs.
So, quick question: what drivers do you guys think would be useful in a C/C++ driver library? If I get some good suggestions, we will likely need them written - and "prizes" would be issued for professionally done code.
On my needed driver list so far, I have the following:
- Asynchronous Serial (like Full Duplex Serial) (may already be done)
- Keyboard / Mouse
- TV / TV Text
- VGA / VGA Text
- I2C
- SPI
- Sigma - Delta ADC driver (this one may require some special sauce)
- Clock (a good timing driver)
- Servo Controller
- Stepper Motor Controller
- Real Random
- PID Controller
If there is something you would like to see, post it here so we can write it !
Comments
A driver and basic demo program for each of the Parallax peripheral devices/sensors: RTC, Compass, Gyro, PING, PIR, etc. Basically a device library to support Parallax devices with a general guide as to how to wrap the drivers up to use with the various memory models and/or multiple COGs.
I'd like to see the examples, demos and such in the Prop Tool library ported over.
Other than that, my needs are pretty simple.
You can probably write anything if no OS gets in the way of accessing the hardware, just how fast do you need it and how much space do you have to store/swap code and data in and out of.......
Only hardware specific libraries should be tackled.
That's probably one of the harder ones to do because it is necessarily threaded.
It would be interesting to see a TV or VGA driver written in C. Most of the other stuff looks like a cake walk.
We already have F32 and 64 bit doubles. We also have SD card file-system examples.
"Interesting"? I reckon "impossible" would be more accurate - at least as far as VGA is concerned
Ross.
*pricks up ears*
That is a good list. For VGA I'd like to see Kye's 160x120 pixel driver. For TV - the highest res possible (limited by hub ram) for 86 colors. All the different size text drivers we have in Spin. I'd love to see Tim Moore's 4 serial driver in one cog.
I've already written ten of these for C - essentially you take the Spin and Pasm, split into two parts, pass all variables via a common array, compile the pasm separately, and then translate the Spin to C. Objects like the Mouse were very easy. Objects like Kye's SD driver would be quite daunting.
I'm very keen to see objects where the pasm and C code are separate, so you can load the cogs from an SD card without using up hub ram, and then you can also reload cogs over and over. I've done this for Catalina - is this possible for C++?
Ideally all the PASM parts would be rewritten so that they can be assembled with the GCC assembler, gas. Yes you can assemble a single module into a stand alone binary blob that could then be read from SD or wherever by your program and started in COG. Probably use the objcopy tool to create a binary blob from an assembled object file.
Hmmm. You can do this with any of the available C compilers right now - possibly even with Zog. But this doesn't really seem to fit the original intent of rewriting the driver in C.
How about it, Daniel - would this kind of thing qualify? If so, we could knock half a dozen or so off your list almost immediately. And how much more "professional" could you get than using Chip's original VGA driver code? Although it might be difficult figuring out whether the prize should go to the person who submits the driver, or to Chip himself!
Ross.
But without wanting to go on about it too much, I do feel fairly passionately about the issue of how data is passed between the cog and the higher level part of a driver. I've been avoiding saying the following but I think I do need to say this now - I think some of the objects in the obex have not been written as well as they could be. Specifically, if you go and look at the original drivers written by Chip, they all seem to conform to a standard - you declare a list of variables in a particular order (or an array) and then all data is passed to and from the cog via this list. This means that all the passed data is grouped together. But the structure of spin/pasm lets you also write code where you declare a variable in the spin part and because it is all compiled together, the cog part knows where that variable is. If you look at a memory map of the hub, you can have longs scattered all over the place. The problem with this is that it makes it hard to reload cogs, and it makes it hard to then reuse most of the hub for, say, a screen buffer, as you will overwrite these variables.
With a new set of drivers, I see a great opportunity to write the code in a better way. Or, more specifically, to follow Chip's protocol.
I guess this is the reason why I only translated ten objects to C. These were the easy ones, because they conformed to Chip's protocol. I gave up because I got stumped with decoding some objects. In the interests of diplomacy I'd rather not mention any specific examples except to say that more than one author has written code in this way. Probably because before we had SD cards and external memory, it did not matter. To decode these, what you need to do is go through the spin code, get a list of every single variable, and then go through the pasm code and get a list of every single variable and then see if there are variables in both lists.
I guess if there are prizes being awarded, maybe there should be better prizes for the harder objects because some are going to be a lot harder than others!
As a general statement, I think it is absolutely brilliant that these C objects are being written. They will be hard to write, but we only have to do it once, and for the end user, the code is going to be much easier to use once all the libraries exist.
Although it is a little off-topic for this particular thread, I agree 100%. A common interface to drivers would make the Prop soooo much easier to use - especially for newbies. And there is no reason why this needs to be language-specific. We should be able to mix and match no matter what language we are using.
Catalina has one mechanism, but I'm perfectly happy to admit that it may not be the best alternative for all driver types and all languages. What about another initiative (perhaps separate to this one) to come up with a language-independent interface between drivers that all Propeller language developers could adopt?
Ross.
I agree. Many existing objects have "linkage" between the PASM part and the variables used by their Spin wrapper. Making it hard to use them in other languages and hard to recycle the precious HUB space the PASM lived in before being loaded to COG.
This has been discussed at length in the past, for example there is Bill Henning and his "mailboxes".
I did at one point suggest that all "Gold Standard" objects should be written in a way that they can be used from any language and loaded from any media. I don't think many people saw the "linkage" problem at the time. I had already met it when trying to use PASM driver code in Zog.
I see no reason why the request for C drivers should not include the use of assembler modules, provided they can be assembled with the GCC assembler. Whilst I did manage to get a fullduplex serial driver written in C and running in COG working I think many drivers will not be amenable to this for size and/or speed reasons.
I seriously think that all new drivers for use with GCC should have a C++ wrapper interface.
Using C++ classes would make it easier and neater to have multiple instances of the same driver object in a program.
Using such drivers would be as simple as using objects in Spin now.
Using C++ classes to create objects in the style of Spin objects is quite easy and does NOT use any more code space or execution time than achieving the same effect in straight C.
Works for the Arduino guys, don't see why it should not work for the Propeller as well.
Heater,
GCC is only just beginning to catch up to where others - such as you and Bill - have been for a while. However, I thought Bill's mailbox was designed specifically for his VMCOG. Catalina certainly uses it for that, and I presume both Zog and GCC do so as well - but it does not seem "generic" enough for much else. Isn't it essentially designed to be used for reading and writing bytes, words or long values?. Most drivers would need a broader range of fundamental operations than that. I suppoose you could build on the basic operations it provides, but you would at least need to add some kind of synchronization or handshaking primitives, so this would seem to be a bit inefficient. Please correct me if I've misunderstood it!
Ross.
My impression is that perhaps one should not try to standardize the layout of such shared memory mail boxes. Isn't enough to make the "rule" that all communication between COG and other code is based off of a given pointer to HUB memory. That is to say there is no "linkage" between the code compiled for a COG and any other code/data.
How that share HUB area is used is up to the designer, may have commands and responses, may have buffers to exchange data through, may have a screen buffer for graphics, whatever.
Well, there is the master cog method. Master writes states to hub, others read them and act as needed.
There is the interlinked cog method too. One cog starts by performing it's action, writing a state when done, switching to read mode to cycle again. Other cogs start in read mode, waiting to see a state they can act on, they do it, writing their state info when done.
A variation on that is to be reading, then when a state is seen, start acting, but write the state bit right away, so that other cogs may continue a sequence of actions. I don't like that one much, and generally found it unnecessary. Perhaps it is.
For things like library cogs, the second method works well. Cog starts up, begins reading it's command state. Other cogs, running whatever SPIN, C, PASM, desire some action needed from the library cog, so they write that action to a shared hub memory location, after having written the arguments needed to define and direct the action. The cog requesting services then waits for the library cog to indicate it's done. This tends to be one cog to another, unless there are multiple action "mailboxes", or things can be put into a queue or list. I've not written any code that does that; namely accept multiple commands from multiple cogs. Anyone else?
How would those break into primitives?
For the library type cog, I've seen and used a defined number of arguments, and optionally, left the state of those preserved so that multiple actions can avoid having to always write shared arguments. Seems to me that could break down into something fairly standard, as in there is always a state memory location, followed by X number of argument locations.
In any case where those things are happening in the COG, they often end up needing to be lean so that most of the COG is doing stuff as opposed to communicating about the stuff to be done.
Another standard element could be the jump table. Frankly, I need to look at how we do jump tables. And that brings me to the last bit:
For all but the simplest case of one cog writing states that others respond to, it's been helpful to see a model first, so planning can be done. It's really easy to over communicate, or end up in some out of control condition.
Maybe standardization could simply be a few nice models or templates, written nicely, debugged, ready to drop code into? Break these and potentially a few other methods down to simplest case, blinking lights or some such. People could grab those, instead of parsing them out of existing cogs like probably most of us did, if we didn't just cook our own at the time of need.
Document the model, referencing comments in it, so the user can grab it, run it, tinker with it to understand, then drop their code into it, or it into their code, knowing it's ready to go, or at the least done "the standard way", or "the linkable way".
Buffers seem to me like they just need a size, start address and alignment option, if the code happens to require alignment. (word or long hubops used instead of bytes, for example) Maybe model those too.
It seems to mean that all aspects of the drivers are to be written in C/C++. Why else offer any incentives?
We have many examples of C-PASM interfaces already - I produced the first one long ago.
So is this about duplicating what has already been done or demonstrating what is possible with C in Propeller?
Peter
My post above was on standardization and communicating that. Curious to see what people mean, or what's possible there, and or what's been done already that could be packaged / published out to gain mind-share. Primitives were mentioned. That's interesting, but I don't see it, which is why I posted up basic use cases. Could be another thread. Maybe should be, but I am interested in how that would look, and whether or not it should...
As for a demo of what could be done with C, writing some other drivers makes sense, but *should* it be done? That's a hard one for me personally. The could be done part is a nice demo, and that has it's merits. But, *should* it be done? Maybe?
Take the TV / VGA driver. If it is done in C, and I think both could be done, the overall capability would be basic compared to what PASM drivers do. Taking Chip's example (a good one too), TV.spin isn't something I would want to put into C. I don't think C is fast enough for it to make sense. But, it could be done, at the expense of advanced / or useful tile / color options, perhaps rendering a simple bitmap, etc... So there is that. Now, the graphics.spin cog perhaps *should* be done, because the number of functions and flexibility of those functions is constrained by the cog code limit. Expanding that could be very cool, at the expense of some speed, and perhaps not that much speed lost. And that stuff could live in external memory, if raw speed isn't such a big deal, and often it just isn't. Wins all around on that.
I've started that with some 8bpp library code lifted from old DOS vga libraries, and some code of my own creation.
So then, if we do it all C, we would very likely end up with more graphics primitive options, because the graphics library could be larger and perform well still, but those graphics would be rendered into a TV / VGA driver that offers less overall capability on screen. Seems to me, splitting the middle makes a lot of sense. Leave the PASM in place for the TV / VGA driver, but use C for support functions.
This is why I started with library code instead of a TV driver. It's hard to justify the time to build up TV / VGA drivers, when the end product really is just to get a C version, at the expense of options / colors / etc...
We don't have 8bpp library code yet, and we don't have library code for other bit depths either, or tiles, etc...
Looking back at what people have done, the TV / VGA.spin + graphics.spin is popular because it offers a lot, meaning users can get stuff done without having to be driver authors. Extending that to C, where larger screen buffers and such can make sense is new capability, and it doesn't need a COG to perform well either, depending on what a developer wants to / needs to do.
Simply re-authoring a PASM display driver really isn't *new* in the sense of what one gets from the display, and will very highly likely be a subset of what is possible even with our oldest PASM drivers.
For reference, Eric Ball did a SPIN TV driver, just because. It works, but not for much! C won't be like that, but it won't be PASM either, which is where the sweet spot is as far as display goes. IMHO, similar arguments can be made for sound too.
So the other part of the thread is what *should* be done, and I'm reading with interest to see what people think.
Edit: Guess I'm saying display drivers don't make a lot of sense, given we can wrap around PASM. Many of the other items do though.
I would also strongly recommend creating a HAL that conforms to some analogue of BSD/POSIX philosophy. As a customer who is currently implementing Parallax chips in commercial products, my single greatest frustration is Parallax's lack of a standard driver model, not just lack of drivers. There is an elegance to having something such as this i2c interface which fits within an abstract driver hierarchy. The fact that Unix/Linux has this abstraction guarantees a certain soundness to the underlying system, because the parts have to fit together logically and with clean names.
As it stands, when developing code in SPIN (and I am not trying to be unnecessarily critical) we currently have to go get a collection of a bunch of ad-hoc, unrelated drivers named like PXSSD_Mmc32SomethingDriverObjectTwo from OBEX and tie them all together. Usually they are not maintained or are used for special use cases without sufficient generality. There's lots of duplicate code and lots of trial and error to see if a driver is sufficiently general or bug-free. I spent a bit of time trying to sift through OBEX to create a standard library of sorts. I gave up after realizing just how incompatible most objects were and that a full rewrite would probably be easier and more effective.
I don't mean to be critical of the community, and I understand the assortment of drivers may be useful to hobbyists and students, but for professional use case a standard "kernel" (if you will) is something extremely desirable. A tree with well-thought out abstract layers (even if they're just for convention and optimization shortcuts these layers for performance) would guarantee rigidity and stability of the driver model.
My second suggestion is that if a driver does not fit nicely into the HAL, it shouldn't even be considered being worked on by Parallax. Period. If a "driver" can't nicely integrate with a well designed HAL (BSD and Linux are great examples of how to do this well), it isn't actually a driver--it's some form of app or firmware wishlist. This means that these apps or firmware can be implemented much like OBEX contributors do today. Those "drivers" would be leaves not critical to the overall model tree, and could be implemented on an ad-hoc basis with competing modules or implementations.
Without starting a technology war, I would just comment that this is why people like projects like Arduino/Processing/Wiring. I think it has less to do with C++ (as opposed to C) than a lot of the people on this forum think. A lot of people such as myself grew up on Linux and appreciate the elegance of writing code in a platform with lots of nice, well-behaved standards. Arduino provides something like that, and I hope that this prop-gcc is going in that direction. I appreciate the architecture of the Propeller and opportunities it can afford my design team, and would like to see that software elegance along with the elegance of the Propeller architecture.
That would be a dream
Here is the Propeller-GCC standard library interface driver definition:
http://propgcc.googlecode.com/hg/doc/Library.html#WritingDrivers
Here is the current driver library:
http://code.google.com/p/propgcc/source/browse/#hg%2Flib%2Fdrivers
We have tried very hard to meet a standard driver requirement. We don't implement ioctl though.
Welcome to the forum.
What is a "driver"?
In the OS world a driver is that piece of code that connects a OS standard application interface, a character device in linux for example, to the actual hardware that is that device, a particular UART chip say. There will be a different driver for each different kind of UART chip.
BUT here in Propeller land we have no peripherals, what we call a "driver" is the often no such thing. It is rather an implementation of the missing device in software.
Take Full Duplex Serial as an examle. The PASM part IS the UART. The Spin wrapper around it is perhaps the "driver", but as we have no OS or standard for such things that is perhaps a misnomer.
I disagree with Jazzed, I see no reason why the "device" cannot be written in assembler and the "driver" written in C.
I never said the "device" can not be written in PASM. A "device" can be written in C and compiled to PASM.
Hmm.... that terminology could get very confusing very quickly. I see what you mean about the "device" because it's an emulated version of what would be dedicated hardware. But everyone I know uses the reverse definitions of "device" and "driver." I've heard "driver" as the kernel module, say "ath" for an atheros wifi card. The "device" is basically a file descriptor that instantiates that driver with a specific piece of hardware (ath0) but it is the higher level of the two when writing software. You can also have physical devices and logical devices. I would say that the lower level definition of device is unnecessary because it's assumed the driver creates it on the Prop in every situation, or it exists as a peripheral (sd card, spi ram, sensor, etc.)
Without an OS, these differences may be purely academic. Depending on the definition, one is just a wrapper call to the other.
Referring back to my earlier post, I am wanting a device model on top of the driver layer to be constructed and think it might be profitable for the community to do so in standard way. You could definitely have PASM drivers, but you could also have a hierarchy of struct objects representing the devices which pass their arguments directly through to the _Driver file descriptor. I only bring this up because I will probably be implementing such a system anyways, but would prefer it if people more knowledgeable than I found utility with this system and could develop it so as to maximize benefit to the community.
What I'm thinking of is creating the structures that would be used in a limited version of FreeBSD's devd. It wouldn't need to do anything as sophisticated as notification or pluggable events, but I'm wanting a clean set of driver abstractions like a microkernel. A modular kernel would coordinate efforts to provide a set of optimal drivers, code reuse, stability, etc.
My rationale for this is there are all sorts of vendor specific drivers for common software subsystems. Like, if you have an A/D driver, it would be nice to have an abstraction to the A/D driver callable in code instead of something like MCP3208 or something. I can see this huge proliferation of drivers not sharing any common implementation details, and I see this as a Bad Thing (TM). We'd have what we have now which is a whole lot of vendor specific i2c, spi, a/d, d/a drivers in the OBEX.
If there's something really wrong with my thinking, I would like to know that too
"device" can be a real piece of hardware, as in the old days an actual UART chip which needs a "driver". Or an abstract device like a modern day com port which is actulally a USB "thing".
Anyway, this is all out of scope. We have a compiler in GCC not an operating system.
Yeah, yeah, I know C standards require a standard in and standard out "device". But that is it.