Where to begin?
pgbpsu
Posts: 460
I'm trying to convert a project from Spin/PASM into C and I can't figure out where to begin. Mike Green's SRAM driver (the one written for the Parallax memory card) is one of the objects in my project so I thought that seems like a good place to start. His code uses PASM to do the transfers back and forth to SRAM but has a spin interface for it. If I'm going to have any chance at converting this whole project to C, this is something I'll need to understand.
Where does one even begin?
I started with spin2cpp, but that fails with a syntax error on Mike's Demo code. spin2cpp might be a really nice way to deal with some of these issue, but I want to understand what's going on, not have some black box do it all for me.
I also found this example:
http://forums.parallax.com/showthread.php/138033-Converting-Spin-PASM-Object
Which is helpful, but it doesn't explain what's going on. It also doesn't address a specific need. Mike declares and populates some PASM variables before launching the cog. That doesn't show up in this example so I don't know how this should be handled.
I've worked around the edges of PropGCC a bit and I can get basic stuff to work, but now I need to figure out how to get an entire project ported over- one that includes SRAM, my own ADC PASM code, counters, etc.
Have I missed the obvious tutorials/examples?
Thanks,
Peter
Where does one even begin?
I started with spin2cpp, but that fails with a syntax error on Mike's Demo code. spin2cpp might be a really nice way to deal with some of these issue, but I want to understand what's going on, not have some black box do it all for me.
I also found this example:
http://forums.parallax.com/showthread.php/138033-Converting-Spin-PASM-Object
Which is helpful, but it doesn't explain what's going on. It also doesn't address a specific need. Mike declares and populates some PASM variables before launching the cog. That doesn't show up in this example so I don't know how this should be handled.
I've worked around the edges of PropGCC a bit and I can get basic stuff to work, but now I need to figure out how to get an entire project ported over- one that includes SRAM, my own ADC PASM code, counters, etc.
Have I missed the obvious tutorials/examples?
Thanks,
Peter
Comments
How much C or other programming experience do you have?
Have you gone through the Parallax Learn tutorials?
Learn Parallax PropellerC: http://learn.parallax.com/propeller-c
If you want to use GCC particularly, then jazzed has pointed you in the right direction.
But if just want to be able to use the Propeller Memory Card from C, then Catalina has support for that card "out of the box" - which means one less driver you will need to worry about.
What are the other PASM objects you need to convert? Maybe Catalina already supports some of those as well.
Ross.
I'm quite comfortable with SPIN/PASM and I've done a bit of non-embedded stuff in C.
Yes. They are fine for learning the basics, but I think/hope I'm past that. I'm looking for instructions/examples on how to build or reuse PASM code into a C project. So any links/threads you could point me to which discuss building a PASM routine for use with C would really help.
p
A thread about converting a Spin/PASM object to a C/PASM usable "library".
It's a good example for taking a standard OBEX object and replacing the SPIN wrapper with a C wrapper and using the same underlying PASM code.
C/C++ introduces the ability for inline assembly, which is a nice change from Spin. The PropGCC source has lots of examples of inline assembly (search for __asm__), PropWare has a new example in UART (I used PropGCC and libpropeller as examples to get me going) and libpropeller has some of the best examples of inline assembly (written by SRLM).
However, I know you were probably asking about an entire module - not something inlinable. That's where I started too - with SPI. Check out PropWare/spi.h, PropWare/spi.cpp, and PropWare/spi_as.S in the PropWare repository. If C++ isn't your thing, here's a link to v1.2 which is the last C release before I switched to C++.
I don't much care what version of C for the prop I use. I've been instructed by the boss to convert this project to C. I can't post the project, but I've used a few objects from the OBEX and written a few of my own. The project has several devices in it-
24-bit ADC MAX11040K - I have a PASM driver for this which I wrote which I believe will need to remain in PASM
PGA MAX9939 - this is simple SPI and I can recode this in C
a NewHaven OLED display that uses SPI - also something I can code in C
a couple of I2C devices including the PCF8574 I2C expander
Microchip 23LC1024 SRAM chip
a couple of serial devices - one for debugging, and one for a GPS unit
an SPI bus where the prop is the slave. This part is not in the current project so rather than converting existing code, I'll need to write this functionality in C/PASM from scratch.
I've been using Mike Green's code for the SRAM chip and that's what I thought I'd start translating/folding into the new project. Data come from the ADC into the prop then onward to the linux system. If the linux system isn't ready I buffer them on the SRAM so I don't lose any data. In reality, every sample comes into the prop from the ADC, then goes to the SRAM and sits there until the linux system requests them.
I doubt there are objects available for all these specific needs so I suspect I'll need to figure this out rather than use canned stuff. And I look forward to the challenge and learning something new. I'm just looking for an example, or "best practices" for incorporating PASM with C.
Thanks,
Peter
I think that thread is the one I was was following earlier- see OP. It's a really great start. Maybe my original question should have been more pointed. In Mike Green's (and much of my PASM) DAT section variables are loaded prior to be launched into the PASM cog. Can that be done with C? The examples I've found so far, like the one you linked to, do all the set-up via mailboxes. I know how I would/could do these things when using SPIN and PASM. I suspect SPIN/PASM interact differently than C/PASM but haven't figured out in what ways.
I've seen a number of your other examples and they've been very helpful. Especially this one:
http://forums.parallax.com/showthread.php/135924-Updated-pasm_toggle-example
Thanks for taking the time to post these things.
p
I'd love to help you out with the SPI slave side of things if you're willing to open-source that module. I haven't run across any implementation of the propeller as an SPI slave - I believe because of speed related issues.
David
Thanks for the links. I did find PropWare yesterday in my searching but couldn't locate the specific modules that would help. I'll have a look right now! Thanks,
p
Yep. I use C pre-processor macros at the top of the file for ease of reading and then reference those at the bottom, just like in PASM. See line 552 to 569 here.
EDIT
Make sure to build your assembly source with the "-xassembler-with-cpp" flag for preprocessor stuff to work correctly.
I've got no problem with the Slave side of things being open source and I'd love the help. Since that is a new part to this project I didn't figure on getting there until AFTER I'd converted the existing code so it could be a while until I'm ready or able to contribute there. For my particular project I figured on dedicating an entire cog to this. If I were to write it in SPIN/PASM I'd launch a cog in PASM to handle the read/writes and pass those data up to HUB for the top level program to deal with. I haven't figure out yet if that paradigm still works or how it's implemented using C.
To start with and to keep things simple I was going to limit the options for CPOL and CPHA. I'm not sure yet which would be easiest, probably 0,0.
p
I don't mind doing the extra work for it to allow any configuration. My original reason for starting PropWare was because I couldn't find any configurable SPI driver in C - I'm not about to create an unconfigurable one now :P. But speed is important - do you have a frequency you're aiming for? Are you okay with blocking calls or do you need a buffer so that when you call spi.read() it simply reads from the already existing buffer? If you need non-blocking calls and a buffer, my guess is you're looking at a dedicated cog for each of master and slave comm.
However, doing this with a pre-processor fixes the assignments before to compilation right?
Here's what an example from Mike's code:
In this case these PASM assignments aren't made until this section of code is called- at run time, right? I guess functionally there's no difference but conceptually those seem very different to me.
p
I've got a very specific application in mind and it's possible I'm not thinking of SPI master/slave as broadly as I should/could.
I don't think I've got enough resources to have a cog for each master and slave.
p
I agree, hard coding would be bad for everyone.
p
I don't believe a Propeller running at 80 MHz can do 2.5 MHz slave SPI communication. Master isn't a problem - I know the SD driver in PropGCC runs at 4 MHz (PropWare doesn't yet, but will as soon as I take the time to learn the counter modules better).
My C/C++ driver also sets pin assignments during runtime. The pre-processor runs before compilation and does a lot of copy/paste for me. For example, BYTE_2 is a macro for 0x00ff0000 - that's the Quickstart's LEDs. So I have
The pre-processor runs before anything else and turns that into
Therefore, testLEDs is initialized before run-time with the correct value.
A receive loop could look like this:
I can't remember the timing exactly on a waitxxx instruction. Your worst case scenario is going to be the first bit, when you have to wait on the chip select line. But assuming the worst for waitpne, that's only only <6 instructions. 100MHz would give you around 4.16 MHz with this. Granted - this is only a single data word. If you need to hold that speed for a variable number of data words, your max clock rate will quickly decrease.
I imagine only spitting out 8-bit words in 512 byte chunks so that should be minimal overhead.
Do you have a logic analyzer? If you do, it's worth scoping your master and seeing how much of a pause (if any) exists between data words. The datasheet for your master might also tell you. If there's an extra pause, that's good - that will allow you to a few extra instructions to shuffle the data variable away to your cog RAM buffer and reset it for the next data word.
But first, I have to say right now that a 2.0Mb/s SPI Slave is impossible with one COG ;-) The best you can do in conventional means is about 400Kb/s.
Now, on to the helpful part ....
New features for a PropellerGCC releases (currently release_1_0) are a bit strapped by the requirements of the Education program because Parallax is the primary customer.
We cooperate with Parallax to bring their product to market. A benefit of the decision to use PropellerGCC is that it is a modern implementation of C that any new BSCS graduate will be able to use without issue. Evidence of this is the in the library efforts of SwimDude99 and SRLM.
The open-source libraries SwimDude99 and SRLM provide for use with PropellerGCC are great values. Parallax provides the Learn libraries and I support that effort. I've also happily helped these other library efforts.
The default branch of PropellerGCC is more advanced supports things like multi-cog XMMC and that PMC board, but it is still in alpha_1_9 state. Over the summer we will be conducting a formal test program with the default branch. We are also a bit hamstrung by Propeller2 completion.
There are many things to learn here and many ways to do it. You can learn from all the C/C++ experts here including: me, Dave Hein, David Betz, ersmith, Heater, RossH, SRLM, SwimDude99, and others (not intentionally trying to exclude anyone ... sorry if I'm hurting anyone's feelings by not remembering to mention them).
In PropellerGCC we were asked to provide freedom for the user to load and use COGs as users wish. This means there is not pre-defined structured interface. It also means that an interface to a COG is your responsibility.
Generally speaking however, there are several projects that can be used to compare C code and Spin code. One example which you may find familiar is in using VGA. Back in 2009 I devised a method of extracting PASM code from a Spin file and creating an interface in C that was essentially the same as that for Spin by using a mailbox concept (the method built on my experience). The method is still in use today with PropellerGCC and Catalina although the mailbox structure is different.
So, let's compare SPIN VGA_Text and C VGA_Text ? It won't be done all in one post ....
I'll start a new topic for the discussion for Propeller-GCC there will be some differences for Catalina and RossH can cover those. Hopefully we can talk only about code there.
Here are some topics on the comparison:
1. Files and their roles.
2. The API (Application Program Interface)
3. Connecting C API code and Driver code.
4. PASM Drivers and alternatives.
I've started a thread for you here. I'll add more later today.
@SwimDude0614
Seems I got your user name wrong in multiple places. Sorry about that.
No problem. I'll probably get over it.
I appreciate you willingness to provide examples. I hope others find it useful. I'm very much a 'learn by example' person so working through these and other examples will surely help educate me. SwimDude has pointed me to some sophisticated but very useful examples. I think by studying them I might get to the heart of my problem.
I think my problems are two fold, I know the basics in C, but some of the more complicated stuff is still beyond me. So if C code isn't well commented it can take a long time for me to get through if ever. The second is I know how the things I want to do function in SPIN/PASM and I carry those notions over to C and want them to function the same way only with different syntax. And that's just not the case. Add to that the different uses/ways of setting up a project in C (I'm referring to memory models here) and things get further muddled.
I'll watch the other thread carefully and hopefully learn what I need. Again, thanks for doing this.
Peter