Toolchain for C with pasm
in Propeller 1
Hi,
short background info.
I'm implementing a one off device (so no production) that will count and monitor STEP and DIR pulses in stepper motor control system.
As I have a propeller kit from many years ago and have searched for a worthwhile project to use it in I'm starting with that.
The device will output step counts (and min pulse widths etc) in real realtime much like a milling machine DRO (Digital Read Out),
so the VGA output from the propeller is very attractive.
My thinking is that most of the stuff I will implement in C in one COG but the fast measurement and counting will be done with Propeller assembly language one STEP/DIR monitoring per COG for four steppers.
I would really, really prefer to do this on macOS, though I have Windows 7/10 running in this Mac as well as several Linux distros under VirtualBox.
I've got +40 years of programming all the usual suspects of languages and processors under my belt.
Ok that is the background.
Now to the specific questions.
I've tried both SimpleIDE and PropellerIDE with my eval board and I can get simple example code to compile, download and execute just fine.
I then tried mixing spin and C but this fails with the propeler-elf-objcopy not being able to find the .dat file for the spin code although it seems to compile just fine and I can see the file.
(Sorry can't copy the error message here because SimpleEdit does not support that apparently.)
Reading from the web I got discouraged that maybe this (mixing C with pasm/spin) is not supported by SimpleIDE ?
Moving on I searched for other tool chains.
I see flexgui but I did not find anyone using it on Mac, looks like it [getting it run on macOS] could be done but that would be distraction from the main goal.
I noted fastspin and it seems to support mixing C and pasm but the P1 support looked like it could be sketchy?
Also I read that fastspin C compiles to pasm which is great except does that not limit the code size to very small as it will have to fit into a single COG?
My application is not going to require a lot of code or math (simple integer math and displaying the results on the VGA display) but it seems unlikely that the code would fit in 496 longs.
So please let the crowd wisdom flourish!
wbr Kusti
short background info.
I'm implementing a one off device (so no production) that will count and monitor STEP and DIR pulses in stepper motor control system.
As I have a propeller kit from many years ago and have searched for a worthwhile project to use it in I'm starting with that.
The device will output step counts (and min pulse widths etc) in real realtime much like a milling machine DRO (Digital Read Out),
so the VGA output from the propeller is very attractive.
My thinking is that most of the stuff I will implement in C in one COG but the fast measurement and counting will be done with Propeller assembly language one STEP/DIR monitoring per COG for four steppers.
I would really, really prefer to do this on macOS, though I have Windows 7/10 running in this Mac as well as several Linux distros under VirtualBox.
I've got +40 years of programming all the usual suspects of languages and processors under my belt.
Ok that is the background.
Now to the specific questions.
I've tried both SimpleIDE and PropellerIDE with my eval board and I can get simple example code to compile, download and execute just fine.
I then tried mixing spin and C but this fails with the propeler-elf-objcopy not being able to find the .dat file for the spin code although it seems to compile just fine and I can see the file.
(Sorry can't copy the error message here because SimpleEdit does not support that apparently.)
Reading from the web I got discouraged that maybe this (mixing C with pasm/spin) is not supported by SimpleIDE ?
Moving on I searched for other tool chains.
I see flexgui but I did not find anyone using it on Mac, looks like it [getting it run on macOS] could be done but that would be distraction from the main goal.
I noted fastspin and it seems to support mixing C and pasm but the P1 support looked like it could be sketchy?
Also I read that fastspin C compiles to pasm which is great except does that not limit the code size to very small as it will have to fit into a single COG?
My application is not going to require a lot of code or math (simple integer math and displaying the results on the VGA display) but it seems unlikely that the code would fit in 496 longs.
So please let the crowd wisdom flourish!
wbr Kusti
Comments
I haven't really used the Prop1 myself so can't give actual guidance on tool use, sorry.
As for what you are trying to do with the Prop1, it sounds perfectly feasible.
Counting code can be done without assemble code. I have never had to use SPIN code.
/* * generate pulse every 2 milliseconds * 80 mhz / 80000 = 1 millisecond * 160000 = 2 milliseconds cut off */ void sidetest() { unsigned long T; low(0); FRQA = 1; CTRA = 0x10000000 | 0; while (1) { T = CNT; PHSA = ~80000; while ((CNT - T) < 160000); PHSA = 0; } }
Mike
Combining pasm objects is possible, but its not so easy depending on which compiler you use.
There is another solution that is much slower, but works seemlessly with pasm and that is spin. Spin is a simple high level language which has a builtin pasm interpreter in ROM. The interpreter loads into cog to run. Spin is simple to get used to if you’ve programmed in most higher level languages. If you are not speed constrained i suggest you try this. You won’t be disappointed.
If you need speed, then what you describe sounds easy to do in pasm. P1 assembler is NOT your typical assembler. Its quite easy to learn, and there are masses of help from this forum. Your code will be small and fast. There’s no interrupts to worry about.
Four cogs will handle the four step/dir with ease presuming its not extremely fast, with plenty of time to do basic integer arithmetic. Youll need a cog for the VGA.
One of Eric’s compilers will translate spin or C to pasm for you and you can then take this as a base and modify it to suit.
BTW there are inbuilt timers and counters in every cog so this will help your tasks.
Just ask as you go. Lots of help is here for you
Sorry, cannot help re mac.
fastspin itself is totally supported on P1, it's actually tested more on P1 than P2.
But the first step in all of this is to verify that you actually need to do the programming in assembly. Both PropGCC and flexspin produce pretty fast code, especially for small loops, so you may well be able to do all the capturing in C itself. You can start C functions up on other COGs using the "cogstart" function defined in propeller.h.
https://forums.parallax.com/discussion/157441/can-spi-in-simple-libraries-be-speeded-up/p1
Tom
There is also Catalina. Catalina is known to compile on a Mac, but I have not done so recently as I no longer have access to one. However, just to try it out you could install it on Windows or Linux. There are examples of integrating Spin and PASM with C in the demos\spinc folder which is included in the distribution.
Here is the README from that folder.
This README file covers two different but related topics: - USING SPIN WITH CATALINA - USING PASM WITH CATALINA There are several options for both of these. Each is described separately, in the sections below. 1. USING SPIN WITH CATALINA =========================== While it is not possible to directly call Spin methods from Catalina C or to call Catalina C functions from Spin, it is possible to run C and Spin programs concurrently, and even to communicate between the two. To do this, Catalina provides a utility (spinc) that converts a compiled Spin binary into an object that can be included in a C program, then initiated from C by calling a prefefined C function. The only limitation is that there must be sufficient free resources (i.e. cogs, pins and RAM) for the Spin program. When a Spin program is executed from Catalina, some Hub RAM, and at least one cog (i.e. the one used to run the Catalina kernel) will already be in use. In most cases, several cogs will already be in use (i.e. any cogs used by a plugin or driver). Any Spin program that assumes it has access to all 8 cogs, or to specific cogs, or which assumes it has access to all 32k, or which uses hardcoded memory addresses to access Hub RAM, will probably NOT be able to run under Catalina. However, all of these are rare cases, and most Spin programs will be able to be executed by Catalina. When executing Spin programs, Catalina can either use the Spin interpreter that is built into the Propeller ROM, or a modified spin interpreter can be provided - as long as it uses the same start-up parameters as the built-in interpreter. Although all Spin programs must execute in Hub RAM, Catalina can execute Spin programs even if the Catalina program itself is executing from XMM RAM. There are several demo programs provided, all of which can be built using the 'build_all' batch file provided. E.g: build_all C3 The example Catalina programs that run Spin programs which this command will build are as follows. Note - make sure to load and execute the "run_xxx.binary" programs (which are the Catalina programs) and not the "xxx.binary" programs (which are the original Spin programs!). run_flash.binary - demontrates a simple Spin program (flash.spin) which flashes a LED at the rate of 1Hz. Note that you will need to modify the LED_PIN number in the Spin program if you have a Propeller platform other than a C3. This program in interesting because as well as flashing the LED via the Spin program, the C program also prints "on!" or "off!" ont the screen - this is done by the C program reading the current value of the LED output from the Spin program's VAR array, and shows the basic mechanism for having Spin and C programs interacting. run_hello.binary - demonstrates a Spin program (hello.spin) that makes use of whatever HMI plugin has been loaded by Catalina. Also uses the file "Catalina_HMI.spin" which provides wrapper functions for all Catalina HMI functions. run_demo.binary - runs the demo program for Bagger's 40*30 TV driver. This demo is interesting because while the TV driver demo is running, Catalina is running using serial HMI drivers. Note that you will need to modify the pin numbers in the file TV_Text_Half_Height_Demo.spin if you are not using a C3. To see the serial output, use payload's interactive mode: payload -i run_demo run_kb_tv.binary - run a simple terminal program that accepts keystrokes and echoes them on the terminal, translating any numeric keys to strings. This is interesting because it is using standard OBEX drivers for HMI functions instead of the normal Catalina HMI functions. Note that you will need to modify the pin numbers in the file HMI.spin if you are not using a C3. run_tiny_hmi.binary - same as above, but uses the spinc -t option instead of -c, to produce a slightly smaller binary suitable only for compiling in TINY mode (the run_hmi.binary program could be compiled to run in SMALL or LARGE mode). Note that you will need to modify the pin numbers in the file TINY_HMI.spin if you are not using a C3. run_PNut.binary - same as run_flash.binary (above), but uses a custom Spin interpreter (actually, it uses the PNut interpreter). For details on the many new spinc options, execute the command spinc -h 2. USING PASM WITH CATALINA =========================== The Catalina compiler conforms to the ANSI C standard. This standard does not include the "asm" keyword (or function) that is sometimes added to C compilers to simplify the inclusion of code written in assembly language in a C program. However, it is still possible to incorporate PASM assembly language code into Catalina C programs. There are at least four different techniques that can be used to do this: 1. Write a target that loads the PASM program during initilization. 2. Convert the PASM program into a Catalina "plugin" and load it during initialization (as is done for the various HMI drivers, the floating point libraries, and the SD card and clock drivers). 3. Load the compiled binary version of a PASM program into a spare cog from within the C program (using the _coginit() function). 4. Write a subroutine in LMM PASM and call it from the C program in the same way that any C function is called. Each of these techniques is described in more detail below. LOAD THE PASM PROGRAM AT INITIALIZATION TIME ============================================ Each Catalina target is a normal SPIN program whose job is to establish the execution environment for the Catalina C program. However, this program can execute any PASM or SPIN code, including loading PASM programs into cogs to be run in parallel with the C program. Of course, the PASM program must not read or write to Hub RAM except under well defined circumstances - e.g. by only writing to an area of high RAM that Catalina reserves for this purpose. For an example of this technique, see the Catalina_Cogstore.spin program. This is a normal PASM program started by various Catalina targets to assist in decoding command-line arguments passed to the program. In this particular case the PASM program is stopped again once its work is completed - but it could be left running if necessary. An example of the latter are the various 'blackcat' targets (e.g. lmm_blackcat_Input.spin). This technique is not discussed any further in this document. CONVERT THE PASM PROGRAM INTO A PLUGIN ====================================== Plugins are a very versatile solution since they can interact with the Catalina C program at run time - but they can be complex to develop and can also be expensive in resources (since they cannot be loaded and unloaded on demand - they are expected to be loaded once and then remain running for the duration of the C program). However, plugins are the best solution when the PASM program and the C program are required to interact since there is a well-defined interface that supports communication between a C program and any plugins that have been loaded to support it. The Catalina Reference Manual described plugins in detail, and there are many examples provided in the Catalina target and target\input directories. Most standard Parallax drivers can be easily converted into plugins. This technique is not discussed any further in this document. LOADING A COMPILED PASM PROGRAM INTO A COG ========================================== Catalina provides a _coginit() function that works in a very similar manner to the corresponding SPIN or PASM 'coginit' operations - i.e. it is used to load a binary PASM program into a cog for execution. A tool to assist in converting a PASM binary into a form suitable for loading from C is provided - this tool is called 'spinc'. It is provided in both source and executable form. Thanks go to Steve Densen for the initial version of this this useful tool! An example of using the spinc tool is provided. It is called 'test_spinc'. To build it, use the 'build_all' batch file provided. E.g: build_all HYDRA +============================================================+ | NOTE: Even though you specify your platform when building, | | you may still need to modify the clock speed and pin | | numbers defined in the file flash_led.spin | +============================================================+ This batch file executes the following commands: a) compiles the flash_led.spin PASM program (to produce flash_led.binary): spinnaker -p -a flash_led.spin -b b) converts the flash_led binary to a C include file: spinc flash_led.binary > flash_led_array.h c) compiles a C program which loads the resulting binary using _coginit(): catalina -lc -I. test_spinc.c Examine each of the files mentioned above for more detail. To load and execute the resulting program, simply type: payload test_spinc WRITING AN LMM PASM FUNCTION THAT CAN BE CALLED FROM C ====================================================== A Catalina C program can call a PASM function directly. However, the PASM must be specially written to allow it to be executed by the LMM Kernel. "LMM" PASM (i.e. PASM intended to be executed by the LMM Kernel) is slightly different to "pure" PASM (i.e. PASM intended to be executed directly by a cog). While many LMM PASM instructions are identical to pure PASM, some pure PASM instructions cannot be executed within the kernel. Instead, they must be replaced by LMM equivalents known as "primitives". A good example of this is the PASM "jmp" instruction. If this instruction were executed within the LMM kernel, the program would jump to the corresponding location within the kernel itself, not the desired location in the PASM program. So Instead of using "jmp", a new LMM PASM "primitive" (called JMPA) is provided: In pure PASM, a jmp instruction might look as follows: loop jmp #loop ' loop forever In LMM PASM, this would have to be replace by the following: loop jmp #JMPA ' loop ... long @loop ' ... forever More information on the pure PASM instructions that need to be replaced by LMM PASM equivalents is given in the Catalina Reference Manual. A full working example of this technique is provided in this directory. It is called 'test_pasm'. To build it, use the 'build_all' batch file provided. E.g: build_all HYDRA +============================================================+ | NOTE: Even though you specify your platform when building, | | you may still need to modify the clock speed and pin | | numbers defined in the file flash_led.obj | +============================================================+ This batch file executes the following catalina command: catalina -lc test_pasm.c flash_led.obj The file flash_led.obj is the LMM PASM program - giving it the 'obj' extension tells catalina that it is not a C file that needs to be compiled, it is a PASM file that is ready to be bound. In fact, all catalina 'obj' files are LMM PASM programs and can be viewed with any text editor. Examine both flash_led.obj and test_pasm.c for more details. To load and execute the resulting program, simply type: payload test_pasm
Note that this README is a little out of date - since I wrote it, I have also added a PASM function to incorporate individual PASM instructions inline into a C program. This is described in the Reference Manual. Here is a short extract:
Using the PASM function Catalina defines a PASM function that can be used for including PASM instructions inline with C code. The prototype for this function (defined in propeller.h) is: extern void PASM(const char *code); However, there is no actual PASM function body. Instead, when it sees a call to this function, the compiler inserts the string literal given as argument (it cannot be a variable) into the assembly language output, to be compiled by the PASM compiler. For example, the following program will toggle the Propeller's P0 output every 500 milliseconds: #include <propeller.h> void main() { PASM("or dira, #1"); // set bit 0 as output while(1) { msleep(500); PASM("xor outa, #1"); // toggle bit 0 } }
You have lots of options!
Ross.
thanks all!
Seems like I do have a lot of options.
I think I will start with C-only.
If that is not fast enough I will look into the other options.
The goal of this device is to just to ensure (or expose) that the STEP and DIR signals meet their specifications. The step signal is max 100 kHz. What I need to ensure is that STEP is not too short. That the STEP signals never come too close together and that the time between two STEP pulses never changes too radically. Also that the time from DIR change to STEP is always higher than a limit.
My thinking is that a single cog will monitor the signals for change and queue them for an other cog to process. This should allow for pretty tight loop in the first cog. If I can figure how ensure that the hardware detects the situation in which a pulse changes back and forth between the first cog sampling the inputs then this should be fool proof and the actual speed is not critical as I expect that this device will (should) NOT detect any violations.
cheers Kusti
For Tk/Tcl installation, see: https://tkdocs.com/tutorial/install.html
Install XQuartz: https://www.xquartz.org
I execute flexgui with the following command, in the XQuartz terminal:
$ cd flexgui_pathname <== the file path to where our have placed flexgui's directory $ wish flexgui.tcl
I slightly mod flexgui's source code (src/gui.tcl), to add more-typical macOS file locations, to get the following option-settings:
# provide some default settings proc setShadowP1Defaults {} { global shadow global WINPREFIX // mod to use fastspin, installed @ "/opt/parallax/bin/" set shadow(compilecmd) "\"fastspin\" -l %O -L \"./spin2cpp/include\" \"%S\"" set shadow(runcmd) "$WINPREFIX \"proploader\" -Dbaudrate=115200 %P \"%B\" -r -t -k" } proc setShadowP2aDefaults {} { global shadow global WINPREFIX // mod to use fastspin & loadp2, installed @ "/opt/parallax/bin/" set shadow(compilecmd) "\"fastspin\" -2a -l %O -L \"./spin2cpp/include\" \"%S\"" set shadow(runcmd) "$WINPREFIX \"loadp2\" %P -l230400 -b230400 \"%B\" -t -k" } proc setShadowP2bDefaults {} { global shadow global WINPREFIX // mod to use fastspin& loadp2, installed @ "/opt/parallax/bin/" set shadow(compilecmd) "\"fastspin\" -2b -l %O -L \"./spin2cpp/include\" \"%S\"" set shadow(runcmd) "$WINPREFIX \"loadp2\" %P -l230400 -b230400 \"%B\" -t -k" }
Not sure if ersmith could "#ifdef macOS" to include these types of changes for other Mac users, but they do work for me :-)
dgately
No need to change the flexgui source code for that. Everything in the "Configure Commands..." dialog is persistent, so you can make those changes in that dialog once and then use them in future invocations. The contents are saved in the .flexgui.config file. If you keep this file across updates you'll keep your changes.