ok, compiling on my IDE I get this. I don't know what to do with the batch program and it probably has to be run from a particular directory which has a particular include in it, which may or may not be the same as your include, which may introduce other variables. But here is the error of that C code as it is, compiled with catalina -lcx -D PLUGIN -x5 -M 256k -D DRACBLADE -D HIRES_VGA NEW.C
===================
SETTING UP CATALINA
===================
CATALINA_DEFINE = [default]
CATALINA_INCLUDE = [default]
CATALINA_LIBRARY = [default]
CATALINA_TARGET = [default]
CATALINA_LCCOPT = [default]
CATALINA_TEMPDIR = [default]
LCCDIR = [default]
Catalina Compiler 3.0
cpp: NEW.C:8 Could not find include file "plugin_array.h"
cpp: NEW.C:39 No newline at end of file
Press any key to continue . . .
But of course we are going backwards a little here, because in getting to where I have already got, I had to pull apart the three #includes to find code that was critical to getting the one bit of code working - which is the write of a zero to 7fd4.
A write to 7fcc does not work on my board. But of course I have hardcoded some things to try to get as much as possible out of any #includes so it is clearer what is going on. EG DUM is 8, and ANY_COG is 7
Can we eliminate any variables by thinking about code with no #includes?
Hi Dr_A,
I'll generate you a version you can compile and run from your IDE.
Now that I can test pasm code quickly I tried about 20 combinations and while nothing works, I have learned quite a bit. I did a debug routine and you are correct
No - it writes it to address 0x7ff0, since this is what t1 contains.
(Even debugging is hard, since catalina has paused at this point so has to be unpaused by forcing a write to 7fd4).
My C code is this
return_value = _sys_plugin (plugin_type, 1);
and deconstructing the pasm code and hardwired some values
rosscode
' cogid t1 ' get ...
mov t1,#7 ' hard code 7
shl t1,#2 ' ... our ...
' add t1,par ' ... registry block
add t1,sevenfdfour ' hard code the registry address
rdlong rqstptr,t1 ' register ...
and rqstptr,low_24 ' ... this ...
wrlong zero,rqstptr ' ... plugin ...
mov t2,#8 ' ... as ...
shl t2,#24 ' ... the ...
or t2,rqstptr ' ... dummy ...
wrlong t2,t1 ' ... type
service_loop
rdlong t1,rqstptr wz ' service request?
if_z jmp #service_loop ' no - just keep waiting
mov time,cnt ' yes - wait ...
add time,five_seconds ' ... for ...
waitcnt time,#0 ' ... five seconds
wrlong zero,rqstptr ' release the kernel
jmp #service_loop ' wait for another request
But if we simplify this down, at the end of the day, is it not just the "wrlongs" that matter?
In which case, we have three of these
1)
wrlong zero,rqstptr ' this writes a zero to hub ram 0x7fcc
2)
wrlong t2,t1 ' this writes 0x08007fcc to hub ram 0x7fcc
3) release the kernel
wrlong zero,rqstptr ' this writes zero to 0x7fcc
If that is correct, then maybe it is my supporting C code that is incorrect - in particular the way I am loading cog code into cog code, unregistering it, then reregistering cog 7 as a dummy.
I just found a bug in the kernel that only affected XMM programs, and only those that used a plugin type of 8 - which just happens to be the number I chose to use for the DUM plugin!
This would explain why it works on my DRACBLADE (since I compile it in LMM mode) and not on yours (since your IDE always compiles programs in XMM mode).
Attached you will find two files:
Catalina_XMM.spin is an updated XMM kernel that needs to be copied to your target directory, overwriting the existing version.
test_plugin_2.c is a single-file version of the test_plugin program that does not need to include any other files (i.e. I have just applied homespun and spinc to the the PASM plugin, and then manually included the result in the appropriate place in the C source).
I compiled and loaded test_plugin_2 using the commands ...
catalina test_plugin_2.c -lc -D DRACBLADE -D LARGE
payload XMM test_plugin_2
... but you should be able to compile it using your IDE.
Hopefully, this will get the test program working for you, and then we can move on from there.
I'll test this in a moment. I'm just going to note something I have realised with my code as well which could be relevant.
When I load up my cog, I think I need a delay to run the subsequent C instructions that unregister and reregister the plugin. If the delay is 1 second, my hack of writing to 7fd4 does not work, so I couldn't debug it any further.
But I think a delay is needed before writing zero/value/zero.
draccode ' wait , then write a zero, write 08007fcc to 7ff0 then write a zero again
' wrlong zero,sevenfdfour ' this always works if here
mov t2,delay ' 1 sec delay if delay = 80000000?
add t2,cnt
waitcnt t2,delay
' wrlong zero,sevenfdfour ' this works if delay is short??
mov rqstptr,sevenffzero ' location of registry for cog 7
wrlong zero,rqstptr ' write a zero to 0x7ff0
wrlong newregistry,rqstptr ' write 08007fcc to 0x7ff0
wrlong zero,rqstptr ' write a zero to 0x7ff0
dracloop jmp #draccode
This is the relevant bit of the C code - load in a cog, unregister it, reregister a fake cog, wait for a reply 2 secs later
printf("Test program to pause catalina\n");
reg = _registry(); // address of registry
printf("Registry is at %x \n",reg);
print_registry();
readcog("plugin.cog",cogject_plugin);
plugin_parameters[1] = (unsigned long) reg; // array[0] is always a pointer to the array itself, so start at 1
plugin_engine_start(7,cogject_plugin,plugin_parameters);
printf("Cog plugin loaded \n",reg);
print_registry(); // print again, should have changed
_unregister_plugin(7); // unregister cog 7 but don't stop it running
printf("Unregister cog 7\n");
_register_plugin(7, 8); // register a fake cog number eg 7 is free, and with 8=DUM
printf("Reregister cog 7 as a dummy plugin\n");
cog = _locate_plugin(8);
printf("Cog number = %x \n",cog); // should print 7
return_value = _sys_plugin (plugin_type, 1); // program should hang here - see demo plugin.spin
printf("Return value = %i \n",return_value); // and not print this unless pluging registered and returning a zero
printf("End program\n");
while (1); // Prop reboots on exit from main()
and this is the pasm bit
draccode
mov rqstptr,sevenfcc ' rqstptr = 7fcc
wrlong zero,rqstptr ' write a zero to 0x7fcc
wrlong newregistry,sevenffzero ' write 08007fcc to 0x7ff0
mov time,cnt ' yes - wait ...
add time,two_seconds ' ... for ...
waitcnt time,#0 ' ... two seconds
wrlong zero,rqstptr ' write a zero to 0x7fcc, restarts catalina
dracloop jmp #dracloop ' endless loop
However, the reason the C code is doing this in a roundabout way is to debug the error which you found last night (the code needed to keep C running as long as possible to print debug values on the screen.) So now this is working, it should be possible to write much simpler C code. Back to coding!
Now we are having fun. This is a demo program that reads a plugin off the sd card, keeps it in external ram, then loads it into a cog as needed, all from C
#include <stdio.h>
// compile with catalina -lcx -D PLUGIN -lm -x5 -M 256k -d DRACBLADE -D HIRES_VGA myprog.c
// compile using XMM
// cogjects - sd card driver in catalina is a little slow so load up once into external ram then (re) load quicker from external ram
unsigned long cogject_plugin[512]; // external memory for plugin
// *************** functions ***************************************
void sleep(int milliseconds) // sleep function
{
_waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
}
int external_plugin_cog_load(int cognumber, unsigned long cogdata[]) // load a plugin from external memory
{
unsigned long hubcog[512]; // create a local array, this is in hub ram, not external ram
int i;
int result;
for(i=0;i<512;i++)
{
hubcog[i]=cogdata[i]; // move from external memory to a local array in hub
}
_coginit (_registry() >> 2, (int) hubcog >> 2, cognumber); // pass the registry, location of cog data and cog to load into
return result;
}
int EoF (FILE* stream)
{
register int c, status = ((c = fgetc(stream)) == EOF);
ungetc(c,stream);
return status;
}
void readcog(char *filename,unsigned long external_cog[]) // read in a .cog file into external memory array
{
int i;
FILE *FP1;
i = 0;
if((FP1=fopen(filename,"rb"))==0) // open the file
{
fprintf(stderr,"Can't open file %s\n",filename);
exit(1);
}
fseek(FP1,0,0);
for(i=0;i<24;i++)
{
getc(FP1); // read in the first 24 bytes and discard
}
i = 0;
while(!EoF(FP1) & (i<505)) // run until end of file or 511-6
{
external_cog[i] = getc(FP1) | (getc(FP1)<<8) | (getc(FP1)<<16) | (getc(FP1)<<24); // get the long
i+=1;
}
if(FP1)
{
fclose(FP1); // close the file
FP1=NULL;
}
//printf("external array cog first long = 0x%x \n",external_cog[0]); // hex value
}
void main ()
{
int result;
printf("Test program to pause catalina\n");
readcog("plugin.cog",cogject_plugin); // read from sd card into an external memory array
result = external_plugin_cog_load(7, cogject_plugin); // move plugin from external memory to cog
printf ("\nthis should take 5 seconds...\n"); // plugin programmed to delay for 5 secs
result = _sys_plugin(8, 1); // 8 is dummy plugin, and 1 is any non zero number
printf("Result (should be zero) = %i \n",result);
while (1); // Prop reboots on exit from main()
}
/*
''
'' Simple plugin that waits 5 seconds, then returns.
''
CON
_clkfreq = 80_000_000 ' 5Mhz Crystal
_clkmode = xtal1 + pll16x ' x 16
PUB Main
coginit(1,@entry,0) ' cog 1, cogstart, dummy value
DAT
org 0
entry
cogid t1 ' get ... t1=7
shl t1,#2 ' ... our ... t1=28
add t1,par ' ... registry block par=7fd4, t1=7ff0
rdlong rqstptr,t1 ' register ... rqstptr = 08007fcc
and rqstptr,low_24 ' ... this ... rqstptr = 00007fcc
wrlong zero,rqstptr ' ... plugin ... hub ram 7fcc = 0
mov t2,#8 ' ... as ... t2=8 = dummy value
shl t2,#24 ' ... the ... t2=08000000
or t2,rqstptr ' ... dummy ... t2=08007fcc
wrlong t2,t1 ' ... type hub ram 7ff0 = 08007fcc
service_loop
rdlong t1,rqstptr wz ' service request? check 7fcc
if_z jmp #service_loop ' no - just keep waiting
mov time,cnt ' yes - wait ...
add time,five_seconds ' ... for ...
waitcnt time,#0 ' ... five seconds
wrlong zero,rqstptr ' release the kernel write zero to 7fcc
jmp #service_loop ' wait for another request
low_24 long $00FFFFFF
five_seconds long _clkfreq*5
time long 0
rqstptr long 0
t1 long 0
t2 long 0
zero long 0
*/
Ok, let's say I want to pass an array to the cog. I can get the address of the array in C using a pointer. I can read the registry in C, work out I'm using cog 7, multiply by 4, add to the registry value, read the long in that location, and work out that 7fcc is the communication long. Is there a long above or below this that is used as a way of passing data back and forward?
Ok, let's say I want to pass an array to the cog. I can get the address of the array in C using a pointer. I can read the registry in C, work out I'm using cog 7, multiply by 4, add to the registry value, read the long in that location, and work out that 7fcc is the communication long. Is there a long above or below this that is used as a way of passing data back and forward?
No - just use the long you have identified. The convention Catalina uses is that the "communication long" (as you call it - I tend to call it the "request long") contains a code in the top 8 bits, and then either:
24 bits of data in the lower 24 bits, or
24 bits of address that point to another block of data (which you must allocate and manage!)
The first case is called a short request, and the second is called a long request. There are suitable functions (and structures) defined in catalina_plugin.h to make these easy to use.
All you need to do is decide on one or more codes you want to use (which must be non-zero) - then both the plugin and the C program must know the codes, and must also know whether each code represents a short or long request. For examples, see any of the HMI plugins.
Thanks Ross. I think that is the last piece of the jigsaw.
Is the request long this line of code? (or is it 7ff0)
rdlong t1,rqstptr wz ' service request? check 7fcc
if_z jmp #service_loop ' no - just keep waiting
If so, is the check on "z" looking for the entire long being zero, or just some of the bits?
I sent the US$250 through your sourceforge link to say thanks for all the coding, particularly for getting it working last night. This sort of instant support is what makes Catalina such a pleasure to use!
Thanks Ross. I think that is the last piece of the jigsaw.
Is the request long this line of code? (or is it 7ff0)
rdlong t1,rqstptr wz ' service request? check 7fcc
if_z jmp #service_loop ' no - just keep waiting
If so, is the check on "z" looking for the entire long being zero, or just some of the bits?
I sent the US$250 through your sourceforge link to say thanks for all the coding, particularly for getting it working last night. This sort of instant support is what makes Catalina such a pleasure to use!
Dr_A,
You didn't need to do that, but thanks very much!
As to your question, yes, the request long is the one the plugin is polling for a non-zero value (i.e. $7fcc). Any non-zero value in any of the bits triggers the exit from that loop, and in the case of this plugin that's all that's required - but most plugins would decode the value to see what service is being requested. As I said earlier, by convention this is the value in the upper 8 bits, and this value tells the plugin how to interpret the lower 24 bits - i.e. as an address or as data.
By the way - have you noticed the demo program called test_plugin_names.c in the demo folder? This program will print out the registry address, plus the registered type and the request block address for each loaded plugin. You can take the code out of that program for inclusion in your own program for debugging purposes.
Thanks for the pointer to test_plugin_names. I am working on the plugin and C code now at the same time so this will come in handy.
Just a thought about something Cluso and I discussed last week - as the size of a C program grows, the download time starts to get significant. It isn't a problem now with a 160k program, but anything that might run on Jazzed's megabyte boards would take a long time to download.
I wonder if it is possible to do a download directly via USB?
The propeller would be a USB slave, so that is a lot easier coding than being a host. One could think about downloading via serial a small startup program that then uses the same or different pins to get a USB connection going. Cluso felt that this could give a 10 fold increase in speed.
Would this be practical?
Also, would the speed start to be limited by the sd card write speed (and would a solution to that be Kye's sd driver)?
Thanks for the pointer to test_plugin_names. I am working on the plugin and C code now at the same time so this will come in handy.
Just a thought about something Cluso and I discussed last week - as the size of a C program grows, the download time starts to get significant. It isn't a problem now with a 160k program, but anything that might run on Jazzed's megabyte boards would take a long time to download.
I wonder if it is possible to do a download directly via USB?
The propeller would be a USB slave, so that is a lot easier coding than being a host. One could think about downloading via serial a small startup program that then uses the same or different pins to get a USB connection going. Cluso felt that this could give a 10 fold increase in speed.
Would this be practical?
Also, would the speed start to be limited by the sd card write speed (and would a solution to that be Kye's sd driver)?
Yes, serial is slow - even with USB speeds, loading a 16Mb program this way would be annoyingly slow. I generally only use serial for one-off loads (typically only during the initial debugging steps).
These days nearly all prop boards seem to have an SD card (or one can be added) so I have Catalyst loaded into EEPROM - then If I want to load a large program I just put the SD Card in my PC, copy the binary to it, and reinsert it into the Prop. Easy!
Yes I do that too. It works up to a point but the unplugging gets a little tedious.
I was thinking a USB connection would be easier.
But what you have said there raises another intriguing possibility. I have my little $2 USB to SD card adaptor. What if one could hack into that, and switch the 4 lines from the SD card to either the propeller or to a USB? There are only a handful of components on these adaptors - maybe the chips themselves are easily available (they can't be expensive since these adaptors are on ebay for $2 and that includes the plug and the case as well).
That could be a "no software" solution. Or at least a minimal software - maybe the propeller could devote an output pin to switch how the USB is controlled.
Or maybe an autodetect? My android pandapad works like that - normally its internal sd card is a drive within the android, but if you plug in a USB cable it switches to become a slave drive on the PC.
One advantage of this hardware solution is you can go straight to USB2 speeds without having to code any software. Then a 32Mb program would be very fast.
Yes I do that too. It works up to a point but the unplugging gets a little tedious.
I was thinking a USB connection would be easier.
But what you have said there raises another intriguing possibility. I have my little $2 USB to SD card adaptor. What if one could hack into that, and switch the 4 lines from the SD card to either the propeller or to a USB? There are only a handful of components on these adaptors - maybe the chips themselves are easily available (they can't be expensive since these adaptors are on ebay for $2 and that includes the plug and the case as well).
That could be a "no software" solution. Or at least a minimal software - maybe the propeller could devote an output pin to switch how the USB is controlled.
Or maybe an autodetect? My android pandapad works like that - normally its internal sd card is a drive within the android, but if you plug in a USB cable it switches to become a slave drive on the PC.
One advantage of this hardware solution is you can go straight to USB2 speeds without having to code any software. Then a 32Mb program would be very fast.
The problem is that these ideas will only work on certain hardware (or they require additional hardware).
The advantage of serial is that it works on every Prop platform. SD card is probably the next most common method for transferring files (every platform I own has one, although one of them I had to add myself). But once you go beyond that, your percentage of platforms supported are likely to be down in the single digits.
I hope one day soon to get some time to speed up the payload program. That should help a bit. David Betz was also working on a faster loader. He may already have something working.
Good points. Probably leave it for the moment then. Am guessing that at about 300k to 400k a program will start getting too tedious to use serial.
Below is a little demo program that sets up an array, starts a cog and the cog changes an element of that array. Very simple I know, but it is the building block for porting cogjects into plugins, pausing catalina and then using the XMM memory pins for other purposes.
#include <stdio.h>
// compile with catalina -lcx -D PLUGIN -lm -x5 -M 256k -d DRACBLADE -D HIRES_VGA myprog.c
// compile using XMM
// cogjects - sd card driver in catalina is a little slow so load up once into external ram then (re) load quicker from external ram
unsigned long cogject_plugin[512]; // external memory for plugin
// *************** functions ***************************************
void sleep(int milliseconds) // sleep function
{
_waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
}
char peek(int address) // function implementation of peek
{
return *((char *)address);
}
void poke(int address, char value) // function implementation of poke
{
*((char *)address) = value;
}
void pokelong(int address, unsigned long value) // poke a long into hub memory 'little endian'
{
poke(address+0, (char) (value & 0x000000ff));
poke(address+1, (char) ((value & 0x0000ff00) >> 8));
poke(address+2, (char) ((value & 0x00ff0000) >> 16));
poke(address+3, (char) ((value & 0xff000000) >> 24));
}
unsigned long peeklong(int address) // peek a long from hub memory 'little endian'
{
unsigned long value;
value = peek(address) | (peek(address+1) <<8) | (peek(address+2) <<16) | (peek(address+3)<<24);
return value;
}
int external_plugin_cog_load(int cognumber, unsigned long cogdata[]) // load a plugin from external memory
{
unsigned long hubcog[512]; // create a local array, this is in hub ram, not external ram
int i;
int result;
for(i=0;i<512;i++)
{
hubcog[i]=cogdata[i]; // move from external memory to a local array in hub
}
_coginit (_registry() >> 2, (int) hubcog >> 2, cognumber); // pass the registry, location of cog data and cog to load into
return result;
}
int EoF (FILE* stream)
{
register int c, status = ((c = fgetc(stream)) == EOF);
ungetc(c,stream);
return status;
}
void readcog(char *filename,unsigned long external_cog[]) // read in a .cog file into external memory array
{
int i;
FILE *FP1;
i = 0;
if((FP1=fopen(filename,"rb"))==0) // open the file
{
fprintf(stderr,"Can't open file %s\n",filename);
exit(1);
}
fseek(FP1,0,0);
for(i=0;i<24;i++)
{
getc(FP1); // read in the first 24 bytes and discard
}
i = 0;
while(!EoF(FP1) & (i<511)) // run until end of file or 511-6
{
external_cog[i] = getc(FP1) | (getc(FP1)<<8) | (getc(FP1)<<16) | (getc(FP1)<<24); // get the long
i+=1;
}
if(FP1)
{
fclose(FP1); // close the file
FP1=NULL;
}
//printf("external array cog first long = 0x%x \n",external_cog[0]); // hex value
}
void print7fcc()
{
int i;
char peekbyte;
printf("4 bytes from 7fcc to 7fc3 are ");
for (i=0;i<4;i++)
{
peekbyte = peek(0x7fcc+i);
if (peekbyte < 16)
{
printf("0");
}
printf("%x ",peekbyte);
}
printf ("\n"); // carriage return
}
void print_registry() // 32 bytes
{
int reg;
int i;
char peekbyte;
reg = _registry();
printf("0x%x = ",reg);
for (i=0;i<32;i++)
{
peekbyte = peek(reg+i);
if (peekbyte < 16)
{
printf("0");
}
printf("%x",peekbyte);
}
printf ("\n"); // carriage return
}
void pass_plugin_data(unsigned long p_array[],int cognumber)
{
int reg;
int a;
unsigned long b,c;
print_registry();
reg = _registry();
a=reg + (cognumber << 2); // registry + cognumber *4
b=peeklong(a); // get the registry for this cog ff007fcc
b = b & 0x0000ffff; // mask off upper two bytes
c = (unsigned long) &p_array[0]; // pointer to p_array
printf("pointer value is %x \n",c);
pokelong(b,c); // address,value 7fcc now contains a pointer to the p_array[0]
print7fcc();
}
void main ()
{
int result;
unsigned long p_array[3] = {1,2,3};
printf("Test program to pause catalina\n");
pass_plugin_data(p_array,7); // pass some data
readcog("plugin.cog",cogject_plugin); // read from sd card into an external memory array
result = external_plugin_cog_load(7, cogject_plugin); // move plugin from external memory to cog
printf ("\nthis should take 2 seconds...\n"); // plugin programmed to delay for 5 secs
result = _sys_plugin(8, 1); // 8 is dummy plugin, and 1 is any non zero number
printf("Result (should be zero) = %i \n",result);
printf("value of p_array[0] = %x \n",p_array[0]);
printf("value of p_array[1] = %x \n",p_array[1]);
printf("value of p_array[2] = %x \n",p_array[2]);
// printf("debug value @ 0x1000 = 0x%x \n",peeklong(0x1000)); // useful debug location
while (1); // Prop reboots on exit from main()
}
and the pasm part
''
'' Simple plugin that waits 5 seconds, then returns.
''
CON
_clkfreq = 80_000_000 ' 5Mhz Crystal
_clkmode = xtal1 + pll16x ' x 16
PUB Main
coginit(1,@entry,0) ' cog 1, cogstart, dummy value
DAT
org 0
entry
cogid t1 ' get ... t1=7
shl t1,#2 ' ... our ... t1=28
add t1,par ' ... registry block par=7fd4, t1=7ff0
rdlong rqstptr,t1 ' register ... rqstptr = 08007fcc
and rqstptr,low_24 ' ... this ... rqstptr = 00007fcc
rdlong arraypointer,rqstptr ' save for later as 7fcc gets changed below arraypointer = 6ab4
wrlong zero,rqstptr ' ... plugin ... hub ram 7fcc = 0
mov t2,#8 ' ... as ... t2=8 = dummy value
shl t2,#24 ' ... the ... t2=08000000
or t2,rqstptr ' ... dummy ... t2=08007fcc
wrlong t2,t1 ' ... type hub ram 7ff0 = 08007fcc
service_loop
rdlong t1,rqstptr wz ' service request? check 7fcc
if_z jmp #service_loop ' no - just keep waiting
mov time,cnt ' yes - wait ...
add time,two_seconds ' ... for ...
waitcnt time,#0 ' ... two seconds
' change the first element of the array
rdlong t1,arraypointer ' get array[0] (6ab4) in t1
add t1,#10 ' add 10
wrlong t1,arraypointer ' write back to hub value, address
wrlong zero,rqstptr ' release the kernel write zero to 7fcc
finish jmp #finish ' finish
low_24 long $00FFFFFF
two_seconds long _clkfreq*2
time long 0
rqstptr long 0
t1 long 0
t2 long 0
zero long 0
'sixabfour long $6AB4
'sevenfcc long $7FCC
arraypointer long 0
'debug long $1000 ' for debugging
'testvalue long $12345678
UPDATE: Another small patch release (3.0.2) is now available, and is attached to this post. This patch release must be installed over an existing Catalina 3.0 installation. This is a cumulative patch that contains all the bug fixes from previous patches. Here are the main changes:
Fixed a bug in the plugin search algorithm used in the XMM kernel which meant the search stopped when the plugin type was 8 instead of when it reached cog number 8! This meant that plugin type 8 (allocated to the DUM plugin) could never be found in XMM programs.
Fixed some typos in the definition of the waitpne and waitpeq macros in the header file catalina_icc.h
I've got a lot of things working in a plugin, but I've come across a small problem with one of the pins - P8 on the chip stays high.
I've narrowed it down to this little bit of code to replicate the problem
org 0
entry
cogid t1 ' get ... t1=7
shl t1,#2 ' ... our ... t1=28
add t1,par ' ... registry block par=7fd4, t1=7ff0
rdlong rqstptr,t1 ' register ... rqstptr = 08007fcc
and rqstptr,low_24 ' ... this ... rqstptr = 00007fcc
rdlong arraypointer,rqstptr ' save for later as 7fcc gets changed below arraypointer = 6ab4
wrlong zero,rqstptr ' ... plugin ... hub ram 7fcc = 0
mov t2,#8 ' ... as ... t2=8 = dummy value
shl t2,#24 ' ... the ... t2=08000000
or t2,rqstptr ' ... dummy ... t2=08007fcc
wrlong t2,t1 ' ... type hub ram 7ff0 = 08007fcc
service_loop
rdlong t1,rqstptr wz ' service request? check 7fcc
if_z jmp #service_loop ' no - just keep waiting
' ********* debug routine to test why pin 8 stays high
mov dira, latch_dir ' set latch direction, enable pins 0-11
mov t1,zero
mov outa,t1 ' should leave all pins 0-11 low but instead pin 8 is high
jmp #finish
finish jmp #finish ' finish
' *************** variables ******************************************************************
low_24 long $00FFFFFF
two_seconds long _clkfreq*2
twohundredms long _clkfreq/5
time long 0
rqstptr long 0
i long 0
t1 long 0
t2 long 0
zero long 0
data long 0
minusone long $FFFFFFFF
'sixabfour long $6AB4
'sevenfcc long $7FCC
arraypointer long 0
debug long $1000 ' for debugging
testvalue long $12345678
lowmask long %00000000_00000000_00000000_11111111
' *** LCD variables ***
latch_dir long %00000000_00000000_00001111_11111111 ' 138 active, gate active and 8 data lines active
latch_lcd long %00000000_00000000_00001010_00000000 ' LCD latch is xxxx101x and gate low xxxxxxx0
enable_lcd long %00000000_00000000_00000000_00000010 ' Enable pin 6 on LCD displays
lcd_delay long 300_000
The
mov dira, latch_dir ' set latch direction, enable pins 0-11
ought to enable the lower 12 pins of the propeller
and
mov t1,zero
mov outa,t1 ' should leave all pins 0-11 low but instead pin 8 is high
ought to leave the pins low.
On my board, pin 8 has a 10k pullup so there are two possibilities
1) the mov dira is being overwritten by something that puts pin 8 back to being tristate (not the other pins though - these stay low)
2) Somthing in catalina is changing the pin high.
I thought maybe catalina hadn't quite shutdown by the time the service request has come though but adding a delay after this but before taking over the pins doesn't seem to help.
Can you think of any reason this pin is staying high?
Simple test, shut down every other cog. If the pin goes down (as intended) then someone else is driving it. Or start with validating dira/outa by toggling a specific pin/light an LED if it holds the correct value.
Thanks kuroneko. I have added code to catalina to shut down cog 0,1,3,4,5,6.
Cog 7 is running my code.
This only leaves one cog running - cog 2 which is the cog that is running catalina. I also put the code to put the pin low into a loop but it still is high. All most strange...
loop mov dira, latch_dir ' set latch direction, enable pins 0-11
mov t1,zero
mov outa,t1 ' should leave all pins 0-11 low but instead pin 8 is high
jmp #loop
After testing lots of combinations shutting down cogs from pasm, it seems cog 1 is the problem, not cog 2. I'll need to find out what cog 1 is running.
After testing lots of combinations shutting down cogs from pasm, it seems cog 1 is the problem, not cog 2. I'll need to find out what cog 1 is running.
Hi Dr_A,
The code in the test_plugin_names.c shows how to easily print out from C what's running in each cog. Just add this to your existing program. Let me know what it is and I'll investigate.
Huge amounts of progress. I put in a cogstop for cog 1 and then dropped in the entire 20x4 LCD display code and it works brilliantly. The Catalina IDE has been very useful here as it is possible to compile and download pasm code and C code independently from the same program and hence quickly debug ideas. What this means is that not only is a plugin possible but also that it is possible to share pins with catalina. This is going to be very useful for the "Propeller GUI" project.
Ran test_plugin_names and cog1 is half the screen driver. (0=keyboard, 1=screen, 2=kernel, 3=screen, 4=hmi, 5=float_a, 6=sd file, 7=unused"
So something in that screen driver seems to be holding pin 8 high.
Huge amounts of progress. I put in a cogstop for cog 1 and then dropped in the entire 20x4 LCD display code and it works brilliantly. The Catalina IDE has been very useful here as it is possible to compile and download pasm code and C code independently from the same program and hence quickly debug ideas. What this means is that not only is a plugin possible but also that it is possible to share pins with catalina. This is going to be very useful for the "Propeller GUI" project.
Ran test_plugin_names and cog1 is half the screen driver. (0=keyboard, 1=screen, 2=kernel, 3=screen, 4=hmi, 5=float_a, 6=sd file, 7=unused"
So something in that screen driver seems to be holding pin 8 high.
Hi Dr_A,
I presume you are using the HiRes VGA driver? That's a parallax driver, pretty much unmodified - I'll have a look at it tonight.
EDIT: Please confirm that you ran the code from the test_plugin_names in the program that actually exhibits the problem - just running that program separately is likley to give a different set of cogs, and we could end up looking at the wrong driver!
EDIT: Please confirm that you ran the code from the test_plugin_names in the program that actually exhibits the problem - just running that program separately is likley to give a different set of cogs, and we could end up looking at the wrong driver!
No I ran it as a separate program. Also I needed to add back in the math routines which takes a cog, otherwise it would not compile and gave two errors about "pow" and "log10". So how would I go about combining test_plugin_names into my program and avoid that math routine error? (which I think is buried in an include somewhere).
No I ran it as a separate program. Also I needed to add back in the math routines which takes a cog, otherwise it would not compile and gave two errors about "pow" and "log10". So how would I go about combining test_plugin_names into my program and avoid that math routine error? (which I think is buried in an include somewhere).
Hi Dr_A,
You could compile with the integer only library (-lci) instead of the standard library(-lc), but I've also rewritten the code to not use printf() (which was pulling in the maths functions):
#include <catalina_plugin.h>
#include <catalina_hmi.h>
char *name(int type) {
switch (type) {
case 0 : return "Kernel";
case 1 : return "HMI";
case 2 : return "Library";
case 3 : return "Float_A";
case 4 : return "Float_B";
case 5 : return "Real-Time Clock";
case 6 : return "SD File System";
case 7 : return "Serial I/O";
case 8 : return "Dummy";
case 9 : return "Graphics";
case 10 : return "Keyboard";
case 11 : return "Screen";
case 12 : return "Mouse";
case 13 : return "Proxy";
default : return "Unknown/None";
}
}
void print_plugin_names() {
int i;
int type;
request_t *rqst;
for (i = 0; i < 8; i++) {
type = REGISTERED_TYPE(i);
rqst = REQUEST_BLOCK(i);
//t_printf("Cog %d (%x) Type = %s\n", i, (unsigned)rqst, name(type));
t_string(1, "Cog ");
t_integer(1, i);
t_string(1, " (");
t_hex(1, (unsigned)rqst);
t_string(1, ") Type = ");
t_string(1, name(type));
t_char(1, '\n');
}
}
Just include the above code in your program, then call print_plugin_names() from somewhere in your main() function.
I've done some experiments. Some of the results are a bit confusing.
1) The printout from my program with your code pasted in
0=keyboard
1=screen
2=kernel
3=screen
4=HMI
5=float_a
6=sd
Next - add this code into the C program:
// _cogstop(0); // not screen
// _cogstop(1); // screen - needs 1 and 3 running
// _cogstop(2); // not screen
// _cogstop(3); // screen - needs 1 and 3 running
// _cogstop(4); // not screen
// _cogstop(5); // not screen
// _cogstop(6); // not screen
And the experiment seems to concur with the printout - ie if either cog 1 or cog 3 is stopped then the screen goes blank.
However, there does not seem to be a combination that allows the LCD display to work.
Next, experiments within the PASM code
' cogstop 0 ' ? screen
' cogstop 1 ' cog 1 is the one that needs to be stopped to get the LCD to work
' cogstop 2
' cogstop 3
' cogstop 4
' cogstop 5
' cogstop 6
This is where things get strange. Stopping cog 1 allows the LCD display to work. Stopping cog 0 makes the screen go blank (0 is the keyboard in the C code). And stopping all of cogs 2 to 6 with cogs 0 and 1 enabled and the screen still is on.
So I am not sure whether cogstop in pasm is the same as cogstop in C.
And so as a result of this, I am not at all sure what is running in cog 1 (pasm) which is the one that needs to be disabled to get the LCD display to work.
' cogstop 0 ' ? screen
' cogstop 1 ' cog 1 is the one that needs to be stopped to get the LCD to work
' cogstop 2
' cogstop 3
' cogstop 4
' cogstop 5
' cogstop 6
Is that how you wrote it (in PASM)? Just checking ... because cogstop takes its parameter in D rather than S so you'd have to do at least something like
Ok, well testing that with the cogs one at a time, and this is the line that allows the LCD display to work:
cogstop $+1
long 2 ' "long 1" = stop cog #1, long 2 = stop cog #2
and I think that is stopping cog2. If that is true, then that would explain why I couldn't replicate the problem from C, because cog2 is the kernel and this is the one cog you can't stop before calling the driver because C needs to be running right up until the driver is called.
So this might now be a question for Ross if it is the kernel that is still controlling pin 8.
Comments
Hi Dr_A,
I'll generate you a version you can compile and run from your IDE.
Ross.
Now that I can test pasm code quickly I tried about 20 combinations and while nothing works, I have learned quite a bit. I did a debug routine and you are correct
(Even debugging is hard, since catalina has paused at this point so has to be unpaused by forcing a write to 7fd4).
My C code is this
and deconstructing the pasm code and hardwired some values
But if we simplify this down, at the end of the day, is it not just the "wrlongs" that matter?
In which case, we have three of these
1)
wrlong zero,rqstptr ' this writes a zero to hub ram 0x7fcc
2)
wrlong t2,t1 ' this writes 0x08007fcc to hub ram 0x7fcc
3) release the kernel
wrlong zero,rqstptr ' this writes zero to 0x7fcc
If that is correct, then maybe it is my supporting C code that is incorrect - in particular the way I am loading cog code into cog code, unregistering it, then reregistering cog 7 as a dummy.
I just found a bug in the kernel that only affected XMM programs, and only those that used a plugin type of 8 - which just happens to be the number I chose to use for the DUM plugin!
This would explain why it works on my DRACBLADE (since I compile it in LMM mode) and not on yours (since your IDE always compiles programs in XMM mode).
Attached you will find two files:
- Catalina_XMM.spin is an updated XMM kernel that needs to be copied to your target directory, overwriting the existing version.
- test_plugin_2.c is a single-file version of the test_plugin program that does not need to include any other files (i.e. I have just applied homespun and spinc to the the PASM plugin, and then manually included the result in the appropriate place in the C source).
I compiled and loaded test_plugin_2 using the commands ...Ross.
When I load up my cog, I think I need a delay to run the subsequent C instructions that unregister and reregister the plugin. If the delay is 1 second, my hack of writing to 7fd4 does not work, so I couldn't debug it any further.
But I think a delay is needed before writing zero/value/zero.
Now off to test your code...
Do you have the source for the code in the plugin array?
The source is plugin.spin from the plugin.zip file. I just compiled it with homespun, and then ran spinc on it.
I'm afraid I'm off to bed now - but thank goodness we're finally making progress!
Ross.
This is the relevant bit of the C code - load in a cog, unregister it, reregister a fake cog, wait for a reply 2 secs later
and this is the pasm bit
However, the reason the C code is doing this in a roundabout way is to debug the error which you found last night (the code needed to keep C running as long as possible to print debug values on the screen.) So now this is working, it should be possible to write much simpler C code. Back to coding!
Ok, let's say I want to pass an array to the cog. I can get the address of the array in C using a pointer. I can read the registry in C, work out I'm using cog 7, multiply by 4, add to the registry value, read the long in that location, and work out that 7fcc is the communication long. Is there a long above or below this that is used as a way of passing data back and forward?
No - just use the long you have identified. The convention Catalina uses is that the "communication long" (as you call it - I tend to call it the "request long") contains a code in the top 8 bits, and then either:
- 24 bits of data in the lower 24 bits, or
- 24 bits of address that point to another block of data (which you must allocate and manage!)
The first case is called a short request, and the second is called a long request. There are suitable functions (and structures) defined in catalina_plugin.h to make these easy to use.All you need to do is decide on one or more codes you want to use (which must be non-zero) - then both the plugin and the C program must know the codes, and must also know whether each code represents a short or long request. For examples, see any of the HMI plugins.
Is the request long this line of code? (or is it 7ff0)
If so, is the check on "z" looking for the entire long being zero, or just some of the bits?
I sent the US$250 through your sourceforge link to say thanks for all the coding, particularly for getting it working last night. This sort of instant support is what makes Catalina such a pleasure to use!
Dr_A,
You didn't need to do that, but thanks very much!
As to your question, yes, the request long is the one the plugin is polling for a non-zero value (i.e. $7fcc). Any non-zero value in any of the bits triggers the exit from that loop, and in the case of this plugin that's all that's required - but most plugins would decode the value to see what service is being requested. As I said earlier, by convention this is the value in the upper 8 bits, and this value tells the plugin how to interpret the lower 24 bits - i.e. as an address or as data.
By the way - have you noticed the demo program called test_plugin_names.c in the demo folder? This program will print out the registry address, plus the registered type and the request block address for each loaded plugin. You can take the code out of that program for inclusion in your own program for debugging purposes.
Just a thought about something Cluso and I discussed last week - as the size of a C program grows, the download time starts to get significant. It isn't a problem now with a 160k program, but anything that might run on Jazzed's megabyte boards would take a long time to download.
I wonder if it is possible to do a download directly via USB?
The propeller would be a USB slave, so that is a lot easier coding than being a host. One could think about downloading via serial a small startup program that then uses the same or different pins to get a USB connection going. Cluso felt that this could give a 10 fold increase in speed.
Would this be practical?
Also, would the speed start to be limited by the sd card write speed (and would a solution to that be Kye's sd driver)?
These days nearly all prop boards seem to have an SD card (or one can be added) so I have Catalyst loaded into EEPROM - then If I want to load a large program I just put the SD Card in my PC, copy the binary to it, and reinsert it into the Prop. Easy!
Ross.
I was thinking a USB connection would be easier.
But what you have said there raises another intriguing possibility. I have my little $2 USB to SD card adaptor. What if one could hack into that, and switch the 4 lines from the SD card to either the propeller or to a USB? There are only a handful of components on these adaptors - maybe the chips themselves are easily available (they can't be expensive since these adaptors are on ebay for $2 and that includes the plug and the case as well).
That could be a "no software" solution. Or at least a minimal software - maybe the propeller could devote an output pin to switch how the USB is controlled.
Or maybe an autodetect? My android pandapad works like that - normally its internal sd card is a drive within the android, but if you plug in a USB cable it switches to become a slave drive on the PC.
One advantage of this hardware solution is you can go straight to USB2 speeds without having to code any software. Then a 32Mb program would be very fast.
The advantage of serial is that it works on every Prop platform. SD card is probably the next most common method for transferring files (every platform I own has one, although one of them I had to add myself). But once you go beyond that, your percentage of platforms supported are likely to be down in the single digits.
I hope one day soon to get some time to speed up the payload program. That should help a bit. David Betz was also working on a faster loader. He may already have something working.
Ross.
Below is a little demo program that sets up an array, starts a cog and the cog changes an element of that array. Very simple I know, but it is the building block for porting cogjects into plugins, pausing catalina and then using the XMM memory pins for other purposes.
and the pasm part
- Fixed some typos in the definition of the waitpne and waitpeq macros in the header file catalina_icc.h
Ross.I've got a lot of things working in a plugin, but I've come across a small problem with one of the pins - P8 on the chip stays high.
I've narrowed it down to this little bit of code to replicate the problem
The
ought to enable the lower 12 pins of the propeller
and
ought to leave the pins low.
On my board, pin 8 has a 10k pullup so there are two possibilities
1) the mov dira is being overwritten by something that puts pin 8 back to being tristate (not the other pins though - these stay low)
2) Somthing in catalina is changing the pin high.
I thought maybe catalina hadn't quite shutdown by the time the service request has come though but adding a delay after this but before taking over the pins doesn't seem to help.
Can you think of any reason this pin is staying high?
Cog 7 is running my code.
This only leaves one cog running - cog 2 which is the cog that is running catalina. I also put the code to put the pin low into a loop but it still is high. All most strange...
After testing lots of combinations shutting down cogs from pasm, it seems cog 1 is the problem, not cog 2. I'll need to find out what cog 1 is running.
The code in the test_plugin_names.c shows how to easily print out from C what's running in each cog. Just add this to your existing program. Let me know what it is and I'll investigate.
Ross.
Ran test_plugin_names and cog1 is half the screen driver. (0=keyboard, 1=screen, 2=kernel, 3=screen, 4=hmi, 5=float_a, 6=sd file, 7=unused"
So something in that screen driver seems to be holding pin 8 high.
Hi Dr_A,
I presume you are using the HiRes VGA driver? That's a parallax driver, pretty much unmodified - I'll have a look at it tonight.
EDIT: Please confirm that you ran the code from the test_plugin_names in the program that actually exhibits the problem - just running that program separately is likley to give a different set of cogs, and we could end up looking at the wrong driver!
Ross.
No I ran it as a separate program. Also I needed to add back in the math routines which takes a cog, otherwise it would not compile and gave two errors about "pow" and "log10". So how would I go about combining test_plugin_names into my program and avoid that math routine error? (which I think is buried in an include somewhere).
Hi Dr_A,
You could compile with the integer only library (-lci) instead of the standard library(-lc), but I've also rewritten the code to not use printf() (which was pulling in the maths functions):
Just include the above code in your program, then call print_plugin_names() from somewhere in your main() function.
Ross.
I've done some experiments. Some of the results are a bit confusing.
1) The printout from my program with your code pasted in
0=keyboard
1=screen
2=kernel
3=screen
4=HMI
5=float_a
6=sd
Next - add this code into the C program:
// _cogstop(0); // not screen
// _cogstop(1); // screen - needs 1 and 3 running
// _cogstop(2); // not screen
// _cogstop(3); // screen - needs 1 and 3 running
// _cogstop(4); // not screen
// _cogstop(5); // not screen
// _cogstop(6); // not screen
And the experiment seems to concur with the printout - ie if either cog 1 or cog 3 is stopped then the screen goes blank.
However, there does not seem to be a combination that allows the LCD display to work.
Next, experiments within the PASM code
' cogstop 0 ' ? screen
' cogstop 1 ' cog 1 is the one that needs to be stopped to get the LCD to work
' cogstop 2
' cogstop 3
' cogstop 4
' cogstop 5
' cogstop 6
This is where things get strange. Stopping cog 1 allows the LCD display to work. Stopping cog 0 makes the screen go blank (0 is the keyboard in the C code). And stopping all of cogs 2 to 6 with cogs 0 and 1 enabled and the screen still is on.
So I am not sure whether cogstop in pasm is the same as cogstop in C.
And so as a result of this, I am not at all sure what is running in cog 1 (pasm) which is the one that needs to be disabled to get the LCD display to work.
Help here would be most appreciated!
Ok, well testing that with the cogs one at a time, and this is the line that allows the LCD display to work:
and I think that is stopping cog2. If that is true, then that would explain why I couldn't replicate the problem from C, because cog2 is the kernel and this is the one cog you can't stop before calling the driver because C needs to be running right up until the driver is called.
So this might now be a question for Ross if it is the kernel that is still controlling pin 8.