@evanh said:
Any idea why the existing symlinks don't work?
No. I was going to suggest that myself as a possible alternative. Perhaps a permissions problem? Does stty -F work on the symlinks?
Okay, found the error message in the source code and added a %s for path string and it prints this: /dev/serial/by-id/usb-Parallax_I instead of this /dev/serial/by-id/usb-Parallax_Inc_Propeller_P2-ES_EVAL_P23YOO42-if00-port0. So that explains the failed open.
Right and the array is this: char comports[PORT_COUNT][NAME_LENGTH+1]={ ...
Changing NAME_LENGTH = 80 fixes it without actually adding the entry to that string array! Now I wonder why even have the array ...
Looks like there have been a few changes since I last used it, and it has a few bugs - but with some manual tweaking here is the output from using it to convert the Spin2 code, plus my own manual re-coding of the PASM functions. The spin2cpp command I used was:
spin2cpp --ccode pllset.spin2
Note: I had to comment out all the PASM stuff, avoid the --main and --p2 command line options, manually change #include <propeller.h> to #include <propeller2.h> and manually add the main() function.
Also, I am not sure what the "compiled constants" clkfreq_ and clkmode_ represent - presumably this is a Flexspin C thing - so I just left them undefined - define them before use, or on the command line to make the code compile.
For example, it compiles with the following command - but HAS NOT BEEN TESTED AT ALL!!!
// automatically generated by spin3cpp v7.0.0-beta-- on Sun May 19 00:55:39 2024
// command line: build/spin2cpp --ccode pllset.spin2
// automatically generated by spin2cpp v7.0.0-beta-- on Sun May 19 00:55:39 2024
// command line: build/spin2cpp --ccode pllset.spin2
#ifndef pllset_Class_Defined__
#define pllset_Class_Defined__
#include <stdint.h>
typedef struct pllset {
char dummy__;
} pllset;
int32_t pllset_pllset(int32_t targetfreq);
int32_t pllset_muldiv65(int32_t mult1a, int32_t mult2a, int32_t divisora);
int32_t pllset_div33(int32_t dividend, int32_t divisor);
#endif
Here is pllset.c ...
/*
PLLSET() - An extension to CLKSET() that automatically finds optimal component multiplier and dividers
when setting the PLL. Based on Chip's posted routine:
https://forums.parallax.com/discussion/comment/1486815/#Comment_1486815
Takes one parameter: Desired sysclock frequency. Also uses the compiled constants: clkfreq_ and clkmode_.
Returns the clock frequency of XI pin. This may default to 20 MHz when no clock constants defined,
or -1 if clock mode wasn't changed due to out of range calculation.
MULDIV65() - A revision of MULDIV64() modified to round-to-nearest instead of rounding down.
DIV33() - Regular 32-bit integer divide but with round-to-nearest.
*/
#include <stdlib.h>
#define __SPIN2CPP__
#include <propeller2.h>
#include "pllset.h"
#if defined(__GNUC__)
#define INLINE__ static inline
#else
#define INLINE__ static
#ifndef __FLEXC__
#define waitcnt(n) _waitcnt(n)
#define coginit(id, code, par) _coginit((unsigned)(par)>>2, (unsigned)(code)>>2, id)
#define cognew(code, par) coginit(0x8, (code), (par))
#define cogstop(i) _cogstop(i)
#endif /* __FLEXC__ */
#ifdef __CATALINA__
#define _CNT CNT
#define _clkfreq _clockfreq()
#endif
#endif
INLINE__ int32_t Shr__(uint32_t a, uint32_t b) { return (a>>b); }
int32_t pllset_pllset(int32_t targetfreq)
{
int32_t mode, mult, divd, divp, post, Fpfd, Fvco, error, besterror, vcolimit;
int32_t xinfreq;
// keep %CC, force %SS, ditch the rest
mode = (clkmode_ & 0xf) | 0x3;
if ((Shr__(clkmode_, 24)) & 0x1) {
// compiled with PLL on
divd = ((Shr__(clkmode_, 18)) & 0x3f) + 1;
mult = ((Shr__(clkmode_, 8)) & 0x3ff) + 1;
divp = ((Shr__(clkmode_, 4)) + 1) & 0xf;
divp = ((divp) ? divp * 2 : 1);
xinfreq = pllset_muldiv65(divp * divd, clkfreq_, mult);
} else {
if ((Shr__(clkmode_, 2)) & 0x3) {
// compiled with PLL off
// clock pass-through
xinfreq = clkfreq_;
} else {
// unknown build mode
// default to 20 MHz crystal
xinfreq = 20000000;
// default to 15 pF loading
mode = 11;
}
}
// _errfreq at 1.0% of targetfreq
besterror = pllset_div33(targetfreq, 100);
vcolimit = targetfreq + besterror;
vcolimit = ((vcolimit < 201000000) ? 201000000 : vcolimit);
for(post = 0; post < 16; post++) {
divp = ((post) ? post * 2 : 1);
for(divd = 64; divd >= 1; --divd) {
Fpfd = pllset_div33(xinfreq, divd);
mult = pllset_muldiv65(divp * divd, targetfreq, xinfreq);
Fvco = pllset_muldiv65(xinfreq, mult, divd);
if ((((Fpfd >= 250000) && (mult <= 1024)) && (Fvco > 99000000)) && (Fvco <= vcolimit)) {
error = pllset_div33(Fvco, divp) - targetfreq;
if ((abs(error)) <= (abs(besterror))) {
// the last iteration at equality gets priority
besterror = error;
mode = ((((mode & 0xf) | (1 << 24)) | ((divd - 1) << 18)) | ((mult - 1) << 8)) | (((post - 1) & 0xf) << 4);
}
}
}
}
if ((Shr__(mode, 24)) & 0x1) {
// PLL-ON bit set when calculation is valid
// make the frequency change, also sets debug port baud as of Pnut v36
clkset(mode, targetfreq + besterror);
} else {
// failed, no change
xinfreq = -1;
}
return xinfreq;
}
int32_t pllset_muldiv65(int32_t mult1, int32_t mult2, int32_t divisor)
{
return PASM(
" qmul _PASM(mult1), _PASM(mult2)\n"
" mov r0, _PASM(divisor)\n"
" shr r0, #1\n"
" getqx _PASM(mult1) ' lower 32 bits of 64-bit intermediate\n"
" getqy _PASM(mult2) ' upper 32 bits of 64-bit intermediate\n"
" add _PASM(mult1), r0 wc ' round-to-nearest\n"
" addx _PASM(mult2), #0 ' round-to-nearest\n"
" setq _PASM(mult2)\n"
" qdiv _PASM(mult1), _PASM(divisor)\n"
" getqx r0\n"
);
}
int32_t pllset_div33(int32_t dividend, int32_t divisor)
{
return PASM(
" mov r0, _PASM(divisor)\n"
" shr r0, #1\n"
" add _PASM(dividend), r0 ' round-to-nearest\n"
" qdiv _PASM(dividend), _PASM(divisor)\n"
" getqx r0\n"
);
}
/* license */
/*
Terms of Use: MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
void main() {
}
Ross.
EDIT: I decided to just leave clkmode_ and clkfreq_ undefined. You need to define them to compile the program.
@RossH said:
Also, I am not sure what the "compiled constants" clkfreq_ and clkmode_ represent - presumably this is a Flexspin C thing - so I just defined them to be Catalina's_clockmode() and _clockfreq() functions to make the code compile.
Fair enough. I'll have to make backups at boot time to perform the equivalent.
Those symbols originated in Spin2. EDIT: Or maybe more specifically, Pasm2. They are documented in the section "Clock Setup" of the Spin2 Manual. They always exist based on what the compiler finally decides to be the compile time settings for clockmode and clockfreq. Eric later added them to his Spin2 compiler to match what Chip had done.
I think they were originally there for early pure Pasm2 programs that were devoid of any wrapper code. They provided a precomputed constant for an easy to use clock mode settings. They have since become an excellent Spin-time resource for managing the adjusting of clock frequency because they provide a reliable detail of the board level crystal info.
You'd have to ask the original author, but I think it was because on Windows the names of com ports are much simpler (i.e. COM1 .. COMn) whereas on Linux they are not, so it is not so easy to step through the set of all ports if you are auto-discovering.
@RossH said:
Note: I had to comment out all the PASM stuff, ...
What options do I have for adding Pasm as functions? I have a rather lot of streamer/FIFO cogexec code I want to try out.
Look at what I did to re-code the Spin2 inline PASM using Catalina's inline PASM syntax. It's pretty mechanical. The only thing that may not be obvious is that I replaced "result" with "r0" because in Catalina all PASM statements return the integer in register r0.
@RossH said:
Also, I am not sure what the "compiled constants" clkfreq_ and clkmode_ represent - presumably this is a Flexspin C thing - so I just defined them to be Catalina's_clockmode() and _clockfreq() functions to make the code compile.
Fair enough. I'll have to make backups at boot time to perform the equivalent.
I edited that code after I first posted it - the new version doesn't define them at all, so you can define them yourself.
Now that I know what they are, I know that I don't have C symbols defined for them, but I do have PASM symbols _CLOCKMODE and _CLOCKFREQ, so you could simply add a couple of C functions to access the PASM symbols from C ...
@RossH said:
Note: I had to comment out all the PASM stuff, ...
What options do I have for adding Pasm as functions? I have a rather lot of streamer/FIFO cogexec code I want to try out.
Cogexec is different. Catalina's Inline PASM currently always uses hubexec, not cogexec. To execute a significant chunk of code using cogexec, you have to turn it into a plugin which you then load into a cog manually whenever you need to execute it. For instance, see the new RANDOM example in the current release. That example is for a Propeller 1, but it would be similar for a Propeller 2.
I suppose I could write a C "wrapper" that would take an arbitrary chunk of PASM and do all the messy stuff like loading it into a cog automatically. I'll give that some thought. It would be be a nice facility to have.
@RossH said:
... do all the messy stuff like loading it into a cog automatically.
What about running in the C cog instead of starting a fresh cog? That loads into the executing cog, or is already sitting there, and then calls that. More like an actual function then.
PS: There is two options in Flexspin C. First option is the commonly used Fcache which is not so much a function call but a special attribute added to an inline __asm {} definition. This produces a temporary use of cogRAM where the code gets copied then jumped to as needed. The optimiser will automatically use this itself for small C loops.
The second option is at the function definition, an attribute specifies if the function is going to be resident to cogRAM or lutRAM instead of the default hubRAM. The attribute applies to the whole function, C code and Pasm equally. I've not used this much for fear of swamping cog space.
@RossH said:
... do all the messy stuff like loading it into a cog automatically.
What about running in the C cog instead of starting a fresh cog? That loads into the executing cog, or is already sitting there, and then calls that. More like an actual function then.
That would work in NATIVE mode. But a better option (since it could be supported in all modes) would be provide a function to load and execute a PASM function from the LUT. I already do that, in fact. But I load them only once - it happens automatically on startup. I could provide a means to load the function manually, but it would be better if it could automatically detect if it was loaded and did not load itself again if it was not necessary. Maybe offer both options - a wrapper function that first checks if the function it calls is loaded, loads it if necessary and then calls it, plus a manual load method and a wrapper function that calls it assuming it is already loaded.
@RossH said:
Now that I know what they are, I know that I don't have C symbols defined for them, but I do have PASM symbols
Nice, thanks, that's working perfect.
PS: I now see precomputed 180 MHz sysclock is using 360 MHz VCO frequency! Not the ideal setting for full temperature range. You're safe from crashing conditions, because DIVP=2, but the PLL will self-limit the VCO below 360 MHz if the die temperature gets high. Which means a non-set frequency.
You'd have to ask the original author, but I think it was because on Windows the names of com ports are much simpler (i.e. COM1 .. COMn) whereas on Linux they are not, so it is not so easy to step through the set of all ports if you are auto-discovering.
I've not investigated but have had no issue so far with chopping the Linux port array down to 8 with increased item size of 100.
#ifndef _WIN32 /* Linux */
#define PORT_COUNT 8
//#define NAME_LENGTH 32 // OS X has long names ("/dev/cu.usbserial.XXXXXXXX")
#define NAME_LENGTH 100 // Linux has even longer device specific names
int Cport[PORT_COUNT];
int error;
struct termios new_port_settings;
struct termios old_port_settings[PORT_COUNT];
char comports[PORT_COUNT][NAME_LENGTH+1]={
"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2","/dev/ttyS3",
"/dev/ttyUSB0","/dev/ttyUSB1","/dev/ttyUSB2","/dev/ttyUSB3"
};
@RossH said:
Now that I know what they are, I know that I don't have C symbols defined for them, but I do have PASM symbols
Nice, thanks, that's working perfect.
PS: I now see precomputed 180 MHz sysclock is using 360 MHz VCO frequency! Not the ideal setting for full temperature range. You're safe from crashing conditions, because DIVP=2, but the PLL will self-limit the VCO below 360 MHz if the die temperature gets high. Which means a non-set frequency.
If the defaults are not what you want, you can set all the clock options in the appropriate platform support file (e.g. P2CUSTOM.inc) or on the Catalina command line. Look up the Clock Configuration support section in Catalina's Reference Manual for the Propeller 2.
Oh, I see, the old time PLL components have to be set manually. No biggie, of course I had assumed one could just specify a compile time sysclock frequency, then the compiler computes the clock mode for you.
EDIT: Huh, it does compute a clock mode, and seems to do a good job, when -f <frequency> is specified in the compile command.
So it's just the P2CUSTOM.inc that needs a little love then.
@evanh said: Oh, I see, the old time PLL components have to be set manually. No biggie, of course I had assumed one could just specify a compile time sysclock frequency, then the compiler computes the clock mode for you.
EDIT: Huh, it does compute a clock mode, and seems to do a good job, when -f <frequency> is specified in the compile command.
So it's just the P2CUSTOM.inc that needs a little love then.
Yes, I usually set the frequency on the command line, which uses Chip's algorithm internally to calculate all the values. I don't often use on the defaults specified in the platform files, which I have never adjusted since I first wrote them. I'll do that in the next release.
Using cogexec in NATIVE mode turns out to be trivial if you use the LUT:
/*****************************************************************************
* Executing code from the LUT on a P2 *
* *
* This program demonstrates how inline PASM can be used to execute *
* code from the LUT using cogexec mode. *
* *
* This program only works in NATIVE mode on a Propeller 2. *
* *
* Compile this program for using a command like: *
* *
* catalina -p2 cogexec.c -y -lci *
* *
*****************************************************************************/
#include <propeller.h>
#include <stdio.h>
// make sure we are compiled using the correct processor and model
#if !defined(__CATALINA_P2) || !defined(__CATALINA_NATIVE)
#error PROGRAM MUST BE COMPILED IN NATIVE MODE ON THE PROPELLER 2
#endif
// lut_function - load and then call a cogexec function
int lut_function(int a, int b, int c, int d) {
return PASM(
" setq2 #(lut_end-lut_start-1)\n" // load code ...
" rdlong 0, ##@lut_start\n" // ... into LUT
" call #lut_start\n" // call the LUT code (cogexec)
" jmp #\\@pasm_exit\n" // jump to PASM exit (hubexec)
" org $200\n" // locate next code at start of LUT
"lut_start\n" // this function ...
" mov r0, _PASM(d)\n" // ...
" add r0, _PASM(c)\n" // ...
" add r0, _PASM(b)\n" // ... just sums ...
" add r0, _PASM(a)\n" // ...
" ret\n" // ... its four arguments
"lut_end\n" //
" orgh\n" // now back to hubexec
"pasm_exit\n"
);
}
void main(void) {
// delcare some local variables
int a = 1;
int b = 2;
int c = 3;
int d = 4;
printf("\nInline PASM can be executed using cogexec from the LUT\n\n");
printf("lut_function returns %d (expecting 10)\n", lut_function(a,b,c,d));
while(1);
}
Because it uses setq2, this version will only work in NATIVE mode. But in the next release I will add a primitive to do the LUT load in other modes, so it can also be done in TINY, COMPACT or XMM modes.
Oh, man! That line formatting is going to drive me up the wall. I do a lot of coding in pasm.
I gather the only way to keep the pasm formatting as normal is to make it a plugin? Which also always requires a fresh cog to run in? EDIT: Hang-on you've got a multitasking kernel feature. Can that run a plugin? Having lots of handler tasks would suit me fine I think.
@evanh said:
Oh, man! That line formatting is going to drive me up the wall. I do a lot of coding in pasm.
You don't need to do it the way I do it, but remember that it is a single C string being passed to the PASM function, so while you can pass it as a single multi-line string, it at least needs \n added as line terminations. Here is another way you could format the function:
// lut_function - load and then call a LUT function using cogexec
int lut_function(int a, int b, int c, int d) {
return PASM(" \n \
setq2 #(lut_end-lut_start-1) \n \
rdlong 0, ##@lut_start \n \
call #lut_start \n \
jmp #\\@pasm_exit \n \
\n \
org $200 \n \
lut_start \n \
mov r0, _PASM(d) \n \
add r0, _PASM(c) \n \
add r0, _PASM(b) \n \
add r0, _PASM(a) \n \
ret \n \
lut_end \n \
\n \
orgh \n \
pasm_exit \n \
");
}
It would be possible to write a script to handle the formatting job. I don't use inline PASM enough to make that a priority.
I gather the only way to keep the pasm formatting as normal is to make it a plugin? Which also always requires a fresh cog to run in? EDIT: Hang-on you've got a multitasking kernel feature. Can that run a plugin? Having lots of handler tasks would suit me fine I think.
Plugins are ordinary PASM programs, so the formatting is as normal PASM. Plugins are nothing special - Catalina is not even required to compile them - just a PASM assembler. Catalina just adds a standard way of managing and interacting with plugins (i.e. the Registry). Can't really see how multitasking would help here.
Is FAT file access to SD cards using any plugin?
I suspect there is a block driver level plugin. That's probably what I want to target replicating. There is lots of areas I've bounced around but SD cards, 4-bit SD protocol mode, is where I'm most active at right now. I'm hoping to apply what I've learned to both Flexspin and Catalina equally.
@evanh said:
Is FAT file access to SD cards using any plugin?
I suspect there is a block driver level plugin. That's probably what I want to target replicating. There is lots of areas I've bounced around but SD cards, 4-bit SD protocol mode, is where I'm most active at right now.
Yes - see the file target\p2\cogsd.t
This plugin implements both SD and CLOCK functions - but there is also a separate CLOCK plugin, so it really only needs to implement three functions - one to initialize the SD, one to read a sector, and one write a sector.
Just experimenting with a possible implementation of macros to simplify the definition of PASM functions to be executed from the LUT using cogexec ...
Here are the macros in a header file (save it in a file called lut.h)...
#ifndef LUT_H
#define LUT_H
/******************************************************************************
* *
* Macros to simplify the definition of inline PASM functions to be *
* loaded into the LUT and executed using cogexec. *
* *
* Use LUT_START and LUT_END to load and then call the function. *
* *
* Use LUT_BEGIN and LUT_END to load but not call the function, and then *
* use LUT_CALL to call the function. *
* *
* In either case, the function can be called (or called again) by using *
* LUT_CALL if it is still loaded *
* *
* For example ... *
* *
* int function_1() { *
* LUT_START("$200","function_1","1"); *
* PASM(" mov r0, #99"); *
* return LUT_END; *
* } *
* *
* ... or ... *
* *
* load_function_2() { *
* LUT_BEGIN("$200","function_2","2"); *
* PASM(" mov r0, #101"); *
* LUT_END; *
* } *
* *
* int function_2() { *
* return LUT_CALL("function_2"); *
* } *
* *
* *
******************************************************************************/
// load (if not loaded) and then call a LUT function
//
// ADDR - string address to put function (e.g. "$200")
// NAME - string name of function (e.g. "function")
// SIGN - string signature of function (e.g. "$DEADBEEF")
//
// Note: the ADDR should be a PASM constant >= $200 and
// the SIGN should be a PASM constant that is unique
// for all functions loaded at that address
//
#define LUT_START(ADDR, NAME, SIGN) \
PASM("\n \
rdlut r0,#"ADDR"-$200\n \
cmp r0,##"SIGN" wz\n \
if_z jmp #.lut_loaded\n \
setq2 #(.lut_end-.lut_start-1)\n \
rdlong "ADDR"-$200, ##@.lut_start\n \
.lut_loaded\n \
call #"NAME"\n \
jmp #\\@.pasm_exit\n \
org "ADDR"\n \
.lut_start\n \
long "SIGN"\n \
"NAME"\n")
// load (if not loaded) but do not call a LUT function
//
// ADDR - string address to put function (e.g. "$200")
// NAME - string name of function (e.g. "function")
// SIGN - string signature of function (e.g. "$DEADBEEF")
//
// Note: the ADDR should be a PASM constant >= $200 and
// the SIGN should be a PASM constant that is unique
// for all functions loaded at that address
//
#define LUT_BEGIN(ADDR, NAME, SIGN) \
PASM("\n \
rdlut r0,#"ADDR"-$200\n \
cmp r0,##"SIGN" wz\n \
if_z jmp #.lut_loaded\n \
setq2 #(.lut_end-.lut_start-1)\n \
rdlong "ADDR"-$200, ##@.lut_start\n \
.lut_loaded\n \
jmp #\\@.pasm_exit\n \
org " ADDR " \n \
.lut_start\n \
long " SIGN" \n \
" NAME "\n")
// terminate the definition of a LUT function, which
// was defined by either LUT_BEGIN or LUT_START
//
#define LUT_END \
PASM("\n \
ret\n \
.lut_end\n \
orgh\n \
.pasm_exit\n")
// call an already loaded LUT function, which was
// loaded by either LUT_BEGIN or LUT_START
//
// NAME - string name of LUT function (e.g. "function")
//
// Note: whatever function is currently loaded at the
// LUT address of this function will be called
//
#define LUT_CALL(NAME) \
PASM("\n \
call #" NAME "\n \
jmp #\\@.pasm_exit2\n \
orgh\n \
.pasm_exit2\n")
#endif
And here is an example of using them ...
/*****************************************************************************
* Executing code from the LUT on a P2 *
* *
* This program demonstrates how inline PASM can be used to execute *
* code from the LUT using cogexec mode. *
* *
* This program only works in NATIVE mode on a Propeller 2. *
* *
* Compile this program for using a command like: *
* *
* catalina -p2 lut.c -y -lci *
* *
*****************************************************************************/
#include <propeller.h>
#include <stdio.h>
#include "lut.h"
// make sure we are compiled using the correct model for our PASM code
#if !defined(__CATALINA_P2) || !defined(__CATALINA_NATIVE)
#error PROGRAM MUST BE COMPILED IN NATIVE MODE ON THE PROPELLER 2
#endif
// function_1 - load (if not already loaded)
// the LUT function and also call it
//
int function_1(int a, int b, int c, int d) {
LUT_START("$200","function_1","$DEADBEEF");
PASM(" \n \
mov r0, _PASM(d) \n \
add r0, _PASM(c) \n \
add r0, _PASM(b) \n \
add r0, _PASM(a) \n \
");
return LUT_END;
}
// load_function_2 - load (if not already loaded)
// the LUT function but do not call it
//
void load_function_2(int a, int b, int c, int d) {
LUT_BEGIN("$200", "function_2", "$FEEDFACE");
PASM(" \n \
mov r0, _PASM(d) \n \
add r0, _PASM(c) \n \
add r0, _PASM(b) \n \
add r0, _PASM(a) \n \
add r0, #100 ' differ from function_1! \n \
");
LUT_END;
}
// function_2 - call an already loaded LUT function
//
// Note that the parameter profile must match the
// function that was used to load the LUT function,
// but the names themselves do not need to match
//
int function_2(int d, int e, int f, int g) {
return LUT_CALL("function_2");
}
void main(void) {
int a, b, c, d; // these are dummy parameters and not strictly required
// load and execute function 1 ...
printf("function_1 returned %d (expected 10)\n", function_1(1,2,3,4));
// load function 2 but do not execute it ...
load_function_2(a, b, c, d);
// now execute function 2 ...
printf("function_2 returned %d (expected 110)\n", function_2(1,2,3,4));
// load function 2 but do not execute it ...
load_function_2(a, b, c, d);
// function 1 is no longer loaded, it will be re-load it and executed ...
printf("function_1 returned %d (expected 26)\n", function_1(5,6,7,8));
// load function 2 but do not execute it ...
load_function_2(a, b, c, d);
// execute the loaded function 2 twice! ...
printf("function_2 returned %d (expected 126)\n", function_2(5,6,7,8));
printf("function_2 returned %d (expected 166)\n", function_2(15,16,17,18));
// function 1 is no longer loaded, it will be re-load it and executed ...
printf("function_1 returned %d (expected 66)\n", function_1(15,16,17,18));
while(1);
}
These only work for NATIVE mode. I'll include something similar in the next release, also implemented for TINY, COMPACT and XMM SMALL and LARGE modes.
EDIT: Fixed an issue if the ADDR parameter was not "$200" and a bug in checking if the code was already loaded.
In my spare time I've continued to experiment with the Multi-Memory model for the Prop1 using the XEPROM model for the Primary and the CMM model for the Secondary. I'm using just a FLiP module by itself for this test, and this arrangement has worked well in the past.
But now that I've removed my previous 6.X version of Catalina and installed version 7.3 I've encountered a new error while attempting to compile the Secondary:Incompatible file size for layout 8.
I have no idea what that means. I've never encountered that error before. The compiler didn't complain about any syntax errors or anything else wrong with the code, it just printed this error and thus refused to output the binary file.
While you think that over, I will continue to examine both the Secondary and Primary code to see if I'm attempting to do something weird but so far I don't think so. I'll attach the two files to a later post once my analysis is complete and if the error remains.
UPDATE
Yup, it appears something is broken.
I went over to the previous thread entitled, The Great Printf() Challenge and extracted the code that pertains to the Secondary and placed it into the attached file called secondary.c, while also extracting the code that pertained to the Primary and placed it into the attached file called primary.c.
These two are known good files and compiled and worked perfectly under Catalina version 6.X.
Now, while compiling using Catalina 7.3 the Secondary gives me the Incompatible file size for layout 8 error. It also didn't print the normal output showing the code, const, init, and file sizes but oddly enough it did output a secondary.binary file.
I haven't attempted to compile the Primary yet because I need the correct output from the Secondary first...
But now that I've removed my previous 6.X version of Catalina and installed version 7.3 I've encountered a new error while attempting to compile the Secondary:Incompatible file size for layout 8.
Yup. There was an incorrect file size check in the new binstats program (introduced in 7.1) which was causing it to abort when it tried to print the statistics for Propeller 1 programs where the binary ended up larger than 32k (which was ok in this case because the binary was not intended to be run, it was being compiled to extract the blob to be used in a multi-model program - I hadn't remembered that case!).
Printing the file stats was definitely broken, but the binary itself was probably ok.
In any case, attached is a patch that should fix it. Now, when I compile and load your programs, I first get ...
Error - Change secondary Memory To 0x57D4
... which is probably to be expected, since the binary output will have changed very slightly with the 7.x releases.
When I do that, I get a whole bunch of lines like ...
Please apply the attached patch (which should work for 7.1, 7.2, 7.3 or 7.4) and let me know how it goes. If it is all good I will release the patch officially.
Ross.
EDIT: Official patch release now available here. I realized I should not have called it 7.3.1 since there was already a release 7.3.1, so the patch is now called 7.4.1 but it is applicable to 7.1,7.2,7.3 or 7.4
@RossH said:
Please apply the attached patch (which should work for 7.1, 7.2, 7.3 or 7.4) and let me know how it goes. If it is all good I will release the patch officially.
Thanks. Yes, that appears to have fixed the problem.
I will continue to experiment with compiling my code and let you know if I see any other strange stuff.
@RossH said:
Please apply the attached patch (which should work for 7.1, 7.2, 7.3 or 7.4) and let me know how it goes. If it is all good I will release the patch officially.
Thanks. Yes, that appears to have fixed the problem.
I will continue to experiment with compiling my code and let you know if I see any other strange stuff.
Ok, good. The official patch release is now available here. It is called 7.4.1 but is applicable to Catalina releases 7.1, 7.2, 7.3 or 7.4.
I should not have called the beta 7.3.1 since there was already a release 7.3.1, so the official patch is 7.4.1 - but it is the same code as the beta version you have apart from the version number.
I've just realized that the cogexec demo in this post is not restricted to executing PASM code from the LUT. It can also execute C code from the LUT.
For instance, the following PASM function is taken from the example in that post:
int function_1(int a, int b, int c, int d) {
LUT_START("$200","function_1","$DEADBEEF");
PASM(" \n \
mov r0, _PASM(d) \n \
add r0, _PASM(c) \n \
add r0, _PASM(b) \n \
add r0, _PASM(a) \n \
");
return LUT_END;
}
This function could also have been written as:
int function_1(int a, int b, int c, int d) {
int result;
LUT_START("$200","function_1","$DEADBEEF");
result = a + b + c + d;
LUT_END;
return result;
}
The result is the same. Any C code between LUT_START and LUT_END (in this case result = a + b + c + d;) is loaded into the LUT if it is not already loaded and executed from there (if it is already loaded, it will not be re-loaded, it will just be executed). You can even mix multiple PASM and C statements between the LUT_START and LUT_END macros.
There are (naturally) some restrictions on this. The main ones are:
Executing C code from the LUT only works for NATIVE programs (whereas for executing PASM from the LUT I now have a version of the macros that work for ANY program type - NATIVE, TINY, COMPACT, SMALL or LARGE).
The LUT C code must compile to 254 longs or less (to allow one long for the signature and one long for the 'ret' instruction which is now added automatically).
The LUT C code has to be leaf code - i.e. it cannot call any other C functions.
I will think about whether I can loosen these restrictions, but as it would probably require yet another code generator back end, it may not be worthwhile for C code that would still be limited to 254 longs (I use the other 256 LUT longs for library functions, but I could potentially reduce that to make more of the LUT available for C code - more thinking required!)
Comments
Okay, found the error message in the source code and added a %s for path string and it prints this:
/dev/serial/by-id/usb-Parallax_I
instead of this/dev/serial/by-id/usb-Parallax_Inc_Propeller_P2-ES_EVAL_P23YOO42-if00-port0
. So that explains the failed open.Right and the array is this:
char comports[PORT_COUNT][NAME_LENGTH+1]={
...Changing NAME_LENGTH = 80 fixes it without actually adding the entry to that string array! Now I wonder why even have the array ...
@evanh
I found spin2cpp - see here.
Looks like there have been a few changes since I last used it, and it has a few bugs - but with some manual tweaking here is the output from using it to convert the Spin2 code, plus my own manual re-coding of the PASM functions. The spin2cpp command I used was:
spin2cpp --ccode pllset.spin2
Note: I had to comment out all the PASM stuff, avoid the
--main
and--p2
command line options, manually change#include <propeller.h>
to#include <propeller2.h>
and manually add themain()
function.Also, I am not sure what the "compiled constants"
clkfreq_
andclkmode_
represent - presumably this is a Flexspin C thing - so I just left them undefined - define them before use, or on the command line to make the code compile.For example, it compiles with the following command - but HAS NOT BEEN TESTED AT ALL!!!
catalina -p2 pllset.c -lci -D clkmode_=0 -D clkfreq_=0
Here is pllset.h ...
Here is pllset.c ...
Ross.
EDIT: I decided to just leave
clkmode_
andclkfreq_
undefined. You need to define them to compile the program.Fair enough. I'll have to make backups at boot time to perform the equivalent.
Those symbols originated in Spin2. EDIT: Or maybe more specifically, Pasm2. They are documented in the section "Clock Setup" of the Spin2 Manual. They always exist based on what the compiler finally decides to be the compile time settings for clockmode and clockfreq. Eric later added them to his Spin2 compiler to match what Chip had done.
I think they were originally there for early pure Pasm2 programs that were devoid of any wrapper code. They provided a precomputed constant for an easy to use clock mode settings. They have since become an excellent Spin-time resource for managing the adjusting of clock frequency because they provide a reliable detail of the board level crystal info.
What options do I have for adding Pasm as functions? I have a rather lot of streamer/FIFO cogexec code I want to try out.
You'd have to ask the original author, but I think it was because on Windows the names of com ports are much simpler (i.e. COM1 .. COMn) whereas on Linux they are not, so it is not so easy to step through the set of all ports if you are auto-discovering.
Look at what I did to re-code the Spin2 inline PASM using Catalina's inline PASM syntax. It's pretty mechanical. The only thing that may not be obvious is that I replaced "result" with "r0" because in Catalina all PASM statements return the integer in register r0.
I edited that code after I first posted it - the new version doesn't define them at all, so you can define them yourself.
Now that I know what they are, I know that I don't have C symbols defined for them, but I do have PASM symbols _CLOCKMODE and _CLOCKFREQ, so you could simply add a couple of C functions to access the PASM symbols from C ...
Ross.
Cogexec is different. Catalina's Inline PASM currently always uses hubexec, not cogexec. To execute a significant chunk of code using cogexec, you have to turn it into a plugin which you then load into a cog manually whenever you need to execute it. For instance, see the new RANDOM example in the current release. That example is for a Propeller 1, but it would be similar for a Propeller 2.
I suppose I could write a C "wrapper" that would take an arbitrary chunk of PASM and do all the messy stuff like loading it into a cog automatically. I'll give that some thought. It would be be a nice facility to have.
Ross.
What about running in the C cog instead of starting a fresh cog? That loads into the executing cog, or is already sitting there, and then calls that. More like an actual function then.
PS: There is two options in Flexspin C. First option is the commonly used Fcache which is not so much a function call but a special attribute added to an inline __asm {} definition. This produces a temporary use of cogRAM where the code gets copied then jumped to as needed. The optimiser will automatically use this itself for small C loops.
The second option is at the function definition, an attribute specifies if the function is going to be resident to cogRAM or lutRAM instead of the default hubRAM. The attribute applies to the whole function, C code and Pasm equally. I've not used this much for fear of swamping cog space.
That would work in NATIVE mode. But a better option (since it could be supported in all modes) would be provide a function to load and execute a PASM function from the LUT. I already do that, in fact. But I load them only once - it happens automatically on startup. I could provide a means to load the function manually, but it would be better if it could automatically detect if it was loaded and did not load itself again if it was not necessary. Maybe offer both options - a wrapper function that first checks if the function it calls is loaded, loads it if necessary and then calls it, plus a manual load method and a wrapper function that calls it assuming it is already loaded.
I'll see what I can do.
Nice, thanks, that's working perfect.
PS: I now see precomputed 180 MHz sysclock is using 360 MHz VCO frequency! Not the ideal setting for full temperature range. You're safe from crashing conditions, because DIVP=2, but the PLL will self-limit the VCO below 360 MHz if the die temperature gets high. Which means a non-set frequency.
I've not investigated but have had no issue so far with chopping the Linux port array down to 8 with increased item size of 100.
If the defaults are not what you want, you can set all the clock options in the appropriate platform support file (e.g. P2CUSTOM.inc) or on the Catalina command line. Look up the Clock Configuration support section in Catalina's Reference Manual for the Propeller 2.
Oh, I see, the old time PLL components have to be set manually. No biggie, of course I had assumed one could just specify a compile time sysclock frequency, then the compiler computes the clock mode for you.
EDIT: Huh, it does compute a clock mode, and seems to do a good job, when
-f <frequency>
is specified in the compile command.So it's just the P2CUSTOM.inc that needs a little love then.
Yes, I usually set the frequency on the command line, which uses Chip's algorithm internally to calculate all the values. I don't often use on the defaults specified in the platform files, which I have never adjusted since I first wrote them. I'll do that in the next release.
@evanh
Using cogexec in NATIVE mode turns out to be trivial if you use the LUT:
Because it uses setq2, this version will only work in NATIVE mode. But in the next release I will add a primitive to do the LUT load in other modes, so it can also be done in TINY, COMPACT or XMM modes.
Ross.
Oh, man! That line formatting is going to drive me up the wall. I do a lot of coding in pasm.
I gather the only way to keep the pasm formatting as normal is to make it a plugin? Which also always requires a fresh cog to run in? EDIT: Hang-on you've got a multitasking kernel feature. Can that run a plugin? Having lots of handler tasks would suit me fine I think.
Another idea I guess is to have a tool that generates the inline formatting from a regular Pasm source file.
You don't need to do it the way I do it, but remember that it is a single C string being passed to the PASM function, so while you can pass it as a single multi-line string, it at least needs \n added as line terminations. Here is another way you could format the function:
It would be possible to write a script to handle the formatting job. I don't use inline PASM enough to make that a priority.
Plugins are ordinary PASM programs, so the formatting is as normal PASM. Plugins are nothing special - Catalina is not even required to compile them - just a PASM assembler. Catalina just adds a standard way of managing and interacting with plugins (i.e. the Registry). Can't really see how multitasking would help here.
Ross.
Is FAT file access to SD cards using any plugin?
I suspect there is a block driver level plugin. That's probably what I want to target replicating. There is lots of areas I've bounced around but SD cards, 4-bit SD protocol mode, is where I'm most active at right now. I'm hoping to apply what I've learned to both Flexspin and Catalina equally.
Yes - see the file target\p2\cogsd.t
This plugin implements both SD and CLOCK functions - but there is also a separate CLOCK plugin, so it really only needs to implement three functions - one to initialize the SD, one to read a sector, and one write a sector.
Ross.
Thanks, good read.
@evanh
Just experimenting with a possible implementation of macros to simplify the definition of PASM functions to be executed from the LUT using cogexec ...
Here are the macros in a header file (save it in a file called lut.h)...
And here is an example of using them ...
These only work for NATIVE mode. I'll include something similar in the next release, also implemented for TINY, COMPACT and XMM SMALL and LARGE modes.
EDIT: Fixed an issue if the ADDR parameter was not "$200" and a bug in checking if the code was already loaded.
Hi @RossH,
In my spare time I've continued to experiment with the Multi-Memory model for the Prop1 using the XEPROM model for the Primary and the CMM model for the Secondary. I'm using just a FLiP module by itself for this test, and this arrangement has worked well in the past.
But now that I've removed my previous 6.X version of Catalina and installed version 7.3 I've encountered a new error while attempting to compile the Secondary: Incompatible file size for layout 8.
I have no idea what that means. I've never encountered that error before. The compiler didn't complain about any syntax errors or anything else wrong with the code, it just printed this error and thus refused to output the binary file.
While you think that over, I will continue to examine both the Secondary and Primary code to see if I'm attempting to do something weird but so far I don't think so. I'll attach the two files to a later post once my analysis is complete and if the error remains.
UPDATE
Yup, it appears something is broken.
I went over to the previous thread entitled, The Great Printf() Challenge and extracted the code that pertains to the Secondary and placed it into the attached file called secondary.c, while also extracting the code that pertained to the Primary and placed it into the attached file called primary.c.
These two are known good files and compiled and worked perfectly under Catalina version 6.X.
Now, while compiling using Catalina 7.3 the Secondary gives me the Incompatible file size for layout 8 error. It also didn't print the normal output showing the code, const, init, and file sizes but oddly enough it did output a secondary.binary file.
I haven't attempted to compile the Primary yet because I need the correct output from the Secondary first...
Definitely something strange going on here...
I'll check it out and let you know.
Ross.
Yup. There was an incorrect file size check in the new binstats program (introduced in 7.1) which was causing it to abort when it tried to print the statistics for Propeller 1 programs where the binary ended up larger than 32k (which was ok in this case because the binary was not intended to be run, it was being compiled to extract the blob to be used in a multi-model program - I hadn't remembered that case!).
Printing the file stats was definitely broken, but the binary itself was probably ok.
In any case, attached is a patch that should fix it. Now, when I compile and load your programs, I first get ...
Error - Change secondary Memory To 0x57D4
... which is probably to be expected, since the binary output will have changed very slightly with the 7.x releases.
When I do that, I get a whole bunch of lines like ...
Which also looks like it is expected.
Please apply the attached patch (which should work for 7.1, 7.2, 7.3 or 7.4) and let me know how it goes. If it is all good I will release the patch officially.
Ross.
EDIT: Official patch release now available here. I realized I should not have called it 7.3.1 since there was already a release 7.3.1, so the patch is now called 7.4.1 but it is applicable to 7.1,7.2,7.3 or 7.4
Thanks. Yes, that appears to have fixed the problem.
I will continue to experiment with compiling my code and let you know if I see any other strange stuff.
Ok, good. The official patch release is now available here. It is called 7.4.1 but is applicable to Catalina releases 7.1, 7.2, 7.3 or 7.4.
I should not have called the beta 7.3.1 since there was already a release 7.3.1, so the official patch is 7.4.1 - but it is the same code as the beta version you have apart from the version number.
Ross.
Interesting ...
I've just realized that the cogexec demo in this post is not restricted to executing PASM code from the LUT. It can also execute C code from the LUT.
For instance, the following PASM function is taken from the example in that post:
This function could also have been written as:
The result is the same. Any C code between LUT_START and LUT_END (in this case
result = a + b + c + d;
) is loaded into the LUT if it is not already loaded and executed from there (if it is already loaded, it will not be re-loaded, it will just be executed). You can even mix multiple PASM and C statements between the LUT_START and LUT_END macros.There are (naturally) some restrictions on this. The main ones are:
I will think about whether I can loosen these restrictions, but as it would probably require yet another code generator back end, it may not be worthwhile for C code that would still be limited to 254 longs (I use the other 256 LUT longs for library functions, but I could potentially reduce that to make more of the LUT available for C code - more thinking required!)
Ross.
[]